Added support for scrolling a control into view (Overflow, only vertically currently).
This commit is contained in:
parent
a6415a1d60
commit
eb46741165
@ -17,6 +17,9 @@ func BuildContainerBase(controls ...Control) ContainerBase {
|
||||
|
||||
func (c *ContainerBase) AddChild(child ...Control) {
|
||||
c.Children = append(c.Children, child...)
|
||||
for _, child := range child {
|
||||
child.SetSelf(child)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ContainerBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||
@ -26,6 +29,19 @@ func (c *ContainerBase) Arrange(ctx Context, bounds geom.RectangleF32, offset ge
|
||||
c.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||
}
|
||||
|
||||
func (c *ContainerBase) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 {
|
||||
if len(path) == 0 {
|
||||
return c.bounds
|
||||
}
|
||||
next := path[0]
|
||||
for _, child := range c.Children {
|
||||
if child == next {
|
||||
return child.BoundsUnclipped(ctx, path[1:])
|
||||
}
|
||||
}
|
||||
panic("child not found in path")
|
||||
}
|
||||
|
||||
func (c *ContainerBase) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 {
|
||||
var max geom.PointF32
|
||||
for _, child := range c.Children {
|
||||
|
@ -11,16 +11,39 @@ type Control interface {
|
||||
Render(Context)
|
||||
|
||||
Bounds() geom.RectangleF32
|
||||
BoundsUnclipped(Context, ControlPath) geom.RectangleF32
|
||||
Disable()
|
||||
Enable()
|
||||
IsDisabled() bool
|
||||
IsInBounds(p geom.PointF32) bool
|
||||
IsOver() bool
|
||||
Offset() geom.PointF32
|
||||
ScrollIntoView(Context, ControlPath)
|
||||
|
||||
Self() Control
|
||||
SetSelf(Control)
|
||||
Parent() Control
|
||||
}
|
||||
|
||||
type ControlPath []Control
|
||||
|
||||
func (p ControlPath) BoundsUnclipped(ctx Context, c Control) geom.RectangleF32 {
|
||||
if len(p) == 0 {
|
||||
return c.Bounds()
|
||||
}
|
||||
// switch next := p[0].(type) {
|
||||
// case *ContainerBase:
|
||||
// return next.BoundsUnclipped(ctx, p[1:])
|
||||
// case *StackPanel:
|
||||
// return next.BoundsUnclipped(ctx, p[1:])
|
||||
// }
|
||||
return p[0].BoundsUnclipped(ctx, p[1:])
|
||||
}
|
||||
|
||||
func (p ControlPath) Prepend(control Control) ControlPath {
|
||||
return append(ControlPath{control}, p...)
|
||||
}
|
||||
|
||||
type RootControl interface {
|
||||
Control
|
||||
|
||||
|
@ -90,6 +90,7 @@ var _ Control = &ControlBase{}
|
||||
type ControlBase struct {
|
||||
bounds geom.RectangleF32
|
||||
offset geom.PointF32
|
||||
self Control
|
||||
parent Control
|
||||
drag Dragable
|
||||
over bool
|
||||
@ -118,6 +119,10 @@ func (c *ControlBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom
|
||||
|
||||
func (c *ControlBase) Bounds() geom.RectangleF32 { return c.bounds }
|
||||
|
||||
func (c *ControlBase) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 {
|
||||
return path.BoundsUnclipped(ctx, c)
|
||||
}
|
||||
|
||||
func (c *ControlBase) ControlClicked() ControlClickedEventHandler { return &c.clicked }
|
||||
|
||||
func (c *ControlBase) DesiredSize(Context, geom.PointF32) geom.PointF32 { return geom.ZeroPtF32 }
|
||||
@ -282,4 +287,20 @@ func (c *ControlBase) TextColor(ctx Context) color.Color {
|
||||
return c.FontColor(ctx, ctx.Style().Palette.Text)
|
||||
}
|
||||
|
||||
func (c *ControlBase) ScrollIntoView(ctx Context, path ControlPath) {
|
||||
if c.parent == nil {
|
||||
return
|
||||
}
|
||||
c.parent.ScrollIntoView(ctx, path.Prepend(c))
|
||||
}
|
||||
|
||||
func (c *ControlBase) Self() Control {
|
||||
if c.self == nil {
|
||||
return c
|
||||
}
|
||||
return c.self
|
||||
}
|
||||
|
||||
func (c *ControlBase) SetSelf(self Control) { c.self = self }
|
||||
|
||||
func (c *ControlBase) ToControlPosition(p geom.PointF32) geom.PointF32 { return p.Sub(c.offset) }
|
||||
|
@ -114,6 +114,10 @@ func (o *overflow) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.Po
|
||||
|
||||
func (o *overflow) Bounds() geom.RectangleF32 { return o.bounds }
|
||||
|
||||
func (o *overflow) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 {
|
||||
return path.BoundsUnclipped(ctx, o)
|
||||
}
|
||||
|
||||
func (o *overflow) DesiredSize(Context, geom.PointF32) geom.PointF32 {
|
||||
return geom.PtF32(geom.NaN32(), geom.NaN32())
|
||||
}
|
||||
@ -193,3 +197,25 @@ func (o *overflow) SetScrollbarColor(bar color.Color, hover color.Color) {
|
||||
o.ver.BarColor = bar
|
||||
o.ver.BarHoverColor = hover
|
||||
}
|
||||
|
||||
func (o *overflow) ScrollIntoView(ctx Context, path ControlPath) {
|
||||
view := path.BoundsUnclipped(ctx, o)
|
||||
size := geom.PtF32(o.hor.Bounds().Dx(), o.ver.Bounds().Dy())
|
||||
view.Min.Y += o.ver.ContentOffset
|
||||
view.Max.Y += o.ver.ContentOffset
|
||||
if view.Max.Y > o.ver.ContentLength {
|
||||
view.Max.Y = o.ver.ContentLength
|
||||
if view.Min.Y > view.Max.Y {
|
||||
view.Min.Y = view.Max.Y
|
||||
}
|
||||
}
|
||||
if view.Min.Y < 0 {
|
||||
view.Min.Y = 0
|
||||
}
|
||||
if view.Max.Y > o.ver.ContentOffset+size.Y {
|
||||
o.ver.ContentOffset = view.Max.Y - size.Y
|
||||
}
|
||||
if view.Min.Y < o.ver.ContentOffset {
|
||||
o.ver.ContentOffset = view.Min.Y
|
||||
}
|
||||
}
|
||||
|
14
ui/proxy.go
14
ui/proxy.go
@ -29,6 +29,10 @@ func (p *Proxy) Bounds() geom.RectangleF32 {
|
||||
return p.Content.Bounds()
|
||||
}
|
||||
|
||||
func (p *Proxy) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 {
|
||||
return path.BoundsUnclipped(ctx, p)
|
||||
}
|
||||
|
||||
func (p *Proxy) Disable() { p.Content.Disable() }
|
||||
|
||||
func (p *Proxy) Enable() { p.Content.Enable() }
|
||||
@ -56,3 +60,13 @@ func (p *Proxy) Shown() {
|
||||
overlay.Shown()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) ScrollIntoView(ctx Context, path ControlPath) {
|
||||
p.Content.Parent().ScrollIntoView(ctx, path.Prepend(p))
|
||||
}
|
||||
|
||||
func (p *Proxy) Self() Control {
|
||||
return p.Content.Self()
|
||||
}
|
||||
|
||||
func (p *Proxy) SetSelf(self Control) { p.Content.SetSelf(self) }
|
||||
|
@ -17,13 +17,13 @@ func BuildStackPanel(o Orientation, fn func(*StackPanel)) *StackPanel {
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||
func (p *StackPanel) preArrange(ctx Context, bounds geom.RectangleF32, clip bool) []geom.RectangleF32 {
|
||||
bounds = p.Orientation.FlipRect(bounds)
|
||||
var length float32
|
||||
var stretch int
|
||||
var desired = make([]geom.PointF32, len(p.Children))
|
||||
for i, child := range p.Children {
|
||||
var size = p.Orientation.FlipPt(child.DesiredSize(ctx, bounds.Size()))
|
||||
var size = p.Orientation.FlipPt(child.DesiredSize(ctx, p.Orientation.FlipPt(bounds.Size())))
|
||||
if geom.IsNaN32(size.Y) {
|
||||
stretch++
|
||||
} else {
|
||||
@ -33,7 +33,8 @@ func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.
|
||||
}
|
||||
var remainder = bounds.Dy() - length
|
||||
var childOffset float32
|
||||
for i, size := range desired {
|
||||
childBounds := make([]geom.RectangleF32, 0, len(p.Children))
|
||||
for _, size := range desired {
|
||||
var height = size.Y
|
||||
if geom.IsNaN32(size.Y) {
|
||||
if remainder < 0 {
|
||||
@ -42,13 +43,44 @@ func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.
|
||||
height = remainder / float32(stretch)
|
||||
}
|
||||
}
|
||||
minY := geom.Min32(bounds.Max.Y, bounds.Min.Y+childOffset)
|
||||
maxY := geom.Min32(bounds.Max.Y, bounds.Min.Y+childOffset+height)
|
||||
minY := bounds.Min.Y + childOffset
|
||||
maxY := minY + height
|
||||
if clip {
|
||||
if minY > bounds.Max.Y {
|
||||
minY = bounds.Max.Y
|
||||
}
|
||||
if maxY > bounds.Max.Y {
|
||||
maxY = bounds.Max.Y
|
||||
}
|
||||
}
|
||||
var child = geom.RectF32(bounds.Min.X, minY, bounds.Max.X, maxY)
|
||||
p.Children[i].Arrange(ctx, p.Orientation.FlipRect(child), offset, p)
|
||||
childBounds = append(childBounds, p.Orientation.FlipRect(child))
|
||||
childOffset += height
|
||||
}
|
||||
p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(bounds), offset, parent)
|
||||
return childBounds
|
||||
}
|
||||
|
||||
func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||
childBounds := p.preArrange(ctx, bounds, true)
|
||||
for i := range p.Children {
|
||||
p.Children[i].Arrange(ctx, childBounds[i], offset, p)
|
||||
}
|
||||
p.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||
}
|
||||
|
||||
func (p *StackPanel) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 {
|
||||
if len(path) == 0 {
|
||||
return p.bounds
|
||||
}
|
||||
next := path[0]
|
||||
childBounds := p.preArrange(ctx, p.bounds, false)
|
||||
for i, child := range p.Children {
|
||||
if child != next && next.Self() != child.Self() {
|
||||
continue
|
||||
}
|
||||
return childBounds[i]
|
||||
}
|
||||
panic("child not found in path")
|
||||
}
|
||||
|
||||
func (p *StackPanel) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 {
|
||||
@ -87,3 +119,10 @@ func (p *StackPanel) Render(ctx Context) {
|
||||
child.Render(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *StackPanel) ScrollIntoView(ctx Context, path ControlPath) {
|
||||
if p.parent == nil {
|
||||
return
|
||||
}
|
||||
p.parent.ScrollIntoView(ctx, path.Prepend(p))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user