@@ -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,46 @@ 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 ) {
37
+ attach_swap ( ext : Ext , a : Entity , b : Entity ) {
36
38
const a_ent = this . attached . remove ( a ) ;
37
39
const b_ent = this . attached . remove ( 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
+ const a_win = ext . windows . get ( a ) ;
42
+ const 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
+ const 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 ) ext . auto_tiler . forest . stacks . get ( a_win . stack ) ?. deactivate ( a_win ) ;
55
+ if ( b_win . stack !== null ) ext . auto_tiler . forest . stacks . get ( b_win . stack ) ?. deactivate ( b_win ) ;
47
56
}
57
+
58
+ const a_fn = a_fork . replace_window ( ext , a_win , b_win ) ;
59
+ this . attached . insert ( b , a_ent ) ;
60
+
61
+ const b_fn = b_fork . replace_window ( ext , b_win , a_win ) ;
62
+ this . attached . insert ( a , b_ent ) ;
63
+
64
+ if ( a_fn ) a_fn ( ) ;
65
+ if ( b_fn ) b_fn ( ) ;
66
+
67
+ a_win . stack = b_stack ;
68
+ b_win . stack = a_stack ;
69
+
70
+ a_win . meta . get_compositor_private ( ) ?. show ( ) ;
71
+ b_win . meta . get_compositor_private ( ) ?. show ( ) ;
72
+
73
+ log . debug ( `swapped: ${ this . forest . fmt ( ext ) } ` ) ;
74
+
75
+ this . tile ( ext , a_fork , a_fork . area ) ;
76
+ this . tile ( ext , b_fork , b_fork . area ) ;
48
77
}
49
78
50
79
update_toplevel ( ext : Ext , fork : Fork , monitor : number , smart_gaps : boolean ) {
@@ -93,8 +122,8 @@ export class AutoTiler {
93
122
}
94
123
95
124
/** 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 ) ;
125
+ attach_to_window ( ext : Ext , attachee : ShellWindow , attacher : ShellWindow , cursor : Rectangle , stack_from_left : boolean = true ) {
126
+ let attached = this . forest . attach_window ( ext , attachee . entity , attacher . entity , cursor , stack_from_left ) ;
98
127
99
128
if ( attached ) {
100
129
const [ , fork ] = attached ;
@@ -155,10 +184,17 @@ export class AutoTiler {
155
184
}
156
185
}
157
186
187
+ /** Destroy all widgets owned by this object. Call before dropping. */
188
+ destroy ( ) {
189
+ for ( const stack of this . forest . stacks . values ( ) ) stack . destroy ( ) ;
190
+
191
+ this . forest . stacks . truncate ( 0 ) ;
192
+ }
193
+
158
194
/** Detaches the window from a tiling branch, if it is attached to one. */
159
195
detach_window ( ext : Ext , win : Entity ) {
160
196
this . attached . take_with ( win , ( prev_fork : Entity ) => {
161
- const reflow_fork = this . forest . detach ( prev_fork , win ) ;
197
+ const reflow_fork = this . forest . detach ( ext , prev_fork , win ) ;
162
198
163
199
if ( reflow_fork ) {
164
200
const fork = reflow_fork [ 1 ] ;
@@ -187,21 +223,21 @@ export class AutoTiler {
187
223
const fork = this . forest . forks . get ( fork_entity ) ;
188
224
189
225
if ( fork ) {
190
- if ( fork . left . kind == NodeKind . WINDOW && fork . right && fork . right . kind == NodeKind . WINDOW ) {
226
+ if ( fork . left . inner . kind === 2 && fork . right && fork . right . inner . kind === 2 ) {
191
227
if ( fork . left . is_window ( win ) ) {
192
- const sibling = ext . windows . get ( fork . right . entity ) ;
228
+ const sibling = ext . windows . get ( fork . right . inner . entity ) ;
193
229
if ( sibling && sibling . rect ( ) . contains ( cursor ) ) {
194
- fork . left . entity = fork . right . entity ;
195
- fork . right . entity = win ;
230
+ fork . left . inner . entity = fork . right . inner . entity ;
231
+ fork . right . inner . entity = win ;
196
232
197
233
this . tile ( ext , fork , fork . area ) ;
198
234
return true ;
199
235
}
200
236
} else if ( fork . right . is_window ( win ) ) {
201
- const sibling = ext . windows . get ( fork . left . entity ) ;
237
+ const sibling = ext . windows . get ( fork . left . inner . entity ) ;
202
238
if ( sibling && sibling . rect ( ) . contains ( cursor ) ) {
203
- fork . right . entity = fork . left . entity ;
204
- fork . left . entity = win ;
239
+ fork . right . inner . entity = fork . left . inner . entity ;
240
+ fork . left . inner . entity = win ;
205
241
206
242
this . tile ( ext , fork , fork . area ) ;
207
243
return true ;
@@ -214,6 +250,30 @@ export class AutoTiler {
214
250
return false ;
215
251
}
216
252
253
+ find_stack ( entity : Entity ) : null | [ Fork , node . Node , boolean ] {
254
+ const att = this . attached . get ( entity ) ;
255
+ if ( att ) {
256
+ const fork = this . forest . forks . get ( att ) ;
257
+ if ( fork ) {
258
+ if ( fork . left . is_in_stack ( entity ) ) {
259
+ return [ fork , fork . left , true ] ;
260
+ } else if ( fork . right ?. is_in_stack ( entity ) ) {
261
+ return [ fork , fork . right , false ] ;
262
+ }
263
+ }
264
+ }
265
+
266
+ return null ;
267
+ }
268
+
269
+ /** Find the fork that belongs to a window */
270
+ get_parent_fork ( window : Entity ) : null | Fork {
271
+ const entity = this . attached . get ( window ) ;
272
+ if ( ! entity ) return null ;
273
+
274
+ return this . forest . forks . get ( entity ) ;
275
+ }
276
+
217
277
/** Performed when a window that has been dropped is destined to be tiled
218
278
*
219
279
* ## Implementation Notes
@@ -288,8 +348,85 @@ export class AutoTiler {
288
348
const result = this . toggle_orientation_ ( ext , window ) ;
289
349
if ( result . kind == ERR ) {
290
350
log . warn ( `toggle_orientation: ${ result . value } ` ) ;
351
+ }
352
+ }
353
+
354
+ toggle_stacking ( ext : Ext ) {
355
+ const focused = ext . focus_window ( ) ;
356
+ if ( ! focused ) return ;
357
+
358
+ // Disable floating if floating is enabled
359
+ if ( ext . contains_tag ( focused . entity , Tags . Floating ) ) {
360
+ ext . delete_tag ( focused . entity , Tags . Floating ) ;
361
+ this . auto_tile ( ext , focused , false ) ;
362
+ }
363
+
364
+ let stack = null , fork = null ;
365
+ const fork_entity = this . attached . get ( focused . entity ) ;
366
+
367
+ if ( fork_entity ) {
368
+ fork = this . forest . forks . get ( fork_entity ) ;
369
+ if ( fork ) {
370
+ const stack_toggle = ( fork : Fork , branch : node . Node ) => {
371
+ // If the stack contains 1 item, unstack it
372
+ const stack = branch . inner as node . NodeStack ;
373
+ if ( stack . entities . length === 1 ) {
374
+ focused . stack = null ;
375
+ this . forest . stacks . remove ( stack . idx ) ?. destroy ( ) ;
376
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
377
+ return node . Node . window ( focused . entity ) ;
378
+ }
379
+
380
+ return null ;
381
+ } ;
382
+
383
+ if ( fork . left . is_window ( focused . entity ) ) {
384
+ // Assign left window as stack.
385
+ focused . stack = this . forest . stacks . insert ( new Stack ( ext , focused . entity , fork . workspace ) ) ;
386
+ fork . left = node . Node . stacked ( focused . entity , focused . stack ) ;
387
+ stack = fork . left . inner as node . NodeStack ;
388
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
389
+ } else if ( fork . left . is_in_stack ( focused . entity ) ) {
390
+ const node = stack_toggle ( fork , fork . left ) ;
391
+ if ( node ) fork . left = node ;
392
+ } else if ( fork . right ?. is_window ( focused . entity ) ) {
393
+ // Assign right window as stack
394
+ focused . stack = this . forest . stacks . insert ( new Stack ( ext , focused . entity , fork . workspace ) ) ;
395
+ fork . right = node . Node . stacked ( focused . entity , focused . stack ) ;
396
+ stack = fork . right . inner as node . NodeStack ;
397
+ fork . measure ( this . forest , ext , fork . area , this . forest . on_record ( ) ) ;
398
+ } else if ( fork . right ?. is_in_stack ( focused . entity ) ) {
399
+ const node = stack_toggle ( fork , fork . right ) ;
400
+ if ( node ) fork . right = node ;
401
+ }
402
+ }
403
+ }
404
+
405
+ if ( stack ) this . update_stack ( ext , stack ) ;
406
+
407
+ if ( fork ) this . tile ( ext , fork , fork . area ) ;
408
+ }
409
+
410
+ update_stack ( ext : Ext , stack : node . NodeStack ) {
411
+ if ( stack . rect ) {
412
+ const container = this . forest . stacks . get ( stack . idx ) ;
413
+ if ( container ) {
414
+ container . clear ( ) ;
415
+
416
+ // Collect names of each entity in the stack
417
+ for ( const entity of stack . entities ) {
418
+ const window = ext . windows . get ( entity ) ;
419
+ if ( window ) {
420
+ window . stack = stack . idx ;
421
+ container . add ( window ) ;
422
+ }
423
+ }
424
+
425
+ container . update_positions ( stack . rect ) ;
426
+ container . auto_activate ( ) ;
427
+ }
291
428
} else {
292
- log . info ( 'toggled orientation ') ;
429
+ log . warn ( 'stack rect was null ') ;
293
430
}
294
431
}
295
432
@@ -366,7 +503,7 @@ export class AutoTiler {
366
503
this . forest . measure ( ext , fork , fork . area ) ;
367
504
368
505
for ( const child of this . forest . iter ( fork_entity , NodeKind . FORK ) ) {
369
- const child_fork = this . forest . forks . get ( child . entity ) ;
506
+ const child_fork = this . forest . forks . get ( ( child . inner as node . NodeFork ) . entity ) ;
370
507
if ( child_fork ) {
371
508
child_fork . rebalance_orientation ( ) ;
372
509
this . forest . measure ( ext , child_fork , child_fork . area ) ;
0 commit comments