package ui import ( "image/color" "opslag.de/schobers/geom" ) type Button struct { ControlBase HoverColor color.Color Icon string // optional: icon to display in front of the text. IconHeight float32 // overrides the height of the icon (overrides auto-scaling when text is provided). Text string Type ButtonType clicked ControlClickedEvents } type ButtonType int const ( ButtonTypeContained ButtonType = iota ButtonTypeIcon ButtonTypeOutlined ButtonTypeText ) func BuildButton(text string, fn func(b *Button)) *Button { return BuildIconButton("", text, fn) } func BuildIconButton(icon, text string, fn func(b *Button)) *Button { var b = &Button{Text: text, Icon: icon} if fn != nil { fn(b) } return b } func (b *Button) desiredSize(ctx Context) geom.PointF32 { var pad = ctx.Style().Dimensions.TextPadding var font = ctx.Fonts().Font(b.FontName(ctx)) var w, h float32 = 0, font.Height() icon, iconW, iconH := b.icon(ctx) if len(b.Text) == 0 { if icon != nil && iconH > 0 { w = pad + iconW + pad h = iconH } } else { w += pad + font.WidthOf(b.Text) + pad if icon != nil && iconH > 0 { if b.IconHeight == 0 { iconW = iconW * h / iconH // iconH = h } w += iconW + pad } } if w == 0 { return geom.ZeroPtF32 } return geom.PtF32(w, pad+h+pad) } func (b *Button) icon(ctx Context) (Texture, float32, float32) { if b.Icon == "" { return nil, 0, 0 } icon := ctx.Textures().Texture(b.Icon) iconW, iconH := icon.Width(), icon.Height() if b.IconHeight != 0 { iconW = b.IconHeight * iconW / iconH iconH = b.IconHeight } return icon, iconW, iconH } func (b *Button) ButtonClicked() ControlClickedEventHandler { return &b.clicked } func (b *Button) DesiredSize(ctx Context, _ geom.PointF32) geom.PointF32 { return b.desiredSize(ctx) } func (b *Button) Handle(ctx Context, e Event) bool { result := b.ControlBase.HandleNotify(ctx, e, b) if b.over { if b.Disabled { ctx.Renderer().SetMouseCursor(MouseCursorNotAllowed) return true } ctx.Renderer().SetMouseCursor(MouseCursorPointer) } return result } func (b *Button) Notify(ctx Context, state interface{}) bool { switch state.(type) { case ControlClickedArgs: if !b.Disabled { if b.clicked.Notify(ctx, state) { return true } } } return b.ControlBase.Notify(ctx, state) } func (b *Button) fillColor(p *Palette) color.Color { if b.Type == ButtonTypeIcon { return nil } if b.Disabled { if b.Background != nil { return p.Disabled } switch b.Type { case ButtonTypeContained: return p.Disabled default: return nil } } if b.Background != nil { if b.over && b.HoverColor != nil { return b.HoverColor } return b.Background } if b.over { if b.HoverColor != nil { return b.HoverColor } switch b.Type { case ButtonTypeContained: return p.PrimaryLight default: return p.PrimaryHighlight } } switch b.Type { case ButtonTypeContained: return p.Primary } return nil } func (b *Button) textColor(p *Palette) color.Color { if b.Disabled { if b.Background != nil { return p.TextOnDisabled } switch b.Type { case ButtonTypeContained: return p.TextOnDisabled } return p.Disabled } if b.Font.Color != nil { return b.Font.Color } switch b.Type { case ButtonTypeContained: return p.TextOnPrimary case ButtonTypeIcon: if b.over { if b.HoverColor != nil { return b.HoverColor } return p.Primary } return p.Text default: return p.Primary } } func (b *Button) Render(ctx Context) { var style = ctx.Style() var palette = style.Palette textColor := b.textColor(palette) fillColor := b.fillColor(palette) if fillColor != nil { ctx.Renderer().FillRectangle(b.bounds, fillColor) } size := b.desiredSize(ctx) bounds := b.bounds deltaX, deltaY := bounds.Dx()-size.X, bounds.Dy()-size.Y bounds.Min.X += .5 * deltaX bounds.Min.Y += .5 * deltaY var pad = style.Dimensions.TextPadding bounds = bounds.Inset(pad) boundsH := bounds.Dy() pos := bounds.Min icon, iconW, iconH := b.icon(ctx) var iconOffsetY float32 if icon != nil && iconH > 0 { if b.Text != "" { if b.IconHeight == 0 { iconH = boundsH scaled, _ := ctx.Textures().ScaledHeight(icon, iconH) // try to pre-scale scaled if scaled != nil { // let the renderer scale icon = scaled } _, iconW = ScaleToHeight(SizeOfTexture(icon), iconH) } iconOffsetY = .5 * (boundsH - iconH) } ctx.Renderer().DrawTextureOptions(icon, geom.RectRelF32(pos.X, pos.Y+iconOffsetY, iconW, iconH), DrawOptions{Tint: textColor}) pos.X += iconW + pad } if len(b.Text) != 0 { fontName := b.FontName(ctx) font := ctx.Fonts().Font(fontName) ctx.Renderer().Text(font, geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), textColor, b.Text) } if b.Type == ButtonTypeOutlined { b.RenderOutlineDefault(ctx, textColor) } }