package ui import ( "math" "opslag.de/schobers/galleg/allegro5" "opslag.de/schobers/geom" ) var _ Control = &ContentScrollbar{} type ContentScrollbarValueChangedFn func(float64) type ContentScrollbar struct { ControlBase Length float64 Value float64 Orientation Orientation OnChanged ContentScrollbarValueChangedFn handle *contentScrollbarHandle barLength float64 scrollDistance float64 } type contentScrollbarHandle struct { ControlBase } func (h *contentScrollbarHandle) Render(ctx Context) { var c = ctx.Palette().Primary() if h.IsOver { if h.IsPressed { } else { } } var min = h.Bounds.Min.To32() var max = h.Bounds.Max.To32() allegro5.DrawFilledRectangle(min.X, min.Y, max.X, max.Y, c) } func (s *ContentScrollbar) Created(ctx Context, p Container) error { s.ControlBase.Created(ctx, p) s.handle = &contentScrollbarHandle{} s.handle.Created(ctx, nil) return nil } func (s *ContentScrollbar) Destroyed(ctx Context) { s.handle.Destroyed(ctx) } func (s *ContentScrollbar) length() (float64, float64) { var min, max float64 switch s.Orientation { case OrientationHorizontal: min = s.Bounds.Min.X max = s.Bounds.Max.X default: min = s.Bounds.Min.Y max = s.Bounds.Max.Y } return max - min, min } func (s *ContentScrollbar) updateBarLength() { var length, _ = s.length() var bar = length if s.Length > length { bar = length * length / s.Length } if bar < 20 { if length < 40 { bar = .5 * length } else { bar = 20 } } s.barLength = bar var d = s.Length - length if d < 0 { d = 0 } s.scrollDistance = d } func (s *ContentScrollbar) barViewCenter() float64 { switch s.Orientation { case OrientationHorizontal: return (s.Bounds.Min.Y + s.Bounds.Max.Y) * .5 default: return (s.Bounds.Min.X + s.Bounds.Max.X) * .5 } } func (s *ContentScrollbar) toValue(x, y float64) float64 { var pos = y if OrientationHorizontal == s.Orientation { pos = x } var length, min = s.length() if length == 0 { return 0 } var offset = (pos - .5*s.barLength - min) / (length - s.barLength) if offset < 0 { return 0 } else if offset > 1 { return s.scrollDistance } return s.scrollDistance * offset } func (s *ContentScrollbar) change(v float64) { if v != s.Value { s.Value = v var onChanged = s.OnChanged if nil != onChanged { onChanged(v) } } } func (s *ContentScrollbar) snapTo(x, y int) { var val = s.toValue(float64(x), float64(y)) s.change(val) } func (s *ContentScrollbar) increment(d int) { if s.Orientation == OrientationVertical { d *= -1 } var val = s.Value + float64(d)*ScrollbarWidth if val < 0 { val = 0 } else if val > s.scrollDistance { val = s.scrollDistance } s.change(val) } func (s *ContentScrollbar) Handle(ctx Context, ev allegro5.Event) { s.ControlBase.Handle(ctx, ev) s.handle.Handle(ctx, ev) switch e := ev.(type) { case *allegro5.MouseMoveEvent: if s.handle.IsPressed { s.snapTo(e.X, e.Y) } if 0 != e.DeltaZ && s.IsOver { var d = e.DeltaZ if allegro5.IsAnyKeyDown(allegro5.KeyLShift, allegro5.KeyRShift) { d *= 10 } s.increment(d) } case *allegro5.MouseButtonDownEvent: if !s.handle.IsPressed && s.IsOver { s.snapTo(e.X, e.Y) } } } func (s *ContentScrollbar) DesiredSize(Context) geom.PointF { switch s.Orientation { case OrientationHorizontal: return geom.PtF(math.NaN(), ScrollbarWidth) } return geom.PtF(ScrollbarWidth, math.NaN()) } func (s *ContentScrollbar) SetRect(rect geom.RectangleF) { switch s.Orientation { case OrientationHorizontal: if rect.Dy() > ScrollbarWidth { rect.Min.Y = rect.Max.Y - ScrollbarWidth } default: if rect.Dx() > ScrollbarWidth { rect.Min.X = rect.Max.X - ScrollbarWidth } } s.ControlBase.SetRect(rect) s.updateBarLength() var offset float64 if 0 < s.scrollDistance { offset = s.Value / s.scrollDistance } var length, min = s.length() var begin = min + (length-s.barLength)*offset var end = begin + s.barLength switch s.Orientation { case OrientationHorizontal: s.handle.SetRect(geom.RectF(begin, rect.Min.Y, end, rect.Max.Y)) default: s.handle.SetRect(geom.RectF(rect.Min.X, begin, rect.Max.X, end)) } } func (s *ContentScrollbar) Render(ctx Context) { s.handle.Render(ctx) s.ControlBase.Render(ctx) }