button.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. import (
  6. "github.com/g3n/engine/window"
  7. )
  8. /***************************************
  9. Button Panel
  10. +-------------------------------+
  11. | Image/Icon Label |
  12. | +----------+ +----------+ |
  13. | | | | | |
  14. | | | | | |
  15. | +----------+ +----------+ |
  16. +-------------------------------+
  17. ****************************************/
  18. // Button represents a button GUI element
  19. type Button struct {
  20. *Panel // Embedded Panel
  21. Label *Label // Label panel
  22. image *Image // pointer to button image (may be nil)
  23. icon *Label // pointer to button icon (may be nil
  24. styles *ButtonStyles // pointer to current button styles
  25. mouseOver bool // true if mouse is over button
  26. pressed bool // true if button is pressed
  27. }
  28. // Button style
  29. type ButtonStyle BasicStyle
  30. // All Button styles
  31. type ButtonStyles struct {
  32. Normal ButtonStyle
  33. Over ButtonStyle
  34. Focus ButtonStyle
  35. Pressed ButtonStyle
  36. Disabled ButtonStyle
  37. }
  38. // NewButton creates and returns a pointer to a new button widget
  39. // with the specified text for the button label.
  40. func NewButton(text string) *Button {
  41. b := new(Button)
  42. b.styles = &StyleDefault().Button
  43. // Initializes the button panel
  44. b.Panel = NewPanel(0, 0)
  45. // Subscribe to panel events
  46. b.Panel.Subscribe(OnKeyDown, b.onKey)
  47. b.Panel.Subscribe(OnKeyUp, b.onKey)
  48. b.Panel.Subscribe(OnMouseUp, b.onMouse)
  49. b.Panel.Subscribe(OnMouseDown, b.onMouse)
  50. b.Panel.Subscribe(OnCursor, b.onCursor)
  51. b.Panel.Subscribe(OnCursorEnter, b.onCursor)
  52. b.Panel.Subscribe(OnCursorLeave, b.onCursor)
  53. b.Panel.Subscribe(OnEnable, func(name string, ev interface{}) { b.update() })
  54. b.Panel.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
  55. // Creates label
  56. b.Label = NewLabel(text)
  57. b.Label.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
  58. b.Panel.Add(b.Label)
  59. b.recalc() // recalc first then update!
  60. b.update()
  61. return b
  62. }
  63. // SetIcon sets the button icon from the default Icon font.
  64. // If there is currently a selected image, it is removed
  65. func (b *Button) SetIcon(icode string) {
  66. ico := NewLabel(icode, true)
  67. if b.image != nil {
  68. b.Panel.Remove(b.image)
  69. b.image = nil
  70. }
  71. if b.icon != nil {
  72. b.Panel.Remove(b.icon)
  73. }
  74. b.icon = ico
  75. b.icon.SetFontSize(b.Label.FontSize() * 1.4)
  76. b.Panel.Add(b.icon)
  77. b.recalc()
  78. b.update()
  79. }
  80. // SetImage sets the button left image from the specified filename
  81. // If there is currently a selected icon, it is removed
  82. func (b *Button) SetImage(imgfile string) error {
  83. img, err := NewImage(imgfile)
  84. if err != nil {
  85. return err
  86. }
  87. if b.image != nil {
  88. b.Panel.Remove(b.image)
  89. }
  90. b.image = img
  91. b.Panel.Add(b.image)
  92. b.recalc()
  93. return nil
  94. }
  95. // SetStyles set the button styles overriding the default style
  96. func (b *Button) SetStyles(bs *ButtonStyles) {
  97. b.styles = bs
  98. b.update()
  99. }
  100. // onCursor process subscribed cursor events
  101. func (b *Button) onCursor(evname string, ev interface{}) {
  102. switch evname {
  103. case OnCursorEnter:
  104. b.mouseOver = true
  105. b.update()
  106. case OnCursorLeave:
  107. b.pressed = false
  108. b.mouseOver = false
  109. b.update()
  110. }
  111. b.root.StopPropagation(StopAll)
  112. }
  113. // onMouseEvent process subscribed mouse events
  114. func (b *Button) onMouse(evname string, ev interface{}) {
  115. switch evname {
  116. case OnMouseDown:
  117. b.root.SetKeyFocus(b)
  118. b.pressed = true
  119. b.update()
  120. b.Dispatch(OnClick, nil)
  121. case OnMouseUp:
  122. b.pressed = false
  123. b.update()
  124. default:
  125. return
  126. }
  127. b.root.StopPropagation(StopAll)
  128. }
  129. // onKey processes subscribed key events
  130. func (b *Button) onKey(evname string, ev interface{}) {
  131. kev := ev.(*window.KeyEvent)
  132. if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
  133. b.pressed = true
  134. b.update()
  135. b.Dispatch(OnClick, nil)
  136. b.root.StopPropagation(Stop3D)
  137. return
  138. }
  139. if evname == OnKeyUp && kev.Keycode == window.KeyEnter {
  140. b.pressed = false
  141. b.update()
  142. b.root.StopPropagation(Stop3D)
  143. return
  144. }
  145. return
  146. }
  147. // update updates the button visual state
  148. func (b *Button) update() {
  149. if !b.Enabled() {
  150. b.applyStyle(&b.styles.Disabled)
  151. return
  152. }
  153. if b.pressed {
  154. b.applyStyle(&b.styles.Pressed)
  155. return
  156. }
  157. if b.mouseOver {
  158. b.applyStyle(&b.styles.Over)
  159. return
  160. }
  161. b.applyStyle(&b.styles.Normal)
  162. }
  163. // applyStyle applies the specified button style
  164. func (b *Button) applyStyle(bs *ButtonStyle) {
  165. b.Panel.ApplyStyle(&bs.PanelStyle)
  166. if b.icon != nil {
  167. b.icon.SetColor4(&bs.FgColor)
  168. }
  169. b.Label.SetColor4(&bs.FgColor)
  170. }
  171. // recalc recalculates all dimensions and position from inside out
  172. func (b *Button) recalc() {
  173. // Current width and height of button content area
  174. width := b.Panel.ContentWidth()
  175. height := b.Panel.ContentHeight()
  176. // Image or icon width
  177. imgWidth := float32(0)
  178. spacing := float32(4)
  179. if b.image != nil {
  180. imgWidth = b.image.Width()
  181. } else if b.icon != nil {
  182. imgWidth = b.icon.Width()
  183. }
  184. if imgWidth == 0 {
  185. spacing = 0
  186. }
  187. // If the label is empty and an icon of image was defined ignore the label widthh
  188. // to centralize the icon/image in the button
  189. labelWidth := spacing + b.Label.Width()
  190. if b.Label.Text() == "" && imgWidth > 0 {
  191. labelWidth = 0
  192. }
  193. // Sets new content width and height if necessary
  194. minWidth := imgWidth + labelWidth
  195. minHeight := b.Label.Height()
  196. resize := false
  197. if width < minWidth {
  198. width = minWidth
  199. resize = true
  200. }
  201. if height < minHeight {
  202. height = minHeight
  203. resize = true
  204. }
  205. if resize {
  206. b.SetContentSize(width, height)
  207. }
  208. // Centralize horizontally
  209. px := (width - minWidth) / 2
  210. // Set label position
  211. ly := (height - b.Label.Height()) / 2
  212. b.Label.SetPosition(px+imgWidth+spacing, ly)
  213. // Image/icon position
  214. if b.image != nil {
  215. iy := (height - b.image.height) / 2
  216. b.image.SetPosition(px, iy)
  217. } else if b.icon != nil {
  218. b.icon.SetPosition(px, ly)
  219. }
  220. }