]> git.lizzy.rs Git - micro.git/blob - cmd/micro/views/splits.go
144437bad12ed9a63dcf4a4175fa0ffd7e2de231
[micro.git] / cmd / micro / views / splits.go
1 package views
2
3 import (
4         "fmt"
5         "log"
6         "strings"
7 )
8
9 type SplitType uint8
10
11 const (
12         STVert  = 0
13         STHoriz = 1
14         STUndef = 2
15 )
16
17 var idcounter uint64
18
19 func NewID() uint64 {
20         idcounter++
21         return idcounter
22 }
23
24 type View struct {
25         X, Y int
26         W, H int
27 }
28
29 type Node struct {
30         View
31
32         Kind SplitType
33
34         parent   *Node
35         children []*Node
36
37         // Nodes can be marked as non resizable if they shouldn't be rescaled
38         // when the terminal window is resized or when a new split is added
39         // Only the splits on the edges of the screen can be marked as non resizable
40         canResize bool
41         // A node may also be marked with proportional scaling. This means that when
42         // the window is resized the split maintains its proportions
43         propScale bool
44
45         id uint64
46 }
47
48 func (n *Node) ID() uint64 {
49         if n.IsLeaf() {
50                 return n.id
51         }
52         return 0
53 }
54 func (n *Node) CanResize() bool {
55         return n.canResize
56 }
57 func (n *Node) PropScale() bool {
58         return n.propScale
59 }
60 func (n *Node) SetResize(b bool) {
61         n.canResize = b
62 }
63 func (n *Node) SetPropScale(b bool) {
64         n.propScale = b
65 }
66 func (n *Node) GetView() View {
67         return n.View
68 }
69 func (n *Node) SetView(v View) {
70         n.X, n.Y, n.W, n.H = v.X, v.Y, v.W, v.H
71 }
72 func (n *Node) Children() []*Node {
73         return n.children
74 }
75
76 func (n *Node) GetNode(id uint64) *Node {
77         if n.id == id && n.IsLeaf() {
78                 return n
79         }
80         for _, c := range n.children {
81                 if c.id == id && c.IsLeaf() {
82                         return c
83                 }
84                 gc := c.GetNode(id)
85                 if gc != nil {
86                         return gc
87                 }
88         }
89         return nil
90 }
91
92 func NewNode(Kind SplitType, x, y, w, h int, parent *Node, id uint64) *Node {
93         n := new(Node)
94         n.Kind = Kind
95         n.canResize = true
96         n.propScale = true
97         n.X, n.Y, n.W, n.H = x, y, w, h
98         n.children = make([]*Node, 0)
99         n.parent = parent
100         n.id = id
101
102         return n
103 }
104
105 func NewRoot(x, y, w, h int) *Node {
106         n1 := NewNode(STUndef, x, y, w, h, nil, NewID())
107
108         return n1
109 }
110
111 func (n *Node) IsLeaf() bool {
112         return len(n.children) == 0
113 }
114
115 func (n *Node) vResizeSplit(i int, size int) bool {
116         if i < 0 || i >= len(n.children)-1 {
117                 return false
118         }
119         c1, c2 := n.children[i], n.children[i+1]
120         toth := c1.H + c2.H
121         if size >= toth {
122                 return false
123         }
124         c2.Y = size
125         c1.Resize(c1.W, size)
126         c2.Resize(c2.W, toth-size)
127         return true
128 }
129 func (n *Node) hResizeSplit(i int, size int) bool {
130         if i < 0 || i >= len(n.children)-1 {
131                 return false
132         }
133         c1, c2 := n.children[i], n.children[i+1]
134         totw := c1.W + c2.W
135         if size >= totw {
136                 return false
137         }
138         c2.X = size
139         c1.Resize(size, c1.H)
140         c2.Resize(totw-size, c2.H)
141         return true
142 }
143
144 func (n *Node) ResizeSplit(size int) bool {
145         ind := 0
146         for i, c := range n.parent.children {
147                 if c.id == n.id {
148                         ind = i
149                 }
150         }
151         if n.parent.Kind == STVert {
152                 return n.parent.vResizeSplit(ind, size)
153         }
154         return n.parent.hResizeSplit(ind, size)
155 }
156
157 func (n *Node) vVSplit(right bool) uint64 {
158         ind := 0
159         for i, c := range n.parent.children {
160                 if c.id == n.id {
161                         ind = i
162                 }
163         }
164         return n.parent.hVSplit(ind, right)
165 }
166 func (n *Node) hHSplit(bottom bool) uint64 {
167         ind := 0
168         for i, c := range n.parent.children {
169                 if c.id == n.id {
170                         ind = i
171                 }
172         }
173         return n.parent.vHSplit(ind, bottom)
174 }
175 func (n *Node) vHSplit(i int, right bool) uint64 {
176         if n.IsLeaf() {
177                 newid := NewID()
178                 hn1 := NewNode(STHoriz, n.X, n.Y, n.W, n.H/2, n, n.id)
179                 hn2 := NewNode(STHoriz, n.X, n.Y+hn1.H, n.W, n.H/2, n, newid)
180                 if !right {
181                         hn1.id, hn2.id = hn2.id, hn1.id
182                 }
183
184                 n.children = append(n.children, hn1, hn2)
185                 n.alignSize()
186                 return newid
187         } else {
188                 numr := 0
189                 numnr := 0
190                 nonrh := 0
191                 for _, c := range n.children {
192                         if !c.CanResize() {
193                                 nonrh += c.H
194                                 numnr++
195                         } else {
196                                 numr++
197                         }
198                 }
199
200                 // if there are no resizable splits make them all resizable
201                 if numr == 0 {
202                         numr = numnr
203                 }
204
205                 height := (n.H - nonrh) / (numr + 1)
206
207                 newid := NewID()
208                 hn := NewNode(STHoriz, n.X, 0, n.W, height, n, newid)
209                 n.children = append(n.children, nil)
210                 inspos := i
211                 if right {
212                         inspos++
213                 }
214                 copy(n.children[inspos+1:], n.children[inspos:])
215                 n.children[inspos] = hn
216
217                 y := n.Y
218                 for _, c := range n.children {
219                         c.Y = y
220                         if c.CanResize() {
221                                 c.Resize(c.W, height)
222                         }
223                         y += c.H
224                 }
225                 n.alignSize()
226                 return newid
227         }
228 }
229 func (n *Node) hVSplit(i int, right bool) uint64 {
230         if n.IsLeaf() {
231                 newid := NewID()
232                 vn1 := NewNode(STVert, n.X, n.Y, n.W/2, n.H, n, n.id)
233                 vn2 := NewNode(STVert, n.X+vn1.W, n.Y, n.W/2, n.H, n, newid)
234                 if !right {
235                         vn1.id, vn2.id = vn2.id, vn1.id
236                 }
237
238                 n.children = append(n.children, vn1, vn2)
239                 n.alignSize()
240                 return newid
241         } else {
242                 numr := 0
243                 numnr := 0
244                 nonrw := 0
245                 for _, c := range n.children {
246                         if !c.CanResize() {
247                                 nonrw += c.W
248                                 numnr++
249                         } else {
250                                 numr++
251                         }
252                 }
253
254                 // if there are no resizable splits make them all resizable
255                 if numr == 0 {
256                         numr = numnr
257                 }
258
259                 width := (n.W - nonrw) / (numr + 1)
260
261                 newid := NewID()
262                 vn := NewNode(STVert, 0, n.Y, width, n.H, n, newid)
263                 n.children = append(n.children, nil)
264                 inspos := i
265                 if right {
266                         inspos++
267                 }
268                 copy(n.children[inspos+1:], n.children[inspos:])
269                 n.children[inspos] = vn
270
271                 x := n.X
272                 for _, c := range n.children {
273                         c.X = x
274                         if c.CanResize() {
275                                 c.Resize(width, c.H)
276                         }
277                         x += c.W
278                 }
279                 n.alignSize()
280                 return newid
281         }
282 }
283
284 func (n *Node) HSplit(bottom bool) uint64 {
285         if !n.IsLeaf() {
286                 return 0
287         }
288         if n.Kind == STUndef {
289                 n.Kind = STVert
290         }
291         if n.Kind == STVert {
292                 return n.vHSplit(0, bottom)
293         }
294         return n.hHSplit(bottom)
295 }
296
297 func (n *Node) VSplit(right bool) uint64 {
298         if !n.IsLeaf() {
299                 return 0
300         }
301         if n.Kind == STUndef {
302                 n.Kind = STHoriz
303         }
304         if n.Kind == STVert {
305                 return n.vVSplit(right)
306         }
307         return n.hVSplit(0, right)
308 }
309
310 func (n *Node) Resize(w, h int) {
311         if n.IsLeaf() {
312                 n.W, n.H = w, h
313         } else {
314                 propW, propH := float64(w)/float64(n.W), float64(h)/float64(n.H)
315                 log.Println(w, h, n.W, n.H, propW, propH)
316                 x, y := n.X, n.Y
317                 for i, c := range n.children {
318                         cW := int(float64(c.W) * propW)
319                         // if c.IsLeaf() && i != len(n.children)-1 {
320                         //      cW++
321                         // }
322                         log.Println("WIDTH:", cW, c.W)
323                         cH := int(float64(c.H) * propH)
324                         c.Resize(cW, cH)
325                         c.X = x
326                         c.Y = y
327                         if n.Kind == STHoriz {
328                                 x += cW
329                         } else {
330                                 y += cH
331                         }
332                 }
333                 n.alignSize()
334                 n.W, n.H = w, h
335         }
336 }
337
338 func (n *Node) alignSize() {
339         if len(n.children) == 0 {
340                 return
341         }
342
343         totw, toth := 0, 0
344         for i, c := range n.children {
345                 if n.Kind == STHoriz {
346                         if i != len(n.children)-1 {
347                                 c.Resize(c.W-1, c.H)
348                         }
349                         totw += c.W
350                 } else {
351                         toth += c.H
352                 }
353         }
354         if n.Kind == STVert && toth != n.H {
355                 last := n.children[len(n.children)-1]
356                 last.Resize(last.W, last.H+n.H-toth)
357         } else if n.Kind == STHoriz && totw != n.W {
358                 last := n.children[len(n.children)-1]
359                 last.Resize(last.W+n.W-totw, last.H)
360         }
361 }
362
363 func (n *Node) Unsplit() {
364
365 }
366
367 func (n *Node) String() string {
368         var strf func(n *Node, ident int) string
369         strf = func(n *Node, ident int) string {
370                 marker := "|"
371                 if n.Kind == STHoriz {
372                         marker = "-"
373                 }
374                 str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id)
375                 if n.IsLeaf() {
376                         str += "🍁"
377                 }
378                 str += "\n"
379                 for _, c := range n.children {
380                         str += strf(c, ident+1)
381                 }
382                 return str
383         }
384         return strf(n, 0)
385 }