From: tokyo4j Date: Wed, 7 Aug 2024 00:17:25 +0000 (+0900) Subject: interactive: allow moving horizontally/vertically maximized window X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=1f1bdad0873561d32c39674f5808931ef8880bec;p=proto%2Flabwc.git 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 . 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. --- diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 7436fc5f..e2c84116 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -330,6 +330,11 @@ this is for compatibility with Openbox. Sets the movement of cursor in pixel required for a tiled or maximized window to be moved with an interactive move. Default is 20. +** + Sets the one-dimentional movement of cursor in pixel required for a + *vertically or horizontally* maximized window to be moved with an + interactive move. Default is 150. + ## FOCUS ** [yes|no] diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f7bba998..cb95ac71 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -114,7 +114,10 @@ 20 20 + 20 + + 150 diff --git a/include/config/rcxml.h b/include/config/rcxml.h index cb56d473..80ce4df7 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -135,6 +135,7 @@ struct rcxml { int screen_edge_strength; int window_edge_strength; int unsnap_threshold; + int unmaximize_threshold; /* window snapping */ int snap_edge_range; diff --git a/include/labwc.h b/include/labwc.h index 810fed13..93cb4038 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -263,13 +263,6 @@ struct server { /* cursor interactive */ enum input_mode input_mode; struct view *grabbed_view; - /* - * When an interactive move is requested for tiled/maximized views by CSD - * clients or by Drag actions, the actual motion and untiling of the view - * can be delayed to prevent the view from being unintentionally untiled. - * During this delay, move_pending is set. - */ - bool move_pending; /* Cursor position when interactive move/resize is requested */ double grab_x, grab_y; /* View geometry when interactive move/resize is requested */ @@ -506,26 +499,15 @@ void seat_reset_pressed(struct seat *seat); void seat_output_layout_changed(struct seat *seat); /** - * interactive_anchor_to_cursor() - repositions the view to remain + * interactive_anchor_to_cursor() - repositions the geometry to remain * underneath the cursor when its size changes during interactive move. + * This function also resizes server->grab_box and repositions it to remain + * underneath server->grab_{x,y}. * - * geometry->{width,height} are provided by the caller. - * geometry->{x,y} are computed by this function. - * - * @note When is non-zero, cursor_x/y should be the original - * cursor position when the button was pressed. - */ -void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, - int cursor_x, int cursor_y); - -/** - * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the - * start of an interactive move or when an interactive move is pending. - * Returns true if the distance of cursor motion exceeds the value of - * and the view is un-tiled. + * geo->{width,height} are provided by the caller. + * geo->{x,y} are computed by this function. */ -bool interactive_move_tiled_view_to(struct server *server, struct view *view, - struct wlr_box *geometry); +void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); diff --git a/include/resistance.h b/include/resistance.h index 65b14869..6a155ceb 100644 --- a/include/resistance.h +++ b/include/resistance.h @@ -3,7 +3,12 @@ #define LABWC_RESISTANCE_H #include "labwc.h" -void resistance_move_apply(struct view *view, double *x, double *y); +/** + * resistance_unsnap_apply() - Apply resistance when dragging a + * maximized/tiled window. Returns true when the view needs to be un-tiled. + */ +bool resistance_unsnap_apply(struct view *view, int *x, int *y); +void resistance_move_apply(struct view *view, int *x, int *y); void resistance_resize_apply(struct view *view, struct wlr_box *new_view_geo); #endif /* LABWC_RESISTANCE_H */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c index d6ce92b6..007ae160 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1043,6 +1043,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.window_edge_strength = atoi(content); } else if (!strcasecmp(nodename, "unSnapThreshold.resistance")) { rc.unsnap_threshold = atoi(content); + } else if (!strcasecmp(nodename, "unMaximizeThreshold.resistance")) { + rc.unmaximize_threshold = atoi(content); } else if (!strcasecmp(nodename, "range.snapping")) { rc.snap_edge_range = atoi(content); } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) { @@ -1370,6 +1372,7 @@ rcxml_init(void) rc.screen_edge_strength = 20; rc.window_edge_strength = 20; rc.unsnap_threshold = 20; + rc.unmaximize_threshold = 150; rc.snap_edge_range = 1; rc.snap_overlay_enabled = true; diff --git a/src/input/cursor.c b/src/input/cursor.c index cd28cf44..4b76d75a 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -235,24 +235,34 @@ process_cursor_move(struct server *server, uint32_t time) { struct view *view = server->grabbed_view; - /* - * Un-tile the view when interactive move is delayed and the distance - * of cursor movement exceeds . - */ - if (server->move_pending && !interactive_move_tiled_view_to( - server, server->grabbed_view, &server->grab_box)) { - return; - } + int x = server->grab_box.x + (server->seat.cursor->x - server->grab_x); + int y = server->grab_box.y + (server->seat.cursor->y - server->grab_y); - double dx = server->seat.cursor->x - server->grab_x; - double dy = server->seat.cursor->y - server->grab_y; + /* Apply resistance for maximized/tiled view */ + bool needs_untile = resistance_unsnap_apply(view, &x, &y); + if (needs_untile) { + /* + * When the view needs to be un-tiled, resize it to natural + * geometry while anchoring it to cursor. 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. + */ + struct wlr_box new_geo = { + .width = view->natural_geometry.width, + .height = view->natural_geometry.height, + }; + interactive_anchor_to_cursor(server, &new_geo); + view_set_untiled(view); + view_restore_to(view, new_geo); + x = new_geo.x; + y = new_geo.y; + } - /* Move the grabbed view to the new position. */ - dx += server->grab_box.x; - dy += server->grab_box.y; - resistance_move_apply(view, &dx, &dy); - view_move(view, dx, dy); + /* Then apply window & edge resistance */ + resistance_move_apply(view, &x, &y); + view_move(view, x, y); overlay_update(&server->seat); } diff --git a/src/interactive.c b/src/interactive.c index 570d80fc..851e2231 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -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) -{ - 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) +interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) { - 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; } - - view_set_untiled(view); - view_restore_to(view, *geometry); - server->move_pending = false; - - return true; + /* 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; + + 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 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 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); diff --git a/src/regions.c b/src/regions.c index 1b265fe3..f52a5619 100644 --- a/src/regions.c +++ b/src/regions.c @@ -20,7 +20,8 @@ regions_should_snap(struct server *server) { if (server->input_mode != LAB_INPUT_STATE_MOVE || wl_list_empty(&rc.regions) - || server->seat.region_prevent_snap) { + || server->seat.region_prevent_snap + || !view_is_floating(server->grabbed_view)) { return false; } diff --git a/src/resistance.c b/src/resistance.c index ce2efbdd..a20f3b3c 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -91,8 +91,38 @@ check_edge_window(int *next, struct edge current, struct edge target, oppose, align, rc.window_edge_strength, lesser); } +bool +resistance_unsnap_apply(struct view *view, int *x, int *y) +{ + if (view_is_floating(view)) { + return false; + } + + int dx = *x - view->current.x; + int dy = *y - view->current.y; + if (view->maximized == VIEW_AXIS_HORIZONTAL) { + if (abs(dx) < rc.unmaximize_threshold) { + *x = view->current.x; + return false; + } + } else if (view->maximized == VIEW_AXIS_VERTICAL) { + if (abs(dy) < rc.unmaximize_threshold) { + *y = view->current.y; + return false; + } + } else { + if (dx * dx + dy * dy < rc.unsnap_threshold * rc.unsnap_threshold) { + *x = view->current.x; + *y = view->current.y; + return false; + } + } + + return true; +} + void -resistance_move_apply(struct view *view, double *x, double *y) +resistance_move_apply(struct view *view, int *x, int *y) { assert(view); diff --git a/src/xdg.c b/src/xdg.c index 23bceba2..b73ff84d 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -93,14 +93,8 @@ do_late_positioning(struct view *view) struct server *server = view->server; if (server->input_mode == LAB_INPUT_STATE_MOVE && view == server->grabbed_view) { - /* Anchor view to original grab position */ - interactive_anchor_to_cursor(view, &view->pending, - server->grab_x, server->grab_y); - /* Next update grab offsets */ - server->grab_box = view->pending; - /* Finally, move by same distance cursor has moved */ - view->pending.x += server->seat.cursor->x - server->grab_x; - view->pending.y += server->seat.cursor->y - server->grab_y; + /* Reposition the view while anchoring it to cursor */ + interactive_anchor_to_cursor(server, &view->pending); } else { /* TODO: smart placement? */ view_compute_centered_position(view, NULL,