diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-02-15 13:11:34 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-02-15 13:28:57 +0300 |
| commit | ca1500ae90f33344fe776898ae11137bf7d1ecc1 (patch) | |
| tree | 75bd257aae6da74f14e6b5f4097c5e7778414f49 /src/layout | |
| parent | d7f3ca00c70b264032025c262b3c0b10e67c04be (diff) | |
| download | niri-ca1500ae90f33344fe776898ae11137bf7d1ecc1.tar.gz niri-ca1500ae90f33344fe776898ae11137bf7d1ecc1.tar.bz2 niri-ca1500ae90f33344fe776898ae11137bf7d1ecc1.zip | |
Implement scrolling the view during DnD
DnD is external to the layout, so we just inform it when one is ongoing.
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/mod.rs | 100 | ||||
| -rw-r--r-- | src/layout/tests.rs | 19 |
2 files changed, 97 insertions, 22 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 739709f4..248a5c4f 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -278,6 +278,8 @@ pub struct Layout<W: LayoutElement> { last_active_workspace_id: HashMap<String, WorkspaceId>, /// Ongoing interactive move. interactive_move: Option<InteractiveMoveState<W>>, + /// Ongoing drag-and-drop operation. + dnd: Option<DndData>, /// Clock for driving animations. clock: Clock, /// Time that we last updated render elements for. @@ -400,6 +402,14 @@ struct InteractiveMoveData<W: LayoutElement> { pub(self) pointer_ratio_within_window: (f64, f64), } +#[derive(Debug)] +pub struct DndData { + /// Output where the pointer is currently located. + output: Output, + /// Current pointer position within output. + pointer_pos_within_output: Point<f64, Logical>, +} + #[derive(Debug, Clone, Copy)] pub struct InteractiveResizeData { pub(self) edges: ResizeEdge, @@ -600,6 +610,7 @@ impl<W: LayoutElement> Layout<W> { is_active: true, last_active_workspace_id: HashMap::new(), interactive_move: None, + dnd: None, clock, update_render_elements_time: Duration::ZERO, options: Rc::new(options), @@ -622,6 +633,7 @@ impl<W: LayoutElement> Layout<W> { is_active: true, last_active_workspace_id: HashMap::new(), interactive_move: None, + dnd: None, clock, update_render_elements_time: Duration::ZERO, options: opts, @@ -2518,7 +2530,7 @@ impl<W: LayoutElement> Layout<W> { workspace.verify_invariants(move_win_id.as_ref()); let has_view_offset_gesture = workspace.scrolling().view_offset().is_gesture(); - if self.interactive_move.is_some() { + if self.dnd.is_some() || self.interactive_move.is_some() { // We'd like to check that all workspaces have the gesture here, furthermore we // want to check that they have the gesture only if the interactive move // targets the scrolling layout. However, we cannot do that because we start @@ -2545,23 +2557,30 @@ impl<W: LayoutElement> Layout<W> { pub fn advance_animations(&mut self) { let _span = tracy_client::span!("Layout::advance_animations"); + let mut dnd_scroll = None; + if let Some(dnd) = &self.dnd { + dnd_scroll = Some((dnd.output.clone(), dnd.pointer_pos_within_output)); + } + if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { move_.tile.advance_animations(); - // Scroll the view if needed. - if !move_.is_floating { - let output = move_.output.clone(); - let pos_within_output = move_.pointer_pos_within_output; - if let Some(mon) = self.monitor_for_output_mut(&output) { - if let Some((ws, offset)) = mon.workspace_under(pos_within_output) { - let ws_id = ws.id(); - let ws = mon - .workspaces - .iter_mut() - .find(|ws| ws.id() == ws_id) - .unwrap(); - ws.dnd_scroll_gesture_scroll(pos_within_output - offset); - } + if !move_.is_floating && dnd_scroll.is_none() { + dnd_scroll = Some((move_.output.clone(), move_.pointer_pos_within_output)); + } + } + + // Scroll the view if needed. + if let Some((output, pos_within_output)) = dnd_scroll { + if let Some(mon) = self.monitor_for_output_mut(&output) { + if let Some((ws, offset)) = mon.workspace_under(pos_within_output) { + let ws_id = ws.id(); + let ws = mon + .workspaces + .iter_mut() + .find(|ws| ws.id() == ws_id) + .unwrap(); + ws.dnd_scroll_gesture_scroll(pos_within_output - offset); } } } @@ -2581,6 +2600,13 @@ impl<W: LayoutElement> Layout<W> { } pub fn are_animations_ongoing(&self, output: Option<&Output>) -> bool { + // Keep advancing animations if we might need to scroll the view. + if let Some(dnd) = &self.dnd { + if output.map_or(true, |output| *output == dnd.output) { + return true; + } + } + if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move { if output.map_or(true, |output| *output == move_.output) { if move_.tile.are_animations_ongoing() { @@ -3991,6 +4017,33 @@ impl<W: LayoutElement> Layout<W> { move_.output == *output } + pub fn dnd_update(&mut self, output: Output, pointer_pos_within_output: Point<f64, Logical>) { + let begin_gesture = self.dnd.is_none(); + + self.dnd = Some(DndData { + output, + pointer_pos_within_output, + }); + + if begin_gesture { + for ws in self.workspaces_mut() { + ws.dnd_scroll_gesture_begin(); + } + } + } + + pub fn dnd_end(&mut self) { + if self.dnd.is_none() { + return; + } + + self.dnd = None; + + for ws in self.workspaces_mut() { + ws.view_offset_gesture_end(false, None); + } + } + pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool { match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => { @@ -4349,7 +4402,8 @@ impl<W: LayoutElement> Layout<W> { self.is_active = is_active; - let mut ongoing_scrolling_dnd = None; + let mut ongoing_scrolling_dnd = self.dnd.is_some().then_some(true); + if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { let win = move_.tile.window_mut(); @@ -4364,15 +4418,17 @@ impl<W: LayoutElement> Layout<W> { win.send_pending_configure(); win.refresh(); - ongoing_scrolling_dnd = Some(!move_.is_floating); + ongoing_scrolling_dnd.get_or_insert(!move_.is_floating); } else if let Some(InteractiveMoveState::Starting { window_id, .. }) = &self.interactive_move { - let (_, _, ws) = self - .workspaces() - .find(|(_, _, ws)| ws.has_window(window_id)) - .unwrap(); - ongoing_scrolling_dnd = Some(!ws.is_floating(window_id)); + ongoing_scrolling_dnd.get_or_insert_with(|| { + let (_, _, ws) = self + .workspaces() + .find(|(_, _, ws)| ws.has_window(window_id)) + .unwrap(); + !ws.is_floating(window_id) + }); } match &mut self.monitor_set { diff --git a/src/layout/tests.rs b/src/layout/tests.rs index ecb9d683..813f4765 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -589,6 +589,15 @@ enum Op { #[proptest(strategy = "1..=5usize")] window: usize, }, + DndUpdate { + #[proptest(strategy = "1..=5usize")] + output_idx: usize, + #[proptest(strategy = "-20000f64..20000f64")] + px: f64, + #[proptest(strategy = "-20000f64..20000f64")] + py: f64, + }, + DndEnd, InteractiveResizeBegin { #[proptest(strategy = "1..=5usize")] window: usize, @@ -1353,6 +1362,16 @@ impl Op { Op::InteractiveMoveEnd { window } => { layout.interactive_move_end(&window); } + Op::DndUpdate { output_idx, px, py } => { + let name = format!("output{output_idx}"); + let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else { + return; + }; + layout.dnd_update(output, Point::from((px, py))); + } + Op::DndEnd => { + layout.dnd_end(); + } Op::InteractiveResizeBegin { window, edges } => { layout.interactive_resize_begin(window, edges); } |
