zntg/ui/overflow.go

154 lines
3.7 KiB
Go
Raw Permalink Normal View History

package ui
import (
"image/color"
"opslag.de/schobers/geom"
)
type overflow struct {
Proxy
Background color.Color
barWidth float32
desired geom.PointF32
bounds geom.RectangleF32
offset geom.PointF32
parent Control
2019-04-09 18:32:42 +00:00
content Buffer
proxied Control
hor *Scrollbar
ver *Scrollbar
}
func Overflow(content Control) Control {
return OverflowBackground(content, nil)
}
func OverflowBackground(content Control, back color.Color) Control {
var o = &overflow{Proxy: Proxy{Content: content}, Background: back}
o.hor = BuildScrollbar(OrientationHorizontal, func(*Scrollbar) {})
o.ver = BuildScrollbar(OrientationVertical, func(*Scrollbar) {})
return o
}
func (o *overflow) shouldScroll(bounds geom.RectangleF32) (hor bool, ver bool) {
var scroll = func(need, actual float32) bool {
return !geom.IsNaN32(need) && need > actual
}
var size = o.desired
hor = scroll(size.X, bounds.Dx())
ver = scroll(size.Y, bounds.Dy())
if ver && !hor {
hor = scroll(size.X+o.barWidth, bounds.Dx())
}
if hor && !ver {
ver = scroll(size.Y+o.barWidth, bounds.Dy())
}
return
}
func (o *overflow) doOnVisibleBars(fn func(bar *Scrollbar)) {
hor, ver := o.shouldScroll(o.bounds)
if hor {
fn(o.hor)
}
if ver {
fn(o.ver)
}
}
func (o *overflow) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
o.barWidth = ctx.Style().Dimensions.ScrollbarWidth
o.desired = o.Content.DesiredSize(ctx)
o.bounds = bounds
o.offset = offset
o.parent = parent
var hor, ver = o.shouldScroll(bounds)
var contentX, contentY float32 = 0, 0
var contentW, contentH = bounds.Dx(), bounds.Dy()
if hor {
contentX -= o.hor.ContentOffset
contentH = geom.Max32(0, contentH-o.barWidth)
}
if ver {
contentY -= o.ver.ContentOffset
contentW = geom.Max32(0, contentW-o.barWidth)
}
o.Content.Arrange(ctx, geom.RectF32(contentX, contentY, contentW, contentH), offset.Add(bounds.Min), o)
if hor {
o.hor.ContentLength = o.desired.X
o.hor.Arrange(ctx, geom.RectF32(bounds.Min.X, bounds.Min.Y+contentH, bounds.Min.X+contentW, bounds.Max.Y), offset, o)
}
if ver {
o.ver.ContentLength = o.desired.Y
o.ver.Arrange(ctx, geom.RectF32(bounds.Min.X+contentW, bounds.Min.Y, bounds.Max.X, bounds.Min.Y+contentH), offset, o)
}
}
func (o *overflow) Bounds() geom.RectangleF32 { return o.bounds }
func (o *overflow) DesiredSize(ctx Context) geom.PointF32 {
return geom.PtF32(geom.NaN32(), geom.NaN32())
}
func (o *overflow) Handle(ctx Context, e Event) {
if o.Content != o.proxied {
o.hor.ContentOffset = 0
o.ver.ContentOffset = 0
o.proxied = o.Content
}
hor, ver := o.shouldScroll(o.bounds)
if hor {
o.hor.Handle(ctx, e)
}
if ver {
o.ver.Handle(ctx, e)
}
switch e := e.(type) {
case *MouseMoveEvent:
if ver {
var contentO = o.Content.Offset()
var content = o.Content.Bounds()
content.Min = contentO
content.Max = content.Max.Add(contentO)
if e.MouseWheel != 0 && e.Pos().In(content) {
o.ver.ContentOffset = geom.Max32(0, geom.Min32(o.ver.ContentLength-content.Dy(), o.ver.ContentOffset-36*e.MouseWheel))
}
}
}
o.Content.Handle(ctx, e)
}
func (o *overflow) IsInBounds(p geom.PointF32) bool { return p.Sub(o.offset).In(o.bounds) }
func (o *overflow) Offset() geom.PointF32 { return o.offset }
func (o *overflow) Parent() Control { return o.parent }
func (o *overflow) Render(ctx Context) {
var renderer = ctx.Renderer()
if o.Background != nil {
renderer.FillRectangle(o.bounds, o.Background)
}
var content = o.Content.Bounds()
content.Min = geom.ZeroPtF32
2019-04-09 18:32:42 +00:00
err := o.content.Update(ctx, content.Size())
if err != nil {
panic(err)
}
o.content.Render(ctx, o.bounds.Min, func(Context, geom.PointF32) {
2019-04-09 18:32:42 +00:00
renderer.Clear(color.Transparent)
o.Content.Render(ctx)
})
o.doOnVisibleBars(func(bar *Scrollbar) {
bar.Render(ctx)
})
}