hscroll.go (1624B)
1 package tui 2 3 import ( 4 "strings" 5 6 "github.com/charmbracelet/x/ansi" 7 ) 8 9 const detailHorizontalStep = 8 10 11 var cutMarkerStyle = dimStyle 12 13 func lineWidths(content string) []int { 14 lines := strings.Split(content, "\n") 15 widths := make([]int, len(lines)) 16 for i, l := range lines { 17 widths[i] = ansi.StringWidth(l) 18 } 19 return widths 20 } 21 22 func (m portfolioModel) detailMaxXOffset() int { 23 longest := 0 24 for _, w := range m.detailLineWidths { 25 longest = max(longest, w) 26 } 27 return max(longest-m.viewport.Width, 0) 28 } 29 30 // scrollDetailX pans the detail viewport sideways; only wide preformatted 31 // blocks are wider than the pane, so prose never moves out of view for long. 32 func (m *portfolioModel) scrollDetailX(delta int) { 33 m.detailXOffset = min(max(m.detailXOffset+delta, 0), m.detailMaxXOffset()) 34 m.viewport.SetXOffset(m.detailXOffset) 35 } 36 37 // detailViewWithCutMarkers renders the viewport and marks lines that continue 38 // past the right edge with › (and past the left edge with ‹ when panned). 39 func (m portfolioModel) detailViewWithCutMarkers() string { 40 view := m.viewport.View() 41 w := m.viewport.Width 42 if w < 4 || m.detailMaxXOffset() == 0 { 43 return view 44 } 45 lines := strings.Split(view, "\n") 46 for i, line := range lines { 47 idx := m.viewport.YOffset + i 48 if idx >= len(m.detailLineWidths) { 49 break 50 } 51 full := m.detailLineWidths[idx] 52 if full > m.detailXOffset+w { 53 line = ansi.Truncate(line, w-1, "") + cutMarkerStyle.Render("›") 54 } 55 if m.detailXOffset > 0 && full > m.detailXOffset { 56 line = cutMarkerStyle.Render("‹") + ansi.Cut(line, 1, w) 57 } 58 lines[i] = line 59 } 60 return strings.Join(lines, "\n") 61 }