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 }