github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/symbolmenu.go (about) 1 package main 2 3 import ( 4 "os" 5 "os/signal" 6 "strconv" 7 "syscall" 8 9 "github.com/xyproto/vt100" 10 ) 11 12 // SymbolMenu starts a loop where keypresses are handled. When a choice is made, a number is returned. 13 // x and y are returned. -1,-1 is "no choice", 0,0 is the top left index. 14 func (e *Editor) SymbolMenu(tty *vt100.TTY, status *StatusBar, title string, choices [][]string, titleColor, textColor, highlightColor vt100.AttributeColor) (int, int, bool) { 15 // Clear the existing handler 16 signal.Reset(syscall.SIGWINCH) 17 18 var ( 19 c = vt100.NewCanvas() 20 symbolMenu = NewSymbolWidget(title, choices, titleColor, textColor, highlightColor, e.Background, int(c.W()), int(c.H())) 21 sigChan = make(chan os.Signal, 1) 22 running = true 23 changed = true 24 cancel = false 25 ) 26 27 // Set up a new resize handler 28 signal.Notify(sigChan, syscall.SIGWINCH) 29 30 go func() { 31 for range sigChan { 32 resizeMut.Lock() 33 // Create a new canvas, with the new size 34 nc := c.Resized() 35 if nc != nil { 36 vt100.Clear() 37 c = nc 38 symbolMenu.Draw(c) 39 c.Redraw() 40 changed = true 41 } 42 43 // Inform all elements that the terminal was resized 44 resizeMut.Unlock() 45 } 46 }() 47 48 vt100.Clear() 49 vt100.Reset() 50 c.Redraw() 51 52 // Set the initial menu index 53 symbolMenu.SelectIndex(0, 0) 54 55 for running { 56 57 // Draw elements in their new positions 58 59 if changed { 60 resizeMut.RLock() 61 symbolMenu.Draw(c) 62 resizeMut.RUnlock() 63 // Update the canvas 64 c.Draw() 65 } 66 67 // Handle events 68 key := tty.String() 69 switch key { 70 case "↑", "c:16": // Up or ctrl-p 71 resizeMut.Lock() 72 symbolMenu.Up() 73 changed = true 74 resizeMut.Unlock() 75 case "←": // Left 76 resizeMut.Lock() 77 symbolMenu.Left() 78 changed = true 79 resizeMut.Unlock() 80 case "↓", "c:14": // Down or ctrl-n 81 resizeMut.Lock() 82 symbolMenu.Down() 83 changed = true 84 resizeMut.Unlock() 85 case "→": // Right 86 resizeMut.Lock() 87 symbolMenu.Right() 88 changed = true 89 resizeMut.Unlock() 90 case "c:9": // Tab, next 91 resizeMut.Lock() 92 symbolMenu.Next() 93 changed = true 94 resizeMut.Unlock() 95 case "c:1": // Top, ctrl-a 96 resizeMut.Lock() 97 symbolMenu.SelectFirst() 98 changed = true 99 resizeMut.Unlock() 100 case "c:5": // Bottom, ctrl-e 101 resizeMut.Lock() 102 symbolMenu.SelectLast() 103 changed = true 104 resizeMut.Unlock() 105 case "c:27", "q", "c:3", "c:17", "c:15": // ESC, q, ctrl-c, ctrl-q or ctrl-o 106 running = false 107 changed = true 108 cancel = true 109 case " ", "c:13": // Space or Return 110 running = false 111 changed = true 112 case "n": // handy shortcut 113 for y := 0; y < len(choices); y++ { 114 for x := 0; x < len(choices[y]); x++ { 115 if choices[y][x] == "ℕ" { 116 return x, y, false 117 } 118 } 119 } 120 case "t": // handy shortcut 121 for y := 0; y < len(choices); y++ { 122 for x := 0; x < len(choices[y]); x++ { 123 if choices[y][x] == "⊤" { 124 return x, y, false 125 } 126 } 127 } 128 case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": // 0 .. 9 129 number, err := strconv.Atoi(key) 130 if err != nil { 131 break 132 } 133 resizeMut.Lock() 134 symbolMenu.SelectIndex(0, number) 135 changed = true 136 resizeMut.Unlock() 137 } 138 139 // If the menu was changed, draw the canvas 140 if changed { 141 c.Draw() 142 } 143 144 } 145 146 // Restore the signal handlers 147 e.SetUpSignalHandlers(c, tty, status) 148 149 x, y := symbolMenu.Selected() 150 return x, y, cancel 151 }