diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index ca37dc82..ff850946 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -399,6 +399,14 @@ impl ConfigClient { self.send(&ClientMessage::SeatFocusTiles { seat }); } + pub fn seat_focus_floats(&self, seat: Seat) { + self.send(&ClientMessage::SeatFocusFloats { seat }); + } + + pub fn seat_toggle_focus_float_tiled(&self, seat: Seat) { + self.send(&ClientMessage::SeatToggleFocusFloatTiled { seat }); + } + pub fn seat_focus(&self, seat: Seat, direction: Direction) { self.send(&ClientMessage::SeatFocus { seat, direction }); } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 6dad4216..9e0415b7 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -747,6 +747,12 @@ pub enum ClientMessage<'a> { SeatFocusTiles { seat: Seat, }, + SeatFocusFloats { + seat: Seat, + }, + SeatToggleFocusFloatTiled { + seat: Seat, + }, SetMiddleClickPasteEnabled { enabled: bool, }, diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index de8bd0ee..970299da 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -321,6 +321,19 @@ impl Seat { get!().seat_focus_tiles(self) } + /// Moves the keyboard focus to the topmost floating window. + pub fn focus_floats(self) { + get!().seat_focus_floats(self) + } + + /// Toggles keyboard focus between the floating and tiled layers. + /// + /// If focus is on the tiled or fullscreen layer, focus moves to the topmost float. + /// If focus is on the floating layer, focus moves to the tiled layer. + pub fn toggle_focus_float_tiled(self) { + get!().seat_toggle_focus_float_tiled(self) + } + /// Moves the keyboard focus of the seat in the specified direction. pub fn focus(self, direction: Direction) { get!().seat_focus(self, direction) diff --git a/src/config/handler.rs b/src/config/handler.rs index 1d07e1c6..4dd15861 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -2359,6 +2359,18 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_seat_focus_floats(&self, seat: Seat) -> Result<(), CphError> { + let seat = self.get_seat(seat)?; + seat.focus_floats(); + Ok(()) + } + + fn handle_seat_toggle_focus_float_tiled(&self, seat: Seat) -> Result<(), CphError> { + let seat = self.get_seat(seat)?; + seat.toggle_focus_float_tiled(); + Ok(()) + } + fn handle_set_middle_click_paste_enabled(&self, enabled: bool) { self.state.set_primary_selection_enabled(enabled); } @@ -3310,6 +3322,13 @@ impl ConfigProxyHandler { ClientMessage::SeatFocusTiles { seat } => { self.handle_seat_focus_tiles(seat).wrn("seat_focus_tiles")? } + ClientMessage::SeatFocusFloats { seat } => { + self.handle_seat_focus_floats(seat).wrn("seat_focus_floats")? + } + ClientMessage::SeatToggleFocusFloatTiled { seat } => { + self.handle_seat_toggle_focus_float_tiled(seat) + .wrn("seat_toggle_focus_float_tiled")? + } ClientMessage::SetMiddleClickPasteEnabled { enabled } => { self.handle_set_middle_click_paste_enabled(enabled) } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index ea94cd79..055591ac 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -1163,6 +1163,36 @@ impl WlSeatGlobal { ); } + pub fn toggle_focus_float_tiled(self: &Rc) { + let current = self.keyboard_node.get(); + match current.node_layer().layer() { + NodeLayer::Tiled | NodeLayer::Fullscreen => self.focus_floats(), + _ => self.focus_tiles(), + } + self.maybe_schedule_warp_mouse_to_focus(); + } + + pub fn focus_floats(self: &Rc) { + let current = self.keyboard_node.get(); + if current.node_layer().layer() == NodeLayer::Stacked { + return; + } + let Some(output) = current.node_output() else { + return; + }; + let Some(ws) = output.workspace.get() else { + return; + }; + if let Some(child) = ws + .stacked + .rev_iter() + .filter_map(|node| (*node).clone().node_into_float()) + .find_map(|float| float.child.get()) + { + child.node_do_focus(self, Direction::Unspecified); + } + } + pub fn focus_tiles(self: &Rc) { let current = self.keyboard_node.get(); if matches!( diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 90488e95..e25ccaa0 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -83,6 +83,7 @@ pub enum SimpleCommand { FocusHistory(Timeline), FocusLayerRel(LayerDirection), FocusTiles, + ToggleFocusFloatTiled, CreateMark, JumpToMark, PopMode(bool), diff --git a/toml-config/src/config/parsers/action.rs b/toml-config/src/config/parsers/action.rs index b7710317..abbb5ce3 100644 --- a/toml-config/src/config/parsers/action.rs +++ b/toml-config/src/config/parsers/action.rs @@ -158,6 +158,7 @@ impl ActionParser<'_> { "focus-below" => FocusLayerRel(LayerDirection::Below), "focus-above" => FocusLayerRel(LayerDirection::Above), "focus-tiles" => FocusTiles, + "toggle-float-focus" => ToggleFocusFloatTiled, "create-mark" => CreateMark, "jump-to-mark" => JumpToMark, "clear-modes" => PopMode(false), diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 171361d9..b46320a7 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -218,6 +218,10 @@ impl Action { let persistent = state.persistent.clone(); b.new(move || persistent.seat.focus_tiles()) } + SimpleCommand::ToggleFocusFloatTiled => { + let persistent = state.persistent.clone(); + b.new(move || persistent.seat.toggle_focus_float_tiled()) + } SimpleCommand::CreateMark => { let persistent = state.persistent.clone(); b.new(move || persistent.seat.create_mark(None))