checkradio.go 5.9 KB

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