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  }