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
This commit is contained in:
Michael Stapelberg 2022-10-18 22:10:03 +02:00 committed by GitHub
parent 941229ee62
commit 5e759ed424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 18 deletions

View File

@ -9,6 +9,8 @@
*/ */
#pragma once #pragma once
#include "all.h"
/** /**
* Tiling drag initiation modes. * Tiling drag initiation modes.
*/ */
@ -19,6 +21,13 @@ typedef enum {
TILING_DRAG_MODIFIER_OR_TITLEBAR = 3 TILING_DRAG_MODIFIER_OR_TITLEBAR = 3
} tiling_drag_t; } 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. * Initiates a mouse drag operation on a tiled window.
* *

View File

@ -0,0 +1 @@
tiling drag: only initiate when there are drop targets

View File

@ -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 */ /* 2: floating modifier pressed, initiate a drag */
if (mod_pressed && is_left_click && !floatingcon && if (mod_pressed && is_left_click && !floatingcon &&
(config.tiling_drag == TILING_DRAG_MODIFIER || (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; const bool use_threshold = !mod_pressed;
tiling_drag(con, event, use_threshold); tiling_drag(con, event, use_threshold);
allow_replay_pointer(event->time); 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 && if (is_left_click &&
((config.tiling_drag == TILING_DRAG_TITLEBAR && dest == CLICK_DECORATION) || ((config.tiling_drag == TILING_DRAG_TITLEBAR && dest == CLICK_DECORATION) ||
(config.tiling_drag == TILING_DRAG_MODIFIER_OR_TITLEBAR && (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); allow_replay_pointer(event->time);
const bool use_threshold = !mod_pressed; const bool use_threshold = !mod_pressed;
tiling_drag(con, event, use_threshold); tiling_drag(con, event, use_threshold);

View File

@ -27,6 +27,62 @@ static Rect con_rect_plus_deco_height(Con *con) {
return rect; 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. * Return an appropriate target at given coordinates.
* *
@ -35,23 +91,13 @@ static Con *find_drop_target(uint32_t x, uint32_t y) {
Con *con; Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) { TAILQ_FOREACH (con, &all_cons, all_cons) {
Rect rect = con_rect_plus_deco_height(con); Rect rect = con_rect_plus_deco_height(con);
if (!rect_contains(rect, x, y) ||
if (rect_contains(rect, x, y) && !is_tiling_drop_target(con)) {
con_has_managed_window(con) && continue;
!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;
} }
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. */ /* Couldn't find leaf container, get a workspace. */