aboutsummaryrefslogtreecommitdiff
path: root/internal/tui/colorpicker.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-29 04:03:38 -0400
committerhistoria <[not public]>2026-06-29 04:03:38 -0400
commitba34490aaed5f7c242c2b0453130fefc07d0d5d0 (patch)
treedb5818757d5080221494cead1c7e61a576d1cb65 /internal/tui/colorpicker.go
parentb1e252c996a6332d391f96624a7d6f149eb097c4 (diff)
downloadtui-ascii-mapper-main.tar.gz
restructured projectHEADmain
Diffstat (limited to 'internal/tui/colorpicker.go')
-rw-r--r--internal/tui/colorpicker.go180
1 files changed, 180 insertions, 0 deletions
diff --git a/internal/tui/colorpicker.go b/internal/tui/colorpicker.go
new file mode 100644
index 0000000..fba388e
--- /dev/null
+++ b/internal/tui/colorpicker.go
@@ -0,0 +1,180 @@
+package tui
+
+import (
+ "fmt"
+ "strings"
+
+ "tui-ascii-mapper/internal/model"
+
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+)
+
+func (m *AppModel) openColorPicker(forText bool) {
+ m.colorPicker = &ColorPickerState{
+ Active: true,
+ Cursor: model.Point{X: -1, Y: -1},
+ Selected: nil,
+ ForText: forText,
+ }
+ if !forText && m.selected < len(m.curPalette()) {
+ for _, c := range m.curPalette()[m.selected].Colors {
+ m.colorPicker.Selected = append(m.colorPicker.Selected, c.Color)
+ }
+ }
+}
+
+func (m *AppModel) handleColorPickerMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
+ x, y := msg.X, msg.Y
+ col := (x - m.colorPicker.GridX) / 2
+ row := y - m.colorPicker.GridY
+
+ if msg.Button == tea.MouseButtonLeft && msg.Action == tea.MouseActionPress {
+ if row >= 0 && row < 16 && col >= 0 && col < 16 {
+ idx := row*16 + col
+ cstr := fmt.Sprintf("%d", idx)
+ found := false
+ for i, c := range m.colorPicker.Selected {
+ if c == cstr {
+ m.colorPicker.Selected = append(m.colorPicker.Selected[:i], m.colorPicker.Selected[i+1:]...)
+ found = true
+ break
+ }
+ }
+ if !found {
+ m.colorPicker.Selected = append(m.colorPicker.Selected, cstr)
+ }
+ }
+ return m, nil
+ }
+ if msg.Action == tea.MouseActionRelease {
+ m.colorPicker.Cursor = model.Point{X: -1, Y: -1}
+ return m, nil
+ }
+ if row >= 0 && row < 16 && col >= 0 && col < 16 {
+ m.colorPicker.Cursor = model.Point{X: col, Y: row}
+ }
+ return m, nil
+}
+
+func (m *AppModel) handleColorPickerKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
+ key := msg.String()
+ if m.colorPicker.Cursor.X < 0 {
+ m.colorPicker.Cursor = model.Point{X: 0, Y: 0}
+ }
+ switch key {
+ case "esc":
+ m.colorPicker = nil
+ return m, nil
+ case "enter":
+ if m.colorPicker.ForText {
+ idx := findTextLabelAtDirect(m.curMap(), m.cursor)
+ if idx >= 0 && len(m.colorPicker.Selected) > 0 {
+ m.dirty = true
+ m.undo.Push(m.curMap())
+ m.curMap().TextLabels[idx].Color = m.colorPicker.Selected[0]
+ }
+ } else {
+ if m.selected < len(m.curPalette()) {
+ var colors []model.TerrainColor
+ for _, c := range m.colorPicker.Selected {
+ colors = append(colors, model.TerrainColor{Color: c, Weight: 100})
+ }
+ if len(colors) > 0 {
+ m.curPalette()[m.selected].Colors = colors
+ }
+ }
+ }
+ m.colorPicker = nil
+ return m, nil
+ case "space":
+ idx := m.colorPicker.Cursor.Y*16 + m.colorPicker.Cursor.X
+ cstr := fmt.Sprintf("%d", idx)
+ found := false
+ for i, c := range m.colorPicker.Selected {
+ if c == cstr {
+ m.colorPicker.Selected = append(m.colorPicker.Selected[:i], m.colorPicker.Selected[i+1:]...)
+ found = true
+ break
+ }
+ }
+ if !found {
+ m.colorPicker.Selected = append(m.colorPicker.Selected, cstr)
+ }
+ case "up", "k":
+ m.colorPicker.Cursor.Y = (m.colorPicker.Cursor.Y - 1 + 16) % 16
+ case "down", "j":
+ m.colorPicker.Cursor.Y = (m.colorPicker.Cursor.Y + 1) % 16
+ case "left", "h":
+ m.colorPicker.Cursor.X = (m.colorPicker.Cursor.X - 1 + 16) % 16
+ case "right", "l":
+ m.colorPicker.Cursor.X = (m.colorPicker.Cursor.X + 1) % 16
+ case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
+ }
+ return m, nil
+}
+
+func findTextLabelAtDirect(m *model.Map, p model.Point) int {
+ for i, tl := range m.TextLabels {
+ runes := []rune(tl.Text)
+ for j := range runes {
+ if tl.Start.X+j == p.X && tl.Start.Y == p.Y {
+ return i
+ }
+ }
+ }
+ return -1
+}
+
+func (m *AppModel) renderColorPickerFullscreen() string {
+ var inner strings.Builder
+ inner.WriteString(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("33")).Render("Color Picker"))
+ inner.WriteString(" (space/click:toggle enter:apply esc:cancel)\n\n")
+ for row := 0; row < 16; row++ {
+ for col := 0; col < 16; col++ {
+ idx := row*16 + col
+ cstr := fmt.Sprintf("%d", idx)
+ sel := false
+ for _, c := range m.colorPicker.Selected {
+ if c == cstr {
+ sel = true
+ break
+ }
+ }
+ marker := " "
+ if sel {
+ marker = "\u25cf "
+ }
+ if row == m.colorPicker.Cursor.Y && col == m.colorPicker.Cursor.X {
+ marker = "\u25cb "
+ }
+ style := lipgloss.NewStyle().Background(lipgloss.Color(cstr)).Foreground(lipgloss.Color("255"))
+ inner.WriteString(style.Render(marker))
+ }
+ inner.WriteByte('\n')
+ }
+ inner.WriteString("\nSelected: ")
+ for _, c := range m.colorPicker.Selected {
+ inner.WriteString(c + " ")
+ }
+ popup := lipgloss.NewStyle().
+ Border(lipgloss.RoundedBorder()).
+ Background(lipgloss.Color("234")).
+ Padding(1, 2).
+ Render(inner.String())
+
+ popupW := lipgloss.Width(popup)
+ popupH := lipgloss.Height(popup)
+ gx := (m.width-popupW)/2 + 3
+ gy := (m.height-popupH)/2 + 4
+ if gx < 0 {
+ gx = 0
+ }
+ if gy < 0 {
+ gy = 0
+ }
+ m.colorPicker.GridX = gx
+ m.colorPicker.GridY = gy
+
+ return m.centeredFullscreen(popup)
+}