button.go 5.8 KB

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