zntg/ui/debug.go

162 lines
3.8 KiB
Go

package ui
import (
"image/color"
"reflect"
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg"
)
const DefaultDebugOverlay = "ui-default-debug"
func controlChildren(control Control) []Control {
container, ok := control.(*ContainerBase)
if ok {
return container.Children
}
proxy, ok := control.(*Proxy)
if ok {
return []Control{proxy.Content}
}
return controlChildrenReflect(reflect.ValueOf(control))
}
func controlChildrenReflect(control reflect.Value) []Control {
switch control.Kind() {
case reflect.Interface:
return controlChildrenReflect(control.Elem())
case reflect.Ptr:
return controlChildrenReflect(control.Elem())
}
if reflect.TypeOf(ContainerBase{}) == control.Type() {
container := control.Interface().(ContainerBase)
return container.Children
}
if reflect.TypeOf(Proxy{}) == control.Type() {
proxy := control.Interface().(Proxy)
return []Control{proxy.Content}
}
if control.NumField() == 0 {
return nil
}
field := control.Type().Field(0)
if !field.Anonymous {
return nil
}
return controlChildrenReflect(control.Field(0))
}
func controlName(control Control) string {
typ := reflect.TypeOf(control)
return typ.Elem().Name()
}
type debugOverlay struct {
ControlBase
root Control
hoverNodes *controlNode
boundsColor color.Color
textColor color.Color
textShadowColor color.Color
}
func NewDebugOverlay(root Control) *debugOverlay {
return &debugOverlay{
root: root,
boundsColor: zntg.MustHexColor(`#00FF003F`),
textColor: zntg.MustHexColor(`#FFFFFF3F`),
textShadowColor: zntg.MustHexColor(`#0000003F`),
}
}
func (o *debugOverlay) renderControl(ctx Context, control Control) {
renderer := ctx.Renderer()
currentColor := zntg.MustHexColor("#FF0000")
parentColor := zntg.MustHexColor("#0000FF")
var maxY float32
var renderHoverNode func(pos geom.PointF32, node *controlNode)
renderHoverNode = func(pos geom.PointF32, node *controlNode) {
if node == nil {
return
}
nameTexture, err := ctx.Fonts().TextTexture("debug", color.White, node.Name)
if err != nil {
return
}
defer nameTexture.Destroy()
nameTextureWidth := float32(nameTexture.Width())
nameTextureHeight := float32(nameTexture.Height())
renderer.FillRectangle(pos.RectRel2D(nameTextureWidth, nameTextureHeight), color.Black)
renderer.DrawTexturePoint(nameTexture, pos)
childPos := pos.Add2D(nameTextureWidth+ctx.Style().Dimensions.Margin, 0)
if len(node.Children) == 0 {
renderer.Rectangle(node.Parent.Bounds, parentColor, 1)
renderer.Rectangle(node.Bounds, currentColor, 1)
}
for _, child := range node.Children {
if childPos.Y == maxY {
childPos.Y = maxY + nameTextureHeight
}
renderHoverNode(childPos, child)
maxY = childPos.Y
childPos.Y += nameTextureHeight + ctx.Style().Dimensions.Margin
}
}
renderHoverNode(geom.PtF32(4, 4), o.hoverNodes)
children := controlChildren(control)
for _, child := range children {
o.renderControl(ctx, child)
}
}
func createHoverNodes(hover geom.PointF32, control Control) *controlNode {
bounds := control.Bounds()
if !hover.In(bounds) {
return nil
}
node := &controlNode{Name: controlName(control), Bounds: bounds}
for _, child := range controlChildren(control) {
childNode := createHoverNodes(hover, child)
if childNode == nil {
continue
}
childNode.Parent = node
node.Children = append(node.Children, childNode)
}
return node
}
func (o *debugOverlay) Handle(ctx Context, e Event) bool {
switch e := e.(type) {
case *MouseMoveEvent:
o.hoverNodes = createHoverNodes(e.Pos(), o.root)
case *MouseLeaveEvent:
o.hoverNodes = nil
}
return false
}
func (o *debugOverlay) Hidden() {}
func (o *debugOverlay) Render(ctx Context) {
o.renderControl(ctx, o.root)
}
func (o *debugOverlay) Shown() {}
type controlNode struct {
Name string
Bounds geom.RectangleF32
Parent *controlNode
Children []*controlNode
}