Sets the movement of cursor in pixel required for a tiled or maximized
window to be moved with an interactive move. Default is 20.
+*<resistance><unMaximizeThreshold>*
+ 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
*<focus><followMouse>* [yes|no]
<resistance>
<screenEdgeStrength>20</screenEdgeStrength>
<windowEdgeStrength>20</windowEdgeStrength>
+ <!-- resistance for maximized/tiled windows -->
<unSnapThreshold>20</unSnapThreshold>
+ <!-- resistance for vertically/horizontally maximized windows -->
+ <unMaximizeThreshold>150</unMaximizeThreshold>
</resistance>
<resize>
int screen_edge_strength;
int window_edge_strength;
int unsnap_threshold;
+ int unmaximize_threshold;
/* window snapping */
int snap_edge_range;
/* 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 */
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 <unSnapThreshold> 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
- * <resistance><unSnapThreshold> 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);
#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 */
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")) {
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;
{
struct view *view = server->grabbed_view;
- /*
- * Un-tile the view when interactive move is delayed and the distance
- * of cursor movement exceeds <resistance><unSnapThreshold>.
- */
- 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);
}
#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
*/
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;
*/
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);
/*
* 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);
/* 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);
}
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;
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);
{
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;
}
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);
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,