checkradio.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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/gui/assets/icon"
  7. "github.com/g3n/engine/window"
  8. )
  9. const (
  10. checkON = string(icon.CheckBox)
  11. checkOFF = string(icon.CheckBoxOutlineBlank)
  12. radioON = string(icon.RadioButtonChecked)
  13. radioOFF = string(icon.RadioButtonUnchecked)
  14. )
  15. // CheckRadio is a GUI element that can be either a checkbox or a radio button
  16. type CheckRadio struct {
  17. Panel // Embedded panel
  18. Label *Label // Text label
  19. icon *Label
  20. styles *CheckRadioStyles
  21. check bool
  22. group string // current group name
  23. cursorOver bool
  24. state bool
  25. codeON string
  26. codeOFF string
  27. subroot bool // indicates root subcription
  28. }
  29. // CheckRadioStyle contains the styling of a CheckRadio
  30. type CheckRadioStyle BasicStyle
  31. // CheckRadioStyles contains an CheckRadioStyle for each valid GUI state
  32. type CheckRadioStyles struct {
  33. Normal CheckRadioStyle
  34. Over CheckRadioStyle
  35. Focus CheckRadioStyle
  36. Disabled CheckRadioStyle
  37. }
  38. // NewCheckBox creates and returns a pointer to a new CheckBox widget
  39. // with the specified text
  40. func NewCheckBox(text string) *CheckRadio {
  41. return newCheckRadio(true, text)
  42. }
  43. // NewRadioButton creates and returns a pointer to a new RadioButton widget
  44. // with the specified text
  45. func NewRadioButton(text string) *CheckRadio {
  46. return newCheckRadio(false, text)
  47. }
  48. // newCheckRadio creates and returns a pointer to a new CheckRadio widget
  49. // with the specified type and text
  50. func newCheckRadio(check bool, text string) *CheckRadio {
  51. cb := new(CheckRadio)
  52. cb.styles = &StyleDefault().CheckRadio
  53. // Adapts to specified type: CheckBox or RadioButton
  54. cb.check = check
  55. cb.state = false
  56. if cb.check {
  57. cb.codeON = checkON
  58. cb.codeOFF = checkOFF
  59. } else {
  60. cb.codeON = radioON
  61. cb.codeOFF = radioOFF
  62. }
  63. // Initialize panel
  64. cb.Panel.Initialize(cb, 0, 0)
  65. // Subscribe to events
  66. cb.Panel.Subscribe(OnKeyDown, cb.onKey)
  67. cb.Panel.Subscribe(OnCursorEnter, cb.onCursor)
  68. cb.Panel.Subscribe(OnCursorLeave, cb.onCursor)
  69. cb.Panel.Subscribe(OnMouseDown, cb.onMouse)
  70. cb.Panel.Subscribe(OnEnable, func(evname string, ev interface{}) { cb.update() })
  71. // Creates label
  72. cb.Label = NewLabel(text)
  73. cb.Label.Subscribe(OnResize, func(evname string, ev interface{}) { cb.recalc() })
  74. cb.Panel.Add(cb.Label)
  75. // Creates icon label
  76. cb.icon = NewIcon(" ")
  77. cb.Panel.Add(cb.icon)
  78. cb.recalc()
  79. cb.update()
  80. return cb
  81. }
  82. // Value returns the current state of the checkbox
  83. func (cb *CheckRadio) Value() bool {
  84. return cb.state
  85. }
  86. // SetValue sets the current state of the checkbox
  87. func (cb *CheckRadio) SetValue(state bool) *CheckRadio {
  88. if state == cb.state {
  89. return cb
  90. }
  91. cb.state = state
  92. cb.update()
  93. cb.Dispatch(OnChange, nil)
  94. return cb
  95. }
  96. // Group returns the name of the radio group
  97. func (cb *CheckRadio) Group() string {
  98. return cb.group
  99. }
  100. // SetGroup sets the name of the radio group
  101. func (cb *CheckRadio) SetGroup(group string) *CheckRadio {
  102. cb.group = group
  103. return cb
  104. }
  105. // SetStyles set the button styles overriding the default style
  106. func (cb *CheckRadio) SetStyles(bs *CheckRadioStyles) {
  107. cb.styles = bs
  108. cb.update()
  109. }
  110. // toggleState toggles the current state of the checkbox/radiobutton
  111. func (cb *CheckRadio) toggleState() {
  112. // Subscribes once to the root panel for OnRadioGroup events
  113. // The root panel is used to dispatch events to all checkradios
  114. if !cb.subroot {
  115. cb.root.Subscribe(OnRadioGroup, func(name string, ev interface{}) {
  116. cb.onRadioGroup(ev.(*CheckRadio))
  117. })
  118. cb.subroot = true
  119. }
  120. if cb.check {
  121. cb.state = !cb.state
  122. } else {
  123. if len(cb.group) == 0 {
  124. cb.state = !cb.state
  125. } else {
  126. if cb.state {
  127. return
  128. }
  129. cb.state = !cb.state
  130. }
  131. }
  132. cb.update()
  133. cb.Dispatch(OnChange, nil)
  134. if !cb.check && len(cb.group) > 0 {
  135. cb.root.Dispatch(OnRadioGroup, cb)
  136. }
  137. }
  138. // onMouse process OnMouseDown events
  139. func (cb *CheckRadio) onMouse(evname string, ev interface{}) {
  140. cb.root.SetKeyFocus(cb)
  141. cb.root.StopPropagation(Stop3D)
  142. cb.toggleState()
  143. // Dispatch OnClick for left mouse button down
  144. if evname == OnMouseDown {
  145. mev := ev.(*window.MouseEvent)
  146. if mev.Button == window.MouseButtonLeft {
  147. cb.Dispatch(OnClick, nil)
  148. }
  149. }
  150. cb.root.StopPropagation(StopAll)
  151. }
  152. // onCursor process OnCursor* events
  153. func (cb *CheckRadio) onCursor(evname string, ev interface{}) {
  154. if evname == OnCursorEnter {
  155. cb.cursorOver = true
  156. } else {
  157. cb.cursorOver = false
  158. }
  159. cb.update()
  160. cb.root.StopPropagation(StopAll)
  161. }
  162. // onKey receives subscribed key events
  163. func (cb *CheckRadio) onKey(evname string, ev interface{}) {
  164. kev := ev.(*window.KeyEvent)
  165. if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
  166. cb.toggleState()
  167. cb.update()
  168. cb.Dispatch(OnClick, nil)
  169. cb.root.StopPropagation(Stop3D)
  170. return
  171. }
  172. return
  173. }
  174. // onRadioGroup receives subscribed OnRadioGroup events
  175. func (cb *CheckRadio) onRadioGroup(other *CheckRadio) {
  176. // If event is for this button, ignore
  177. if cb == other {
  178. return
  179. }
  180. // If other radio group is not the group of this button, ignore
  181. if cb.group != other.group {
  182. return
  183. }
  184. // Toggle this button state
  185. cb.SetValue(!other.Value())
  186. }
  187. // update updates the visual appearance of the checkbox
  188. func (cb *CheckRadio) update() {
  189. if cb.state {
  190. cb.icon.SetText(cb.codeON)
  191. } else {
  192. cb.icon.SetText(cb.codeOFF)
  193. }
  194. if !cb.Enabled() {
  195. cb.applyStyle(&cb.styles.Disabled)
  196. return
  197. }
  198. if cb.cursorOver {
  199. cb.applyStyle(&cb.styles.Over)
  200. return
  201. }
  202. cb.applyStyle(&cb.styles.Normal)
  203. }
  204. // setStyle sets the specified checkradio style
  205. func (cb *CheckRadio) applyStyle(s *CheckRadioStyle) {
  206. cb.Panel.ApplyStyle(&s.PanelStyle)
  207. cb.icon.SetColor4(&s.FgColor)
  208. cb.Label.SetColor4(&s.FgColor)
  209. }
  210. // recalc recalculates dimensions and position from inside out
  211. func (cb *CheckRadio) recalc() {
  212. // Sets icon position
  213. cb.icon.SetFontSize(cb.Label.FontSize() * 1.3)
  214. cb.icon.SetPosition(0, 0)
  215. // Label position
  216. spacing := float32(4)
  217. cb.Label.SetPosition(cb.icon.Width()+spacing, 0)
  218. // Content width
  219. width := cb.icon.Width() + spacing + cb.Label.Width()
  220. cb.SetContentSize(width, cb.Label.Height())
  221. }