129 lines
3.0 KiB
Go
129 lines
3.0 KiB
Go
|
package ui
|
||
|
|
||
|
import (
|
||
|
"image/color"
|
||
|
|
||
|
"opslag.de/schobers/geom"
|
||
|
)
|
||
|
|
||
|
type overflow struct {
|
||
|
Proxy
|
||
|
|
||
|
Background color.Color
|
||
|
|
||
|
barWidth float32
|
||
|
desired geom.PointF32
|
||
|
bounds geom.RectangleF32
|
||
|
buffer Image
|
||
|
|
||
|
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) {
|
||
|
o.barWidth = ctx.Style().Dimensions.ScrollbarWidth
|
||
|
o.desired = o.Content.DesiredSize(ctx)
|
||
|
o.bounds = bounds
|
||
|
|
||
|
var hor, ver = o.shouldScroll(bounds)
|
||
|
var contentX, contentY float32 = 0, 0
|
||
|
var contentW, contentH = bounds.Dx(), bounds.Dy()
|
||
|
if hor {
|
||
|
contentX -= o.hor.Offset
|
||
|
contentH = geom.Max32(0, contentH-o.barWidth)
|
||
|
}
|
||
|
if ver {
|
||
|
contentY -= o.ver.Offset
|
||
|
contentW = geom.Max32(0, contentW-o.barWidth)
|
||
|
}
|
||
|
o.Content.Arrange(ctx, geom.RectF32(contentX, contentY, contentW, contentH), offset.Add(bounds.Min))
|
||
|
if hor {
|
||
|
o.hor.Content = o.desired.X
|
||
|
o.hor.Arrange(ctx, geom.RectF32(bounds.Min.X, bounds.Min.Y+contentH, bounds.Min.X+contentW, bounds.Max.Y), offset)
|
||
|
}
|
||
|
if ver {
|
||
|
o.ver.Content = o.desired.Y
|
||
|
o.ver.Arrange(ctx, geom.RectF32(bounds.Min.X+contentW, bounds.Min.Y, bounds.Max.X, bounds.Max.Y), offset)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (o *overflow) Handle(ctx Context, e Event) {
|
||
|
hor, ver := o.shouldScroll(o.bounds)
|
||
|
if hor {
|
||
|
o.hor.Handle(ctx, e)
|
||
|
}
|
||
|
if ver {
|
||
|
o.ver.Handle(ctx, e)
|
||
|
}
|
||
|
o.Content.Handle(ctx, e)
|
||
|
}
|
||
|
|
||
|
func (o *overflow) Render(ctx Context) {
|
||
|
var renderer = ctx.Renderer()
|
||
|
|
||
|
if o.Background != nil {
|
||
|
ctx.Renderer().FillRectangle(o.bounds, o.Background)
|
||
|
}
|
||
|
|
||
|
var content = o.Content.Bounds()
|
||
|
if o.buffer == nil || o.buffer.Width() != content.Dx() || o.buffer.Height() != content.Dy() {
|
||
|
if o.buffer != nil {
|
||
|
o.buffer.Destroy()
|
||
|
o.buffer = nil
|
||
|
}
|
||
|
buffer, err := renderer.CreateImageSize(content.Dx(), content.Dy())
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
o.buffer = buffer
|
||
|
}
|
||
|
|
||
|
target := renderer.Target()
|
||
|
renderer.RenderTo(o.buffer)
|
||
|
renderer.Clear(color.Transparent)
|
||
|
o.Content.Render(ctx)
|
||
|
renderer.RenderTo(target)
|
||
|
renderer.DrawImage(o.bounds.Min, o.buffer)
|
||
|
|
||
|
o.doOnVisibleBars(func(bar *Scrollbar) {
|
||
|
bar.Render(ctx)
|
||
|
})
|
||
|
}
|