diff options
| author | historia <[not public]> | 2026-06-29 04:03:38 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-29 04:03:38 -0400 |
| commit | ba34490aaed5f7c242c2b0453130fefc07d0d5d0 (patch) | |
| tree | db5818757d5080221494cead1c7e61a576d1cb65 /internal/tui/view.go | |
| parent | b1e252c996a6332d391f96624a7d6f149eb097c4 (diff) | |
| download | tui-ascii-mapper-ba34490aaed5f7c242c2b0453130fefc07d0d5d0.tar.gz | |
Diffstat (limited to 'internal/tui/view.go')
| -rw-r--r-- | internal/tui/view.go | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/internal/tui/view.go b/internal/tui/view.go new file mode 100644 index 0000000..fd8acdc --- /dev/null +++ b/internal/tui/view.go @@ -0,0 +1,142 @@ +package tui + +import ( + "fmt" + "strings" + + "tui-ascii-mapper/internal/model" + + "github.com/charmbracelet/lipgloss" +) + +func (m *AppModel) View() string { + if m.quitting { + return "" + } + + if m.mode == model.ModeDialog { + return m.renderDialogFullscreen() + } + if m.colorPicker != nil && m.colorPicker.Active { + return m.renderColorPickerFullscreen() + } + return m.baseView() +} + +func (m *AppModel) baseView() string { + gridAreaW := m.width - sidebarW + gridH := m.height - 1 + showStatusHelp := m.height > 6 + if showStatusHelp { + gridH -= 2 + } + if gridAreaW < 1 { + gridAreaW = 1 + } + if gridH < 1 { + gridH = 1 + } + m.scrollToCursor(gridAreaW, gridH) + + var sb strings.Builder + sb.WriteString(m.renderToolbar()) + sb.WriteByte('\n') + + gridLines := strings.Split(m.renderGrid(gridAreaW, gridH), "\n") + sidebarLines := strings.Split(m.renderSidebar(gridH), "\n") + n := max(len(gridLines), len(sidebarLines)) + for i := 0; i < n; i++ { + if i < len(gridLines) { + sb.WriteString(padRight(gridLines[i], gridAreaW)) + } else { + sb.WriteString(strings.Repeat(" ", gridAreaW)) + } + if i < len(sidebarLines) { + sb.WriteString(sidebarLines[i]) + } + sb.WriteByte('\n') + } + if showStatusHelp { + sb.WriteString(m.renderStatus()) + sb.WriteByte('\n') + sb.WriteString(m.renderHelp()) + } + return sb.String() +} + +func (m *AppModel) showFeedback(msg string) { + m.dialogMsg = msg +} + +func (m *AppModel) scrollToCursor(gw, gh int) { + if m.cursor.X < m.offset.X { + m.offset.X = m.cursor.X + } + if m.cursor.X >= m.offset.X+gw { + m.offset.X = m.cursor.X - gw + 1 + } + if m.cursor.Y < m.offset.Y { + m.offset.Y = m.cursor.Y + } + if m.cursor.Y >= m.offset.Y+gh { + m.offset.Y = m.cursor.Y - gh + 1 + } + m.offset.X = model.Clamp(m.offset.X, 0, max(0, m.curMap().Width-gw)) + m.offset.Y = model.Clamp(m.offset.Y, 0, max(0, m.curMap().Height-gh)) +} + +func (m *AppModel) centeredFullscreen(content string) string { + return lipgloss.Place(m.width, m.height, + lipgloss.Center, lipgloss.Center, + content) +} + +func renderPopup(title, label, value string) string { + lines := []string{ + lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("33")).Render(title), + "", + label + " " + value, + } + w := 40 + for i, l := range lines { + lines[i] = lipgloss.NewStyle().Width(w).Render(l) + } + return lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + Background(lipgloss.Color("234")). + Padding(1, 2). + Width(w + 4). + Render(strings.Join(lines, "\n")) +} + +func padRight(s string, w int) string { + for lipgloss.Width(s) < w { + s += " " + } + return s +} + +func formatSize(size int64) string { + const unit = 1024 + if size < unit { + return fmt.Sprintf("%4dB", size) + } + div, exp := int64(unit), 0 + for n := size / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%3.0f%c", float64(size)/float64(div), "KMGTPE"[exp]) +} + +func shortFilename(path string) string { + if path == "" { + return "(unsaved)" + } + for i := len(path) - 1; i >= 0; i-- { + if path[i] == '/' || path[i] == '\\' { + return path[i+1:] + } + } + return path +} |
