Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement interactive move #547

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open

Implement interactive move #547

wants to merge 32 commits into from

Conversation

Pajn
Copy link

@Pajn Pajn commented Jul 15, 2024

I've started working on interactive move support as I eventually want #122 but it's a little too big to start with. Interactive move feels smaller but still like a step towards it.

Currently implemented:

  • Can pickup with Mod+MouseLeft and dragging a titlebar
  • Windows follow cursor after pickup
  • Can drop as both new columns and in existing columns using the same 1/3 rule as interactive resize
  • Dragging to different monitors (or workspaces)
  • Some visual indication of where you'll drop
  • Animations and rerenders needs some more love
  • Some way to scroll workspaces while dragging (Press middle mouse button during the move)
  • Investigate if needing to prevent workspace actions during move (Tested actions work well during move)
  • Border/focus ring on moving window
  • Output updates during move

@YaLTeR
Copy link
Owner

YaLTeR commented Jul 15, 2024

Cool! But just FYI, I was going to get this done as part of adding floating windows, which will need lots of refactoring.

@YaLTeR
Copy link
Owner

YaLTeR commented Jul 15, 2024

(I am postponing it until then specifically because it will influence the architecture and design of moving windows)

@Pajn
Copy link
Author

Pajn commented Jul 15, 2024

@YaLTeR If you are on it I can drop it. I wanted to try and get a feel for the code before committing to something publicly but do understand that it can mean it doesn't at all fit with your plans or is already started in some other way.

@YaLTeR
Copy link
Owner

YaLTeR commented Jul 15, 2024

I am not "on it" (busy with uni atm, don't want to take on big tasks for the time being), but it will likely lead to different design and code compared to implementing move without a floating layer. For example, I thought that moving a window out of a column would just put it on the floating layer for the duration of the move (and then whether it stays there or goes back into a column depends on where you drop it). Which is obviously not possible without a floating layer. Or, how to handle moves across monitors, which will also be just transparently handled by the floating layer, etc.

@Pajn
Copy link
Author

Pajn commented Jul 15, 2024

That was badly worded. Having some thoughts about the implementation definately counts in what I inteded with "on it" even if not actively or having started coding. And you should ofc. prio uni.
I'm very happy to just drop it if you want. Or rework it in a way closer to what you describe.

I'm thinking maybe I could take a shot at adding a "floating layer" that to start with will only be used for holding the window under drag and then full floating support can be added to it later?

However that would immediately raise at the very least one architectural question that maybe you don't want to nail down atm. Is the floating layer bound to a workspace (so floating windows are hidden when you switch workspace/possibly other floating windows from the new workspace are shown) or is the floating layer above the workspace so the same floating windows are shown over all workspaces?

Or is there anything else I can look at that could help bring floating windows closer while still being a limited scope?

@YaLTeR
Copy link
Owner

YaLTeR commented Jul 15, 2024

However that would immediately raise at the very least one architectural question that maybe you don't want to nail down atm. Is the floating layer bound to a workspace (so floating windows are hidden when you switch workspace/possibly other floating windows from the new workspace are shown) or is the floating layer above the workspace so the same floating windows are shown over all workspaces?

I think floating should be global across monitors (use the same coordinate system as the mouse pointer essentially) and, for the first impl, always on top (no workspaces). This means, among other things, moving monitor position tracking into the layout.

I'm very happy to just drop it if you want. Or rework it in a way closer to what you describe.

I'm thinking maybe I could take a shot at adding a "floating layer" that to start with will only be used for holding the window under drag and then full floating support can be added to it later?

Well, I really don't want to tell people to drop something, especially when I haven't started working on it myself. I took a very brief glance at the contents of this PR and it seems to change all the expected places, which is good. But it's just, with larger refactors like this, it may turn out that reviewing someone else's work and shaping it to my vision takes way more effort for both parties than if I eventually just do a several day long dive myself.

@Pajn
Copy link
Author

Pajn commented Jul 15, 2024

But it's just, with larger refactors like this, it may turn out that reviewing someone else's work and shaping it to my vision takes way more effort for both parties than if I eventually just do a several day long dive myself.

Totally agree and why not tackling floating layout directly and also why I'm happy to drop this work if you think it's too close to it. I'll start looking into shaping this into something that could work well with the floating layout you describe and see if it's possible while keeping it small.

@Pajn
Copy link
Author

Pajn commented Jul 18, 2024

I've reworked it to remove the window from the workspace and store it on layout during the move instead. So while no floating layer atm. I think it should work pretty well with one, with some code just getting moved into it instead.

So far it has been very kind to the existing structure but I do start to need to extract some stuff from Tile into some kind of TopLevel structure that can be used by both Tile and the moving window (as well as floating windows in the future). For example borders and the rendering logic.
I think I can leave monitor position tracking untouched as it should be able to recover on the next motion event but will look into re-config during move later to make sure it's not too broken.

Still very wip and not ready for review but I'm at a point where its very usable for myself at least.
I've also added a very basic solid color insert hint which really helps seeing what's happening. I tried to make it autoscroll to show the insert hint outside of the screen but that part feels quite yanky to use. Probably just going to drop it and add a way to manually scroll the workspace during the move.

@YaLTeR
Copy link
Owner

YaLTeR commented Jul 18, 2024

I do start to need to extract some stuff from Tile into some kind of TopLevel structure that can be used by both Tile and the moving window (as well as floating windows in the future)

That is what the Tile already is. Window with borders and other stuff. Don't mind that it's called Tile.

@Pajn
Copy link
Author

Pajn commented Jul 22, 2024

I think this is working quite well now.

  • Switched to using Tile which cleaned up a few things and added back focus-ring rendering
  • Switched the attempt at auto-scrolling to instead allow pressing the middle mouse button during the move for manual scrolling.
  • Tested workspace and output actions during moving and made sure it doesn't blow up

@Pajn Pajn changed the title WIP: Implement interactive move Implement interactive move Jul 22, 2024
@Pajn Pajn marked this pull request as ready for review July 22, 2024 09:35
@YaLTeR
Copy link
Owner

YaLTeR commented Jul 23, 2024

I'll try to take a look over the following days.

Copy link
Owner

@YaLTeR YaLTeR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks very solid already!

These clients are very useful for testing: https://gitlab.freedesktop.org/emersion/wleird

Especially wleird-resize-loop. I also found useful these patches to it:

diff --git a/resize-loop.c b/resize-loop.c
index c1171a5..200908f 100644
--- a/resize-loop.c
+++ b/resize-loop.c
@@ -6,7 +6,7 @@
 
 #define MIN 2
 #define MAX 512
-#define SPEED 10
+#define SPEED 3
 static int size = MIN;
 
 static struct wleird_toplevel toplevel = {0};
@@ -27,6 +27,8 @@ static void callback_handle_done(void *data, struct wl_callback *callback,
 
 	toplevel.surface.width = abs(size);
 	toplevel.surface.height = abs(size);
+	xdg_toplevel_set_min_size(toplevel.xdg_toplevel, abs(size), abs(size));
+	xdg_toplevel_set_max_size(toplevel.xdg_toplevel, abs(size), abs(size));
 	surface_render(&toplevel.surface);
 
 	size += SPEED;

@@ -178,6 +178,14 @@ layout {
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
}

insert-hint {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh I'd not include this in the default config, not that important for first time users.

@@ -2919,8 +2923,18 @@ impl Niri {
if mon.render_above_top_layer() {
elements.extend(monitor_elements.into_iter().map(OutputRenderElements::from));
extend_from_layer(&mut elements, Layer::Top);
elements.extend(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, how to handle this ordering is one of the design questions... I guess this makes sense even if a bit weird.

@@ -69,6 +69,11 @@ pub struct Mapped {
/// Snapshot right before an animated commit.
animation_snapshot: Option<LayoutElementRenderSnapshot>,

/// Last time interactive move was started.
///
/// Used for double-move-click tracking.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we do with a double-move-click? Whatever we do, I'm not sure it's a good idea, because when moving clients by title bar, double click will cause a maximize. So I think it should either be also maximize, or nothing, to avoid the ambiguity.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I copied the setup from resize without thinking about that. Nothing probably makes sense for now at least. It still allows adding maximize or other things later.

.cloned();
if let Some(output) = output {
self.is_moving = true;
data.niri.layout.view_offset_gesture_begin(&output, false);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we shouldn't re-use the view offset gesture like this, and rather add the necessary logic into interactive move itself. Especially when we go into the floating layer direction where it will behave differently.

#[derive(Debug, Clone, Copy)]
pub struct InteractiveResizeData {
pub edges: ResizeEdge,
}

pub trait LayoutElement {
/// Type that can be used as a unique ID of this element.
type Id: PartialEq + std::fmt::Debug;
type Id: Clone + PartialEq + std::fmt::Debug;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

@@ -41,11 +41,13 @@ use smithay::backend::renderer::element::Id;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a workspace switches, the new workspace should get an insert hint right away. Currently it doesn't until a mouse movement.

let size = Size::from((self.data.get(column_index)?.width, 300.));
let loc = Point::from((
self.column_x(column_index),
self.columns.get(column_index)?.tile_offset(tile_index).y - size.h / 2.,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't take topmost/bottommost gap into account, so the hint extends up/down there. I feel like it should end on the same pixel the window would end.

@@ -2246,6 +2361,154 @@ impl<W: LayoutElement> Layout<W> {
None
}

pub fn interactive_move_begin(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When starting a move while in the middle of a workspace switch, the grabbed window position is wrong (does not correspond to where you grabbed the window).

true
}

pub fn interactive_move_update(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When in the middle of a workspace switch, you can't interactive-move across the two workspaces. Up to you if you want to fix this because this is a bit of an edge case.

fn insert_hint_area(&self, insert_hint: &InsertHint) -> Option<Rectangle<f64, Logical>> {
let mut hint_area = match insert_hint.position {
InsertPosition::NewColumn(column_index) => {
if column_index == 0 || column_index == self.columns.len() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hint at index 0 is rendered one gap further to the right than it should be (so it touches the next window).

@v3xro
Copy link

v3xro commented Sep 7, 2024

This now needs to be rebased to remove the gradient changes that were force-pushed to main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants