diff --git a/src/input/mod.rs b/src/input/mod.rs index 8928eb98..971400e2 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -6,8 +6,8 @@ use crate::{ shell::{ focus::{target::PointerFocusTarget, FocusDirection}, grabs::{ResizeEdge, SeatMoveGrabState}, - layout::tiling::{Direction, FocusResult, MoveResult, SwapWindowGrab, TilingLayout}, - OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace, + layout::tiling::{Direction, MoveResult, SwapWindowGrab, TilingLayout}, + FocusResult, OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace, }, state::Common, utils::prelude::*, @@ -1305,19 +1305,14 @@ impl State { let current_output = seat.active_output(); let overview = self.common.shell.overview_mode().0; let workspace = self.common.shell.active_space_mut(¤t_output); - let focus_stack = workspace.focus_stack.get(seat); - let mut result = workspace.tiling_layer.next_focus( + let result = workspace.next_focus( focus, seat, - focus_stack.iter(), match overview { OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) => Some(desc), _ => None, }, ); - if workspace.get_fullscreen(¤t_output).is_some() { - result = FocusResult::None; - } match result { FocusResult::None => { @@ -1367,7 +1362,6 @@ impl State { } FocusResult::Handled => {} FocusResult::Some(target) => { - std::mem::drop(focus_stack); Common::set_focus(self, Some(&target), seat, None); } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 40b8a234..237c0aa2 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -5,13 +5,16 @@ use smithay::{ element::{AsRenderElements, RenderElement}, ImportAll, ImportMem, Renderer, }, - desktop::{layer_map_for_output, space::SpaceElement, Space, WindowSurfaceType}, + desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Space, WindowSurfaceType}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, utils::{Logical, Point, Rectangle, Size}, + wayland::seat::WaylandFocus, }; use std::collections::HashMap; +use crate::shell::focus::FocusDirection; +use crate::shell::FocusResult; use crate::{ backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage}, shell::{ @@ -25,7 +28,9 @@ use crate::{ }, state::State, utils::prelude::*, - wayland::protocols::toplevel_info::ToplevelInfoState, + wayland::{ + handlers::xdg_shell::popup::get_popup_toplevel, protocols::toplevel_info::ToplevelInfoState, + }, }; mod grabs; @@ -347,6 +352,77 @@ impl FloatingLayout { true } + pub fn next_focus<'a>( + &mut self, + direction: FocusDirection, + seat: &Seat, + _focus_stack: impl Iterator + 'a, + ) -> FocusResult { + let Some(target) = seat.get_keyboard().unwrap().current_focus() else { + return FocusResult::None + }; + + let Some(focused) = (match target { + KeyboardFocusTarget::Popup(popup) => { + let Some(toplevel_surface) = (match popup { + PopupKind::Xdg(xdg) => get_popup_toplevel(&xdg), + }) else { + return FocusResult::None + }; + self.space.elements().find(|elem| elem.wl_surface().as_ref() == Some(&toplevel_surface)) + }, + KeyboardFocusTarget::Element(elem) => self.space.elements().find(|e| *e == &elem), + _ => None, + }) else { + return FocusResult::None + }; + + if focused.handle_focus(direction, None) { + return FocusResult::Handled; + } + + let geometry = self.space.element_geometry(focused).unwrap(); + + let next = match direction { + FocusDirection::Up => self.space.elements().min_by_key(|other| { + let res = geometry.loc.y - self.space.element_geometry(other).unwrap().loc.y; + if res.is_positive() { + res + } else { + i32::MAX + } + }), + FocusDirection::Down => self.space.elements().max_by_key(|other| { + let res = geometry.loc.y - self.space.element_geometry(other).unwrap().loc.y; + if res.is_negative() { + res + } else { + i32::MIN + } + }), + FocusDirection::Left => self.space.elements().min_by_key(|other| { + let res = geometry.loc.x - self.space.element_geometry(other).unwrap().loc.x; + if res.is_positive() { + res + } else { + i32::MAX + } + }), + FocusDirection::Right => self.space.elements().max_by_key(|other| { + let res = geometry.loc.x - self.space.element_geometry(other).unwrap().loc.x; + if res.is_negative() { + res + } else { + i32::MIN + } + }), + _ => return FocusResult::None, + }; + + next.map(|elem| FocusResult::Some(KeyboardFocusTarget::Element(elem.clone()))) + .unwrap_or(FocusResult::None) + } + pub fn mapped(&self) -> impl Iterator { self.space.elements().rev() } diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 01bb78f1..5178b511 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -22,7 +22,8 @@ use crate::{ }, grabs::ResizeEdge, layout::Orientation, - CosmicSurface, OutputNotMapped, OverviewMode, ResizeDirection, ResizeMode, Trigger, + CosmicSurface, FocusResult, OutputNotMapped, OverviewMode, ResizeDirection, ResizeMode, + Trigger, }, utils::{prelude::*, tween::EaseRectangle}, wayland::{ @@ -131,13 +132,6 @@ impl std::ops::Not for Direction { } } -#[derive(Debug, Clone, PartialEq)] -pub enum FocusResult { - None, - Handled, - Some(KeyboardFocusTarget), -} - #[derive(Debug, Clone, PartialEq)] pub enum MoveResult { Done, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 322026fa..26dfb098 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -65,7 +65,7 @@ use super::{ }, focus::{ target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup}, - FocusStack, FocusStackMut, + FocusDirection, FocusStack, FocusStackMut, }, grabs::{ResizeEdge, ResizeGrab}, layout::tiling::{Data, NodeDesc}, @@ -133,6 +133,25 @@ pub enum ManagedState { Floating, } +#[derive(Debug, Clone, PartialEq)] +pub enum FocusResult { + None, + Handled, + Some(KeyboardFocusTarget), +} + +impl FocusResult { + pub fn or_else(self, f: F) -> FocusResult + where + F: FnOnce() -> FocusResult, + { + match self { + FocusResult::None => f(), + x => x, + } + } +} + impl Workspace { pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace { Workspace { @@ -795,6 +814,25 @@ impl Workspace { } } + pub fn next_focus<'a>( + &mut self, + direction: FocusDirection, + seat: &Seat, + swap_desc: Option, + ) -> FocusResult { + if self.fullscreen.contains_key(&seat.active_output()) { + return FocusResult::None; + } + + let focus_stack = self.focus_stack.get(seat); + self.floating_layer + .next_focus(direction, seat, focus_stack.iter()) + .or_else(|| { + self.tiling_layer + .next_focus(direction, seat, focus_stack.iter(), swap_desc) + }) + } + pub fn render_output<'a, R>( &self, renderer: &mut R,