From 96389610ed531b02571ea6dc56aa75489f2e6fb6 Mon Sep 17 00:00:00 2001 From: Hisham Aburaqibah Date: Thu, 19 Dec 2024 12:49:49 +0200 Subject: [PATCH] fix: UI for filtering config options Also added vim-like movement (hjkl, g, shift+g) h -> left j -> down k -> up l -> right g -> Go to first config shift+g -> Go to last config --- app.go | 72 +++++++++++++++++++++++++------------------------- config.go | 32 +++++++++++++++++++++- config_test.go | 23 ++++++++++++++++ 3 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 config_test.go diff --git a/app.go b/app.go index ddf8ccb..91adf71 100644 --- a/app.go +++ b/app.go @@ -41,7 +41,8 @@ type model struct { Quitting bool Cancelled bool Screen Screen - Config map[int]Config + Config []Config + VisibleConfig []Config FocusIndex int ParsedConfig ParsedConfig DryRun bool @@ -54,12 +55,6 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *model { s.Spinner = spinner.Dot s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) - cfg := make(map[int]Config) - - for i := 0; i < len(Configs); i++ { - cfg[i] = Configs[i] - } - return &model{ IsDirectory: fileInfo.IsDir(), CurrentFileName: fileInfo.Name(), @@ -72,12 +67,23 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *model { TotalProgressBar: progress.New(progress.WithDefaultGradient()), TotalProgress: 0.0, Quitting: false, - Config: cfg, + Config: Configs, + VisibleConfig: getVisibleConfigs(Configs), ErrQuit: false, ErrQuitMessage: "", } } +func (m model) updateConfigFocusedOptions() { + for _, visCfg := range m.VisibleConfig { + for i := range m.Config { + if m.Config[i].Name == visCfg.Name { + m.Config[i].FocusedOption = visCfg.FocusedOption + } + } + } +} + func (m model) Init() tea.Cmd { return tea.Batch(m.Spinner.Tick, initCfg) } @@ -102,28 +108,34 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit case "enter", " ": // parse config and switch to main screen if we're focused on the start button - if m.FocusIndex == len(Configs) { + if m.FocusIndex == len(m.VisibleConfig) { return m, m.parseConfig(false) } // parse config, print it and exit - if m.FocusIndex == len(Configs)+1 { + if m.FocusIndex == len(m.VisibleConfig)+1 { return m, m.parseConfig(true) } - case "tab", "shift+tab", "up", "down": - if key == "up" || key == "shift+tab" { + case "g": + m.FocusIndex = 0 + case "G": + m.FocusIndex = len(m.VisibleConfig) + 1 + case "tab", "shift+tab", "up", "down", "j", "k": + if key == "up" || key == "shift+tab" || key == "k" { m.FocusIndex-- } else { m.FocusIndex++ } - if m.FocusIndex >= len(Configs)+2 { + if m.FocusIndex >= len(m.VisibleConfig)+2 { m.FocusIndex = 0 } else if m.FocusIndex < 0 { - m.FocusIndex = len(Configs) + 1 + m.FocusIndex = len(m.VisibleConfig) + 1 } - case "left", "right": - if cfg, ok := m.Config[m.FocusIndex]; ok { - if key == "right" { + case "left", "right", "h", "l": + // If we're not hovering a button + if m.FocusIndex < len(m.VisibleConfig) { + cfg := &m.VisibleConfig[m.FocusIndex] + if key == "right" || key == "l" { cfg.FocusedOption++ } else { cfg.FocusedOption-- @@ -135,7 +147,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cfg.FocusedOption = len(cfg.Opts) - 1 } - m.Config[m.FocusIndex] = cfg + m.updateConfigFocusedOptions() + m.VisibleConfig = getVisibleConfigs(m.Config) } } case quitMsg: @@ -224,7 +237,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } -func parseConfig(cfg map[int]Config) ParsedConfig { +func parseConfig(cfg []Config) ParsedConfig { vEncoder := find(cfg, "Video Encoder") aEncoder := find(cfg, "Audio Encoder") preset := find(cfg, "Preset") @@ -243,24 +256,11 @@ func parseConfig(cfg map[int]Config) ParsedConfig { func CfgScreenView(m model) string { view := "" - config := parseConfig(m.Config) - - for i, cfg := range Configs { - switch config.VideoEncoder { - case "copy", "librav1e": - if cfg.Name == "Preset" || cfg.Name == "Constant Rate Factor (CRF)" { - continue - } - case "libvpx-vp9", "libsvtav1": - if cfg.Name == "Preset" { - continue - } - } - + for i, cfg := range m.VisibleConfig { opts := "" for j, opt := range cfg.Opts { - if m.Config[i].FocusedOption == j { + if m.VisibleConfig[i].FocusedOption == j { opts += FocusedOption.Render(opt) } else { opts += BlurredOption.Render(opt) @@ -278,13 +278,13 @@ func CfgScreenView(m model) string { } } - if m.FocusIndex == len(Configs) { + if m.FocusIndex == len(m.VisibleConfig) { view += FocusedStartButton } else { view += BlurredStartButton } - if m.FocusIndex == len(Configs)+1 { + if m.FocusIndex == len(m.VisibleConfig)+1 { view += FocusedDryRunButton } else { view += BlurredDryRunButton diff --git a/config.go b/config.go index 4864ed5..2c17f1f 100644 --- a/config.go +++ b/config.go @@ -26,7 +26,7 @@ type ParsedConfig struct { CRF string } -func find(cfgs map[int]Config, name string) Config { +func find(cfgs []Config, name string) Config { for _, cfg := range cfgs { if cfg.Name == name { return cfg @@ -36,3 +36,33 @@ func find(cfgs map[int]Config, name string) Config { err := fmt.Sprintf("Couldn't find requested cfg: %s", name) panic(err) } + +func filter(cfgs []Config, names ...string) []Config { + filtered := make([]Config, 0) + +cfgloop: + for _, cfg := range cfgs { + for _, name := range names { + if cfg.Name == name { + continue cfgloop + } + } + + filtered = append(filtered, cfg) + } + + return filtered +} + +func getVisibleConfigs(cfgs []Config) []Config { + parsed := parseConfig(cfgs) + + switch parsed.VideoEncoder { + case "copy", "librav1e": + return filter(cfgs, "Preset", "Constant Rate Factor (CRF)") + case "libvpx-vp9", "libsvtav1": + return filter(cfgs, "Preset") + } + + return cfgs +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..e696d42 --- /dev/null +++ b/config_test.go @@ -0,0 +1,23 @@ +package main + +import "testing" + +func TestFilterConfigs(t *testing.T) { + cfgs := filter(Configs, "Preset", "Constant Rate Factor (CRF)") + + for _, cfg := range cfgs { + if cfg.Name == "Preset" { + t.Fatalf("Failed to filter out config \"Preset\"") + } else if cfg.Name == "Constant Rate Factor (CRF)" { + t.Fatalf("Failed to filter out config \"Constant Rate Factor (CRF)\"") + } + } +} + +func TestGetVisibleConfigs(t *testing.T) { + cfgs := getVisibleConfigs(Configs) + + if len(cfgs) == len(Configs) { + t.Fatalf("Failed to get the correct visible configs. Expected VisibleConfigs len: %v. Got %v", 4, len(cfgs)) + } +}