diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
new file mode 100644
index 0000000000..66e21fbf67
--- /dev/null
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -0,0 +1,72 @@
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input;
+using OpenTK.Input;
+
+namespace osu.Game.Graphics.Containers
+{
+ class OsuScrollContainer : ScrollContainer
+ {
+ ///
+ /// Add the ability to seek to an absolute scroll position when the right mouse button is pressed or dragged.
+ /// Uses the value of to smoothly scroll to the dragged location.
+ ///
+ public bool RightMouseScrollbar = false;
+
+ ///
+ /// 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 shouldPerformRelativeDrag(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right);
+
+ private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar);
+
+ private bool mouseScrollBarDragging;
+
+ protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ if (shouldPerformRelativeDrag(state))
+ {
+ scrollToRelative(state.Mouse.Position[ScrollDim]);
+ return true;
+ }
+
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnDrag(InputState state)
+ {
+ if (mouseScrollBarDragging)
+ {
+ scrollToRelative(state.Mouse.Position[ScrollDim]);
+ return true;
+ }
+
+ return base.OnDrag(state);
+ }
+
+ protected override bool OnDragStart(InputState state)
+ {
+ if (shouldPerformRelativeDrag(state))
+ {
+ mouseScrollBarDragging = true;
+ return true;
+ }
+
+ return base.OnDragStart(state);
+ }
+
+ protected override bool OnDragEnd(InputState state)
+ {
+ if (mouseScrollBarDragging)
+ {
+ mouseScrollBarDragging = false;
+ return true;
+ }
+
+ return base.OnDragEnd(state);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index 1d792f1b78..37c3804543 100644
--- a/osu.Game/Graphics/Containers/SectionsContainer.cs
+++ b/osu.Game/Graphics/Containers/SectionsContainer.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Graphics.Containers
public SectionsContainer()
{
- Add(ScrollContainer = new ScrollContainer()
+ Add(ScrollContainer = new OsuScrollContainer()
{
RelativeSizeAxes = Axes.Both,
Masking = false,
diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
index 9f61d13813..135f8f43b9 100644
--- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
+++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Chat
Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING },
Children = new[]
{
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new[]
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index f8b7c7e581..e51f931959 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
@@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Chat
Children = new Drawable[]
{
- scroll = new ScrollContainer
+ scroll = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index eeb072fb00..ca46bdea95 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Database;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Music
{
@@ -49,7 +50,7 @@ namespace osu.Game.Overlays.Music
{
Children = new Drawable[]
{
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationManager.cs
index 382683cbcc..18cb49f335 100644
--- a/osu.Game/Overlays/NotificationManager.cs
+++ b/osu.Game/Overlays/NotificationManager.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Overlays
Colour = Color4.Black,
Alpha = 0.6f,
},
- scrollContainer = new ScrollContainer
+ scrollContainer = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = Toolbar.Toolbar.HEIGHT },
diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
index 25f6b4f60b..c5b8b0cf85 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics.Backgrounds;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
@@ -60,7 +61,7 @@ namespace osu.Game.Overlays.SearchableList
RelativeSizeAxes = Axes.Both,
Children = new[]
{
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs
index 3a822be791..761204dda4 100644
--- a/osu.Game/Screens/Multiplayer/RoomInspector.cs
+++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs
@@ -17,6 +17,7 @@ using osu.Framework.Localisation;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Users;
@@ -341,7 +342,7 @@ namespace osu.Game.Screens.Multiplayer
},
},
},
- participantsScroll = new ScrollContainer
+ participantsScroll = new OsuScrollContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index e560cfe413..2a8e4ca5fb 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -12,6 +12,7 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Database;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Scoring;
using osu.Game.Online.API;
@@ -74,7 +75,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{
Children = new Drawable[]
{
- scrollContainer = new ScrollContainer
+ scrollContainer = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3d84f287f0..691df1d2d1 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -78,6 +78,7 @@
+