From e97db0dc0a977dc62746a9c3531b336fce71579b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 9 Nov 2023 19:00:25 +0100 Subject: [PATCH] wip --- src/shell/element/mod.rs | 10 +- src/shell/layout/floating/mod.rs | 329 ++++++++++++++++++++++++++----- 2 files changed, 288 insertions(+), 51 deletions(-) diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4bf43b2cd..d21b3263c 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -73,7 +73,10 @@ use tracing::debug; use super::{ focus::FocusDirection, - layout::{floating::ResizeState, tiling::NodeDesc}, + layout::{ + floating::{ResizeState, TiledCorners}, + tiling::NodeDesc, + }, Direction, ManagedLayer, }; @@ -104,6 +107,7 @@ pub struct CosmicMapped { pub(super) resize_state: Arc>>, pub last_geometry: Arc>>>, pub moved_since_mapped: Arc, + pub floating_tiled: Arc>>, #[cfg(feature = "debug")] debug: Arc>>, @@ -119,6 +123,7 @@ impl fmt::Debug for CosmicMapped { .field("resize_state", &self.resize_state) .field("last_geometry", &self.last_geometry) .field("moved_since_mapped", &self.moved_since_mapped) + .field("floating_tiled", &self.floating_tiled) .finish() } } @@ -1105,6 +1110,7 @@ impl From for CosmicMapped { resize_state: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), moved_since_mapped: Arc::new(AtomicBool::new(false)), + floating_tiled: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } @@ -1121,7 +1127,7 @@ impl From for CosmicMapped { resize_state: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), moved_since_mapped: Arc::new(AtomicBool::new(false)), - #[cfg(feature = "debug")] + floating_tiled: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index adc6fdf9b..378086076 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -10,7 +10,7 @@ use smithay::{ desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Space, WindowSurfaceType}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - utils::{Logical, Point, Rectangle, Size}, + utils::{IsAlive, Logical, Point, Rectangle, Size}, wayland::seat::WaylandFocus, }; @@ -41,6 +41,77 @@ pub struct FloatingLayout { spawn_order: Vec, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TiledCorners { + Top, + TopRight, + Right, + BottomRight, + Bottom, + BottomLeft, + Left, + TopLeft, +} + +impl TiledCorners { + pub fn relative_geometry( + &self, + output_geometry: Rectangle, + ) -> Rectangle { + let (loc, size) = match self { + TiledCorners::Bottom => ( + Point::from(( + output_geometry.loc.x, + output_geometry.loc.y + (output_geometry.size.h / 2), + )), + Size::from((output_geometry.size.w, output_geometry.size.h / 2)), + ), + TiledCorners::BottomLeft => ( + Point::from(( + output_geometry.loc.x, + output_geometry.loc.y + (output_geometry.size.h / 2), + )), + Size::from((output_geometry.size.w / 2, output_geometry.size.h / 2)), + ), + TiledCorners::BottomRight => ( + Point::from(( + output_geometry.loc.x + (output_geometry.size.w / 2), + output_geometry.loc.y + (output_geometry.size.h / 2), + )), + Size::from((output_geometry.size.w / 2, output_geometry.size.h / 2)), + ), + TiledCorners::Left => ( + output_geometry.loc, + Size::from((output_geometry.size.w / 2, output_geometry.size.h)), + ), + TiledCorners::Top => ( + output_geometry.loc, + Size::from((output_geometry.size.w, output_geometry.size.h / 2)), + ), + TiledCorners::TopLeft => ( + output_geometry.loc, + Size::from((output_geometry.size.w / 2, output_geometry.size.h / 2)), + ), + TiledCorners::TopRight => ( + Point::from(( + output_geometry.loc.x + (output_geometry.size.w / 2), + output_geometry.loc.y, + )), + Size::from((output_geometry.size.w / 2, output_geometry.size.h / 2)), + ), + TiledCorners::Right => ( + Point::from(( + output_geometry.loc.x + (output_geometry.size.w / 2), + output_geometry.loc.y, + )), + Size::from((output_geometry.size.w / 2, output_geometry.size.h)), + ), + }; + + Rectangle::from_loc_and_size(loc, size).as_local() + } +} + impl FloatingLayout { pub fn new(output: &Output) -> FloatingLayout { let mut layout = Self::default(); @@ -53,9 +124,41 @@ impl FloatingLayout { self.space.unmap_output(&old_output); self.space.map_output(output, (0, 0)); - /* - TODO: rescale all positions? (evem rescale windows?) - */ + let old_output_geometry = { + let layers = layer_map_for_output(&old_output); + layers.non_exclusive_zone() + }; + let output_geometry = { + let layers = layer_map_for_output(&output); + layers.non_exclusive_zone() + }; + + for mapped in self + .space + .elements() + .cloned() + .collect::>() + .into_iter() + { + let tiled_state = mapped.floating_tiled.lock().unwrap().clone(); + if let Some(tiled_state) = tiled_state { + let geometry = tiled_state.relative_geometry(output_geometry); + self.map_internal(mapped, Some(geometry.loc), Some(geometry.size.as_logical())); + } else { + let geometry = self.space.element_geometry(&mapped).unwrap(); + let new_loc = ( + geometry.loc.x.saturating_sub(old_output_geometry.loc.x) + / old_output_geometry.size.w + * output_geometry.size.w + + output_geometry.loc.x, + geometry.loc.y.saturating_sub(old_output_geometry.loc.y) + / old_output_geometry.size.h + * output_geometry.size.h + + output_geometry.loc.y, + ); + self.map_internal(mapped, Some(Point::from(new_loc)), None); + } + } self.refresh(); } @@ -83,7 +186,7 @@ impl FloatingLayout { mapped.configure(); if let Some(pos) = self.spawn_order.iter().position(|m| m == &mapped) { - self.spawn_order.remove(pos); + self.spawn_order.truncate(pos); } mapped.moved_since_mapped.store(true, Ordering::SeqCst); @@ -105,6 +208,7 @@ impl FloatingLayout { let output_geometry = layers.non_exclusive_zone(); mapped.set_bounds(output_geometry.size); let last_geometry = mapped.last_geometry.lock().unwrap().clone(); + let min_size = mapped.min_size().unwrap_or((320, 240).into()); if let Some(size) = size .map(SizeExt::as_local) @@ -112,7 +216,6 @@ impl FloatingLayout { { win_geo.size = size; } else { - let min_size = mapped.min_size().unwrap_or((320, 240).into()); let max_size = mapped.max_size().unwrap_or( ( min_size.w.max(output_geometry.size.w / 3 * 2), @@ -159,19 +262,22 @@ impl FloatingLayout { if let Some(pos) = self .spawn_order .iter() - .position(|w| w.moved_since_mapped.load(Ordering::SeqCst)) + .position(|w| !w.alive() || w.moved_since_mapped.load(Ordering::SeqCst)) { self.spawn_order.truncate(pos); } + let three_fours_width = (output_geometry.size.w / 4 * 3).max(360); + // figure out new position let pos = self .spawn_order .last() .and_then(|window| self.space.element_geometry(window)) - .map(|mut geometry| { - // new size - geometry.size = win_geo.size.as_logical(); + .filter(|geo| { + geo.size.w < three_fours_width && win_geo.size.w < three_fours_width + }) + .map(|geometry| { let mut geometry: Rectangle = Rectangle::from_loc_and_size( (geometry.loc.x as u32, geometry.loc.y as u32), (geometry.size.w as u32, geometry.size.h as u32), @@ -180,12 +286,56 @@ impl FloatingLayout { // move down geometry.loc.y += 48; - // do we need a new column? - if geometry.loc.y + geometry.size.h > (output_geometry.size.h - 16) as u32 { + // do we need to address the height? + let new_column = if geometry.loc.y + min_size.h as u32 + <= (output_geometry.loc.y + output_geometry.size.h - 16) as u32 + { + // alternate to the sides + let offset = if self + .spawn_order + .iter() + .flat_map(|w| self.space.element_geometry(w)) + .filter(|geo| geo.size.w < three_fours_width) + .count() + % 2 + == 0 + { + (geometry.loc.x + geometry.size.w) + .checked_sub(96 + (win_geo.size.w as u32)) + } else { + (geometry.loc.x + geometry.size.w) + .checked_sub((win_geo.size.w as u32).saturating_sub(48)) + }; + + if let Some(offset) = offset { + geometry.loc.x = offset; + // do we need to resize? + if geometry.loc.y as i32 + win_geo.size.h + <= output_geometry.loc.y + output_geometry.size.h - 16 + { + win_geo.size.h = + (output_geometry.loc.y + output_geometry.size.h - 16) + - geometry.loc.y as i32; + } + + false + } else { + true + } + } else { + true + }; + + if new_column { let min_y = self .spawn_order .iter() - .flat_map(|w| self.space.element_geometry(w).map(|geo| geo.loc.y)) + .flat_map(|w| { + self.space + .element_geometry(w) + .filter(|geo| geo.size.w < three_fours_width) + .map(|geo| geo.loc.y) + }) .min() .unwrap() as u32; geometry.loc.y = min_y.saturating_sub(16); @@ -194,48 +344,37 @@ impl FloatingLayout { Some(new_x) => geometry.loc.x = new_x, None => { // if we go out to the left, cycle around to the right - geometry.loc.x = (output_geometry.size.w as u32) - .saturating_sub(geometry.size.w) + geometry.loc.x = + ((output_geometry.loc.x + output_geometry.size.w) as u32) + .saturating_sub(geometry.size.w + 16) } }; - } else { - // alternate to the sides - if self.spawn_order.len() % 2 == 0 { - geometry.loc.x = (geometry.loc.x + geometry.size.w) - .saturating_sub(96 + geometry.size.w); - } else { - geometry.loc.x = (geometry.loc.x + geometry.size.w) - .saturating_sub(geometry.size.w.saturating_sub(48)); - } } - // check all sides for padding again - let remaining_x = (output_geometry.size.w as u32 - geometry.size.w) / 2; - let remaining_y = (output_geometry.size.h as u32 - geometry.size.h) / 2; - if geometry.loc.x < 16 { - geometry.loc.x = remaining_x.min(16); - } - if geometry.loc.y < 16 { - geometry.loc.y = remaining_y.min(16); + // check padding again + if geometry.loc.x < (output_geometry.loc.x + 16) as u32 { + geometry.loc.x = (output_geometry.loc.x + 16) as u32; } - if geometry.loc.x + geometry.size.w > (output_geometry.size.w - 16) as u32 { - geometry.loc.x = output_geometry.size.w as u32 - - geometry.size.w - - remaining_x.min(16); + if geometry.loc.y < (output_geometry.loc.y + 16) as u32 { + geometry.loc.y = (output_geometry.loc.y + 16) as u32; } - if geometry.loc.y + geometry.size.h > (output_geometry.size.h - 16) as u32 { - geometry.loc.y = output_geometry.size.h as u32 - - geometry.size.h - - remaining_y.min(16); + // if the width would be too high, we wouldn't be here + if geometry.loc.y as i32 + win_geo.size.h + > (output_geometry.loc.y + output_geometry.size.h - 16) + { + win_geo.size.h = output_geometry.loc.y + output_geometry.size.h + - 16 + - geometry.loc.y as i32; } Point::::from((geometry.loc.x as i32, geometry.loc.y as i32)) }) .unwrap_or_else(|| { ( - output_geometry.size.w / 2 - win_geo.size.w / 2, - (output_geometry.size.h / 2 - win_geo.size.h / 2) - .min(output_geometry.size.h / 8), + output_geometry.loc.x + output_geometry.size.w / 2 - win_geo.size.w / 2, + output_geometry.loc.y + + (output_geometry.size.h / 2 - win_geo.size.h / 2) + .min(output_geometry.size.h / 8), ) .into() }) @@ -255,7 +394,18 @@ impl FloatingLayout { } pub fn unmap(&mut self, window: &CosmicMapped) -> bool { - if !window.is_maximized(true) || !window.is_fullscreen(true) { + if let Some(_) = window.floating_tiled.lock().unwrap().take() { + if let Some(last_size) = window.last_geometry.lock().unwrap().map(|geo| geo.size) { + if let Some(location) = self.space.element_location(window) { + window.set_tiled(false); + window.set_geometry( + Rectangle::from_loc_and_size(location, last_size.as_logical()) + .as_local() + .to_global(self.space.outputs().next().unwrap()), + ); + } + } + } else if !window.is_maximized(true) || !window.is_fullscreen(true) { if let Some(location) = self.space.element_location(window) { *window.last_geometry.lock().unwrap() = Some( Rectangle::from_loc_and_size( @@ -276,6 +426,7 @@ impl FloatingLayout { self.spawn_order.truncate(pos); } window.moved_since_mapped.store(true, Ordering::SeqCst); + window.floating_tiled.lock().unwrap().take(); } was_unmaped } @@ -294,6 +445,7 @@ impl FloatingLayout { if seat.get_pointer().is_some() { let location = self.space.element_location(&mapped).unwrap(); let size = mapped.geometry().size; + mapped.moved_since_mapped.store(true, Ordering::SeqCst); Some(grabs::ResizeSurfaceGrab::new( start_data, @@ -392,6 +544,7 @@ impl FloatingLayout { initial_window_size: original_geo.size, })); + mapped.moved_since_mapped.store(true, Ordering::SeqCst); mapped.set_resizing(true); mapped.set_geometry( geo.as_local() @@ -501,7 +654,7 @@ impl FloatingLayout { }; match focused.handle_move(direction) { - StackMoveResult::Handled => return MoveResult::Done, + StackMoveResult::Handled => MoveResult::Done, StackMoveResult::MoveOut(surface, loop_handle) => { let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle, theme).into(); let output = seat.active_output(); @@ -524,12 +677,87 @@ impl FloatingLayout { .then_some(pos); self.map_internal(mapped.clone(), position.map(PointExt::as_local), None); - return MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped)); + MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped)) } - StackMoveResult::Default => {} - }; + StackMoveResult::Default => { + let mut tiled_state = focused.floating_tiled.lock().unwrap(); + let new_state = match (direction, &*tiled_state) { + // figure out if we are moving between workspaces/outputs + ( + Direction::Up, + Some(TiledCorners::Top) + | Some(TiledCorners::TopLeft) + | Some(TiledCorners::TopRight), + ) + | ( + Direction::Down, + Some(TiledCorners::Bottom) + | Some(TiledCorners::BottomLeft) + | Some(TiledCorners::BottomRight), + ) + | ( + Direction::Left, + Some(TiledCorners::Left) + | Some(TiledCorners::TopLeft) + | Some(TiledCorners::BottomLeft), + ) + | ( + Direction::Right, + Some(TiledCorners::Right) + | Some(TiledCorners::TopRight) + | Some(TiledCorners::BottomRight), + ) => { + return MoveResult::MoveFurther(KeyboardFocusTarget::Element( + focused.clone(), + )); + } + + // figure out if we need to quater tile + (Direction::Up, Some(TiledCorners::Left)) + | (Direction::Left, Some(TiledCorners::Top)) => TiledCorners::TopLeft, + (Direction::Right, Some(TiledCorners::Top)) + | (Direction::Up, Some(TiledCorners::Right)) => TiledCorners::TopRight, + (Direction::Down, Some(TiledCorners::Left)) + | (Direction::Left, Some(TiledCorners::Bottom)) => TiledCorners::BottomLeft, + (Direction::Right, Some(TiledCorners::Bottom)) + | (Direction::Down, Some(TiledCorners::Right)) => TiledCorners::BottomRight, + // figure out if we need to extend a quater tile + (Direction::Up, Some(TiledCorners::BottomLeft)) + | (Direction::Down, Some(TiledCorners::TopLeft)) => TiledCorners::Left, + (Direction::Up, Some(TiledCorners::BottomRight)) + | (Direction::Down, Some(TiledCorners::TopRight)) => TiledCorners::Right, + (Direction::Left, Some(TiledCorners::TopRight)) + | (Direction::Right, Some(TiledCorners::TopLeft)) => TiledCorners::Top, + (Direction::Left, Some(TiledCorners::BottomRight)) + | (Direction::Right, Some(TiledCorners::BottomLeft)) => TiledCorners::Bottom, + // else we have a simple case + (Direction::Up, _) => TiledCorners::Top, + (Direction::Right, _) => TiledCorners::Right, + (Direction::Down, _) => TiledCorners::Bottom, + (Direction::Left, _) => TiledCorners::Left, + }; + + let output = self.space.outputs().next().unwrap().clone(); + let layers = layer_map_for_output(&output); + let output_geometry = layers.non_exclusive_zone(); + std::mem::drop(layers); - MoveResult::MoveFurther(KeyboardFocusTarget::Element(focused.clone())) + let new_geo = new_state.relative_geometry(output_geometry); + let (new_pos, new_size) = (new_geo.loc, new_geo.size); + focused.set_tiled(true); // TODO: More fine grained? + + *tiled_state = Some(new_state); + std::mem::drop(tiled_state); + + *focused.last_geometry.lock().unwrap() = + self.space.element_geometry(focused).map(RectExt::as_local); + focused.moved_since_mapped.store(true, Ordering::SeqCst); + let focused = focused.clone(); + self.map_internal(focused, Some(new_pos), Some(new_size.as_logical())); + + MoveResult::Done + } + } } pub fn mapped(&self) -> impl Iterator { @@ -545,6 +773,9 @@ impl FloatingLayout { puffin::profile_function!(); self.space.refresh(); + if let Some(pos) = self.spawn_order.iter().position(|w| !w.alive()) { + self.spawn_order.truncate(pos); + } for element in self .space .elements()