diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-04-25 08:53:16 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-04-25 02:00:18 -0700 |
| commit | 9571d149b2cecd3df8ba3f90f0af296e9f69af6e (patch) | |
| tree | 185e7a30f094f5e0eb42400e7709e233e86c9bff /src/layout | |
| parent | 99358e36b38c1aa5cc0c0fb36a91ae7cb4a2e3ba (diff) | |
| download | niri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.tar.gz niri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.tar.bz2 niri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.zip | |
Render workspaces separately with gaps between
This design makes more sense spatially, and is required for the
Overview. Gaps also make it clear how clipping windows to workspace
bounds works.
Background and bottom layer-shell surfaces get duplicated for each
workspace, while top and overlay stay "on top".
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/mod.rs | 2 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 147 |
2 files changed, 92 insertions, 57 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 040a5cb4..cdb96113 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -2279,7 +2279,7 @@ impl<W: LayoutElement> Layout<W> { ) -> Option<(&W, HitType)> { if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move { if move_.output == *output { - let tile_pos = move_.tile_render_location(1.); + let tile_pos = move_.tile_render_location(); HitType::hit_tile(&move_.tile, tile_pos, pos_within_output) } else { None diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 4c20d533..ab5384a8 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -25,7 +25,9 @@ use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::RenderTarget; use crate::rubber_band::RubberBand; use crate::utils::transaction::Transaction; -use crate::utils::{output_size, ResizeEdge}; +use crate::utils::{ + output_size, round_logical_in_physical, round_logical_in_physical_max1, ResizeEdge, +}; /// Amount of touchpad movement to scroll the height of one workspace. const WORKSPACE_GESTURE_MOVEMENT: f64 = 300.; @@ -943,6 +945,23 @@ impl<W: LayoutElement> Monitor<W> { self.active_workspace_ref().active_tile_visual_rectangle() } + fn workspace_size(&self, zoom: f64) -> Size<f64, Logical> { + let ws_size = self.view_size.upscale(zoom); + let scale = self.scale.fractional_scale(); + ws_size.to_physical_precise_ceil(scale).to_logical(scale) + } + + fn workspace_gap(&self, zoom: f64) -> f64 { + let scale = self.scale.fractional_scale(); + let gap = self.view_size.h * 0.1 * zoom; + round_logical_in_physical_max1(scale, gap) + } + + fn workspace_size_with_gap(&self, zoom: f64) -> Size<f64, Logical> { + let gap = self.workspace_gap(zoom); + self.workspace_size(zoom) + Size::from((0., gap)) + } + pub fn workspace_render_idx(&self) -> f64 { if let Some(switch) = &self.workspace_switch { switch.current_idx() @@ -953,17 +972,19 @@ impl<W: LayoutElement> Monitor<W> { pub fn workspaces_render_geo(&self) -> impl Iterator<Item = Rectangle<f64, Logical>> { let scale = self.scale.fractional_scale(); - let size = self.view_size; + let zoom = 1.; - // Ceil the workspace size in physical pixels. - let ws_size = size.to_physical_precise_ceil(scale).to_logical(scale); + let ws_size = self.workspace_size(zoom); + let gap = self.workspace_gap(zoom); + let ws_height_with_gap = ws_size.h + gap; - let first_ws_y = -self.workspace_render_idx() * ws_size.h; + let first_ws_y = -self.workspace_render_idx() * ws_height_with_gap; + let first_ws_y = round_logical_in_physical(scale, first_ws_y); - (0..self.workspaces.len()).map(move |idx| { - let y = first_ws_y + idx as f64 * ws_size.h; + // Return position for one-past-last workspace too. + (0..=self.workspaces.len()).map(move |idx| { + let y = first_ws_y + idx as f64 * ws_height_with_gap; let loc = Point::from((0., y)); - let loc = loc.to_physical_precise_round(scale).to_logical(scale); Rectangle::new(loc, ws_size) }) } @@ -1031,7 +1052,12 @@ impl<W: LayoutElement> Monitor<W> { renderer: &'a mut R, target: RenderTarget, focus_ring: bool, - ) -> impl Iterator<Item = MonitorRenderElement<R>> + 'a { + ) -> impl Iterator< + Item = ( + Rectangle<f64, Logical>, + impl Iterator<Item = MonitorRenderElement<R>> + 'a, + ), + > { let _span = tracy_client::span!("Monitor::render_elements"); let scale = self.scale.fractional_scale(); @@ -1072,45 +1098,45 @@ impl<W: LayoutElement> Monitor<W> { } } - self.workspaces_with_render_geo() - .flat_map(move |(ws, geo)| { - let map_ws_contents = move |elem: WorkspaceRenderElement<R>| { + self.workspaces_with_render_geo().map(move |(ws, geo)| { + let map_ws_contents = move |elem: WorkspaceRenderElement<R>| { + let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?; + let elem = MonitorInnerRenderElement::Workspace(elem); + Some(elem) + }; + + let (floating, scrolling) = ws.render_elements(renderer, target, focus_ring); + let floating = floating.filter_map(map_ws_contents); + let scrolling = scrolling.filter_map(map_ws_contents); + + let hint = if matches!(insert_hint, Some((hint_ws_id, _)) if hint_ws_id == ws.id()) { + let iter = insert_hint.take().unwrap().1; + let iter = iter.filter_map(move |elem| { let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?; - let elem = MonitorInnerRenderElement::Workspace(elem); + let elem = MonitorInnerRenderElement::InsertHint(elem); Some(elem) - }; - - let (floating, scrolling) = ws.render_elements(renderer, target, focus_ring); - let floating = floating.filter_map(map_ws_contents); - let scrolling = scrolling.filter_map(map_ws_contents); - - let hint = if matches!(insert_hint, Some((hint_ws_id, _)) if hint_ws_id == ws.id()) - { - let iter = insert_hint.take().unwrap().1; - let iter = iter.filter_map(move |elem| { - let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?; - let elem = MonitorInnerRenderElement::InsertHint(elem); - Some(elem) - }); - Some(iter) - } else { - None - }; - let hint = hint.into_iter().flatten(); - - let iter = floating.chain(hint).chain(scrolling); - - iter.map(move |elem| { - RelocateRenderElement::from_element( - elem, - // The offset we get from workspaces_with_render_positions() is already - // rounded to physical pixels, but it's in the logical coordinate - // space, so we need to convert it to physical. - geo.loc.to_physical_precise_round(scale), - Relocate::Relative, - ) - }) - }) + }); + Some(iter) + } else { + None + }; + let hint = hint.into_iter().flatten(); + + let iter = floating.chain(hint).chain(scrolling); + + let iter = iter.map(move |elem| { + RelocateRenderElement::from_element( + elem, + // The offset we get from workspaces_with_render_positions() is already + // rounded to physical pixels, but it's in the logical coordinate + // space, so we need to convert it to physical. + geo.loc.to_physical_precise_round(scale), + Relocate::Relative, + ) + }); + + (geo, iter) + }) } pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) { @@ -1133,7 +1159,7 @@ impl<W: LayoutElement> Monitor<W> { timestamp: Duration, is_touchpad: bool, ) -> Option<bool> { - let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { + let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else { return None; }; @@ -1141,13 +1167,18 @@ impl<W: LayoutElement> Monitor<W> { return None; } - gesture.tracker.push(delta_y, timestamp); - let total_height = if gesture.is_touchpad { WORKSPACE_GESTURE_MOVEMENT } else { - self.workspaces[0].view_size().h + self.workspace_size_with_gap(1.).h + }; + + let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { + return None; }; + + gesture.tracker.push(delta_y, timestamp); + let pos = gesture.tracker.pos() / total_height; let (min, max) = gesture.min_max(self.workspaces.len()); @@ -1163,7 +1194,7 @@ impl<W: LayoutElement> Monitor<W> { } pub fn workspace_switch_gesture_end(&mut self, is_touchpad: Option<bool>) -> bool { - let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { + let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else { return false; }; @@ -1171,16 +1202,20 @@ impl<W: LayoutElement> Monitor<W> { return false; } - // Take into account any idle time between the last event and now. - let now = self.clock.now_unadjusted(); - gesture.tracker.push(0., now); - let total_height = if gesture.is_touchpad { WORKSPACE_GESTURE_MOVEMENT } else { - self.workspaces[0].view_size().h + self.workspace_size_with_gap(1.).h + }; + + let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { + return false; }; + // Take into account any idle time between the last event and now. + let now = self.clock.now_unadjusted(); + gesture.tracker.push(0., now); + let mut velocity = gesture.tracker.velocity() / total_height; let current_pos = gesture.tracker.pos() / total_height; let pos = gesture.tracker.projected_end_pos() / total_height; |
