forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stack.go
160 lines (147 loc) · 3.78 KB
/
stack.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
package logos
import (
"fmt"
"strings"
"github.com/gdamore/tcell/v2"
)
//----------------------------------------
// Stack
// A Stack is like a Page, but it only highlights the top
// element, and dims, occludes, or hides lower elements. A
// Stack is therefore ideal for showing modal views. NOTE:
// While most applications shouldn't, it's perfectly fine to
// embed Stacks within Stacks, or layer them on top within a
// stack.
type Stack struct {
Page // a Stack has the same fields as a page.
}
func NewStack(size Size) *Stack {
return &Stack{
Page: Page{
Size: size, // dontcare.
Elems: nil, // nil layers.
Cursor: -1,
},
}
}
func (st *Stack) StringIndented(indent string) string {
elines := []string{}
eindent := indent + " "
for _, elem := range st.Elems {
elines = append(elines, eindent+elem.StringIndented(eindent))
}
return fmt.Sprintf("Stack%v@%p\n%s",
st.Size,
st,
strings.Join(elines, "\n"))
}
func (st *Stack) String() string {
return fmt.Sprintf("Stack%v{%d}@%p",
st.Size,
len(st.Elems),
st)
}
func (st *Stack) PushLayer(layer Elem) {
layer.SetParent(st)
st.Elems = append(st.Elems, layer)
st.Cursor++
st.SetIsDirty(true)
}
// A Stack's size is simply determined by its .Size.
func (st *Stack) Measure() Size {
return st.Size
}
// A Stack's render function behaves the same as a Page's;
// it renders its elements (here, its layers).
func (st *Stack) Render() (updated bool) {
return st.Page.Render()
}
// Draw the rendered layers onto the view. Any dimming of
// occluded layers must actually stretch in all directions
// infinitely (since we can scroll beyond the bounds of any
// view and we expect the dimming effect to carry while we
// scroll), so the entire view is dimmed first, and then the
// upper-most layer is drawn.
func (st *Stack) Draw(offset Coord, view View) {
// Draw bottom layers.
if 1 < len(st.Elems) {
for _, elem := range st.Elems[:len(st.Elems)-1] {
loffset := offset.Sub(elem.GetCoord())
elem.Draw(loffset, view)
}
}
if 0 < len(st.Elems) {
last := st.Elems[len(st.Elems)-1]
loffset := offset.Sub(last.GetCoord())
// Draw occlusion screen on view.
for y := 0; y < view.Bounds.Height; y++ {
for x := 0; x < view.Bounds.Width; x++ {
vcell := view.GetCell(x, y)
inBounds := IsInBounds(x, y,
loffset.Neg(),
last.GetSize())
if inBounds {
// Reset unsets residual "occluded",
// "cursor", and other attributes from the
// previous layer which are no longer
// relevant.
vcell.Reset()
} else {
vcell.SetIsOccluded(true)
}
}
}
// Draw last (top) layer.
last.Draw(loffset, view)
} else {
// Draw occlusion screen on view.
for y := 0; y < view.Bounds.Height; y++ {
for x := 0; x < view.Bounds.Width; x++ {
vcell := view.GetCell(x, y)
vcell.SetIsOccluded(true)
}
}
}
}
func (st *Stack) ProcessEventKey(ev *EventKey) bool {
// An empty *Stack is inert.
if len(st.Page.Elems) == 0 {
return false
}
// Try to let the last layer handle it.
last := st.Page.Elems[len(st.Page.Elems)-1]
if last.ProcessEventKey(ev) {
return true
}
// Maybe it's something for the stack.
switch ev.Key() {
case tcell.KeyEsc:
if 1 < len(st.Page.Elems) {
// Pop the last layer.
st.Elems = st.Elems[:len(st.Elems)-1]
st.Cursor--
st.SetIsDirty(true)
return true
} else {
// Let the last layer stick around.
return false
}
default:
return false
}
}
// Traverses the inclusive ancestors of elem and returns the
// first *Stack encountered. The purpose of this function is
// to find where to push new layers and modal elements for
// drawing.
func StackOf(elem Elem) *Stack {
for elem != nil {
fmt.Println("StackOf", elem)
if st, ok := elem.(*Stack); ok {
return st
} else {
elem = elem.GetParent()
}
}
return nil // no stack
}