1
1
package list
2
2
3
3
import (
4
- "bytes"
5
4
"fmt"
6
5
tea "github.com/charmbracelet/bubbletea"
7
6
"github.com/muesli/reflow/ansi"
@@ -15,11 +14,12 @@ import (
15
14
type Model struct {
16
15
focus bool
17
16
18
- listItems []item
19
- curIndex int // curser
20
- visibleOffset int // begin of the visible lines
21
- lineCurserOffset int // offset or margin between the cursor and the viewport(visible) border
22
- less func (k , l string ) bool // function used for sorting
17
+ listItems []item
18
+ curIndex int // curser
19
+ visibleOffset int // begin of the visible lines
20
+ less func (k , l string ) bool // function used for sorting
21
+
22
+ CurserOffset int // offset or margin between the cursor and the viewport(visible) border
23
23
24
24
Width int
25
25
Height int
@@ -66,9 +66,14 @@ func View(model tea.Model) string {
66
66
if ! ok {
67
67
return "could not perform assertion on model"
68
68
}
69
+ return strings .Join (m .Lines (), "\n " )
70
+ }
69
71
72
+ // Lines returns the Visible lines of the list items
73
+ // used to display the current user interface
74
+ func (m * Model ) Lines () []string {
70
75
// check visible area
71
- height := m .Height - 1 // TODO question: why does the first line get cut of, if i ommit the -1?
76
+ height := m .Height
72
77
width := m .Width
73
78
offset := m .visibleOffset
74
79
if height * width <= 0 {
@@ -123,7 +128,7 @@ func View(model tea.Model) string {
123
128
}
124
129
125
130
var visLines int
126
- var holeString bytes. Buffer
131
+ stringLines := make ([] string , 0 , height )
127
132
out:
128
133
// Handle list items, start at first visible and go till end of list or visible (break)
129
134
for index := offset ; index < len (m .listItems ); index ++ {
174
179
line := fmt .Sprintf ("%s%s" , linePrefix , item .wrapedLines [0 ])
175
180
176
181
// Highlight and write first line
177
- holeString .WriteString (style .Styled (line ))
178
- holeString .WriteString ("\n " )
182
+ stringLines = append (stringLines , style .Styled (line ))
179
183
visLines ++
180
184
181
185
// Only write lines that are visible
@@ -195,17 +199,16 @@ out:
195
199
padLine := fmt .Sprintf ("%s%s" , wrapPrefix , line )
196
200
197
201
// Highlight and write wraped line
198
- holeString .WriteString (style .Styled (padLine ))
199
- holeString .WriteString ("\n " )
202
+ stringLines = append (stringLines , style .Styled (padLine ))
200
203
visLines ++
201
204
202
205
// Only write lines that are visible
203
- if visLines >= height {
206
+ if visLines > height {
204
207
break out
205
208
}
206
209
}
207
210
}
208
- return holeString . String ()
211
+ return stringLines
209
212
}
210
213
211
214
// lineNumber returns line number of the given index
@@ -315,24 +318,57 @@ func (m *Model) Up() error {
315
318
316
319
// Move moves the cursor by amount, does nothing if amount is 0
317
320
// and returns error != nil if amount gos beyond list borders
321
+ // or if the CurserOffset is greater than half of the display height
318
322
func (m * Model ) Move (amount int ) error {
323
+ // do nothing
319
324
if amount == 0 {
320
325
return nil
321
326
}
327
+ var err error
328
+ curOff := m .CurserOffset
329
+ visOff := m .visibleOffset
330
+ height := m .Height
331
+ if curOff >= height / 2 {
332
+ curOff = 0
333
+ err = fmt .Errorf ("cursor offset must be less than halfe of the display height: setting it to zero" )
334
+ }
335
+
322
336
target := m .curIndex + amount
323
337
if ! m .CheckWithinBorder (target ) {
324
338
return fmt .Errorf ("Cant move outside the list: %d" , target )
325
339
}
340
+ // move visible part of list if Curser is going beyond border.
341
+ lowerBorder := height + visOff - curOff
342
+ upperBorder := visOff + curOff
343
+
344
+ direction := 1
345
+ if amount < 0 {
346
+ direction = - 1
347
+ }
348
+
349
+ // visible Down movement
350
+ if direction > 0 && target > lowerBorder {
351
+ visOff = target - (height - curOff )
352
+ }
353
+ // visible Up movement
354
+ if direction < 0 && target < upperBorder {
355
+ visOff = target - curOff
356
+ }
357
+ // dont go infront of list begin
358
+ if visOff < 0 {
359
+ visOff = 0
360
+ }
326
361
m .curIndex = target
327
- return nil
362
+ m .visibleOffset = visOff
363
+ return err
328
364
}
329
365
330
366
// NewModel returns a Model with some save/sane defaults
331
367
func NewModel () Model {
332
368
p := termenv .ColorProfile ()
333
369
style := termenv.Style {}.Background (p .Color ("#ff0000" ))
334
370
return Model {
335
- lineCurserOffset : 5 ,
371
+ CurserOffset : 5 ,
336
372
337
373
Wrap : true ,
338
374
@@ -422,7 +458,7 @@ func (m *Model) Top() {
422
458
func (m * Model ) Bottom () {
423
459
end := len (m .listItems ) - 1
424
460
m .curIndex = end
425
- maxVisItems := m .Height - m .lineCurserOffset
461
+ maxVisItems := m .Height - m .CurserOffset
426
462
var visLines , smallestVisIndex int
427
463
for c := end ; visLines < maxVisItems ; c -- {
428
464
if c < 0 {
0 commit comments