Borders draw the same few strings in the same roles over and over, so a frame keeps what it has drawn in each role (Model.paint) instead of rendering a glyph per cell, and a line down a cell's padding replaces the space without cutting the styled text. stress.Laidout is a report of 8192 rows with every cell bordered, a column of notes wrapping over three lines and a merged title: BenchmarkView and BenchmarkFrame draw it as laidout-8192x26.
- commit
- 2def060eab32372cf7300c568f0cb9bb539ff7cb
- parent
- b771362b1a76ea0d4d9e4a8c68cced977821fac3
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 26 | ++++++++++++++++++ |
| M | internal/ | 43 | +++++++++++++++++++++++------- |
| M | internal/ | 8 | +++-- |
| M | internal/ | 1 | + |
| M | internal/ | 1 | + |
@@ -343,3 +343,29 @@ func Shapes() []Shape {
343343 {"dense-1Mx10", func() *sheet.Sheet { return Dense(MaxRows, MaxCols) }, at(0, 500000), "5"},344344 }345345 }346+347+// Laidout is a report of rows x cols: every cell bordered, a last column348+// of notes that wrap over three lines, and a title merged across the349+// top: what borders, tall rows and merged cells cost a frame.350+func Laidout(rows, cols int) *sheet.Sheet {351+ s := sheet.New()352+ r := rand.New(rand.NewPCG(5, 6))353+ lines := sheet.Style{Borders: sheet.Borders{Top: sheet.LineThin, Bottom: sheet.LineThin, Left: sheet.LineThin, Right: sheet.LineThin}}354+ notes := lines355+ notes.Wrap = sheet.WrapOn356+ put := func(a sheet.Addr, input string, st sheet.Style) {357+ if err := s.Load(a, input, sheet.Format{}, st); err != nil {358+ panic(err)359+ }360+ }361+ put(at(0, 0), "Report", sheet.Style{Bold: true})362+ s.LoadMerge(sheet.Rect{From: at(0, 0), To: at(cols-1, 0)})363+ for row := 1; row <= rows; row++ {364+ for col := range cols - 1 {365+ put(at(col, row), fmt.Sprintf("%.2f", r.Float64()*1000), lines)366+ }367+ put(at(cols-1, row), "a note that wraps over three lines", notes)368+ }369+ s.RecalcAll()370+ return s371+}@@ -3,6 +3,7 @@ package ui
33 import (44 "strings"55 6+ "charm.land/lipgloss/v2"67 "github.com/charmbracelet/x/ansi"78 89 "github.com/FelineStateMachine/012/internal/sheet"@@ -156,7 +157,31 @@ func (m *Model) leftEdge(a sheet.Addr, text string) string {
156157 if e == sheet.LineNone {157158 return text158159 }159- return m.th.CellBorder.Render(theme.Junction(e, e, sheet.LineNone, sheet.LineNone)) + ansi.Cut(text, 1, m.sheet.ColWidth(a.Col))160+ glyph := m.paint(&m.th.CellBorder, theme.Junction(e, e, sheet.LineNone, sheet.LineNone))161+ if strings.HasPrefix(text, " ") { // padding, as it mostly is162+ return glyph + text[1:]163+ }164+ return glyph + ansi.Cut(text, 1, m.sheet.ColWidth(a.Col))165+}166+167+// paintKey is a text drawn in a role.168+type paintKey struct {169+ role *lipgloss.Style170+ text string171+}172+173+// paint draws text in role, as role.Render does, keeping what it drew174+// until the next frame: borders draw the same few strings over and over.175+func (m *Model) paint(role *lipgloss.Style, text string) string {176+ if s, ok := m.painted[paintKey{role, text}]; ok {177+ return s178+ }179+ if m.painted == nil {180+ m.painted = map[paintKey]string{}181+ }182+ s := role.Render(text)183+ m.painted[paintKey{role, text}] = s184+ return s160185 }161186 162187 // ruleLine draws the border line above row: each column's top edge,@@ -180,35 +205,33 @@ func (m *Model) ruleLine(row int) string {
180205 181206 // ruleCols draws the rule line of ncols columns from first.182207 func (m *Model) ruleCols(b *strings.Builder, lc *lineCtx, above, first, ncols int) {208+ left := sheet.LineNone // the edge of the column before, on the screen183209 for c := first; c < first+ncols; c++ {184210 a := sheet.Addr{Col: c, Row: lc.row}185211 w := m.sheet.ColWidth(c)186- style, colored := m.th.CellBorder, false212+ style, colored := &m.th.CellBorder, false187213 if lc.selecting && lc.sel.Contains(a) && lc.sel.Contains(sheet.Addr{Col: c, Row: above}) {188- style, colored = m.th.Selection, true214+ style, colored = &m.th.Selection, true189215 }190216 mg, inMerge := lc.mergeOf(c)191217 inside := inMerge && lc.row > mg.From.Row192218 if inside && mg.From == lc.focus {193- style, colored = m.th.Pointer, true219+ style, colored = &m.th.Pointer, true194220 }195221 h := m.hEdge(a)196- left := sheet.LineNone197- if c > first {198- left = m.hEdge(sheet.Addr{Col: c - 1, Row: lc.row})199- }200222 joint := theme.Junction(m.vEdge(sheet.Addr{Col: c, Row: above}), m.vEdge(a), left, h)201223 if inside && c > mg.From.Col {202224 joint = " "203225 }226+ left = h204227 run := strings.Repeat(theme.Junction(sheet.LineNone, sheet.LineNone, h, h), w-1)205228 switch {206229 case !colored && joint == " " && h == sheet.LineNone:207230 b.WriteString(strings.Repeat(" ", w))208231 case joint == " ":209- b.WriteString(style.Render(joint + run))232+ b.WriteString(m.paint(style, joint+run))210233 default: // the joint in the border's role, as leftEdge draws the lines through it211- b.WriteString(m.th.CellBorder.Render(joint) + style.Render(run))234+ b.WriteString(m.paint(&m.th.CellBorder, joint) + m.paint(style, run))212235 }213236 }214237 }@@ -87,9 +87,11 @@ type Model struct {
8787 protectOK bool // an edit to a protected range was agreed to: protect.go8888 // borderLine is the line Format > Borders draws with: layoutfmt.go.8989 borderLine sheet.Line90- note string // feedback on the last action, e.g. "Undid: clear B3"91- warn string // like note, for something that went wrong, e.g. a macro's error92- errMsg string // the message ERROR mode shows90+ // painted is what borders drew this frame, by role: gridlines.go.91+ painted map[paintKey]string92+ note string // feedback on the last action, e.g. "Undid: clear B3"93+ warn string // like note, for something that went wrong, e.g. a macro's error94+ errMsg string // the message ERROR mode shows9395 9496 // Components. Each owns its state and the handling of the input it9597 // takes; Model routes messages to them and composes what they draw.@@ -29,6 +29,7 @@ func (m *Model) View() tea.View {
2929 if telemetry.Enabled() {3030 defer m.timeFrame(time.Now())3131 }32+ clear(m.painted) // the theme may have changed since the last frame3233 lines := []string{m.menuBarLine(), m.formulaBar(), m.contextLineText(), m.headerRow()}3334 for _, b := range m.bands() {3435 lines = m.appendBand(lines, b)