package ui import ( "image/color" "github.com/nfnt/resize" "opslag.de/schobers/geom" ) type Button struct { ControlBase Type ButtonType Text string Icon Image IconScale float32 scale float32 icon Image } type ButtonType int const ( ButtonTypeContained ButtonType = iota ButtonTypeIcon ButtonTypeOutlined ButtonTypeText ) func BuildButton(text string, fn func(b *Button)) *Button { return BuildIconButton(nil, text, fn) } func BuildIconButton(i Image, text string, fn func(b *Button)) *Button { var b = &Button{Text: text, Icon: i} if fn != nil { fn(b) } return b } func (b *Button) desiredSize(ctx Context) geom.PointF32 { var pad = ctx.Style().Dimensions.TextPadding var font = ctx.Renderer().Font(b.FontName(ctx)) var w, h float32 = 0, font.Height() if len(b.Text) != 0 { w += pad + font.WidthOf(b.Text) } if b.Icon != nil && b.Icon.Height() > 0 { iconW := b.Icon.Width() * h / b.Icon.Height() if b.IconScale > 0 { iconW *= b.IconScale } w += pad + iconW } if w == 0 { return geom.ZeroPtF32 } return geom.PtF32(w+pad, pad+h+pad) } func (b *Button) DesiredSize(ctx Context) geom.PointF32 { return b.desiredSize(ctx) } func (b *Button) Handle(ctx Context, e Event) { b.ControlBase.Handle(ctx, e) if b.over { ctx.Renderer().SetMouseCursor(MouseCursorPointer) } } func (b *Button) fillColor(p *Palette) color.Color { if b.Background != nil { return b.Background } if b.over { switch b.Type { case ButtonTypeContained: return p.PrimaryHighlight case ButtonTypeIcon: default: return p.PrimaryLight } } switch b.Type { case ButtonTypeContained: return p.Primary case ButtonTypeIcon: default: } return nil } func (b *Button) scaledIcon(ctx Context, height float32) Image { scale := height / b.Icon.Height() if b.IconScale > 0 { scale *= b.IconScale } if geom.IsNaN32(scale) { return nil } if scale == 1 { b.scale = 1 return b.Icon } if b.icon == nil || b.scale != scale { if b.icon != nil { b.icon.Destroy() b.icon = nil } w := uint(b.Icon.Width() * scale) if w == 0 { return nil } im := resize.Resize(w, 0, b.Icon.Image(), resize.Bilinear) icon, err := ctx.Renderer().CreateImage(im) if err != nil { return nil } b.icon = icon b.scale = scale } return b.icon } func (b *Button) textColor(p *Palette) color.Color { if b.Font.Color != nil { return b.Font.Color } switch b.Type { case ButtonTypeContained: return p.TextOnPrimary case ButtonTypeIcon: if b.over { 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) pos := bounds.Min pos.X += pad if b.Icon != nil && b.Icon.Height() > 0 { icon := b.scaledIcon(ctx, bounds.Dy()) if icon != nil { ctx.Renderer().DrawImageOptions(icon, geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-icon.Height())), DrawOptions{Tint: textColor}) pos.X += icon.Width() + pad } } if len(b.Text) != 0 { var fontName = b.FontName(ctx) var font = ctx.Renderer().Font(fontName) ctx.Renderer().Text(geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-font.Height())), fontName, textColor, b.Text) } if b.Type == ButtonTypeOutlined { ctx.Renderer().Rectangle(b.bounds, palette.TextDisabled, 1) } }