turbo-editors/turbo-corepublic Fork 0
28d59854361aeda8541d853093e732126f3d7bff
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on 28d59854361aeda8541d853093e732126f3d7bff · k33g · 18h ago
view_draw.go · 107 lines · 2.9 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
package terminal

import (
	"github.com/gdamore/tcell/v2"

	"codeberg.org/turbo-editors/turbo-core/theme"
	"codeberg.org/turbo-editors/turbo-core/ui"
)

// Draw paints the terminal's grid.
func (v *View) Draw(p *ui.Painter, th *theme.Theme) {
	v.mu.Lock()
	defer v.mu.Unlock()

	area := p.Size()
	base := th.Style(theme.KeyTerminalText)
	p.Fill(area, ' ', base)

	for row := range area.H {
		v.drawLine(p, v.lineAt(row), row, area.W, base)
	}
	v.drawCursor(p, th)
}

// drawLine paints one row of the grid.
func (v *View) drawLine(p *ui.Painter, line Line, row, width int, base tcell.Style) {
	for col, cell := range line {
		if col >= width {
			return
		}
		p.SetCell(col, row, cell.Rune, resolve(cell.Style, base))
	}
}

// resolve fills a cell's unset colours in from the theme.
//
// A program that has said nothing about colour gets the theme's, which is what
// lets a terminal window look like the rest of the editor instead of like the
// terminal's own defaults.
func resolve(style, base tcell.Style) tcell.Style {
	foreground, background, _ := style.Decompose()
	baseForeground, baseBackground, _ := base.Decompose()

	if foreground == tcell.ColorDefault {
		style = style.Foreground(baseForeground)
	}
	if background == tcell.ColorDefault {
		style = style.Background(baseBackground)
	}
	return style
}

// drawCursor marks where the shell's cursor is.
//
// It is drawn only on the live screen: a cursor shown while the user is
// reading back through the history would point at a line the shell is not
// writing to.
func (v *View) drawCursor(p *ui.Painter, th *theme.Theme) {
	screen := v.parser.Screen()
	if v.scrollOffset != 0 || !screen.CursorVisible() || !v.Focused() {
		return
	}

	cursor := screen.Cursor()
	character, _ := p.CellAt(cursor.Col, cursor.Row)
	p.SetCell(cursor.Col, cursor.Row, character, th.Style(theme.KeyTerminalCursor))
	p.ShowCursor(cursor.Col, cursor.Row)
}

// lineAt returns the line to show on a visible row, which comes from the
// history when the view has been scrolled back.
//
// It must be called with the lock held.
func (v *View) lineAt(row int) Line {
	screen := v.parser.Screen()
	index := screen.ScrollbackLen() - v.scrollOffset + row

	if index < screen.ScrollbackLen() {
		return screen.ScrollbackLine(index)
	}
	return screen.Line(index - screen.ScrollbackLen())
}

// ScrollOffset returns how many lines back into the history the view is
// looking. Zero is the live screen.
func (v *View) ScrollOffset() int {
	v.mu.Lock()
	defer v.mu.Unlock()
	return v.scrollOffset
}

// ScrollBy moves the view back through the history, or forwards towards the
// live screen with a negative count. It stops at either end.
func (v *View) ScrollBy(lines int) {
	v.mu.Lock()
	defer v.mu.Unlock()

	v.scrollOffset = min(max(v.scrollOffset+lines, 0), v.parser.Screen().ScrollbackLen())
}

// ScrollToBottom goes back to the live screen, which is where any key press
// puts the view.
func (v *View) ScrollToBottom() {
	v.mu.Lock()
	defer v.mu.Unlock()
	v.scrollOffset = 0
}