slider.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. Slider
  10. +--------------------------------+
  11. | +--------------------------+ |
  12. | | +----------+ | |
  13. | | | | | |
  14. | | | | | |
  15. | | +----------+ | |
  16. | +--------------------------+ |
  17. +--------------------------------+
  18. **/
  19. // Slider is the GUI element for sliders and progress bars
  20. type Slider struct {
  21. Panel // Embedded panel
  22. slider Panel // embedded slider panel
  23. label *Label // optional label
  24. horiz bool // orientation
  25. styles *SliderStyles // pointer to styles
  26. pos float32 // current slider position
  27. posLast float32 // last position of the mouse cursor when dragging
  28. pressed bool // mouse button is pressed and dragging
  29. cursorOver bool // mouse is over slider
  30. scaleFactor float32 // scale factor (default = 1.0)
  31. }
  32. // SliderStyle contains the styling of a Slider
  33. type SliderStyle BasicStyle
  34. // SliderStyles contains a SliderStyle for each valid GUI state
  35. type SliderStyles struct {
  36. Normal SliderStyle
  37. Over SliderStyle
  38. Focus SliderStyle
  39. Disabled SliderStyle
  40. }
  41. // NewHSlider creates and returns a pointer to a new horizontal slider
  42. // with the specified initial dimensions.
  43. func NewHSlider(width, height float32) *Slider {
  44. return newSlider(true, width, height)
  45. }
  46. // NewVSlider creates and returns a pointer to a new vertical slider
  47. // with the specified initial dimensions.
  48. func NewVSlider(width, height float32) *Slider {
  49. return newSlider(false, width, height)
  50. }
  51. // NewSlider creates and returns a pointer to a new slider with the
  52. // specified initial dimensions.
  53. func newSlider(horiz bool, width, height float32) *Slider {
  54. s := new(Slider)
  55. s.horiz = horiz
  56. s.styles = &StyleDefault().Slider
  57. s.scaleFactor = 1.0
  58. // Initialize main panel
  59. s.Panel.Initialize(width, height)
  60. s.Panel.Subscribe(OnMouseDown, s.onMouse)
  61. s.Panel.Subscribe(OnMouseUp, s.onMouse)
  62. s.Panel.Subscribe(OnCursor, s.onCursor)
  63. s.Panel.Subscribe(OnCursorEnter, s.onCursor)
  64. s.Panel.Subscribe(OnCursorLeave, s.onCursor)
  65. s.Panel.Subscribe(OnScroll, s.onScroll)
  66. s.Panel.Subscribe(OnKeyDown, s.onKey)
  67. s.Panel.Subscribe(OnKeyRepeat, s.onKey)
  68. s.Panel.Subscribe(OnResize, s.onResize)
  69. s.Panel.Subscribe(OnEnable, func(evname string, ev interface{}) { s.update() })
  70. // Initialize slider panel
  71. s.slider.Initialize(0, 0)
  72. s.Panel.Add(&s.slider)
  73. s.recalc()
  74. s.update()
  75. return s
  76. }
  77. // SetStyles set the slider styles overriding the default style
  78. func (s *Slider) SetStyles(ss *SliderStyles) {
  79. s.styles = ss
  80. s.update()
  81. }
  82. // SetText sets the text of the slider optional label
  83. func (s *Slider) SetText(text string) {
  84. if s.label == nil {
  85. s.label = NewLabel(text)
  86. s.Panel.Add(s.label)
  87. } else {
  88. s.label.SetText(text)
  89. }
  90. s.update()
  91. s.recalc()
  92. }
  93. // SetValue sets the value of the slider considering the current scale factor
  94. // and updates its visual appearance.
  95. func (s *Slider) SetValue(value float32) {
  96. pos := value / s.scaleFactor
  97. s.setPos(pos)
  98. }
  99. // Value returns the current value of the slider considering the current scale factor
  100. func (s *Slider) Value() float32 {
  101. return s.pos * s.scaleFactor
  102. }
  103. // SetScaleFactor set the slider scale factor (default = 1.0)
  104. func (s *Slider) SetScaleFactor(factor float32) {
  105. s.scaleFactor = factor
  106. }
  107. // ScaleFactor returns the slider current scale factor (default = 1.0)
  108. func (s *Slider) ScaleFactor() float32 {
  109. return s.scaleFactor
  110. }
  111. // setPos sets the slider position from 0.0 to 1.0
  112. // and updates its visual appearance.
  113. func (s *Slider) setPos(pos float32) {
  114. const eps = 0.01
  115. if pos < 0 {
  116. pos = 0
  117. } else if pos > 1.0 {
  118. pos = 1
  119. }
  120. if pos > (s.pos+eps) && pos < (s.pos+eps) {
  121. return
  122. }
  123. s.pos = pos
  124. s.recalc()
  125. s.Dispatch(OnChange, nil)
  126. }
  127. // onMouse process subscribed mouse events over the outer panel
  128. func (s *Slider) onMouse(evname string, ev interface{}) {
  129. mev := ev.(*window.MouseEvent)
  130. switch evname {
  131. case OnMouseDown:
  132. s.pressed = true
  133. if s.horiz {
  134. s.posLast = mev.Xpos
  135. } else {
  136. s.posLast = mev.Ypos
  137. }
  138. s.root.SetMouseFocus(s)
  139. s.root.SetKeyFocus(s)
  140. case OnMouseUp:
  141. s.pressed = false
  142. if !s.cursorOver {
  143. s.root.SetCursorNormal()
  144. }
  145. s.root.SetMouseFocus(nil)
  146. default:
  147. return
  148. }
  149. s.root.StopPropagation(Stop3D)
  150. }
  151. // onCursor process subscribed cursor events
  152. func (s *Slider) onCursor(evname string, ev interface{}) {
  153. if evname == OnCursorEnter {
  154. s.root.SetScrollFocus(s)
  155. if s.horiz {
  156. s.root.SetCursorHResize()
  157. } else {
  158. s.root.SetCursorVResize()
  159. }
  160. s.cursorOver = true
  161. s.update()
  162. } else if evname == OnCursorLeave {
  163. s.root.SetScrollFocus(nil)
  164. s.root.SetCursorNormal()
  165. s.cursorOver = false
  166. s.update()
  167. } else if evname == OnCursor {
  168. if !s.pressed {
  169. return
  170. }
  171. cev := ev.(*window.CursorEvent)
  172. var pos float32
  173. if s.horiz {
  174. delta := cev.Xpos - s.posLast
  175. s.posLast = cev.Xpos
  176. newpos := s.slider.Width() + delta
  177. pos = newpos / s.Panel.ContentWidth()
  178. } else {
  179. delta := cev.Ypos - s.posLast
  180. s.posLast = cev.Ypos
  181. newpos := s.slider.Height() - delta
  182. pos = newpos / s.Panel.ContentHeight()
  183. }
  184. s.setPos(pos)
  185. }
  186. s.root.StopPropagation(Stop3D)
  187. }
  188. // onScroll process subscribed scroll events
  189. func (s *Slider) onScroll(evname string, ev interface{}) {
  190. sev := ev.(*window.ScrollEvent)
  191. v := s.pos
  192. v += sev.Yoffset * 0.01
  193. s.setPos(v)
  194. s.root.StopPropagation(Stop3D)
  195. }
  196. // onKey process subscribed key events
  197. func (s *Slider) onKey(evname string, ev interface{}) {
  198. kev := ev.(*window.KeyEvent)
  199. delta := float32(0.01)
  200. // Horizontal slider
  201. if s.horiz {
  202. switch kev.Keycode {
  203. case window.KeyLeft:
  204. s.setPos(s.pos - delta)
  205. case window.KeyRight:
  206. s.setPos(s.pos + delta)
  207. default:
  208. return
  209. }
  210. // Vertical slider
  211. } else {
  212. switch kev.Keycode {
  213. case window.KeyDown:
  214. s.setPos(s.pos - delta)
  215. case window.KeyUp:
  216. s.setPos(s.pos + delta)
  217. default:
  218. return
  219. }
  220. }
  221. s.root.StopPropagation(Stop3D)
  222. }
  223. // onResize process subscribed resize events
  224. func (s *Slider) onResize(evname string, ev interface{}) {
  225. s.recalc()
  226. }
  227. // update updates the slider visual state
  228. func (s *Slider) update() {
  229. if !s.Enabled() {
  230. s.applyStyle(&s.styles.Disabled)
  231. return
  232. }
  233. if s.cursorOver {
  234. s.applyStyle(&s.styles.Over)
  235. return
  236. }
  237. s.applyStyle(&s.styles.Normal)
  238. }
  239. // applyStyle applies the specified slider style
  240. func (s *Slider) applyStyle(ss *SliderStyle) {
  241. s.Panel.ApplyStyle(&ss.PanelStyle)
  242. s.slider.SetColor4(&ss.FgColor)
  243. }
  244. // recalc recalculates the dimensions and positions of the internal panels.
  245. func (s *Slider) recalc() {
  246. if s.horiz {
  247. if s.label != nil {
  248. lx := (s.Panel.ContentWidth() - s.label.Width()) / 2
  249. if s.Panel.ContentHeight() < s.label.Height() {
  250. s.Panel.SetContentHeight(s.label.Height())
  251. }
  252. ly := (s.Panel.ContentHeight() - s.label.Height()) / 2
  253. s.label.SetPosition(lx, ly)
  254. }
  255. width := s.Panel.ContentWidth() * s.pos
  256. s.slider.SetSize(width, s.Panel.ContentHeight())
  257. } else {
  258. if s.label != nil {
  259. if s.Panel.ContentWidth() < s.label.Width() {
  260. s.Panel.SetContentWidth(s.label.Width())
  261. }
  262. lx := (s.Panel.ContentWidth() - s.label.Width()) / 2
  263. ly := (s.Panel.ContentHeight() - s.label.Height()) / 2
  264. s.label.SetPosition(lx, ly)
  265. }
  266. height := s.Panel.ContentHeight() * s.pos
  267. s.slider.SetPositionY(s.Panel.ContentHeight() - height)
  268. s.slider.SetSize(s.Panel.ContentWidth(), height)
  269. }
  270. }