cat / 012
clone

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

main
012 / internal / ui / tabstrip.go
tabstrip.go 3.0K
1package ui23import (4 "slices"5 "strconv"67 tea "charm.land/bubbletea/v2"89 "src.rest/cat/012/internal/macro"10 "src.rest/cat/012/internal/sheet"11 "src.rest/cat/012/internal/ui/tabstrip"12)1314// The sheet tabs (package tabstrip) as the model shows them and what15// clicking them does: clicking a tab shows it, double-clicking renames16// it, right-clicking opens its menu, and dragging it moves it.1718// tabView is what the tab strip shows: the sheets, the one shown, and19// what the mouse is over or dragging.20func (m *Model) tabView() tabstrip.View {21 sheets := m.visibleSheets()22 v := tabstrip.View{Sheets: sheets, Active: slices.Index(sheets, m.sheet), Busy: m.mouse.drag != dragNone && m.mouse.drag != dragTab}23 switch m.mouse.hover.kind {24 case hitTab:25 v.Hover, v.HoverIndex = tabstrip.Tab, m.mouse.hover.addr.Col26 case hitTabAdd:27 v.Hover = tabstrip.Add28 }29 return v30}3132// tabHits are the hits of the parts of the tab strip.33var tabHits = map[tabstrip.Kind]hitKind{tabstrip.Tab: hitTab, tabstrip.Add: hitTabAdd, tabstrip.Prev: hitTabPrev, tabstrip.Next: hitTabNext}3435// tabAt returns what part of the tab strip is at x on the status line.36func (m *Model) tabAt(x int) (hit, bool) {37 _, spans := m.statusLayout()38 for _, sp := range spans {39 if x >= sp.X && x < sp.X+sp.W {40 return hit{kind: tabHits[sp.Kind], addr: sheet.Addr{Col: sp.Index}}, true41 }42 }43 return hit{}, false44}4546// tabPress handles a click on the tab strip: show the tab (renaming it on47// a double click, or starting to drag it), add a sheet, or step through48// tabs with the arrows.49func (m *Model) tabPress(h hit, double bool) tea.Cmd {50 switch h.kind {51 case hitTabPrev:52 return m.runCommand("sheet.prev")53 case hitTabNext:54 return m.runCommand("sheet.next")55 case hitTabAdd:56 if m.mode == modePoint {57 m.resumeEntry(m.pointRef())58 }59 if m.editing() && !m.commit() {60 return nil61 }62 return m.runCommand("sheet.new")63 case hitTab:64 s := m.tabSheet(h.addr.Col)65 if m.editing() {66 m.pointInto(s)67 return nil68 }69 m.showSheet(s)70 if double {71 return m.runCommand("sheet.rename")72 }73 m.mouse.drag = dragTab74 }75 return nil76}7778// dropTab moves the dragged sheet to the tab it was released over.79func (m *Model) dropTab() {80 if m.mouse.hover.kind != hitTab {81 return82 }83 if to := m.book().Index(m.tabSheet(m.mouse.hover.addr.Col)); to != m.book().Index(m.sheet) {84 m.book().MoveSheet(m.sheet, to)85 m.note = "Moved " + m.sheet.Name() + " to position " + strconv.Itoa(to+1)86 m.record(macro.Call("move_sheet", to+1))87 }88}8990// tabSheet is the sheet of tab i, counting the tabs shown (hidden sheets91// have none).92func (m *Model) tabSheet(i int) *sheet.Sheet {93 tabs := m.visibleSheets()94 return tabs[clamp(i, 0, len(tabs)-1)]95}9697// tabRightClick shows the tab's sheet and opens its menu.98func (m *Model) tabRightClick(h hit, x, y int) {99 if h.kind != hitTab {100 if h.kind == hitTabAdd {101 m.showContextMenu([]menuItem{{cmd: "sheet.new"}, {cmd: "sheet.goto"}}, x, y)102 }103 return104 }105 m.showSheet(m.tabSheet(h.addr.Col))106 m.showContextMenu(tabMenu, x, y)107}