cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

cat/module-path
012 / internal / ui / overlay / overlay.go
overlay.go 3.9K
1// Package overlay is the contract between the UI's root model and the2// components that take over input while open: menus, pickers, bars on3// the context line and dialogs. It holds what they share (the boxes they4// draw, the mouse events they get, a scrolling list) and nothing of the5// model, so a component can live in a package of its own and be tested6// alone. Each component is built with the small host interface it acts7// on; the model implements those.8package overlay910import (11 "strings"1213 tea "charm.land/bubbletea/v2"14 "github.com/charmbracelet/x/ansi"15)1617// The control panel's rows that overlays anchor to.18const (19 MenuLine = 0 // the menu bar; dropdowns and pickers open under it20 FormulaLine = 1 // the formula bar: the name box, then the cell's contents or an entry21 ContextLine = 2 // prompts and bars; completions open under it22 GridTop = 4 // the grid's first row, under the column header; boxes over the grid start here23 // FormulaBarX is the column the formula bar's text starts at, after24 // the name box.25 FormulaBarX = 1226)2728// SearchPrompt starts a search field, in pickers and bars.29const SearchPrompt = " › "3031// KeyFor makes a key press from a keystroke like "alt+c", for chips that32// stand for a key when clicked.33func KeyFor(s string) tea.KeyPressMsg {34 k := tea.KeyPressMsg{}35 parts := strings.Split(s, "+")36 for _, p := range parts[:len(parts)-1] {37 if p == "alt" {38 k.Mod |= tea.ModAlt39 }40 }41 k.Code = rune(parts[len(parts)-1][0])42 return k43}4445// Overlay is a floating box drawn over the grid: a dropdown or context46// menu, the command palette or a dialog. While one is open it takes every47// key and mouse event, and Esc closes one level. Overlays are composited48// on top of the finished screen, so the grid underneath never moves.49type Overlay interface {50 // Indicator is the mode shown at the top right, e.g. "MENU".51 Indicator() string52 // Layout places the overlay's boxes on the screen, bottom first.53 Layout() []Box54 Key(k tea.KeyPressMsg) tea.Cmd55 Mouse(e MouseEvent) tea.Cmd56 // Status is what the status line says while the overlay is open: what57 // the highlighted item does, and the keys that apply.58 Status() (desc, keys string)59}6061// Text is an overlay with a text input, which gets the terminal cursor.62// The text is edited in the model's shared edit line, like any other63// entry.64type Text interface {65 Overlay66 // Cursor is where the caret is; a negative x hides it, while no67 // field of the overlay is being typed in.68 Cursor() (x, y int)69 Changed() // the text changed70}7172// Liner is an overlay drawn on the context line, such as the find bar.73type Liner interface {74 ContextLine() (left, right string)75}7677// Bar is an overlay typed on the formula bar, such as the shell prompt:78// FormulaBar is what the bar shows after the name box.79type Bar interface {80 FormulaBar() string81}8283// Box is a rectangle of styled lines at a screen position. Every line has84// the same display width.85type Box struct {86 ID string // identifies the box in mouse hit tests87 X, Y int88 Lines []string89}9091func (b Box) Width() int { return ansi.StringWidth(b.Lines[0]) }92func (b Box) Height() int { return len(b.Lines) }9394// MouseKind is what a mouse event is.95type MouseKind int9697const (98 MousePress MouseKind = iota99 MouseMotion100 MouseRelease101 MouseWheel102)103104// MouseEvent is a mouse event with what's under it already worked out.105type MouseEvent struct {106 Kind MouseKind107 Button tea.MouseButton108 X, Y int109 Box string // ID of the topmost box under the mouse, "" for none110 Col, Row int // position inside that box111}112113// List is the highlighted row and scroll position of a list in a box.114type List struct {115 Sel, Top int116}117118// Move highlights the row d steps away, wrapping around the ends.119func (l *List) Move(d, n int) {120 if n > 0 {121 l.Sel = ((l.Sel+d)%n + n) % n122 }123}124125// Show scrolls so the highlighted row is among the visible rows.126func (l *List) Show(rows int) {127 l.Top = max(l.Sel-rows+1, min(l.Top, l.Sel))128 l.Top = max(l.Top, 0)129}