fix(tui): align floating menu hit testing
This commit is contained in:
parent
a4303eebc4
commit
aff4efbf5d
28
tui/tui.go
28
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
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user