Skip to content
Draft
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
2 changes: 2 additions & 0 deletions internal/ui/dialog/dialog.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const (
titleContentHeight = 1
// inputContentHeight is the height of the input content line.
inputContentHeight = 1
// mouseScrollLines is the number of lines to scroll on mouse wheel events.
mouseScrollLines = 3
)

// CloseKey is the default key binding to close dialogs.
Expand Down
66 changes: 63 additions & 3 deletions internal/ui/dialog/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@
Previous key.Binding
Close key.Binding
}
list *ModelsList
input textinput.Model
help help.Model
list *ModelsList

Check failure on line 92 in internal/ui/dialog/models.go

View workflow job for this annotation

GitHub Actions / lint / lint (ubuntu-latest)

File is not properly formatted (gofumpt)
input textinput.Model
help help.Model
listScreenY int
}

var _ Dialog = (*Models)(nil)
Expand Down Expand Up @@ -164,6 +165,43 @@
// HandleMsg implements Dialog.
func (m *Models) HandleMsg(msg tea.Msg) Action {
switch msg := msg.(type) {
case tea.MouseClickMsg:
idx, _ := m.list.ItemIndexAtPosition(0, msg.Y-m.listScreenY)
if idx >= 0 {
m.list.SetSelected(idx)
selectedItem := m.list.SelectedItem()
if selectedItem == nil {
break
}
modelItem, ok := selectedItem.(*ModelItem)
if !ok {
break
}
return ActionSelectModel{
Provider: modelItem.prov,
Model: modelItem.SelectedModel(),
ModelType: modelItem.SelectedModelType(),
}
}
case tea.MouseMotionMsg:
idx, _ := m.list.ItemIndexAtPosition(0, msg.Y-m.listScreenY)
if idx >= 0 {
m.list.SetSelected(idx)
}
case tea.MouseWheelMsg:
switch msg.Button {
case tea.MouseWheelUp:
m.list.ScrollBy(-mouseScrollLines)
case tea.MouseWheelDown:
m.list.ScrollBy(mouseScrollLines)
}
start, end := m.list.VisibleItemIndices()
sel := m.list.Selected()
if sel < start {
m.list.SetSelected(start)
} else if sel > end {
m.list.SetSelected(end)
}
case tea.KeyPressMsg:
switch {
case key.Matches(msg, m.keyMap.Close):
Expand Down Expand Up @@ -295,6 +333,28 @@

rc.Help = m.help.View(m)

// Compute list screen Y for mouse hover detection.
view := rc.Render()
viewWidth, viewHeight := lipgloss.Size(view)
var dialogMinY int
if m.isOnboarding {
blRect := common.BottomLeftRect(area, viewWidth, viewHeight)
dialogMinY = blRect.Min.Y
} else {
centerRect := common.CenterRect(area, viewWidth, viewHeight)
dialogMinY = centerRect.Min.Y
}
// heightOffset includes dialogViewFrameSize (top+bottom) and help.
// List Y from dialog top = borderTop+paddingTop + title + input.
listYFromDialogTop := heightOffset -
t.Dialog.View.GetBorderBottomSize() - t.Dialog.View.GetPaddingBottom() -
t.Dialog.HelpView.GetVerticalFrameSize()
if m.isOnboarding {
// Onboarding renders without dialog view wrapping.
listYFromDialogTop -= t.Dialog.View.GetBorderTopSize() + t.Dialog.View.GetPaddingTop()
}
m.listScreenY = dialogMinY + listYFromDialogTop

cur := m.Cursor()

if m.isOnboarding {
Expand Down
52 changes: 44 additions & 8 deletions internal/ui/dialog/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Session struct {
input textinput.Model
selectedSessionInx int
sessions []session.Session
listScreenY int

sessionsMode sessionsMode

Expand Down Expand Up @@ -141,6 +142,41 @@ func (s *Session) ID() string {
// HandleMsg implements Dialog.
func (s *Session) HandleMsg(msg tea.Msg) Action {
switch msg := msg.(type) {
case tea.MouseClickMsg:
if s.sessionsMode == sessionsModeNormal {
idx, _ := s.list.ItemIndexAtPosition(0, msg.Y-s.listScreenY)
if idx >= 0 {
s.list.SetSelected(idx)
if item := s.list.SelectedItem(); item != nil {
sessionItem := item.(*SessionItem)
return ActionSelectSession{sessionItem.Session}
}
}
}
case tea.MouseMotionMsg:
if s.sessionsMode == sessionsModeNormal {
idx, _ := s.list.ItemIndexAtPosition(0, msg.Y-s.listScreenY)
if idx >= 0 {
s.list.SetSelected(idx)
}
}
case tea.MouseWheelMsg:
if s.sessionsMode == sessionsModeNormal {
switch msg.Button {
case tea.MouseWheelUp:
s.list.ScrollBy(-mouseScrollLines)
case tea.MouseWheelDown:
s.list.ScrollBy(mouseScrollLines)
}
start, end := s.list.VisibleItemIndices()
sel := s.list.Selected()
if sel < start {
s.list.SetSelected(start)
} else if sel > end {
s.list.SetSelected(end)
}
}
return nil
case tea.KeyPressMsg:
switch s.sessionsMode {
case sessionsModeDeleting:
Expand Down Expand Up @@ -243,14 +279,6 @@ func (s *Session) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
s.list.SetSize(listWidth, listHeight)
s.help.SetWidth(innerWidth)

// This makes it so we do not scroll the list if we don't have to
start, end := s.list.VisibleItemIndices()

// if selected index is outside visible range, scroll to it
if s.selectedSessionInx < start || s.selectedSessionInx > end {
s.list.ScrollToSelected()
}

var cur *tea.Cursor
rc := NewRenderContext(t, width)
rc.Title = "Sessions"
Expand Down Expand Up @@ -321,6 +349,14 @@ func (s *Session) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {

view := rc.Render()

// Compute list screen Y for mouse hover detection.
viewWidth, viewHeight := lipgloss.Size(view)
centerRect := common.CenterRect(area, viewWidth, viewHeight)
listYFromDialogTop := heightOffset -
t.Dialog.View.GetBorderBottomSize() - t.Dialog.View.GetPaddingBottom() -
t.Dialog.HelpView.GetVerticalFrameSize()
s.listScreenY = centerRect.Min.Y + listYFromDialogTop

DrawCenterCursor(scr, area, view, cur)
return cur
}
Expand Down
19 changes: 18 additions & 1 deletion internal/ui/list/filterable.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,23 @@

// Render renders the filterable list.
func (f *FilterableList) Render() string {
f.List.SetItems(f.FilteredItems()...)
filtered := f.FilteredItems()
if !f.itemsMatch(filtered) {
f.List.SetItems(filtered...)
}
return f.List.Render()
}

// itemsMatch reports whether the current list items match the given slice
// by pointer identity and length.
func (f *FilterableList) itemsMatch(items []Item) bool {
if f.List.Len() != len(items) {

Check failure on line 133 in internal/ui/list/filterable.go

View workflow job for this annotation

GitHub Actions / lint / lint (ubuntu-latest)

QF1008: could remove embedded field "List" from selector (staticcheck)
return false
}
for i, item := range items {
if f.List.ItemAt(i) != item {

Check failure on line 137 in internal/ui/list/filterable.go

View workflow job for this annotation

GitHub Actions / lint / lint (ubuntu-latest)

QF1008: could remove embedded field "List" from selector (staticcheck)
return false
}
}
return true
}
22 changes: 17 additions & 5 deletions internal/ui/model/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.MouseClickMsg:
// Pass mouse events to dialogs first if any are open.
if m.dialog.HasDialogs() {
m.dialog.Update(msg)
if cmd := m.handleDialogMsg(msg); cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}

Expand All @@ -798,7 +800,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.MouseMotionMsg:
// Pass mouse events to dialogs first if any are open.
if m.dialog.HasDialogs() {
m.dialog.Update(msg)
if cmd := m.handleDialogMsg(msg); cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}

Expand Down Expand Up @@ -836,7 +840,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.MouseReleaseMsg:
// Pass mouse events to dialogs first if any are open.
if m.dialog.HasDialogs() {
m.dialog.Update(msg)
if cmd := m.handleDialogMsg(msg); cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}

Expand All @@ -858,7 +864,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.MouseWheelMsg:
// Pass mouse events to dialogs first if any are open.
if m.dialog.HasDialogs() {
m.dialog.Update(msg)
if cmd := m.handleDialogMsg(msg); cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}

Expand Down Expand Up @@ -2334,7 +2342,11 @@ func (m *UI) View() tea.View {
if !m.isTransparent {
v.BackgroundColor = m.com.Styles.Background
}
v.MouseMode = tea.MouseModeCellMotion
if m.dialog.HasDialogs() {
v.MouseMode = tea.MouseModeAllMotion
} else {
v.MouseMode = tea.MouseModeCellMotion
}
v.ReportFocus = m.caps.ReportFocusEvents
v.WindowTitle = "crush " + home.Short(m.com.Workspace.WorkingDir())

Expand Down
Loading