aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@users.noreply.github.com>2022-10-18 22:10:03 +0200
committerGitHub <noreply@github.com>2022-10-18 22:10:03 +0200
commit5e759ed42458cad850dbff3b8b3d3a60f675571c (patch)
tree5b534e49a10225cd32b060cb94098022eeccfa82
parent941229ee6231fb881ed32d7d1236f3ed1604f298 (diff)
downloadi3-5e759ed42458cad850dbff3b8b3d3a60f675571c.tar.gz
i3-5e759ed42458cad850dbff3b8b3d3a60f675571c.zip
tiling drag: only start when there are drop targets (#5213)
This prevents potentially confusing drag & drop on fullscreen containers and only-containers on workspaces. fixes https://github.com/i3/i3/issues/5184
-rw-r--r--include/tiling_drag.h9
-rw-r--r--release-notes/changes/2-tiling-drag-targets1
-rw-r--r--src/click.c6
-rw-r--r--src/tiling_drag.c78
4 files changed, 76 insertions, 18 deletions
diff --git a/include/tiling_drag.h b/include/tiling_drag.h
index d78744c9..3091b734 100644
--- a/include/tiling_drag.h
+++ b/include/tiling_drag.h
@@ -9,6 +9,8 @@
*/
#pragma once
+#include "all.h"
+
/**
* Tiling drag initiation modes.
*/
@@ -20,6 +22,13 @@ typedef enum {
} tiling_drag_t;
/**
+ * Returns whether there currently are any drop targets.
+ * Used to only initiate a drag when there is something to drop onto.
+ *
+ */
+bool has_drop_targets(void);
+
+/**
* Initiates a mouse drag operation on a tiled window.
*
*/
diff --git a/release-notes/changes/2-tiling-drag-targets b/release-notes/changes/2-tiling-drag-targets
new file mode 100644
index 00000000..9981a199
--- /dev/null
+++ b/release-notes/changes/2-tiling-drag-targets
@@ -0,0 +1 @@
+tiling drag: only initiate when there are drop targets
diff --git a/src/click.c b/src/click.c
index 6f1cb5c2..e262516f 100644
--- a/src/click.c
+++ b/src/click.c
@@ -230,7 +230,8 @@ static void route_click(Con *con, xcb_button_press_event_t *event, const bool mo
/* 2: floating modifier pressed, initiate a drag */
if (mod_pressed && is_left_click && !floatingcon &&
(config.tiling_drag == TILING_DRAG_MODIFIER ||
- config.tiling_drag == TILING_DRAG_MODIFIER_OR_TITLEBAR)) {
+ config.tiling_drag == TILING_DRAG_MODIFIER_OR_TITLEBAR) &&
+ has_drop_targets()) {
const bool use_threshold = !mod_pressed;
tiling_drag(con, event, use_threshold);
allow_replay_pointer(event->time);
@@ -311,7 +312,8 @@ static void route_click(Con *con, xcb_button_press_event_t *event, const bool mo
if (is_left_click &&
((config.tiling_drag == TILING_DRAG_TITLEBAR && dest == CLICK_DECORATION) ||
(config.tiling_drag == TILING_DRAG_MODIFIER_OR_TITLEBAR &&
- (mod_pressed || dest == CLICK_DECORATION)))) {
+ (mod_pressed || dest == CLICK_DECORATION))) &&
+ has_drop_targets()) {
allow_replay_pointer(event->time);
const bool use_threshold = !mod_pressed;
tiling_drag(con, event, use_threshold);
diff --git a/src/tiling_drag.c b/src/tiling_drag.c
index 94908872..a35a66e6 100644
--- a/src/tiling_drag.c
+++ b/src/tiling_drag.c
@@ -27,6 +27,62 @@ static Rect con_rect_plus_deco_height(Con *con) {
return rect;
}
+static bool is_tiling_drop_target(Con *con) {
+ if (!con_has_managed_window(con) ||
+ con_is_floating(con) ||
+ con_is_hidden(con)) {
+ return false;
+ }
+ Con *ws = con_get_workspace(con);
+ if (con_is_internal(ws)) {
+ /* Skip containers on i3-internal containers like the scratchpad, which are
+ technically visible on their pseudo-output. */
+ return false;
+ }
+ if (!workspace_is_visible(ws)) {
+ return false;
+ }
+ Con *fs = con_get_fullscreen_covering_ws(ws);
+ if (fs != NULL && fs != con) {
+ /* Workspace is visible, but con is not visible because some other
+ container is in fullscreen. */
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Returns whether there currently are any drop targets.
+ * Used to only initiate a drag when there is something to drop onto.
+ *
+ */
+bool has_drop_targets(void) {
+ int drop_targets = 0;
+ Con *con;
+ TAILQ_FOREACH (con, &all_cons, all_cons) {
+ if (!is_tiling_drop_target(con)) {
+ continue;
+ }
+ drop_targets++;
+ }
+
+ /* In addition to tiling containers themselves, an visible but empty
+ * workspace (in a multi-monitor scenario) also is a drop target. */
+ Con *output;
+ TAILQ_FOREACH (output, &(croot->focus_head), focused) {
+ if (con_is_internal(output)) {
+ continue;
+ }
+ Con *visible_ws = NULL;
+ GREP_FIRST(visible_ws, output_get_content(output), workspace_is_visible(child));
+ if (visible_ws != NULL && con_num_children(visible_ws) == 0) {
+ drop_targets++;
+ }
+ }
+
+ return drop_targets > 1;
+}
+
/*
* Return an appropriate target at given coordinates.
*
@@ -35,23 +91,13 @@ static Con *find_drop_target(uint32_t x, uint32_t y) {
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) {
Rect rect = con_rect_plus_deco_height(con);
-
- if (rect_contains(rect, x, y) &&
- con_has_managed_window(con) &&
- !con_is_floating(con) &&
- !con_is_hidden(con)) {
- Con *ws = con_get_workspace(con);
- if (strcmp(ws->name, "__i3_scratch") == 0) {
- /* Skip containers on the scratchpad, which are technically
- visible on their pseudo-output. */
- continue;
- }
- if (!workspace_is_visible(ws)) {
- continue;
- }
- Con *fs = con_get_fullscreen_covering_ws(ws);
- return fs ? fs : con;
+ if (!rect_contains(rect, x, y) ||
+ !is_tiling_drop_target(con)) {
+ continue;
}
+ Con *ws = con_get_workspace(con);
+ Con *fs = con_get_fullscreen_covering_ws(ws);
+ return fs ? fs : con;
}
/* Couldn't find leaf container, get a workspace. */