/
list.go
197 lines (172 loc) · 5.95 KB
/
list.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// SPDX-License-Identifier: Unlicense OR MIT
/* github.com/utopiagio/utopia/list.go */
package utopia
import (
"image"
"math"
layout_gio "github.com/utopiagio/gio/layout"
widget_gio "github.com/utopiagio/gio/widget"
"github.com/utopiagio/utopia/metrics"
)
// ListStyle configures the presentation of a layout_gio.List with a scrollbar.
type GoListBoxObj struct {
GioObject
GioWidget
Scrollbar GoScrollbar
AnchorStrategy
state *widget_gio.List
}
// List constructs a ListStyle using the provided theme and state.
func GoListBox(parent GoObject) *GoListBoxObj {
theme := GoApp.Theme()
lightFg := theme.ColorFg.NRGBA()
lightFg.A = 150
darkFg := lightFg
darkFg.A = 200
state := &widget_gio.List{}
object := GioObject{parent, parent.ParentWindow(), []GoObject{}, GetSizePolicy(FixedWidth, FixedHeight)}
tagCounter++
widget := GioWidget{
GoBorder: GoBorder{BorderNone, Color_Black, 0, 0, 0},
GoMargin: GoMargin{0,0,0,0},
GoPadding: GoPadding{0,0,0,0},
GoSize: GoSize{0, 0, 300, 300, 16777215, 16777215, 300, 300},
Visible: true,
//target: nil,
tag: tagCounter,
}
scrollbar := GoScrollbar{
Scrollbar: &state.Scrollbar,
Track: ScrollTrackStyle{
MajorPadding: 2,
MinorPadding: 2,
},
Indicator: ScrollIndicatorStyle{
MajorMinLen: 8,
MinorWidth: 6,
CornerRadius: 3,
Color: lightFg,
HoverColor: darkFg,
},
}
hListBox := &GoListBoxObj {
GioObject: object,
GioWidget: widget,
Scrollbar: scrollbar,
//ScrollbarStyle: Scrollbar(theme, state.Scrollbar),
AnchorStrategy: Occupy,
state: state,
}
parent.AddControl(hListBox)
return hListBox
}
func (ob *GoListBoxObj) ObjectType() (string) {
return "GoListBoxObj"
}
func (ob *GoListBoxObj) Widget() (*GioWidget) {
return &ob.GioWidget
}
func (ob *GoListBoxObj) SetLayoutMode(mode GoLayoutDirection) {
ob.state.Axis = layout_gio.Axis(uint8(mode))
}
/*func (ob *GoListBoxObj) SetSizePolicy(horiz GoSizeType, vert GoSizeType) {
ob.SetSizePolicy(GetSizePolicy(horiz, vert))
}*/
func (ob *GoListBoxObj) Draw(gtx layout_gio.Context) (dims layout_gio.Dimensions) {
gtx.Constraints = ob.SetConstraints(ob.Size(), gtx.Constraints)
dims = layout_gio.Dimensions {Size: image.Point{X: 0, Y: 0,}}
if ob.Visible {
dims = ob.GoMargin.Layout(gtx, func(gtx C) D {
return ob.GoBorder.Layout(gtx, func(gtx C) D {
return ob.GoPadding.Layout(gtx, func(gtx C) D {
return ob.Layout(gtx, len(ob.Controls), func(gtx C, i int) D {
return ob.Controls[i].Draw(gtx)
})
})
})
})
ob.dims = dims
ob.AbsWidth = metrics.PxToDp(GoDpr, dims.Size.X)
ob.AbsHeight = metrics.PxToDp(GoDpr, dims.Size.Y)
}
return dims
}
// layout the list and its scrollbar.
func (ob *GoListBoxObj) Layout(gtx layout_gio.Context, length int, w layout_gio.ListElement) layout_gio.Dimensions {
originalConstraints := gtx.Constraints
// Determine how much space the scrollbar occupies.
barWidth := gtx.Dp(ob.Scrollbar.Width())
if ob.AnchorStrategy == Occupy {
// Reserve space for the scrollbar using the gtx constraints.
max := ob.state.Axis.Convert(gtx.Constraints.Max)
min := ob.state.Axis.Convert(gtx.Constraints.Min)
max.Y -= barWidth
if max.Y < 0 {
max.Y = 0
}
min.Y -= barWidth
if min.Y < 0 {
min.Y = 0
}
gtx.Constraints.Max = ob.state.Axis.Convert(max)
gtx.Constraints.Min = ob.state.Axis.Convert(min)
}
listDims := ob.state.List.Layout(gtx, length, w)
gtx.Constraints = originalConstraints
// Draw the scrollbar.
anchoring := layout_gio.E
if ob.state.Axis == layout_gio.Horizontal {
anchoring = layout_gio.S
}
majorAxisSize := ob.state.Axis.Convert(listDims.Size).X
start, end := ob.fromListPosition(ob.state.Position, length, majorAxisSize)
// layout.Direction respects the minimum, so ensure that the
// scrollbar will be drawn on the correct edge even if the provided
// layout.Context had a zero minimum constraint.
gtx.Constraints.Min = listDims.Size
if ob.AnchorStrategy == Occupy {
min := ob.state.Axis.Convert(gtx.Constraints.Min)
min.Y += barWidth
gtx.Constraints.Min = ob.state.Axis.Convert(min)
}
anchoring.Layout(gtx, func(gtx layout_gio.Context) layout_gio.Dimensions {
return ob.Scrollbar.Layout(gtx, ob.state.Axis, start, end)
})
if delta := ob.state.ScrollDistance(); delta != 0 {
// Handle any changes to the list position as a result of user interaction
// with the scrollbar.
ob.state.List.Position.Offset += int(math.Round(float64(float32(ob.state.Position.Length) * delta)))
// Ensure that the list pays attention to the Offset field when the scrollbar drag
// is started while the bar is at the end of the list. Without this, the scrollbar
// cannot be dragged away from the end.
ob.state.List.Position.BeforeEnd = true
}
if ob.AnchorStrategy == Occupy {
// Increase the width to account for the space occupied by the scrollbar.
cross := ob.state.Axis.Convert(listDims.Size)
cross.Y += barWidth
listDims.Size = ob.state.Axis.Convert(cross)
}
//log.Println("listDims :", listDims)
if ob.MinWidth > listDims.Size.X {
listDims.Size.X = ob.MinWidth
}
return listDims
}
// fromListPosition converts a layout.Position into two floats representing
// the location of the viewport on the underlying content. It needs to know
// the number of elements in the list and the major-axis size of the list
// in order to do this. The returned values will be in the range [0,1], and
// start will be less than or equal to end.
func (ob *GoListBoxObj) fromListPosition(lp layout_gio.Position, elements int, majorAxisSize int) (start, end float32) {
// Approximate the size of the scrollable content.
lengthPx := float32(lp.Length)
meanElementHeight := lengthPx / float32(elements)
// Determine how much of the content is visible.
listOffsetF := float32(lp.Offset)
visiblePx := float32(majorAxisSize)
visibleFraction := visiblePx / lengthPx
// Compute the location of the beginning of the viewport.
viewportStart := (float32(lp.First)*meanElementHeight + listOffsetF) / lengthPx
return viewportStart, clamp1(viewportStart + visibleFraction)
}