diff --git a/tui/tui.go b/tui/tui.go index debbcee..1996d85 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -674,13 +674,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.MouseMsg: if typed.Action == tea.MouseActionMotion && typed.Button == tea.MouseButtonNone { if m.menuOpen { - m.handleMenuMouse(typed) + return m, m.handleMenuMouse(typed) } return m, nil } if m.menuOpen { - m.handleMenuMouse(typed) - return m, nil + return m, m.handleMenuMouse(typed) } switch { case typed.Type == tea.MouseWheelUp || typed.Button == tea.MouseButtonWheelUp: @@ -937,47 +936,47 @@ func (m *model) clearLastClick() { m.lastClickAt = time.Time{} } -func (m *model) handleMenuMouse(msg tea.MouseMsg) { +func (m *model) handleMenuMouse(msg tea.MouseMsg) tea.Cmd { switch { case msg.Type == tea.MouseWheelUp || msg.Button == tea.MouseButtonWheelUp: m.menuIndex = m.nextSelectableMenuIndex(-1) m.keepMenuVisible() - return + return nil case msg.Type == tea.MouseWheelDown || msg.Button == tea.MouseButtonWheelDown: m.menuIndex = m.nextSelectableMenuIndex(1) m.keepMenuVisible() - return + return nil case msg.Button == tea.MouseButtonRight && msg.Action == tea.MouseActionPress: m.closeMenu() - return + return nil } index, ok := m.menuIndexAtMouse(msg.X, msg.Y) if msg.Action == tea.MouseActionMotion { if !ok || index < 0 || index >= len(m.menuItems) { - return + return nil } m.menuIndex = m.nearestSelectableMenuIndex(index, 1) m.keepMenuVisible() - return + return nil } if msg.Button != tea.MouseButtonLeft || msg.Action != tea.MouseActionPress { - return + return nil } if !ok { m.closeMenu() - return + return nil } if index < 0 || index >= len(m.menuItems) { - return + return nil } if !m.menuItems[index].selectable() { m.menuIndex = m.nearestSelectableMenuIndex(index, 1) m.keepMenuVisible() - return + return nil } m.menuIndex = index m.keepMenuVisible() - _ = m.runMenuItem(m.menuItems[m.menuIndex]) + return m.runMenuItem(m.menuItems[m.menuIndex]) } func (m model) menuIndexAtMouse(x, y int) (int, bool) { @@ -985,6 +984,7 @@ func (m model) menuIndexAtMouse(x, y int) (int, bool) { rowOffset := 4 if m.menuFloating { menuRect = m.menuRect + rowOffset = 3 } if !menuRect.contains(x, y) { return 0, false diff --git a/tui/tui_test.go b/tui/tui_test.go index e90d93f..b0a76e4 100644 --- a/tui/tui_test.go +++ b/tui/tui_test.go @@ -1317,6 +1317,76 @@ func TestRightClickPlacesFloatingMenu(t *testing.T) { } } +func TestMouseClickUsesFloatingMenuOffset(t *testing.T) { + m := newModel(Options{Title: "archive", Items: []Item{{Title: "alpha"}}}) + m.width = 140 + m.height = 32 + m.menuOpen = true + m.menuFloating = true + m.menuRect = rect{x: 5, y: 3, w: 40, h: 12} + m.menuOff = 5 + m.menuItems = make([]menuItem, 8) + for index := range m.menuItems { + m.menuItems[index] = menuItem{label: fmt.Sprintf("Item %d", index), action: actionQuit} + } + + updated, cmd := m.Update(tea.MouseMsg{ + X: m.menuRect.x + 2, + Y: m.menuRect.y + 3, + Button: tea.MouseButtonLeft, + Action: tea.MouseActionPress, + }) + m = updated.(model) + + if m.menuIndex != 5 { + t.Fatalf("floating menu click selected %d, want offset row 5", m.menuIndex) + } + if cmd == nil { + t.Fatal("floating menu click did not run selected item") + } + if !m.menuOpen || !m.menuFloating { + t.Fatalf("floating menu should stay open for submenu-like actions, open=%v floating=%v", m.menuOpen, m.menuFloating) + } +} + +func TestMouseMotionHoversFloatingMenuItems(t *testing.T) { + m := newModel(Options{Title: "archive", Items: []Item{{Title: "alpha"}}}) + m.width = 140 + m.height = 32 + m.menuOpen = true + m.menuFloating = true + m.menuRect = rect{x: 5, y: 3, w: 40, h: 12} + m.menuOff = 1 + m.menuItems = make([]menuItem, 6) + for index := range m.menuItems { + m.menuItems[index] = menuItem{label: fmt.Sprintf("Item %d", index), action: actionClose} + } + + updated, _ := m.Update(tea.MouseMsg{ + X: m.menuRect.x + 2, + Y: m.menuRect.y + 5, + Button: tea.MouseButtonNone, + Action: tea.MouseActionMotion, + }) + m = updated.(model) + + if m.menuIndex != 3 { + t.Fatalf("hover selected %d, want item 3", m.menuIndex) + } + + updated, _ = m.Update(tea.MouseMsg{ + X: m.menuRect.x + 2, + Y: m.menuRect.y + 6, + Button: tea.MouseButtonRight, + Action: tea.MouseActionMotion, + }) + m = updated.(model) + + if m.menuIndex != 4 { + t.Fatalf("right-button hover selected %d, want item 4", m.menuIndex) + } +} + func TestClickingRowsHeaderSorts(t *testing.T) { m := newModel(Options{ Title: "archive",