layout support
haldean
2 years ago
0 | import illwill | |
1 | 0 | import unicode |
2 | 1 | |
3 | 2 | type |
16 | 15 | stride: int |
17 | 16 | Blit* = RectBuf[Char] |
18 | 17 | |
19 | Layer* = ref object | |
20 | blit: Blit | |
21 | src: Pt | |
22 | dst: Rect | |
23 | invalid: Rect | |
24 | ||
25 | Comp* = ref object | |
26 | size: Size | |
27 | tb*: illwill.TerminalBuffer | |
28 | layers: seq[Layer] | |
29 | srclayer: RectBuf[int] | |
30 | fullrepaint: bool | |
31 | ||
32 | 18 | const NO_CHAR*: Char = (Rune(' '), Style(0xFF)) |
33 | const NO_LAYER: int = -1 | |
34 | 19 | |
35 | 20 | ## POINT MATH |
36 | 21 | |
37 | proc `+`(p0: Pt, p1: Pt): Pt = | |
22 | proc `+`*(p0: Pt, p1: Pt): Pt = | |
38 | 23 | Pt(x: p0.x + p1.x, y: p0.y + p1.y) |
39 | 24 | |
40 | proc `-`(p0: Pt, p1: Pt): Pt = | |
25 | proc `-`*(p0: Pt, p1: Pt): Pt = | |
41 | 26 | Pt(x: p0.x - p1.x, y: p0.y - p1.y) |
42 | 27 | |
43 | proc `+=`(p0: var Pt, p1: Pt) = | |
28 | proc `+=`*(p0: var Pt, p1: Pt) = | |
44 | 29 | p0.x += p1.x |
45 | 30 | p0.y += p1.y |
46 | 31 | |
56 | 41 | return 0 |
57 | 42 | return r.hi.y - r.lo.y + 1 |
58 | 43 | |
44 | proc size*(r: Rect): Size = | |
45 | Size(w: r.width(), h: r.height()) | |
46 | ||
59 | 47 | proc isempty*(r: Rect): bool = r.width() <= 0 or r.height() <= 0 |
60 | 48 | |
61 | 49 | proc width*[T](b: RectBuf[T]): int = b.stride |
66 | 54 | proc size*[T](b: RectBuf[T]): Size = |
67 | 55 | Size(w: b.width(), h: b.height()) |
68 | 56 | |
69 | proc size*(c: Comp): Size = c.size | |
70 | ||
71 | 57 | proc contains*(r: Rect, p: Pt): bool = |
72 | 58 | r.lo.x <= p.x and p.x <= r.hi.x and |
73 | 59 | r.lo.y <= p.y and p.y <= r.hi.y |
75 | 61 | proc contains*[T](b: RectBuf[T], p: Pt): bool = |
76 | 62 | 0 <= p.x and p.x < b.width() and |
77 | 63 | 0 <= p.y and p.y < b.height() |
78 | ||
79 | proc contains*(l: Layer, p: Pt): bool = | |
80 | l.dst.contains(p) and l.blit.contains(p - l.src) | |
81 | 64 | |
82 | 65 | proc `+=`*(r0: var Rect, r1: Rect) = |
83 | 66 | if r1.isempty(): |
104 | 87 | proc get*[T](b: RectBuf[T], p: Pt): T = |
105 | 88 | b.c[b.index(p)] |
106 | 89 | |
107 | proc get*(l: Layer, dst: Pt): Char = | |
108 | var src = Pt( | |
109 | x: l.src.x + (dst.x - l.dst.lo.x), | |
110 | y: l.src.y + (dst.y - l.dst.lo.y)) | |
111 | if l.blit.contains(src): | |
112 | return l.blit.get(src) | |
113 | return NO_CHAR | |
114 | ||
115 | proc get*(c: Comp, p: Pt): Char = | |
116 | let layerindex = c.srclayer.get(p) | |
117 | if layerindex == NO_LAYER: | |
118 | return NO_CHAR | |
119 | return c.layers[layerindex].get(p) | |
120 | ||
121 | 90 | proc resize*[T](b: var RectBuf[T], size: Size) = |
122 | 91 | let s = size.w * size.h |
123 | 92 | b.c = newSeq[T](s) |
128 | 97 | proc newPt*(x, y: int): Pt = |
129 | 98 | Pt(x: x, y: y) |
130 | 99 | |
131 | proc emptyRect(): Rect = | |
100 | proc emptyRect*(): Rect = | |
132 | 101 | Rect(lo: newPt(0, 0), hi: newPt(-1, -1)) |
133 | 102 | |
134 | 103 | proc rectFromSize*(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect = |
144 | 113 | b.fill(NO_CHAR) |
145 | 114 | return b |
146 | 115 | |
147 | proc newLayer*(size: Size): Layer = | |
148 | Layer(blit: newBlit(size), invalid: emptyRect()) | |
116 | ## ITERATORS | |
149 | 117 | |
150 | proc newLayer*(blit: Blit): Layer = | |
151 | Layer(blit: blit, invalid: emptyRect()) | |
152 | ||
153 | proc newComp*(size: Size): Comp = | |
154 | Comp(size: size, | |
155 | tb: newTerminalBuffer(size.w, size.h), | |
156 | layers: newSeq[Layer](), | |
157 | fullrepaint: true, | |
158 | srclayer: newRectBuf[int](size)) | |
159 | ||
160 | ## PAINTING | |
161 | ||
162 | iterator pointsinside(rects: seq[Rect]): Pt = | |
118 | iterator pointsInside*(rects: seq[Rect]): Pt = | |
163 | 119 | var domain = emptyRect() |
164 | 120 | for r in rects: |
165 | 121 | domain += r |
170 | 126 | if r.contains(p): |
171 | 127 | yield p |
172 | 128 | break |
173 | ||
174 | proc paint*(c: Comp) = | |
175 | c.tb.clear() | |
176 | for layer in c.layers: | |
177 | for point in pointsinside(@[layer.dst]): | |
178 | c.tb.write(point.x, point.y, layer.get(point)[0].toUTF8) | |
179 | c.tb.display() | |
180 | ||
181 | ## LAYER MANAGEMENT | |
182 | ||
183 | proc pushlayer*(c: var Comp, l: Layer) = | |
184 | c.layers.add(l) | |
185 | ||
186 | proc poplayer*(c: var Comp) = | |
187 | c.layers.delete(c.layers.len - 1) | |
188 | ||
189 | proc `size=`*(c: var Comp, s: Size) = | |
190 | c.tb = newTerminalBuffer(s.w, s.h) | |
191 | c.size = s | |
192 | ||
193 | proc translate*(l: var Layer, amt: Pt) = | |
194 | l.src += amt | |
195 | ||
196 | proc `dst=`*(l: var Layer, r: Rect) = | |
197 | l.invalid += l.dst | |
198 | l.invalid += r | |
199 | l.dst = r | |
200 | ||
201 | ## DEBUG | |
202 | ||
203 | proc print*(b: Comp) = | |
204 | for j in 0..(b.size.h - 1): | |
205 | for i in 0..(b.size.w - 1): | |
206 | var c = b.get(newPt(i, j)) | |
207 | if c == NO_CHAR: | |
208 | stdout.write(' ') | |
209 | else: | |
210 | stdout.write(c[0].toUTF8) | |
211 | stdout.write('\n') | |
212 | ||
213 | proc printsrc*(b: Comp) = | |
214 | for j in 0..(b.size.h - 1): | |
215 | for i in 0..(b.size.w - 1): | |
216 | var c = b.srclayer.get(newPt(i, j)) | |
217 | stdout.write(if c == NO_LAYER: 'X' else: char(int('1') + c)) | |
218 | stdout.write('\n') | |
219 | ||
220 | proc printdebug*(c: Comp) = | |
221 | echo c.layers[0].src, " -> ", c.layers[0].dst |
0 | import blit | |
1 | ||
2 | import bitops | |
3 | import illwill | |
4 | import math | |
5 | import unicode | |
6 | ||
7 | type | |
8 | LayoutTag* {.pure.} = enum | |
9 | center, left, right | |
10 | ||
11 | Layer* = ref object | |
12 | blit*: Blit | |
13 | src*: Pt | |
14 | dst*: Rect | |
15 | tag*: LayoutTag | |
16 | ||
17 | Comp* = ref object | |
18 | size: Size | |
19 | tb: illwill.TerminalBuffer | |
20 | layers: seq[Layer] | |
21 | ||
22 | proc size*(l: Layer): Size = l.dst.size() | |
23 | proc size*(c: Comp): Size = c.size | |
24 | ||
25 | proc contains*(l: Layer, p: Pt): bool = | |
26 | l.dst.contains(p) and l.blit.contains(p - l.src) | |
27 | ||
28 | proc get*(l: Layer, dst: Pt): Char = | |
29 | var src = Pt( | |
30 | x: l.src.x + (dst.x - l.dst.lo.x), | |
31 | y: l.src.y + (dst.y - l.dst.lo.y)) | |
32 | if l.blit.contains(src): | |
33 | return l.blit.get(src) | |
34 | return NO_CHAR | |
35 | ||
36 | proc get*(c: Comp, p: Pt): TerminalChar = | |
37 | return c.tb[p.x, p.y] | |
38 | ||
39 | proc newLayer*(size: Size): Layer = | |
40 | Layer(blit: newBlit(size), tag: LayoutTag.center) | |
41 | ||
42 | proc newLayer*(blit: Blit): Layer = | |
43 | Layer(blit: blit, tag: LayoutTag.center) | |
44 | ||
45 | proc newComp*(size: Size): Comp = | |
46 | Comp(size: size, | |
47 | tb: newTerminalBuffer(size.w, size.h), | |
48 | layers: newSeq[Layer]()) | |
49 | ||
50 | proc layout(c: var Comp) = | |
51 | var | |
52 | lcenter: Layer = nil | |
53 | lleft: Layer = nil | |
54 | lright: Layer = nil | |
55 | for layer in c.layers: | |
56 | case layer.tag | |
57 | of center: | |
58 | if lcenter == nil: | |
59 | lcenter = layer | |
60 | of right: | |
61 | if lright == nil: | |
62 | lright = layer | |
63 | of left: | |
64 | if lleft == nil: | |
65 | lleft = layer | |
66 | ||
67 | var present = 0 | |
68 | let full = rectFromSize(c.size) | |
69 | if lleft != nil: | |
70 | present = bitor(present, 0b100) | |
71 | lleft.dst = full | |
72 | if lcenter != nil: | |
73 | present = bitor(present, 0b010) | |
74 | lcenter.dst = full | |
75 | if lright != nil: | |
76 | present = bitor(present, 0b001) | |
77 | lright.dst = full | |
78 | ||
79 | case present | |
80 | of 0b010, 0b100, 0b001: discard | |
81 | of 0b110: | |
82 | let split = int(ceil(c.size.w.float * 0.33)) | |
83 | lleft.dst.hi.x = split - 1 | |
84 | lcenter.dst.lo.x = split | |
85 | of 0b011: | |
86 | let split = int(ceil(c.size.w.float * 0.67)) | |
87 | lcenter.dst.hi.x = split - 1 | |
88 | lright.dst.lo.x = split | |
89 | of 0b101: | |
90 | let split = int(c.size.w.float * 0.5) | |
91 | lleft.dst.hi.x = split - 1 | |
92 | lright.dst.lo.x = split | |
93 | of 0b111: | |
94 | let lsplit = int(ceil(c.size.w.float * 0.25)) | |
95 | let rsplit = int(ceil(c.size.w.float * 0.75)) | |
96 | lleft.dst.hi.x = lsplit - 1 | |
97 | lcenter.dst.lo.x = lsplit | |
98 | lcenter.dst.hi.x = rsplit - 1 | |
99 | lright.dst.lo.x = rsplit | |
100 | else: discard | |
101 | ||
102 | proc paint*(c: var Comp) = | |
103 | c.layout() | |
104 | c.tb.clear() | |
105 | for layer in c.layers: | |
106 | for point in pointsInside(@[layer.dst]): | |
107 | c.tb.write(point.x, point.y, layer.get(point)[0].toUTF8) | |
108 | c.tb.display() | |
109 | ||
110 | proc pushlayer*(c: var Comp, l: Layer) = | |
111 | c.layers.add(l) | |
112 | ||
113 | proc poplayer*(c: var Comp) = | |
114 | c.layers.delete(c.layers.len - 1) | |
115 | ||
116 | proc `size=`*(c: var Comp, s: Size) = | |
117 | c.tb = newTerminalBuffer(s.w, s.h) | |
118 | c.size = s | |
119 | ||
120 | proc print*(b: Comp) = | |
121 | for j in 0..(b.size.h - 1): | |
122 | for i in 0..(b.size.w - 1): | |
123 | var c = b.get(newPt(i, j)) | |
124 | stdout.write(c.ch.toUTF8) | |
125 | stdout.write('\n') | |
126 | ||
127 | proc printdebug*(c: Comp) = | |
128 | echo c.layers[0].src, " -> ", c.layers[0].dst |
0 | import blit, buf | |
0 | import blit, buf, comp | |
1 | 1 | import illwill |
2 | 2 | import os, system |
3 | 3 | |
4 | var comp = blit.newComp(Size(w: 30, h: 20)) | |
4 | var c = newComp(Size(w: 30, h: 20)) | |
5 | 5 | |
6 | 6 | var bx = newBuf() |
7 | 7 | bx.load("test/pg844.txt") |
8 | 8 | |
9 | var lx = blit.newLayer(bx.blit) | |
10 | lx.dst = rectFromSize(comp.size()) | |
11 | comp.pushlayer(lx) | |
9 | var lx = newLayer(bx.blit) | |
10 | c.pushlayer(lx) | |
11 | ||
12 | var ly = newLayer(bx.blit) | |
13 | ly.tag = LayoutTag.left | |
14 | c.pushlayer(ly) | |
15 | ||
16 | var lz = newLayer(bx.blit) | |
17 | lz.tag = LayoutTag.right | |
18 | c.pushlayer(lz) | |
12 | 19 | |
13 | 20 | proc exitProc() {.noconv.} = |
14 | 21 | illwillDeinit() |
29 | 36 | try: |
30 | 37 | while true: |
31 | 38 | var size = Size(w: terminalWidth(), h: terminalHeight()) |
32 | if size != comp.size: | |
33 | comp.size = size | |
39 | if size != c.size: | |
40 | c.size = size | |
34 | 41 | |
35 | 42 | var k = illwill.getKey() |
36 | 43 | case k |
37 | 44 | of Key.Escape, Key.Q: |
38 | 45 | exitProc() |
39 | 46 | of Key.J: |
40 | lx.translate(newPt(0, 1)) | |
47 | lx.src.y += 1 | |
41 | 48 | of Key.K: |
42 | lx.translate(newPt(0, -1)) | |
49 | lx.src.y -= 1 | |
43 | 50 | of Key.H: |
44 | lx.translate(newPt(-1, 0)) | |
51 | lx.src.x += 1 | |
45 | 52 | of Key.L: |
46 | lx.translate(newPt(1, 0)) | |
53 | lx.src.x -= 1 | |
47 | 54 | of Key.None: |
48 | 55 | discard |
49 | 56 | else: |
50 | 57 | discard |
51 | 58 | |
52 | comp.paint() | |
59 | c.paint() | |
53 | 60 | sleep(16) |
54 | 61 | except: |
55 | 62 | exitProc() |