button.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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. if !b.Enabled() {
  115. return
  116. }
  117. switch evname {
  118. case OnMouseDown:
  119. Manager().SetKeyFocus(b)
  120. b.pressed = true
  121. b.update()
  122. case OnMouseUpOut:
  123. fallthrough
  124. case OnMouseUp:
  125. if b.pressed && b.mouseOver {
  126. b.Dispatch(OnClick, nil)
  127. }
  128. b.pressed = false
  129. b.update()
  130. default:
  131. return
  132. }
  133. }
  134. // onKey processes subscribed key events
  135. func (b *Button) onKey(evname string, ev interface{}) {
  136. kev := ev.(*window.KeyEvent)
  137. if kev.Key != window.KeyEnter {
  138. return
  139. }
  140. switch evname {
  141. case OnKeyDown:
  142. b.pressed = true
  143. b.update()
  144. b.Dispatch(OnClick, nil)
  145. case OnKeyUp:
  146. b.pressed = false
  147. b.update()
  148. }
  149. }
  150. // update updates the button visual state
  151. func (b *Button) update() {
  152. if !b.Enabled() {
  153. b.applyStyle(&b.styles.Disabled)
  154. return
  155. }
  156. if b.pressed && b.mouseOver {
  157. b.applyStyle(&b.styles.Pressed)
  158. return
  159. }
  160. if b.mouseOver {
  161. b.applyStyle(&b.styles.Over)
  162. return
  163. }
  164. b.applyStyle(&b.styles.Normal)
  165. }
  166. // applyStyle applies the specified button style
  167. func (b *Button) applyStyle(bs *ButtonStyle) {
  168. b.Panel.ApplyStyle(&bs.PanelStyle)
  169. if b.icon != nil {
  170. b.icon.SetColor4(&bs.FgColor)
  171. }
  172. b.Label.SetColor4(&bs.FgColor)
  173. }
  174. // recalc recalculates all dimensions and position from inside out
  175. func (b *Button) recalc() {
  176. // Current width and height of button content area
  177. width := b.Panel.ContentWidth()
  178. height := b.Panel.ContentHeight()
  179. // Image or icon width
  180. imgWidth := float32(0)
  181. spacing := float32(4)
  182. if b.image != nil {
  183. imgWidth = b.image.Width()
  184. } else if b.icon != nil {
  185. imgWidth = b.icon.Width()
  186. }
  187. if imgWidth == 0 {
  188. spacing = 0
  189. }
  190. // If the label is empty and an icon of image was defined ignore the label widthh
  191. // to centralize the icon/image in the button
  192. labelWidth := spacing + b.Label.Width()
  193. if b.Label.Text() == "" && imgWidth > 0 {
  194. labelWidth = 0
  195. }
  196. // Sets new content width and height if necessary
  197. minWidth := imgWidth + labelWidth
  198. minHeight := b.Label.Height()
  199. resize := false
  200. if width < minWidth {
  201. width = minWidth
  202. resize = true
  203. }
  204. if height < minHeight {
  205. height = minHeight
  206. resize = true
  207. }
  208. if resize {
  209. b.SetContentSize(width, height)
  210. }
  211. // Centralize horizontally
  212. px := (width - minWidth) / 2
  213. // Set label position
  214. ly := (height - b.Label.Height()) / 2
  215. b.Label.SetPosition(px+imgWidth+spacing, ly)
  216. // Image/icon position
  217. if b.image != nil {
  218. iy := (height - b.image.height) / 2
  219. b.image.SetPosition(px, iy)
  220. } else if b.icon != nil {
  221. b.icon.SetPosition(px, ly)
  222. }
  223. }