|
|
@@ -0,0 +1,278 @@
|
|
|
+// Copyright 2016 The G3N Authors. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+package gui
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/g3n/engine/math32"
|
|
|
+ "github.com/g3n/engine/texture"
|
|
|
+ "github.com/g3n/engine/window"
|
|
|
+)
|
|
|
+
|
|
|
+type ImageButton struct {
|
|
|
+ *Panel // Embedded Panel
|
|
|
+ label *Label // Label panel
|
|
|
+ iconLabel bool // True if icon
|
|
|
+ image *Image // pointer to button image (may be nil)
|
|
|
+ styles *ImageButtonStyles // pointer to current button styles
|
|
|
+ mouseOver bool // true if mouse is over button
|
|
|
+ pressed bool // true if button is pressed
|
|
|
+ stateImages [ButtonDisabled+1]*texture.Texture2D // array of images for each button state
|
|
|
+}
|
|
|
+
|
|
|
+type ButtonState int
|
|
|
+
|
|
|
+const (
|
|
|
+ ButtonNormal ButtonState = iota
|
|
|
+ ButtonOver
|
|
|
+ //ButtonFocus
|
|
|
+ ButtonPressed
|
|
|
+ ButtonDisabled
|
|
|
+)
|
|
|
+
|
|
|
+// ImageButton style
|
|
|
+type ImageButtonStyle struct {
|
|
|
+ Border BorderSizes
|
|
|
+ Paddings BorderSizes
|
|
|
+ BorderColor math32.Color4
|
|
|
+ BgColor math32.Color4
|
|
|
+ FgColor math32.Color
|
|
|
+}
|
|
|
+
|
|
|
+// All ImageButton styles
|
|
|
+type ImageButtonStyles struct {
|
|
|
+ Normal ImageButtonStyle
|
|
|
+ Over ImageButtonStyle
|
|
|
+ Focus ImageButtonStyle
|
|
|
+ Pressed ImageButtonStyle
|
|
|
+ Disabled ImageButtonStyle
|
|
|
+}
|
|
|
+
|
|
|
+// NewImageButton creates and returns a pointer to a new ImageButton widget
|
|
|
+// with the specified image.
|
|
|
+func NewImageButton(normalImgPath string) (*ImageButton, error) {
|
|
|
+
|
|
|
+ b := new(ImageButton)
|
|
|
+ b.styles = &StyleDefault.ImageButton
|
|
|
+
|
|
|
+ tex, err := texture.NewTexture2DFromImage(normalImgPath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ b.stateImages[ButtonNormal] = tex
|
|
|
+ b.image = NewImageFromTex(tex)
|
|
|
+
|
|
|
+ // Initializes the button panel
|
|
|
+ b.Panel = NewPanel(0, 0)
|
|
|
+ b.Panel.SetContentSize(b.image.Width(), b.image.Height())
|
|
|
+ b.Panel.SetBorders(5, 5, 5, 5)
|
|
|
+ b.Panel.Add(b.image)
|
|
|
+
|
|
|
+ // Subscribe to panel events
|
|
|
+ b.Panel.Subscribe(OnKeyDown, b.onKey)
|
|
|
+ b.Panel.Subscribe(OnKeyUp, b.onKey)
|
|
|
+ b.Panel.Subscribe(OnMouseUp, b.onMouse)
|
|
|
+ b.Panel.Subscribe(OnMouseDown, b.onMouse)
|
|
|
+ b.Panel.Subscribe(OnCursor, b.onCursor)
|
|
|
+ b.Panel.Subscribe(OnCursorEnter, b.onCursor)
|
|
|
+ b.Panel.Subscribe(OnCursorLeave, b.onCursor)
|
|
|
+ b.Panel.Subscribe(OnEnable, func(name string, ev interface{}) { b.update() })
|
|
|
+ b.Panel.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
|
|
|
+
|
|
|
+ b.recalc()
|
|
|
+ b.update()
|
|
|
+ return b, nil
|
|
|
+}
|
|
|
+
|
|
|
+// SetText sets the text of the label
|
|
|
+func (b *ImageButton) SetText(text string) {
|
|
|
+
|
|
|
+ if b.iconLabel && b.label != nil {
|
|
|
+ b.Panel.Remove(b.label)
|
|
|
+ b.label.Dispose()
|
|
|
+ b.label = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ b.iconLabel = false
|
|
|
+ if b.label == nil {
|
|
|
+ // Create label
|
|
|
+ b.label = NewLabel(text)
|
|
|
+ b.Panel.Add(b.label)
|
|
|
+ } else {
|
|
|
+ b.label.SetText(text)
|
|
|
+ }
|
|
|
+ b.recalc()
|
|
|
+}
|
|
|
+
|
|
|
+// SetIcon sets the icon
|
|
|
+func (b *ImageButton) SetIcon(icode int) {
|
|
|
+
|
|
|
+ if b.iconLabel == false && b.label != nil {
|
|
|
+ b.Panel.Remove(b.label)
|
|
|
+ b.label.Dispose()
|
|
|
+ b.label = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ b.iconLabel = true
|
|
|
+ if b.label == nil {
|
|
|
+ // Create icon
|
|
|
+ b.label = NewIconLabel(string(icode))
|
|
|
+ b.Panel.Add(b.label)
|
|
|
+ } else {
|
|
|
+ b.label.SetText(string(icode))
|
|
|
+ }
|
|
|
+ b.recalc()
|
|
|
+}
|
|
|
+
|
|
|
+// SetFontSize sets the font size of the label/icon
|
|
|
+func (b *ImageButton) SetFontSize(size float64) {
|
|
|
+
|
|
|
+ if b.label != nil {
|
|
|
+ b.label.SetFontSize(size)
|
|
|
+ b.recalc()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// SetImage sets the button left image from the specified filename
|
|
|
+// If there is currently a selected icon, it is removed
|
|
|
+func (b *ImageButton) SetImage(state ButtonState, imgfile string) error {
|
|
|
+
|
|
|
+ tex, err := texture.NewTexture2DFromImage(imgfile)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if b.stateImages[state] != nil {
|
|
|
+ b.stateImages[state].Dispose()
|
|
|
+ }
|
|
|
+ b.stateImages[state] = tex
|
|
|
+
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Dispose releases resources used by this widget
|
|
|
+func (b *ImageButton) Dispose() {
|
|
|
+ b.Panel.Dispose()
|
|
|
+ for _, tex := range b.stateImages {
|
|
|
+ if tex != nil {
|
|
|
+ tex.Dispose()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// SetStyles set the button styles overriding the default style
|
|
|
+func (b *ImageButton) SetStyles(bs *ImageButtonStyles) {
|
|
|
+
|
|
|
+ b.styles = bs
|
|
|
+ b.update()
|
|
|
+}
|
|
|
+
|
|
|
+// onCursor process subscribed cursor events
|
|
|
+func (b *ImageButton) onCursor(evname string, ev interface{}) {
|
|
|
+
|
|
|
+ switch evname {
|
|
|
+ case OnCursorEnter:
|
|
|
+ b.mouseOver = true
|
|
|
+ b.update()
|
|
|
+ case OnCursorLeave:
|
|
|
+ b.pressed = false
|
|
|
+ b.mouseOver = false
|
|
|
+ b.update()
|
|
|
+ }
|
|
|
+ b.root.StopPropagation(StopAll)
|
|
|
+}
|
|
|
+
|
|
|
+// onMouseEvent process subscribed mouse events
|
|
|
+func (b *ImageButton) onMouse(evname string, ev interface{}) {
|
|
|
+
|
|
|
+ switch evname {
|
|
|
+ case OnMouseDown:
|
|
|
+ b.root.SetKeyFocus(b)
|
|
|
+ b.pressed = true
|
|
|
+ b.update()
|
|
|
+ b.Dispatch(OnClick, nil)
|
|
|
+ case OnMouseUp:
|
|
|
+ b.pressed = false
|
|
|
+ b.update()
|
|
|
+ default:
|
|
|
+ return
|
|
|
+ }
|
|
|
+ b.root.StopPropagation(StopAll)
|
|
|
+}
|
|
|
+
|
|
|
+// onKey processes subscribed key events
|
|
|
+func (b *ImageButton) onKey(evname string, ev interface{}) {
|
|
|
+
|
|
|
+ kev := ev.(*window.KeyEvent)
|
|
|
+ if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
|
|
|
+ b.pressed = true
|
|
|
+ b.update()
|
|
|
+ b.Dispatch(OnClick, nil)
|
|
|
+ b.root.StopPropagation(Stop3D)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if evname == OnKeyUp && kev.Keycode == window.KeyEnter {
|
|
|
+ b.pressed = false
|
|
|
+ b.update()
|
|
|
+ b.root.StopPropagation(Stop3D)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// update updates the button visual state
|
|
|
+func (b *ImageButton) update() {
|
|
|
+
|
|
|
+ if !b.Enabled() {
|
|
|
+ if b.stateImages[ButtonDisabled] != nil {
|
|
|
+ b.image.SetTexture(b.stateImages[ButtonDisabled])
|
|
|
+ }
|
|
|
+ b.applyStyle(&b.styles.Disabled)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if b.pressed {
|
|
|
+ if b.stateImages[ButtonPressed] != nil {
|
|
|
+ b.image.SetTexture(b.stateImages[ButtonPressed])
|
|
|
+ }
|
|
|
+ b.applyStyle(&b.styles.Pressed)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if b.mouseOver {
|
|
|
+ if b.stateImages[ButtonOver] != nil {
|
|
|
+ b.image.SetTexture(b.stateImages[ButtonOver])
|
|
|
+ }
|
|
|
+ b.applyStyle(&b.styles.Over)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ b.image.SetTexture(b.stateImages[ButtonNormal])
|
|
|
+ b.applyStyle(&b.styles.Normal)
|
|
|
+}
|
|
|
+
|
|
|
+// applyStyle applies the specified button style
|
|
|
+func (b *ImageButton) applyStyle(bs *ImageButtonStyle) {
|
|
|
+
|
|
|
+ b.SetBordersColor4(&bs.BorderColor)
|
|
|
+ b.SetBordersFrom(&bs.Border)
|
|
|
+ b.SetPaddingsFrom(&bs.Paddings)
|
|
|
+ b.SetColor4(&bs.BgColor)
|
|
|
+ if b.label != nil {
|
|
|
+ b.label.SetColor(&bs.FgColor)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// recalc recalculates all dimensions and position from inside out
|
|
|
+func (b *ImageButton) recalc() {
|
|
|
+
|
|
|
+ // Only need to recal if there's a label preset
|
|
|
+ if b.label != nil {
|
|
|
+ width := b.Panel.ContentWidth()
|
|
|
+ height := b.Panel.ContentHeight()
|
|
|
+
|
|
|
+ x := (width - b.label.Width()) / 2
|
|
|
+ y := (height - b.label.Height()) / 2
|
|
|
+
|
|
|
+ b.label.SetPosition(x, y)
|
|
|
+ }
|
|
|
+}
|