aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/seatop_move_tiling.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/seatop_move_tiling.c')
-rw-r--r--sway/input/seatop_move_tiling.c132
1 files changed, 120 insertions, 12 deletions
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 446612c6..223c6c08 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -16,6 +16,10 @@
16// Thickness of the dropzone when dragging to the edge of a layout container 16// Thickness of the dropzone when dragging to the edge of a layout container
17#define DROP_LAYOUT_BORDER 30 17#define DROP_LAYOUT_BORDER 30
18 18
19// Thickness of indicator when dropping onto a titlebar. This should be a
20// multiple of 2.
21#define DROP_SPLIT_INDICATOR 10
22
19struct seatop_move_tiling_event { 23struct seatop_move_tiling_event {
20 struct sway_container *con; 24 struct sway_container *con;
21 struct sway_node *target_node; 25 struct sway_node *target_node;
@@ -23,6 +27,8 @@ struct seatop_move_tiling_event {
23 struct wlr_box drop_box; 27 struct wlr_box drop_box;
24 double ref_lx, ref_ly; // cursor's x/y at start of op 28 double ref_lx, ref_ly; // cursor's x/y at start of op
25 bool threshold_reached; 29 bool threshold_reached;
30 bool split_target;
31 bool insert_after_target;
26}; 32};
27 33
28static void handle_render(struct sway_seat *seat, 34static void handle_render(struct sway_seat *seat,
@@ -92,8 +98,76 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge,
92 } 98 }
93} 99}
94 100
101static void split_border(double pos, int offset, int len, int n_children,
102 int avoid, int *out_pos, bool *out_after) {
103 int region = 2 * n_children * (pos - offset) / len;
104 // If the cursor is over the right side of a left-adjacent titlebar, or the
105 // left side of a right-adjacent titlebar, it's position when dropped will
106 // be the same. To avoid this, shift the region for adjacent containers.
107 if (avoid >= 0) {
108 if (region == 2 * avoid - 1 || region == 2 * avoid) {
109 region--;
110 } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) {
111 region++;
112 }
113 }
114
115 int child_index = (region + 1) / 2;
116 *out_after = region % 2;
117 // When dropping at the beginning or end of a container, show the drop
118 // region within the container boundary, otherwise show it on top of the
119 // border between two titlebars.
120 if (child_index == 0) {
121 *out_pos = offset;
122 } else if (child_index == n_children) {
123 *out_pos = offset + len - DROP_SPLIT_INDICATOR;
124 } else {
125 *out_pos = offset + child_index * len / n_children -
126 DROP_SPLIT_INDICATOR / 2;
127 }
128}
129
130static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
131 struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) {
132 struct sway_container *con = node->sway_container;
133 struct sway_node *parent = &con->pending.parent->node;
134 int title_height = container_titlebar_height();
135 struct wlr_box box;
136 int n_children, avoid_index;
137 enum sway_container_layout layout =
138 parent ? node_get_layout(parent) : L_NONE;
139 if (layout == L_TABBED || layout == L_STACKED) {
140 node_get_box(parent, &box);
141 n_children = node_get_children(parent)->length;
142 avoid_index = list_find(node_get_children(parent), avoid);
143 } else {
144 node_get_box(node, &box);
145 n_children = 1;
146 avoid_index = -1;
147 }
148 if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) {
149 // Drop into stacked titlebars.
150 title_box->width = box.width;
151 title_box->height = DROP_SPLIT_INDICATOR;
152 title_box->x = box.x;
153 split_border(cursor->y, box.y, title_height * n_children,
154 n_children, avoid_index, &title_box->y, after);
155 return true;
156 } else if (layout != L_STACKED && cursor->y < box.y + title_height) {
157 // Drop into side-by-side titlebars.
158 title_box->width = DROP_SPLIT_INDICATOR;
159 title_box->height = title_height;
160 title_box->y = box.y;
161 split_border(cursor->x, box.x, box.width, n_children,
162 avoid_index, &title_box->x, after);
163 return true;
164 }
165 return false;
166}
167
95static void handle_motion_postthreshold(struct sway_seat *seat) { 168static void handle_motion_postthreshold(struct sway_seat *seat) {
96 struct seatop_move_tiling_event *e = seat->seatop_data; 169 struct seatop_move_tiling_event *e = seat->seatop_data;
170 e->split_target = false;
97 struct wlr_surface *surface = NULL; 171 struct wlr_surface *surface = NULL;
98 double sx, sy; 172 double sx, sy;
99 struct sway_cursor *cursor = seat->cursor; 173 struct sway_cursor *cursor = seat->cursor;
@@ -127,27 +201,53 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
127 return; 201 return;
128 } 202 }
129 203
204 // Check if the cursor is over a tilebar only if the destination
205 // container is not a descendant of the source container.
206 if (!surface && !container_has_ancestor(con, e->con) &&
207 split_titlebar(node, e->con, cursor->cursor,
208 &e->drop_box, &e->insert_after_target)) {
209 // Don't allow dropping over the source container's titlebar
210 // to give users a chance to cancel a drag operation.
211 if (con == e->con) {
212 e->target_node = NULL;
213 } else {
214 e->target_node = node;
215 e->split_target = true;
216 }
217 e->target_edge = WLR_EDGE_NONE;
218 return;
219 }
220
130 // Traverse the ancestors, trying to find a layout container perpendicular 221 // Traverse the ancestors, trying to find a layout container perpendicular
131 // to the edge. Eg. close to the top or bottom of a horiz layout. 222 // to the edge. Eg. close to the top or bottom of a horiz layout.
223 int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER;
224 int thresh_bottom = con->pending.content_y +
225 con->pending.content_height - DROP_LAYOUT_BORDER;
226 int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER;
227 int thresh_right = con->pending.content_x +
228 con->pending.content_width - DROP_LAYOUT_BORDER;
132 while (con) { 229 while (con) {
133 enum wlr_edges edge = WLR_EDGE_NONE; 230 enum wlr_edges edge = WLR_EDGE_NONE;
134 enum sway_container_layout layout = container_parent_layout(con); 231 enum sway_container_layout layout = container_parent_layout(con);
135 struct wlr_box parent; 232 struct wlr_box box;
136 con->pending.parent ? container_get_box(con->pending.parent, &parent) : 233 node_get_box(node_get_parent(&con->node), &box);
137 workspace_get_box(con->pending.workspace, &parent);
138 if (layout == L_HORIZ || layout == L_TABBED) { 234 if (layout == L_HORIZ || layout == L_TABBED) {
139 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { 235 if (cursor->cursor->y < thresh_top) {
140 edge = WLR_EDGE_TOP; 236 edge = WLR_EDGE_TOP;
141 } else if (cursor->cursor->y > parent.y + parent.height 237 box.height = thresh_top - box.y;
142 - DROP_LAYOUT_BORDER) { 238 } else if (cursor->cursor->y > thresh_bottom) {
143 edge = WLR_EDGE_BOTTOM; 239 edge = WLR_EDGE_BOTTOM;
240 box.height = box.y + box.height - thresh_bottom;
241 box.y = thresh_bottom;
144 } 242 }
145 } else if (layout == L_VERT || layout == L_STACKED) { 243 } else if (layout == L_VERT || layout == L_STACKED) {
146 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { 244 if (cursor->cursor->x < thresh_left) {
147 edge = WLR_EDGE_LEFT; 245 edge = WLR_EDGE_LEFT;
148 } else if (cursor->cursor->x > parent.x + parent.width 246 box.width = thresh_left - box.x;
149 - DROP_LAYOUT_BORDER) { 247 } else if (cursor->cursor->x > thresh_right) {
150 edge = WLR_EDGE_RIGHT; 248 edge = WLR_EDGE_RIGHT;
249 box.width = box.x + box.width - thresh_right;
250 box.x = thresh_right;
151 } 251 }
152 } 252 }
153 if (edge) { 253 if (edge) {
@@ -156,8 +256,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
156 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
157 } 257 }
158 e->target_edge = edge; 258 e->target_edge = edge;
159 node_get_box(e->target_node, &e->drop_box); 259 e->drop_box = box;
160 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
161 desktop_damage_box(&e->drop_box); 260 desktop_damage_box(&e->drop_box);
162 return; 261 return;
163 } 262 }
@@ -241,7 +340,8 @@ static void finalize_move(struct sway_seat *seat) {
241 target_node->sway_workspace : target_node->sway_container->pending.workspace; 340 target_node->sway_workspace : target_node->sway_container->pending.workspace;
242 enum wlr_edges edge = e->target_edge; 341 enum wlr_edges edge = e->target_edge;
243 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; 342 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
244 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; 343 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER &&
344 !e->split_target;
245 345
246 if (!swap) { 346 if (!swap) {
247 container_detach(con); 347 container_detach(con);
@@ -250,6 +350,14 @@ static void finalize_move(struct sway_seat *seat) {
250 // Moving container into empty workspace 350 // Moving container into empty workspace
251 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { 351 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
252 con = workspace_add_tiling(new_ws, con); 352 con = workspace_add_tiling(new_ws, con);
353 } else if (e->split_target) {
354 struct sway_container *target = target_node->sway_container;
355 enum sway_container_layout layout = container_parent_layout(target);
356 if (layout != L_TABBED && layout != L_STACKED) {
357 container_split(target, L_TABBED);
358 }
359 container_add_sibling(target, con, e->insert_after_target);
360 ipc_event_window(con, "move");
253 } else if (target_node->type == N_CONTAINER) { 361 } else if (target_node->type == N_CONTAINER) {
254 // Moving container before/after another 362 // Moving container before/after another
255 struct sway_container *target = target_node->sway_container; 363 struct sway_container *target = target_node->sway_container;