shell: Have Move-shortcut for floating layer and fullscreen windows

This commit is contained in:
Victoria Brekenfeld 2023-09-20 18:57:58 +02:00
parent 3d10ca6105
commit 4709a1d684
11 changed files with 207 additions and 69 deletions

View file

@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::shell::{ use crate::shell::{focus::FocusDirection, grabs::ResizeEdge, Direction, ResizeDirection};
focus::FocusDirection, grabs::ResizeEdge, layout::tiling::Direction, ResizeDirection,
};
use serde::Deserialize; use serde::Deserialize;
use smithay::{ use smithay::{
backend::input::KeyState, backend::input::KeyState,

View file

@ -6,8 +6,9 @@ use crate::{
shell::{ shell::{
focus::{target::PointerFocusTarget, FocusDirection}, focus::{target::PointerFocusTarget, FocusDirection},
grabs::{ResizeEdge, SeatMoveGrabState}, grabs::{ResizeEdge, SeatMoveGrabState},
layout::tiling::{Direction, MoveResult, SwapWindowGrab, TilingLayout}, layout::tiling::{SwapWindowGrab, TilingLayout},
FocusResult, OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace, Direction, FocusResult, MoveResult, OverviewMode, ResizeDirection, ResizeMode, Trigger,
Workspace,
}, },
state::Common, state::Common,
utils::prelude::*, utils::prelude::*,
@ -1373,7 +1374,7 @@ impl State {
return; // TODO, is this what we want? How do we indicate the switch? return; // TODO, is this what we want? How do we indicate the switch?
} }
match workspace.tiling_layer.move_current_node(direction, seat) { match workspace.move_current_element(direction, seat) {
MoveResult::MoveFurther(_move_further) => { MoveResult::MoveFurther(_move_further) => {
match (direction, self.common.config.static_conf.workspace_layout) { match (direction, self.common.config.static_conf.workspace_layout) {
(Direction::Left, WorkspaceLayout::Horizontal) (Direction::Left, WorkspaceLayout::Horizontal)
@ -1417,7 +1418,7 @@ impl State {
MoveResult::ShiftFocus(shift) => { MoveResult::ShiftFocus(shift) => {
Common::set_focus(self, Some(&shift), seat, None); Common::set_focus(self, Some(&shift), seat, None);
} }
MoveResult::Done => { _ => {
if let Some(focused_window) = workspace.focus_stack.get(seat).last() { if let Some(focused_window) = workspace.focus_stack.get(seat).last() {
if workspace.is_tiled(focused_window) { if workspace.is_tiled(focused_window) {
self.common.shell.set_overview_mode( self.common.shell.set_overview_mode(

View file

@ -73,10 +73,8 @@ use tracing::debug;
use super::{ use super::{
focus::FocusDirection, focus::FocusDirection,
layout::{ layout::{floating::ResizeState, tiling::NodeDesc},
floating::ResizeState, Direction,
tiling::{Direction, NodeDesc},
},
}; };
space_elements! { space_elements! {

View file

@ -1,10 +1,7 @@
use super::{CosmicMapped, CosmicSurface, CosmicWindow}; use super::{CosmicMapped, CosmicSurface, CosmicWindow};
use crate::{ use crate::{
shell::{ shell::{
focus::FocusDirection, focus::FocusDirection, grabs::MoveGrab, layout::tiling::NodeDesc, Direction, Shell, Trigger,
grabs::MoveGrab,
layout::tiling::{Direction, NodeDesc},
Shell, Trigger,
}, },
state::State, state::State,
utils::iced::{IcedElement, Program}, utils::iced::{IcedElement, Program},

View file

@ -97,12 +97,22 @@ impl Shell {
serial: Option<Serial>, serial: Option<Serial>,
) { ) {
// update FocusStack and notify layouts about new focus (if any window) // update FocusStack and notify layouts about new focus (if any window)
if let Some(KeyboardFocusTarget::Element(mapped)) = target { let element = match target {
if let Some(workspace) = state.common.shell.space_for_mut(mapped) { Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
Some(KeyboardFocusTarget::Fullscreen(window)) => state
.common
.shell
.element_for_surface(&window.surface())
.cloned(),
_ => None,
};
if let Some(mapped) = element {
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
let mut focus_stack = workspace.focus_stack.get_mut(active_seat); let mut focus_stack = workspace.focus_stack.get_mut(active_seat);
if Some(mapped) != focus_stack.last() { if Some(&mapped) != focus_stack.last() {
trace!(?mapped, "Focusing window."); trace!(?mapped, "Focusing window.");
focus_stack.append(mapped); focus_stack.append(&mapped);
// also remove popup grabs, if we are switching focus // also remove popup grabs, if we are switching focus
if let Some(mut popup_grab) = active_seat if let Some(mut popup_grab) = active_seat
.user_data() .user_data()

View file

@ -13,18 +13,18 @@ use smithay::{
}; };
use std::collections::HashMap; use std::collections::HashMap;
use crate::shell::focus::FocusDirection;
use crate::shell::FocusResult;
use crate::{ use crate::{
backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage}, backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage},
shell::{ shell::{
element::{ element::{
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, resize_indicator::ResizeIndicator,
window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement, stack::{CosmicStackRenderElement, MoveResult as StackMoveResult},
window::CosmicWindowRenderElement,
CosmicMapped, CosmicMappedRenderElement, CosmicWindow,
}, },
focus::target::KeyboardFocusTarget, focus::{target::KeyboardFocusTarget, FocusDirection},
grabs::ResizeEdge, grabs::ResizeEdge,
CosmicSurface, ResizeDirection, ResizeMode, CosmicSurface, Direction, FocusResult, MoveResult, ResizeDirection, ResizeMode,
}, },
state::State, state::State,
utils::prelude::*, utils::prelude::*,
@ -423,6 +423,62 @@ impl FloatingLayout {
.unwrap_or(FocusResult::None) .unwrap_or(FocusResult::None)
} }
pub fn move_current_element<'a>(
&mut self,
direction: Direction,
seat: &Seat<State>,
) -> MoveResult {
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return MoveResult::None
};
let Some(focused) = (match target {
KeyboardFocusTarget::Popup(popup) => {
let Some(toplevel_surface) = (match popup {
PopupKind::Xdg(xdg) => get_popup_toplevel(&xdg),
}) else {
return MoveResult::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 MoveResult::None
};
match focused.handle_move(direction) {
StackMoveResult::Handled => return MoveResult::Done,
StackMoveResult::MoveOut(surface, loop_handle) => {
let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle).into();
let output = seat.active_output();
let pos = self.space.element_geometry(focused).unwrap().loc
+ match direction {
Direction::Up => Point::from((5, -10)),
Direction::Down => Point::from((5, 10)),
Direction::Left => Point::from((-10, 5)),
Direction::Right => Point::from((10, 5)),
};
let position = self
.space
.output_geometry(&output)
.unwrap()
.overlaps({
let mut geo = mapped.geometry();
geo.loc += pos;
geo
})
.then_some(pos);
self.map_internal(mapped.clone(), &output, position);
return MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped));
}
StackMoveResult::Default => {}
};
MoveResult::MoveFurther(KeyboardFocusTarget::Element(focused.clone()))
}
pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> { pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
self.space.elements().rev() self.space.elements().rev()
} }

View file

@ -22,8 +22,8 @@ use crate::{
}, },
grabs::ResizeEdge, grabs::ResizeEdge,
layout::Orientation, layout::Orientation,
CosmicSurface, FocusResult, OutputNotMapped, OverviewMode, ResizeDirection, ResizeMode, CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode,
Trigger, ResizeDirection, ResizeMode, Trigger,
}, },
utils::{prelude::*, tween::EaseRectangle}, utils::{prelude::*, tween::EaseRectangle},
wayland::{ wayland::{
@ -112,33 +112,6 @@ pub struct NodeDesc {
pub stack_window: Option<CosmicSurface>, pub stack_window: Option<CosmicSurface>,
} }
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Left,
Right,
Up,
Down,
}
impl std::ops::Not for Direction {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MoveResult {
Done,
MoveFurther(KeyboardFocusTarget),
ShiftFocus(KeyboardFocusTarget),
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum TargetZone { enum TargetZone {
Initial, Initial,
@ -1454,12 +1427,12 @@ impl TilingLayout {
let mut tree = queue.trees.back().unwrap().0.copy_clone(); let mut tree = queue.trees.back().unwrap().0.copy_clone();
let Some(target) = seat.get_keyboard().unwrap().current_focus() else { let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return MoveResult::Done; return MoveResult::None;
}; };
let Some((node_id, data)) = let Some((node_id, data)) =
TilingLayout::currently_focused_node(&mut tree, &seat.active_output(), target) TilingLayout::currently_focused_node(&mut tree, &seat.active_output(), target)
else { else {
return MoveResult::Done; return MoveResult::None;
}; };
// stacks may handle movement internally // stacks may handle movement internally

View file

@ -66,7 +66,7 @@ use self::{
grabs::ResizeEdge, grabs::ResizeEdge,
layout::{ layout::{
floating::{FloatingLayout, ResizeState}, floating::{FloatingLayout, ResizeState},
tiling::{Direction, NodeDesc, TilingLayout}, tiling::{NodeDesc, TilingLayout},
}, },
}; };
@ -1500,13 +1500,47 @@ impl Shell {
.get_mut(to_idx, to_output) .get_mut(to_idx, to_output)
.unwrap(); // checked above .unwrap(); // checked above
let focus_stack = to_workspace.focus_stack.get(&seat); let focus_stack = to_workspace.focus_stack.get(&seat);
if window_state == ManagedState::Floating { if window_state.layer == ManagedLayer::Floating {
to_workspace.floating_layer.map(mapped.clone(), &seat, None); to_workspace.floating_layer.map(mapped.clone(), &seat, None);
} else { } else {
to_workspace to_workspace
.tiling_layer .tiling_layer
.map(mapped.clone(), &seat, focus_stack.iter(), direction); .map(mapped.clone(), &seat, focus_stack.iter(), direction);
} }
let focus_target = match window_state.was_fullscreen {
Some(true) => {
to_workspace.fullscreen_request(
&mapped.active_window(),
&to_output,
state.common.event_loop_handle.clone(),
);
KeyboardFocusTarget::from(
to_workspace
.fullscreen
.get(to_output)
.unwrap()
.window
.clone(),
)
}
Some(false) => {
to_workspace.maximize_request(
&mapped.active_window(),
&to_output,
state.common.event_loop_handle.clone(),
);
KeyboardFocusTarget::from(
to_workspace
.fullscreen
.get(to_output)
.unwrap()
.window
.clone(),
)
}
None => KeyboardFocusTarget::from(mapped.clone()),
};
for (toplevel, _) in mapped.windows() { for (toplevel, _) in mapped.windows() {
if from_output != to_output { if from_output != to_output {
state state
@ -1531,7 +1565,7 @@ impl Shell {
} }
if follow { if follow {
Common::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); Common::set_focus(state, Some(&focus_target), &seat, None);
} }
Ok(new_pos) Ok(new_pos)
} }

View file

@ -128,11 +128,36 @@ impl IsAlive for FullscreenSurface {
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>); pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ManagedState { pub struct ManagedState {
pub layer: ManagedLayer,
pub was_fullscreen: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ManagedLayer {
Tiling, Tiling,
Floating, Floating,
} }
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Left,
Right,
Up,
Down,
}
impl std::ops::Not for Direction {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum FocusResult { pub enum FocusResult {
None, None,
@ -152,6 +177,26 @@ impl FocusResult {
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub enum MoveResult {
None,
Done,
MoveFurther(KeyboardFocusTarget),
ShiftFocus(KeyboardFocusTarget),
}
impl MoveResult {
pub fn or_else<F>(self, f: F) -> MoveResult
where
F: FnOnce() -> MoveResult,
{
match self {
MoveResult::None => f(),
x => x,
}
}
}
impl Workspace { impl Workspace {
pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace { pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace {
Workspace { Workspace {
@ -279,7 +324,9 @@ impl Workspace {
assert!(was_floating != was_tiling); assert!(was_floating != was_tiling);
} }
if mapped.is_maximized(true) || mapped.is_fullscreen(true) { let was_maximized = mapped.is_maximized(true);
let was_fullscreen = mapped.is_fullscreen(true);
if was_maximized || was_fullscreen {
self.unmaximize_request(&mapped.active_window()); self.unmaximize_request(&mapped.active_window());
} }
@ -288,9 +335,15 @@ impl Workspace {
.values_mut() .values_mut()
.for_each(|set| set.retain(|m| m != mapped)); .for_each(|set| set.retain(|m| m != mapped));
if was_floating { if was_floating {
Some(ManagedState::Floating) Some(ManagedState {
layer: ManagedLayer::Floating,
was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen),
})
} else if was_tiling { } else if was_tiling {
Some(ManagedState::Tiling) Some(ManagedState {
layer: ManagedLayer::Tiling,
was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen),
})
} else { } else {
None None
} }
@ -389,14 +442,14 @@ impl Workspace {
window.set_maximized(true); window.set_maximized(true);
self.set_fullscreen(window, output, false, evlh) self.set_fullscreen(window, output, false, evlh)
} }
pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> { pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
if self if self
.fullscreen .fullscreen
.values() .values()
.any(|f| &f.window.surface() == window) .any(|f| &f.window.surface() == window)
{ {
self.unfullscreen_request(window); self.unfullscreen_request(window)
self.floating_layer.unmaximize_request(window)
} else { } else {
None None
} }
@ -476,7 +529,7 @@ impl Workspace {
); );
} }
pub fn unfullscreen_request(&mut self, window: &CosmicSurface) { pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
if let Some((output, f)) = self if let Some((output, f)) = self
.fullscreen .fullscreen
.iter_mut() .iter_mut()
@ -485,7 +538,7 @@ impl Workspace {
f.window.output_leave(output); f.window.output_leave(output);
window.set_maximized(false); window.set_maximized(false);
window.set_fullscreen(false); window.set_fullscreen(false);
self.floating_layer.unmaximize_request(window); let result = self.floating_layer.unmaximize_request(window);
self.floating_layer.refresh(); self.floating_layer.refresh();
self.tiling_layer.recalculate(output); self.tiling_layer.recalculate(output);
self.tiling_layer.refresh(); self.tiling_layer.refresh();
@ -520,6 +573,10 @@ impl Workspace {
old_signal.store(true, Ordering::SeqCst); old_signal.store(true, Ordering::SeqCst);
} }
} }
result
} else {
None
} }
} }
@ -833,6 +890,20 @@ impl Workspace {
}) })
} }
pub fn move_current_element<'a>(
&mut self,
direction: Direction,
seat: &Seat<State>,
) -> MoveResult {
if let Some(f) = self.fullscreen.get(&seat.active_output()) {
MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.window.clone()))
} else {
self.floating_layer
.move_current_element(direction, seat)
.or_else(|| self.tiling_layer.move_current_node(direction, seat))
}
}
pub fn render_output<'a, R>( pub fn render_output<'a, R>(
&self, &self,
renderer: &mut R, renderer: &mut R,

View file

@ -227,7 +227,7 @@ impl XdgShellHandler for State {
.windows() .windows()
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
.unwrap(); .unwrap();
workspace.unfullscreen_request(&window) workspace.unfullscreen_request(&window);
} }
} }
} }

View file

@ -460,7 +460,7 @@ impl XwmHandler for Data {
{ {
if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) { if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) {
let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap();
workspace.unfullscreen_request(&window) workspace.unfullscreen_request(&window);
} }
} }
} }