aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-04-25 08:53:16 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-04-25 02:00:18 -0700
commit9571d149b2cecd3df8ba3f90f0af296e9f69af6e (patch)
tree185e7a30f094f5e0eb42400e7709e233e86c9bff /src/layout
parent99358e36b38c1aa5cc0c0fb36a91ae7cb4a2e3ba (diff)
downloadniri-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.rs2
-rw-r--r--src/layout/monitor.rs147
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;