mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement interactive move to a new workspace above/between
This commit is contained in:
+126
-76
@@ -37,7 +37,7 @@ use std::mem;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use monitor::{InsertHint, InsertPosition, MonitorAddWindowTarget};
|
use monitor::{InsertHint, InsertPosition, InsertWorkspace, MonitorAddWindowTarget};
|
||||||
use niri_config::{
|
use niri_config::{
|
||||||
CenterFocusedColumn, Config, CornerRadius, FloatOrInt, PresetSize, Struts,
|
CenterFocusedColumn, Config, CornerRadius, FloatOrInt, PresetSize, Struts,
|
||||||
Workspace as WorkspaceConfig, WorkspaceReference,
|
Workspace as WorkspaceConfig, WorkspaceReference,
|
||||||
@@ -3031,40 +3031,51 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No insert hint when targeting floating.
|
|
||||||
if move_.is_floating {
|
|
||||||
self.interactive_move = Some(InteractiveMoveState::Moving(move_));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _span = tracy_client::span!("Layout::update_insert_hint::update");
|
let _span = tracy_client::span!("Layout::update_insert_hint::update");
|
||||||
|
|
||||||
if let Some(mon) = self.monitor_for_output_mut(&move_.output) {
|
if let Some(mon) = self.monitor_for_output_mut(&move_.output) {
|
||||||
let zoom = mon.overview_zoom();
|
let zoom = mon.overview_zoom();
|
||||||
if let Some((ws, geo)) = mon.workspace_under(move_.pointer_pos_within_output) {
|
let (insert_ws, geo) = mon.insert_position(move_.pointer_pos_within_output);
|
||||||
let ws_id = ws.id();
|
match insert_ws {
|
||||||
let ws = mon
|
InsertWorkspace::Existing(ws_id) => {
|
||||||
.workspaces
|
let ws = mon
|
||||||
.iter_mut()
|
.workspaces
|
||||||
.find(|ws| ws.id() == ws_id)
|
.iter_mut()
|
||||||
.unwrap();
|
.find(|ws| ws.id() == ws_id)
|
||||||
let pos_within_workspace =
|
.unwrap();
|
||||||
(move_.pointer_pos_within_output - geo.loc).downscale(zoom);
|
let pos_within_workspace =
|
||||||
let position = ws.scrolling_insert_position(pos_within_workspace);
|
(move_.pointer_pos_within_output - geo.loc).downscale(zoom);
|
||||||
|
let position = if move_.is_floating {
|
||||||
|
InsertPosition::Floating
|
||||||
|
} else {
|
||||||
|
ws.scrolling_insert_position(pos_within_workspace)
|
||||||
|
};
|
||||||
|
|
||||||
let rules = move_.tile.window().rules();
|
let rules = move_.tile.window().rules();
|
||||||
let border_width = move_.tile.effective_border_width().unwrap_or(0.);
|
let border_width = move_.tile.effective_border_width().unwrap_or(0.);
|
||||||
let corner_radius = rules
|
let corner_radius = rules
|
||||||
.geometry_corner_radius
|
.geometry_corner_radius
|
||||||
.map_or(CornerRadius::default(), |radius| {
|
.map_or(CornerRadius::default(), |radius| {
|
||||||
radius.expanded_by(border_width as f32)
|
radius.expanded_by(border_width as f32)
|
||||||
|
});
|
||||||
|
mon.insert_hint = Some(InsertHint {
|
||||||
|
workspace: insert_ws,
|
||||||
|
position,
|
||||||
|
corner_radius,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
mon.insert_hint = Some(InsertHint {
|
InsertWorkspace::NewAt(_) => {
|
||||||
workspace: ws_id,
|
let position = if move_.is_floating {
|
||||||
position,
|
InsertPosition::Floating
|
||||||
corner_radius,
|
} else {
|
||||||
});
|
InsertPosition::NewColumn(0)
|
||||||
|
};
|
||||||
|
mon.insert_hint = Some(InsertHint {
|
||||||
|
workspace: insert_ws,
|
||||||
|
position,
|
||||||
|
corner_radius: CornerRadius::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4393,59 +4404,81 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
active_monitor_idx,
|
active_monitor_idx,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (mon, ws_idx, position, offset, zoom) = if let Some(mon) =
|
let (mon, insert_ws, position, offset, zoom) =
|
||||||
monitors.iter_mut().find(|mon| mon.output == move_.output)
|
if let Some(mon) = monitors.iter_mut().find(|mon| mon.output == move_.output) {
|
||||||
{
|
let zoom = mon.overview_zoom();
|
||||||
let zoom = mon.overview_zoom();
|
|
||||||
|
|
||||||
let (ws, ws_geo) = mon
|
let (insert_ws, geo) = mon.insert_position(move_.pointer_pos_within_output);
|
||||||
.workspace_under(move_.pointer_pos_within_output)
|
let (position, offset) = match insert_ws {
|
||||||
// If the pointer is somehow outside the move output and a workspace switch
|
InsertWorkspace::Existing(ws_id) => {
|
||||||
// is in progress, this won't necessarily do the expected thing.
|
let ws_idx = mon
|
||||||
.unwrap_or_else(|| mon.workspaces_with_render_geo().next().unwrap());
|
.workspaces
|
||||||
|
.iter_mut()
|
||||||
|
.position(|ws| ws.id() == ws_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let ws_id = ws.id();
|
let position = if move_.is_floating {
|
||||||
let ws_idx = mon
|
InsertPosition::Floating
|
||||||
.workspaces
|
} else {
|
||||||
.iter_mut()
|
let pos_within_workspace =
|
||||||
.position(|ws| ws.id() == ws_id)
|
(move_.pointer_pos_within_output - geo.loc).downscale(zoom);
|
||||||
.unwrap();
|
let ws = &mut mon.workspaces[ws_idx];
|
||||||
|
ws.scrolling_insert_position(pos_within_workspace)
|
||||||
|
};
|
||||||
|
|
||||||
let position = if move_.is_floating {
|
(position, Some(geo.loc))
|
||||||
InsertPosition::Floating
|
}
|
||||||
|
InsertWorkspace::NewAt(_) => {
|
||||||
|
let position = if move_.is_floating {
|
||||||
|
InsertPosition::Floating
|
||||||
|
} else {
|
||||||
|
InsertPosition::NewColumn(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
(position, None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(mon, insert_ws, position, offset, zoom)
|
||||||
} else {
|
} else {
|
||||||
let pos_within_workspace =
|
let mon = &mut monitors[*active_monitor_idx];
|
||||||
(move_.pointer_pos_within_output - ws_geo.loc).downscale(zoom);
|
let zoom = mon.overview_zoom();
|
||||||
let ws = &mut mon.workspaces[ws_idx];
|
// No point in trying to use the pointer position on the wrong output.
|
||||||
ws.scrolling_insert_position(pos_within_workspace)
|
let (ws, ws_geo) = mon.workspaces_with_render_geo().next().unwrap();
|
||||||
|
|
||||||
|
let position = if move_.is_floating {
|
||||||
|
InsertPosition::Floating
|
||||||
|
} else {
|
||||||
|
ws.scrolling_insert_position(Point::from((0., 0.)))
|
||||||
|
};
|
||||||
|
|
||||||
|
let insert_ws = InsertWorkspace::Existing(ws.id());
|
||||||
|
(mon, insert_ws, position, Some(ws_geo.loc), zoom)
|
||||||
};
|
};
|
||||||
|
|
||||||
(mon, ws_idx, position, ws_geo.loc, zoom)
|
|
||||||
} else {
|
|
||||||
let mon = &mut monitors[*active_monitor_idx];
|
|
||||||
let zoom = mon.overview_zoom();
|
|
||||||
// No point in trying to use the pointer position on the wrong output.
|
|
||||||
let (ws, ws_geo) = mon.workspaces_with_render_geo().next().unwrap();
|
|
||||||
|
|
||||||
let position = if move_.is_floating {
|
|
||||||
InsertPosition::Floating
|
|
||||||
} else {
|
|
||||||
ws.scrolling_insert_position(Point::from((0., 0.)))
|
|
||||||
};
|
|
||||||
|
|
||||||
let ws_id = ws.id();
|
|
||||||
let ws_idx = mon
|
|
||||||
.workspaces
|
|
||||||
.iter_mut()
|
|
||||||
.position(|ws| ws.id() == ws_id)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(mon, ws_idx, position, ws_geo.loc, zoom)
|
|
||||||
};
|
|
||||||
|
|
||||||
let win_id = move_.tile.window().id().clone();
|
let win_id = move_.tile.window().id().clone();
|
||||||
let window_render_loc = move_.tile_render_location(zoom) + move_.tile.window_loc();
|
let window_render_loc = move_.tile_render_location(zoom) + move_.tile.window_loc();
|
||||||
|
|
||||||
|
let ws_idx = match insert_ws {
|
||||||
|
InsertWorkspace::Existing(ws_id) => mon
|
||||||
|
.workspaces
|
||||||
|
.iter()
|
||||||
|
.position(|ws| ws.id() == ws_id)
|
||||||
|
.unwrap(),
|
||||||
|
InsertWorkspace::NewAt(ws_idx) => {
|
||||||
|
if self.options.empty_workspace_above_first && ws_idx == 0 {
|
||||||
|
// Reuse the top empty workspace.
|
||||||
|
0
|
||||||
|
} else if mon.workspaces.len() - 1 <= ws_idx {
|
||||||
|
// Reuse the bottom empty workspace.
|
||||||
|
mon.workspaces.len() - 1
|
||||||
|
} else {
|
||||||
|
mon.add_workspace_at(ws_idx);
|
||||||
|
ws_idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match position {
|
match position {
|
||||||
InsertPosition::NewColumn(column_idx) => {
|
InsertPosition::NewColumn(column_idx) => {
|
||||||
let ws_id = mon.workspaces[ws_idx].id();
|
let ws_id = mon.workspaces[ws_idx].id();
|
||||||
@@ -4474,10 +4507,27 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
let tile_render_loc = move_.tile_render_location(zoom);
|
let tile_render_loc = move_.tile_render_location(zoom);
|
||||||
|
|
||||||
let mut tile = move_.tile;
|
let mut tile = move_.tile;
|
||||||
|
tile.floating_pos = None;
|
||||||
|
|
||||||
let pos = (tile_render_loc - offset).downscale(zoom);
|
match insert_ws {
|
||||||
let pos = mon.workspaces[ws_idx].floating_logical_to_size_frac(pos);
|
InsertWorkspace::Existing(_) => {
|
||||||
tile.floating_pos = Some(pos);
|
if let Some(offset) = offset {
|
||||||
|
let pos = (tile_render_loc - offset).downscale(zoom);
|
||||||
|
let pos =
|
||||||
|
mon.workspaces[ws_idx].floating_logical_to_size_frac(pos);
|
||||||
|
tile.floating_pos = Some(pos);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"offset unset for inserting a floating tile \
|
||||||
|
to existing workspace"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InsertWorkspace::NewAt(_) => {
|
||||||
|
// When putting a floating tile on a new workspace, we don't really
|
||||||
|
// have a good pre-existing position.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the floating size so it takes into account any window resizing that
|
// Set the floating size so it takes into account any window resizing that
|
||||||
// took place during the move.
|
// took place during the move.
|
||||||
|
|||||||
+169
-27
@@ -127,16 +127,22 @@ pub(super) enum InsertPosition {
|
|||||||
Floating,
|
Floating,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(super) enum InsertWorkspace {
|
||||||
|
Existing(WorkspaceId),
|
||||||
|
NewAt(usize),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct InsertHint {
|
pub(super) struct InsertHint {
|
||||||
pub workspace: WorkspaceId,
|
pub workspace: InsertWorkspace,
|
||||||
pub position: InsertPosition,
|
pub position: InsertPosition,
|
||||||
pub corner_radius: CornerRadius,
|
pub corner_radius: CornerRadius,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct InsertHintRenderLoc {
|
struct InsertHintRenderLoc {
|
||||||
workspace: WorkspaceId,
|
workspace: InsertWorkspace,
|
||||||
location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +173,7 @@ niri_render_elements! {
|
|||||||
MonitorInnerRenderElement<R> => {
|
MonitorInnerRenderElement<R> => {
|
||||||
Workspace = CropRenderElement<WorkspaceRenderElement<R>>,
|
Workspace = CropRenderElement<WorkspaceRenderElement<R>>,
|
||||||
InsertHint = CropRenderElement<InsertHintRenderElement>,
|
InsertHint = CropRenderElement<InsertHintRenderElement>,
|
||||||
|
UncroppedInsertHint = InsertHintRenderElement,
|
||||||
Shadow = ShadowRenderElement,
|
Shadow = ShadowRenderElement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,6 +238,15 @@ impl WorkspaceSwitchGesture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InsertWorkspace {
|
||||||
|
fn existing_id(self) -> Option<WorkspaceId> {
|
||||||
|
match self {
|
||||||
|
InsertWorkspace::Existing(id) => Some(id),
|
||||||
|
InsertWorkspace::NewAt(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OverviewProgress {
|
impl OverviewProgress {
|
||||||
pub fn value(&self) -> f64 {
|
pub fn value(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
@@ -885,7 +901,10 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
|
|
||||||
pub fn update_render_elements(&mut self, is_active: bool) {
|
pub fn update_render_elements(&mut self, is_active: bool) {
|
||||||
let mut insert_hint_ws_geo = None;
|
let mut insert_hint_ws_geo = None;
|
||||||
let insert_hint_ws_id = self.insert_hint.as_ref().map(|hint| hint.workspace);
|
let insert_hint_ws_id = self
|
||||||
|
.insert_hint
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|hint| hint.workspace.existing_id());
|
||||||
|
|
||||||
for (ws, geo) in self.workspaces_with_render_geo_mut() {
|
for (ws, geo) in self.workspaces_with_render_geo_mut() {
|
||||||
ws.update_render_elements(is_active);
|
ws.update_render_elements(is_active);
|
||||||
@@ -897,38 +916,75 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
|
|
||||||
self.insert_hint_render_loc = None;
|
self.insert_hint_render_loc = None;
|
||||||
if let Some(hint) = &self.insert_hint {
|
if let Some(hint) = &self.insert_hint {
|
||||||
if let Some(ws) = self.workspaces.iter().find(|ws| ws.id() == hint.workspace) {
|
match hint.workspace {
|
||||||
if let Some(mut area) = ws.insert_hint_area(hint.position) {
|
InsertWorkspace::Existing(ws_id) => {
|
||||||
let scale = ws.scale().fractional_scale();
|
if let Some(ws) = self.workspaces.iter().find(|ws| ws.id() == ws_id) {
|
||||||
let view_size = ws.view_size();
|
if let Some(mut area) = ws.insert_hint_area(hint.position) {
|
||||||
|
let scale = ws.scale().fractional_scale();
|
||||||
|
let view_size = ws.view_size();
|
||||||
|
|
||||||
// Make sure the hint is at least partially visible.
|
// Make sure the hint is at least partially visible.
|
||||||
if matches!(hint.position, InsertPosition::NewColumn(_)) {
|
if matches!(hint.position, InsertPosition::NewColumn(_)) {
|
||||||
let zoom = self.overview_zoom();
|
let zoom = self.overview_zoom();
|
||||||
let geo = insert_hint_ws_geo.unwrap();
|
let geo = insert_hint_ws_geo.unwrap();
|
||||||
let geo = geo.downscale(zoom);
|
let geo = geo.downscale(zoom);
|
||||||
|
|
||||||
area.loc.x = area.loc.x.max(-geo.loc.x - area.size.w / 2.);
|
area.loc.x = area.loc.x.max(-geo.loc.x - area.size.w / 2.);
|
||||||
area.loc.x = area.loc.x.min(geo.loc.x + geo.size.w - area.size.w / 2.);
|
area.loc.x =
|
||||||
|
area.loc.x.min(geo.loc.x + geo.size.w - area.size.w / 2.);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round to physical pixels.
|
||||||
|
area = area.to_physical_precise_round(scale).to_logical(scale);
|
||||||
|
|
||||||
|
let view_rect = Rectangle::new(area.loc.upscale(-1.), view_size);
|
||||||
|
self.insert_hint_element.update_render_elements(
|
||||||
|
area.size,
|
||||||
|
view_rect,
|
||||||
|
hint.corner_radius,
|
||||||
|
scale,
|
||||||
|
);
|
||||||
|
self.insert_hint_render_loc = Some(InsertHintRenderLoc {
|
||||||
|
workspace: hint.workspace,
|
||||||
|
location: area.loc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("insert hint workspace missing from monitor");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
InsertWorkspace::NewAt(ws_idx) => {
|
||||||
|
let scale = self.scale.fractional_scale();
|
||||||
|
let zoom = self.overview_zoom();
|
||||||
|
let gap = self.workspace_gap(zoom);
|
||||||
|
|
||||||
// Round to physical pixels.
|
let hint_gap = round_logical_in_physical(scale, gap * 0.1);
|
||||||
area = area.to_physical_precise_round(scale).to_logical(scale);
|
let hint_height = gap - hint_gap * 2.;
|
||||||
|
|
||||||
|
let next_ws_geo = self.workspaces_render_geo().nth(ws_idx).unwrap();
|
||||||
|
let hint_loc_diff = Point::from((0., hint_height + hint_gap));
|
||||||
|
let hint_loc = next_ws_geo.loc - hint_loc_diff;
|
||||||
|
let hint_size = Size::from((next_ws_geo.size.w, hint_height));
|
||||||
|
|
||||||
|
// FIXME: sometimes the hint ends up 1 px wider than necessary and/or 1 px
|
||||||
|
// narrower than necessary. The values here seem correct. Might have to do with
|
||||||
|
// how zooming out currently doesn't round to output scale properly.
|
||||||
|
|
||||||
|
// Compute view rect as if we're above the next workspace (rather than below
|
||||||
|
// the previous one).
|
||||||
|
let view_rect = Rectangle::new(hint_loc_diff, next_ws_geo.size);
|
||||||
|
|
||||||
let view_rect = Rectangle::new(area.loc.upscale(-1.), view_size);
|
|
||||||
self.insert_hint_element.update_render_elements(
|
self.insert_hint_element.update_render_elements(
|
||||||
area.size,
|
hint_size,
|
||||||
view_rect,
|
view_rect,
|
||||||
hint.corner_radius,
|
CornerRadius::default(),
|
||||||
scale,
|
scale,
|
||||||
);
|
);
|
||||||
self.insert_hint_render_loc = Some(InsertHintRenderLoc {
|
self.insert_hint_render_loc = Some(InsertHintRenderLoc {
|
||||||
workspace: hint.workspace,
|
workspace: hint.workspace,
|
||||||
location: area.loc,
|
location: hint_loc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
error!("insert hint workspace missing from monitor");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1239,6 +1295,17 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
.filter(move |(_ws, geo)| geo.intersection(output_geo).is_some())
|
.filter(move |(_ws, geo)| geo.intersection(output_geo).is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn workspaces_with_render_geo_idx(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = ((usize, &Workspace<W>), Rectangle<f64, Logical>)> {
|
||||||
|
let output_geo = Rectangle::from_size(self.view_size);
|
||||||
|
|
||||||
|
let geo = self.workspaces_render_geo();
|
||||||
|
zip(self.workspaces.iter().enumerate(), geo)
|
||||||
|
// Cull out workspaces outside the output.
|
||||||
|
.filter(move |(_ws, geo)| geo.intersection(output_geo).is_some())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn workspaces_with_render_geo_mut(
|
pub fn workspaces_with_render_geo_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> impl Iterator<Item = (&mut Workspace<W>, Rectangle<f64, Logical>)> {
|
) -> impl Iterator<Item = (&mut Workspace<W>, Rectangle<f64, Logical>)> {
|
||||||
@@ -1298,6 +1365,55 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
ws.resize_edges_under(pos_within_output - geo.loc)
|
ws.resize_edges_under(pos_within_output - geo.loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn insert_position(
|
||||||
|
&self,
|
||||||
|
pos_within_output: Point<f64, Logical>,
|
||||||
|
) -> (InsertWorkspace, Rectangle<f64, Logical>) {
|
||||||
|
let mut iter = self.workspaces_with_render_geo_idx();
|
||||||
|
|
||||||
|
let dummy = Rectangle::default();
|
||||||
|
|
||||||
|
// Monitors always have at least one workspace.
|
||||||
|
let ((idx, ws), geo) = iter.next().unwrap();
|
||||||
|
|
||||||
|
// Check if above first.
|
||||||
|
if pos_within_output.y < geo.loc.y {
|
||||||
|
return (InsertWorkspace::NewAt(idx), dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
let contains = move |geo: Rectangle<f64, Logical>| {
|
||||||
|
geo.loc.y <= pos_within_output.y && pos_within_output.y < geo.loc.y + geo.size.h
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check first.
|
||||||
|
if contains(geo) {
|
||||||
|
return (InsertWorkspace::Existing(ws.id()), geo);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut last_geo = geo;
|
||||||
|
let mut last_idx = idx;
|
||||||
|
for ((idx, ws), geo) in iter {
|
||||||
|
// Check gap above.
|
||||||
|
let gap_loc = Point::from((last_geo.loc.x, last_geo.loc.y + last_geo.size.h));
|
||||||
|
let gap_size = Size::from((geo.size.w, geo.loc.y - gap_loc.y));
|
||||||
|
let gap_geo = Rectangle::new(gap_loc, gap_size);
|
||||||
|
if contains(gap_geo) {
|
||||||
|
return (InsertWorkspace::NewAt(idx), dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check workspace itself.
|
||||||
|
if contains(geo) {
|
||||||
|
return (InsertWorkspace::Existing(ws.id()), geo);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_geo = geo;
|
||||||
|
last_idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything below.
|
||||||
|
(InsertWorkspace::NewAt(last_idx + 1), dummy)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_above_top_layer(&self) -> bool {
|
pub fn render_above_top_layer(&self) -> bool {
|
||||||
// Render above the top layer only if the view is stationary.
|
// Render above the top layer only if the view is stationary.
|
||||||
if self.workspace_switch.is_some() || self.overview_progress.is_some() {
|
if self.workspace_switch.is_some() || self.overview_progress.is_some() {
|
||||||
@@ -1308,6 +1424,30 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
ws.render_above_top_layer()
|
ws.render_above_top_layer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_insert_hint_between_workspaces<R: NiriRenderer>(
|
||||||
|
&self,
|
||||||
|
renderer: &mut R,
|
||||||
|
) -> impl Iterator<Item = MonitorRenderElement<R>> {
|
||||||
|
let mut rv = None;
|
||||||
|
|
||||||
|
if !self.options.insert_hint.off {
|
||||||
|
if let Some(render_loc) = self.insert_hint_render_loc {
|
||||||
|
if let InsertWorkspace::NewAt(_) = render_loc.workspace {
|
||||||
|
let iter = self
|
||||||
|
.insert_hint_element
|
||||||
|
.render(renderer, render_loc.location)
|
||||||
|
.map(MonitorInnerRenderElement::UncroppedInsertHint);
|
||||||
|
rv = Some(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.into_iter().flatten().map(|elem| {
|
||||||
|
let elem = RescaleRenderElement::from_element(elem, Point::default(), 1.);
|
||||||
|
RelocateRenderElement::from_element(elem, Point::default(), Relocate::Relative)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_elements<'a, R: NiriRenderer>(
|
pub fn render_elements<'a, R: NiriRenderer>(
|
||||||
&'a self,
|
&'a self,
|
||||||
renderer: &'a mut R,
|
renderer: &'a mut R,
|
||||||
@@ -1354,11 +1494,13 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
let mut insert_hint = None;
|
let mut insert_hint = None;
|
||||||
if !self.options.insert_hint.off {
|
if !self.options.insert_hint.off {
|
||||||
if let Some(render_loc) = self.insert_hint_render_loc {
|
if let Some(render_loc) = self.insert_hint_render_loc {
|
||||||
insert_hint = Some((
|
if let InsertWorkspace::Existing(workspace_id) = render_loc.workspace {
|
||||||
render_loc.workspace,
|
insert_hint = Some((
|
||||||
self.insert_hint_element
|
workspace_id,
|
||||||
.render(renderer, render_loc.location),
|
self.insert_hint_element
|
||||||
));
|
.render(renderer, render_loc.location),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
@@ -3971,6 +3971,7 @@ impl Niri {
|
|||||||
mon.render_elements(renderer, target, focus_ring)
|
mon.render_elements(renderer, target, focus_ring)
|
||||||
.map(|(geo, iter)| (geo, Vec::from_iter(iter))),
|
.map(|(geo, iter)| (geo, Vec::from_iter(iter))),
|
||||||
);
|
);
|
||||||
|
let insert_hint_elements = mon.render_insert_hint_between_workspaces(renderer);
|
||||||
let int_move_elements: Vec<_> = self
|
let int_move_elements: Vec<_> = self
|
||||||
.layout
|
.layout
|
||||||
.render_interactive_move_for_output(renderer, output, target)
|
.render_interactive_move_for_output(renderer, output, target)
|
||||||
@@ -4006,6 +4007,11 @@ impl Niri {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(OutputRenderElements::from),
|
.map(OutputRenderElements::from),
|
||||||
);
|
);
|
||||||
|
elements.extend(
|
||||||
|
insert_hint_elements
|
||||||
|
.into_iter()
|
||||||
|
.map(OutputRenderElements::from),
|
||||||
|
);
|
||||||
elements.extend(
|
elements.extend(
|
||||||
monitor_elements
|
monitor_elements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -4027,6 +4033,12 @@ impl Niri {
|
|||||||
.map(OutputRenderElements::from),
|
.map(OutputRenderElements::from),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
elements.extend(
|
||||||
|
insert_hint_elements
|
||||||
|
.into_iter()
|
||||||
|
.map(OutputRenderElements::from),
|
||||||
|
);
|
||||||
|
|
||||||
for (ws_geo, ws_elements) in monitor_elements {
|
for (ws_geo, ws_elements) in monitor_elements {
|
||||||
// Collect all other layer-shell elements.
|
// Collect all other layer-shell elements.
|
||||||
let mut layer_elems = SplitElements::default();
|
let mut layer_elems = SplitElements::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user