Scrollbar drawing

This commit is contained in:
Sasha Koshka 2023-01-20 15:35:43 -05:00
parent 3acbe40665
commit faf38b96c2
3 changed files with 112 additions and 35 deletions

View File

@ -168,6 +168,6 @@ type Scrollable interface {
ScrollAxes () (horizontal, vertical bool) ScrollAxes () (horizontal, vertical bool)
// OnScrollBoundsChange sets a function to be called when the element's // OnScrollBoundsChange sets a function to be called when the element's
// ScrollContentBounds or ScrollViewportBounds are changed. // ScrollContentBounds, ScrollViewportBounds, or ScrollAxes are changed.
OnScrollBoundsChange (callback func ()) OnScrollBoundsChange (callback func ())
} }

View File

@ -12,16 +12,22 @@ type ScrollContainer struct {
*core.Core *core.Core
core core.CoreControl core core.CoreControl
selected bool selected bool
child tomo.Scrollable child tomo.Scrollable
childWidth, childHeight int
horizontal struct { horizontal struct {
exists bool
enabled bool enabled bool
bounds image.Rectangle gutter image.Rectangle
bar image.Rectangle
} }
vertical struct { vertical struct {
exists bool
enabled bool enabled bool
bounds image.Rectangle gutter image.Rectangle
bar image.Rectangle
} }
onSelectionRequest func () (granted bool) onSelectionRequest func () (granted bool)
@ -34,8 +40,8 @@ func NewScrollContainer (horizontal, vertical bool) (element *ScrollContainer) {
element = &ScrollContainer { } element = &ScrollContainer { }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.updateMinimumSize() element.updateMinimumSize()
element.horizontal.enabled = horizontal element.horizontal.exists = horizontal
element.vertical.enabled = vertical element.vertical.exists = vertical
return return
} }
@ -44,8 +50,8 @@ func (element *ScrollContainer) Resize (width, height int) {
element.core.AllocateCanvas(width, height) element.core.AllocateCanvas(width, height)
element.recalculate() element.recalculate()
element.child.Resize ( element.child.Resize (
element.vertical.bounds.Min.X, element.childWidth,
element.horizontal.bounds.Min.Y) element.childHeight)
element.draw() element.draw()
} }
@ -63,6 +69,7 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) {
if child != nil { if child != nil {
child.OnDamage(element.childDamageCallback) child.OnDamage(element.childDamageCallback)
child.OnMinimumSizeChange(element.updateMinimumSize) child.OnMinimumSizeChange(element.updateMinimumSize)
child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback)
if newChild, ok := child.(tomo.Selectable); ok { if newChild, ok := child.(tomo.Selectable); ok {
newChild.OnSelectionRequest ( newChild.OnSelectionRequest (
element.childSelectionRequestCallback) element.childSelectionRequestCallback)
@ -74,11 +81,13 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) {
// redraw the element. // redraw the element.
element.updateMinimumSize() element.updateMinimumSize()
element.horizontal.enabled,
element.vertical.enabled = element.child.ScrollAxes()
if element.core.HasImage() { if element.core.HasImage() {
element.recalculate()
element.child.Resize ( element.child.Resize (
element.horizontal.bounds.Min.X, element.childWidth,
element.vertical.bounds.Min.X) element.childHeight)
element.draw() element.draw()
} }
} }
@ -192,9 +201,10 @@ func (element *ScrollContainer) childSelectionMotionRequestCallback (
return element.onSelectionMotionRequest(direction) return element.onSelectionMotionRequest(direction)
} }
func (element *ScrollContainer) clearChildEventHandlers (child tomo.Element) { func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) {
child.OnDamage(nil) child.OnDamage(nil)
child.OnMinimumSizeChange(nil) child.OnMinimumSizeChange(nil)
child.OnScrollBoundsChange(nil)
if child0, ok := child.(tomo.Selectable); ok { if child0, ok := child.(tomo.Selectable); ok {
child0.OnSelectionRequest(nil) child0.OnSelectionRequest(nil)
child0.OnSelectionMotionRequest(nil) child0.OnSelectionMotionRequest(nil)
@ -213,25 +223,71 @@ func (element *ScrollContainer) recalculate () {
bounds := element.Bounds() bounds := element.Bounds()
thickness := theme.Padding() * 2 thickness := theme.Padding() * 2
// calculate child size
element.childWidth = bounds.Dx()
element.childHeight = bounds.Dy()
// reset bounds // reset bounds
horizontal.bounds = image.Rectangle { } horizontal.gutter = image.Rectangle { }
vertical.bounds = image.Rectangle { } vertical.gutter = image.Rectangle { }
horizontal.bar = image.Rectangle { }
vertical.bar = image.Rectangle { }
// if enabled, give substance to the bars // if enabled, give substance to the gutters
if horizontal.enabled { if horizontal.exists {
horizontal.bounds.Max.X = bounds.Max.X - thickness horizontal.gutter.Min.Y = bounds.Max.Y - thickness
horizontal.bounds.Max.Y = thickness horizontal.gutter.Max.X = bounds.Max.X
horizontal.gutter.Max.Y = bounds.Max.Y
if vertical.exists {
horizontal.gutter.Max.X -= thickness
} }
if vertical.enabled { element.childHeight -= thickness
vertical.bounds.Max.X = thickness }
vertical.bounds.Max.Y = bounds.Max.Y - thickness if vertical.exists {
vertical.gutter.Min.X = bounds.Max.X - thickness
vertical.gutter.Max.X = bounds.Max.X
vertical.gutter.Max.Y = bounds.Max.Y
if horizontal.exists {
vertical.gutter.Max.Y -= thickness
}
element.childWidth -= thickness
} }
// move the bars to the edge of the element // if enabled, calculate the positions of the bars
horizontal.bounds.Min.Y += bounds.Max.Y - horizontal.bounds.Dy() contentBounds := element.child.ScrollContentBounds()
horizontal.bounds.Max.Y += bounds.Max.Y viewportBounds := element.child.ScrollViewportBounds()
vertical.bounds.Min.X += bounds.Max.X - vertical.bounds.Dx() if horizontal.exists && horizontal.enabled {
vertical.bounds.Max.X += bounds.Max.X horizontal.bar.Min.Y = horizontal.gutter.Min.Y
horizontal.bar.Max.Y = horizontal.gutter.Max.Y
scale := float64(horizontal.gutter.Dx()) /
float64(contentBounds.Dx())
horizontal.bar.Min.X = int(float64(viewportBounds.Min.X) * scale)
horizontal.bar.Max.X = int(float64(viewportBounds.Max.X) * scale)
horizontal.bar.Min.X += horizontal.gutter.Min.X
horizontal.bar.Max.X += horizontal.gutter.Min.X
}
if vertical.exists && vertical.enabled {
vertical.bar.Min.X = vertical.gutter.Min.X
vertical.bar.Max.X = vertical.gutter.Max.X
scale := float64(vertical.gutter.Dy()) /
float64(contentBounds.Dy())
vertical.bar.Min.Y = int(float64(viewportBounds.Min.Y) * scale)
vertical.bar.Max.Y = int(float64(viewportBounds.Max.Y) * scale)
vertical.bar.Min.Y += vertical.gutter.Min.Y
vertical.bar.Max.Y += vertical.gutter.Min.Y
}
// if the scroll bars are out of bounds, don't display them.
if !horizontal.bar.In(horizontal.gutter) {
horizontal.bar = image.Rectangle { }
}
if !vertical.bar.In(vertical.gutter) {
vertical.bar = image.Rectangle { }
}
} }
func (element *ScrollContainer) draw () { func (element *ScrollContainer) draw () {
@ -239,24 +295,34 @@ func (element *ScrollContainer) draw () {
artist.FillRectangle ( artist.FillRectangle (
element, theme.DeadPattern(), element, theme.DeadPattern(),
image.Rect ( image.Rect (
element.vertical.bounds.Min.X, element.vertical.gutter.Min.X,
element.horizontal.bounds.Min.Y, element.horizontal.gutter.Min.Y,
element.vertical.bounds.Max.X, element.vertical.gutter.Max.X,
element.horizontal.bounds.Max.Y)) element.horizontal.gutter.Max.Y))
element.drawHorizontalBar() element.drawHorizontalBar()
element.drawVerticalBar() element.drawVerticalBar()
} }
func (element *ScrollContainer) drawHorizontalBar () { func (element *ScrollContainer) drawHorizontalBar () {
artist.FillRectangle ( artist.FillRectangle (
element, theme.SunkenPattern(), element,
element.horizontal.bounds) theme.ScrollGutterPattern(true, element.horizontal.enabled),
element.horizontal.gutter)
artist.FillRectangle (
element,
theme.ScrollBarPattern(true, element.horizontal.enabled),
element.horizontal.bar)
} }
func (element *ScrollContainer) drawVerticalBar () { func (element *ScrollContainer) drawVerticalBar () {
artist.FillRectangle ( artist.FillRectangle (
element, theme.SunkenPattern(), element,
element.vertical.bounds) theme.ScrollGutterPattern(false, element.vertical.enabled),
element.vertical.gutter)
artist.FillRectangle (
element,
theme.ScrollBarPattern(false, element.vertical.enabled),
element.vertical.bar)
} }
func (element *ScrollContainer) updateMinimumSize () { func (element *ScrollContainer) updateMinimumSize () {
@ -269,3 +335,14 @@ func (element *ScrollContainer) updateMinimumSize () {
} }
element.core.SetMinimumSize(width, height) element.core.SetMinimumSize(width, height)
} }
func (element *ScrollContainer) childScrollBoundsChangeCallback () {
element.horizontal.enabled,
element.vertical.enabled = element.child.ScrollAxes()
if element.core.HasImage() {
element.recalculate()
element.drawHorizontalBar()
element.drawVerticalBar()
}
}

View File

@ -18,7 +18,7 @@ func run () {
container.Adopt(basic.NewLabel("look at this non sense", false), false) container.Adopt(basic.NewLabel("look at this non sense", false), false)
textBox := basic.NewTextBox("", "sample text sample text") textBox := basic.NewTextBox("", "sample text sample text")
scrollContainer := basic.NewScrollContainer(true, true) scrollContainer := basic.NewScrollContainer(true, false)
scrollContainer.Adopt(textBox) scrollContainer.Adopt(textBox)
container.Adopt(scrollContainer, true) container.Adopt(scrollContainer, true)