@@ -18,19 +18,22 @@ type updateOpLogMsg struct {
1818}
1919
2020var (
21- _ list.IList = (* Model )(nil )
22- _ common.Model = (* Model )(nil )
21+ _ list.IList = (* Model )(nil )
22+ _ common.Model = (* Model )(nil )
23+ _ common.IMouseAware = (* Model )(nil )
2324)
2425
2526type Model struct {
2627 * common.ViewNode
27- context * context.MainContext
28- renderer * list.ListRenderer
29- rows []row
30- cursor int
31- keymap config.KeyMappings [key.Binding ]
32- textStyle lipgloss.Style
33- selectedStyle lipgloss.Style
28+ * common.MouseAware
29+ context * context.MainContext
30+ renderer * list.ListRenderer
31+ rows []row
32+ cursor int
33+ keymap config.KeyMappings [key.Binding ]
34+ textStyle lipgloss.Style
35+ selectedStyle lipgloss.Style
36+ ensureCursorView bool
3437}
3538
3639func (m * Model ) Len () int {
@@ -71,22 +74,91 @@ func (m *Model) Init() tea.Cmd {
7174 return m .load ()
7275}
7376
77+ func (m * Model ) ClickAt (x , y int ) tea.Cmd {
78+ if len (m .rows ) == 0 {
79+ return nil
80+ }
81+
82+ localY := y - m .Frame .Min .Y
83+
84+ currentStart := m .renderer .ViewRange .Start
85+ if localY >= m .Height {
86+ localY = m .Height - 1
87+ if localY < 0 {
88+ return nil
89+ }
90+ }
91+ line := currentStart + localY
92+ row := m .rowAtLine (line )
93+ if row == - 1 {
94+ return nil
95+ }
96+
97+ m .cursor = row
98+ m .ensureCursorView = true
99+ return m .updateSelection ()
100+ }
101+
102+ func (m * Model ) rowAtLine (line int ) int {
103+ for _ , rr := range m .renderer .RowRanges () {
104+ if line >= rr .StartLine && line < rr .EndLine {
105+ return rr .Row
106+ }
107+ }
108+ return - 1
109+ }
110+
111+ func (m * Model ) Scroll (delta int ) tea.Cmd {
112+ m .ensureCursorView = false
113+ desiredStart := m .renderer .ViewRange .Start + delta
114+ if desiredStart < 0 {
115+ desiredStart = 0
116+ }
117+
118+ totalLines := m .renderer .AbsoluteLineCount ()
119+ maxStart := totalLines - m .Height
120+ if maxStart < 0 {
121+ maxStart = 0
122+ }
123+ newStart := desiredStart
124+ if newStart > maxStart {
125+ newStart = maxStart
126+ }
127+ m .renderer .ViewRange .Start = newStart
128+ return nil
129+ }
130+
74131func (m * Model ) Update (msg tea.Msg ) tea.Cmd {
75132 switch msg := msg .(type ) {
76133 case updateOpLogMsg :
77134 m .rows = msg .Rows
78135 m .renderer .Reset ()
136+ case tea.MouseMsg :
137+ switch msg .Action {
138+ case tea .MouseActionPress :
139+ switch msg .Button {
140+ case tea .MouseButtonLeft :
141+ return m .ClickAt (msg .X , msg .Y )
142+ case tea .MouseButtonWheelUp :
143+ return m .Scroll (- 3 )
144+ case tea .MouseButtonWheelDown :
145+ return m .Scroll (3 )
146+ }
147+ return nil
148+ }
79149 case tea.KeyMsg :
80150 switch {
81151 case key .Matches (msg , m .keymap .Cancel ):
82152 return common .Close
83153 case key .Matches (msg , m .keymap .Up ):
84154 if m .cursor > 0 {
85155 m .cursor --
156+ m .ensureCursorView = true
86157 }
87158 case key .Matches (msg , m .keymap .Down ):
88159 if m .cursor < len (m .rows )- 1 {
89160 m .cursor ++
161+ m .ensureCursorView = true
90162 }
91163 case key .Matches (msg , m .keymap .Diff ):
92164 return func () tea.Msg {
@@ -118,7 +190,7 @@ func (m *Model) View() string {
118190 m .renderer .Reset ()
119191 m .renderer .SetWidth (m .Width )
120192 m .renderer .SetHeight (m .Height )
121- content := m .renderer .Render ( m .cursor )
193+ content := m .renderer .RenderWithOptions (list. RenderOptions { FocusIndex : m .cursor , EnsureFocusVisible : m . ensureCursorView } )
122194 return m .textStyle .Render (content )
123195}
124196
@@ -139,6 +211,7 @@ func New(context *context.MainContext) *Model {
139211 node := common .NewViewNode (0 , 0 )
140212 m := & Model {
141213 ViewNode : node ,
214+ MouseAware : common .NewMouseAware (),
142215 context : context ,
143216 keymap : keyMap ,
144217 rows : nil ,
0 commit comments