-
Notifications
You must be signed in to change notification settings - Fork 9
/
window.go
280 lines (239 loc) · 7.6 KB
/
window.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
package winman
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
// Window interface defines primitives that can be managed by the Window Manager
type Window interface {
tview.Primitive
// IsModal defines this window as modal. When a window is modal, input cannot go to other windows
IsModal() bool
// IsMaximized returns true when the window is maximized and takes all the space available to the
// Window Manager
IsMaximized() bool
// IsResizable returns true when the window can be resized by the user
IsResizable() bool
// IsDraggable returns true when the window can be moved by the user by dragging
// the title bar
IsDraggable() bool
// IsVisible returns true when the window has to be drawn and can receive focus
IsVisible() bool
// HasBorder returns true if the window must have a border
HasBorder() bool
}
// WindowBase defines a basic window
type WindowBase struct {
*tview.Box
root tview.Primitive // The item contained in the window
buttons []*Button // window buttons on the title bar
border bool // whether to render a border
restoreRect Rect // store previous coordinates after restoring from maximize
maximized bool // whether the window is maximized to the entire window manager area
Draggable bool //whether this window can be dragged around with the mouse
Resizable bool // whether this window is user-resizable
Modal bool // whether this window is modal
Visible bool // whether this window is rendered
}
// NewWindow creates a new window
func NewWindow() *WindowBase {
window := &WindowBase{
Box: tview.NewBox(), // initialize underlying box
}
window.restoreRect = NewRect(window.GetRect())
window.SetBorder(true)
return window
}
// SetRoot sets the main content of the window
func (w *WindowBase) SetRoot(root tview.Primitive) *WindowBase {
w.root = root
return w
}
// GetRoot returns the primitive that represents the main content of the window
func (w *WindowBase) GetRoot() tview.Primitive {
return w.root
}
// SetModal makes this window modal. A modal window captures all input
func (w *WindowBase) SetModal(modal bool) *WindowBase {
w.Modal = modal
return w
}
// IsModal returns true if this window is modal
func (w *WindowBase) IsModal() bool {
return w.Modal
}
// HasBorder returns true if this window has a border
// windows without border cannot be resized or dragged by the user
func (w *WindowBase) HasBorder() bool {
return w.border
}
// SetBorder sets the flag indicating whether or not the box should have a
// border.
func (w *WindowBase) SetBorder(show bool) *WindowBase {
w.border = show
w.Box.SetBorder(show)
return w
}
// IsDraggable returns true if this window can be dragged by the user
func (w *WindowBase) IsDraggable() bool {
return w.Draggable
}
// SetDraggable sets if this window can be dragged by the user
func (w *WindowBase) SetDraggable(draggable bool) *WindowBase {
w.Draggable = draggable
return w
}
// IsResizable returns true if the user may resize this window
func (w *WindowBase) IsResizable() bool {
return w.Resizable
}
// SetResizable sets if this window can be resized
func (w *WindowBase) SetResizable(resizable bool) *WindowBase {
w.Resizable = resizable
return w
}
// SetTitle sets the window title
func (w *WindowBase) SetTitle(text string) *WindowBase {
w.Box.SetTitle(text)
return w
}
// IsVisible returns true if this window is rendered and may
// get focus
func (w *WindowBase) IsVisible() bool {
return w.Visible
}
// Show makes the window visible
func (w *WindowBase) Show() *WindowBase {
w.Visible = true
return w
}
// Hide hides this window
func (w *WindowBase) Hide() *WindowBase {
w.Visible = false
return w
}
// Draw draws this primitive on to the screen
func (w *WindowBase) Draw(screen tcell.Screen) {
if w.HasFocus() { // if the window has focus, make sure the underlying box shows a thicker border
w.Box.Focus(nil)
} else {
w.Box.Blur()
}
w.Box.Draw(screen) // draw the window frame
// draw the underlying root primitive within the window bounds
if w.root != nil {
x, y, width, height := w.GetInnerRect()
w.root.SetRect(x, y, width, height)
w.root.Draw(NewClipRegion(screen, x, y, width, height))
}
// draw the window border
if w.border {
x, y, width, height := w.GetRect()
screen = NewClipRegion(screen, x, y, width, height)
for _, button := range w.buttons {
buttonX, buttonY := button.offsetX+x, button.offsetY+y
if button.offsetX < 0 {
buttonX += width
}
if button.offsetY < 0 {
buttonY += height
}
// render the window title buttons
tview.Print(screen, tview.Escape(fmt.Sprintf("[%c]", button.Symbol)), buttonX-1, buttonY, 9, 0, tcell.ColorYellow)
}
}
}
// Maximize signals the window manager to resize this window to the maximum size available
func (w *WindowBase) Maximize() *WindowBase {
w.restoreRect = NewRect(w.GetRect())
w.maximized = true
return w
}
// IsMaximized returns true if this window is maximized
func (w *WindowBase) IsMaximized() bool {
return w.maximized
}
// Restore restores the window to the size it had before maximizing
func (w *WindowBase) Restore() *WindowBase {
w.SetRect(w.restoreRect.Rect())
w.maximized = false
return w
}
// Focus is called when this primitive receives focus.
func (w *WindowBase) Focus(delegate func(p tview.Primitive)) {
if w.root != nil {
delegate(w.root)
} else {
delegate(w.Box)
}
w.Visible = true
}
// HasFocus returns whether or not this primitive has focus.
func (w *WindowBase) HasFocus() bool {
if !w.Visible {
return false
}
if w.root != nil {
return w.root.HasFocus()
}
return w.Box.HasFocus()
}
// MouseHandler returns a mouse handler for this primitive
func (w *WindowBase) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
return w.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
if action == tview.MouseLeftClick {
x, y := event.Position()
wx, wy, width, _ := w.GetRect()
// check if any window button was pressed
// if the window does not have border, it cannot receive button events
if y == wy && w.border {
for _, button := range w.buttons {
if button.offsetX >= 0 && x == wx+button.offsetX || button.offsetX < 0 && x == wx+width+button.offsetX {
if button.OnClick != nil {
button.OnClick()
}
return true, nil
}
}
}
}
// pass on clicks to the root primitive, if any
if w.root != nil {
return w.root.MouseHandler()(action, event, setFocus)
}
return w.Box.MouseHandler()(action, event, setFocus)
})
}
// InputHandler returns a handler which receives key events when it has focus.
func (w *WindowBase) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
if w.root != nil {
return w.root.InputHandler()
}
return nil
}
// AddButton adds a new window button to the title bar
func (w *WindowBase) AddButton(button *Button) *WindowBase {
w.buttons = append(w.buttons, button)
offsetLeft, offsetRight := 2, -3
for _, button := range w.buttons {
if button.Alignment == ButtonRight {
button.offsetX = offsetRight
offsetRight -= 3
} else {
button.offsetX = offsetLeft
offsetLeft += 3
}
}
return w
}
// GetButton returns the given button
func (w *WindowBase) GetButton(i int) *Button {
if i < 0 || i >= len(w.buttons) {
return nil
}
return w.buttons[i]
}
// ButtonCount returns the number of buttons in the window title bar
func (w *WindowBase) ButtonCount() int {
return len(w.buttons)
}