interactive: allow moving horizontally/vertically maximized window

Applies drag resistance unidirectionally for horizontally/vertically
maximized windows, allowing them to be dragged without being untiled
immediately. When the distance of cursor movement orthogonal to the
maximized direction exceeds <resistance><unMaximizeThreshold>.

While dragging a horizontally/vertically maximized window, edge/region
snapping is disabled to prevent unintentional snapping and overlays.

This commit also includes some refactoring to simplify the logic.
This commit is contained in:
tokyo4j 2024-08-07 09:17:25 +09:00 committed by Consolatis
parent 2e19bd4d5b
commit 1f1bdad087
11 changed files with 137 additions and 120 deletions

View file

@ -9,58 +9,47 @@
#include "view.h"
#include "window-rules.h"
/*
* pos_old pos_cursor
* v v
* +---------+-------------------+
* <-----------size_old---------->
*
* return value
* v
* +----+---------+
* <---size_new--->
*/
static int
max_move_scale(double pos_cursor, double pos_current,
double size_current, double size_orig)
max_move_scale(double pos_cursor, double pos_old, double size_old,
double size_new)
{
double anchor_frac = (pos_cursor - pos_current) / size_current;
int pos_new = pos_cursor - (size_orig * anchor_frac);
if (pos_new < pos_current) {
double anchor_frac = (pos_cursor - pos_old) / size_old;
int pos_new = pos_cursor - (size_new * anchor_frac);
if (pos_new < pos_old) {
/* Clamp by using the old offsets of the maximized window */
pos_new = pos_current;
pos_new = pos_old;
}
return pos_new;
}
void
interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry,
int cursor_x, int cursor_y)
interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
{
geometry->x = max_move_scale(cursor_x, view->current.x,
view->current.width, geometry->width);
geometry->y = max_move_scale(cursor_y, view->current.y,
view->current.height, geometry->height);
}
bool
interactive_move_tiled_view_to(struct server *server, struct view *view,
struct wlr_box *geometry)
{
assert(!view_is_floating(view));
int threshold = rc.unsnap_threshold;
if (server->input_mode == LAB_INPUT_STATE_MOVE) {
/* When called from cursor motion handler */
assert(server->move_pending && server->grabbed_view == view);
double dx = server->seat.cursor->x - server->grab_x;
double dy = server->seat.cursor->y - server->grab_y;
if (dx * dx + dy * dy < threshold * threshold) {
return false;
}
} else {
/* When called from interactive_begin() */
if (threshold > 0) {
return false;
}
assert(server->input_mode == LAB_INPUT_STATE_MOVE);
if (wlr_box_empty(geo)) {
return;
}
/* Resize grab_box while anchoring it to grab_box.{x,y} */
server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x,
server->grab_box.width, geo->width);
server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y,
server->grab_box.height, geo->height);
server->grab_box.width = geo->width;
server->grab_box.height = geo->height;
view_set_untiled(view);
view_restore_to(view, *geometry);
server->move_pending = false;
return true;
geo->x = server->grab_box.x + (server->seat.cursor->x - server->grab_x);
geo->y = server->grab_box.y + (server->seat.cursor->y - server->grab_y);
}
void
@ -73,7 +62,6 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
*/
struct server *server = view->server;
struct seat *seat = &server->seat;
struct wlr_box geometry = view->current;
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
@ -97,31 +85,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
*/
return;
}
if (!view_is_floating(view)) {
/*
* Un-maximize and restore natural width/height.
* If the natural geometry is unknown (possible
* with xdg-shell views), then we set a size of
* 0x0 here and determine the correct geometry
* later. See do_late_positioning() in xdg.c.
*/
geometry.width = view->natural_geometry.width;
geometry.height = view->natural_geometry.height;
if (!wlr_box_empty(&geometry)) {
interactive_anchor_to_cursor(view, &geometry,
seat->cursor->x, seat->cursor->y);
}
/*
* If <resistance><unSnapThreshold> is non-zero, the
* tiled/maximized view is un-tiled later in cursor
* motion handler.
*/
if (!interactive_move_tiled_view_to(
server, view, &geometry)) {
server->move_pending = true;
}
} else {
if (view_is_floating(view)) {
/* Store natural geometry at start of move */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
@ -151,7 +115,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
/*
* If tiled or maximized in only one direction, reset
* tiled/maximized state but keep the same geometry as
* maximized/tiled state but keep the same geometry as
* the starting point for the resize.
*/
view_set_untiled(view);
@ -168,8 +132,24 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
/* Remember view and cursor positions at start of move/resize */
server->grab_x = seat->cursor->x;
server->grab_y = seat->cursor->y;
server->grab_box = geometry;
server->grab_box = view->current;
server->resize_edges = edges;
/*
* Un-tile maximized/tiled view immediately if <unSnapThreshold> is
* zero. Otherwise, un-tile it later in cursor motion handler.
* If the natural geometry is unknown (possible with xdg-shell views),
* then we set a size of 0x0 here and determine the correct geometry
* later. See do_late_positioning() in xdg.c.
*/
if (mode == LAB_INPUT_STATE_MOVE && !view_is_floating(view)
&& rc.unsnap_threshold <= 0) {
struct wlr_box natural_geo = view->natural_geometry;
interactive_anchor_to_cursor(server, &natural_geo);
view_set_untiled(view);
view_restore_to(view, natural_geo);
}
if (rc.resize_indicator) {
resize_indicator_show(view);
}
@ -181,6 +161,10 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
enum view_edge
edge_from_cursor(struct seat *seat, struct output **dest_output)
{
if (!view_is_floating(seat->server->grabbed_view)) {
return VIEW_EDGE_INVALID;
}
int snap_range = rc.snap_edge_range;
if (!snap_range) {
return VIEW_EDGE_INVALID;
@ -296,7 +280,6 @@ interactive_cancel(struct view *view)
view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
view->server->grabbed_view = NULL;
view->server->move_pending = false;
/* Update focus/cursor image */
cursor_update_focus(view->server);