button.go 5.8 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. // ButtonStyle contains the styling of a Button
  29. type ButtonStyle BasicStyle
  30. // ButtonStyles contains one ButtonStyle for each possible button state
  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.Initialize(b, 0, 0)
  45. // Subscribe to panel events
  46. b.Subscribe(OnKeyDown, b.onKey)
  47. b.Subscribe(OnKeyUp, b.onKey)
  48. b.Subscribe(OnMouseUp, b.onMouse)
  49. b.Subscribe(OnMouseDown, b.onMouse)
  50. b.Subscribe(OnMouseUpOut, b.onMouse)
  51. b.Subscribe(OnCursor, b.onCursor)
  52. b.Subscribe(OnCursorEnter, b.onCursor)
  53. b.Subscribe(OnCursorLeave, b.onCursor)
  54. b.Subscribe(OnEnable, func(name string, ev interface{}) { b.update() })
  55. b.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
  56. // Creates label
  57. b.Label = NewLabel(text)
  58. b.Label.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
  59. b.Panel.Add(b.Label)
  60. b.recalc() // recalc first then update!
  61. b.update()
  62. return b
  63. }
  64. // SetIcon sets the button icon from the default Icon font.
  65. // If there is currently a selected image, it is removed
  66. func (b *Button) SetIcon(icode string) {
  67. ico := NewIcon(icode)
  68. if b.image != nil {
  69. b.Panel.Remove(b.image)
  70. b.image = nil
  71. }
  72. if b.icon != nil {
  73. b.Panel.Remove(b.icon)
  74. }
  75. b.icon = ico
  76. b.icon.SetFontSize(b.Label.FontSize() * 1.4)
  77. b.Panel.Add(b.icon)
  78. b.recalc()
  79. b.update()
  80. }
  81. // SetImage sets the button left image from the specified filename
  82. // If there is currently a selected icon, it is removed
  83. func (b *Button) SetImage(imgfile string) error {
  84. img, err := NewImage(imgfile)
  85. if err != nil {
  86. return err
  87. }
  88. if b.image != nil {
  89. b.Panel.Remove(b.image)
  90. }
  91. b.image = img
  92. b.Panel.Add(b.image)
  93. b.recalc()
  94. return nil
  95. }
  96. // SetStyles set the button styles overriding the default style
  97. func (b *Button) SetStyles(bs *ButtonStyles) {
  98. b.styles = bs
  99. b.update()
  100. }
  101. // onCursor process subscribed cursor events
  102. func (b *Button) onCursor(evname string, ev interface{}) {
  103. switch evname {
  104. case OnCursorEnter:
  105. b.mouseOver = true
  106. b.update()
  107. case OnCursorLeave:
  108. b.mouseOver = false
  109. b.update()
  110. }
  111. }
  112. // onMouseEvent process subscribed mouse events
  113. func (b *Button) onMouse(evname string, ev interface{}) {
  114. switch evname {
  115. case OnMouseDown:
  116. Manager().SetKeyFocus(b)
  117. b.pressed = true
  118. b.update()
  119. case OnMouseUpOut:
  120. fallthrough
  121. case OnMouseUp:
  122. if b.pressed && b.mouseOver {
  123. b.Dispatch(OnClick, nil)
  124. }
  125. b.pressed = false
  126. b.update()
  127. default:
  128. return
  129. }
  130. }
  131. // onKey processes subscribed key events
  132. func (b *Button) onKey(evname string, ev interface{}) {
  133. kev := ev.(*window.KeyEvent)
  134. if kev.Key != window.KeyEnter {
  135. return
  136. }
  137. switch evname {
  138. case OnKeyDown:
  139. b.pressed = true
  140. b.update()
  141. b.Dispatch(OnClick, nil)
  142. case OnKeyUp:
  143. b.pressed = false
  144. b.update()
  145. }
  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 && b.mouseOver {
  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. }