forked from loov/lensm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsymbollist.go
134 lines (116 loc) · 3.22 KB
/
symbollist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"fmt"
"regexp"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
)
// SymbolSelectionList lists symbols for filtering and selection.
type SymbolSelectionList struct {
Symbols []*Symbol
Filter widget.Editor
FilterError string
Filtered []*Symbol
Selected string
SelectedSymbol *Symbol
List SelectList
}
// NewSymbolList creates a new symbol list with the specified theme.
func NewSymbolList(theme *material.Theme) *SymbolSelectionList {
ui := &SymbolSelectionList{}
ui.Filter.SingleLine = true
ui.List = NewVerticalSelectList(unit.Dp(theme.TextSize) + 4)
return ui
}
// SelectIndex selects the specified item.
func (ui *SymbolSelectionList) SelectIndex(index int) {
if !InRange(index, len(ui.Filtered)) {
return
}
ui.List.Selected = index
ui.Selected = ui.Filtered[index].Name
ui.SelectedSymbol = ui.Filtered[index]
}
// SetSymbols updates the symbol list.
func (ui *SymbolSelectionList) SetSymbols(symbols []*Symbol) {
ui.Symbols = symbols
ui.updateFiltered()
}
// SetFilter sets the filter.
func (ui *SymbolSelectionList) SetFilter(filter string) {
ui.Filter.SetText(filter)
ui.updateFiltered()
}
// updateFiltered updates the filtered list from the unfiltered content.
func (ui *SymbolSelectionList) updateFiltered() {
defer func() {
ui.List.Selected = -1
for i, sym := range ui.Filtered {
if sym.Name == ui.Selected {
ui.List.Selected = i
ui.SelectedSymbol = sym
// TODO, maybe scroll into view?
break
}
}
}()
rx, err := regexp.Compile("(?i)" + ui.Filter.Text())
ui.FilterError = ""
if err != nil {
ui.FilterError = err.Error()
return
}
ui.Filtered = ui.Filtered[:0]
for _, sym := range ui.Symbols {
if rx.MatchString(sym.Name) {
ui.Filtered = append(ui.Filtered, sym)
}
}
}
// Layout draws the symbol selection list.
func (ui *SymbolSelectionList) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions {
paint.FillShape(gtx.Ops, secondaryBackground, clip.Rect{Max: gtx.Constraints.Min}.Op())
defer func() {
ui.SelectIndex(ui.List.Selected)
changed := false
for _, ev := range ui.Filter.Events() {
if _, ok := ev.(widget.ChangeEvent); ok {
changed = true
}
}
if changed {
ui.updateFiltered()
op.InvalidateOp{}.Add(gtx.Ops)
}
}()
return layout.Flex{
Axis: layout.Vertical,
}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return FocusBorder(th, ui.Filter.Focused()).Layout(gtx,
material.Editor(th, &ui.Filter, "Filter (regexp)").Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if ui.FilterError == "" {
return layout.Dimensions{}
}
return material.Body1(th, ui.FilterError).Layout(gtx)
}),
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return ui.List.Layout(th, gtx, len(ui.Filtered),
StringListItem(th, &ui.List, func(index int) string {
return ui.Filtered[index].Name
}))
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
body := material.Body1(th, fmt.Sprintf("%d / %d", len(ui.Filtered), len(ui.Symbols)))
body.TextSize *= 0.8
return layout.Center.Layout(gtx, body.Layout)
}),
)
}