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 }