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/colorpicker.go | |
| parent | b1e252c996a6332d391f96624a7d6f149eb097c4 (diff) | |
| download | tui-ascii-mapper-main.tar.gz | |
Diffstat (limited to 'internal/tui/colorpicker.go')
| -rw-r--r-- | internal/tui/colorpicker.go | 180 |
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) +} |
