// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { public partial class RangeSlider : CompositeDrawable { /// /// The lower limiting value /// public Bindable LowerBound { get => lowerBound.Current; set => lowerBound.Current = value; } /// /// The upper limiting value /// public Bindable UpperBound { get => upperBound.Current; set => upperBound.Current = value; } /// /// Text that describes this RangeSlider's functionality /// public string Label { set => label.Text = value; } public float NubWidth { set => lowerBound.NubWidth = upperBound.NubWidth = value; } /// /// Minimum difference between the lower bound and higher bound /// public float MinRange { set => minRange = value; } /// /// lower bound display for when it is set to its default value /// public string DefaultStringLowerBound { set => lowerBound.DefaultString = value; } /// /// upper bound display for when it is set to its default value /// public string DefaultStringUpperBound { set => upperBound.DefaultString = value; } public LocalisableString DefaultTooltipLowerBound { set => lowerBound.DefaultTooltip = value; } public LocalisableString DefaultTooltipUpperBound { set => upperBound.DefaultTooltip = value; } public string TooltipSuffix { set => upperBound.TooltipSuffix = lowerBound.TooltipSuffix = value; } private float minRange = 0.1f; private readonly OsuSpriteText label; private readonly LowerBoundSlider lowerBound; private readonly UpperBoundSlider upperBound; public RangeSlider() { const float vertical_offset = 13; InternalChildren = new Drawable[] { label = new OsuSpriteText { Font = OsuFont.GetFont(size: 14), }, upperBound = new UpperBoundSlider { KeyboardStep = 0.1f, RelativeSizeAxes = Axes.X, Y = vertical_offset, }, lowerBound = new LowerBoundSlider { KeyboardStep = 0.1f, RelativeSizeAxes = Axes.X, Y = vertical_offset, }, upperBound.Nub.CreateProxy(), lowerBound.Nub.CreateProxy(), }; } protected override void LoadComplete() { base.LoadComplete(); lowerBound.Current.ValueChanged += min => upperBound.Current.Value = Math.Max(min.NewValue + minRange, upperBound.Current.Value); upperBound.Current.ValueChanged += max => lowerBound.Current.Value = Math.Min(max.NewValue - minRange, lowerBound.Current.Value); } private partial class LowerBoundSlider : BoundSlider { protected override void LoadComplete() { base.LoadComplete(); LeftBox.Height = 6; // hide any colour bleeding from overlap AccentColour = BackgroundColour; BackgroundColour = Color4.Transparent; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && screenSpacePos.X <= Nub.ScreenSpaceDrawQuad.TopRight.X; } private partial class UpperBoundSlider : BoundSlider { protected override void LoadComplete() { base.LoadComplete(); RightBox.Height = 6; // just to match the left bar height really } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && screenSpacePos.X >= Nub.ScreenSpaceDrawQuad.TopLeft.X; } protected partial class BoundSlider : RoundedSliderBar { public new Nub Nub => base.Nub; public string? DefaultString; public LocalisableString? DefaultTooltip; public string? TooltipSuffix; public float NubWidth { get; set; } = Nub.HEIGHT; public override LocalisableString TooltipText => (Current.IsDefault ? DefaultTooltip : Current.Value.ToString($@"0.## {TooltipSuffix}")) ?? Current.Value.ToString($@"0.## {TooltipSuffix}"); protected override bool OnHover(HoverEvent e) { base.OnHover(e); return true; // Make sure only one nub shows hover effect at once. } protected override void LoadComplete() { base.LoadComplete(); Nub.Width = NubWidth; RangePadding = Nub.Width / 2; OsuSpriteText currentDisplay; Nub.Add(currentDisplay = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Y = -0.5f, Colour = Color4.White, Font = OsuFont.Torus.With(size: 10), }); Current.BindValueChanged(current => { currentDisplay.Text = (current.NewValue != Current.Default ? current.NewValue.ToString("N1") : DefaultString) ?? current.NewValue.ToString("N1"); }, true); } [BackgroundDependencyLoader(true)] private void load(OverlayColourProvider? colourProvider) { if (colourProvider == null) return; AccentColour = colourProvider.Background2; Nub.AccentColour = colourProvider.Background2; Nub.GlowingAccentColour = colourProvider.Background1; Nub.GlowColour = colourProvider.Background2; } } } }