-
Notifications
You must be signed in to change notification settings - Fork 50
/
checkbox.go
206 lines (170 loc) · 4.72 KB
/
checkbox.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package clui
import (
xs "github.com/huandu/xstrings"
term "github.com/nsf/termbox-go"
)
/*
CheckBox control. It can be two-state one(on and off) - it is default mode - or three-state.
State values are 0=off, 1=on, 2=third state
*/
type CheckBox struct {
BaseControl
state int
allow3state bool
onChange func(int)
}
/*
CreateCheckBox creates a new CheckBox control.
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
width - is minimal width of the control.
title - button title.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
CheckBox state can be changed using mouse or pressing space on keyboard while the control is active
*/
func CreateCheckBox(parent Control, width int, title string, scale int) *CheckBox {
c := new(CheckBox)
c.BaseControl = NewBaseControl()
c.parent = parent
if width == AutoSize {
width = xs.Len(title) + 4
}
c.SetSize(width, 1) // TODO: only one line checkboxes are supported at that moment
c.SetConstraints(width, 1)
c.state = 0
c.SetTitle(title)
c.SetTabStop(true)
c.allow3state = false
c.onChange = nil
c.SetScale(scale)
if parent != nil {
parent.AddChild(c)
}
return c
}
// Repaint draws the control on its View surface
func (c *CheckBox) Draw() {
if c.hidden {
return
}
c.mtx.RLock()
defer c.mtx.RUnlock()
PushAttributes()
defer PopAttributes()
x, y := c.Pos()
w, h := c.Size()
fg, bg := RealColor(c.fg, c.Style(), ColorControlText), RealColor(c.bg, c.Style(), ColorControlBack)
if !c.Enabled() {
fg, bg = RealColor(c.fg, c.Style(), ColorControlDisabledText), RealColor(c.bg, c.Style(), ColorControlDisabledBack)
} else if c.Active() {
fg, bg = RealColor(c.fg, c.Style(), ColorControlActiveText), RealColor(c.bg, c.Style(), ColorControlActiveBack)
}
parts := []rune(SysObject(ObjCheckBox))
cOpen, cClose, cEmpty, cCheck, cUnknown := parts[0], parts[1], parts[2], parts[3], parts[4]
cState := []rune{cEmpty, cCheck, cUnknown}
SetTextColor(fg)
SetBackColor(bg)
FillRect(x, y, w, h, ' ')
if w < 3 {
return
}
PutChar(x, y, cOpen)
PutChar(x+2, y, cClose)
PutChar(x+1, y, cState[c.state])
if w < 5 {
return
}
shift, text := AlignColorizedText(c.title, w-4, c.align)
DrawText(x+4+shift, y, text)
}
//ProcessEvent processes all events come from the control parent. If a control
// processes an event it should return true. If the method returns false it means
// that the control do not want or cannot process the event and the caller sends
// the event to the control parent
func (c *CheckBox) ProcessEvent(event Event) bool {
if (!c.Active() && event.Type == EventKey) || !c.Enabled() {
return false
}
if (event.Type == EventKey && event.Key == term.KeySpace) || (event.Type == EventClick) {
if c.state == 0 {
c.SetState(1)
} else if c.state == 2 {
c.SetState(0)
} else {
if c.allow3state {
c.SetState(2)
} else {
c.SetState(0)
}
}
return true
}
return false
}
// SetState changes the current state of CheckBox
// Value must be 0 or 1 if Allow3State is off,
// and 0, 1, or 2 if Allow3State is on
func (c *CheckBox) SetState(val int) {
c.mtx.Lock()
defer c.mtx.Unlock()
if val == c.state {
return
}
if val < 0 {
val = 0
}
if val > 1 && !c.allow3state {
val = 1
}
if val > 2 {
val = 2
}
c.state = val
if c.onChange != nil {
go c.onChange(val)
}
}
// State returns current state of CheckBox
func (c *CheckBox) State() int {
c.mtx.RLock()
defer c.mtx.RUnlock()
return c.state
}
// SetAllow3State sets if ComboBox should use 3 states. If the current
// state is unknown and one disables Allow3State option then the current
// value resets to off
func (c *CheckBox) SetAllow3State(enable bool) {
if !enable && c.state == 2 {
c.state = 0
}
c.allow3state = enable
}
// Allow3State returns true if ComboBox uses 3 states
func (c *CheckBox) Allow3State() bool {
return c.allow3state
}
// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// CheckBox height cannot be changed - it equals 1 always
func (c *CheckBox) SetSize(width, height int) {
if width != KeepValue && (width > 1000 || width < c.minW) {
return
}
if height != KeepValue && (height > 200 || height < c.minH) {
return
}
if width != KeepValue {
c.width = width
}
c.height = 1
}
// OnChange sets the callback that is called whenever the state
// of the CheckBox is changed. Argument of callback is the current
// CheckBox state: 0 - off, 1 - on, 2 - third state
func (c *CheckBox) OnChange(fn func(int)) {
c.mtx.Lock()
defer c.mtx.Unlock()
c.onChange = fn
}