1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
#!/usr/bin/env python3
"""panes -- panes and tabs, the way your hands expect.
The opener: keys and mouse doing the same work. Split with a chord, split by
clicking a border, drag the gap, then carry a pane by its title -- dropped in
the middle it swaps, dropped on an edge band it tucks in beside, which is the
whole layout rebuilt without the keyboard. Zoom, and hop through a tab, to
close. Every coordinate is read from the compositor's hit list, so the demo
cannot disagree with the program.
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from castgen import Recorder, out_path
ENV = {"PS1": "$ ", "ENV": "/dev/null"}
r = Recorder(["/bin/sh"], cols=100, rows=28, title="panes and tabs", env=ENV)
r.capture(force=True)
r.pause(0.8)
# A chord: C-a \ splits into columns.
r.key("\\")
r.pause(0.5)
r.run("echo splits are one chord away", wait="one chord")
r.pause(0.9)
# Or a click: the middle of a border splits toward it, and the hover says so
# before the button goes down.
h = r.snapshot().handle(4, "b") or r.snapshot().handle(2, "b")
r.move_to(*h, dur=0.8)
r.pause(0.8)
r.press()
r.release()
r.pause(0.9)
# Name the players so the next two moves read as something.
ids = sorted(p["id"] for p in r.api("panes")["panes"])
for pid, name in zip(ids, ["editor", "server", "logs"]):
r.api("set-name", target="pane", id=pid, name=name)
r.capture(dt=0.4)
r.pause(0.6)
# Drag the gap: the boundary follows the pointer.
h = r.snapshot().handle(ids[0], "r")
r.move_to(*h, dur=0.7)
r.press()
r.drag_to(h[0] - 13, h[1], dur=0.8)
r.release()
r.pause(1.0)
# Drag a pane by its title onto another: dropped in the middle, they swap.
snap = r.snapshot()
title = next(e for e in snap.hits if e["action"] == f"title:{ids[0]}")
tx, ty = title["x"] + title["w"] // 2, title["y"]
other = next(p for p in r.api("panes")["panes"] if p["id"] != ids[0])
r.move_to(tx, ty, dur=0.7)
r.press()
r.drag_to(
other["content_x"] + other["content_w"] // 2,
other["content_y"] + other["content_h"] // 2,
dur=0.9,
)
r.pause(0.4)
r.release()
r.pause(1.1)
# The same grip, the other drop: carried over a pane, it subdivides into a
# centre and four edge bands. The centre is the swap above; a band means "in
# beside it, on this side", and fills the half of the pane the drop would
# hand over while every other candidate greys out. Linger over the centre
# first, then steer into the bottom band -- read off the hit list, like
# every promise this program draws. Drop: the layout rebuilds, no keyboard.
# The source is the top-right pane; the target the pane alone in the left
# column, so the drop restacks the whole tab -- a rearrangement no swap
# could produce, which is the point of the bands.
panes = r.api("panes")["panes"]
src = next(p for p in panes if p["id"] == ids[0])
target = min((p for p in panes if p["id"] != ids[0]), key=lambda p: p["content_x"])
title = next(e for e in r.snapshot().hits if e["action"] == f"title:{ids[0]}")
r.move_to(title["x"] + title["w"] // 2, title["y"], dur=0.7)
r.press()
r.drag_to(
target["content_x"] + target["content_w"] // 2,
target["content_y"] + target["content_h"] // 2,
dur=0.8,
)
r.pause(0.7)
band = next(e for e in r.snapshot().hits if e["action"] == f"drop:{target['id']}:b")
r.drag_to(band["x"] + band["w"] // 2, band["y"] + band["h"] // 2, dur=0.5)
r.pause(0.9)
r.release()
r.pause(1.4)
# Zoom the focused pane to the whole tab, and back.
r.key("z")
r.pause(1.0)
r.key("z")
r.pause(0.8)
# A fresh tab, and back: the first one kept everything.
r.key("c")
r.pause(0.6)
r.run("echo a fresh tab costs nothing", wait="costs")
r.pause(0.9)
r.key("1")
r.pause(1.6)
r.save(out_path("panes"), tail=1.8)
|