util.go 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package chat
  2. import (
  3. "strings"
  4. "github.com/ayn2op/discordo/internal/config"
  5. "github.com/ayn2op/discordo/internal/ui"
  6. "github.com/ayn2op/tview"
  7. "github.com/ayn2op/tview/list"
  8. "github.com/ayn2op/tview/picker"
  9. "github.com/gdamore/tcell/v3"
  10. )
  11. func ConfigurePicker(model *picker.Model, cfg *config.Config, title string) {
  12. model.Box = ui.ConfigureBox(tview.NewBox(), &cfg.Theme)
  13. // When a child of the parent flex is focused, the parent layout itself is not reported as focused.
  14. // Instead, the focused child (picker) is considered focused.
  15. // Therefore, we manually set the active border style on the picker to ensure it displays the correct focused appearance.
  16. model.
  17. SetBlurFunc(nil).
  18. SetFocusFunc(nil).
  19. SetBorderSet(cfg.Theme.Border.ActiveSet.BorderSet).
  20. SetBorderStyle(cfg.Theme.Border.ActiveStyle.Style).
  21. SetTitleStyle(cfg.Theme.Title.ActiveStyle.Style).
  22. SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style)
  23. model.SetTitle(title)
  24. model.SetScrollBarVisibility(cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility)
  25. model.SetScrollBar(tview.NewScrollBar().
  26. SetTrackStyle(cfg.Theme.ScrollBar.TrackStyle.Style).
  27. SetThumbStyle(cfg.Theme.ScrollBar.ThumbStyle.Style).
  28. SetGlyphSet(cfg.Theme.ScrollBar.GlyphSet.GlyphSet))
  29. model.SetKeybinds(picker.Keybinds{
  30. Cancel: cfg.Keybinds.Picker.Cancel.Keybind,
  31. Keybinds: list.Keybinds{
  32. SelectUp: cfg.Keybinds.Picker.Up.Keybind,
  33. SelectDown: cfg.Keybinds.Picker.Down.Keybind,
  34. SelectTop: cfg.Keybinds.Picker.Top.Keybind,
  35. SelectBottom: cfg.Keybinds.Picker.Bottom.Keybind,
  36. },
  37. Select: cfg.Keybinds.Picker.Select.Keybind,
  38. })
  39. }
  40. // pickerBrowseHandleKey implements a two-phase ESC for overlay pickers.
  41. // First ESC enters browse mode (j/k navigate, i returns to input).
  42. // Second ESC calls closeFn to close the picker.
  43. // Returns (command, handled). If handled is false, the caller should
  44. // fall through to the normal picker event handling.
  45. func pickerBrowseHandleKey(event *tview.KeyEvent, browseMode *bool, model *picker.Model, closeFn func()) (tview.Command, bool) {
  46. if !*browseMode {
  47. if event.Key() == tcell.KeyEsc {
  48. *browseMode = true
  49. return nil, true
  50. }
  51. return nil, false
  52. }
  53. // Browse mode: intercept keys before they reach the input field.
  54. switch {
  55. case event.Key() == tcell.KeyEsc:
  56. *browseMode = false
  57. closeFn()
  58. return nil, true
  59. case event.Key() == tcell.KeyRune && event.Str() == "i":
  60. *browseMode = false
  61. return nil, true
  62. case event.Key() == tcell.KeyRune && event.Str() == "j":
  63. return model.HandleEvent(tcell.NewEventKey(tcell.KeyCtrlN, "", tcell.ModCtrl)), true
  64. case event.Key() == tcell.KeyRune && event.Str() == "k":
  65. return model.HandleEvent(tcell.NewEventKey(tcell.KeyCtrlP, "", tcell.ModCtrl)), true
  66. case event.Key() == tcell.KeyRune && event.Str() == "g":
  67. return model.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone)), true
  68. case event.Key() == tcell.KeyRune && event.Str() == "G":
  69. return model.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone)), true
  70. case event.Key() == tcell.KeyEnter:
  71. return model.HandleEvent(event), true
  72. }
  73. // Swallow other keys in browse mode so they don't reach the input.
  74. return nil, true
  75. }
  76. func humanJoin(items []string) string {
  77. count := len(items)
  78. switch count {
  79. case 0:
  80. return ""
  81. case 1:
  82. return items[0]
  83. case 2:
  84. return items[0] + " and " + items[1]
  85. default:
  86. return strings.Join(items[:count-1], ", ") + ", and " + items[count-1]
  87. }
  88. }