1// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
2// Use of this source code is governed by a MIT license that can
3// be found in the LICENSE file.
4
5package termui
6
7type gridItemType uint
8
9const (
10	col gridItemType = 0
11	row gridItemType = 1
12)
13
14type Grid struct {
15	Block
16	Items []*GridItem
17}
18
19// GridItem represents either a Row or Column in a grid.
20// Holds sizing information and either an []GridItems or a widget.
21type GridItem struct {
22	Type        gridItemType
23	XRatio      float64
24	YRatio      float64
25	WidthRatio  float64
26	HeightRatio float64
27	Entry       interface{} // Entry.type == GridBufferer if IsLeaf else []GridItem
28	IsLeaf      bool
29	ratio       float64
30}
31
32func NewGrid() *Grid {
33	g := &Grid{
34		Block: *NewBlock(),
35	}
36	g.Border = false
37	return g
38}
39
40// NewCol takes a height percentage and either a widget or a Row or Column
41func NewCol(ratio float64, i ...interface{}) GridItem {
42	_, ok := i[0].(Drawable)
43	entry := i[0]
44	if !ok {
45		entry = i
46	}
47	return GridItem{
48		Type:   col,
49		Entry:  entry,
50		IsLeaf: ok,
51		ratio:  ratio,
52	}
53}
54
55// NewRow takes a width percentage and either a widget or a Row or Column
56func NewRow(ratio float64, i ...interface{}) GridItem {
57	_, ok := i[0].(Drawable)
58	entry := i[0]
59	if !ok {
60		entry = i
61	}
62	return GridItem{
63		Type:   row,
64		Entry:  entry,
65		IsLeaf: ok,
66		ratio:  ratio,
67	}
68}
69
70// Set is used to add Columns and Rows to the grid.
71// It recursively searches the GridItems, adding leaves to the grid and calculating the dimensions of the leaves.
72func (self *Grid) Set(entries ...interface{}) {
73	entry := GridItem{
74		Type:   row,
75		Entry:  entries,
76		IsLeaf: false,
77		ratio:  1.0,
78	}
79	self.setHelper(entry, 1.0, 1.0)
80}
81
82func (self *Grid) setHelper(item GridItem, parentWidthRatio, parentHeightRatio float64) {
83	var HeightRatio float64
84	var WidthRatio float64
85	switch item.Type {
86	case col:
87		HeightRatio = 1.0
88		WidthRatio = item.ratio
89	case row:
90		HeightRatio = item.ratio
91		WidthRatio = 1.0
92	}
93	item.WidthRatio = parentWidthRatio * WidthRatio
94	item.HeightRatio = parentHeightRatio * HeightRatio
95
96	if item.IsLeaf {
97		self.Items = append(self.Items, &item)
98	} else {
99		XRatio := 0.0
100		YRatio := 0.0
101		cols := false
102		rows := false
103
104		children := InterfaceSlice(item.Entry)
105
106		for i := 0; i < len(children); i++ {
107			if children[i] == nil {
108				continue
109			}
110			child, _ := children[i].(GridItem)
111
112			child.XRatio = item.XRatio + (item.WidthRatio * XRatio)
113			child.YRatio = item.YRatio + (item.HeightRatio * YRatio)
114
115			switch child.Type {
116			case col:
117				cols = true
118				XRatio += child.ratio
119				if rows {
120					item.HeightRatio /= 2
121				}
122			case row:
123				rows = true
124				YRatio += child.ratio
125				if cols {
126					item.WidthRatio /= 2
127				}
128			}
129
130			self.setHelper(child, item.WidthRatio, item.HeightRatio)
131		}
132	}
133}
134
135func (self *Grid) Draw(buf *Buffer) {
136	width := float64(self.Dx()) + 1
137	height := float64(self.Dy()) + 1
138
139	for _, item := range self.Items {
140		entry, _ := item.Entry.(Drawable)
141
142		x := int(width*item.XRatio) + self.Min.X
143		y := int(height*item.YRatio) + self.Min.Y
144		w := int(width * item.WidthRatio)
145		h := int(height * item.HeightRatio)
146
147		if x+w > self.Dx() {
148			w--
149		}
150		if y+h > self.Dy() {
151			h--
152		}
153
154		entry.SetRect(x, y, x+w, y+h)
155
156		entry.Lock()
157		entry.Draw(buf)
158		entry.Unlock()
159	}
160}
161