don't focus window until super+tab is released
This commit is contained in:
@@ -6,8 +6,8 @@ commands for application and same-application window switching.
|
||||
|
||||
The application HUD groups windows by Hyprland `class`, orders applications and
|
||||
their windows by most-recent focus, and resolves names and icons from installed
|
||||
desktop files. Selecting an application raises all of its windows and focuses
|
||||
its most-recent window.
|
||||
desktop files. Cycling changes only the HUD selection; accepting that selection
|
||||
raises all of the application's windows and focuses its most-recent window.
|
||||
|
||||
This implementation uses only standard Hyprland IPC. It does not depend on the
|
||||
custom `cycleapp` or `cycleappwindow` dispatchers.
|
||||
@@ -16,7 +16,7 @@ custom `cycleapp` or `cycleappwindow` dispatchers.
|
||||
|
||||
- `Super+Tab` / `Super+Shift+Tab`: show the application HUD and move selection.
|
||||
- Release `Super` or press `Enter`: accept the selection.
|
||||
- `Escape`: cancel and restore the original application.
|
||||
- `Escape`: cancel without changing application focus or window stacking.
|
||||
- `Super+grave` / `Super+Shift+grave`: cycle windows in the current application.
|
||||
- `Super+H`: hide every window in the current application.
|
||||
|
||||
@@ -91,10 +91,11 @@ Set `RUST_LOG=hypr_switcher=debug` on the daemon command for diagnostics.
|
||||
- `src/main.rs` owns the daemon command socket, switch sessions, GTK HUD, and
|
||||
keyboard handling.
|
||||
|
||||
The daemon freezes MRU updates while the HUD is previewing applications so its
|
||||
own focus requests do not reorder the active switch session. Final focus is
|
||||
sent shortly after the layer-shell HUD closes, allowing its exclusive keyboard
|
||||
grab to be released first.
|
||||
The daemon freezes MRU updates while the HUD is active, keeping the application
|
||||
order stable throughout the switch session. It does not raise or focus the
|
||||
highlighted application while cycling. Final focus is sent shortly after the
|
||||
layer-shell HUD closes, allowing its exclusive keyboard grab to be released
|
||||
first.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
+10
-29
@@ -43,7 +43,6 @@ struct Session {
|
||||
id: u64,
|
||||
groups: Vec<AppGroup>,
|
||||
selected: usize,
|
||||
original: usize,
|
||||
destination_workspace: String,
|
||||
}
|
||||
|
||||
@@ -306,7 +305,6 @@ impl Switcher {
|
||||
id: session_id,
|
||||
groups,
|
||||
selected,
|
||||
original,
|
||||
destination_workspace: workspace.name,
|
||||
});
|
||||
drop(state);
|
||||
@@ -315,7 +313,6 @@ impl Switcher {
|
||||
self.window
|
||||
.set_keyboard_mode(layer::KeyboardMode::Exclusive);
|
||||
self.window.present();
|
||||
self.activate_selected();
|
||||
}
|
||||
|
||||
fn cycle(&self, direction: Direction) {
|
||||
@@ -330,24 +327,6 @@ impl Switcher {
|
||||
};
|
||||
drop(state);
|
||||
self.render();
|
||||
self.activate_selected();
|
||||
}
|
||||
|
||||
fn activate_selected(&self) {
|
||||
let selected = self
|
||||
.state
|
||||
.borrow()
|
||||
.session
|
||||
.as_ref()
|
||||
.map(|session| session.groups[session.selected].clone());
|
||||
if let Some(group) = selected {
|
||||
if group.hidden {
|
||||
return;
|
||||
}
|
||||
if let Err(error) = ipc::activate_app(&group) {
|
||||
log::error!("failed to activate {}: {error:#}", group.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hide_current_app(&self) {
|
||||
@@ -480,26 +459,28 @@ impl Switcher {
|
||||
}
|
||||
|
||||
fn finish(&self, commit: bool) {
|
||||
let (target, destination_workspace) = {
|
||||
let selection = {
|
||||
let mut state = self.state.borrow_mut();
|
||||
let Some(session) = state.session.take() else {
|
||||
return;
|
||||
};
|
||||
let target_index = if commit {
|
||||
session.selected
|
||||
} else {
|
||||
session.original
|
||||
};
|
||||
let target = session.groups[target_index].clone();
|
||||
if commit {
|
||||
let target = session.groups[session.selected].clone();
|
||||
if let Some(window) = target.representative() {
|
||||
state.history.touch(&window.address);
|
||||
}
|
||||
state.ignore_focus_until = Some(Instant::now() + Duration::from_millis(200));
|
||||
(target, session.destination_workspace)
|
||||
Some((target, session.destination_workspace))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
self.window.set_keyboard_mode(layer::KeyboardMode::None);
|
||||
self.window.hide();
|
||||
let Some((target, destination_workspace)) = selection else {
|
||||
return;
|
||||
};
|
||||
// The layer surface releases its exclusive keyboard focus
|
||||
// asynchronously. Wait for that round-trip before focusing the
|
||||
// selected toplevel, otherwise Hyprland raises it without granting
|
||||
|
||||
Reference in New Issue
Block a user