package ui import ( "opslag.de/schobers/geom" ) type Scrollbar struct { ControlBase Orientation Orientation Content float32 Offset float32 handle ScrollbarHandle startDragOffset float32 } func BuildScrollbar(o Orientation, fn func(s *Scrollbar)) *Scrollbar { var s = &Scrollbar{Orientation: o, Content: 0, Offset: 0} s.handle.OnDragStart(func(_ Context, _ Control, _ geom.PointF32) { s.startDragOffset = s.Offset }) s.handle.OnDragMove(func(_ Context, _ Control, start, move geom.PointF32) { var length = s.Orientation.SizeParallel(s.bounds) var handleMaxOffset = length - s.Orientation.SizeParallel(s.handle.bounds) var hidden = s.Content - length var offset = (s.Orientation.LengthParallel(move) - s.Orientation.LengthParallel(start)) / handleMaxOffset s.Offset = 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) { s.ControlBase.Arrange(ctx, bounds, offset) s.updateBar(ctx) } func (s *Scrollbar) DesiredSize(ctx Context) geom.PointF32 { return s.Orientation.Pt(geom.NaN32(), ctx.Style().Dimensions.ScrollbarWidth) } func (s *Scrollbar) Handle(ctx Context, e Event) { s.handle.Handle(ctx, e) switch e := e.(type) { case *MouseMoveEvent: if e.MouseWheel != 0 && e.Pos().Sub(s.offset).In(s.bounds) { s.Offset = geom.Max32(0, geom.Min32(s.Content-s.Orientation.SizeParallel(s.bounds), s.Offset-36*e.MouseWheel)) } } s.ControlBase.Handle(ctx, e) } func (s *Scrollbar) Render(ctx Context) { s.handle.Render(ctx) } 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.Content > length { handleLength = geom.Max32(2*width, length*length/s.Content) var hidden = s.Content - length if s.Offset > hidden { s.Offset = hidden } var offset = geom.Min32(1, s.Offset/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) } type ScrollbarHandle struct { ControlBase } func (h *ScrollbarHandle) Render(ctx Context) { h.RenderBackground(ctx) p := ctx.Style().Palette fill := p.Primary if h.over { fill = p.PrimaryLight } ctx.Renderer().FillRectangle(h.bounds.Inset(1), fill) }