-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.go
200 lines (179 loc) · 6.53 KB
/
util.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
198
199
200
package driver
import (
"math"
"github.com/Jaeel/fyne"
"github.com/Jaeel/fyne/internal/cache"
)
// AbsolutePositionForObject returns the absolute position of an object in a set of object trees.
// If the object is not part of any of the trees, the position (0,0) is returned.
func AbsolutePositionForObject(object fyne.CanvasObject, trees []fyne.CanvasObject) fyne.Position {
var pos fyne.Position
findPos := func(o fyne.CanvasObject, p fyne.Position, _ fyne.Position, _ fyne.Size) bool {
if o == object {
pos = p
return true
}
return false
}
for _, tree := range trees {
if WalkVisibleObjectTree(tree, findPos, nil) {
break
}
}
return pos
}
// FindObjectAtPositionMatching is used to find an object in a canvas at the specified position.
// The matches function determines of the type of object that is found at this position is of a suitable type.
// The various canvas roots and overlays that can be searched are also passed in.
func FindObjectAtPositionMatching(mouse fyne.Position, matches func(object fyne.CanvasObject) bool, overlay fyne.CanvasObject, roots ...fyne.CanvasObject) (fyne.CanvasObject, fyne.Position, int) {
var found fyne.CanvasObject
var foundPos fyne.Position
findFunc := func(walked fyne.CanvasObject, pos fyne.Position, clipPos fyne.Position, clipSize fyne.Size) bool {
if !walked.Visible() {
return false
}
if mouse.X < clipPos.X || mouse.Y < clipPos.Y {
return false
}
if mouse.X >= clipPos.X+clipSize.Width || mouse.Y >= clipPos.Y+clipSize.Height {
return false
}
if mouse.X < pos.X || mouse.Y < pos.Y {
return false
}
if mouse.X >= pos.X+walked.Size().Width || mouse.Y >= pos.Y+walked.Size().Height {
return false
}
if matches(walked) {
found = walked
foundPos = fyne.NewPos(mouse.X-pos.X, mouse.Y-pos.Y)
}
return false
}
layer := 0
if overlay != nil {
WalkVisibleObjectTree(overlay, findFunc, nil)
} else {
for _, root := range roots {
layer++
if root == nil {
continue
}
WalkVisibleObjectTree(root, findFunc, nil)
if found != nil {
break
}
}
}
return found, foundPos, layer
}
// ReverseWalkVisibleObjectTree will walk an object tree in reverse order for all visible objects
// executing the passed functions following the following rules:
// - beforeChildren is called for the start obj before traversing its children
// - the obj's children are traversed by calling walkObjects on each of the visible items
// - afterChildren is called for the obj after traversing the obj's children
// The walk can be aborted by returning true in one of the functions:
// - if beforeChildren returns true, further traversing is stopped immediately, the after function
// will not be called for the obj where the walk stopped, however, it will be called for all its
// parents
func ReverseWalkVisibleObjectTree(
obj fyne.CanvasObject,
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
) bool {
clipSize := fyne.NewSize(math.MaxInt32, math.MaxInt32)
return walkObjectTree(obj, true, nil, fyne.NewPos(0, 0), fyne.NewPos(0, 0), clipSize, beforeChildren, afterChildren, true)
}
// WalkCompleteObjectTree will walk an object tree for all objects (ignoring visible state) executing the passed
// functions following the following rules:
// - beforeChildren is called for the start obj before traversing its children
// - the obj's children are traversed by calling walkObjects on each of the items
// - afterChildren is called for the obj after traversing the obj's children
// The walk can be aborted by returning true in one of the functions:
// - if beforeChildren returns true, further traversing is stopped immediately, the after function
// will not be called for the obj where the walk stopped, however, it will be called for all its
// parents
func WalkCompleteObjectTree(
obj fyne.CanvasObject,
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
) bool {
clipSize := fyne.NewSize(math.MaxInt32, math.MaxInt32)
return walkObjectTree(obj, false, nil, fyne.NewPos(0, 0), fyne.NewPos(0, 0), clipSize, beforeChildren, afterChildren, false)
}
// WalkVisibleObjectTree will walk an object tree for all visible objects executing the passed functions following
// the following rules:
// - beforeChildren is called for the start obj before traversing its children
// - the obj's children are traversed by calling walkObjects on each of the visible items
// - afterChildren is called for the obj after traversing the obj's children
// The walk can be aborted by returning true in one of the functions:
// - if beforeChildren returns true, further traversing is stopped immediately, the after function
// will not be called for the obj where the walk stopped, however, it will be called for all its
// parents
func WalkVisibleObjectTree(
obj fyne.CanvasObject,
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
) bool {
clipSize := fyne.NewSize(math.MaxInt32, math.MaxInt32)
return walkObjectTree(obj, false, nil, fyne.NewPos(0, 0), fyne.NewPos(0, 0), clipSize, beforeChildren, afterChildren, true)
}
func walkObjectTree(
obj fyne.CanvasObject,
reverse bool,
parent fyne.CanvasObject,
offset, clipPos fyne.Position,
clipSize fyne.Size,
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
requireVisible bool,
) bool {
if obj == nil {
return false
}
if requireVisible && !obj.Visible() {
return false
}
pos := obj.Position().Add(offset)
var children []fyne.CanvasObject
switch co := obj.(type) {
case *fyne.Container:
children = co.Objects
case fyne.Widget:
children = cache.Renderer(co).Objects()
}
if _, ok := obj.(fyne.Scrollable); ok {
clipPos = pos
clipSize = obj.Size()
}
if beforeChildren != nil {
if beforeChildren(obj, pos, clipPos, clipSize) {
return true
}
}
cancelled := false
followChild := func(child fyne.CanvasObject) bool {
if walkObjectTree(child, reverse, obj, pos, clipPos, clipSize, beforeChildren, afterChildren, requireVisible) {
cancelled = true
return true
}
return false
}
if reverse {
for i := len(children) - 1; i >= 0; i-- {
if followChild(children[i]) {
break
}
}
} else {
for _, child := range children {
if followChild(child) {
break
}
}
}
if afterChildren != nil {
afterChildren(obj, parent)
}
return cancelled
}