forked from rivo/tview
-
Notifications
You must be signed in to change notification settings - Fork 0
/
flex.go
153 lines (135 loc) · 3.99 KB
/
flex.go
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package tview
import (
"github.com/gdamore/tcell"
)
// Configuration values.
const (
FlexRow = iota
FlexColumn
)
// flexItem holds layout options for one item.
type flexItem struct {
Item Primitive // The item to be positioned.
FixedSize int // The item's fixed size which may not be changed, 0 if it has no fixed size.
Proportion int // The item's proportion.
Focus bool // Whether or not this item attracts the layout's focus.
}
// Flex is a basic implementation of the Flexbox layout.
//
// See https://github.com/rivo/tview/wiki/Flex for an example.
type Flex struct {
*Box
// The items to be positioned.
items []flexItem
// FlexRow or FlexColumn.
direction int
// If set to true, will use the entire screen as its available space instead
// its box dimensions.
fullScreen bool
}
// NewFlex returns a new flexbox layout container with the given primitives.
// The items all have no fixed size. If more control is needed, call AddItem().
// The direction argument must be FlexRow or FlexColumn.
func NewFlex() *Flex {
f := &Flex{
Box: NewBox(),
direction: FlexColumn,
}
f.focus = f
return f
}
// SetDirection sets the direction in which the contained primitives are
// distributed. This can be either FlexColumn (default) or FlexRow.
func (f *Flex) SetDirection(direction int) *Flex {
f.direction = direction
return f
}
// SetFullScreen sets the flag which, when true, causes the flex layout to use
// the entire screen space instead of whatever size it is currently assigned to.
func (f *Flex) SetFullScreen(fullScreen bool) *Flex {
f.fullScreen = fullScreen
return f
}
// AddItem adds a new item to the container. The "fixedSize" argument is a width
// or height that may not be changed by the layout algorithm. A value of 0 means
// that its size is flexible and may be changed. The "proportion" argument
// defines the relative size of the item compared to other flexible-size items.
// For example, items with a proportion of 2 will be twice as large as items
// with a proportion of 1. Must be at least 1 if fixedSize > 0 (ignored
// otherwise)
//
// If "focus" is set to true, the item will receive focus when the Flex
// primitive receives focus. If multiple items have the "focus" flag set to
// true, the first one will receive focus.
func (f *Flex) AddItem(item Primitive, fixedSize, proportion int, focus bool) *Flex {
f.items = append(f.items, flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus})
return f
}
// Draw draws this primitive onto the screen.
func (f *Flex) Draw(screen tcell.Screen) {
// Calculate size and position of the items.
// Do we use the entire screen?
if f.fullScreen {
f.x = 0
f.y = 0
width, height := screen.Size()
f.width = width
f.height = height
}
// How much space can we distribute?
x, y, width, height := f.GetInnerRect()
var proportionSum int
distSize := width
if f.direction == FlexRow {
distSize = height
}
for _, item := range f.items {
if item.FixedSize > 0 {
distSize -= item.FixedSize
} else {
proportionSum += item.Proportion
}
}
// Calculate positions and draw items.
pos := x
if f.direction == FlexRow {
pos = y
}
for _, item := range f.items {
size := item.FixedSize
if size <= 0 {
size = distSize * item.Proportion / proportionSum
distSize -= size
proportionSum -= item.Proportion
}
if f.direction == FlexColumn {
item.Item.SetRect(pos, y, size, height)
} else {
item.Item.SetRect(x, pos, width, size)
}
pos += size
if item.Item.GetFocusable().HasFocus() {
defer item.Item.Draw(screen)
} else {
item.Item.Draw(screen)
}
}
}
// Focus is called when this primitive receives focus.
func (f *Flex) Focus(delegate func(p Primitive)) {
for _, item := range f.items {
if item.Focus {
delegate(item.Item)
return
}
}
}
// HasFocus returns whether or not this primitive has focus.
func (f *Flex) HasFocus() bool {
for _, item := range f.items {
if item.Item.GetFocusable().HasFocus() {
return true
}
}
return false
}