scrollbar.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. ScrollBar Panel
  11. +--------------------------------+
  12. | scroll button |
  13. | +--------------+ |
  14. | | | |
  15. | | | |
  16. | +--------------+ |
  17. +--------------------------------+
  18. **/
  19. // ScrollBar is the scrollbar GUI element.
  20. type ScrollBar struct {
  21. Panel // Embedded panel
  22. styles *ScrollBarStyles // styles of the scrollbar
  23. vertical bool // type of scrollbar
  24. button scrollBarButton // scrollbar button
  25. cursorOver bool
  26. }
  27. type scrollBarButton struct {
  28. Panel // Embedded panel
  29. sb *ScrollBar // pointer to parent scroll bar
  30. pressed bool // mouse button pressed flag
  31. mouseX float32 // last mouse click x position
  32. mouseY float32 // last mouse click y position
  33. Size float32 // button size
  34. MinSize float32 // minimum button size
  35. }
  36. // ScrollBarStyles contains a ScrollBarStyle for each valid GUI state.
  37. type ScrollBarStyles struct {
  38. Normal ScrollBarStyle
  39. Over ScrollBarStyle
  40. Disabled ScrollBarStyle
  41. }
  42. // ScrollBarStyle contains the styling of a ScrollBar.
  43. type ScrollBarStyle struct {
  44. PanelStyle
  45. Button PanelStyle
  46. ButtonLength float32 // This is the default/minimum button length
  47. // TODO ScrollSpeed ?
  48. }
  49. // NewVScrollBar creates and returns a pointer to a new vertical scroll bar
  50. // with the specified dimensions.
  51. func NewVScrollBar(width, height float32) *ScrollBar {
  52. return newScrollBar(width, height, true)
  53. }
  54. // NewHScrollBar creates and returns a pointer to a new horizontal scroll bar
  55. // with the specified dimensions.
  56. func NewHScrollBar(width, height float32) *ScrollBar {
  57. return newScrollBar(width, height, false)
  58. }
  59. // newScrollBar creates and returns a pointer to a new scroll bar panel
  60. // with the specified width, height, orientation and target.
  61. func newScrollBar(width, height float32, vertical bool) *ScrollBar {
  62. sb := new(ScrollBar)
  63. sb.initialize(width, height, vertical)
  64. return sb
  65. }
  66. // initialize initializes this scrollbar
  67. func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
  68. sb.styles = &StyleDefault().ScrollBar
  69. sb.vertical = vertical
  70. sb.Panel.Initialize(width, height)
  71. sb.Panel.Subscribe(OnMouseDown, sb.onMouse)
  72. // Initialize scrollbar button
  73. sb.button.Panel.Initialize(0, 0)
  74. sb.button.Panel.Subscribe(OnMouseDown, sb.button.onMouse)
  75. sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
  76. sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)
  77. sb.button.SetMargins(1, 1, 1, 1)
  78. sb.button.Size = sb.styles.Normal.ButtonLength
  79. sb.button.sb = sb
  80. sb.Add(&sb.button)
  81. sb.update()
  82. sb.recalc()
  83. }
  84. // SetButtonSize sets the button size
  85. func (sb *ScrollBar) SetButtonSize(size float32) {
  86. // Clamp to minimum size if requested size smaller than minimum
  87. if size > sb.button.MinSize {
  88. sb.button.Size = size
  89. } else {
  90. sb.button.Size = sb.button.MinSize
  91. }
  92. sb.recalc()
  93. }
  94. // Value returns the current position of the button in the scrollbar
  95. // The returned value is between 0.0 and 1.0
  96. func (sb *ScrollBar) Value() float64 {
  97. if sb.vertical {
  98. den := float64(sb.content.Height) - float64(sb.button.height)
  99. if den == 0 {
  100. return 0
  101. }
  102. return float64(sb.button.Position().Y) / den
  103. }
  104. // horizontal
  105. den := float64(sb.content.Width) - float64(sb.button.width)
  106. if den == 0 {
  107. return 0
  108. }
  109. return float64(sb.button.Position().X) / den
  110. }
  111. // SetValue sets the position of the button of the scrollbar
  112. // from 0.0 (minimum) to 1.0 (maximum).
  113. func (sb *ScrollBar) SetValue(v float32) {
  114. v = math32.Clamp(v, 0.0, 1.0)
  115. if sb.vertical {
  116. pos := v * (float32(sb.content.Height) - float32(sb.button.height))
  117. sb.button.SetPositionY(pos)
  118. } else {
  119. pos := v * (float32(sb.content.Width) - float32(sb.button.width))
  120. sb.button.SetPositionX(pos)
  121. }
  122. }
  123. // onMouse receives subscribed mouse events over the scrollbar outer panel
  124. func (sb *ScrollBar) onMouse(evname string, ev interface{}) {
  125. e := ev.(*window.MouseEvent)
  126. if e.Button != window.MouseButtonLeft {
  127. return
  128. }
  129. if sb.vertical {
  130. posy := e.Ypos - sb.pospix.Y
  131. newY := math32.Clamp(posy-(sb.button.height/2), 0, sb.content.Height-sb.button.height)
  132. sb.button.SetPositionY(newY)
  133. } else {
  134. posx := e.Xpos - sb.pospix.X
  135. newX := math32.Clamp(posx-(sb.button.width/2), 0, sb.content.Width-sb.button.width)
  136. sb.button.SetPositionX(newX)
  137. }
  138. sb.root.StopPropagation(StopAll)
  139. sb.Dispatch(OnChange, nil)
  140. }
  141. // recalc recalculates sizes and positions
  142. func (sb *ScrollBar) recalc() {
  143. if sb.vertical {
  144. sb.button.SetSize(sb.content.Width, sb.button.Size)
  145. } else {
  146. sb.button.SetSize(sb.button.Size, sb.content.Height)
  147. }
  148. }
  149. // update updates the visual state
  150. func (sb *ScrollBar) update() {
  151. // TODO disabling the scrollbar only affects style, needs to affect behavior
  152. if !sb.Enabled() {
  153. sb.applyStyle(&sb.styles.Disabled)
  154. return
  155. }
  156. // TODO cursorOver is never set to true for the scrollbar
  157. if sb.cursorOver {
  158. sb.applyStyle(&sb.styles.Over)
  159. return
  160. }
  161. sb.applyStyle(&sb.styles.Normal)
  162. }
  163. // update updates border sizes and colors
  164. func (sb *ScrollBar) applyStyle(sbs *ScrollBarStyle) {
  165. sb.Panel.ApplyStyle(&sbs.PanelStyle)
  166. sb.button.ApplyStyle(&sbs.Button)
  167. sb.button.MinSize = sbs.ButtonLength
  168. }
  169. // onMouse receives subscribed mouse events for the scroll bar button
  170. func (button *scrollBarButton) onMouse(evname string, ev interface{}) {
  171. e := ev.(*window.MouseEvent)
  172. if e.Button != window.MouseButtonLeft {
  173. return
  174. }
  175. switch evname {
  176. case OnMouseDown:
  177. button.pressed = true
  178. button.mouseX = e.Xpos
  179. button.mouseY = e.Ypos
  180. button.sb.root.SetMouseFocus(button)
  181. case OnMouseUp:
  182. button.pressed = false
  183. button.sb.root.SetMouseFocus(nil)
  184. default:
  185. return
  186. }
  187. button.sb.root.StopPropagation(StopAll)
  188. }
  189. // onCursor receives subscribed cursor events for the scroll bar button
  190. func (button *scrollBarButton) onCursor(evname string, ev interface{}) {
  191. e := ev.(*window.CursorEvent)
  192. if !button.pressed {
  193. return
  194. }
  195. if button.sb.vertical {
  196. dy := button.mouseY - e.Ypos
  197. py := button.Position().Y
  198. button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.Size))
  199. } else {
  200. dx := button.mouseX - e.Xpos
  201. px := button.Position().X
  202. button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.Size))
  203. }
  204. button.mouseX = e.Xpos
  205. button.mouseY = e.Ypos
  206. button.sb.Dispatch(OnChange, nil)
  207. button.sb.root.StopPropagation(StopAll)
  208. }