Smart warp/swap for window drag actions
dominiklohmann opened this issue · 14 comments
Discussion / Feature request
Currently, dragging window A onto window B (1) swaps their position when they are on the same display, and (2) warps window A onto window B when are on different displays. (3) Dragging window A onto an empty desktop on another display moves window A to the other display.
This feature request aims to unify (1) and (2).
To do this, separate window B into 5 zones: A center rectangle, and four trapezoids by drawing a line from the rectangles corners to the window corners. See the mockup below.
Dragging onto the center rectangle should swap the windows, and dragging onto any of the trapezoids should warp onto the window in that direction.
Obviously some warps are swaps already, and warps that are no-ops (e.g. warp east from the eastward window that is the windows direct child) should be swaps instead.
The edges of the center rectangle should be at 25% and 75% of the total width and height of the window respectively.
An alternative design would be to have the trapezoids have a fixed width/height of maybe 50px, but at most 25% of the total window width/height.
Math to check for the positions is quite simple. Pseudocode:
# check if p1 and p2 are on the same side of the line drawn from a to b
function same_side(p1, p2, a, b):
return dot_product(
cross_product(b - a, p1 - a),
cross_product(b - a, p2 - a)) >= 0
# check if p is in the triangle with the corners a, b and c
function point_in_triangle(p, a, b, c):
return (same_side(p, a, b, c)
AND same_side(p, b, a, c)
AND same_side(p, c, a, b))
# check if p is in the center 50% of the frame
function point_in_inner_rectangle(p, frame):
return (p.x > frame.x + 0.25 * frame.w
AND p.x < frame.x + 0.75 * frame.w
AND p.y < frame.y + 0.25 * frame.h
AND p.y < frame.y + 0.75 * frame.h)
# check if p is in the top triangle, but not in the inner rectangle
function is_in_top_trapezoid(p, frame):
a.x, a.y = frame.x, frame.y
b.x, b.y = frame.x + frame.w, frame.y
c.x, c.y = frame.x + 0.5 * frame.w, frame.y + 0.5 * frame.y
return (NOT point_in_inner_rectangle(p, frame)
AND point_in_triangle(p, a, b, c))
Yeah, as a person who exclusively uses mouse to control the tiling windows, warping will be very, very helpful for my workflow.
I was playing with react-mosaic the other day and it looks like this might be some inspiration?̊̈
I was playing with react-mosaic the other day and it looks like this might be some inspiration?̊̈
This certainly looks nice. Only does warp, no swap like I suggested, and additionally does warp at outer edges of the space. Really nice example to play with.
I also like the overlay. A visual indicator like this might be nice to have.
Implemented on master, although there is no visual feedback as to which action is performed.
The center 25% -> 75% region (both width and height) will trigger a swap operation.
Any other section will be a warp in the appropriate direction.
I'll likely do some modifications to check whether the warp should actually be a swap etc before this goes public in a release. done.
This is working amazingly well so far.
One small thing I've noticed:
If I have two windows left/right of each other, dragging one onto the top/bottom trapezoid of the other window should warp instead of swapping the windows position.
Fixed last quirks on master.
I've wondered whether the same rules should apply for drags across displays. Currently that's always a warp.
It should follow the same rules across displays. I'll do a test and fix it if not, when I have time.
So the reason that it can only warp when movement happens across displays, is that I am stupid. Will fix later today, hopefully.
Edit: Fixed on latest master.
I had a great deal of trouble working this out. I was trying to drag it to the edge of the window, below. It might be worth sharing that zones image above in the docs?
How could i enable visual feedback? is there another project I can use or does this need to be coded into the project?
loving yabai 👍
I had a great deal of trouble working this out. I was trying to drag it to the edge of the window, below. It might be worth sharing that zones image above in the docs?
Totally agree. The "erratic" behaviour of dragged windows was driving me nuts... So the drop-zone for warp it is 25% space on all edges?
So the drop-sone for warp it is 25% space on all edges?
Yeah, that is correct. It works as shown in the picture in the first comment. I'll make a note to expand on this in the wiki, as I think it is not mentioned there at all.
@koekeishiya Might be a good idea to make that 0.25 configurable if that's feasible without much effort? Then users can set it to 0 if they want to always have the center action, or adjust it to some value that works better for them.
I wonder if it would be fine if there were some kind of visual feedback. I sometimes have issues eye-ing the separation of the drop-zones and drop the window slightly to the wrong side of the zone. Making the 25% configurable shouldn't be particularly hard.
I often find I'll miss the zone I want by just a tiny bit. A visual indicator would be a huge help; I recently used the tiling on PopOS which has visual indicators and it would be awesome to have here.