i3 - improved tiling WM


Movement into a branch considers movement direction

Patch status: merged

Patch by Tony Crisci

Long description:

Change the behavior of movement into a branch with respect to the
position the moving con will be placed within the branch when the
movement is complete.

The correct position is determined by the direction of movement or the
position of the focused-inactive container within the branch.

If the direction of movement is the same as the orientation of the
branch container, append or prepend the container to the branch in the
obvious way.  If the movement is to the right or downward, insert the
moving container in the first position (i.e., the leftmost or top
position resp.) If the movement is to the left or upward, insert the
moving container in the last position (i.e., the rightmost or bottom
position resp.)

If the direction of movement is different from the orientation of the
branch container, insert the container into the branch after the
focused-inactive container.

fixes #1060

To apply this patch, use:
curl http://cr.i3wm.org/patch/345/raw.patch | git am

b/src/move.c

35
@@ -125,7 +125,11 @@ static void move_to_output_directed(Con *con, direction_t direction) {
36
  *
37
  */
38
 void tree_move(int direction) {
39
+    position_t position;
40
+    Con *target;
41
+
42
     DLOG("Moving in direction %d\n", direction);
43
+
44
     /* 1: get the first parent with the same orientation */
45
     Con *con = focused;
46
 
47
@@ -173,7 +177,13 @@ void tree_move(int direction) {
48
                           TAILQ_PREV(con, nodes_head, nodes) :
49
                           TAILQ_NEXT(con, nodes)))) {
50
                 if (!con_is_leaf(swap)) {
51
-                    insert_con_into(con, con_descend_focused(swap), AFTER);
52
+                    DLOG("Moving into our bordering branch\n");
53
+                    target = con_descend_direction(swap, direction);
54
+                    position = (con_orientation(target->parent) != o ||
55
+                            direction == D_UP ||
56
+                            direction == D_LEFT ?
57
+                            AFTER : BEFORE);
58
+                    insert_con_into(con, target, position);
59
                     goto end;
60
                 }
61
                 if (direction == D_LEFT || direction == D_UP)
62
@@ -214,22 +224,24 @@ void tree_move(int direction) {
63
     }
64
 
65
     DLOG("above = %p\n", above);
66
-    Con *next;
67
-    position_t position;
68
-    if (direction == D_UP || direction == D_LEFT) {
69
-        position = BEFORE;
70
-        next = TAILQ_PREV(above, nodes_head, nodes);
71
-    } else {
72
-        position = AFTER;
73
-        next = TAILQ_NEXT(above, nodes);
74
-    }
75
 
76
-    /* special case: there is a split container in the direction we are moving
77
-     * to, so descend and append */
78
-    if (next && !con_is_leaf(next))
79
-        insert_con_into(con, con_descend_focused(next), AFTER);
80
-    else
81
+    Con *next = (direction == D_UP || direction == D_LEFT ?
82
+            TAILQ_PREV(above, nodes_head, nodes) :
83
+            TAILQ_NEXT(above, nodes));
84
+
85
+    if (next && !con_is_leaf(next)) {
86
+        DLOG("Moving into the bordering branch of our adjacent container\n");
87
+        target = con_descend_direction(next, direction);
88
+        position = (con_orientation(target->parent) != o ||
89
+                direction == D_UP ||
90
+                direction == D_LEFT ?
91
+                AFTER : BEFORE);
92
+        insert_con_into(con, target, position);
93
+    } else {
94
+        DLOG("Moving into container above\n");
95
+        position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
96
         insert_con_into(con, above, position);
97
+    }
98
 
99
 end:
100
     /* We need to call con_focus() to fix the focus stack "above" the container

b/testcases/t/213-move-branch-position.t

106
@@ -0,0 +1,376 @@
107
+#!perl
108
+# vim:ts=4:sw=4:expandtab
109
+#
110
+# Please read the following documents before working on tests:
111
+# • http://build.i3wm.org/docs/testsuite.html
112
+#   (or docs/testsuite)
113
+#
114
+# • http://build.i3wm.org/docs/lib-i3test.html
115
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
116
+#
117
+# • http://build.i3wm.org/docs/ipc.html
118
+#   (or docs/ipc)
119
+#
120
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
121
+#   (unless you are already familiar with Perl)
122
+#
123
+# Test that movement of a con into a branch will place the moving con at the
124
+# correct position within the branch.
125
+#
126
+# If the direction of movement is the same as the orientation of the branch
127
+# container, append or prepend the container to the branch in the obvious way.
128
+# If the movement is to the right or downward, insert the moving container in
129
+# the first position (i.e., the leftmost or top position resp.) If the movement
130
+# is to the left or upward, insert the moving container in the last position
131
+# (i.e., the rightmost or bottom position resp.)
132
+#
133
+# If the direction of movement is different from the orientation of the branch
134
+# container, insert the container into the branch after the focused-inactive
135
+# container.
136
+#
137
+# For testing purposes, we will demonstrate the behavior for tabbed containers
138
+# to represent the case of split-horizontal branches and stacked containers to
139
+# represent the case of split-vertical branches.
140
+#
141
+# Ticket: #1060
142
+# Bug still in: 4.6-109-g18cfc36
143
+
144
+use i3test;
145
+
146
+# Opens tabs on the presently focused branch and adds several additional
147
+# windows. Shifts focus to somewhere in the middle of the tabs so the most
148
+# general case can be assumed.
149
+sub open_tabs {
150
+    cmd 'layout tabbed';
151
+    open_window;
152
+    open_window;
153
+    open_window;
154
+    open_window;
155
+    cmd 'focus left; focus left'
156
+}
157
+
158
+# Likewise for a stack
159
+sub open_stack {
160
+    cmd 'layout stacking';
161
+    open_window;
162
+    open_window;
163
+    open_window;
164
+    open_window;
165
+    cmd 'focus up; focus up'
166
+}
167
+
168
+# Gets the position of the given leaf within the given branch. The first
169
+# position is one (1). Returns negative one (-1) if the leaf cannot be found
170
+# within the branch.
171
+sub get_leaf_position {
172
+    my ($branch, $leaf) = @_;
173
+    my $position = -1;
174
+    for my $i (0 .. @{$branch->{nodes}}) {
175
+        if ($branch->{nodes}[$i]->{id} == $leaf) {
176
+            $position = $i + 1;
177
+            last;
178
+        };
179
+    }
180
+    return $position;
181
+}
182
+
183
+# convenience function to focus a con by id to avoid having to type an ugly
184
+# command each time
185
+sub focus_con {
186
+    my $con_id = shift @_;
187
+    cmd "[con_id=\"$con_id\"] focus";
188
+}
189
+
190
+# Places a leaf into a branch and focuses the leaf. The newly created branch
191
+# will have orientation specified by the second parameter.
192
+sub branchify {
193
+    my ($con_id, $orientation) = @_;
194
+    focus_con($con_id);
195
+    $orientation eq 'horizontal' ? cmd 'splith' : cmd 'splitv';
196
+    open_window;
197
+    focus_con($con_id);
198
+}
199
+
200
+##############################################################################
201
+# When moving a con right into tabs, the moving con should be placed as the
202
+# first tab in the branch
203
+##############################################################################
204
+my $ws = fresh_workspace;
205
+
206
+# create the target leaf
207
+open_window;
208
+my $target_leaf = get_focused($ws);
209
+
210
+# create the tabbed branch container
211
+open_window;
212
+cmd 'splith';
213
+open_tabs;
214
+
215
+# move the target leaf into the tabbed branch
216
+focus_con($target_leaf);
217
+cmd 'move right';
218
+
219
+# the target leaf should be the first in the branch
220
+my $branch = shift @{get_ws_content($ws)};
221
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs placed it as the first tab in the branch');
222
+
223
+# repeat the test when the target is in a branch
224
+cmd 'move up; move left';
225
+branchify($target_leaf, 'vertical');
226
+cmd 'move right';
227
+
228
+$branch = pop @{get_ws_content($ws)};
229
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs from a branch placed it as the first tab in the branch');
230
+
231
+##############################################################################
232
+# When moving a con right into a stack, the moving con should be placed
233
+# below the focused-inactive leaf
234
+##############################################################################
235
+$ws = fresh_workspace;
236
+
237
+# create the target leaf
238
+open_window;
239
+$target_leaf = get_focused($ws);
240
+
241
+# create the stacked branch container and find the focused leaf
242
+open_window;
243
+cmd 'splith';
244
+open_stack;
245
+my $secondary_leaf = get_focused($ws);
246
+
247
+# move the target leaf into the stacked branch
248
+focus_con($target_leaf);
249
+cmd 'move right';
250
+
251
+# the secondary focus leaf should be below the target
252
+$branch = shift @{get_ws_content($ws)};
253
+my $target_leaf_position = get_leaf_position($branch, $target_leaf);
254
+my $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
255
+
256
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack placed it below the focused-inactive leaf');
257
+
258
+# repeat the test when the target is in a branch
259
+cmd 'move up; move left';
260
+branchify($target_leaf, 'vertical');
261
+cmd 'move right';
262
+
263
+$branch = pop @{get_ws_content($ws)};
264
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
265
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
266
+
267
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack from a branch placed it below the focused-inactive leaf');
268
+
269
+##############################################################################
270
+# When moving a con down into a stack, the moving con should be placed at the
271
+# top of the stack
272
+##############################################################################
273
+$ws = fresh_workspace;
274
+cmd 'layout splitv';
275
+
276
+# create the target leaf
277
+open_window;
278
+$target_leaf = get_focused($ws);
279
+
280
+# create the stacked branch container
281
+open_window;
282
+cmd 'splitv';
283
+open_stack;
284
+
285
+# move the target leaf into the stacked branch
286
+focus_con($target_leaf);
287
+cmd 'move down';
288
+
289
+# the target leaf should be on the top of the stack
290
+$branch = shift @{get_ws_content($ws)};
291
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack placed it on the top of the stack');
292
+
293
+# repeat the test when the target is in a branch
294
+cmd 'move right; move up';
295
+branchify($target_leaf, 'horizontal');
296
+cmd 'move down';
297
+
298
+$branch = pop @{get_ws_content($ws)};
299
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack from a branch placed it on the top of the stack');
300
+
301
+##############################################################################
302
+# When moving a con down into tabs, the moving con should be placed after the
303
+# focused-inactive tab
304
+##############################################################################
305
+$ws = fresh_workspace;
306
+cmd 'layout splitv';
307
+
308
+# create the target leaf
309
+open_window;
310
+$target_leaf = get_focused($ws);
311
+
312
+# create the tabbed branch container and find the focused tab
313
+open_window;
314
+cmd 'splitv';
315
+open_tabs;
316
+$secondary_leaf = get_focused($ws);
317
+
318
+# move the target leaf into the tabbed branch
319
+focus_con($target_leaf);
320
+cmd 'move down';
321
+
322
+# the secondary focus tab should be to the right
323
+$branch = shift @{get_ws_content($ws)};
324
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
325
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
326
+
327
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs placed it after the focused-inactive tab');
328
+
329
+# repeat the test when the target is in a branch
330
+cmd 'move right; move up';
331
+branchify($target_leaf, 'horizontal');
332
+cmd 'move down';
333
+
334
+$branch = pop @{get_ws_content($ws)};
335
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
336
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
337
+
338
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs from a branch placed it after the focused-inactive tab');
339
+
340
+##############################################################################
341
+# When moving a con left into tabs, the moving con should be placed as the last
342
+# tab in the branch
343
+##############################################################################
344
+$ws = fresh_workspace;
345
+
346
+# create the tabbed branch container
347
+open_window;
348
+cmd 'splith';
349
+open_tabs;
350
+
351
+# create the target leaf
352
+cmd 'focus parent';
353
+open_window;
354
+$target_leaf = get_focused($ws);
355
+
356
+# move the target leaf into the tabbed branch
357
+cmd 'move left';
358
+
359
+# the target leaf should be last in the branch
360
+$branch = shift @{get_ws_content($ws)};
361
+
362
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs placed it as the last tab in the branch');
363
+
364
+# repeat the test when the target leaf is in a branch
365
+cmd 'move up; move right';
366
+branchify($target_leaf, 'vertical');
367
+cmd 'move left';
368
+
369
+$branch = shift @{get_ws_content($ws)};
370
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs from a branch placed it as the last tab in the branch');
371
+
372
+##############################################################################
373
+# When moving a con left into a stack, the moving con should be placed below
374
+# the focused-inactive leaf
375
+##############################################################################
376
+$ws = fresh_workspace;
377
+
378
+# create the stacked branch container and find the focused leaf
379
+open_window;
380
+open_stack;
381
+$secondary_leaf = get_focused($ws);
382
+
383
+# create the target leaf to the right
384
+cmd 'focus parent';
385
+open_window;
386
+$target_leaf = get_focused($ws);
387
+
388
+# move the target leaf into the stacked branch
389
+cmd 'move left';
390
+
391
+# the secondary focus leaf should be below
392
+$branch = shift @{get_ws_content($ws)};
393
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
394
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
395
+
396
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack placed it below the focused-inactive leaf');
397
+
398
+# repeat the test when the target leaf is in a branch
399
+cmd 'move up; move right';
400
+branchify($target_leaf, 'vertical');
401
+cmd 'move left';
402
+
403
+$branch = shift @{get_ws_content($ws)};
404
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
405
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
406
+
407
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack from a branch placed it below the focused-inactive leaf');
408
+
409
+##############################################################################
410
+# When moving a con up into a stack, the moving con should be placed last in
411
+# the stack
412
+##############################################################################
413
+$ws = fresh_workspace;
414
+cmd 'layout splitv';
415
+
416
+# create the stacked branch container
417
+open_window;
418
+cmd 'splitv';
419
+open_stack;
420
+
421
+# create the target leaf
422
+cmd 'focus parent';
423
+open_window;
424
+$target_leaf = get_focused($ws);
425
+
426
+# move the target leaf into the stacked branch
427
+cmd 'move up';
428
+
429
+# the target leaf should be on the bottom of the stack
430
+$branch = shift @{get_ws_content($ws)};
431
+
432
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack placed it on the bottom of the stack');
433
+
434
+# repeat the test when the target leaf is in a branch
435
+cmd 'move right; move down';
436
+branchify($target_leaf, 'horizontal');
437
+cmd 'move up';
438
+
439
+$branch = shift @{get_ws_content($ws)};
440
+
441
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack from a branch placed it on the bottom of the stack');
442
+
443
+##############################################################################
444
+# When moving a con up into tabs, the moving con should be placed after the
445
+# focused-inactive tab
446
+##############################################################################
447
+$ws = fresh_workspace;
448
+cmd 'layout splitv';
449
+
450
+# create the tabbed branch container and find the focused leaf
451
+open_window;
452
+cmd 'splitv';
453
+open_tabs;
454
+$secondary_leaf = get_focused($ws);
455
+
456
+# create the target leaf below
457
+cmd 'focus parent';
458
+open_window;
459
+$target_leaf = get_focused($ws);
460
+
461
+# move the target leaf into the tabbed branch
462
+cmd 'move up';
463
+
464
+# the secondary focus tab should be to the right
465
+$branch = shift @{get_ws_content($ws)};
466
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
467
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
468
+
469
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs placed it after the focused-inactive tab');
470
+
471
+# repeat the test when the target leaf is in a branch
472
+cmd 'move right; move down';
473
+branchify($target_leaf, 'horizontal');
474
+cmd 'move up';
475
+
476
+$branch = shift @{get_ws_content($ws)};
477
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
478
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
479
+
480
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs from a branch placed it after the focused-inactive tab');
481
+
482
+done_testing;