Adds a config option <snapping><dragResistance> with default value 20.
This prevents tiled/maximized windows from being unintentionally untiled.
SnapToEdge action for that edge. A *range* of 0 disables snapping via
interactive moves. Default is 1.
+*<snapping><dragResistance>*
+ Sets the movement of cursor in pixel required for a tiled or maximized
+ window to be moved with an interactive move. Default is 20.
+
*<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping to a window to an edge. Default is yes.
<snapping>
<!-- Set range to 0 to disable window snapping completely -->
<range>1</range>
+ <dragResistance>20</dragResistance>
<overlay enabled="yes">
<delay inner="500" outer="500" />
</overlay>
int snap_overlay_delay_outer;
bool snap_top_maximize;
enum tiling_events_mode snap_tiling_events_mode;
+ int snap_drag_resistance;
enum resize_indicator_mode resize_indicator;
bool resize_draw_contents;
/* 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 */
struct wlr_box grab_box;
uint32_t resize_edges;
*/
void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry);
+/**
+ * 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
+ * <snapping><dragResistance> and the view is un-tiled.
+ */
+bool interactive_move_tiled_view_to(struct server *server, struct view *view,
+ struct wlr_box *geometry);
+
void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges);
void interactive_finish(struct view *view);
void interactive_cancel(struct view *view);
} else {
wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient");
}
+ } else if (!strcasecmp(nodename, "dragResistance.snapping")) {
+ rc.snap_drag_resistance = atoi(content);
/* <windowSwitcher show="" preview="" outlines="" /> */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
rc.snap_overlay_delay_outer = 500;
rc.snap_top_maximize = true;
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
+ rc.snap_drag_resistance = 20;
rc.window_switcher.show = true;
rc.window_switcher.preview = true;
static void
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 <snapping><dragResistance>.
+ */
+ if (server->move_pending && !interactive_move_tiled_view_to(
+ server, server->grabbed_view, &server->grab_box)) {
+ return;
+ }
+
double dx = server->seat.cursor->x - server->grab_x;
double dy = server->seat.cursor->y - server->grab_y;
- struct view *view = server->grabbed_view;
/* Move the grabbed view to the new position. */
dx += server->grab_box.x;
// SPDX-License-Identifier: GPL-2.0-only
+#include <assert.h>
#include "edges.h"
#include "input/keyboard.h"
#include "labwc.h"
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 resistance = rc.snap_drag_resistance;
+
+ 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 < resistance * resistance) {
+ return false;
+ }
+ } else {
+ /* When called from interactive_begin() */
+ if (resistance > 0) {
+ return false;
+ }
+ }
+
+ view_set_shade(view, false);
+ view_set_untiled(view);
+ view_restore_to(view, *geometry);
+ server->move_pending = false;
+
+ return true;
+}
+
void
interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
{
interactive_anchor_to_cursor(view, &geometry);
}
- view_set_shade(view, false);
- view_set_untiled(view);
- view_restore_to(view, geometry);
+ /*
+ * If <snapping><dragResistance> 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 {
/* Store natural geometry at start of move */
view_store_natural_geometry(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);
if (server->input_mode == LAB_INPUT_STATE_MOVE
&& view == server->grabbed_view) {
/* Keep view underneath cursor */
+ /* TODO: resistance is not considered */
interactive_anchor_to_cursor(view, &view->pending);
/* Update grab offsets */
server->grab_x = server->seat.cursor->x;