18 // NewID returns a new unique id
24 // A View is a size and location of a split
30 // A Node describes a split in the tree
31 // If a node is a leaf node then it corresponds to a buffer that is being
32 // displayed otherwise it has a number of children of the opposite type
33 // (vertical splits have horizontal children and vice versa)
42 // Nodes can be marked as non resizable if they shouldn't be rescaled
43 // when the terminal window is resized or when a new split is added
44 // Only the splits on the edges of the screen can be marked as non resizable
46 // A node may also be marked with proportional scaling. This means that when
47 // the window is resized the split maintains its proportions
50 // Defines the proportion of the screen this node should take up if propScale is
53 // The id is unique for each leaf node and provides a way to keep track of a split
58 // NewNode returns a new node with the given specifications
59 func NewNode(Kind SplitType, x, y, w, h int, parent *Node, id uint64) *Node {
64 n.X, n.Y, n.W, n.H = x, y, w, h
65 n.children = make([]*Node, 0)
69 n.propW, n.propH = float64(w)/float64(parent.W), float64(h)/float64(parent.H)
71 n.propW, n.propH = 1, 1
77 // NewRoot returns an empty Node with a size and location
78 // The type of the node will be determined by the first action on the node
79 // In other words, a lone split is neither horizontal nor vertical, it only
80 // becomes one or the other after a vsplit or hsplit is made
81 func NewRoot(x, y, w, h int) *Node {
82 n1 := NewNode(STUndef, x, y, w, h, nil, NewID())
87 // IsLeaf returns if this node is a leaf node
88 func (n *Node) IsLeaf() bool {
89 return len(n.children) == 0
92 // ID returns this node's id or 0 if it is not viewable
93 func (n *Node) ID() uint64 {
100 // CanResize returns if this node can be resized
101 func (n *Node) CanResize() bool {
105 // PropScale returns if this node is proportionally scaled
106 func (n *Node) PropScale() bool {
110 // SetResize sets the resize flag
111 func (n *Node) SetResize(b bool) {
115 // SetPropScale sets the propScale flag
116 func (n *Node) SetPropScale(b bool) {
120 // Children returns this node's children
121 func (n *Node) Children() []*Node {
125 // GetNode returns the node with the given id in the tree of children
126 // that this node has access to or nil if the node with that id cannot be found
127 func (n *Node) GetNode(id uint64) *Node {
128 if n.id == id && n.IsLeaf() {
131 for _, c := range n.children {
132 if c.id == id && c.IsLeaf() {
143 func (n *Node) vResizeSplit(i int, size int) bool {
144 if i < 0 || i >= len(n.children) {
148 if i == len(n.children)-1 {
149 c1, c2 = n.children[i-1], n.children[i]
151 c1, c2 = n.children[i], n.children[i+1]
158 c1.Resize(c1.W, size)
159 c2.Resize(c2.W, toth-size)
161 n.alignSizes(n.W, n.H)
164 func (n *Node) hResizeSplit(i int, size int) bool {
165 if i < 0 || i >= len(n.children) {
169 if i == len(n.children)-1 {
170 c1, c2 = n.children[i-1], n.children[i]
172 c1, c2 = n.children[i], n.children[i+1]
179 c1.Resize(size, c1.H)
180 c2.Resize(totw-size, c2.H)
182 n.alignSizes(n.W, n.H)
186 // ResizeSplit resizes a certain split to a given size
187 func (n *Node) ResizeSplit(size int) bool {
188 if len(n.parent.children) <= 1 {
189 // cannot resize a lone node
193 for i, c := range n.parent.children {
198 if n.parent.Kind == STVert {
199 return n.parent.vResizeSplit(ind, size)
201 return n.parent.hResizeSplit(ind, size)
204 // Resize sets this node's size and resizes all children accordlingly
205 func (n *Node) Resize(w, h int) {
214 for _, c := range n.children {
215 cW := int(float64(w) * c.propW)
216 cH := int(float64(h) * c.propH)
220 if n.Kind == STHoriz {
229 n.alignSizes(totw, toth)
232 func (n *Node) alignSizes(totw, toth int) {
233 // Make sure that there are no off-by-one problems with the rounding
234 // of the sizes by making the final split fill the screen
235 if n.Kind == STVert && toth != n.H {
236 last := n.children[len(n.children)-1]
237 last.Resize(last.W, last.H+n.H-toth)
238 } else if n.Kind == STHoriz && totw != n.W {
239 last := n.children[len(n.children)-1]
240 last.Resize(last.W+n.W-totw, last.H)
244 // Resets all proportions for children
245 func (n *Node) markSizes() {
246 for _, c := range n.children {
247 c.propW = float64(c.W) / float64(n.W)
248 c.propH = float64(c.H) / float64(n.H)
253 func (n *Node) markResize() {
258 // vsplits a vertical split and returns the id of the new split
259 func (n *Node) vVSplit(right bool) uint64 {
261 for i, c := range n.parent.children {
266 return n.parent.hVSplit(ind, right)
269 // hsplits a horizontal split
270 func (n *Node) hHSplit(bottom bool) uint64 {
272 for i, c := range n.parent.children {
277 return n.parent.vHSplit(ind, bottom)
280 // Returns the size of the non-resizable area and the number of resizable
282 func (n *Node) getResizeInfo(h bool) (int, int) {
286 for _, c := range n.children {
299 // if there are no resizable splits make them all resizable
307 func (n *Node) applyNewSize(size int, h bool) {
312 for _, c := range n.children {
334 // hsplits a vertical split
335 func (n *Node) vHSplit(i int, right bool) uint64 {
338 hn1 := NewNode(STHoriz, n.X, n.Y, n.W, n.H/2, n, n.id)
339 hn2 := NewNode(STHoriz, n.X, n.Y+hn1.H, n.W, n.H/2, n, newid)
341 hn1.id, hn2.id = hn2.id, hn1.id
344 n.children = append(n.children, hn1, hn2)
348 nonrh, numr := n.getResizeInfo(true)
350 // size of resizable area
351 height := (n.H - nonrh) / (numr + 1)
354 hn := NewNode(STHoriz, n.X, 0, n.W, height, n, newid)
356 // insert the node into the correct slot
357 n.children = append(n.children, nil)
362 copy(n.children[inspos+1:], n.children[inspos:])
363 n.children[inspos] = hn
365 n.applyNewSize(height, true)
370 // vsplits a horizontal split
371 func (n *Node) hVSplit(i int, right bool) uint64 {
374 vn1 := NewNode(STVert, n.X, n.Y, n.W/2, n.H, n, n.id)
375 vn2 := NewNode(STVert, n.X+vn1.W, n.Y, n.W/2, n.H, n, newid)
377 vn1.id, vn2.id = vn2.id, vn1.id
380 n.children = append(n.children, vn1, vn2)
384 nonrw, numr := n.getResizeInfo(false)
386 width := (n.W - nonrw) / (numr + 1)
389 vn := NewNode(STVert, 0, n.Y, width, n.H, n, newid)
391 // Inser the node into the correct slot
392 n.children = append(n.children, nil)
397 copy(n.children[inspos+1:], n.children[inspos:])
398 n.children[inspos] = vn
400 n.applyNewSize(width, false)
405 // HSplit creates a horizontal split and returns the id of the new split
406 // bottom specifies if the new split should be created on the top or bottom
407 // of the current split
408 func (n *Node) HSplit(bottom bool) uint64 {
412 if n.Kind == STUndef {
415 if n.Kind == STVert {
416 return n.vHSplit(0, bottom)
418 return n.hHSplit(bottom)
421 // VSplit creates a vertical split and returns the id of the new split
422 // right specifies if the new split should be created on the right or left
423 // of the current split
424 func (n *Node) VSplit(right bool) uint64 {
428 if n.Kind == STUndef {
431 if n.Kind == STVert {
432 return n.vVSplit(right)
434 return n.hVSplit(0, right)
437 // unsplits the child of a split
438 func (n *Node) unsplit(i int, h bool) {
439 copy(n.children[i:], n.children[i+1:])
440 n.children[len(n.children)-1] = nil
441 n.children = n.children[:len(n.children)-1]
443 nonrs, numr := n.getResizeInfo(h)
445 // This means that this was the last child
446 // The parent will get cleaned up in the next iteration and
447 // will resolve all sizing issues with its parent
450 size := (n.W - nonrs) / numr
452 size = (n.H - nonrs) / numr
454 n.applyNewSize(size, h)
457 // Unsplit deletes this split and resizes everything
459 func (n *Node) Unsplit() {
464 for i, c := range n.parent.children {
469 if n.parent.Kind == STVert {
470 n.parent.unsplit(ind, true)
472 n.parent.unsplit(ind, false)
475 if n.parent.IsLeaf() {
480 // String returns the string form of the node and all children (used for debugging)
481 func (n *Node) String() string {
482 var strf func(n *Node, ident int) string
483 strf = func(n *Node, ident int) string {
485 if n.Kind == STHoriz {
488 str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id)
493 for _, c := range n.children {
494 str += strf(c, ident+1)