slider.go 7.8 KB

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