package ui import ( "image/color" "opslag.de/schobers/geom" "opslag.de/schobers/zntg" ) type Scrollbar struct { ControlBase Orientation Orientation BarColor color.Color BarHoverColor color.Color ContentLength float32 ContentOffset float32 handle ScrollbarHandle startDragOffset float32 } func BuildScrollbar(o Orientation, fn func(s *Scrollbar)) *Scrollbar { var s = &Scrollbar{Orientation: o, ContentLength: 0, ContentOffset: 0} s.handle.DragStarted().AddHandler(func(Context, DragStartedArgs) { s.startDragOffset = s.ContentOffset }) s.handle.DragMoved().AddHandler(func(_ Context, args DragMovedArgs) { var length = s.Orientation.SizeParallel(s.bounds) var handleMaxOffset = length - s.Orientation.SizeParallel(s.handle.bounds) var hidden = s.ContentLength - length var offset = (s.Orientation.LengthParallel(args.Current) - s.Orientation.LengthParallel(args.Start)) / handleMaxOffset s.ContentOffset = geom.Max32(0, geom.Min32(s.startDragOffset+offset*hidden, hidden)) }) if fn != nil { fn(s) } return s } func (s *Scrollbar) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) { s.ControlBase.Arrange(ctx, bounds, offset, parent) s.updateBar(ctx) } func (s *Scrollbar) DesiredSize(ctx Context, _ geom.PointF32) geom.PointF32 { return s.Orientation.Pt(geom.NaN32(), ctx.Style().Dimensions.ScrollbarWidth) } func (s *Scrollbar) Handle(ctx Context, e Event) bool { s.handle.Handle(ctx, e) switch e := e.(type) { case *MouseMoveEvent: if e.MouseWheel != 0 && e.Pos().Sub(s.offset).In(s.bounds) { s.ContentOffset = geom.Max32(0, geom.Min32(s.ContentLength-s.Orientation.SizeParallel(s.bounds), s.ContentOffset-36*e.MouseWheel)) return true } } return s.ControlBase.Handle(ctx, e) } func (s *Scrollbar) Render(ctx Context) { ctx.Renderer().FillRectangle(s.bounds, zntg.RGBA(0, 0, 0, 1)) s.handle.Render(ctx, s) } func (s *Scrollbar) updateBar(ctx Context) { var length = s.Orientation.SizeParallel(s.bounds) var width = ctx.Style().Dimensions.ScrollbarWidth var handleLength = length var handleOffset = s.Orientation.LengthParallel(s.bounds.Min) if s.ContentLength > length { handleLength = geom.Max32(2*width, length*length/s.ContentLength) var hidden = s.ContentLength - length if s.ContentOffset > hidden { s.ContentOffset = hidden } var offset = geom.Min32(1, s.ContentOffset/hidden) handleOffset = handleOffset + offset*(length-handleLength) } var min = s.Orientation.Pt(handleOffset, s.Orientation.LengthPerpendicular(s.bounds.Max)-width) s.handle.Arrange(ctx, s.Orientation.Rect(min, handleLength, width), s.offset, s) } type ScrollbarHandle struct { ControlBase } func (h *ScrollbarHandle) Render(ctx Context, s *Scrollbar) { h.RenderBackground(ctx) p := ctx.Style().Palette var fill color.Color if h.over { if s.BarHoverColor == nil { fill = p.PrimaryLight } else { fill = s.BarHoverColor } } else { if s.BarColor == nil { fill = p.Primary } else { fill = s.BarColor } } ctx.Renderer().FillRectangle(h.bounds.Inset(1), fill) }