zntg/ui/button.go
Sander Schobers 0f03760e66 Added Resize & SetIcon to Renderer.
Refactored Size (on Renderer) to return geom.Point instead of geom.PointF32.
Refactored Width and Height (on Texture) to return int instead of float32.

Refactored texture dimensions to be represented by ints instead of float32s.
2020-12-13 07:40:19 +01:00

233 lines
5.0 KiB
Go

package ui
import (
"image/color"
"opslag.de/schobers/geom"
)
type Button struct {
ControlBase
DisabledColor color.Color
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 := float32(icon.Width()), float32(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 {
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) disabledColor(p *Palette) color.Color {
if b.DisabledColor != nil {
return b.DisabledColor
}
return p.Disabled
}
func (b *Button) fillColor(p *Palette) color.Color {
if b.Type == ButtonTypeIcon {
return nil
}
if b.Disabled {
if b.Background != nil {
return b.disabledColor(p)
}
switch b.Type {
case ButtonTypeContained:
return b.disabledColor(p)
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 b.disabledColor(p)
}
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).ToF32(), 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)
}
}