button.go 6.1 KB

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