Files
settuings/internal/ui/input/keyboard.go
T
2026-08-13 17:59:03 +02:00

135 lines
3.2 KiB
Go

package input
import (
"fmt"
"charm.land/bubbles/v2/key"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/settuings/internal/icons"
inputpkg "github.com/anotherhadi/settuings/internal/input"
"github.com/anotherhadi/settuings/internal/keys"
)
func (m *Model) handleLayout(msg layoutMsg) {
m.layoutErr = msg.err
if msg.err != nil {
return
}
m.currentLayout = msg.layout
if !m.cursorInit {
m.cursorInit = true
for i, l := range inputpkg.Layouts {
if l.Code == msg.layout {
m.cursor = i
break
}
}
}
}
func (m *Model) handleSetLayout(msg setLayoutMsg) tea.Cmd {
m.layoutPending = false
if msg.err != nil {
m.layoutFeedback = msg.err.Error()
return nil
}
m.layoutFeedback = ""
return fetchLayoutCmd()
}
func (m Model) selectedLayout() *inputpkg.Layout {
if m.cursor < 0 || m.cursor >= len(inputpkg.Layouts) {
return nil
}
return &inputpkg.Layouts[m.cursor]
}
func (m *Model) updateLayout(msg tea.Msg) tea.Cmd {
keyMsg, ok := msg.(tea.KeyPressMsg)
if !ok {
return nil
}
g := keys.Keys.Global
in := keys.Keys.Input
switch {
case key.Matches(keyMsg, g.Up):
if m.cursor > 0 {
m.cursor--
}
case key.Matches(keyMsg, g.Down):
if m.cursor < len(inputpkg.Layouts)-1 {
m.cursor++
}
case key.Matches(keyMsg, in.Select):
return m.applySelectedLayout()
}
return nil
}
func (m *Model) applySelectedLayout() tea.Cmd {
sel := m.selectedLayout()
if sel == nil || sel.Code == m.currentLayout {
return nil
}
m.layoutPending = true
m.layoutFeedback = ""
return setLayoutCmd(sel.Code)
}
func (m Model) renderLayout(width, height int) string {
title := sectionTitleStyle(m.focus == focusLayout).Render("Keyboard Layout")
switch {
case m.layoutErr != nil:
return lipgloss.JoinVertical(lipgloss.Left, title, "", errorStyle().Render(m.layoutErr.Error()))
case m.currentLayout == "" && !m.cursorInit:
return lipgloss.JoinVertical(lipgloss.Left, title, "", ilovetui.S.Faint.Render("Loading current layout…"))
}
current := subtleStyle().Render("Current: ") + inputpkg.LayoutName(m.currentLayout) + subtleStyle().Render(" ("+m.currentLayout+")")
lines := []string{title, "", current, ""}
available := height - len(lines) - 2
if available < 1 {
available = 1
}
p, start, end := paginate(m.cursor, len(inputpkg.Layouts), available)
for i := start; i < end; i++ {
lines = append(lines, m.renderLayoutRow(inputpkg.Layouts[i], i == m.cursor))
}
footer := renderPaginatorDots(p)
switch {
case m.layoutPending:
footer = mutedStyle().Render("applying…")
case m.layoutFeedback != "":
footer = errorStyle().Render(m.layoutFeedback)
}
lines = append(lines, "", footer)
return lipgloss.JoinVertical(lipgloss.Left, lines...)
}
func (m Model) renderLayoutRow(l inputpkg.Layout, selected bool) string {
textStyle := lipgloss.NewStyle().Foreground(ilovetui.S.Text)
if selected {
textStyle = textStyle.Bold(true)
if m.focus == focusLayout {
textStyle = textStyle.Foreground(ilovetui.S.Primary)
}
}
cursor := " "
if selected {
cursor = "> "
}
check := " "
if l.Code == m.currentLayout {
check = successStyle().Render(icons.I.Check)
}
return fmt.Sprintf("%s%s%s", cursor, check, textStyle.Render(l.Name+" ("+l.Code+")"))
}