/
util.go
137 lines (115 loc) · 3.27 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
package wm
import (
"math"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/test"
)
// FindObjectAtPixelPositionMatching looks for objects in the given canvas that are under pixel
// position at x, y. Objects must match the criteria in 'fn' and the first match will be returned.
func FindObjectAtPixelPositionMatching(x, y int, c fyne.Canvas, fn func(fyne.CanvasObject) bool) fyne.CanvasObject {
if c == nil {
return nil
}
pos := fyne.NewPos(unscaleInt(c, x), unscaleInt(c, y))
obj, _ := findObjectAtPositionMatching(pos, fn, c.Content())
return obj
}
// some internal Fyne functions that we find very useful as we have to drive the frame UI
func findObjectAtPositionMatching(mouse fyne.Position, matches func(object fyne.CanvasObject) bool,
overlay fyne.CanvasObject, roots ...fyne.CanvasObject) (fyne.CanvasObject, fyne.Position) {
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
}
if overlay != nil {
walkVisibleObjectTree(overlay, findFunc, nil)
} else {
for _, root := range roots {
if root == nil {
continue
}
walkVisibleObjectTree(root, findFunc, nil)
if found != nil {
break
}
}
}
return found, foundPos
}
func unscaleInt(c fyne.Canvas, v int) float32 {
switch c.Scale() {
case 1.0:
return float32(v)
default:
return float32(v) / c.Scale()
}
}
func walkObjectTree(
obj fyne.CanvasObject,
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 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 = test.WidgetRenderer(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
for _, child := range children {
if walkObjectTree(child, obj, pos, clipPos, clipSize, beforeChildren, afterChildren, requireVisible) {
cancelled = true
break
}
}
if afterChildren != nil {
afterChildren(obj, parent)
}
return cancelled
}
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, nil, fyne.NewPos(0, 0), fyne.NewPos(0, 0), clipSize, beforeChildren, afterChildren, true)
}