Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions internal/app/app_input_mouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,35 @@ func (a *App) handleMouseMsg(msg tea.Msg) tea.Cmd {

// routeMouseWheel routes mouse wheel events to the appropriate pane.
func (a *App) routeMouseWheel(msg tea.MouseWheelMsg) tea.Cmd {
// Route wheel input by keyboard focus; child models currently ignore wheel
// while unfocused.
if a.prefixPaletteContainsPoint(msg.X, msg.Y) {
// Palette wheel input is currently non-interactive; consume it so hidden
// panes cannot scroll or steal focus while prefix mode is active.
return nil
}

targetPane := a.focusedPane
// Modal overlays and toast overlays do not consume wheel today, so preserve
// focused-pane routing instead of hit-testing obscured panes beneath them.
if !a.overlayVisible() && !a.toastCoversPoint(msg.X, msg.Y) {
// Route wheel input by pointer target when possible so hovered panes
// scroll without requiring a prior click. Fall back to keyboard focus
// when the pointer is outside interactive pane geometry.
hoverPane, hasTarget := a.paneForPoint(msg.X, msg.Y)
if hasTarget {
// Dashboard wheel handling activates rows, so do not retarget passive
// hover wheel input into it from another pane.
if hoverPane != messages.PaneDashboard || a.focusedPane == messages.PaneDashboard {
if a.canRetargetWheelToPane(hoverPane) {
targetPane = hoverPane
}
}
}
}

var focusCmd tea.Cmd
if targetPane != a.focusedPane {
focusCmd = a.focusPaneOnWheel(targetPane)
}

switch targetPane {
case messages.PaneDashboard:
Expand All @@ -104,31 +130,44 @@ func (a *App) routeMouseWheel(msg tea.MouseWheelMsg) tea.Cmd {
}
newDashboard, cmd := a.dashboard.Update(adjusted)
a.dashboard = newDashboard
return cmd
return common.SafeBatch(focusCmd, cmd)
case messages.PaneCenter:
adjusted := msg
if a.layout != nil {
adjusted.Y -= a.layout.TopGutter()
}
newCenter, cmd := a.center.Update(adjusted)
a.center = newCenter
return cmd
return common.SafeBatch(focusCmd, cmd)
case messages.PaneSidebarTerminal:
newTerm, cmd := a.sidebarTerminal.Update(msg)
a.sidebarTerminal = newTerm
return cmd
return common.SafeBatch(focusCmd, cmd)
case messages.PaneSidebar:
adjusted := msg
if a.layout != nil {
adjusted.X, adjusted.Y = a.adjustSidebarMouseXY(adjusted.X, adjusted.Y)
}
newSidebar, cmd := a.sidebar.Update(adjusted)
a.sidebar = newSidebar
return cmd
return common.SafeBatch(focusCmd, cmd)
}
return nil
}

func (a *App) canRetargetWheelToPane(pane messages.PaneType) bool {
switch pane {
case messages.PaneCenter:
return a.center != nil && a.center.CanConsumeWheel()
case messages.PaneSidebar:
return a.sidebar != nil && a.sidebar.CanConsumeWheel()
case messages.PaneSidebarTerminal:
return a.sidebarTerminal != nil && a.sidebarTerminal.CanConsumeWheel()
default:
return false
}
}

// routeMouseMotion routes mouse motion events to the appropriate pane.
func (a *App) routeMouseMotion(msg tea.MouseMotionMsg) tea.Cmd {
// Keep left-button drag motion bound to the pane focused on mouse-down.
Expand Down
Loading
Loading