package ui import ( "image/color" "opslag.de/schobers/geom" ) type ClickFn func(ctx Context, c Control, pos geom.PointF32, btn MouseButton) type DragEndFn func(ctx Context, c Control, start, end geom.PointF32) type DragMoveFn func(ctx Context, c Control, start, move geom.PointF32) type DragStartFn func(ctx Context, c Control, start geom.PointF32) type MouseEnterFn func(ctx Context, c Control) type MouseLeaveFn func(ctx Context, c Control) var _ Control = &ControlBase{} type ControlBase struct { bounds geom.RectangleF32 offset geom.PointF32 parent Control dragStart *geom.PointF32 over bool pressed bool onClick ClickFn onDragEnd DragEndFn onDragMove DragMoveFn onDragStart DragStartFn Background color.Color Font FontStyle TextAlignment HorizontalAlignment } func (c *ControlBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) { c.bounds = bounds c.offset = offset c.parent = parent } func (c *ControlBase) Bounds() geom.RectangleF32 { return c.bounds } func (c *ControlBase) DesiredSize(Context) geom.PointF32 { return geom.ZeroPtF32 } func (c *ControlBase) Handle(ctx Context, e Event) { var over = func(e MouseEvent) bool { pos := e.Pos() if !c.IsInBounds(pos) { return false } parent := c.Parent() for parent != nil { if !parent.IsInBounds(pos) { return false } parent = parent.Parent() } return true } switch e := e.(type) { case *MouseMoveEvent: c.over = over(e.MouseEvent) if c.pressed { if c.dragStart == nil { var start = c.ToControlPosition(e.Pos()) c.dragStart = &start if c.onDragStart != nil { c.onDragStart(ctx, c, start) } } else { var start = *c.dragStart var move = c.ToControlPosition(e.Pos()) if c.onDragMove != nil { c.onDragMove(ctx, c, start, move) } } } case *MouseButtonDownEvent: c.over = over(e.MouseEvent) if c.over && e.Button == MouseButtonLeft { c.pressed = true } case *MouseButtonUpEvent: if e.Button == MouseButtonLeft { if c.dragStart != nil { var start = *c.dragStart var end = c.ToControlPosition(e.Pos()) c.dragStart = nil if c.onDragEnd != nil { c.onDragEnd(ctx, c, start, end) } } if c.pressed { if c.onClick != nil { c.onClick(ctx, c, c.ToControlPosition(e.Pos()), e.Button) } } c.pressed = false } } } func (c *ControlBase) FontColor(ctx Context) color.Color { var text = c.Font.Color if text == nil { text = ctx.Style().Palette.Text } return text } func (c *ControlBase) FontName(ctx Context) string { var name = c.Font.Name if len(name) == 0 { name = ctx.Style().Fonts.Default } return name } func (c *ControlBase) IsInBounds(p geom.PointF32) bool { bounds := c.bounds if bounds.Min.X < 0 { bounds.Min.X = 0 } if bounds.Min.Y < 0 { bounds.Min.Y = 0 } return c.ToControlPosition(p).In(c.bounds) } func (c *ControlBase) IsOver() bool { return c.over } func (c *ControlBase) Parent() Control { return c.parent } func (c *ControlBase) IsPressed() bool { return c.pressed } func (c *ControlBase) Offset() geom.PointF32 { return c.offset } func (c *ControlBase) OnClick(fn ClickFn) { c.onClick = fn } func (c *ControlBase) OnDragStart(fn DragStartFn) { c.onDragStart = fn } func (c *ControlBase) OnDragMove(fn DragMoveFn) { c.onDragMove = fn } func (c *ControlBase) OnDragEnd(fn DragEndFn) { c.onDragEnd = fn } func (c *ControlBase) Render(Context) {} func (c *ControlBase) RenderBackground(ctx Context) { if c.Background != nil { ctx.Renderer().FillRectangle(c.bounds, c.Background) } } func (c *ControlBase) RenderOutline(ctx Context) { style := ctx.Style() width := style.Dimensions.OutlineWidth color := style.Palette.Primary if c.Font.Color != nil { color = c.Font.Color } ctx.Renderer().Rectangle(c.bounds.Inset(.5*width), color, width) } func (c *ControlBase) ToControlPosition(p geom.PointF32) geom.PointF32 { return p.Sub(c.offset) }