cat / 012
clone

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

Tidy mouse geometry and name runCommand as the command hook

Dami committed fdd6132
dragTarget depends only on the grid given the drag's anchor, so it
joins the other pane geometry; the formula bar's position needs no
model at all. runCommand's comment says it is the one place commands
run, where a command log would attach. UX.md points at the theme
package for roles, key labels, chips and frames.
commit
fdd61322ea6db898dd7871f51e5e84d7c5dadc41
parent
62af88b0fb5d0702ec13d4afe6fd8cf956fec5ea
author
Dami <dami.etoile@gmail.com>
5 files changed, +70 -68
Mdocs/UX.md8++--
Minternal/ui/commands.go4+-
Minternal/ui/mouse.go67+++---------------------------
Minternal/ui/mouse_test.go4+-
Minternal/ui/panes.go55+++++++++++++++++++++++++
M docs/UX.md +4 -4
M internal/ui/commands.go +3 -1
M internal/ui/mouse.go +6 -61
@@ -63,20 +63,20 @@ type hit struct {
6363 6464 // formulaBarAt returns where the active cell's contents are drawn in READY6565 // mode: the screen line and the x of the first character.66-func (m *Model) formulaBarAt() (line, x int) { return formulaLine, formulaBarTextX() }66+func formulaBarAt() (line, x int) { return formulaLine, formulaBarTextX() }6767 6868 // editLineAt returns where the entry being typed is drawn: in place, in the6969 // formula bar.70-func (m *Model) editLineAt() (line, x int) { return formulaLine, formulaBarTextX() }70+func editLineAt() (line, x int) { return formulaLine, formulaBarTextX() }7171 7272 // hitTest maps a screen position to what's there.7373 func (m *Model) hitTest(x, y int) hit {7474 switch {7575 case y < panelLines:76- if line, tx := m.editLineAt(); y == line && m.editing() {76+ if line, tx := editLineAt(); y == line && m.editing() {7777 return hit{kind: hitEditLine, x: x - tx}7878 }79- if line, tx := m.formulaBarAt(); y == line && x >= tx && m.mode == modeReady {79+ if line, tx := formulaBarAt(); y == line && x >= tx && m.mode == modeReady {8080 return hit{kind: hitFormulaBar, x: x - tx}8181 }8282 return hit{kind: hitPanel}
@@ -121,61 +121,6 @@ func (m *Model) hitTest(x, y int) hit {
121121 return hit{}122122 }123123 124-// dragTarget maps a position during a drag to a cell, clamping to the125-// visible grid, and reports which way to autoscroll when it's outside.126-// Dragging from the scrolling area into the frozen panes scrolls back127-// toward them, as in Sheets, until the two meet.128-func (m *Model) dragTarget(x, y int) (a sheet.Addr, dc, dr int) {129- rows := m.screenRows()130- fr, fc := m.frozen()131- anchor := m.dragAnchor()132- firstRow := m.visibleRow(fr)133- scrolledDown := m.top > firstRow134- var last int135- for _, r := range rows {136- if r != divider {137- last = r138- }139- }140- switch row, ok := m.rowAt(y); {141- case ok && (row >= fr || !scrolledDown || anchor.Row < fr):142- a.Row = row143- case y >= gridTop+len(rows):144- a.Row, dr = last, 1145- case y >= gridTop && !scrolledDown: // the divider, with nothing scrolled146- a.Row = m.top147- case scrolledDown && anchor.Row >= fr:148- a.Row, dr = m.top, -1149- default: // above the grid150- a.Row = rows[0]151- if scrolledDown {152- dr = -1153- }154- }155- scrolledRight := m.left > fc156- cols := m.visibleCols(m.left)157- right := m.colStart(m.left + cols)158- switch col, _, ok := m.colSpan(x); {159- case x >= right:160- a.Col, dc = m.left+cols-1, 1161- case ok && (col >= fc || !scrolledRight || anchor.Col < fc):162- a.Col = col163- case x >= rowHdrW && !scrolledRight:164- a.Col = m.left165- case scrolledRight && anchor.Col >= fc:166- a.Col, dc = m.left, -1167- default: // over the row numbers168- a.Col = 0169- if fc == 0 {170- a.Col = m.left171- }172- if scrolledRight || fc == 0 && m.left > 0 {173- dc = -1174- }175- }176- return clampAddr(a), dc, dr177-}178-179124 // dragAnchor is where the drag in progress started.180125 func (m *Model) dragAnchor() sheet.Addr {181126 if m.mouse.drag == dragPoint {
@@ -326,7 +271,7 @@ func (m *Model) dragTo(x, y int) tea.Cmd {
326271 case dragTab:327272 return nil // handleMotion tracks the tab under the mouse328273 }329- a, dc, dr := m.dragTarget(x, y)274+ a, dc, dr := m.dragTarget(x, y, m.dragAnchor())330275 switch m.mouse.drag {331276 case dragPoint:332277 m.point.at, m.point.anchored = a, a != m.point.anchor || m.point.anchored
@@ -359,7 +304,7 @@ func (m *Model) handleAutoscroll() tea.Cmd {
359304 m.mouse.autoscrolling = false360305 return nil361306 }362- _, dc, dr := m.dragTarget(m.mouse.x, m.mouse.y)307+ _, dc, dr := m.dragTarget(m.mouse.x, m.mouse.y, m.dragAnchor())363308 if dc == 0 && dr == 0 {364309 m.mouse.autoscrolling = false365310 return nil
M internal/ui/mouse_test.go +2 -2
M internal/ui/panes.go +55 -0
@@ -261,3 +261,58 @@ func (g *grid) cellPos(a sheet.Addr) (x, y int) {
261261 func (g *grid) clampBox(x, y, w, h int) (int, int) {262262 return max(min(x, g.width-w), 0), max(min(y, g.height-h), 0)263263 }264+265+// dragTarget maps a position during a drag to a cell, clamping to the266+// visible grid, and reports which way to autoscroll when it's outside.267+// Dragging from the scrolling area into the frozen panes scrolls back268+// toward them, as in Sheets, until the two meet; anchor is where the269+// drag started.270+func (g *grid) dragTarget(x, y int, anchor sheet.Addr) (a sheet.Addr, dc, dr int) {271+ rows := g.screenRows()272+ fr, fc := g.frozen()273+ firstRow := g.visibleRow(fr)274+ scrolledDown := g.top > firstRow275+ var last int276+ for _, r := range rows {277+ if r != divider {278+ last = r279+ }280+ }281+ switch row, ok := g.rowAt(y); {282+ case ok && (row >= fr || !scrolledDown || anchor.Row < fr):283+ a.Row = row284+ case y >= gridTop+len(rows):285+ a.Row, dr = last, 1286+ case y >= gridTop && !scrolledDown: // the divider, with nothing scrolled287+ a.Row = g.top288+ case scrolledDown && anchor.Row >= fr:289+ a.Row, dr = g.top, -1290+ default: // above the grid291+ a.Row = rows[0]292+ if scrolledDown {293+ dr = -1294+ }295+ }296+ scrolledRight := g.left > fc297+ cols := g.visibleCols(g.left)298+ right := g.colStart(g.left + cols)299+ switch col, _, ok := g.colSpan(x); {300+ case x >= right:301+ a.Col, dc = g.left+cols-1, 1302+ case ok && (col >= fc || !scrolledRight || anchor.Col < fc):303+ a.Col = col304+ case x >= rowHdrW && !scrolledRight:305+ a.Col = g.left306+ case scrolledRight && anchor.Col >= fc:307+ a.Col, dc = g.left, -1308+ default: // over the row numbers309+ a.Col = 0310+ if fc == 0 {311+ a.Col = g.left312+ }313+ if scrolledRight || fc == 0 && g.left > 0 {314+ dc = -1315+ }316+ }317+ return clampAddr(a), dc, dr318+}