@@ -32,6 +32,29 @@ type ViewPos struct {
32
32
LineOffset int
33
33
}
34
34
35
+ // ScreenInfo holds all information about the screen Area
36
+ type ScreenInfo struct {
37
+ Width int
38
+ Height int
39
+ Profile termenv.Profile
40
+ }
41
+
42
+ // Prefixer is used to prefix all visible Lines.
43
+ // Init gets called ones on the beginning of the Lines methode
44
+ // and then Prefix ones, per line to draw, to generate according prefixes.
45
+ type Prefixer interface {
46
+ InitPrefixer (ViewPos , ScreenInfo ) int
47
+ Prefix (currentItem , currentLine int , selected , lastLine bool ) string
48
+ }
49
+
50
+ // Suffixer is used to suffix all visible Lines.
51
+ // InitSuffixer gets called ones on the beginning of the Lines methode
52
+ // and then Suffix ones, per line to draw, to generate according suffixes.
53
+ type Suffixer interface {
54
+ InitSuffixer (ViewPos , ScreenInfo ) int
55
+ Suffix (currentItem , currentLine int , selected , lastLine bool ) string
56
+ }
57
+
35
58
// Model is a bubbletea List of strings
36
59
type Model struct {
37
60
focus bool
@@ -51,11 +74,8 @@ type Model struct {
51
74
52
75
Wrap bool
53
76
54
- PrefixFunc func (position ViewPos , height int ) (func (currentIndex int , selected bool , wrapedIndex int ) string , int )
55
- // PrefixFunc func(value fmt.Stringer, position ViewPos, selected bool) string
56
- // PrefixWrapFunc func(value fmt.Stringer, position ViewPos, selected bool, wrapedIndex int) string
57
- // SuffixFunc func(value fmt.Stringer, position ViewPos, selected bool) string
58
- // SuffixWrapFunc func(value fmt.Stringer, position ViewPos, selected bool, wrapedIndex int) string
77
+ // PrefixFunc func(position ViewPos, height int) (func(currentIndex int, selected bool, wrapedIndex int) string, int)
78
+ PrefixGen Prefixer
59
79
60
80
LineStyle termenv.Style
61
81
SelectedStyle termenv.Style
@@ -113,13 +133,10 @@ func (m *Model) Lines() []string {
113
133
panic ("Can't display with zero width or hight of Viewport" )
114
134
}
115
135
116
- var prefixFunc func ( int , bool , int ) string
136
+ // This is only used if the Lines methode is called befor the Update methode is called the first time
117
137
var holePrefixWidth int
118
- if m .PrefixFunc != nil {
119
- prefixFunc , holePrefixWidth = m .PrefixFunc (m .viewPos , height )
120
- } else {
121
- // use default
122
- prefixFunc , holePrefixWidth = AbsolutLinePrefix (m .viewPos , height )
138
+ if m .PrefixGen != nil {
139
+ holePrefixWidth = m .PrefixGen .InitPrefixer (m .viewPos , ScreenInfo {Height : m .Height , Width : m .Width , Profile : termenv .ColorProfile ()})
123
140
}
124
141
125
142
// Get actual content width
@@ -171,10 +188,9 @@ out:
171
188
// TODO hard limit the string length
172
189
}
173
190
174
- // retrieve line prefix from prefix closure
175
191
var linePrefix string
176
- if prefixFunc != nil {
177
- linePrefix = prefixFunc (index , item .selected , 0 )
192
+ if m . PrefixGen != nil {
193
+ linePrefix = m . PrefixGen . Prefix (index , 0 , item .selected , item . wrapedLenght == 0 )
178
194
}
179
195
180
196
// join pad and first line content
221
237
// Pad left of line
222
238
// NOTE line break is not added here because it would mess with the highlighting
223
239
var wrapPrefix string
224
- if prefixFunc != nil {
225
- wrapPrefix = prefixFunc (index , item .selected , i + 1 )
240
+ if m . PrefixGen != nil {
241
+ wrapPrefix = m . PrefixGen . Prefix (index , i + 1 , item .selected , i == item . wrapedLenght - 2 )
226
242
}
227
243
padLine := fmt .Sprintf ("%s%s" , wrapPrefix , line )
228
244
@@ -249,6 +265,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
249
265
if ! m .focus {
250
266
return m , nil
251
267
}
268
+
269
+ if m .PrefixGen == nil {
270
+ // use default
271
+ m .PrefixGen = NewDefault ()
272
+ }
273
+
252
274
var cmd tea.Cmd
253
275
254
276
switch msg := msg .(type ) {
@@ -780,30 +802,72 @@ func (m *Model) keepVisibleWrap(target int) (ViewPos, error) {
780
802
return ViewPos {ItemOffset : m .viewPos .ItemOffset , LineOffset : m .viewPos .LineOffset , Cursor : target }, nil
781
803
}
782
804
783
- // AbsolutLinePrefix returns a function which will be used for prefix generation
784
- func AbsolutLinePrefix (position ViewPos , height int ) (func (currentLine int , selected bool , wrapIndex int ) string , int ) {
785
-
786
- PrefixWrap := false
805
+ // DefaultPrefixer is the default struct used for Prefixing a line
806
+ type DefaultPrefixer struct {
807
+ PrefixWrap bool
787
808
788
809
// Make clear where a item begins and where it ends
789
- Seperator := "╭"
790
- SeperatorWrap := "│"
810
+ Seperator string
811
+ SeperatorWrap string
791
812
792
813
// Mark it so that even without color support all is explicit
793
- CurrentMarker := ">"
794
- SelectedPrefix := "*"
814
+ CurrentMarker string
815
+ SelectedPrefix string
795
816
796
817
// enable Linenumber
797
- Number := true
798
- NumberRelative := false
818
+ Number bool
819
+ NumberRelative bool
820
+
821
+ UnSelectedPrefix string
799
822
800
- UnSelectedPrefix := ""
823
+ prefixWidth int
824
+ viewPos ViewPos
825
+
826
+ markWidth int
827
+ numWidth int
828
+
829
+ unmark string
830
+ mark string
831
+
832
+ selectedString string
833
+ unselect string
834
+
835
+ wrapSelectPad string
836
+ wrapUnSelePad string
837
+
838
+ sepItem string
839
+ sepWrap string
840
+ }
841
+
842
+ // NewDefault returns a DefautPrefixer with default values
843
+ func NewDefault () * DefaultPrefixer {
844
+ return & DefaultPrefixer {
845
+ PrefixWrap : false ,
846
+
847
+ // Make clear where a item begins and where it ends
848
+ Seperator : "╭" ,
849
+ SeperatorWrap : "│" ,
850
+
851
+ // Mark it so that even without color support all is explicit
852
+ CurrentMarker : ">" ,
853
+ SelectedPrefix : "*" ,
854
+ UnSelectedPrefix : "" ,
855
+
856
+ // enable Linenumber
857
+ Number : true ,
858
+ NumberRelative : false ,
859
+ }
860
+ }
861
+
862
+ // InitPrefixer returns a function which will be used for prefix generation
863
+ func (d * DefaultPrefixer ) InitPrefixer (position ViewPos , screen ScreenInfo ) int {
864
+ d .viewPos = position
801
865
802
866
offset := position .ItemOffset
803
867
804
868
// Get separators width
805
- widthItem := ansi .PrintableRuneWidth (Seperator )
806
- widthWrap := ansi .PrintableRuneWidth (SeperatorWrap )
869
+ widthItem := ansi .PrintableRuneWidth (d . Seperator )
870
+ widthWrap := ansi .PrintableRuneWidth (d . SeperatorWrap )
807
871
808
872
// Find max width
809
873
sepWidth := widthItem
@@ -812,83 +876,83 @@ func AbsolutLinePrefix(position ViewPos, height int) (func(currentLine int, sele
812
876
}
813
877
814
878
// get widest possible number, for padding
815
- numWidth : = len (fmt .Sprintf ("%d" , offset + height - 1 ))
879
+ d . numWidth = len (fmt .Sprintf ("%d" , offset + screen . Height ))
816
880
817
881
// pad all prefixes to the same width for easy exchange
818
- selectedString := SelectedPrefix
819
- unselect := UnSelectedPrefix
820
- selWid := ansi .PrintableRuneWidth (selectedString )
821
- tmpWid := ansi .PrintableRuneWidth (unselect )
882
+ d . selectedString = d . SelectedPrefix
883
+ d . unselect = d . UnSelectedPrefix
884
+ selWid := ansi .PrintableRuneWidth (d . selectedString )
885
+ tmpWid := ansi .PrintableRuneWidth (d . unselect )
822
886
823
887
selectWidth := selWid
824
888
if tmpWid > selectWidth {
825
889
selectWidth = tmpWid
826
890
}
827
- selectedString = strings .Repeat (" " , selectWidth - selWid ) + selectedString
891
+ d . selectedString = strings .Repeat (" " , selectWidth - selWid ) + d . selectedString
828
892
829
- wrapSelectPad : = strings .Repeat (" " , selectWidth )
830
- wrapUnSelePad : = strings .Repeat (" " , selectWidth )
831
- if PrefixWrap {
832
- wrapSelectPad = strings .Repeat (" " , selectWidth - selWid ) + selectedString
833
- wrapUnSelePad = strings .Repeat (" " , selectWidth - tmpWid ) + unselect
893
+ d . wrapSelectPad = strings .Repeat (" " , selectWidth )
894
+ d . wrapUnSelePad = strings .Repeat (" " , selectWidth )
895
+ if d . PrefixWrap {
896
+ d . wrapSelectPad = strings .Repeat (" " , selectWidth - selWid ) + d . selectedString
897
+ d . wrapUnSelePad = strings .Repeat (" " , selectWidth - tmpWid ) + d . unselect
834
898
}
835
899
836
- unselect = strings .Repeat (" " , selectWidth - tmpWid ) + unselect
900
+ d . unselect = strings .Repeat (" " , selectWidth - tmpWid ) + d . unselect
837
901
838
902
// pad all separators to the same width for easy exchange
839
- sepItem : = strings .Repeat (" " , sepWidth - widthItem ) + Seperator
840
- sepWrap : = strings .Repeat (" " , sepWidth - widthWrap ) + SeperatorWrap
903
+ d . sepItem = strings .Repeat (" " , sepWidth - widthItem ) + d . Seperator
904
+ d . sepWrap = strings .Repeat (" " , sepWidth - widthWrap ) + d . SeperatorWrap
841
905
842
906
// pad right of prefix, with length of current pointer
843
- mark := CurrentMarker
844
- markWidth : = ansi .PrintableRuneWidth (mark )
845
- unmark : = strings .Repeat (" " , markWidth )
907
+ d . mark = d . CurrentMarker
908
+ d . markWidth = ansi .PrintableRuneWidth (d . mark )
909
+ d . unmark = strings .Repeat (" " , d . markWidth )
846
910
847
911
// Get the hole prefix width
848
- holePrefixWidth := numWidth + selectWidth + sepWidth + markWidth
849
-
850
- // Closure varibale
851
- currentCursor := position .Cursor
852
-
853
- return func (currentIndex int , selected bool , wrapIndex int ) string {
854
- // if a number is set, prepend first line with number and both with enough spaces
855
- firstPad := strings .Repeat (" " , numWidth )
856
- var wrapPad string
857
- var lineNum int
858
- if Number {
859
- lineNum = lineNumber (NumberRelative , currentCursor , currentIndex )
860
- }
861
- number := fmt .Sprintf ("%d" , lineNum )
862
- // since digits are only single bytes, len is sufficient:
863
- firstPad = strings .Repeat (" " , numWidth - len (number )) + number
864
- // pad wrapped lines
865
- wrapPad = strings .Repeat (" " , numWidth )
866
- // Selecting: handle highlighting and prefixing of selected lines
867
- selString := unselect
868
-
869
- wrapPrePad := wrapUnSelePad
870
- if selected {
871
- selString = selectedString
872
- wrapPrePad = wrapSelectPad
873
- }
912
+ d .prefixWidth = d .numWidth + selectWidth + sepWidth + d .markWidth
874
913
875
- // Current: handle highlighting of current item/first-line
876
- curPad := unmark
877
- if currentIndex == position .Cursor {
878
- curPad = mark
879
- }
914
+ return d .prefixWidth
915
+ }
880
916
881
- // join all prefixes
882
- var wrapPrefix , linePrefix string
917
+ // Prefix prefixes a given line
918
+ func (d * DefaultPrefixer ) Prefix (currentIndex int , wrapIndex int , selected , lastLine bool ) string {
919
+ // if a number is set, prepend first line with number and both with enough spaces
920
+ firstPad := strings .Repeat (" " , d .numWidth )
921
+ var wrapPad string
922
+ var lineNum int
923
+ if d .Number {
924
+ lineNum = lineNumber (d .NumberRelative , d .viewPos .Cursor , currentIndex )
925
+ }
926
+ number := fmt .Sprintf ("%d" , lineNum )
927
+ // since digits are only single bytes, len is sufficient:
928
+ firstPad = strings .Repeat (" " , d .numWidth - len (number )) + number
929
+ // pad wrapped lines
930
+ wrapPad = strings .Repeat (" " , d .numWidth )
931
+ // Selecting: handle highlighting and prefixing of selected lines
932
+ selString := d .unselect
883
933
884
- linePrefix = strings .Join ([]string {firstPad , selString , sepItem , curPad }, "" )
885
- if wrapIndex > 0 {
886
- wrapPrefix = strings .Join ([]string {wrapPad , wrapPrePad , sepWrap , unmark }, "" ) // don't prefix wrap lines with CurrentMarker (unmark)
887
- return wrapPrefix
888
- }
934
+ wrapPrePad := d .wrapUnSelePad
935
+ if selected {
936
+ selString = d .selectedString
937
+ wrapPrePad = d .wrapSelectPad
938
+ }
939
+
940
+ // Current: handle highlighting of current item/first-line
941
+ curPad := d .unmark
942
+ if currentIndex == d .viewPos .Cursor {
943
+ curPad = d .mark
944
+ }
945
+
946
+ // join all prefixes
947
+ var wrapPrefix , linePrefix string
948
+
949
+ linePrefix = strings .Join ([]string {firstPad , selString , d .sepItem , curPad }, "" )
950
+ if wrapIndex > 0 {
951
+ wrapPrefix = strings .Join ([]string {wrapPad , wrapPrePad , d .sepWrap , d .unmark }, "" ) // don't prefix wrap lines with CurrentMarker (unmark)
952
+ return wrapPrefix
953
+ }
889
954
890
- return linePrefix
891
- }, holePrefixWidth
955
+ return linePrefix
892
956
}
893
957
894
958
// lineNumber returns line number of the given index
0 commit comments