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 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 o.offset = offset 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) DesiredSize(ctx Context) geom.PointF32 { return geom.PtF32(geom.NaN32(), geom.NaN32()) } 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) } 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.Offset = geom.Max32(0, geom.Min32(o.ver.Content-content.Dy(), o.ver.Offset-36*e.MouseWheel)) } } } o.Content.Handle(ctx, e) } func (o *overflow) Offset() geom.PointF32 { return o.offset } 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() content.Min = geom.ZeroPtF32 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.buffer, o.bounds.Min) o.doOnVisibleBars(func(bar *Scrollbar) { bar.Render(ctx) }) }