slider.go 7.4 KB

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