Added support for scrolling a control into view (Overflow, only vertically currently).
This commit is contained in:
parent
a6415a1d60
commit
365a74a2d9
@ -17,6 +17,9 @@ func BuildContainerBase(controls ...Control) ContainerBase {
|
|||||||
|
|
||||||
func (c *ContainerBase) AddChild(child ...Control) {
|
func (c *ContainerBase) AddChild(child ...Control) {
|
||||||
c.Children = append(c.Children, child...)
|
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) {
|
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)
|
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 {
|
func (c *ContainerBase) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 {
|
||||||
var max geom.PointF32
|
var max geom.PointF32
|
||||||
for _, child := range c.Children {
|
for _, child := range c.Children {
|
||||||
|
@ -11,16 +11,39 @@ type Control interface {
|
|||||||
Render(Context)
|
Render(Context)
|
||||||
|
|
||||||
Bounds() geom.RectangleF32
|
Bounds() geom.RectangleF32
|
||||||
|
BoundsUnclipped(Context, ControlPath) geom.RectangleF32
|
||||||
Disable()
|
Disable()
|
||||||
Enable()
|
Enable()
|
||||||
IsDisabled() bool
|
IsDisabled() bool
|
||||||
IsInBounds(p geom.PointF32) bool
|
IsInBounds(p geom.PointF32) bool
|
||||||
IsOver() bool
|
IsOver() bool
|
||||||
Offset() geom.PointF32
|
Offset() geom.PointF32
|
||||||
|
ScrollIntoView(Context, ControlPath)
|
||||||
|
|
||||||
|
Self() Control
|
||||||
|
SetSelf(Control)
|
||||||
Parent() 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 {
|
type RootControl interface {
|
||||||
Control
|
Control
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ var _ Control = &ControlBase{}
|
|||||||
type ControlBase struct {
|
type ControlBase struct {
|
||||||
bounds geom.RectangleF32
|
bounds geom.RectangleF32
|
||||||
offset geom.PointF32
|
offset geom.PointF32
|
||||||
|
self Control
|
||||||
parent Control
|
parent Control
|
||||||
drag Dragable
|
drag Dragable
|
||||||
over bool
|
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) 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) ControlClicked() ControlClickedEventHandler { return &c.clicked }
|
||||||
|
|
||||||
func (c *ControlBase) DesiredSize(Context, geom.PointF32) geom.PointF32 { return geom.ZeroPtF32 }
|
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)
|
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) }
|
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) 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 {
|
func (o *overflow) DesiredSize(Context, geom.PointF32) geom.PointF32 {
|
||||||
return geom.PtF32(geom.NaN32(), geom.NaN32())
|
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.BarColor = bar
|
||||||
o.ver.BarHoverColor = hover
|
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()
|
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) Disable() { p.Content.Disable() }
|
||||||
|
|
||||||
func (p *Proxy) Enable() { p.Content.Enable() }
|
func (p *Proxy) Enable() { p.Content.Enable() }
|
||||||
@ -56,3 +60,13 @@ func (p *Proxy) Shown() {
|
|||||||
overlay.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
|
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)
|
bounds = p.Orientation.FlipRect(bounds)
|
||||||
var length float32
|
var length float32
|
||||||
var stretch int
|
var stretch int
|
||||||
var desired = make([]geom.PointF32, len(p.Children))
|
var desired = make([]geom.PointF32, len(p.Children))
|
||||||
for i, child := range 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) {
|
if geom.IsNaN32(size.Y) {
|
||||||
stretch++
|
stretch++
|
||||||
} else {
|
} else {
|
||||||
@ -33,7 +33,8 @@ func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.
|
|||||||
}
|
}
|
||||||
var remainder = bounds.Dy() - length
|
var remainder = bounds.Dy() - length
|
||||||
var childOffset float32
|
var childOffset float32
|
||||||
for i, size := range desired {
|
childBounds := make([]geom.RectangleF32, 0, len(p.Children))
|
||||||
|
for _, size := range desired {
|
||||||
var height = size.Y
|
var height = size.Y
|
||||||
if geom.IsNaN32(size.Y) {
|
if geom.IsNaN32(size.Y) {
|
||||||
if remainder < 0 {
|
if remainder < 0 {
|
||||||
@ -42,15 +43,46 @@ func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.
|
|||||||
height = remainder / float32(stretch)
|
height = remainder / float32(stretch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
minY := geom.Min32(bounds.Max.Y, bounds.Min.Y+childOffset)
|
minY := bounds.Min.Y + childOffset
|
||||||
maxY := geom.Min32(bounds.Max.Y, bounds.Min.Y+childOffset+height)
|
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)
|
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
|
childOffset += height
|
||||||
}
|
}
|
||||||
|
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, p.Orientation.FlipRect(childBounds[i]), offset, p)
|
||||||
|
}
|
||||||
p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(bounds), offset, parent)
|
p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(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 {
|
func (p *StackPanel) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 {
|
||||||
var length float32
|
var length float32
|
||||||
var width float32
|
var width float32
|
||||||
@ -87,3 +119,10 @@ func (p *StackPanel) Render(ctx Context) {
|
|||||||
child.Render(ctx)
|
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