scroller.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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. "github.com/g3n/engine/math32"
  8. )
  9. // Scroller is the GUI element that allows scrolling of a target IPanel.
  10. // A scroller can have up to two scrollbars, one vertical and one horizontal.
  11. // The vertical scrollbar, if any, can be located either on the left or on the right.
  12. // The horizontal scrollbar, if any, can be located either on the top or on the bottom.
  13. // The interlocking of the scrollbars (which happens when both scrollbars are visible) can be configured.
  14. // Whether each scrollbar overlaps the content can also be configured (useful for transparent UIs).
  15. type Scroller struct {
  16. Panel // Embedded panel
  17. mode ScrollMode // ScrollMode specifies which scroll directions are allowed
  18. target IPanel // The IPanel that will be scrolled through
  19. hscroll *ScrollBar // Horizontal scrollbar (may be nil)
  20. vscroll *ScrollBar // Vertical scrollbar (may be nil)
  21. style *ScrollerStyle // The current style
  22. corner *Panel // The optional corner panel (can be visible when scrollMode==Both, interlocking==None, corner=true)
  23. cursorOver bool // Cursor is over the scroller
  24. modKeyPressed bool // Modifier key is pressed
  25. }
  26. // ScrollMode specifies which scroll directions are allowed
  27. type ScrollMode int
  28. const (
  29. ScrollNone = ScrollMode(0x00) // No scrolling allowed
  30. ScrollVertical = ScrollMode(0x01) // Vertical scrolling allowed
  31. ScrollHorizontal = ScrollMode(0x02) // Horizontal scrolling allowed
  32. ScrollBoth = ScrollMode(ScrollVertical | ScrollHorizontal) // Both vertical and horizontal scrolling allowed
  33. )
  34. // ScrollbarInterlocking specifies what happens where the vertical and horizontal scrollbars meet.
  35. type ScrollbarInterlocking int
  36. const (
  37. ScrollbarInterlockingNone = ScrollbarInterlocking(iota) // No scrollbar interlocking
  38. ScrollbarInterlockingVertical // Vertical scrollbar takes precedence
  39. ScrollbarInterlockingHorizontal // Horizontal scrollbar takes precedence
  40. )
  41. // ScrollbarPosition specifies where the scrollbar is located.
  42. // For the vertical scrollbar it specifies whether it's added to the left or to the right.
  43. // For the horizontal scrollbar it specifies whether it's added to the top or to the bottom.
  44. type ScrollbarPosition int
  45. const (
  46. ScrollbarLeft = ScrollbarPosition(iota) // Scrollbar is positioned on the left of the scroller
  47. ScrollbarRight // Scrollbar is positioned on the right of the scroller
  48. ScrollbarTop // Scrollbar is positioned on the top of the scroller
  49. ScrollbarBottom // Scrollbar is positioned on the bottom of the scroller
  50. )
  51. // ScrollerStyle contains the styling of a Scroller
  52. type ScrollerStyle struct {
  53. PanelStyle // Embedded PanelStyle
  54. VerticalScrollbar ScrollerScrollbarStyle // The style of the vertical scrollbar
  55. HorizontalScrollbar ScrollerScrollbarStyle // The style of the horizontal scrollbar
  56. CornerPanel PanelStyle // The style of the corner panel
  57. ScrollbarInterlocking ScrollbarInterlocking // Specifies what happens where the vertical and horizontal scrollbars meet
  58. CornerCovered bool // True indicates that the corner panel should be visible when appropriate
  59. }
  60. // ScrollerScrollbarStyle is the set of style options for a scrollbar that is part of a scroller.
  61. type ScrollerScrollbarStyle struct {
  62. ScrollBarStyle // Embedded ScrollBarStyle (TODO, should be ScrollBarStyle*S*, implement style logic)
  63. Position ScrollbarPosition // Specifies the positioning of the scrollbar
  64. Broadness float32 // Broadness of the scrollbar
  65. OverlapContent bool // Specifies whether the scrollbar is shown above the content area
  66. AutoSizeButton bool // Specifies whether the scrollbar button size is adjusted based on content/view proportion
  67. }
  68. // TODO these configuration variables could be made part of a global engine configuration object in the future
  69. // They should not be added to style since they are not style changes and not to the struct since they are global
  70. // ScrollModifierKey is the Key that changes the scrolling direction from vertical to horizontal
  71. const ScrollModifierKey = window.KeyLeftShift
  72. // NewScroller creates and returns a pointer to a new Scroller with the specified
  73. // target IPanel and ScrollMode.
  74. func NewScroller(width, height float32, mode ScrollMode, target IPanel) *Scroller {
  75. s := new(Scroller)
  76. s.initialize(width, height, mode, target)
  77. return s
  78. }
  79. // initialize initializes this scroller and can be called by other types which embed a scroller
  80. func (s *Scroller) initialize(width, height float32, mode ScrollMode, target IPanel) {
  81. s.Panel.Initialize(width, height)
  82. s.style = &StyleDefault().Scroller
  83. s.target = target
  84. s.Panel.Add(s.target)
  85. s.mode = mode
  86. s.Subscribe(OnCursorEnter, s.onCursor)
  87. s.Subscribe(OnCursorLeave, s.onCursor)
  88. s.Subscribe(OnScroll, s.onScroll)
  89. s.Subscribe(OnKeyDown, s.onKey)
  90. s.Subscribe(OnKeyUp, s.onKey)
  91. s.Subscribe(OnResize, s.onResize)
  92. s.Update()
  93. }
  94. // SetScrollMode sets the scroll mode
  95. func (s *Scroller) SetScrollMode(mode ScrollMode) {
  96. s.mode = mode
  97. s.Update()
  98. }
  99. // ScrollMode returns the current scroll mode
  100. func (s *Scroller) ScrollMode() ScrollMode {
  101. return s.mode
  102. }
  103. // SetScrollbarInterlocking sets the scrollbar interlocking mode
  104. func (s *Scroller) SetScrollbarInterlocking(interlocking ScrollbarInterlocking) {
  105. s.style.ScrollbarInterlocking = interlocking
  106. s.Update()
  107. }
  108. // ScrollbarInterlocking returns the current scrollbar interlocking mode
  109. func (s *Scroller) ScrollbarInterlocking() ScrollbarInterlocking {
  110. return s.style.ScrollbarInterlocking
  111. }
  112. // SetCornerCovered specifies whether the corner covering panel is shown when appropriate
  113. func (s *Scroller) SetCornerCovered(state bool) {
  114. s.style.CornerCovered = state
  115. s.Update()
  116. }
  117. // CornerCovered returns whether the corner covering panel is being shown when appropriate
  118. func (s *Scroller) CornerCovered() bool {
  119. return s.style.CornerCovered
  120. }
  121. // SetVerticalScrollbarPosition sets the position of the vertical scrollbar (i.e. left or right)
  122. func (s *Scroller) SetVerticalScrollbarPosition(pos ScrollbarPosition) {
  123. s.style.VerticalScrollbar.Position = pos
  124. s.recalc()
  125. }
  126. // VerticalScrollbarPosition returns the current position of the vertical scrollbar (i.e. left or right)
  127. func (s *Scroller) VerticalScrollbarPosition() ScrollbarPosition {
  128. return s.style.VerticalScrollbar.Position
  129. }
  130. // SetHorizontalScrollbarPosition sets the position of the horizontal scrollbar (i.e. top or bottom)
  131. func (s *Scroller) SetHorizontalScrollbarPosition(pos ScrollbarPosition) {
  132. s.style.HorizontalScrollbar.Position = pos
  133. s.recalc()
  134. }
  135. // HorizontalScrollbarPosition returns the current position of the horizontal scrollbar (i.e. top or bottom)
  136. func (s *Scroller) HorizontalScrollbarPosition() ScrollbarPosition {
  137. return s.style.HorizontalScrollbar.Position
  138. }
  139. // SetVerticalScrollbarOverlapping specifies whether the vertical scrollbar overlaps the content area
  140. func (s *Scroller) SetVerticalScrollbarOverlapping(state bool) {
  141. s.style.VerticalScrollbar.OverlapContent = state
  142. s.Update()
  143. }
  144. // VerticalScrollbarOverlapping returns whether the vertical scrollbar overlaps the content area
  145. func (s *Scroller) VerticalScrollbarOverlapping() bool {
  146. return s.style.VerticalScrollbar.OverlapContent
  147. }
  148. // SetHorizontalScrollbarOverlapping specifies whether the horizontal scrollbar overlaps the content area
  149. func (s *Scroller) SetHorizontalScrollbarOverlapping(state bool) {
  150. s.style.HorizontalScrollbar.OverlapContent = state
  151. s.Update()
  152. }
  153. // HorizontalScrollbarOverlapping returns whether the horizontal scrollbar overlaps the content area
  154. func (s *Scroller) HorizontalScrollbarOverlapping() bool {
  155. return s.style.HorizontalScrollbar.OverlapContent
  156. }
  157. // SetVerticalScrollbarAutoSizeButton specifies whether the vertical scrollbar button is sized automatically
  158. func (s *Scroller) SetVerticalScrollbarAutoSizeButton(state bool) {
  159. s.style.VerticalScrollbar.AutoSizeButton = state
  160. if s.vscroll != nil {
  161. if state == false {
  162. s.vscroll.SetButtonSize(s.style.VerticalScrollbar.ScrollBarStyle.ButtonLength)
  163. }
  164. s.recalc()
  165. }
  166. }
  167. // VerticalScrollbarAutoSizeButton returns whether the vertical scrollbar button is sized automatically
  168. func (s *Scroller) VerticalScrollbarAutoSizeButton() bool {
  169. return s.style.VerticalScrollbar.AutoSizeButton
  170. }
  171. // SetHorizontalScrollbarAutoSizeButton specifies whether the horizontal scrollbar button is sized automatically
  172. func (s *Scroller) SetHorizontalScrollbarAutoSizeButton(state bool) {
  173. s.style.HorizontalScrollbar.AutoSizeButton = state
  174. if s.hscroll != nil {
  175. if state == false {
  176. s.hscroll.SetButtonSize(s.style.HorizontalScrollbar.ScrollBarStyle.ButtonLength)
  177. }
  178. s.recalc()
  179. }
  180. }
  181. // HorizontalScrollbarAutoSizeButton returns whether the horizontal scrollbar button is sized automatically
  182. func (s *Scroller) HorizontalScrollbarAutoSizeButton() bool {
  183. return s.style.HorizontalScrollbar.AutoSizeButton
  184. }
  185. // SetVerticalScrollbarBroadness sets the broadness of the vertical scrollbar
  186. func (s *Scroller) SetVerticalScrollbarBroadness(broadness float32) {
  187. s.style.VerticalScrollbar.Broadness = broadness
  188. if s.vscroll != nil {
  189. s.vscroll.SetWidth(broadness)
  190. s.Update()
  191. }
  192. }
  193. // VerticalScrollbarBroadness returns the broadness of the vertical scrollbar
  194. func (s *Scroller) VerticalScrollbarBroadness() float32 {
  195. return s.style.VerticalScrollbar.Broadness
  196. }
  197. // SetHorizontalScrollbarBroadness sets the broadness of the horizontal scrollbar
  198. func (s *Scroller) SetHorizontalScrollbarBroadness(broadness float32) {
  199. s.style.HorizontalScrollbar.Broadness = broadness
  200. if s.hscroll != nil {
  201. s.hscroll.SetHeight(broadness)
  202. s.Update()
  203. }
  204. }
  205. // HorizontalScrollbarBroadness returns the broadness of the horizontal scrollbar
  206. func (s *Scroller) HorizontalScrollbarBroadness() float32 {
  207. return s.style.HorizontalScrollbar.Broadness
  208. }
  209. // ScrollTo scrolls the target panel such that the specified target point is centered on the scroller's view area
  210. func (s *Scroller) ScrollTo(x, y float32) {
  211. // TODO
  212. }
  213. // onCursor receives subscribed cursor events over the panel
  214. func (s *Scroller) onCursor(evname string, ev interface{}) {
  215. switch evname {
  216. case OnCursorEnter:
  217. s.root.SetScrollFocus(s)
  218. s.root.SetKeyFocus(s)
  219. s.cursorOver = true
  220. case OnCursorLeave:
  221. s.root.SetScrollFocus(nil)
  222. s.root.SetKeyFocus(nil)
  223. s.cursorOver = false
  224. }
  225. s.root.StopPropagation(Stop3D)
  226. }
  227. // onScroll receives mouse scroll events when this scroller has the scroll focus (set by OnMouseEnter)
  228. func (s *Scroller) onScroll(evname string, ev interface{}) {
  229. sev := ev.(*window.ScrollEvent)
  230. vScrollVisible := (s.vscroll != nil) && s.vscroll.Visible()
  231. hScrollVisible := (s.hscroll != nil) && s.hscroll.Visible()
  232. mult := float32(1)/float32(10)
  233. offsetX := sev.Xoffset * mult
  234. offsetY := sev.Yoffset * mult
  235. // If modifier key is pressed (left shift by default) - then scroll in the horizontal direction
  236. if s.modKeyPressed {
  237. if math32.Abs(offsetY) > math32.Abs(offsetX) {
  238. offsetX = offsetY
  239. }
  240. offsetY = 0
  241. }
  242. log.Error("X: %v, Y: %v", offsetX, offsetY)
  243. if vScrollVisible {
  244. if hScrollVisible {
  245. // Both scrollbars are present - scroll both
  246. s.vscroll.SetValue(float32(s.vscroll.Value()) - offsetY)
  247. s.hscroll.SetValue(float32(s.hscroll.Value()) - offsetX)
  248. } else {
  249. // Only vertical scrollbar present - scroll it
  250. s.vscroll.SetValue(float32(s.vscroll.Value()) - offsetY)
  251. }
  252. } else if hScrollVisible {
  253. // Only horizontal scrollbar present - scroll it
  254. s.hscroll.SetValue(float32(s.hscroll.Value()) - offsetX)
  255. }
  256. s.recalc()
  257. s.root.StopPropagation(Stop3D)
  258. }
  259. // onKey receives key events
  260. func (s *Scroller) onKey(evname string, ev interface{}) {
  261. key := ev.(*window.KeyEvent)
  262. log.Error("Key %v", key)
  263. if key.Keycode == ScrollModifierKey {
  264. if evname == OnKeyDown {
  265. s.modKeyPressed = true
  266. log.Error("true")
  267. } else if evname == OnKeyUp {
  268. log.Error("false")
  269. s.modKeyPressed = false
  270. }
  271. }
  272. s.root.StopPropagation(Stop3D)
  273. }
  274. // onResize receives resize events
  275. func (s *Scroller) onResize(evname string, ev interface{}) {
  276. s.Update()
  277. }
  278. // setVerticalScrollbarVisible sets the vertical scrollbar visible, creating and initializing it if it's the first time
  279. func (s *Scroller) setVerticalScrollbarVisible() {
  280. if s.vscroll == nil {
  281. s.vscroll = NewVScrollBar(s.style.VerticalScrollbar.Broadness, 0)
  282. s.vscroll.applyStyle(&s.style.VerticalScrollbar.ScrollBarStyle)
  283. s.vscroll.Subscribe(OnChange, s.onScrollBarEvent)
  284. s.Add(s.vscroll)
  285. }
  286. s.vscroll.SetVisible(true)
  287. }
  288. // setVerticalScrollbarVisible sets the horizontal scrollbar visible, creating and initializing it if it's the first time
  289. func (s *Scroller) setHorizontalScrollbarVisible() {
  290. if s.hscroll == nil {
  291. s.hscroll = NewHScrollBar(0, s.style.HorizontalScrollbar.Broadness)
  292. s.hscroll.applyStyle(&s.style.HorizontalScrollbar.ScrollBarStyle)
  293. s.hscroll.Subscribe(OnChange, s.onScrollBarEvent)
  294. s.Add(s.hscroll)
  295. }
  296. s.hscroll.SetVisible(true)
  297. }
  298. // updateScrollbarsVisibility updates the visibility of the scrollbars and corner panel, creating them if necessary.
  299. // This method should be called when either the target panel changes size or when either the scroll mode or
  300. // style of the Scroller changes.
  301. func (s *Scroller) updateScrollbarsVisibility() {
  302. // Obtain the size of the target panel
  303. targetWidth := s.target.TotalWidth()
  304. targetHeight := s.target.TotalHeight()
  305. // If vertical scrolling is enabled and the vertical scrollbar should be visible
  306. if (s.mode&ScrollVertical > 0) && (targetHeight > s.content.Height) {
  307. s.setVerticalScrollbarVisible()
  308. } else if s.vscroll != nil {
  309. s.vscroll.SetVisible(false)
  310. s.vscroll.SetValue(0)
  311. }
  312. // If horizontal scrolling is enabled and the horizontal scrollbar should be visible
  313. if (s.mode&ScrollHorizontal > 0) && (targetWidth > s.content.Width) {
  314. s.setHorizontalScrollbarVisible()
  315. } else if s.hscroll != nil {
  316. s.hscroll.SetVisible(false)
  317. s.hscroll.SetValue(0)
  318. }
  319. // If both scrollbars can be visible we need to check whether we should show the corner panel and also whether
  320. // any scrollbar's presence caused the other to be required. The latter is a literal and figurative edge case
  321. // that happens when the target panel is larger than the content in one dimension but smaller than the content
  322. // in the other, and in the dimension that it is smaller than the content, the difference is less than the width
  323. // of the scrollbar. In that case we need to show both scrollbars to allow viewing of the complete target panel.
  324. if s.mode == ScrollBoth {
  325. vScrollVisible := (s.vscroll != nil) && s.vscroll.Visible()
  326. hScrollVisible := (s.hscroll != nil) && s.hscroll.Visible()
  327. // Check if adding any of the scrollbars ended up covering an edge of the target. If that's the case,
  328. // then show the other scrollbar as well (if the covering scrollbar's style is set to non-overlapping).
  329. // If the vertical scrollbar is visible and covering the target (and its style is not set to overlap)
  330. if vScrollVisible && (targetWidth > (s.content.Width - s.vscroll.width)) && !s.style.VerticalScrollbar.OverlapContent {
  331. s.setHorizontalScrollbarVisible() // Show the other scrollbar too
  332. }
  333. // If the horizontal scrollbar is visible and covering the target (and its style is not set to overlap)
  334. if hScrollVisible && (targetHeight > (s.content.Height - s.hscroll.height)) && !s.style.HorizontalScrollbar.OverlapContent {
  335. s.setVerticalScrollbarVisible() // Show the other scrollbar too
  336. }
  337. // Update visibility variables since they may have changed
  338. vScrollVisible = (s.vscroll != nil) && s.vscroll.Visible()
  339. hScrollVisible = (s.hscroll != nil) && s.hscroll.Visible()
  340. // If both vertical and horizontal scrolling is enabled, and the style specifies no interlocking
  341. // and a corner panel, and both scrollbars are visible - then the corner panel should be visible
  342. if (s.style.ScrollbarInterlocking == ScrollbarInterlockingNone) && s.style.CornerCovered && vScrollVisible && hScrollVisible {
  343. if s.corner == nil {
  344. s.corner = NewPanel(s.vscroll.width, s.hscroll.height)
  345. s.corner.ApplyStyle(&s.style.CornerPanel)
  346. s.Add(s.corner)
  347. }
  348. s.corner.SetVisible(true)
  349. } else if s.corner != nil {
  350. s.corner.SetVisible(false)
  351. }
  352. }
  353. }
  354. // onScrollEvent is called when the scrollbar value changes
  355. func (s *Scroller) onScrollBarEvent(evname string, ev interface{}) {
  356. s.recalc()
  357. }
  358. // recalc recalculates the positions and sizes of the scrollbars and corner panel,
  359. // updates the size of the scrollbar buttons, and repositions the target panel
  360. func (s *Scroller) recalc() {
  361. // The multipliers of the scrollbars' [0,1] values.
  362. // After applied, they will give the correct target panel position.
  363. // They can be thought of as the range of motion of the target panel in each axis
  364. multHeight := s.target.TotalHeight() - s.content.Height
  365. multWidth := s.target.TotalWidth() - s.content.Width
  366. var targetX, targetY float32
  367. var offsetX, offsetY float32
  368. vScrollVisible := (s.mode&ScrollVertical > 0) && (s.vscroll != nil) && s.vscroll.Visible()
  369. hScrollVisible := (s.mode&ScrollHorizontal > 0) && (s.hscroll != nil) && s.hscroll.Visible()
  370. // If the vertical scrollbar is visible
  371. if vScrollVisible {
  372. s.recalcV() // Recalculate scrollbar size/position (checks for the other scrollbar's presence)
  373. targetY = -float32(s.vscroll.Value())
  374. // If we don't want it to overlap the content area
  375. if s.style.VerticalScrollbar.OverlapContent == false {
  376. // Increase the target's range of X motion by the width of the vertical scrollbar
  377. multWidth += s.vscroll.width
  378. // If the vertical scrollbar is on the left we also want to add an offset to the target panel
  379. if s.style.VerticalScrollbar.Position == ScrollbarLeft {
  380. offsetX += s.vscroll.width
  381. }
  382. }
  383. }
  384. // If the horizontal scrollbar is visible
  385. if hScrollVisible {
  386. s.recalcH() // Recalculate scrollbar size/position (checks for the other scrollbar's presence)
  387. targetX = -float32(s.hscroll.Value())
  388. // If we don't want it to overlap the content area
  389. if s.style.HorizontalScrollbar.OverlapContent == false {
  390. // Increase the target's range of Y motion by the height of the horizontal scrollbar
  391. multHeight += s.hscroll.height
  392. // If the horizontal scrollbar is on the top we also want to add an offset to the target panel
  393. if s.style.HorizontalScrollbar.Position == ScrollbarTop {
  394. offsetY += s.hscroll.height
  395. }
  396. }
  397. }
  398. // Reposition the target panel
  399. s.target.SetPosition(targetX*multWidth+offsetX, targetY*multHeight+offsetY)
  400. // If the corner panel should be visible, update its position and size
  401. if (s.mode == ScrollBoth) && (s.style.ScrollbarInterlocking == ScrollbarInterlockingNone) &&
  402. (s.style.CornerCovered == true) && vScrollVisible && hScrollVisible {
  403. s.corner.SetPosition(s.vscroll.Position().X, s.hscroll.Position().Y)
  404. s.corner.SetSize(s.vscroll.width, s.hscroll.height)
  405. }
  406. }
  407. // recalcV recalculates the size and position of the vertical scrollbar
  408. func (s *Scroller) recalcV() {
  409. // Position the vertical scrollbar horizontally according to the style
  410. var vscrollPosX float32 // = 0 (ScrollbarLeft)
  411. if s.style.VerticalScrollbar.Position == ScrollbarRight {
  412. vscrollPosX = s.ContentWidth() - s.vscroll.width
  413. }
  414. // Start with the default Y position and height of the vertical scrollbar
  415. var vscrollPosY float32
  416. vscrollHeight := s.ContentHeight()
  417. viewHeight := s.ContentHeight()
  418. // If the horizontal scrollbar is present - reduce the viewHeight ...
  419. if (s.hscroll != nil) && s.hscroll.Visible() {
  420. if s.style.HorizontalScrollbar.OverlapContent == false {
  421. viewHeight -= s.hscroll.height
  422. }
  423. // If the interlocking style doesn't give precedence to the vertical scrollbar - reduce the scrollbar height ...
  424. if s.style.ScrollbarInterlocking != ScrollbarInterlockingVertical {
  425. vscrollHeight -= s.hscroll.height
  426. // If the horizontal scrollbar is on top - offset the vertical scrollbar vertically
  427. if s.style.HorizontalScrollbar.Position == ScrollbarTop {
  428. vscrollPosY = s.hscroll.height
  429. }
  430. }
  431. }
  432. // Adjust the scrollbar button size to the correct proportion proportion according to the style
  433. if s.style.VerticalScrollbar.AutoSizeButton {
  434. s.vscroll.SetButtonSize(vscrollHeight * viewHeight / s.target.TotalHeight())
  435. }
  436. // Update the position and height of the vertical scrollbar
  437. s.vscroll.SetPosition(vscrollPosX, vscrollPosY)
  438. s.vscroll.SetHeight(vscrollHeight)
  439. }
  440. // recalcH recalculates the size and position of the horizontal scrollbar
  441. func (s *Scroller) recalcH() {
  442. // Position the horizontal scrollbar vertically according to the style
  443. var hscrollPosY float32 // = 0 (ScrollbarTop)
  444. if s.style.HorizontalScrollbar.Position == ScrollbarBottom {
  445. hscrollPosY = s.ContentHeight() - s.hscroll.height
  446. }
  447. // Start with default X position and width of the horizontal scrollbar
  448. var hscrollPosX float32
  449. hscrollWidth := s.ContentWidth()
  450. viewWidth := s.ContentWidth()
  451. // If the vertical scrollbar is present - reduce the viewWidth ...
  452. if (s.vscroll != nil) && s.vscroll.Visible() {
  453. if s.style.VerticalScrollbar.OverlapContent == false {
  454. viewWidth -= s.vscroll.width
  455. }
  456. // If the interlocking style doesn't give precedence to the horizontal scrollbar - reduce the scrollbar width ...
  457. if s.style.ScrollbarInterlocking != ScrollbarInterlockingHorizontal {
  458. hscrollWidth -= s.vscroll.width
  459. // If the vertical scrollbar is on the left - offset the horizontal scrollbar horizontally
  460. if s.style.VerticalScrollbar.Position == ScrollbarLeft {
  461. hscrollPosX = s.vscroll.width
  462. }
  463. }
  464. }
  465. // Adjust the scrollbar button size to the correct proportion proportion according to the style
  466. if s.style.HorizontalScrollbar.AutoSizeButton {
  467. s.hscroll.SetButtonSize(hscrollWidth * viewWidth / s.target.TotalWidth())
  468. }
  469. // Update the position and width of the horizontal scrollbar
  470. s.hscroll.SetPosition(hscrollPosX, hscrollPosY)
  471. s.hscroll.SetWidth(hscrollWidth)
  472. }
  473. // Update updates the visibility of the scrollbars, corner panel, and then recalculates
  474. func (s *Scroller) Update() {
  475. s.updateScrollbarsVisibility()
  476. s.recalc()
  477. }
  478. // TODO - if the style is changed this needs to be called to update the scrollbars and corner panel
  479. func (s *Scroller) applyStyle(ss *ScrollerStyle) {
  480. s.style = ss
  481. s.vscroll.applyStyle(&s.style.VerticalScrollbar.ScrollBarStyle)
  482. s.hscroll.applyStyle(&s.style.HorizontalScrollbar.ScrollBarStyle)
  483. s.corner.ApplyStyle(&s.style.CornerPanel)
  484. s.Update()
  485. }