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