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  }