github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/symbolwidget.go (about)

     1  package main
     2  
     3  import (
     4  	"github.com/xyproto/vt100"
     5  )
     6  
     7  // SymbolWidget represents a TUI widget for presenting a menu with choices for the user
     8  type SymbolWidget struct {
     9  	title          string               // title
    10  	choices        [][]string           // a slice of a slice of menu items
    11  	bgColor        vt100.AttributeColor // background color
    12  	highlightColor vt100.AttributeColor // selected color (the choice that has been selected after return has been pressed)
    13  	textColor      vt100.AttributeColor // text color (the choices that are not highlighted)
    14  	titleColor     vt100.AttributeColor // title color (above the choices)
    15  	x              int                  // current position
    16  	marginLeft     int                  // margin, may be negative?
    17  	marginTop      int                  // margin, may be negative?
    18  	oldy           int                  // previous position
    19  	y              int                  // current position
    20  	oldx           int                  // previous position
    21  	h              int                  // height (number of menu items)
    22  	w              int                  // width
    23  }
    24  
    25  // NewSymbolWidget creates a new SymbolWidget
    26  func NewSymbolWidget(title string, choices [][]string, titleColor, textColor, highlightColor, bgColor vt100.AttributeColor, canvasWidth, canvasHeight int) *SymbolWidget {
    27  	maxlen := 0
    28  	for _, choice := range choices {
    29  		if len(choice) > maxlen {
    30  			maxlen = len(choice)
    31  		}
    32  	}
    33  	marginLeft := 10
    34  	if canvasWidth-(maxlen+marginLeft) <= 0 {
    35  		marginLeft = 0
    36  	}
    37  	marginTop := 8
    38  	if int(canvasHeight)-(len(choices)+marginTop) <= 8 {
    39  		marginTop = 2
    40  	} else if int(canvasHeight)-(len(choices)+marginTop) <= 0 {
    41  		marginTop = 0
    42  	}
    43  	return &SymbolWidget{
    44  		title:          title,
    45  		w:              marginLeft + maxlen,
    46  		h:              len(choices),
    47  		x:              0,
    48  		oldx:           0,
    49  		y:              0,
    50  		oldy:           0,
    51  		marginLeft:     marginLeft,
    52  		marginTop:      marginTop,
    53  		choices:        choices,
    54  		titleColor:     titleColor,
    55  		textColor:      textColor,
    56  		highlightColor: highlightColor,
    57  		bgColor:        bgColor,
    58  	}
    59  }
    60  
    61  // Selected returns the currently selected item
    62  func (sw *SymbolWidget) Selected() (int, int) {
    63  	return int(sw.x), int(sw.y)
    64  }
    65  
    66  // Draw will draw this menu widget on the given canvas
    67  func (sw *SymbolWidget) Draw(c *vt100.Canvas) {
    68  	// Draw the title
    69  	titleHeight := 2
    70  	for x, r := range sw.title {
    71  		c.PlotColor(uint(sw.marginLeft+x), uint(sw.marginTop), sw.titleColor, r)
    72  	}
    73  	// Draw the menu entries, with various colors
    74  	for y := 0; y < len(sw.choices); y++ {
    75  		row := sw.choices[y]
    76  		for x := 0; x < len(row); x++ {
    77  			symbol := sw.choices[y][x]
    78  			// vt100.SetXY(0, uint(sw.marginTop+y+titleHeight))
    79  			if y == int(sw.y) && x == int(sw.x) {
    80  				c.Write(uint(sw.marginLeft+x*2), uint(sw.marginTop+y+titleHeight), sw.highlightColor, sw.bgColor, symbol)
    81  			} else {
    82  				c.Write(uint(sw.marginLeft+x*2), uint(sw.marginTop+y+titleHeight), sw.textColor, sw.bgColor, symbol)
    83  			}
    84  		}
    85  
    86  	}
    87  }
    88  
    89  // Up will move the highlight up (with wrap-around)
    90  func (sw *SymbolWidget) Up() {
    91  	sw.oldy = sw.y
    92  	if sw.y == 0 {
    93  		sw.y = len(sw.choices) - 1
    94  	} else {
    95  		sw.y--
    96  	}
    97  	// just in case rows have differing lengths
    98  	l := len(sw.choices[sw.y])
    99  	if sw.x >= l {
   100  		sw.x = l - 1
   101  	}
   102  }
   103  
   104  // Down will move the highlight down (with wrap-around)
   105  func (sw *SymbolWidget) Down() {
   106  	sw.oldy = sw.y
   107  	sw.y++
   108  	if sw.y >= len(sw.choices) {
   109  		sw.y = 0
   110  	}
   111  	l := len(sw.choices[sw.y])
   112  	if sw.x >= l {
   113  		sw.x = l - 1
   114  	}
   115  }
   116  
   117  // Left will move the highlight left (with wrap-around)
   118  func (sw *SymbolWidget) Left() bool {
   119  	sw.oldx = sw.x
   120  	sw.x--
   121  	if sw.x < 0 {
   122  		row := sw.choices[sw.y]
   123  		sw.x = len(row) - 1
   124  	}
   125  	return true
   126  }
   127  
   128  // Right will move the highlight right (with wrap-around)
   129  func (sw *SymbolWidget) Right() {
   130  	sw.oldx = sw.x
   131  	sw.x++
   132  	row := sw.choices[sw.y]
   133  	if sw.x >= len(row) {
   134  		sw.x = 0
   135  	}
   136  }
   137  
   138  // Next will move the highlight to the next cell
   139  func (sw *SymbolWidget) Next() {
   140  	sw.oldx = sw.x
   141  	sw.x++
   142  	row := sw.choices[sw.y]
   143  	if sw.x >= len(row) {
   144  		sw.x = 0
   145  		sw.y++
   146  	}
   147  	row = sw.choices[sw.y]
   148  	if sw.x >= len(row) {
   149  		sw.x = 0
   150  		sw.y++
   151  	}
   152  	if sw.y >= len(sw.choices) {
   153  		sw.y = 0
   154  	}
   155  }
   156  
   157  // SelectIndex will select a specific index. Returns false if it was not possible.
   158  func (sw *SymbolWidget) SelectIndex(x, y int) bool {
   159  	if y >= sw.h || x >= sw.w {
   160  		return false
   161  	}
   162  	sw.oldx = sw.x
   163  	sw.oldy = sw.y
   164  	sw.x = x
   165  	sw.y = y
   166  	return true
   167  }
   168  
   169  // SelectFirst will select the first menu choice
   170  func (sw *SymbolWidget) SelectFirst() bool {
   171  	return sw.SelectIndex(0, 0)
   172  }
   173  
   174  // SelectLast will select the last menu choice
   175  func (sw *SymbolWidget) SelectLast() bool {
   176  	sw.oldx = sw.x
   177  	sw.oldy = sw.y
   178  	sw.x = sw.w - 1
   179  	sw.y = sw.h - 1
   180  	return true
   181  }