summaryrefslogtreecommitdiff
path: root/internal/app/model_keys.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-10-02 08:38:03 +0300
committerPaul Buetow <paul@buetow.org>2025-10-02 08:38:03 +0300
commit0c1b108ff5fccf39ae5bc6dc06802ce565bda633 (patch)
tree914e65e04bae26d3eae565f9d6a64d08ade361d0 /internal/app/model_keys.go
parent36be499ed342d92969ccaaff083c557a0951def9 (diff)
new version major refactorv0.2.0
Diffstat (limited to 'internal/app/model_keys.go')
-rw-r--r--internal/app/model_keys.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/internal/app/model_keys.go b/internal/app/model_keys.go
new file mode 100644
index 0000000..d02cf46
--- /dev/null
+++ b/internal/app/model_keys.go
@@ -0,0 +1,138 @@
+package app
+
+import (
+ "fmt"
+
+ tea "github.com/charmbracelet/bubbletea"
+)
+
+func (m model) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
+ if cmd, handled := globalKeyHandler(msg); handled {
+ return m, cmd
+ }
+ if m.loading {
+ return m, nil
+ }
+ if m.showFilters {
+ return m.handleFilterKey(msg)
+ }
+ return m.handleTableKey(msg)
+}
+
+func globalKeyHandler(msg tea.KeyMsg) (tea.Cmd, bool) {
+ switch msg.String() {
+ case "ctrl+c", "q":
+ return tea.Quit, true
+ default:
+ return nil, false
+ }
+}
+
+func (m model) handleFilterKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
+ switch msg.String() {
+ case "esc":
+ m.showFilters = false
+ m.statusMessage = "Filter closed"
+ return m, nil
+ case "enter":
+ cmd := m.applyFiltersFromInputs()
+ return m, cmd
+ case "tab":
+ m.inputs.focus = (m.inputs.focus + 1) % len(m.inputs.fields)
+ case "shift+tab":
+ m.inputs.focus = (m.inputs.focus - 1 + len(m.inputs.fields)) % len(m.inputs.fields)
+ }
+ m.syncFilterFocus()
+ updated, cmd := m.updateFilterInputs(msg)
+ m.inputs = updated
+ return m, cmd
+}
+
+func (m *model) applyFiltersFromInputs() tea.Cmd {
+ if err := m.applyFilterInputs(); err != nil {
+ m.statusMessage = err.Error()
+ return nil
+ }
+ m.showFilters = false
+ m.applyFiltersAndSort()
+ m.statusMessage = fmt.Sprintf("Filters applied (%d videos)", len(m.filtered))
+ return nil
+}
+
+func (m *model) syncFilterFocus() {
+ for i := range m.inputs.fields {
+ if i == m.inputs.focus {
+ m.inputs.fields[i].Focus()
+ continue
+ }
+ m.inputs.fields[i].Blur()
+ }
+}
+
+func (m model) handleTableKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
+ switch msg.String() {
+ case "/", "f":
+ return m.openFilters()
+ case "enter":
+ return m.playSelection()
+ case "n":
+ return m.sortAndReport(sortByName)
+ case "l":
+ return m.sortAndReport(sortByDuration)
+ case "a":
+ return m.sortAndReport(sortByAge)
+ case "c":
+ return m.toggleCrop()
+ case "r":
+ return m.resetFilterState()
+ default:
+ return m.updateTable(msg)
+ }
+}
+
+func (m model) openFilters() (tea.Model, tea.Cmd) {
+ m.showFilters = true
+ m.statusMessage = "Editing filters"
+ return m, nil
+}
+
+func (m model) playSelection() (tea.Model, tea.Cmd) {
+ if len(m.filtered) == 0 {
+ return m, nil
+ }
+ idx := m.table.Cursor()
+ if idx < 0 || idx >= len(m.filtered) {
+ return m, nil
+ }
+ video := m.filtered[idx]
+ m.statusMessage = fmt.Sprintf("Launching VLC: %s", video.Name)
+ return m, playVideoCmd(video.Path, m.activeCrop())
+}
+
+func (m model) sortAndReport(field sortField) (tea.Model, tea.Cmd) {
+ m.toggleSort(field)
+ m.applyFiltersAndSort()
+ m.statusMessage = fmt.Sprintf("Sorted %d videos", len(m.filtered))
+ return m, nil
+}
+
+func (m model) toggleCrop() (tea.Model, tea.Cmd) {
+ if m.cropValue == "" {
+ m.statusMessage = "No crop value set (start with --crop)"
+ return m, nil
+ }
+ m.cropEnabled = !m.cropEnabled
+ if m.cropEnabled {
+ m.statusMessage = fmt.Sprintf("Crop enabled (%s)", m.cropValue)
+ return m, nil
+ }
+ m.statusMessage = "Crop disabled"
+ return m, nil
+}
+
+func (m model) resetFilterState() (tea.Model, tea.Cmd) {
+ m.resetFilters()
+ m.applyFiltersAndSort()
+ m.statusMessage = fmt.Sprintf("Filters cleared (%d videos)", len(m.filtered))
+ return m, nil
+}