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) }