203 lines
6.6 KiB
Go
203 lines
6.6 KiB
Go
package driver
|
|
|
|
import (
|
|
"math"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/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.Position, 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.Position, 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.Position, 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.Position, 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:
|
|
if cache.IsRendered(co) || requireVisible {
|
|
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, pos, parent)
|
|
}
|
|
return cancelled
|
|
}
|