1 package tview
2
3 import (
4 "sort"
5
6 "github.com/gdamore/tcell/v2"
7 colorful "github.com/lucasb-eyer/go-colorful"
8 )
9
10
11
12
13 type TableCell struct {
14
15 Reference interface{}
16
17
18 Text string
19
20
21
22 Align int
23
24
25
26
27 MaxWidth int
28
29
30
31 Expansion int
32
33
34 Color tcell.Color
35
36
37 BackgroundColor tcell.Color
38
39
40
41 Transparent bool
42
43
44 Attributes tcell.AttrMask
45
46
47 NotSelectable bool
48
49
50
51
52 Clicked func() bool
53
54
55 x, y, width int
56 }
57
58
59
60
61 func NewTableCell(text string) *TableCell {
62 return &TableCell{
63 Text: text,
64 Align: AlignLeft,
65 Color: Styles.PrimaryTextColor,
66 BackgroundColor: Styles.PrimitiveBackgroundColor,
67 Transparent: true,
68 }
69 }
70
71
72 func (c *TableCell) SetText(text string) *TableCell {
73 c.Text = text
74 return c
75 }
76
77
78
79 func (c *TableCell) SetAlign(align int) *TableCell {
80 c.Align = align
81 return c
82 }
83
84
85
86
87 func (c *TableCell) SetMaxWidth(maxWidth int) *TableCell {
88 c.MaxWidth = maxWidth
89 return c
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 func (c *TableCell) SetExpansion(expansion int) *TableCell {
106 if expansion < 0 {
107 panic("Table cell expansion values may not be negative")
108 }
109 c.Expansion = expansion
110 return c
111 }
112
113
114 func (c *TableCell) SetTextColor(color tcell.Color) *TableCell {
115 c.Color = color
116 return c
117 }
118
119
120
121 func (c *TableCell) SetBackgroundColor(color tcell.Color) *TableCell {
122 c.BackgroundColor = color
123 c.Transparent = false
124 return c
125 }
126
127
128
129
130 func (c *TableCell) SetTransparency(transparent bool) *TableCell {
131 c.Transparent = transparent
132 return c
133 }
134
135
136
137
138
139 func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell {
140 c.Attributes = attr
141 return c
142 }
143
144
145
146 func (c *TableCell) SetStyle(style tcell.Style) *TableCell {
147 c.Color, c.BackgroundColor, c.Attributes = style.Decompose()
148 return c
149 }
150
151
152 func (c *TableCell) SetSelectable(selectable bool) *TableCell {
153 c.NotSelectable = !selectable
154 return c
155 }
156
157
158
159
160 func (c *TableCell) SetReference(reference interface{}) *TableCell {
161 c.Reference = reference
162 return c
163 }
164
165
166 func (c *TableCell) GetReference() interface{} {
167 return c.Reference
168 }
169
170
171
172
173
174
175
176
177
178 func (c *TableCell) GetLastPosition() (x, y, width int) {
179 return c.x, c.y, c.width
180 }
181
182
183
184
185 func (c *TableCell) SetClickedFunc(clicked func() bool) *TableCell {
186 c.Clicked = clicked
187 return c
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201 type TableContent interface {
202
203
204
205 GetCell(row, column int) *TableCell
206
207
208 GetRowCount() int
209
210
211 GetColumnCount() int
212
213
214
215
216
217
218
219
220
221 SetCell(row, column int, cell *TableCell)
222
223
224
225 RemoveRow(row int)
226
227
228
229 RemoveColumn(column int)
230
231
232
233
234 InsertRow(row int)
235
236
237
238
239 InsertColumn(column int)
240
241
242 Clear()
243 }
244
245
246
247
248
249
250 type TableContentReadOnly struct{}
251
252
253 func (t TableContentReadOnly) SetCell(row, column int, cell *TableCell) {
254
255 }
256
257
258 func (t TableContentReadOnly) RemoveRow(row int) {
259
260 }
261
262
263 func (t TableContentReadOnly) RemoveColumn(column int) {
264
265 }
266
267
268 func (t TableContentReadOnly) InsertRow(row int) {
269
270 }
271
272
273 func (t TableContentReadOnly) InsertColumn(column int) {
274
275 }
276
277
278 func (t TableContentReadOnly) Clear() {
279
280 }
281
282
283
284 type tableDefaultContent struct {
285
286 cells [][]*TableCell
287
288
289 lastColumn int
290 }
291
292
293 func (t *tableDefaultContent) Clear() {
294 t.cells = nil
295 t.lastColumn = -1
296 }
297
298
299 func (t *tableDefaultContent) SetCell(row, column int, cell *TableCell) {
300 if row >= len(t.cells) {
301 t.cells = append(t.cells, make([][]*TableCell, row-len(t.cells)+1)...)
302 }
303 rowLen := len(t.cells[row])
304 if column >= rowLen {
305 t.cells[row] = append(t.cells[row], make([]*TableCell, column-rowLen+1)...)
306 for c := rowLen; c < column; c++ {
307 t.cells[row][c] = &TableCell{}
308 }
309 }
310 t.cells[row][column] = cell
311 if column > t.lastColumn {
312 t.lastColumn = column
313 }
314 }
315
316
317 func (t *tableDefaultContent) RemoveRow(row int) {
318 if row < 0 || row >= len(t.cells) {
319 return
320 }
321 t.cells = append(t.cells[:row], t.cells[row+1:]...)
322 }
323
324
325 func (t *tableDefaultContent) RemoveColumn(column int) {
326 for row := range t.cells {
327 if column < 0 || column >= len(t.cells[row]) {
328 continue
329 }
330 t.cells[row] = append(t.cells[row][:column], t.cells[row][column+1:]...)
331 }
332 if column >= 0 && column <= t.lastColumn {
333 t.lastColumn--
334 }
335 }
336
337
338 func (t *tableDefaultContent) InsertRow(row int) {
339 if row >= len(t.cells) {
340 return
341 }
342 t.cells = append(t.cells, nil)
343 copy(t.cells[row+1:], t.cells[row:])
344 t.cells[row] = nil
345 }
346
347
348 func (t *tableDefaultContent) InsertColumn(column int) {
349 for row := range t.cells {
350 if column >= len(t.cells[row]) {
351 continue
352 }
353 t.cells[row] = append(t.cells[row], nil)
354 copy(t.cells[row][column+1:], t.cells[row][column:])
355 t.cells[row][column] = &TableCell{}
356 }
357 }
358
359
360 func (t *tableDefaultContent) GetCell(row, column int) *TableCell {
361 if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) {
362 return nil
363 }
364 return t.cells[row][column]
365 }
366
367
368 func (t *tableDefaultContent) GetRowCount() int {
369 return len(t.cells)
370 }
371
372
373 func (t *tableDefaultContent) GetColumnCount() int {
374 if len(t.cells) == 0 {
375 return 0
376 }
377 return t.lastColumn + 1
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429 type Table struct {
430 *Box
431
432
433 borders bool
434
435
436 bordersColor tcell.Color
437
438
439 separator rune
440
441
442 content TableContent
443
444
445
446 evaluateAllRows bool
447
448
449 fixedRows, fixedColumns int
450
451
452
453 rowsSelectable, columnsSelectable bool
454
455
456 selectedRow, selectedColumn int
457
458
459
460 clampToSelection bool
461
462
463
464
465 wrapHorizontally, wrapVertically bool
466
467
468
469 rowOffset, columnOffset int
470
471
472 trackEnd bool
473
474
475 visibleRows int
476
477
478 visibleColumnIndices []int
479
480
481
482 visibleColumnWidths []int
483
484
485
486 selectedStyle tcell.Style
487
488
489
490
491 selected func(row, column int)
492
493
494
495
496 selectionChanged func(row, column int)
497
498
499
500 done func(key tcell.Key)
501 }
502
503
504 func NewTable() *Table {
505 t := &Table{
506 Box: NewBox(),
507 bordersColor: Styles.GraphicsColor,
508 separator: ' ',
509 }
510 t.SetContent(nil)
511 return t
512 }
513
514
515
516
517
518
519
520
521 func (t *Table) SetContent(content TableContent) *Table {
522 if content != nil {
523 t.content = content
524 } else {
525 t.content = &tableDefaultContent{
526 lastColumn: -1,
527 }
528 }
529 return t
530 }
531
532
533 func (t *Table) Clear() *Table {
534 t.content.Clear()
535 return t
536 }
537
538
539
540 func (t *Table) SetBorders(show bool) *Table {
541 t.borders = show
542 return t
543 }
544
545
546 func (t *Table) SetBordersColor(color tcell.Color) *Table {
547 t.bordersColor = color
548 return t
549 }
550
551
552
553
554
555
556
557
558 func (t *Table) SetSelectedStyle(style tcell.Style) *Table {
559 t.selectedStyle = style
560 return t
561 }
562
563
564
565
566
567
568
569
570 func (t *Table) SetSeparator(separator rune) *Table {
571 t.separator = separator
572 return t
573 }
574
575
576
577
578 func (t *Table) SetFixed(rows, columns int) *Table {
579 t.fixedRows, t.fixedColumns = rows, columns
580 return t
581 }
582
583
584
585
586
587
588
589
590 func (t *Table) SetSelectable(rows, columns bool) *Table {
591 t.rowsSelectable, t.columnsSelectable = rows, columns
592 return t
593 }
594
595
596
597 func (t *Table) GetSelectable() (rows, columns bool) {
598 return t.rowsSelectable, t.columnsSelectable
599 }
600
601
602
603
604 func (t *Table) GetSelection() (row, column int) {
605 return t.selectedRow, t.selectedColumn
606 }
607
608
609
610
611
612
613 func (t *Table) Select(row, column int) *Table {
614 t.selectedRow, t.selectedColumn = row, column
615 t.clampToSelection = true
616 if t.selectionChanged != nil {
617 t.selectionChanged(row, column)
618 }
619 return t
620 }
621
622
623
624
625
626
627 func (t *Table) SetOffset(row, column int) *Table {
628 t.rowOffset, t.columnOffset = row, column
629 t.trackEnd = false
630 return t
631 }
632
633
634
635 func (t *Table) GetOffset() (row, column int) {
636 return t.rowOffset, t.columnOffset
637 }
638
639
640
641
642
643
644
645
646
647
648 func (t *Table) SetEvaluateAllRows(all bool) *Table {
649 t.evaluateAllRows = all
650 return t
651 }
652
653
654
655
656
657 func (t *Table) SetSelectedFunc(handler func(row, column int)) *Table {
658 t.selected = handler
659 return t
660 }
661
662
663
664
665
666 func (t *Table) SetSelectionChangedFunc(handler func(row, column int)) *Table {
667 t.selectionChanged = handler
668 return t
669 }
670
671
672
673
674
675 func (t *Table) SetDoneFunc(handler func(key tcell.Key)) *Table {
676 t.done = handler
677 return t
678 }
679
680
681
682
683
684
685
686
687
688
689
690 func (t *Table) SetCell(row, column int, cell *TableCell) *Table {
691 t.content.SetCell(row, column, cell)
692 return t
693 }
694
695
696 func (t *Table) SetCellSimple(row, column int, text string) *Table {
697 t.SetCell(row, column, NewTableCell(text))
698 return t
699 }
700
701
702
703
704
705
706 func (t *Table) GetCell(row, column int) *TableCell {
707 cell := t.content.GetCell(row, column)
708 if cell == nil {
709 cell = &TableCell{}
710 }
711 return cell
712 }
713
714
715
716 func (t *Table) RemoveRow(row int) *Table {
717 t.content.RemoveRow(row)
718 return t
719 }
720
721
722
723 func (t *Table) RemoveColumn(column int) *Table {
724 t.content.RemoveColumn(column)
725 return t
726 }
727
728
729
730
731 func (t *Table) InsertRow(row int) *Table {
732 t.content.InsertRow(row)
733 return t
734 }
735
736
737
738
739
740 func (t *Table) InsertColumn(column int) *Table {
741 t.content.InsertColumn(column)
742 return t
743 }
744
745
746 func (t *Table) GetRowCount() int {
747 return t.content.GetRowCount()
748 }
749
750
751 func (t *Table) GetColumnCount() int {
752 return t.content.GetColumnCount()
753 }
754
755
756
757
758
759 func (t *Table) cellAt(x, y int) (row, column int) {
760 rectX, rectY, _, _ := t.GetInnerRect()
761
762
763 if t.borders {
764 row = (y - rectY - 1) / 2
765 } else {
766 row = y - rectY
767 }
768
769
770 if row >= 0 {
771 if row >= t.fixedRows {
772 row += t.rowOffset
773 }
774 if row >= t.content.GetRowCount() {
775 row = -1
776 }
777 }
778
779
780 column = -1
781 if x >= rectX {
782 columnX := rectX
783 if t.borders {
784 columnX++
785 }
786 for index, width := range t.visibleColumnWidths {
787 columnX += width + 1
788 if x < columnX {
789 column = t.visibleColumnIndices[index]
790 break
791 }
792 }
793 }
794
795 return
796 }
797
798
799
800
801 func (t *Table) ScrollToBeginning() *Table {
802 t.trackEnd = false
803 t.columnOffset = 0
804 t.rowOffset = 0
805 return t
806 }
807
808
809
810
811
812 func (t *Table) ScrollToEnd() *Table {
813 t.trackEnd = true
814 t.columnOffset = 0
815 t.rowOffset = t.content.GetRowCount()
816 return t
817 }
818
819
820
821
822
823
824
825
826
827
828
829 func (t *Table) SetWrapSelection(vertical, horizontal bool) *Table {
830 t.wrapHorizontally = horizontal
831 t.wrapVertically = vertical
832 return t
833 }
834
835
836 func (t *Table) Draw(screen tcell.Screen) {
837 t.Box.DrawForSubclass(screen, t)
838
839
840 _, totalHeight := screen.Size()
841 x, y, width, height := t.GetInnerRect()
842 netWidth := width
843 if t.borders {
844 t.visibleRows = height / 2
845 netWidth -= 2
846 } else {
847 t.visibleRows = height
848 }
849
850
851 rowCount, columnCount := t.content.GetRowCount(), t.content.GetColumnCount()
852 if t.rowsSelectable || t.columnsSelectable {
853 if t.selectedColumn < 0 {
854 t.selectedColumn = 0
855 }
856 if t.selectedRow < 0 {
857 t.selectedRow = 0
858 }
859 for t.selectedRow < rowCount {
860 cell := t.content.GetCell(t.selectedRow, t.selectedColumn)
861 if cell != nil && !cell.NotSelectable {
862 break
863 }
864 t.selectedColumn++
865 if t.selectedColumn > columnCount-1 {
866 t.selectedColumn = 0
867 t.selectedRow++
868 }
869 }
870 }
871
872
873 defer func() {
874 t.clampToSelection = false
875 }()
876 if t.clampToSelection && t.rowsSelectable {
877 if t.selectedRow >= t.fixedRows && t.selectedRow < t.fixedRows+t.rowOffset {
878 t.rowOffset = t.selectedRow - t.fixedRows
879 t.trackEnd = false
880 }
881 if t.borders {
882 if t.selectedRow+1-t.rowOffset >= height/2 {
883 t.rowOffset = t.selectedRow + 1 - height/2
884 t.trackEnd = false
885 }
886 } else {
887 if t.selectedRow+1-t.rowOffset >= height {
888 t.rowOffset = t.selectedRow + 1 - height
889 t.trackEnd = false
890 }
891 }
892 }
893 if t.rowOffset < 0 {
894 t.rowOffset = 0
895 }
896 if t.borders {
897 if rowCount-t.rowOffset < height/2 {
898 t.trackEnd = true
899 }
900 } else {
901 if rowCount-t.rowOffset < height {
902 t.trackEnd = true
903 }
904 }
905 if t.trackEnd {
906 if t.borders {
907 t.rowOffset = rowCount - height/2
908 } else {
909 t.rowOffset = rowCount - height
910 }
911 }
912 if t.rowOffset < 0 {
913 t.rowOffset = 0
914 }
915
916
917 if t.columnOffset >= columnCount-t.fixedColumns {
918 t.columnOffset = columnCount - t.fixedColumns - 1
919 }
920 if t.columnOffset < 0 {
921 t.columnOffset = 0
922 }
923
924
925 var (
926 rows, allRows []int
927 tableHeight int
928 )
929 rowStep := 1
930 if t.borders {
931 rowStep = 2
932 }
933 if t.evaluateAllRows {
934 allRows = make([]int, rowCount)
935 for row := 0; row < rowCount; row++ {
936 allRows[row] = row
937 }
938 }
939 indexRow := func(row int) bool {
940 if tableHeight >= height {
941 return false
942 }
943 rows = append(rows, row)
944 tableHeight += rowStep
945 return true
946 }
947 for row := 0; row < t.fixedRows && row < rowCount; row++ {
948 if !indexRow(row) {
949 break
950 }
951 }
952 for row := t.fixedRows + t.rowOffset; row < rowCount; row++ {
953 if !indexRow(row) {
954 break
955 }
956 }
957
958
959
960 var (
961 tableWidth, expansionTotal int
962 columns, widths, expansions []int
963 )
964 includesSelection := !t.clampToSelection || !t.columnsSelectable
965
966
967
968 indexColumn := func(column int) bool {
969 if netWidth == 0 || tableWidth >= netWidth {
970 return true
971 }
972
973 var maxWidth, expansion int
974 evaluationRows := rows
975 if t.evaluateAllRows {
976 evaluationRows = allRows
977 }
978 for _, row := range evaluationRows {
979 if cell := t.content.GetCell(row, column); cell != nil {
980 _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false)
981 if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth {
982 cellWidth = cell.MaxWidth
983 }
984 if cellWidth > maxWidth {
985 maxWidth = cellWidth
986 }
987 if cell.Expansion > expansion {
988 expansion = cell.Expansion
989 }
990 }
991 }
992 clampedMaxWidth := maxWidth
993 if tableWidth+maxWidth > netWidth {
994 clampedMaxWidth = netWidth - tableWidth
995 }
996 columns = append(columns, column)
997 widths = append(widths, clampedMaxWidth)
998 expansions = append(expansions, expansion)
999 tableWidth += clampedMaxWidth + 1
1000 expansionTotal += expansion
1001 if t.columnsSelectable && t.clampToSelection && column == t.selectedColumn {
1002
1003 includesSelection = clampedMaxWidth == maxWidth
1004 }
1005
1006 return false
1007 }
1008
1009
1010
1011
1012 indexColumns := func(start, maxEnd int) int {
1013 if start == maxEnd {
1014 return -1
1015 }
1016
1017 if start < maxEnd {
1018
1019 for column := start; column < maxEnd; column++ {
1020 if indexColumn(column) {
1021 return column
1022 }
1023 }
1024 return -1
1025 }
1026
1027
1028 startLen := len(columns)
1029 defer func() {
1030
1031 for i, j := startLen, len(columns)-1; i < j; i, j = i+1, j-1 {
1032 columns[i], columns[j] = columns[j], columns[i]
1033 widths[i], widths[j] = widths[j], widths[i]
1034 expansions[i], expansions[j] = expansions[j], expansions[i]
1035 }
1036 }()
1037 for column := start; column >= maxEnd; column-- {
1038 if indexColumn(column) {
1039 return column
1040 }
1041 }
1042 return -1
1043 }
1044
1045
1046 var fixedTableWidth, fixedExpansionTotal int
1047 resetColumns := func() {
1048 tableWidth = fixedTableWidth
1049 expansionTotal = fixedExpansionTotal
1050 columns = columns[:t.fixedColumns]
1051 widths = widths[:t.fixedColumns]
1052 expansions = expansions[:t.fixedColumns]
1053 }
1054
1055
1056 if indexColumns(0, t.fixedColumns) < 0 {
1057 fixedTableWidth = tableWidth
1058 fixedExpansionTotal = expansionTotal
1059
1060
1061 if column := indexColumns(t.fixedColumns+t.columnOffset, columnCount); !includesSelection || column < 0 && t.columnOffset > 0 {
1062
1063 if !includesSelection {
1064
1065 resetColumns()
1066 if t.selectedColumn <= t.fixedColumns+t.columnOffset {
1067
1068 t.columnOffset = t.selectedColumn - t.fixedColumns
1069 indexColumns(t.fixedColumns+t.columnOffset, columnCount)
1070 } else {
1071
1072 if column := indexColumns(t.selectedColumn, t.fixedColumns); column >= 0 {
1073 t.columnOffset = column + 1 - t.fixedColumns
1074 } else {
1075 t.columnOffset = 0
1076 }
1077 }
1078 } else if tableWidth < netWidth {
1079
1080 resetColumns()
1081 if column := indexColumns(columnCount-1, t.fixedColumns); column >= 0 {
1082 t.columnOffset = column + 1 - t.fixedColumns
1083 } else {
1084 t.columnOffset = 0
1085 }
1086 }
1087 }
1088 }
1089
1090
1091 if tableWidth < netWidth {
1092 toDistribute := netWidth - tableWidth
1093 for index, expansion := range expansions {
1094 if expansionTotal <= 0 {
1095 break
1096 }
1097 expWidth := toDistribute * expansion / expansionTotal
1098 widths[index] += expWidth
1099 toDistribute -= expWidth
1100 expansionTotal -= expansion
1101 }
1102 }
1103
1104
1105 borderStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.bordersColor)
1106 drawBorder := func(colX, rowY int, ch rune) {
1107 screen.SetContent(x+colX, y+rowY, ch, nil, borderStyle)
1108 }
1109
1110
1111 var columnX int
1112 if t.borders {
1113 columnX++
1114 }
1115 for columnIndex, column := range columns {
1116 columnWidth := widths[columnIndex]
1117 for rowY, row := range rows {
1118 if t.borders {
1119
1120 rowY *= 2
1121 for pos := 0; pos < columnWidth && columnX+pos < width; pos++ {
1122 drawBorder(columnX+pos, rowY, Borders.Horizontal)
1123 }
1124 ch := Borders.Cross
1125 if row == 0 {
1126 if column == 0 {
1127 ch = Borders.TopLeft
1128 } else {
1129 ch = Borders.TopT
1130 }
1131 } else if column == 0 {
1132 ch = Borders.LeftT
1133 }
1134 drawBorder(columnX-1, rowY, ch)
1135 rowY++
1136 if rowY >= height || y+rowY >= totalHeight {
1137 break
1138 }
1139 drawBorder(columnX-1, rowY, Borders.Vertical)
1140 } else if columnIndex < len(columns)-1 {
1141
1142 drawBorder(columnX+columnWidth, rowY, t.separator)
1143 }
1144
1145
1146 cell := t.content.GetCell(row, column)
1147 if cell == nil {
1148 continue
1149 }
1150
1151
1152 finalWidth := columnWidth
1153 if columnX+columnWidth >= width {
1154 finalWidth = width - columnX
1155 }
1156 cell.x, cell.y, cell.width = x+columnX, y+rowY, finalWidth
1157 _, printed, _, _ := printWithStyle(screen, cell.Text, x+columnX, y+rowY, 0, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color).Attributes(cell.Attributes), true)
1158 if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 {
1159 _, _, style, _ := screen.GetContent(x+columnX+finalWidth-1, y+rowY)
1160 printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+finalWidth-1, y+rowY, 0, 1, AlignLeft, style, false)
1161 }
1162 }
1163
1164
1165 if rowY := 2 * len(rows); t.borders && rowY > 0 && rowY < height {
1166 for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ {
1167 drawBorder(columnX+pos, rowY, Borders.Horizontal)
1168 }
1169 ch := Borders.Cross
1170 if rows[len(rows)-1] == rowCount-1 {
1171 if column == 0 {
1172 ch = Borders.BottomLeft
1173 } else {
1174 ch = Borders.BottomT
1175 }
1176 } else if column == 0 {
1177 ch = Borders.BottomLeft
1178 }
1179 drawBorder(columnX-1, rowY, ch)
1180 }
1181
1182 columnX += columnWidth + 1
1183 }
1184
1185
1186 columnX--
1187 if t.borders && len(rows) > 0 && len(columns) > 0 && columnX < width {
1188 lastColumn := columns[len(columns)-1] == columnCount-1
1189 for rowY := range rows {
1190 rowY *= 2
1191 if rowY+1 < height {
1192 drawBorder(columnX, rowY+1, Borders.Vertical)
1193 }
1194 ch := Borders.Cross
1195 if rowY == 0 {
1196 if lastColumn {
1197 ch = Borders.TopRight
1198 } else {
1199 ch = Borders.TopT
1200 }
1201 } else if lastColumn {
1202 ch = Borders.RightT
1203 }
1204 drawBorder(columnX, rowY, ch)
1205 }
1206 if rowY := 2 * len(rows); rowY < height {
1207 ch := Borders.BottomT
1208 if lastColumn {
1209 ch = Borders.BottomRight
1210 }
1211 drawBorder(columnX, rowY, ch)
1212 }
1213 }
1214
1215
1216
1217
1218
1219
1220
1221 colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, backgroundTransparent, textTransparent bool, attr tcell.AttrMask, invert bool) {
1222 for by := 0; by < h && fromY+by < y+height; by++ {
1223 for bx := 0; bx < w && fromX+bx < x+width; bx++ {
1224 m, c, style, _ := screen.GetContent(fromX+bx, fromY+by)
1225 fg, bg, a := style.Decompose()
1226 if invert {
1227 style = style.Background(textColor).Foreground(backgroundColor)
1228 } else {
1229 if !backgroundTransparent {
1230 bg = backgroundColor
1231 }
1232 if !textTransparent {
1233 fg = textColor
1234 }
1235 if attr != 0 {
1236 a = attr
1237 }
1238 style = style.Background(bg).Foreground(fg).Attributes(a)
1239 }
1240 screen.SetContent(fromX+bx, fromY+by, m, c, style)
1241 }
1242 }
1243 }
1244
1245
1246
1247 type cellInfo struct {
1248 x, y, w, h int
1249 cell *TableCell
1250 selected bool
1251 }
1252 cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo)
1253 var backgroundColors []tcell.Color
1254 for rowY, row := range rows {
1255 columnX := 0
1256 rowSelected := t.rowsSelectable && !t.columnsSelectable && row == t.selectedRow
1257 for columnIndex, column := range columns {
1258 columnWidth := widths[columnIndex]
1259 cell := t.content.GetCell(row, column)
1260 if cell == nil {
1261 continue
1262 }
1263 bx, by, bw, bh := x+columnX, y+rowY, columnWidth+1, 1
1264 if t.borders {
1265 by = y + rowY*2
1266 bw++
1267 bh = 3
1268 }
1269 columnSelected := t.columnsSelectable && !t.rowsSelectable && column == t.selectedColumn
1270 cellSelected := !cell.NotSelectable && (columnSelected || rowSelected || t.rowsSelectable && t.columnsSelectable && column == t.selectedColumn && row == t.selectedRow)
1271 entries, ok := cellsByBackgroundColor[cell.BackgroundColor]
1272 cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &cellInfo{
1273 x: bx,
1274 y: by,
1275 w: bw,
1276 h: bh,
1277 cell: cell,
1278 selected: cellSelected,
1279 })
1280 if !ok {
1281 backgroundColors = append(backgroundColors, cell.BackgroundColor)
1282 }
1283 columnX += columnWidth + 1
1284 }
1285 }
1286 sort.Slice(backgroundColors, func(i int, j int) bool {
1287
1288 r, g, b := backgroundColors[i].RGB()
1289 c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
1290 _, _, li := c.Hcl()
1291 r, g, b = backgroundColors[j].RGB()
1292 c = colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
1293 _, _, lj := c.Hcl()
1294 return li < lj
1295 })
1296 selFg, selBg, selAttr := t.selectedStyle.Decompose()
1297 for _, bgColor := range backgroundColors {
1298 entries := cellsByBackgroundColor[bgColor]
1299 for _, info := range entries {
1300 if info.selected {
1301 if t.selectedStyle != (tcell.Style{}) {
1302 defer colorBackground(info.x, info.y, info.w, info.h, selBg, selFg, false, false, selAttr, false)
1303 } else {
1304 defer colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, false, false, 0, true)
1305 }
1306 } else {
1307 colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, info.cell.Transparent, true, 0, false)
1308 }
1309 }
1310 }
1311
1312
1313 t.visibleColumnIndices, t.visibleColumnWidths = columns, widths
1314 }
1315
1316
1317 func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
1318 return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
1319 key := event.Key()
1320
1321 if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) ||
1322 key == tcell.KeyEscape ||
1323 key == tcell.KeyTab ||
1324 key == tcell.KeyBacktab {
1325 if t.done != nil {
1326 t.done(key)
1327 }
1328 return
1329 }
1330
1331
1332 previouslySelectedRow, previouslySelectedColumn := t.selectedRow, t.selectedColumn
1333 lastColumn := t.content.GetColumnCount() - 1
1334 rowCount := t.content.GetRowCount()
1335 if rowCount == 0 {
1336 return
1337 }
1338 var (
1339
1340
1341 forward = func(finalRow, finalColumn int) bool {
1342 row, column := t.selectedRow, t.selectedColumn
1343 for {
1344
1345 cell := t.content.GetCell(row, column)
1346 if cell != nil && !cell.NotSelectable {
1347 t.selectedRow, t.selectedColumn = row, column
1348 return true
1349 }
1350
1351
1352 if row == finalRow && column == finalColumn {
1353 return false
1354 }
1355
1356
1357 column++
1358 if column > lastColumn {
1359 column = 0
1360 row++
1361 if row >= rowCount {
1362 row = 0
1363 }
1364 }
1365 }
1366 }
1367
1368
1369
1370 backwards = func(finalRow, finalColumn int) bool {
1371 row, column := t.selectedRow, t.selectedColumn
1372 for {
1373
1374 cell := t.content.GetCell(row, column)
1375 if cell != nil && !cell.NotSelectable {
1376 t.selectedRow, t.selectedColumn = row, column
1377 return true
1378 }
1379
1380
1381 if row == finalRow && column == finalColumn {
1382 return false
1383 }
1384
1385
1386 column--
1387 if column < 0 {
1388 column = lastColumn
1389 row--
1390 if row < 0 {
1391 row = rowCount - 1
1392 }
1393 }
1394 }
1395 }
1396
1397 home = func() {
1398 if t.rowsSelectable {
1399 t.selectedRow = 0
1400 t.selectedColumn = 0
1401 forward(rowCount-1, lastColumn)
1402 t.clampToSelection = true
1403 } else {
1404 t.trackEnd = false
1405 t.rowOffset = 0
1406 t.columnOffset = 0
1407 }
1408 }
1409
1410 end = func() {
1411 if t.rowsSelectable {
1412 t.selectedRow = rowCount - 1
1413 t.selectedColumn = lastColumn
1414 backwards(0, 0)
1415 t.clampToSelection = true
1416 } else {
1417 t.trackEnd = true
1418 t.columnOffset = 0
1419 }
1420 }
1421
1422 down = func() {
1423 if t.rowsSelectable {
1424 row, column := t.selectedRow, t.selectedColumn
1425 t.selectedRow++
1426 if t.selectedRow >= rowCount {
1427 if t.wrapVertically {
1428 t.selectedRow = 0
1429 } else {
1430 t.selectedRow = rowCount - 1
1431 }
1432 }
1433 finalRow, finalColumn := rowCount-1, lastColumn
1434 if t.wrapVertically {
1435 finalRow = row
1436 finalColumn = column
1437 }
1438 if !forward(finalRow, finalColumn) {
1439 backwards(row, column)
1440 }
1441 t.clampToSelection = true
1442 } else {
1443 t.rowOffset++
1444 }
1445 }
1446
1447 up = func() {
1448 if t.rowsSelectable {
1449 row, column := t.selectedRow, t.selectedColumn
1450 t.selectedRow--
1451 if t.selectedRow < 0 {
1452 if t.wrapVertically {
1453 t.selectedRow = rowCount - 1
1454 } else {
1455 t.selectedRow = 0
1456 }
1457 }
1458 finalRow, finalColumn := 0, 0
1459 if t.wrapVertically {
1460 finalRow = row
1461 finalColumn = column
1462 }
1463 if !backwards(finalRow, finalColumn) {
1464 forward(row, column)
1465 }
1466 t.clampToSelection = true
1467 } else {
1468 t.trackEnd = false
1469 t.rowOffset--
1470 }
1471 }
1472
1473 left = func() {
1474 if t.columnsSelectable {
1475 row, column := t.selectedRow, t.selectedColumn
1476 t.selectedColumn--
1477 if t.selectedColumn < 0 {
1478 if t.wrapHorizontally {
1479 t.selectedColumn = lastColumn
1480 t.selectedRow--
1481 if t.selectedRow < 0 {
1482 if t.wrapVertically {
1483 t.selectedRow = rowCount - 1
1484 } else {
1485 t.selectedColumn = 0
1486 t.selectedRow = 0
1487 }
1488 }
1489 } else {
1490 t.selectedColumn = 0
1491 }
1492 }
1493 finalRow, finalColumn := row, column
1494 if !t.wrapHorizontally {
1495 finalColumn = 0
1496 } else if !t.wrapVertically {
1497 finalRow = 0
1498 finalColumn = 0
1499 }
1500 if !backwards(finalRow, finalColumn) {
1501 forward(row, column)
1502 }
1503 t.clampToSelection = true
1504 } else {
1505 t.columnOffset--
1506 }
1507 }
1508
1509 right = func() {
1510 if t.columnsSelectable {
1511 row, column := t.selectedRow, t.selectedColumn
1512 t.selectedColumn++
1513 if t.selectedColumn > lastColumn {
1514 if t.wrapHorizontally {
1515 t.selectedColumn = 0
1516 t.selectedRow++
1517 if t.selectedRow >= rowCount {
1518 if t.wrapVertically {
1519 t.selectedRow = 0
1520 } else {
1521 t.selectedColumn = lastColumn
1522 t.selectedRow = rowCount - 1
1523 }
1524 }
1525 } else {
1526 t.selectedColumn = lastColumn
1527 }
1528 }
1529 finalRow, finalColumn := row, column
1530 if !t.wrapHorizontally {
1531 finalColumn = lastColumn
1532 } else if !t.wrapVertically {
1533 finalRow = rowCount - 1
1534 finalColumn = lastColumn
1535 }
1536 if !forward(finalRow, finalColumn) {
1537 backwards(row, column)
1538 }
1539 t.clampToSelection = true
1540 } else {
1541 t.columnOffset++
1542 }
1543 }
1544
1545 pageDown = func() {
1546 offsetAmount := t.visibleRows - t.fixedRows
1547 if offsetAmount < 0 {
1548 offsetAmount = 0
1549 }
1550 if t.rowsSelectable {
1551 row, column := t.selectedRow, t.selectedColumn
1552 t.selectedRow += offsetAmount
1553 if t.selectedRow >= rowCount {
1554 t.selectedRow = rowCount - 1
1555 }
1556 finalRow, finalColumn := rowCount-1, lastColumn
1557 if !forward(finalRow, finalColumn) {
1558 backwards(row, column)
1559 }
1560 t.clampToSelection = true
1561 } else {
1562 t.rowOffset += offsetAmount
1563 }
1564 }
1565
1566 pageUp = func() {
1567 offsetAmount := t.visibleRows - t.fixedRows
1568 if offsetAmount < 0 {
1569 offsetAmount = 0
1570 }
1571 if t.rowsSelectable {
1572 row, column := t.selectedRow, t.selectedColumn
1573 t.selectedRow -= offsetAmount
1574 if t.selectedRow < 0 {
1575 t.selectedRow = 0
1576 }
1577 finalRow, finalColumn := 0, 0
1578 if !backwards(finalRow, finalColumn) {
1579 forward(row, column)
1580 }
1581 t.clampToSelection = true
1582 } else {
1583 t.trackEnd = false
1584 t.rowOffset -= offsetAmount
1585 }
1586 }
1587 )
1588
1589 switch key {
1590 case tcell.KeyRune:
1591 switch event.Rune() {
1592 case 'g':
1593 home()
1594 case 'G':
1595 end()
1596 case 'j':
1597 down()
1598 case 'k':
1599 up()
1600 case 'h':
1601 left()
1602 case 'l':
1603 right()
1604 }
1605 case tcell.KeyHome:
1606 home()
1607 case tcell.KeyEnd:
1608 end()
1609 case tcell.KeyUp:
1610 up()
1611 case tcell.KeyDown:
1612 down()
1613 case tcell.KeyLeft:
1614 left()
1615 case tcell.KeyRight:
1616 right()
1617 case tcell.KeyPgDn, tcell.KeyCtrlF:
1618 pageDown()
1619 case tcell.KeyPgUp, tcell.KeyCtrlB:
1620 pageUp()
1621 case tcell.KeyEnter:
1622 if (t.rowsSelectable || t.columnsSelectable) && t.selected != nil {
1623 t.selected(t.selectedRow, t.selectedColumn)
1624 }
1625 }
1626
1627
1628 if t.selectionChanged != nil &&
1629 (t.rowsSelectable && previouslySelectedRow != t.selectedRow ||
1630 t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) {
1631 t.selectionChanged(t.selectedRow, t.selectedColumn)
1632 }
1633 })
1634 }
1635
1636
1637 func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
1638 return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
1639 x, y := event.Position()
1640 if !t.InRect(x, y) {
1641 return false, nil
1642 }
1643
1644 switch action {
1645 case MouseLeftDown:
1646 setFocus(t)
1647 consumed = true
1648 case MouseLeftClick:
1649 selectEvent := true
1650 row, column := t.cellAt(x, y)
1651 cell := t.content.GetCell(row, column)
1652 if cell != nil && cell.Clicked != nil {
1653 if noSelect := cell.Clicked(); noSelect {
1654 selectEvent = false
1655 }
1656 }
1657 if selectEvent && (t.rowsSelectable || t.columnsSelectable) {
1658 t.Select(row, column)
1659 }
1660 consumed = true
1661 case MouseScrollUp:
1662 t.trackEnd = false
1663 t.rowOffset--
1664 consumed = true
1665 case MouseScrollDown:
1666 t.rowOffset++
1667 consumed = true
1668 }
1669
1670 return
1671 })
1672 }
1673
View as plain text