Added ScrollContainer
This commit is contained in:
parent
71bc235c0e
commit
e45e391f6d
@ -278,7 +278,13 @@ func (this scrollbarLayout) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
|
|||||||
// calculate handle length
|
// calculate handle length
|
||||||
handleLength := gutterLength * this.viewportContentRatio()
|
handleLength := gutterLength * this.viewportContentRatio()
|
||||||
if handleLength < handleMin { handleLength = handleMin }
|
if handleLength < handleMin { handleLength = handleMin }
|
||||||
if handleLength > gutterLength { handleLength = gutterLength }
|
if handleLength >= gutterLength {
|
||||||
|
// just hide the handle if it isn't needed.
|
||||||
|
// TODO: we need a way to hide and show boxes, this is janky as
|
||||||
|
// fuck
|
||||||
|
boxes[0].SetBounds(image.Rect(-16, -16, 0, 0))
|
||||||
|
return
|
||||||
|
}
|
||||||
if this.vertical {
|
if this.vertical {
|
||||||
handle.Max.Y = int(handleLength)
|
handle.Max.Y = int(handleLength)
|
||||||
} else {
|
} else {
|
||||||
|
167
scrollcontainer.go
Normal file
167
scrollcontainer.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/event"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/theme"
|
||||||
|
|
||||||
|
// ScrollSide determines which Scrollbars are active in a ScrollContainer.
|
||||||
|
type ScrollSide int; const (
|
||||||
|
ScrollVertical ScrollSide = 1 << iota
|
||||||
|
ScrollHorizontal
|
||||||
|
ScrollBoth = ScrollVertical | ScrollHorizontal
|
||||||
|
)
|
||||||
|
|
||||||
|
// Horizontal returns true if the side includes a horizontal bar.
|
||||||
|
func (sides ScrollSide) Horizontal () bool {
|
||||||
|
return sides & ScrollHorizontal > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical returns true if the side includes a vertical bar.
|
||||||
|
func (sides ScrollSide) Vertical () bool {
|
||||||
|
return sides & ScrollVertical > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns one of:
|
||||||
|
// - both
|
||||||
|
// - horizontal
|
||||||
|
// - vertical
|
||||||
|
// - none
|
||||||
|
func (sides ScrollSide) String () string {
|
||||||
|
switch {
|
||||||
|
case sides.Horizontal() && sides.Vertical(): return "both"
|
||||||
|
case sides.Horizontal(): return "horizontal"
|
||||||
|
case sides.Vertical(): return "vertical"
|
||||||
|
default: return "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScrollContainer couples a ContentBox with one or two Scrollbars.
|
||||||
|
type ScrollContainer struct {
|
||||||
|
tomo.ContainerBox
|
||||||
|
layout *scrollContainerLayout
|
||||||
|
|
||||||
|
horizontalCookie event.Cookie
|
||||||
|
verticalCookie event.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScrollContainer creates a new scroll container.
|
||||||
|
func NewScrollContainer (sides ScrollSide) *ScrollContainer {
|
||||||
|
this := &ScrollContainer {
|
||||||
|
ContainerBox: tomo.NewContainerBox(),
|
||||||
|
layout: &scrollContainerLayout { },
|
||||||
|
}
|
||||||
|
if sides.Vertical() {
|
||||||
|
this.layout.vertical = NewVerticalScrollbar()
|
||||||
|
this.Add(this.layout.vertical)
|
||||||
|
}
|
||||||
|
if sides.Horizontal() {
|
||||||
|
this.layout.horizontal = NewHorizontalScrollbar()
|
||||||
|
this.Add(this.layout.horizontal)
|
||||||
|
}
|
||||||
|
this.CaptureScroll(true)
|
||||||
|
this.OnScroll(this.handleScroll)
|
||||||
|
theme.Apply(this, theme.R("objects", "ScrollContainer", sides.String()))
|
||||||
|
this.SetLayout(this.layout)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoot sets the root child of the ScrollContainer. There can only be one at
|
||||||
|
// a time, and setting it will remove and unlink the current child if there is
|
||||||
|
// one.
|
||||||
|
func (this *ScrollContainer) SetRoot (root tomo.ContentBox) {
|
||||||
|
if this.layout.root != nil {
|
||||||
|
// delete root and close cookies
|
||||||
|
this.Delete(this.layout.root)
|
||||||
|
if this.horizontalCookie != nil {
|
||||||
|
this.horizontalCookie.Close()
|
||||||
|
this.horizontalCookie = nil
|
||||||
|
}
|
||||||
|
if this.verticalCookie != nil {
|
||||||
|
this.verticalCookie.Close()
|
||||||
|
this.verticalCookie = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.layout.root = root
|
||||||
|
if root != nil {
|
||||||
|
// insert root at the beginning (for keynav)
|
||||||
|
switch {
|
||||||
|
case this.layout.vertical != nil:
|
||||||
|
this.Insert(root, this.layout.vertical)
|
||||||
|
case this.layout.horizontal != nil:
|
||||||
|
this.Insert(root, this.layout.horizontal)
|
||||||
|
default:
|
||||||
|
this.Add(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// link root and remember cookies
|
||||||
|
if this.layout.horizontal != nil {
|
||||||
|
this.horizontalCookie = this.layout.horizontal.Link(root)
|
||||||
|
}
|
||||||
|
if this.layout.vertical != nil {
|
||||||
|
this.verticalCookie = this.layout.vertical.Link(root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ScrollContainer) handleScroll (x, y float64) {
|
||||||
|
if this.layout.root == nil { return }
|
||||||
|
this.layout.root.ScrollTo (
|
||||||
|
this.layout.root.ContentBounds().Min.
|
||||||
|
Add(image.Pt(int(x), int(y))))
|
||||||
|
}
|
||||||
|
|
||||||
|
type scrollContainerLayout struct {
|
||||||
|
root tomo.ContentBox
|
||||||
|
horizontal *Scrollbar
|
||||||
|
vertical *Scrollbar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *scrollContainerLayout) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
|
||||||
|
var minimum image.Point; if this.root != nil {
|
||||||
|
minimum = this.root.MinimumSize()
|
||||||
|
}
|
||||||
|
if this.horizontal != nil {
|
||||||
|
minimum.Y += this.horizontal.MinimumSize().Y
|
||||||
|
}
|
||||||
|
if this.vertical != nil {
|
||||||
|
minimum.X += this.vertical.MinimumSize().X
|
||||||
|
minimum.Y = max(minimum.Y, this.vertical.MinimumSize().Y)
|
||||||
|
}
|
||||||
|
if this.horizontal != nil {
|
||||||
|
minimum.X = max(minimum.X, this.horizontal.MinimumSize().X)
|
||||||
|
}
|
||||||
|
return minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *scrollContainerLayout) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
|
||||||
|
rootBounds := hints.Bounds
|
||||||
|
if this.horizontal != nil {
|
||||||
|
rootBounds.Max.Y -= this.horizontal.MinimumSize().Y
|
||||||
|
}
|
||||||
|
if this.vertical != nil {
|
||||||
|
rootBounds.Max.X -= this.vertical.MinimumSize().X
|
||||||
|
}
|
||||||
|
if this.root != nil {
|
||||||
|
this.root.SetBounds(rootBounds)
|
||||||
|
}
|
||||||
|
if this.horizontal != nil {
|
||||||
|
this.horizontal.SetBounds(image.Rect (
|
||||||
|
hints.Bounds.Min.X,
|
||||||
|
rootBounds.Max.Y,
|
||||||
|
rootBounds.Max.X,
|
||||||
|
hints.Bounds.Max.Y))
|
||||||
|
}
|
||||||
|
if this.vertical != nil {
|
||||||
|
this.vertical.SetBounds(image.Rect (
|
||||||
|
rootBounds.Max.X,
|
||||||
|
hints.Bounds.Min.Y,
|
||||||
|
hints.Bounds.Max.X,
|
||||||
|
rootBounds.Max.Y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func max (x, y int) int {
|
||||||
|
if x > y { return x }
|
||||||
|
return y
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user