diff --git a/config.ron b/config.ron index 3e6573f0..5fdba9b0 100644 --- a/config.ron +++ b/config.ron @@ -25,10 +25,14 @@ (modifiers: [Super, Shift], key: "9"): MoveToWorkspace(9), (modifiers: [Super, Shift], key: "0"): MoveToLastWorkspace, - (modifiers: [Super, Ctrl, Alt], key: "Down"): MoveToNextOutput, - (modifiers: [Super, Ctrl, Alt], key: "Up"): MoveToPreviousOutput, - (modifiers: [Super, Ctrl, Alt], key: "j"): MoveToNextOutput, - (modifiers: [Super, Ctrl, Alt], key: "k"): MoveToPreviousOutput, + (modifiers: [Super, Ctrl, Alt], key: "Left"): MoveToOutput(Left), + (modifiers: [Super, Ctrl, Alt], key: "Down"): MoveToOutput(Down), + (modifiers: [Super, Ctrl, Alt], key: "Up"): MoveToOutput(Up), + (modifiers: [Super, Ctrl, Alt], key: "Right"): MoveToOutput(Right), + (modifiers: [Super, Ctrl, Alt], key: "h"): MoveToOutput(Left), + (modifiers: [Super, Ctrl, Alt], key: "k"): MoveToOutput(Down), + (modifiers: [Super, Ctrl, Alt], key: "j"): MoveToOutput(Up), + (modifiers: [Super, Ctrl, Alt], key: "l"): MoveToOutput(Right), (modifiers: [Super], key: "Period"): NextOutput, (modifiers: [Super], key: "Comma"): PreviousOutput, diff --git a/src/config/key_bindings.rs b/src/config/key_bindings.rs index 227bbef8..2580771c 100644 --- a/src/config/key_bindings.rs +++ b/src/config/key_bindings.rs @@ -101,6 +101,16 @@ impl KeyPattern { key, } } + + pub fn inferred_direction(&self) -> Option { + match self.key? { + Keysym::Left | Keysym::h | Keysym::H => Some(Direction::Left), + Keysym::Down | Keysym::j | Keysym::J => Some(Direction::Down), + Keysym::Up | Keysym::k | Keysym::K => Some(Direction::Up), + Keysym::Right | Keysym::l | Keysym::L => Some(Direction::Right), + _ => None, + } + } } impl ToString for KeyPattern { @@ -153,6 +163,9 @@ pub enum Action { MoveToPreviousOutput, SendToNextOutput, SendToPreviousOutput, + SwitchOutput(Direction), + MoveToOutput(Direction), + SendToOutput(Direction), Focus(FocusDirection), Move(Direction), @@ -195,19 +208,23 @@ pub fn add_default_bindings( key_bindings: &mut HashMap, workspace_layout: WorkspaceLayout, ) { - let (workspace_previous, workspace_next, output_previous, output_next) = match workspace_layout - { + let ( + workspace_previous, + workspace_next, + (output_previous, output_previous_dir), + (output_next, output_next_dir), + ) = match workspace_layout { WorkspaceLayout::Horizontal => ( [Keysym::Left, Keysym::h], [Keysym::Right, Keysym::l], - [Keysym::Up, Keysym::k], - [Keysym::Down, Keysym::j], + ([Keysym::Up, Keysym::k], Direction::Up), + ([Keysym::Down, Keysym::j], Direction::Down), ), WorkspaceLayout::Vertical => ( [Keysym::Up, Keysym::k], [Keysym::Down, Keysym::j], - [Keysym::Left, Keysym::h], - [Keysym::Right, Keysym::l], + ([Keysym::Left, Keysym::h], Direction::Left), + ([Keysym::Right, Keysym::l], Direction::Right), ), }; @@ -262,7 +279,7 @@ pub fn add_default_bindings( ..Default::default() }, output_previous.iter().copied(), - Action::PreviousOutput, + Action::SwitchOutput(output_previous_dir), ); insert_binding( key_bindings, @@ -272,7 +289,7 @@ pub fn add_default_bindings( ..Default::default() }, output_next.iter().copied(), - Action::NextOutput, + Action::SwitchOutput(output_next_dir), ); insert_binding( key_bindings, @@ -283,7 +300,7 @@ pub fn add_default_bindings( ..Default::default() }, output_previous.iter().copied(), - Action::MoveToPreviousOutput, + Action::MoveToOutput(output_previous_dir), ); insert_binding( key_bindings, @@ -294,6 +311,6 @@ pub fn add_default_bindings( ..Default::default() }, output_next.iter().copied(), - Action::MoveToNextOutput, + Action::MoveToOutput(output_next_dir), ); } diff --git a/src/input/mod.rs b/src/input/mod.rs index 700c5fff..61d19e5b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1221,7 +1221,16 @@ impl State { .activate(¤t_output, workspace) .is_err() { - self.handle_action(Action::NextOutput, seat, serial, time, pattern, direction); + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::SwitchOutput(inferred), + seat, + serial, + time, + pattern, + direction, + ) + }; } } Action::PreviousWorkspace => { @@ -1239,14 +1248,16 @@ impl State { .activate(¤t_output, workspace) .is_err() { - self.handle_action( - Action::PreviousOutput, - seat, - serial, - time, - pattern, - direction, - ); + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::SwitchOutput(inferred), + seat, + serial, + time, + pattern, + direction, + ) + }; } } Action::LastWorkspace => { @@ -1295,18 +1306,20 @@ impl State { ) .is_err() { - self.handle_action( - if matches!(x, Action::MoveToNextWorkspace) { - Action::MoveToNextOutput - } else { - Action::SendToNextOutput - }, - seat, - serial, - time, - pattern, - direction, - ) + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + if matches!(x, Action::MoveToNextWorkspace) { + Action::MoveToOutput(inferred) + } else { + Action::SendToOutput(inferred) + }, + seat, + serial, + time, + pattern, + direction, + ) + } } } x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => { @@ -1329,18 +1342,20 @@ impl State { ) .is_err() { - self.handle_action( - if matches!(x, Action::MoveToNextWorkspace) { - Action::MoveToPreviousOutput - } else { - Action::SendToPreviousOutput - }, - seat, - serial, - time, - pattern, - direction, - ) + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + if matches!(x, Action::MoveToPreviousWorkspace) { + Action::MoveToOutput(inferred) + } else { + Action::SendToOutput(inferred) + }, + seat, + serial, + time, + pattern, + direction, + ) + } } } x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => { @@ -1360,6 +1375,39 @@ impl State { None, ); } + Action::SwitchOutput(direction) => { + let current_output = seat.active_output(); + let next_output = self + .common + .shell + .next_output(¤t_output, direction) + .cloned(); + + if let Some(next_output) = next_output { + let idx = self.common.shell.workspaces.active_num(&next_output).1; + match self.common.shell.activate(&next_output, idx) { + Ok(Some(new_pos)) => { + seat.set_active_output(&next_output); + if let Some(ptr) = seat.get_pointer() { + ptr.motion( + self, + None, + &MotionEvent { + location: new_pos.to_f64().as_logical(), + serial, + time, + }, + ); + ptr.frame(self); + } + } + Ok(None) => { + seat.set_active_output(&next_output); + } + _ => {} + } + } + } Action::NextOutput => { let current_output = seat.active_output(); let next_output = self @@ -1431,6 +1479,45 @@ impl State { } } } + action @ Action::MoveToOutput(_) | action @ Action::SendToOutput(_) => { + let is_move_action = matches!(action, Action::MoveToOutput(_)); + let direction = match action { + Action::MoveToOutput(dir) => dir, + Action::SendToOutput(dir) => dir, + _ => unreachable!(), + }; + + let current_output = seat.active_output(); + let next_output = self + .common + .shell + .next_output(¤t_output, direction) + .cloned(); + + if let Some(next_output) = next_output { + if let Ok(Some(new_pos)) = Shell::move_current_window( + self, + seat, + ¤t_output, + (&next_output, None), + is_move_action, + Some(direction), + ) { + if let Some(ptr) = seat.get_pointer() { + ptr.motion( + self, + None, + &MotionEvent { + location: new_pos.to_f64().as_logical(), + serial, + time, + }, + ); + ptr.frame(self); + } + } + } + } x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => { let current_output = seat.active_output(); let next_output = self @@ -1537,25 +1624,31 @@ impl State { direction, ), (FocusDirection::Left, WorkspaceLayout::Vertical) - | (FocusDirection::Up, WorkspaceLayout::Horizontal) => self - .handle_action( - Action::PreviousOutput, - seat, - serial, - time, - pattern, - direction, - ), + | (FocusDirection::Up, WorkspaceLayout::Horizontal) => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::SwitchOutput(inferred), + seat, + serial, + time, + pattern, + direction, + ) + } + } (FocusDirection::Right, WorkspaceLayout::Vertical) - | (FocusDirection::Down, WorkspaceLayout::Horizontal) => self - .handle_action( - Action::NextOutput, - seat, - serial, - time, - pattern, - direction, - ), + | (FocusDirection::Down, WorkspaceLayout::Horizontal) => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::SwitchOutput(inferred), + seat, + serial, + time, + pattern, + direction, + ) + } + } _ => {} } } @@ -1591,23 +1684,31 @@ impl State { Some(direction), ), (Direction::Left, WorkspaceLayout::Vertical) - | (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action( - Action::MoveToPreviousOutput, - seat, - serial, - time, - pattern, - Some(direction), - ), + | (Direction::Up, WorkspaceLayout::Horizontal) => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::MoveToOutput(inferred), + seat, + serial, + time, + pattern, + Some(direction), + ) + } + } (Direction::Right, WorkspaceLayout::Vertical) - | (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action( - Action::MoveToNextOutput, - seat, - serial, - time, - pattern, - Some(direction), - ), + | (Direction::Down, WorkspaceLayout::Horizontal) => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + Action::MoveToOutput(inferred), + seat, + serial, + time, + pattern, + Some(direction), + ) + } + } } } MoveResult::ShiftFocus(shift) => { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b2e812a6..7d27e5ec 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1146,6 +1146,41 @@ impl Shell { ) } + pub fn next_output(&self, current_output: &Output, direction: Direction) -> Option<&Output> { + let current_output_geo = current_output.geometry(); + self.outputs() + .filter(|o| *o != current_output) + .filter(|o| { + let geo = o.geometry(); + match direction { + Direction::Left | Direction::Right => { + !(geo.loc.y + geo.size.h < current_output_geo.loc.y + || geo.loc.y > current_output_geo.loc.y + current_output_geo.size.h) + } + Direction::Up | Direction::Down => { + !(geo.loc.x + geo.size.w < current_output_geo.loc.x + || geo.loc.x > current_output_geo.loc.x + current_output_geo.size.w) + } + } + }) + .filter_map(|o| { + let origin = o.geometry().loc; + let res = match direction { + Direction::Up => current_output_geo.loc.y - origin.y, + Direction::Down => origin.y - current_output_geo.loc.y, + Direction::Left => current_output_geo.loc.x - origin.x, + Direction::Right => origin.x - current_output_geo.loc.x, + }; + if res > 0 { + Some((o, res)) + } else { + None + } + }) + .min_by_key(|(_, res)| *res) + .map(|(o, _)| o) + } + pub fn global_space(&self) -> Rectangle { self.outputs() .fold(