// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable enable using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Graphics.Containers { public class OsuScrollContainer : OsuScrollContainer { public OsuScrollContainer() { } public OsuScrollContainer(Direction direction) : base(direction) { } } public class OsuScrollContainer : ScrollContainer where T : Drawable { public const float SCROLL_BAR_HEIGHT = 10; public const float SCROLL_BAR_PADDING = 3; /// /// Allows controlling the scroll bar from any position in the container using the right mouse button. /// Uses the value of to smoothly scroll to the dragged location. /// public bool RightMouseScrollbar; /// /// Controls the rate with which the target position is approached when performing a relative drag. Default is 0.02. /// public double DistanceDecayOnRightMouseScrollbar = 0.02; private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right; private void scrollFromMouseEvent(MouseEvent e) => ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim]) * Content.DrawSize[ScrollDim], true, DistanceDecayOnRightMouseScrollbar); private bool rightMouseDragging; protected override bool IsDragging => base.IsDragging || rightMouseDragging; public OsuScrollContainer(Direction scrollDirection = Direction.Vertical) : base(scrollDirection) { } /// /// Scrolls a into view. /// /// The to scroll into view. /// Whether to animate the movement. /// An added amount to scroll beyond the requirement to bring the target into view. public void ScrollIntoView(Drawable d, bool animated = true, float extraScroll = 0) { float childPos0 = GetChildPosInContent(d); float childPos1 = GetChildPosInContent(d, d.DrawSize); float minPos = Math.Min(childPos0, childPos1); float maxPos = Math.Max(childPos0, childPos1); if (minPos < Current || (minPos > Current && d.DrawSize[ScrollDim] > DisplayableContent)) ScrollTo(minPos - extraScroll, animated); else if (maxPos > Current + DisplayableContent) ScrollTo(maxPos - DisplayableContent + extraScroll, animated); } protected override bool OnMouseDown(MouseDownEvent e) { if (shouldPerformRightMouseScroll(e)) { scrollFromMouseEvent(e); return true; } return base.OnMouseDown(e); } protected override void OnDrag(DragEvent e) { if (rightMouseDragging) { scrollFromMouseEvent(e); return; } base.OnDrag(e); } protected override bool OnDragStart(DragStartEvent e) { if (shouldPerformRightMouseScroll(e)) { rightMouseDragging = true; return true; } return base.OnDragStart(e); } protected override void OnDragEnd(DragEndEvent e) { if (rightMouseDragging) { rightMouseDragging = false; return; } base.OnDragEnd(e); } protected override bool OnScroll(ScrollEvent e) { // allow for controlling volume when alt is held. // mostly for compatibility with osu-stable. if (e.AltPressed) return false; return base.OnScroll(e); } protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction); protected class OsuScrollbar : ScrollbarContainer { private Color4 hoverColour; private Color4 defaultColour; private Color4 highlightColour; private readonly Box box; public OsuScrollbar(Direction scrollDir) : base(scrollDir) { Blending = BlendingParameters.Additive; CornerRadius = 5; // needs to be set initially for the ResizeTo to respect minimum size Size = new Vector2(SCROLL_BAR_HEIGHT); const float margin = 3; Margin = new MarginPadding { Left = scrollDir == Direction.Vertical ? margin : 0, Right = scrollDir == Direction.Vertical ? margin : 0, Top = scrollDir == Direction.Horizontal ? margin : 0, Bottom = scrollDir == Direction.Horizontal ? margin : 0, }; Masking = true; Child = box = new Box { RelativeSizeAxes = Axes.Both }; } [BackgroundDependencyLoader(true)] private void load(OverlayColourProvider? colourProvider, OsuColour colours) { Colour = defaultColour = colours.Gray8; hoverColour = colours.GrayF; highlightColour = colourProvider?.Highlight1 ?? colours.Green; } public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) { Vector2 size = new Vector2(SCROLL_BAR_HEIGHT) { [(int)ScrollDirection] = val }; this.ResizeTo(size, duration, easing); } protected override bool OnHover(HoverEvent e) { this.FadeColour(hoverColour, 100); return true; } protected override void OnHoverLost(HoverLostEvent e) { this.FadeColour(defaultColour, 100); } protected override bool OnMouseDown(MouseDownEvent e) { if (!base.OnMouseDown(e)) return false; // note that we are changing the colour of the box here as to not interfere with the hover effect. box.FadeColour(highlightColour, 100); return true; } protected override void OnMouseUp(MouseUpEvent e) { if (e.Button != MouseButton.Left) return; box.FadeColour(Color4.White, 100); base.OnMouseUp(e); } } } }