scroller.go 24 KB

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