diff options
author | Michael Stapelberg <stapelberg@users.noreply.github.com> | 2022-10-18 22:10:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-18 22:10:03 +0200 |
commit | 5e759ed42458cad850dbff3b8b3d3a60f675571c (patch) | |
tree | 5b534e49a10225cd32b060cb94098022eeccfa82 | |
parent | 941229ee6231fb881ed32d7d1236f3ed1604f298 (diff) | |
download | i3-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.h | 9 | ||||
-rw-r--r-- | release-notes/changes/2-tiling-drag-targets | 1 | ||||
-rw-r--r-- | src/click.c | 6 | ||||
-rw-r--r-- | src/tiling_drag.c | 78 |
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. */ |