github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/misc/logos/stack.go (about) 1 package logos 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/gdamore/tcell/v2" 8 ) 9 10 //---------------------------------------- 11 // Stack 12 13 // A Stack is like a Page, but it only highlights the top 14 // element, and dims, occludes, or hides lower elements. A 15 // Stack is therefore ideal for showing modal views. NOTE: 16 // While most applications shouldn't, it's perfectly fine to 17 // embed Stacks within Stacks, or layer them on top within a 18 // stack. 19 type Stack struct { 20 Page // a Stack has the same fields as a page. 21 } 22 23 func NewStack(size Size) *Stack { 24 return &Stack{ 25 Page: Page{ 26 Size: size, // dontcare. 27 Elems: nil, // nil layers. 28 Cursor: -1, 29 }, 30 } 31 } 32 33 func (st *Stack) StringIndented(indent string) string { 34 elines := []string{} 35 eindent := indent + " " 36 for _, elem := range st.Elems { 37 elines = append(elines, eindent+elem.StringIndented(eindent)) 38 } 39 return fmt.Sprintf("Stack%v@%p\n%s", 40 st.Size, 41 st, 42 strings.Join(elines, "\n")) 43 } 44 45 func (st *Stack) String() string { 46 return fmt.Sprintf("Stack%v{%d}@%p", 47 st.Size, 48 len(st.Elems), 49 st) 50 } 51 52 func (st *Stack) PushLayer(layer Elem) { 53 layer.SetParent(st) 54 st.Elems = append(st.Elems, layer) 55 st.Cursor++ 56 st.SetIsDirty(true) 57 } 58 59 // A Stack's size is simply determined by its .Size. 60 func (st *Stack) Measure() Size { 61 return st.Size 62 } 63 64 // A Stack's render function behaves the same as a Page's; 65 // it renders its elements (here, its layers). 66 func (st *Stack) Render() (updated bool) { 67 return st.Page.Render() 68 } 69 70 // Draw the rendered layers onto the view. Any dimming of 71 // occluded layers must actually stretch in all directions 72 // infinitely (since we can scroll beyond the bounds of any 73 // view and we expect the dimming effect to carry while we 74 // scroll), so the entire view is dimmed first, and then the 75 // upper-most layer is drawn. 76 func (st *Stack) Draw(offset Coord, view View) { 77 // Draw bottom layers. 78 if 1 < len(st.Elems) { 79 for _, elem := range st.Elems[:len(st.Elems)-1] { 80 loffset := offset.Sub(elem.GetCoord()) 81 elem.Draw(loffset, view) 82 } 83 } 84 if 0 < len(st.Elems) { 85 last := st.Elems[len(st.Elems)-1] 86 loffset := offset.Sub(last.GetCoord()) 87 // Draw occlusion screen on view. 88 for y := 0; y < view.Bounds.Height; y++ { 89 for x := 0; x < view.Bounds.Width; x++ { 90 vcell := view.GetCell(x, y) 91 inBounds := IsInBounds(x, y, 92 loffset.Neg(), 93 last.GetSize()) 94 if inBounds { 95 // Reset unsets residual "occluded", 96 // "cursor", and other attributes from the 97 // previous layer which are no longer 98 // relevant. 99 vcell.Reset() 100 } else { 101 vcell.SetIsOccluded(true) 102 } 103 } 104 } 105 // Draw last (top) layer. 106 last.Draw(loffset, view) 107 } else { 108 // Draw occlusion screen on view. 109 for y := 0; y < view.Bounds.Height; y++ { 110 for x := 0; x < view.Bounds.Width; x++ { 111 vcell := view.GetCell(x, y) 112 vcell.SetIsOccluded(true) 113 } 114 } 115 } 116 } 117 118 func (st *Stack) ProcessEventKey(ev *EventKey) bool { 119 // An empty *Stack is inert. 120 if len(st.Page.Elems) == 0 { 121 return false 122 } 123 // Try to let the last layer handle it. 124 last := st.Page.Elems[len(st.Page.Elems)-1] 125 if last.ProcessEventKey(ev) { 126 return true 127 } 128 // Maybe it's something for the stack. 129 switch ev.Key() { 130 case tcell.KeyEsc: 131 if 1 < len(st.Page.Elems) { 132 // Pop the last layer. 133 st.Elems = st.Elems[:len(st.Elems)-1] 134 st.Cursor-- 135 st.SetIsDirty(true) 136 return true 137 } else { 138 // Let the last layer stick around. 139 return false 140 } 141 default: 142 return false 143 } 144 } 145 146 // Traverses the inclusive ancestors of elem and returns the 147 // first *Stack encountered. The purpose of this function is 148 // to find where to push new layers and modal elements for 149 // drawing. 150 func StackOf(elem Elem) *Stack { 151 for elem != nil { 152 fmt.Println("StackOf", elem) 153 if st, ok := elem.(*Stack); ok { 154 return st 155 } else { 156 elem = elem.GetParent() 157 } 158 } 159 return nil // no stack 160 }