2023-01-19 11:07:27 -07:00
|
|
|
package basic
|
|
|
|
|
|
|
|
import "image"
|
|
|
|
import "git.tebibyte.media/sashakoshka/tomo"
|
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/theme"
|
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
|
|
|
|
2023-01-29 23:30:13 -07:00
|
|
|
var scrollContainerCase = theme.C("basic", "scrollContainer")
|
|
|
|
var scrollBarHorizontalCase = theme.C("basic", "scrollBarHorizontal")
|
|
|
|
var scrollBarVerticalCase = theme.C("basic", "scrollBarVertical")
|
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
// ScrollContainer is a container that is capable of holding a scrollable
|
|
|
|
// element.
|
|
|
|
type ScrollContainer struct {
|
|
|
|
*core.Core
|
|
|
|
core core.CoreControl
|
|
|
|
selected bool
|
2023-01-20 13:35:43 -07:00
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
child tomo.Scrollable
|
2023-01-20 13:35:43 -07:00
|
|
|
childWidth, childHeight int
|
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
horizontal struct {
|
2023-01-20 13:35:43 -07:00
|
|
|
exists bool
|
2023-01-19 11:07:27 -07:00
|
|
|
enabled bool
|
2023-01-20 23:15:30 -07:00
|
|
|
dragging bool
|
2023-01-20 23:24:24 -07:00
|
|
|
dragOffset int
|
2023-01-20 13:35:43 -07:00
|
|
|
gutter image.Rectangle
|
2023-01-28 23:49:01 -07:00
|
|
|
track image.Rectangle
|
2023-01-20 13:35:43 -07:00
|
|
|
bar image.Rectangle
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
vertical struct {
|
2023-01-20 13:35:43 -07:00
|
|
|
exists bool
|
2023-01-19 11:07:27 -07:00
|
|
|
enabled bool
|
2023-01-20 23:15:30 -07:00
|
|
|
dragging bool
|
2023-01-20 23:24:24 -07:00
|
|
|
dragOffset int
|
2023-01-20 13:35:43 -07:00
|
|
|
gutter image.Rectangle
|
2023-01-28 23:49:01 -07:00
|
|
|
track image.Rectangle
|
2023-01-20 13:35:43 -07:00
|
|
|
bar image.Rectangle
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
2023-01-19 14:49:34 -07:00
|
|
|
|
2023-01-19 15:35:19 -07:00
|
|
|
onSelectionRequest func () (granted bool)
|
|
|
|
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewScrollContainer creates a new scroll container with the specified scroll
|
|
|
|
// bars.
|
|
|
|
func NewScrollContainer (horizontal, vertical bool) (element *ScrollContainer) {
|
|
|
|
element = &ScrollContainer { }
|
|
|
|
element.Core, element.core = core.NewCore(element)
|
|
|
|
element.updateMinimumSize()
|
2023-01-20 13:35:43 -07:00
|
|
|
element.horizontal.exists = horizontal
|
|
|
|
element.vertical.exists = vertical
|
2023-01-19 11:07:27 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize resizes the scroll box.
|
|
|
|
func (element *ScrollContainer) Resize (width, height int) {
|
|
|
|
element.core.AllocateCanvas(width, height)
|
|
|
|
element.recalculate()
|
|
|
|
element.child.Resize (
|
2023-01-20 13:35:43 -07:00
|
|
|
element.childWidth,
|
|
|
|
element.childHeight)
|
2023-01-19 11:07:27 -07:00
|
|
|
element.draw()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adopt adds a scrollable element to the scroll container. The container can
|
|
|
|
// only contain one scrollable element at a time, and when a new one is adopted
|
|
|
|
// it replaces the last one.
|
|
|
|
func (element *ScrollContainer) Adopt (child tomo.Scrollable) {
|
|
|
|
// disown previous child if it exists
|
|
|
|
if element.child != nil {
|
2023-01-19 14:49:34 -07:00
|
|
|
element.clearChildEventHandlers(child)
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// adopt new child
|
|
|
|
element.child = child
|
|
|
|
if child != nil {
|
2023-01-19 15:35:19 -07:00
|
|
|
child.OnDamage(element.childDamageCallback)
|
|
|
|
child.OnMinimumSizeChange(element.updateMinimumSize)
|
2023-01-20 13:35:43 -07:00
|
|
|
child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback)
|
2023-01-19 15:35:19 -07:00
|
|
|
if newChild, ok := child.(tomo.Selectable); ok {
|
|
|
|
newChild.OnSelectionRequest (
|
|
|
|
element.childSelectionRequestCallback)
|
|
|
|
newChild.OnSelectionMotionRequest (
|
|
|
|
element.childSelectionMotionRequestCallback)
|
|
|
|
}
|
2023-01-19 11:07:27 -07:00
|
|
|
|
|
|
|
// TODO: somehow inform the core that we do not in fact want to
|
|
|
|
// redraw the element.
|
|
|
|
element.updateMinimumSize()
|
2023-01-20 13:35:43 -07:00
|
|
|
|
|
|
|
element.horizontal.enabled,
|
|
|
|
element.vertical.enabled = element.child.ScrollAxes()
|
2023-01-19 11:07:27 -07:00
|
|
|
|
|
|
|
if element.core.HasImage() {
|
|
|
|
element.child.Resize (
|
2023-01-20 13:35:43 -07:00
|
|
|
element.childWidth,
|
|
|
|
element.childHeight)
|
2023-01-20 13:52:46 -07:00
|
|
|
element.core.DamageAll()
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-20 15:40:28 -07:00
|
|
|
func (element *ScrollContainer) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) {
|
2023-01-19 15:35:19 -07:00
|
|
|
if child, ok := element.child.(tomo.KeyboardTarget); ok {
|
2023-01-20 15:40:28 -07:00
|
|
|
child.HandleKeyDown(key, modifiers)
|
2023-01-19 15:35:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) {
|
|
|
|
if child, ok := element.child.(tomo.KeyboardTarget); ok {
|
|
|
|
child.HandleKeyUp(key, modifiers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 16:03:50 -07:00
|
|
|
func (element *ScrollContainer) HandleMouseDown (x, y int, button tomo.Button) {
|
2023-01-20 23:15:30 -07:00
|
|
|
point := image.Pt(x, y)
|
2023-01-20 23:24:24 -07:00
|
|
|
if point.In(element.horizontal.bar) {
|
2023-01-20 23:15:30 -07:00
|
|
|
element.horizontal.dragging = true
|
2023-01-20 23:24:24 -07:00
|
|
|
element.horizontal.dragOffset =
|
|
|
|
point.Sub(element.horizontal.bar.Min).X
|
2023-01-20 23:15:30 -07:00
|
|
|
element.dragHorizontalBar(point)
|
|
|
|
|
2023-01-21 19:05:51 -07:00
|
|
|
} else if point.In(element.horizontal.gutter) {
|
|
|
|
// FIXME: x backend and scroll container should pull these
|
|
|
|
// values from the same place
|
|
|
|
if x > element.horizontal.bar.Min.X {
|
|
|
|
element.scrollChildBy(16, 0)
|
|
|
|
} else {
|
|
|
|
element.scrollChildBy(-16, 0)
|
|
|
|
}
|
|
|
|
|
2023-01-20 23:24:24 -07:00
|
|
|
} else if point.In(element.vertical.bar) {
|
2023-01-20 23:15:30 -07:00
|
|
|
element.vertical.dragging = true
|
2023-01-20 23:24:24 -07:00
|
|
|
element.vertical.dragOffset =
|
|
|
|
point.Sub(element.vertical.bar.Min).Y
|
2023-01-20 23:15:30 -07:00
|
|
|
element.dragVerticalBar(point)
|
|
|
|
|
2023-01-21 19:05:51 -07:00
|
|
|
} else if point.In(element.vertical.gutter) {
|
|
|
|
if y > element.vertical.bar.Min.Y {
|
|
|
|
element.scrollChildBy(0, 16)
|
|
|
|
} else {
|
|
|
|
element.scrollChildBy(0, -16)
|
|
|
|
}
|
|
|
|
|
2023-01-20 23:15:30 -07:00
|
|
|
} else if child, ok := element.child.(tomo.MouseTarget); ok {
|
2023-01-19 16:03:50 -07:00
|
|
|
child.HandleMouseDown(x, y, button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleMouseUp (x, y int, button tomo.Button) {
|
2023-01-20 23:15:30 -07:00
|
|
|
if element.horizontal.dragging {
|
|
|
|
element.horizontal.dragging = false
|
2023-01-21 18:58:25 -07:00
|
|
|
element.drawHorizontalBar()
|
|
|
|
element.core.DamageRegion(element.horizontal.bar)
|
2023-01-20 23:15:30 -07:00
|
|
|
|
|
|
|
} else if element.vertical.dragging {
|
|
|
|
element.vertical.dragging = false
|
2023-01-21 18:58:25 -07:00
|
|
|
element.drawVerticalBar()
|
|
|
|
element.core.DamageRegion(element.vertical.bar)
|
2023-01-20 23:15:30 -07:00
|
|
|
|
|
|
|
} else if child, ok := element.child.(tomo.MouseTarget); ok {
|
2023-01-19 16:03:50 -07:00
|
|
|
child.HandleMouseUp(x, y, button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleMouseMove (x, y int) {
|
2023-01-20 23:15:30 -07:00
|
|
|
if element.horizontal.dragging {
|
|
|
|
element.dragHorizontalBar(image.Pt(x, y))
|
|
|
|
|
|
|
|
} else if element.vertical.dragging {
|
|
|
|
element.dragVerticalBar(image.Pt(x, y))
|
|
|
|
|
|
|
|
} else if child, ok := element.child.(tomo.MouseTarget); ok {
|
2023-01-19 16:03:50 -07:00
|
|
|
child.HandleMouseMove(x, y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleMouseScroll (
|
|
|
|
x, y int,
|
|
|
|
deltaX, deltaY float64,
|
|
|
|
) {
|
2023-01-21 19:05:51 -07:00
|
|
|
element.scrollChildBy(int(deltaX), int(deltaY))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) scrollChildBy (x, y int) {
|
|
|
|
if element.child == nil { return }
|
|
|
|
scrollPoint :=
|
|
|
|
element.child.ScrollViewportBounds().Min.
|
|
|
|
Add(image.Pt(x, y))
|
2023-01-20 14:44:07 -07:00
|
|
|
element.child.ScrollTo(scrollPoint)
|
2023-01-19 16:03:50 -07:00
|
|
|
}
|
|
|
|
|
2023-01-19 15:35:19 -07:00
|
|
|
func (element *ScrollContainer) Selected () (selected bool) {
|
|
|
|
return element.selected
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) Select () {
|
|
|
|
if element.onSelectionRequest != nil {
|
|
|
|
element.onSelectionRequest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleSelection (
|
|
|
|
direction tomo.SelectionDirection,
|
|
|
|
) (
|
|
|
|
accepted bool,
|
|
|
|
) {
|
|
|
|
if child, ok := element.child.(tomo.Selectable); ok {
|
|
|
|
element.selected = true
|
|
|
|
return child.HandleSelection(direction)
|
|
|
|
} else {
|
|
|
|
element.selected = false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) HandleDeselection () {
|
|
|
|
if child, ok := element.child.(tomo.Selectable); ok {
|
|
|
|
child.HandleDeselection()
|
|
|
|
}
|
|
|
|
element.selected = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) OnSelectionRequest (callback func () (granted bool)) {
|
|
|
|
element.onSelectionRequest = callback
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) OnSelectionMotionRequest (
|
|
|
|
callback func (direction tomo.SelectionDirection) (granted bool),
|
|
|
|
) {
|
|
|
|
element.onSelectionMotionRequest = callback
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) childDamageCallback (region tomo.Canvas) {
|
|
|
|
element.core.DamageRegion(artist.Paste(element, region, image.Point { }))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) childSelectionRequestCallback () (granted bool) {
|
|
|
|
child, ok := element.child.(tomo.Selectable)
|
|
|
|
if !ok { return false }
|
|
|
|
if element.onSelectionRequest != nil && element.onSelectionRequest() {
|
|
|
|
child.HandleSelection(tomo.SelectionDirectionNeutral)
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) childSelectionMotionRequestCallback (
|
|
|
|
direction tomo.SelectionDirection,
|
|
|
|
) (
|
|
|
|
granted bool,
|
|
|
|
) {
|
|
|
|
if element.onSelectionMotionRequest == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return element.onSelectionMotionRequest(direction)
|
|
|
|
}
|
|
|
|
|
2023-01-20 13:35:43 -07:00
|
|
|
func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) {
|
2023-01-19 14:49:34 -07:00
|
|
|
child.OnDamage(nil)
|
|
|
|
child.OnMinimumSizeChange(nil)
|
2023-01-20 13:35:43 -07:00
|
|
|
child.OnScrollBoundsChange(nil)
|
2023-01-19 14:49:34 -07:00
|
|
|
if child0, ok := child.(tomo.Selectable); ok {
|
|
|
|
child0.OnSelectionRequest(nil)
|
|
|
|
child0.OnSelectionMotionRequest(nil)
|
|
|
|
if child0.Selected() {
|
|
|
|
child0.HandleDeselection()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if child0, ok := child.(tomo.Flexible); ok {
|
|
|
|
child0.OnFlexibleHeightChange(nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
func (element *ScrollContainer) recalculate () {
|
2023-01-29 23:30:13 -07:00
|
|
|
_, gutterInsetHorizontal := theme.GutterPattern(theme.PatternState {
|
|
|
|
Case: scrollBarHorizontalCase,
|
|
|
|
})
|
|
|
|
_, gutterInsetVertical := theme.GutterPattern(theme.PatternState {
|
|
|
|
Case: scrollBarHorizontalCase,
|
|
|
|
})
|
2023-01-28 23:49:01 -07:00
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
horizontal := &element.horizontal
|
|
|
|
vertical := &element.vertical
|
|
|
|
bounds := element.Bounds()
|
2023-01-29 23:30:13 -07:00
|
|
|
thicknessHorizontal :=
|
|
|
|
theme.HandleWidth() +
|
|
|
|
gutterInsetHorizontal[3] +
|
|
|
|
gutterInsetHorizontal[1]
|
|
|
|
thicknessVertical :=
|
|
|
|
theme.HandleWidth() +
|
|
|
|
gutterInsetVertical[3] +
|
|
|
|
gutterInsetVertical[1]
|
2023-01-19 11:07:27 -07:00
|
|
|
|
2023-01-20 13:35:43 -07:00
|
|
|
// calculate child size
|
|
|
|
element.childWidth = bounds.Dx()
|
|
|
|
element.childHeight = bounds.Dy()
|
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
// reset bounds
|
2023-01-20 13:35:43 -07:00
|
|
|
horizontal.gutter = image.Rectangle { }
|
|
|
|
vertical.gutter = image.Rectangle { }
|
|
|
|
horizontal.bar = image.Rectangle { }
|
|
|
|
vertical.bar = image.Rectangle { }
|
|
|
|
|
|
|
|
// if enabled, give substance to the gutters
|
|
|
|
if horizontal.exists {
|
2023-01-29 23:30:13 -07:00
|
|
|
horizontal.gutter.Min.Y = bounds.Max.Y - thicknessHorizontal
|
2023-01-20 13:35:43 -07:00
|
|
|
horizontal.gutter.Max.X = bounds.Max.X
|
|
|
|
horizontal.gutter.Max.Y = bounds.Max.Y
|
|
|
|
if vertical.exists {
|
2023-01-29 23:30:13 -07:00
|
|
|
horizontal.gutter.Max.X -= thicknessVertical
|
2023-01-20 13:35:43 -07:00
|
|
|
}
|
2023-01-29 23:30:13 -07:00
|
|
|
element.childHeight -= thicknessHorizontal
|
|
|
|
horizontal.track = gutterInsetHorizontal.Apply(horizontal.gutter)
|
2023-01-20 13:35:43 -07:00
|
|
|
}
|
|
|
|
if vertical.exists {
|
2023-01-29 23:30:13 -07:00
|
|
|
vertical.gutter.Min.X = bounds.Max.X - thicknessVertical
|
2023-01-20 13:35:43 -07:00
|
|
|
vertical.gutter.Max.X = bounds.Max.X
|
|
|
|
vertical.gutter.Max.Y = bounds.Max.Y
|
|
|
|
if horizontal.exists {
|
2023-01-29 23:30:13 -07:00
|
|
|
vertical.gutter.Max.Y -= thicknessHorizontal
|
2023-01-20 13:35:43 -07:00
|
|
|
}
|
2023-01-29 23:30:13 -07:00
|
|
|
element.childWidth -= thicknessVertical
|
|
|
|
vertical.track = gutterInsetVertical.Apply(vertical.gutter)
|
2023-01-20 13:35:43 -07:00
|
|
|
}
|
2023-01-19 11:07:27 -07:00
|
|
|
|
2023-01-20 13:35:43 -07:00
|
|
|
// if enabled, calculate the positions of the bars
|
|
|
|
contentBounds := element.child.ScrollContentBounds()
|
|
|
|
viewportBounds := element.child.ScrollViewportBounds()
|
|
|
|
if horizontal.exists && horizontal.enabled {
|
2023-01-28 23:49:01 -07:00
|
|
|
horizontal.bar.Min.Y = horizontal.track.Min.Y
|
|
|
|
horizontal.bar.Max.Y = horizontal.track.Max.Y
|
2023-01-20 13:35:43 -07:00
|
|
|
|
2023-01-28 23:49:01 -07:00
|
|
|
scale := float64(horizontal.track.Dx()) /
|
2023-01-20 13:35:43 -07:00
|
|
|
float64(contentBounds.Dx())
|
|
|
|
horizontal.bar.Min.X = int(float64(viewportBounds.Min.X) * scale)
|
|
|
|
horizontal.bar.Max.X = int(float64(viewportBounds.Max.X) * scale)
|
|
|
|
|
2023-01-28 23:49:01 -07:00
|
|
|
horizontal.bar.Min.X += horizontal.track.Min.X
|
|
|
|
horizontal.bar.Max.X += horizontal.track.Min.X
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
2023-01-20 13:35:43 -07:00
|
|
|
if vertical.exists && vertical.enabled {
|
2023-01-28 23:49:01 -07:00
|
|
|
vertical.bar.Min.X = vertical.track.Min.X
|
|
|
|
vertical.bar.Max.X = vertical.track.Max.X
|
2023-01-20 13:35:43 -07:00
|
|
|
|
2023-01-28 23:49:01 -07:00
|
|
|
scale := float64(vertical.track.Dy()) /
|
2023-01-20 13:35:43 -07:00
|
|
|
float64(contentBounds.Dy())
|
|
|
|
vertical.bar.Min.Y = int(float64(viewportBounds.Min.Y) * scale)
|
|
|
|
vertical.bar.Max.Y = int(float64(viewportBounds.Max.Y) * scale)
|
|
|
|
|
2023-01-28 23:49:01 -07:00
|
|
|
vertical.bar.Min.Y += vertical.track.Min.Y
|
|
|
|
vertical.bar.Max.Y += vertical.track.Min.Y
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
2023-01-20 13:35:43 -07:00
|
|
|
// if the scroll bars are out of bounds, don't display them.
|
2023-01-28 23:49:01 -07:00
|
|
|
if horizontal.bar.Dx() >= horizontal.track.Dx() {
|
2023-01-20 13:35:43 -07:00
|
|
|
horizontal.bar = image.Rectangle { }
|
|
|
|
}
|
2023-01-28 23:49:01 -07:00
|
|
|
if vertical.bar.Dy() >= vertical.track.Dy() {
|
2023-01-20 13:35:43 -07:00
|
|
|
vertical.bar = image.Rectangle { }
|
|
|
|
}
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) draw () {
|
|
|
|
artist.Paste(element.core, element.child, image.Point { })
|
2023-01-29 23:30:13 -07:00
|
|
|
deadPattern, _ := theme.DeadPattern(theme.PatternState {
|
|
|
|
Case: scrollContainerCase,
|
|
|
|
})
|
2023-01-19 16:03:50 -07:00
|
|
|
artist.FillRectangle (
|
2023-01-28 23:49:01 -07:00
|
|
|
element, deadPattern,
|
2023-01-19 16:03:50 -07:00
|
|
|
image.Rect (
|
2023-01-20 13:35:43 -07:00
|
|
|
element.vertical.gutter.Min.X,
|
|
|
|
element.horizontal.gutter.Min.Y,
|
|
|
|
element.vertical.gutter.Max.X,
|
|
|
|
element.horizontal.gutter.Max.Y))
|
2023-01-19 16:29:57 -07:00
|
|
|
element.drawHorizontalBar()
|
|
|
|
element.drawVerticalBar()
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) drawHorizontalBar () {
|
2023-01-28 23:49:01 -07:00
|
|
|
gutterPattern, _ := theme.GutterPattern (theme.PatternState {
|
2023-01-29 23:30:13 -07:00
|
|
|
Case: scrollBarHorizontalCase,
|
2023-01-28 23:49:01 -07:00
|
|
|
Disabled: !element.horizontal.enabled,
|
|
|
|
})
|
|
|
|
artist.FillRectangle(element, gutterPattern, element.horizontal.gutter)
|
|
|
|
|
|
|
|
handlePattern, _ := theme.HandlePattern (theme.PatternState {
|
2023-01-29 23:30:13 -07:00
|
|
|
Case: scrollBarHorizontalCase,
|
2023-01-28 23:49:01 -07:00
|
|
|
Disabled: !element.horizontal.enabled,
|
|
|
|
Pressed: element.horizontal.dragging,
|
|
|
|
})
|
|
|
|
artist.FillRectangle(element, handlePattern, element.horizontal.bar)
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) drawVerticalBar () {
|
2023-01-28 23:49:01 -07:00
|
|
|
gutterPattern, _ := theme.GutterPattern (theme.PatternState {
|
2023-01-29 23:30:13 -07:00
|
|
|
Case: scrollBarVerticalCase,
|
2023-01-28 23:49:01 -07:00
|
|
|
Disabled: !element.vertical.enabled,
|
|
|
|
})
|
|
|
|
artist.FillRectangle(element, gutterPattern, element.vertical.gutter)
|
|
|
|
|
|
|
|
handlePattern, _ := theme.HandlePattern (theme.PatternState {
|
2023-01-29 23:30:13 -07:00
|
|
|
Case: scrollBarVerticalCase,
|
2023-01-28 23:49:01 -07:00
|
|
|
Disabled: !element.vertical.enabled,
|
|
|
|
Pressed: element.vertical.dragging,
|
|
|
|
})
|
|
|
|
artist.FillRectangle(element, handlePattern, element.vertical.bar)
|
2023-01-19 11:07:27 -07:00
|
|
|
}
|
|
|
|
|
2023-01-20 23:15:30 -07:00
|
|
|
func (element *ScrollContainer) dragHorizontalBar (mousePosition image.Point) {
|
|
|
|
scrollX :=
|
|
|
|
float64(element.child.ScrollContentBounds().Dx()) /
|
2023-01-28 23:49:01 -07:00
|
|
|
float64(element.horizontal.track.Dx()) *
|
2023-01-20 23:24:24 -07:00
|
|
|
float64(mousePosition.X - element.horizontal.dragOffset)
|
2023-01-20 23:15:30 -07:00
|
|
|
scrollY := element.child.ScrollViewportBounds().Min.Y
|
|
|
|
element.child.ScrollTo(image.Pt(int(scrollX), scrollY))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (element *ScrollContainer) dragVerticalBar (mousePosition image.Point) {
|
|
|
|
scrollY :=
|
|
|
|
float64(element.child.ScrollContentBounds().Dy()) /
|
2023-01-28 23:49:01 -07:00
|
|
|
float64(element.vertical.track.Dy()) *
|
2023-01-20 23:24:24 -07:00
|
|
|
float64(mousePosition.Y - element.vertical.dragOffset)
|
2023-01-20 23:15:30 -07:00
|
|
|
scrollX := element.child.ScrollViewportBounds().Min.X
|
|
|
|
element.child.ScrollTo(image.Pt(scrollX, int(scrollY)))
|
|
|
|
}
|
|
|
|
|
2023-01-19 11:07:27 -07:00
|
|
|
func (element *ScrollContainer) updateMinimumSize () {
|
2023-01-29 23:30:13 -07:00
|
|
|
_, gutterInsetHorizontal := theme.GutterPattern(theme.PatternState {
|
|
|
|
Case: scrollBarHorizontalCase,
|
|
|
|
})
|
|
|
|
_, gutterInsetVertical := theme.GutterPattern(theme.PatternState {
|
|
|
|
Case: scrollBarHorizontalCase,
|
|
|
|
})
|
|
|
|
|
|
|
|
thicknessHorizontal :=
|
|
|
|
theme.HandleWidth() +
|
|
|
|
gutterInsetHorizontal[3] +
|
|
|
|
gutterInsetHorizontal[1]
|
|
|
|
thicknessVertical :=
|
|
|
|
theme.HandleWidth() +
|
|
|
|
gutterInsetVertical[3] +
|
|
|
|
gutterInsetVertical[1]
|
2023-01-28 23:49:01 -07:00
|
|
|
|
2023-01-29 23:30:13 -07:00
|
|
|
width := thicknessHorizontal
|
|
|
|
height := thicknessVertical
|
2023-01-19 11:07:27 -07:00
|
|
|
if element.child != nil {
|
|
|
|
childWidth, childHeight := element.child.MinimumSize()
|
|
|
|
width += childWidth
|
|
|
|
height += childHeight
|
|
|
|
}
|
|
|
|
element.core.SetMinimumSize(width, height)
|
|
|
|
}
|
2023-01-20 13:35:43 -07:00
|
|
|
|
|
|
|
func (element *ScrollContainer) childScrollBoundsChangeCallback () {
|
|
|
|
element.horizontal.enabled,
|
|
|
|
element.vertical.enabled = element.child.ScrollAxes()
|
|
|
|
if element.core.HasImage() {
|
|
|
|
element.recalculate()
|
|
|
|
element.drawHorizontalBar()
|
|
|
|
element.drawVerticalBar()
|
2023-01-20 13:52:46 -07:00
|
|
|
element.core.DamageRegion(element.horizontal.gutter)
|
|
|
|
element.core.DamageRegion(element.vertical.gutter)
|
2023-01-20 13:35:43 -07:00
|
|
|
}
|
|
|
|
}
|