@@ -5,16 +5,18 @@ import * as lib from 'lib';
5
5
import * as log from 'log' ;
6
6
import * as node from 'node' ;
7
7
import * as result from 'result' ;
8
+ import * as stack from 'stack' ;
8
9
9
10
import type { Entity } from 'ecs' ;
10
11
import type { Ext } from 'extension' ;
11
12
import type { Forest } from 'forest' ;
12
13
import type { Fork } from 'fork' ;
13
14
import type { Rectangle } from 'rectangle' ;
14
15
import type { Result } from 'result' ;
15
- import type { ShellWindow } from 'window' ;
16
+ import type { ShellWindow } from 'window' ;
16
17
17
- const { Ok, Err, ERR } = result ;
18
+ const { Stack } = stack ;
19
+ const { Ok, Err, ERR } = result ;
18
20
const { NodeKind} = node ;
19
21
const Tags = Me . imports . tags ;
20
22
@@ -32,19 +34,63 @@ export class AutoTiler {
32
34
* Call this when a window has swapped positions with another, so that we
33
35
* may update the associations in the auto-tiler world.
34
36
*/
35
- attach_swap ( a : Entity , b : Entity ) {
36
- const a_ent = this . attached . remove ( a ) ;
37
- const b_ent = this . attached . remove ( b ) ;
37
+ attach_swap ( ext : Ext , a : Entity , b : Entity ) {
38
+ const a_ent = this . attached . get ( a ) ,
39
+ b_ent = this . attached . get ( b ) ;
38
40
39
- if ( a_ent ) {
40
- this . forest . forks . with ( a_ent , ( fork ) => fork . replace_window ( a , b ) ) ;
41
- this . attached . insert ( b , a_ent ) ;
42
- }
41
+ let a_win = ext . windows . get ( a ) ,
42
+ b_win = ext . windows . get ( b ) ;
43
+
44
+ if ( ! a_ent || ! b_ent || ! a_win || ! b_win ) return ;
45
+
46
+ const a_fork = this . forest . forks . get ( a_ent ) ,
47
+ b_fork = this . forest . forks . get ( b_ent ) ;
48
+
49
+ if ( ! a_fork || ! b_fork ) return ;
43
50
44
- if ( b_ent ) {
45
- this . forest . forks . with ( b_ent , ( fork ) => fork . replace_window ( b , a ) ) ;
46
- this . attached . insert ( a , b_ent ) ;
51
+ const a_stack = a_win . stack , b_stack = b_win . stack ;
52
+
53
+ if ( ext . auto_tiler ) {
54
+ if ( a_win . stack !== null ) {
55
+ const stack = ext . auto_tiler . forest . stacks . get ( a_win . stack ) ;
56
+ if ( stack ) {
57
+ a = stack . active ;
58
+ a_win = ext . windows . get ( a ) ;
59
+ if ( ! a_win ) return ;
60
+
61
+ stack . deactivate ( a_win ) ;
62
+ }
63
+ }
64
+
65
+ if ( b_win . stack !== null ) {
66
+ const stack = ext . auto_tiler . forest . stacks . get ( b_win . stack ) ;
67
+ if ( stack ) {
68
+ b = stack . active ;
69
+ b_win = ext . windows . get ( b ) ;
70
+ if ( ! b_win ) return ;
71
+
72
+ stack . deactivate ( b_win ) ;
73
+ }
74
+ }
47
75
}
76
+
77
+ const a_fn = a_fork . replace_window ( ext , a_win , b_win ) ;
78
+ this . attached . insert ( b , a_ent ) ;
79
+
80
+ const b_fn = b_fork . replace_window ( ext , b_win , a_win ) ;
81
+ this . attached . insert ( a , b_ent ) ;
82
+
83
+ if ( a_fn ) a_fn ( ) ;
84
+ if ( b_fn ) b_fn ( ) ;
85
+
86
+ a_win . stack = b_stack ;
87
+ b_win . stack = a_stack ;
88
+
89
+ a_win . meta . get_compositor_private ( ) ?. show ( ) ;
90
+ b_win . meta . get_compositor_private ( ) ?. show ( ) ;
91
+
92
+ this . tile ( ext , a_fork , a_fork . area ) ;
93
+ this . tile ( ext , b_fork , b_fork . area ) ;
48
94
}
49
95
50
96
update_toplevel ( ext : Ext , fork : Fork , monitor : number , smart_gaps : boolean ) {
@@ -93,8 +139,8 @@ export class AutoTiler {
93
139
}
94
140
95
141
/** Tiles a window into another */
96
- attach_to_window ( ext : Ext , attachee : ShellWindow , attacher : ShellWindow , cursor : Rectangle ) {
97
- let attached = this . forest . attach_window ( ext , attachee . entity , attacher . entity , cursor ) ;
142
+ attach_to_window ( ext : Ext , attachee : ShellWindow , attacher : ShellWindow , cursor : Rectangle , stack_from_left : boolean = true ) {
143
+ let attached = this . forest . attach_window ( ext , attachee . entity , attacher . entity , cursor , stack_from_left ) ;
98
144
99
145
if ( attached ) {
100
146
const [ , fork ] = attached ;
@@ -155,10 +201,17 @@ export class AutoTiler {
155
201
}
156
202
}
157
203
204
+ /** Destroy all widgets owned by this object. Call before dropping. */
205
+ destroy ( ) {
206
+ for ( const stack of this . forest . stacks . values ( ) ) stack . destroy ( ) ;
207
+
208
+ this . forest . stacks . truncate ( 0 ) ;
209
+ }
210
+
158
211
/** Detaches the window from a tiling branch, if it is attached to one. */
159
212
detach_window ( ext : Ext , win : Entity ) {
160
213
this . attached . take_with ( win , ( prev_fork : Entity ) => {
161
- const reflow_fork = this . forest . detach ( prev_fork , win ) ;
214
+ const reflow_fork = this . forest . detach ( ext , prev_fork , win ) ;
162
215
163
216
if ( reflow_fork ) {
164
217
const fork = reflow_fork [ 1 ] ;
@@ -187,21 +240,21 @@ export class AutoTiler {
187
240
const fork = this . forest . forks . get ( fork_entity ) ;
188
241
189
242
if ( fork ) {
190
- if ( fork . left . kind == NodeKind . WINDOW && fork . right && fork . right . kind == NodeKind . WINDOW ) {
243
+ if ( fork . left . inner . kind === 2 && fork . right && fork . right . inner . kind === 2 ) {
191
244
if ( fork . left . is_window ( win ) ) {
192
- const sibling = ext . windows . get ( fork . right . entity ) ;
245
+ const sibling = ext . windows . get ( fork . right . inner . entity ) ;
193
246
if ( sibling && sibling . rect ( ) . contains ( cursor ) ) {
194
- fork . left . entity = fork . right . entity ;
195
- fork . right . entity = win ;
247
+ fork . left . inner . entity = fork . right . inner . entity ;
248
+ fork . right . inner . entity = win ;
196
249
197
250
this . tile ( ext , fork , fork . area ) ;
198
251
return true ;
199
252
}
200
253
} else if ( fork . right . is_window ( win ) ) {
201
- const sibling = ext . windows . get ( fork . left . entity ) ;
254
+ const sibling = ext . windows . get ( fork . left . inner . entity ) ;
202
255
if ( sibling && sibling . rect ( ) . contains ( cursor ) ) {
203
- fork . right . entity = fork . left . entity ;
204
- fork . left . entity = win ;
256
+ fork . right . inner . entity = fork . left . inner . entity ;
257
+ fork . left . inner . entity = win ;
205
258
206
259
this . tile ( ext , fork , fork . area ) ;
207
260
return true ;
@@ -214,6 +267,30 @@ export class AutoTiler {
214
267
return false ;
215
268
}
216
269
270
+ find_stack ( entity : Entity ) : null | [ Fork , node . Node , boolean ] {
271
+ const att = this . attached . get ( entity ) ;
272
+ if ( att ) {
273
+ const fork = this . forest . forks . get ( att ) ;
274
+ if ( fork ) {
275
+ if ( fork . left . is_in_stack ( entity ) ) {
276
+ return [ fork , fork . left , true ] ;
277
+ } else if ( fork . right ?. is_in_stack ( entity ) ) {
278
+ return [ fork , fork . right , false ] ;
279
+ }
280
+ }
281
+ }
282
+
283
+ return null ;
284
+ }
285
+
286
+ /** Find the fork that belongs to a window */
287
+ get_parent_fork ( window : Entity ) : null | Fork {
288
+ const entity = this . attached . get ( window ) ;
289
+ if ( ! entity ) return null ;
290
+
291
+ return this . forest . forks . get ( entity ) ;
292
+ }
293
+
217
294
/** Performed when a window that has been dropped is destined to be tiled
218
295
*
219
296
* ## Implementation Notes
@@ -288,8 +365,85 @@ export class AutoTiler {
288
365
const result = this . toggle_orientation_ ( ext , window ) ;
289
366
if ( result . kind == ERR ) {
290
367
log . warn ( `toggle_orientation: ${ result . value } ` ) ;
368
+ }
369
+ }
370
+
371
+ toggle_stacking ( ext : Ext ) {
372
+ const focused = ext . focus_window ( ) ;
373
+ if ( ! focused ) return ;
374
+
375
+ // Disable floating if floating is enabled
376
+ if ( ext . contains_tag ( focused . entity , Tags . Floating ) ) {
377
+ ext . delete_tag ( focused . entity , Tags . Floating ) ;
378
+ this . auto_tile ( ext , focused , false ) ;
379
+ }
380
+
381
+ let stack = null , fork = null ;
382
+ const fork_entity = this . attached . get ( focused . entity ) ;
383
+
384
+ if ( fork_entity ) {
385
+ fork = this . forest . forks . get ( fork_entity ) ;
386
+ if ( fork ) {
387
+ const stack_toggle = ( fork : Fork , branch : node . Node ) => {
388
+ // If the stack contains 1 item, unstack it
389
+ const stack = branch . inner as node . NodeStack ;
390
+ if ( stack . entities . length === 1 ) {
391
+ focused . stack = null ;
392
+ this . forest . stacks . remove ( stack . idx ) ?. destroy ( ) ;
393
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
394
+ return node . Node . window ( focused . entity ) ;
395
+ }
396
+
397
+ return null ;
398
+ } ;
399
+
400
+ if ( fork . left . is_window ( focused . entity ) ) {
401
+ // Assign left window as stack.
402
+ focused . stack = this . forest . stacks . insert ( new Stack ( ext , focused . entity , fork . workspace ) ) ;
403
+ fork . left = node . Node . stacked ( focused . entity , focused . stack ) ;
404
+ stack = fork . left . inner as node . NodeStack ;
405
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
406
+ } else if ( fork . left . is_in_stack ( focused . entity ) ) {
407
+ const node = stack_toggle ( fork , fork . left ) ;
408
+ if ( node ) fork . left = node ;
409
+ } else if ( fork . right ?. is_window ( focused . entity ) ) {
410
+ // Assign right window as stack
411
+ focused . stack = this . forest . stacks . insert ( new Stack ( ext , focused . entity , fork . workspace ) ) ;
412
+ fork . right = node . Node . stacked ( focused . entity , focused . stack ) ;
413
+ stack = fork . right . inner as node . NodeStack ;
414
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
415
+ } else if ( fork . right ?. is_in_stack ( focused . entity ) ) {
416
+ const node = stack_toggle ( fork , fork . right ) ;
417
+ if ( node ) fork . right = node ;
418
+ }
419
+ }
420
+ }
421
+
422
+ if ( stack ) this . update_stack ( ext , stack ) ;
423
+
424
+ if ( fork ) this . tile ( ext , fork , fork . area ) ;
425
+ }
426
+
427
+ update_stack ( ext : Ext , stack : node . NodeStack ) {
428
+ if ( stack . rect ) {
429
+ const container = this . forest . stacks . get ( stack . idx ) ;
430
+ if ( container ) {
431
+ container . clear ( ) ;
432
+
433
+ // Collect names of each entity in the stack
434
+ for ( const entity of stack . entities ) {
435
+ const window = ext . windows . get ( entity ) ;
436
+ if ( window ) {
437
+ window . stack = stack . idx ;
438
+ container . add ( window ) ;
439
+ }
440
+ }
441
+
442
+ container . update_positions ( stack . rect ) ;
443
+ container . auto_activate ( ) ;
444
+ }
291
445
} else {
292
- log . info ( 'toggled orientation ') ;
446
+ log . warn ( 'stack rect was null ') ;
293
447
}
294
448
}
295
449
@@ -366,7 +520,7 @@ export class AutoTiler {
366
520
this . forest . measure ( ext , fork , fork . area ) ;
367
521
368
522
for ( const child of this . forest . iter ( fork_entity , NodeKind . FORK ) ) {
369
- const child_fork = this . forest . forks . get ( child . entity ) ;
523
+ const child_fork = this . forest . forks . get ( ( child . inner as node . NodeFork ) . entity ) ;
370
524
if ( child_fork ) {
371
525
child_fork . rebalance_orientation ( ) ;
372
526
this . forest . measure ( ext , child_fork , child_fork . area ) ;
0 commit comments