Skip to content

Commit 6a2d6eb

Browse files
committed
feat(tiling): On window move via keyboard, place in tree on nearest branch
1 parent ad72dee commit 6a2d6eb

File tree

3 files changed

+55
-29
lines changed

3 files changed

+55
-29
lines changed

src/auto_tiler.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as stack from 'stack';
99

1010
import type { Entity } from 'ecs';
1111
import type { Ext } from 'extension';
12-
import type { Forest } from 'forest';
12+
import type { Forest, MoveBy } from 'forest';
1313
import type { Fork } from 'fork';
1414
import type { Rectangle } from 'rectangle';
1515
import type { Result } from 'result';
@@ -146,8 +146,8 @@ export class AutoTiler {
146146
}
147147

148148
/** Tiles a window into another */
149-
attach_to_window(ext: Ext, attachee: ShellWindow, attacher: ShellWindow, cursor: Rectangle, stack_from_left: boolean = true) {
150-
let attached = this.forest.attach_window(ext, attachee.entity, attacher.entity, cursor, stack_from_left);
149+
attach_to_window(ext: Ext, attachee: ShellWindow, attacher: ShellWindow, move_by: MoveBy, stack_from_left: boolean = true) {
150+
let attached = this.forest.attach_window(ext, attachee.entity, attacher.entity, move_by, stack_from_left);
151151

152152
if (attached) {
153153
const [, fork] = attached;
@@ -186,7 +186,7 @@ export class AutoTiler {
186186
if (toplevel) {
187187
const onto = this.forest.largest_window_on(ext, toplevel);
188188
if (onto) {
189-
if (this.attach_to_window(ext, onto, win, lib.cursor_rect())) {
189+
if (this.attach_to_window(ext, onto, win, { cursor: lib.cursor_rect() })) {
190190
return;
191191
}
192192
}
@@ -208,7 +208,7 @@ export class AutoTiler {
208208
if (result.kind == ERR) {
209209
this.attach_to_workspace(ext, win, ext.workspace_id(win));
210210
} else {
211-
this.attach_to_window(ext, result.value, win, lib.cursor_rect())
211+
this.attach_to_window(ext, result.value, win, { cursor: lib.cursor_rect() })
212212
}
213213
}
214214

@@ -333,7 +333,7 @@ export class AutoTiler {
333333
if (toplevel) {
334334
const attach_to = this.forest.largest_window_on(ext, toplevel);
335335
if (attach_to) {
336-
this.attach_to_window(ext, attach_to, win, cursor);
336+
this.attach_to_window(ext, attach_to, win, {cursor});
337337
return;
338338
}
339339
}
@@ -360,7 +360,7 @@ export class AutoTiler {
360360
this.detach_window(ext, win.entity);
361361

362362
if (attach_to) {
363-
this.attach_to_window(ext, attach_to, win, cursor);
363+
this.attach_to_window(ext, attach_to, win, {cursor});
364364
} else {
365365
attach_mon()
366366
}

src/forest.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as movement from 'movement';
99
import * as Rect from 'rectangle';
1010
import * as Node from 'node';
1111
import * as Fork from 'fork';
12+
import * as geom from 'geom';
1213

1314
import type { Entity } from 'ecs';
1415
import type { Rectangle } from './rectangle';
@@ -25,6 +26,13 @@ const UP = Movement.UP;
2526
const LEFT = Movement.LEFT;
2627
const RIGHT = Movement.RIGHT;
2728

29+
export interface MoveByCursor { cursor: Rectangular }
30+
31+
export interface MoveByKeyboard {
32+
src: Rectangular
33+
}
34+
35+
export type MoveBy = MoveByCursor | MoveByKeyboard
2836

2937
/** A request to move a window into a new location. */
3038
interface Request {
@@ -178,9 +186,34 @@ export class Forest extends Ecs.World {
178186
}
179187

180188
/** Attaches a `new` window to the fork which `onto` is attached to. */
181-
attach_window(ext: Ext, onto_entity: Entity, new_entity: Entity, cursor: Rectangle, stack_from_left: boolean): [Entity, Fork.Fork] | null {
189+
attach_window(ext: Ext, onto_entity: Entity, new_entity: Entity, move_by: MoveBy, stack_from_left: boolean): [Entity, Fork.Fork] | null {
182190
const right_node = Node.Node.window(new_entity);
183191

192+
function swap_branches(fork: Fork.Fork) {
193+
const temp = fork.left
194+
fork.left = fork.right as Node.Node
195+
fork.right = temp
196+
}
197+
198+
const placement = (fork: Fork.Fork, left_: [number, number, number, number], right_: [number, number, number, number]) => {
199+
const inner_left = new Rect.Rectangle(left_), inner_right = new Rect.Rectangle(right_)
200+
201+
if ("cursor" in move_by) {
202+
if (inner_left.contains(move_by.cursor)) {
203+
swap_branches(fork)
204+
}
205+
} else if ("src" in move_by) {
206+
const { src } = move_by
207+
208+
const from : [number, number] = [src.x + (src.width / 2), src.y + (src.height / 2)]
209+
210+
const left = geom.shortest_side(from, inner_left)
211+
const right = geom.shortest_side(from, inner_right)
212+
213+
if (left < right) swap_branches(fork)
214+
}
215+
}
216+
184217
for (const [entity, fork] of this.forks.iter()) {
185218
if (fork.left.is_window(onto_entity)) {
186219
if (fork.right) {
@@ -189,16 +222,11 @@ export class Forest extends Ecs.World {
189222

190223
const { x, y, width, height } = new_fork.area;
191224

192-
const inner_left = new Rect.Rectangle(new_fork.is_horizontal()
193-
? [x, y, width / 2, height]
194-
: [x, y, width, height / 2]
195-
);
225+
const [left_, right_]: [[number, number, number, number], [number, number, number, number]] = new_fork.is_horizontal()
226+
? [[x, y, width / 2, height], [x + (width / 2), y, width / 2, height]]
227+
: [[x, y, width, height / 2], [x, y + (height / 2), width, height / 2]]
196228

197-
if (inner_left.contains(cursor)) {
198-
const temp = new_fork.left;
199-
new_fork.left = new_fork.right as Node.Node;
200-
new_fork.right = temp;
201-
}
229+
placement(new_fork, left_, right_)
202230

203231
fork.left = Node.Node.fork(fork_entity);
204232
this.parents.insert(fork_entity, entity);
@@ -217,18 +245,14 @@ export class Forest extends Ecs.World {
217245
const [fork_entity, new_fork] = this.create_fork(fork.right, right_node, area, fork.workspace, fork.monitor);
218246
const { x, y, width, height } = new_fork.area;
219247

220-
const inner_left = new Rect.Rectangle(new_fork.is_horizontal()
221-
? [x, y, width / 2, height]
222-
: [x, y, width, height / 2]
223-
);
224-
225-
if (inner_left.contains(cursor)) {
226-
const temp = new_fork.left;
227-
new_fork.left = new_fork.right as Node.Node;
228-
new_fork.right = temp;
229-
}
248+
const [left_, right_]: [[number, number, number, number], [number, number, number, number]] = new_fork.is_horizontal()
249+
? [[x, y, width / 2, height], [x + width / 2, y, width / 2, height]]
250+
: [[x, y, width, height / 2], [x, y + height / 2, width, height / 2]]
230251

231252
fork.right = Node.Node.fork(fork_entity);
253+
254+
placement(new_fork, left_, right_)
255+
232256
this.parents.insert(fork_entity, entity);
233257
return this._attach(onto_entity, new_entity, this.on_attach, entity, fork, [fork_entity, new_fork]);
234258
} else if (fork.right.is_in_stack(onto_entity)) {

src/tiling.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ export class Tiler {
438438

439439
focused.ignore_detach = true;
440440
at.detach_window(ext, focused.entity);
441-
at.attach_to_window(ext, move_to, focused, Lib.cursor_rect(), stack_from_left);
441+
at.attach_to_window(ext, move_to, focused, { cursor: Lib.cursor_rect() }, stack_from_left);
442442
watching = focused;
443443
} else {
444444
const parent = at.windows_are_siblings(focused.entity, move_to.entity);
@@ -466,9 +466,11 @@ export class Tiler {
466466
}
467467

468468
if (!watching) {
469+
let movement = { src: focused.meta.get_frame_rect()}
470+
469471
focused.ignore_detach = true;
470472
at.detach_window(ext, focused.entity);
471-
at.attach_to_window(ext, move_to, focused, Lib.cursor_rect(), false);
473+
at.attach_to_window(ext, move_to, focused, movement, false);
472474
watching = focused;
473475
}
474476
}

0 commit comments

Comments
 (0)