Scrollbar drawing
This commit is contained in:
parent
3acbe40665
commit
faf38b96c2
@ -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 ())
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
@ -73,12 +80,14 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) {
|
|||||||
// TODO: somehow inform the core that we do not in fact want to
|
// TODO: somehow inform the core that we do not in fact want to
|
||||||
// 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
|
||||||
|
}
|
||||||
|
element.childHeight -= thickness
|
||||||
}
|
}
|
||||||
if vertical.enabled {
|
if vertical.exists {
|
||||||
vertical.bounds.Max.X = thickness
|
vertical.gutter.Min.X = bounds.Max.X - thickness
|
||||||
vertical.bounds.Max.Y = bounds.Max.Y - 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user