actions: Shortcut handling changes for #1005

This commit is contained in:
Victoria Brekenfeld 2025-02-26 17:13:31 +01:00 committed by Victoria Brekenfeld
parent 638684642c
commit d76f372c29
6 changed files with 286 additions and 477 deletions

View file

@ -28,6 +28,13 @@ use std::{os::unix::process::CommandExt, thread, time::Duration};
use super::gestures;
fn propagate_by_default(action: &shortcuts::Action) -> bool {
match action {
shortcuts::Action::Focus(_) | shortcuts::Action::Move(_) => true,
_ => false,
}
}
impl State {
pub fn handle_action(
&mut self,
@ -37,7 +44,6 @@ impl State {
time: u32,
pattern: shortcuts::Binding,
direction: Option<Direction>,
propagate: bool,
) {
// TODO: Detect if started from login manager or tty, and only allow
// `Terminate` if it will return to login manager.
@ -52,9 +58,12 @@ impl State {
}
match action {
Action::Shortcut(action) => self
.handle_shortcut_action(action, seat, serial, time, pattern, direction, propagate),
Action::Shortcut(action) => {
let propagate = propagate_by_default(&action);
self.handle_shortcut_action(
action, seat, serial, time, pattern, direction, propagate,
)
}
Action::Private(PrivateAction::Escape) => {
{
let mut shell = self.common.shell.write().unwrap();
@ -184,16 +193,40 @@ impl State {
}
Action::NextWorkspace => {
if let Some(direction) = pattern.inferred_direction() {
if ((direction == Direction::Left || direction == Direction::Right)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Vertical)
|| ((direction == Direction::Up || direction == Direction::Down)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal)
{
return;
}
}
let next = to_next_workspace(
&mut *self.common.shell.write().unwrap(),
seat,
false,
&mut self.common.workspace_state.update(),
);
if next.is_err() && propagate {
if let Some(inferred) = pattern.inferred_direction() {
if next.is_err() {
if propagate {
if let Some(inferred) = pattern.inferred_direction() {
self.handle_shortcut_action(
Action::SwitchOutput(inferred),
seat,
serial,
time,
pattern,
direction,
false,
)
};
} else {
self.handle_shortcut_action(
Action::SwitchOutput(inferred),
Action::Workspace(1),
seat,
serial,
time,
@ -201,21 +234,45 @@ impl State {
direction,
false,
)
};
}
}
}
Action::PreviousWorkspace => {
if let Some(direction) = pattern.inferred_direction() {
if ((direction == Direction::Left || direction == Direction::Right)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Vertical)
|| ((direction == Direction::Up || direction == Direction::Down)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal)
{
return;
}
}
let previous = to_previous_workspace(
&mut *self.common.shell.write().unwrap(),
seat,
false,
&mut self.common.workspace_state.update(),
);
if previous.is_err() && propagate {
if let Some(inferred) = pattern.inferred_direction() {
if previous.is_err() {
if propagate {
if let Some(inferred) = pattern.inferred_direction() {
self.handle_shortcut_action(
Action::SwitchOutput(inferred),
seat,
serial,
time,
pattern,
direction,
false,
)
};
} else {
self.handle_shortcut_action(
Action::SwitchOutput(inferred),
Action::LastWorkspace,
seat,
serial,
time,
@ -223,7 +280,7 @@ impl State {
direction,
false,
)
};
}
}
}
@ -284,24 +341,39 @@ impl State {
}
x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => {
if let Some(direction) = pattern.inferred_direction() {
if ((direction == Direction::Left || direction == Direction::Right)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Vertical)
|| ((direction == Direction::Up || direction == Direction::Down)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal)
{
return;
}
}
let Some(focused_output) = seat.focused_output() else {
return;
};
let res = {
let mut shell = self.common.shell.write().unwrap();
let workspace = shell
shell
.workspaces
.active_num(&focused_output)
.1
.saturating_add(1);
shell.move_current_window(
seat,
&focused_output,
(&focused_output, Some(workspace)),
matches!(x, Action::MoveToNextWorkspace),
direction,
&mut self.common.workspace_state.update(),
)
.checked_add(1)
.ok_or(InvalidWorkspaceIndex)
.and_then(|workspace| {
shell.move_current_window(
seat,
&focused_output,
(&focused_output, Some(workspace)),
matches!(x, Action::MoveToNextWorkspace),
direction,
&mut self.common.workspace_state.update(),
)
})
};
match res {
@ -315,6 +387,7 @@ impl State {
matches!(x, Action::MoveToNextWorkspace),
);
}
Ok(None) => {}
Err(_) if propagate => {
if let Some(inferred) = pattern.inferred_direction() {
self.handle_shortcut_action(
@ -332,30 +405,59 @@ impl State {
)
}
}
_ => {}
Err(_) => {
// cycle through
self.handle_shortcut_action(
if matches!(x, Action::MoveToNextWorkspace) {
Action::MoveToWorkspace(1)
} else {
Action::SendToWorkspace(1)
},
seat,
serial,
time,
pattern,
direction,
false,
)
}
}
}
x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => {
if let Some(direction) = pattern.inferred_direction() {
if ((direction == Direction::Left || direction == Direction::Right)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Vertical)
|| ((direction == Direction::Up || direction == Direction::Down)
&& self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal)
{
return;
}
}
let Some(focused_output) = seat.focused_output() else {
return;
};
let res = {
let mut shell = self.common.shell.write().unwrap();
let workspace = shell
shell
.workspaces
.active_num(&focused_output)
.1
.saturating_sub(1);
// TODO: Possibly move to prev output, if idx < 0
shell.move_current_window(
seat,
&focused_output,
(&focused_output, Some(workspace)),
matches!(x, Action::MoveToPreviousWorkspace),
direction,
&mut self.common.workspace_state.update(),
)
.checked_sub(1)
.ok_or(InvalidWorkspaceIndex)
.and_then(|workspace| {
shell.move_current_window(
seat,
&focused_output,
(&focused_output, Some(workspace)),
matches!(x, Action::MoveToPreviousWorkspace),
direction,
&mut self.common.workspace_state.update(),
)
})
};
match res {
@ -368,6 +470,7 @@ impl State {
matches!(x, Action::MoveToPreviousWorkspace),
);
}
Ok(None) => {}
Err(_) if propagate => {
if let Some(inferred) = pattern.inferred_direction() {
self.handle_shortcut_action(
@ -385,7 +488,22 @@ impl State {
)
}
}
_ => {}
Err(_) => {
// cycle through
self.handle_shortcut_action(
if matches!(x, Action::MoveToPreviousWorkspace) {
Action::MoveToLastWorkspace
} else {
Action::SendToLastWorkspace
},
seat,
serial,
time,
pattern,
direction,
false,
)
}
}
}
@ -441,155 +559,15 @@ impl State {
ptr.frame(self);
}
}
} else if propagate {
std::mem::drop(shell);
let action = match (
direction,
self.common.config.cosmic_conf.workspaces.workspace_layout,
) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => {
Some(Action::PreviousWorkspace)
}
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => {
Some(Action::NextWorkspace)
}
_ => None,
};
if let Some(action) = action {
self.handle_shortcut_action(
action,
seat,
serial,
time,
pattern,
Some(direction),
false,
)
}
}
}
Action::NextOutput => {
let current_output = seat.active_output();
let mut shell = self.common.shell.write().unwrap();
let next_output = shell
.outputs()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned();
if let Some(next_output) = next_output {
let idx = shell.workspaces.active_num(&next_output).1;
let res = shell.activate(
&next_output,
idx,
WorkspaceDelta::new_shortcut(),
&mut self.common.workspace_state.update(),
);
seat.set_active_output(&next_output);
if let Ok(Some(new_pos)) = res {
let new_target = shell
.workspaces
.active(&next_output)
.unwrap()
.1
.focus_stack
.get(&seat)
.last()
.cloned()
.map(KeyboardFocusTarget::from);
std::mem::drop(shell);
let move_cursor = if let Some(under) = new_target {
let update_cursor = self.common.config.cosmic_conf.focus_follows_cursor;
Shell::set_focus(self, Some(&under), seat, None, update_cursor);
!update_cursor
} else {
true
};
if let Some(ptr) = seat.get_pointer() {
if move_cursor {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64().as_logical(),
serial,
time,
},
);
}
ptr.frame(self);
}
}
}
warn!("Skipping deprecated shortcut NextOutput");
}
Action::PreviousOutput => {
let current_output = seat.active_output();
let mut shell = self.common.shell.write().unwrap();
let prev_output = shell
.outputs()
.rev()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned();
if let Some(prev_output) = prev_output {
let idx = shell.workspaces.active_num(&prev_output).1;
let res = shell.activate(
&prev_output,
idx,
WorkspaceDelta::new_shortcut(),
&mut self.common.workspace_state.update(),
);
seat.set_active_output(&prev_output);
if let Ok(Some(new_pos)) = res {
let new_target = shell
.workspaces
.active(&prev_output)
.unwrap()
.1
.focus_stack
.get(&seat)
.last()
.cloned()
.map(KeyboardFocusTarget::from);
std::mem::drop(shell);
let move_cursor = if let Some(under) = new_target {
let update_cursor = self.common.config.cosmic_conf.focus_follows_cursor;
Shell::set_focus(self, Some(&under), seat, None, update_cursor);
!update_cursor
} else {
true
};
if let Some(ptr) = seat.get_pointer() {
if move_cursor {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64().as_logical(),
serial,
time,
},
);
}
ptr.frame(self);
}
}
}
warn!("Skipping deprecated shortcut PreviousOutput");
}
action @ Action::MoveToOutput(_) | action @ Action::SendToOutput(_) => {
@ -631,170 +609,15 @@ impl State {
ptr.frame(self);
}
}
} else if propagate {
std::mem::drop(shell);
match (
direction,
self.common.config.cosmic_conf.workspaces.workspace_layout,
) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => self
.handle_shortcut_action(
Action::MoveToPreviousWorkspace,
seat,
serial,
time,
pattern,
Some(direction),
false,
),
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => self
.handle_shortcut_action(
Action::MoveToNextWorkspace,
seat,
serial,
time,
pattern,
Some(direction),
false,
),
_ => {}
}
}
}
x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => {
let Some(focused_output) = seat.focused_output() else {
return;
};
let mut shell = self.common.shell.write().unwrap();
let next_output = shell
.outputs()
.skip_while(|o| *o != &focused_output)
.skip(1)
.next()
.cloned();
if let Some(next_output) = next_output {
let res = shell.move_current_window(
seat,
&focused_output,
(&next_output, None),
matches!(x, Action::MoveToNextOutput),
direction,
&mut self.common.workspace_state.update(),
);
if let Ok(Some((target, new_pos))) = res {
std::mem::drop(shell);
Shell::set_focus(
self,
Some(&target),
seat,
None,
matches!(x, Action::MoveToNextOutput),
);
if let Some(ptr) = seat.get_pointer() {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64().as_logical(),
serial,
time,
},
);
ptr.frame(self);
}
}
}
Action::MoveToNextOutput | Action::SendToNextOutput => {
warn!("Ignoring deprecated action Move/SendToNextOutput");
}
x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => {
let Some(focused_output) = seat.focused_output() else {
return;
};
let mut shell = self.common.shell.write().unwrap();
let prev_output = shell
.outputs()
.rev()
.skip_while(|o| *o != &focused_output)
.skip(1)
.next()
.cloned();
if let Some(prev_output) = prev_output {
let res = shell.move_current_window(
seat,
&focused_output,
(&prev_output, None),
matches!(x, Action::MoveToPreviousOutput),
direction,
&mut self.common.workspace_state.update(),
);
if let Ok(Some((target, new_pos))) = res {
std::mem::drop(shell);
Shell::set_focus(
self,
Some(&target),
seat,
None,
matches!(x, Action::MoveToPreviousOutput),
);
if let Some(ptr) = seat.get_pointer() {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64().as_logical(),
serial,
time,
},
);
ptr.frame(self);
}
}
}
}
Action::MigrateWorkspaceToNextOutput => {
let active_output = seat.active_output();
let (active, next_output) = {
let shell = self.common.shell.read().unwrap();
let output = shell
.outputs()
.skip_while(|o| *o != &active_output)
.skip(1)
.next()
.cloned();
(shell.active_space(&active_output).unwrap().handle, output)
};
if let Some(next_output) = next_output {
self.common
.migrate_workspace(&active_output, &next_output, &active);
}
}
Action::MigrateWorkspaceToPreviousOutput => {
let active_output = seat.active_output();
let (active, prev_output) = {
let shell = self.common.shell.read().unwrap();
let output = shell
.outputs()
.rev()
.skip_while(|o| *o != &active_output)
.skip(1)
.next()
.cloned();
(shell.active_space(&active_output).unwrap().handle, output)
};
if let Some(prev_output) = prev_output {
self.common
.migrate_workspace(&active_output, &prev_output, &active);
}
Action::MoveToPreviousOutput | Action::SendToPreviousOutput => {
warn!("Ignoring deprecated action Move/SendToPreviousOutput");
}
Action::MigrateWorkspaceToOutput(direction) => {
@ -814,6 +637,14 @@ impl State {
}
}
Action::MigrateWorkspaceToNextOutput => {
warn!("Ignoring deprecated action MigrateWorkspaceToNextOutput");
}
Action::MigrateWorkspaceToPreviousOutput => {
warn!("Ignoring deprecated action MigrateWorkspaceToPreviousOutput");
}
Action::Focus(focus) => {
let result = self.common.shell.read().unwrap().next_focus(focus, seat);
@ -828,8 +659,23 @@ impl State {
};
if let Some(direction) = dir {
let action = match (
direction,
self.common.config.cosmic_conf.workspaces.workspace_layout,
) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => {
Action::PreviousWorkspace
}
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => {
Action::NextWorkspace
}
_ => Action::SwitchOutput(direction),
};
self.handle_shortcut_action(
Action::SwitchOutput(direction),
action,
seat,
serial,
time,
@ -854,15 +700,32 @@ impl State {
.unwrap()
.move_current_element(direction, seat);
match res {
MoveResult::MoveFurther(_move_further) => self.handle_shortcut_action(
Action::MoveToOutput(direction),
seat,
serial,
time,
pattern,
Some(direction),
true,
),
MoveResult::MoveFurther(_move_further) => {
let action = match (
direction,
self.common.config.cosmic_conf.workspaces.workspace_layout,
) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => {
Action::MoveToPreviousWorkspace
}
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => {
Action::MoveToNextWorkspace
}
_ => Action::MoveToOutput(direction),
};
self.handle_shortcut_action(
action,
seat,
serial,
time,
pattern,
Some(direction),
true,
)
}
MoveResult::ShiftFocus(shift) => {
Shell::set_focus(self, Some(&shift), seat, None, true);
}
@ -1159,7 +1022,8 @@ fn to_next_workspace(
.workspaces
.active_num(&current_output)
.1
.saturating_add(1);
.checked_add(1)
.ok_or(InvalidWorkspaceIndex)?;
shell.activate(
&current_output,
@ -1184,7 +1048,8 @@ fn to_previous_workspace(
.workspaces
.active_num(&current_output)
.1
.saturating_sub(1);
.checked_sub(1)
.ok_or(InvalidWorkspaceIndex)?;
shell.activate(
&current_output,

View file

@ -250,7 +250,7 @@ impl State {
FilterResult::<()>::Forward
});
}
self.handle_action(action, &seat, serial, time, pattern, None, true)
self.handle_action(action, &seat, serial, time, pattern, None)
}
// If we want to track numlock state so it can be reused on the next boot...
@ -1639,7 +1639,6 @@ impl State {
time.overflowing_add(duration as u32).0,
key_pattern_clone.clone(),
None,
true,
);
calloop::timer::TimeoutAction::ToDuration(Duration::from_millis(25))
},