1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 10:33:30 +08:00

Merge branch 'master' into skin-editor-undo-support

This commit is contained in:
Dean Herbert 2023-02-08 15:41:58 +09:00
commit 3a3c2e78a1
43 changed files with 821 additions and 228 deletions

View File

@ -29,6 +29,7 @@ namespace osu.Desktop
internal partial class OsuGameDesktop : OsuGame
{
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
private ArchiveImportIPCChannel? archiveImportIPCChannel;
public OsuGameDesktop(string[]? args = null)
: base(args)
@ -123,6 +124,7 @@ namespace osu.Desktop
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
}
public override void SetHost(GameHost host)
@ -181,6 +183,7 @@ namespace osu.Desktop
{
base.Dispose(isDisposing);
osuSchemeLinkIPCChannel?.Dispose();
archiveImportIPCChannel?.Dispose();
}
private class SDL2BatteryInfo : BatteryInfo

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
};
}
private partial class ManiaScrollSlider : OsuSliderBar<double>
private partial class ManiaScrollSlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
}

View File

@ -214,7 +214,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration));
// It is intentional that we don't consider the loop count (40) as part of the end time calculation to match stable's handling.
// If we were to include the loop count, storyboards which loop for stupid long loop counts would continue playing the outro forever.
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + loop_duration));
}
}
}

View File

@ -31,8 +31,8 @@ namespace osu.Game.Tests.Visual.Audio
private WaveformTestBeatmap beatmap;
private OsuSliderBar<int> lowPassSlider;
private OsuSliderBar<int> highPassSlider;
private RoundedSliderBar<int> lowPassSlider;
private RoundedSliderBar<int> highPassSlider;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Audio
Text = $"Low Pass: {lowPassFilter.Cutoff}hz",
Font = new FontUsage(size: 40)
},
lowPassSlider = new OsuSliderBar<int>
lowPassSlider = new RoundedSliderBar<int>
{
Width = 500,
Height = 50,
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Audio
Text = $"High Pass: {highPassFilter.Cutoff}hz",
Font = new FontUsage(size: 40)
},
highPassSlider = new OsuSliderBar<int>
highPassSlider = new RoundedSliderBar<int>
{
Width = 500,
Height = 50,

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Framework;
@ -14,6 +15,8 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Visual.Navigation
{
@ -23,11 +26,13 @@ namespace osu.Game.Tests.Visual.Navigation
{
private HeadlessGameHost ipcSenderHost = null!;
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCReceiver = null!;
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!;
private ArchiveImportIPCChannel archiveImportIPCSender = null!;
private const int requested_beatmap_set_id = 1;
protected override TestOsuGame CreateTestGame() => new IpcGame(LocalStorage, API);
[Resolved]
private GameHost gameHost { get; set; } = null!;
@ -56,11 +61,11 @@ namespace osu.Game.Tests.Visual.Navigation
return false;
};
});
AddStep("create IPC receiver channel", () => osuSchemeLinkIPCReceiver = new OsuSchemeLinkIPCChannel(gameHost, Game));
AddStep("create IPC sender channel", () =>
AddStep("create IPC sender channels", () =>
{
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
});
}
@ -72,15 +77,50 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
}
[Test]
public void TestArchiveImportLinkIPCChannel()
{
string? beatmapFilepath = null;
AddStep("import beatmap via IPC", () => archiveImportIPCSender.ImportAsync(beatmapFilepath = TestResources.GetQuickTestBeatmapForImport()).WaitSafely());
AddUntilStep("import complete notification was presented", () => Game.Notifications.ChildrenOfType<ProgressCompletionNotification>().Count(), () => Is.EqualTo(1));
AddAssert("original file deleted", () => File.Exists(beatmapFilepath), () => Is.False);
}
public override void TearDownSteps()
{
AddStep("dispose IPC receiver", () => osuSchemeLinkIPCReceiver.Dispose());
AddStep("dispose IPC sender", () =>
AddStep("dispose IPC senders", () =>
{
osuSchemeLinkIPCSender.Dispose();
archiveImportIPCSender.Dispose();
ipcSenderHost.Dispose();
});
base.TearDownSteps();
}
private partial class IpcGame : TestOsuGame
{
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
private ArchiveImportIPCChannel? archiveImportIPCChannel;
public IpcGame(Storage storage, IAPIProvider api, string[]? args = null)
: base(storage, api, args)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
osuSchemeLinkIPCChannel?.Dispose();
archiveImportIPCChannel?.Dispose();
}
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Profile.Header.Components;
using osuTK;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public partial class TestSceneGroupBadges : OsuTestScene
{
public TestSceneGroupBadges()
{
var groups = new[]
{
new APIUser(),
new APIUser
{
Groups = new[]
{
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
}
},
new APIUser
{
Groups = new[]
{
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
}
},
new APIUser
{
Groups = new[]
{
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
}
},
new APIUser
{
Groups = new[]
{
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
new APIUserGroup { Colour = "#999999", ShortName = "ALM", Name = "osu! Alumni" },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko" }, IsProbationary = true }
}
}
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Colour4.DarkGray
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(40),
Children = new[]
{
new FillFlowContainer<GroupBadgeFlow>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5),
ChildrenEnumerable = groups.Select(g => new GroupBadgeFlow { User = { Value = g } })
},
}
}
};
}
}
}

View File

@ -90,7 +90,9 @@ namespace osu.Game.Tests.Visual.Online
{
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "mania" } },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko", "fruits", "mania" } },
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko", "fruits", "mania" }, IsProbationary = true }
},
ProfileOrder = new[]
{

View File

@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep($"Set {name} slider to {value}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = value);
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = value);
}
private void checkBindableAtValue(string name, float? expectedValue)
@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddAssert($"Slider {name} at {expectedValue}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value == expectedValue);
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value == expectedValue);
}
private void setBeatmapWithDifficultyParameters(float value)

View File

@ -270,7 +270,7 @@ namespace osu.Game.Tests.Visual.UserInterface
createScreen();
AddStep("select difficulty adjust via panel", () => getPanelForMod(typeof(OsuModDifficultyAdjust)).TriggerClick());
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = 8);
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = 8);
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);

View File

@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public partial class TestSceneShearedSliderBar : OsuTestScene
{
[Cached]
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Purple);
private readonly BindableDouble current = new BindableDouble(5)
{
Precision = 0.1f,
MinValue = 0,
MaxValue = 15
};
[BackgroundDependencyLoader]
private void load()
{
Child = new ShearedSliderBar<double>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Current = current,
RelativeSizeAxes = Axes.X,
Width = 0.4f
};
}
}
}

View File

@ -44,8 +44,11 @@ namespace osu.Game.Graphics.Containers
content.AutoSizeAxes = AutoSizeAxes;
}
AddInternal(content);
Add(CreateHoverSounds(sampleSet));
AddRangeInternal(new Drawable[]
{
content,
CreateHoverSounds(sampleSet)
});
}
protected override void ClearInternal(bool disposeChildren = true) =>

View File

@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface
/// </summary>
public partial class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
where TSlider : OsuSliderBar<T>, new()
where TSlider : RoundedSliderBar<T>, new()
{
private readonly OsuSpriteText label;
private readonly TSlider slider;
@ -130,7 +130,7 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
/// </summary>
public partial class ExpandableSlider<T> : ExpandableSlider<T, OsuSliderBar<T>>
public partial class ExpandableSlider<T> : ExpandableSlider<T, RoundedSliderBar<T>>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
}

View File

@ -44,7 +44,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnClick(ClickEvent e)
{
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
if (buttons.Contains(e.Button))
{
var channel = Enabled.Value ? sampleClick?.GetChannel() : sampleClickDisabled?.GetChannel();

View File

@ -5,19 +5,22 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Configuration;
using osuTK;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// Handles debouncing hover sounds at a global level to ensure the effects are not overwhelming.
/// </summary>
public abstract partial class HoverSampleDebounceComponent : CompositeDrawable
public abstract partial class HoverSampleDebounceComponent : Component
{
private Bindable<double?> lastPlaybackTime;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) == true;
[BackgroundDependencyLoader]
private void load(SessionStatics statics)
{

View File

@ -1,10 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using JetBrains.Annotations;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@ -58,7 +55,7 @@ namespace osu.Game.Graphics.UserInterface
}
[BackgroundDependencyLoader(true)]
private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour colours)
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
GlowingAccentColour = colourProvider?.Highlight1.Lighten(0.2f) ?? colours.PinkLighter;

View File

@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface
});
if (hoverSounds.HasValue)
Add(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
AddInternal(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
}
[BackgroundDependencyLoader]

View File

@ -1,195 +1,59 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Globalization;
using JetBrains.Annotations;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Framework.Utils;
using osu.Game.Overlays;
using osu.Game.Utils;
namespace osu.Game.Graphics.UserInterface
{
public partial class OsuSliderBar<T> : SliderBar<T>, IHasTooltip, IHasAccentColour
public abstract partial class OsuSliderBar<T> : SliderBar<T>, IHasTooltip
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
/// <summary>
/// Maximum number of decimal digits to be displayed in the tooltip.
/// </summary>
private const int max_decimal_digits = 5;
private Sample sample;
private double lastSampleTime;
private T lastSampleValue;
protected readonly Nub Nub;
protected readonly Box LeftBox;
protected readonly Box RightBox;
private readonly Container nubContainer;
public virtual LocalisableString TooltipText { get; private set; }
public bool PlaySamplesOnAdjust { get; set; } = true;
private readonly HoverClickSounds hoverClickSounds;
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
public bool DisplayAsPercentage { get; set; }
private Color4 accentColour;
public virtual LocalisableString TooltipText { get; private set; }
public Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
LeftBox.Colour = value;
}
}
/// <summary>
/// Maximum number of decimal digits to be displayed in the tooltip.
/// </summary>
private const int max_decimal_digits = 5;
private Colour4 backgroundColour;
private Sample sample = null!;
public Color4 BackgroundColour
{
get => backgroundColour;
set
{
backgroundColour = value;
RightBox.Colour = value;
}
}
private double lastSampleTime;
private T lastSampleValue;
public OsuSliderBar()
{
Height = Nub.HEIGHT;
RangePadding = Nub.EXPANDED_SIZE / 2;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Padding = new MarginPadding { Horizontal = 2 },
Child = new CircularContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Masking = true,
CornerRadius = 5f,
Children = new Drawable[]
{
LeftBox = new Box
{
Height = 5,
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.None,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
RightBox = new Box
{
Height = 5,
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.None,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
},
},
},
},
nubContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Child = Nub = new Nub
{
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.X,
Current = { Value = true }
},
},
hoverClickSounds = new HoverClickSounds()
};
}
[BackgroundDependencyLoader(true)]
private void load(AudioManager audio, [CanBeNull] OverlayColourProvider colourProvider, OsuColour colours)
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sample = audio.Samples.Get(@"UI/notch-tick");
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
}
protected override void Update()
{
base.Update();
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
}
protected override void LoadComplete()
{
base.LoadComplete();
CurrentNumber.BindValueChanged(current => TooltipText = getTooltipText(current.NewValue), true);
Current.BindDisabledChanged(disabled =>
{
Alpha = disabled ? 0.3f : 1;
hoverClickSounds.Enabled.Value = !disabled;
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateGlow();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateGlow();
base.OnHoverLost(e);
}
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
protected override void OnDragEnd(DragEndEvent e)
{
updateGlow();
base.OnDragEnd(e);
}
private void updateGlow()
{
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
}
protected override void OnUserChange(T value)
{
base.OnUserChange(value);
playSample(value);
TooltipText = getTooltipText(value);
}
@ -236,18 +100,6 @@ namespace osu.Game.Graphics.UserInterface
return floatValue.ToString($"N{significantDigits}");
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
}
protected override void UpdateValue(float value)
{
Nub.MoveToX(value, 250, Easing.OutQuint);
}
/// <summary>
/// Removes all non-significant digits, keeping at most a requested number of decimal digits.
/// </summary>

View File

@ -158,7 +158,7 @@ namespace osu.Game.Graphics.UserInterface
&& screenSpacePos.X >= Nub.ScreenSpaceDrawQuad.TopLeft.X;
}
protected partial class BoundSlider : OsuSliderBar<double>
protected partial class BoundSlider : RoundedSliderBar<double>
{
public string? DefaultString;
public LocalisableString? DefaultTooltip;

View File

@ -0,0 +1,170 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Overlays;
namespace osu.Game.Graphics.UserInterface
{
public partial class RoundedSliderBar<T> : OsuSliderBar<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
protected readonly Nub Nub;
protected readonly Box LeftBox;
protected readonly Box RightBox;
private readonly Container nubContainer;
private readonly HoverClickSounds hoverClickSounds;
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
LeftBox.Colour = value;
}
}
private Colour4 backgroundColour;
public Color4 BackgroundColour
{
get => backgroundColour;
set
{
backgroundColour = value;
RightBox.Colour = value;
}
}
public RoundedSliderBar()
{
Height = Nub.HEIGHT;
RangePadding = Nub.EXPANDED_SIZE / 2;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Padding = new MarginPadding { Horizontal = 2 },
Child = new CircularContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Masking = true,
CornerRadius = 5f,
Children = new Drawable[]
{
LeftBox = new Box
{
Height = 5,
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.None,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
RightBox = new Box
{
Height = 5,
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.None,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
},
},
},
},
nubContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Child = Nub = new Nub
{
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.X,
Current = { Value = true }
},
},
hoverClickSounds = new HoverClickSounds()
};
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
}
protected override void Update()
{
base.Update();
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindDisabledChanged(disabled =>
{
Alpha = disabled ? 0.3f : 1;
hoverClickSounds.Enabled.Value = !disabled;
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateGlow();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateGlow();
base.OnHoverLost(e);
}
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
protected override void OnDragEnd(DragEndEvent e)
{
updateGlow();
base.OnDragEnd(e);
}
private void updateGlow()
{
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
}
protected override void UpdateValue(float value)
{
Nub.MoveToX(value, 250, Easing.OutQuint);
}
}
}

View File

@ -0,0 +1,183 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public partial class ShearedNub : Container, IHasCurrentValue<bool>, IHasAccentColour
{
protected const float BORDER_WIDTH = 3;
public const int HEIGHT = 30;
public const float EXPANDED_SIZE = 50;
public static readonly Vector2 SHEAR = new Vector2(0.15f, 0);
private readonly Box fill;
private readonly Container main;
/// <summary>
/// Implements the shape for the nub, allowing for any type of container to be used.
/// </summary>
/// <returns></returns>
public ShearedNub()
{
Size = new Vector2(EXPANDED_SIZE, HEIGHT);
InternalChild = main = new Container
{
Shear = SHEAR,
BorderColour = Colour4.White,
BorderThickness = BORDER_WIDTH,
Masking = true,
CornerRadius = 5,
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = fill = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
}
};
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
GlowingAccentColour = colourProvider?.Highlight1.Lighten(0.4f) ?? colours.PinkLighter;
GlowColour = colourProvider?.Highlight1 ?? colours.PinkLighter;
main.EdgeEffect = new EdgeEffectParameters
{
Colour = GlowColour.Opacity(0),
Type = EdgeEffectType.Glow,
Radius = 8,
Roundness = 4,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(onCurrentValueChanged, true);
}
private bool glowing;
public bool Glowing
{
get => glowing;
set
{
if (glowing == value)
return;
glowing = value;
if (value)
{
main.FadeColour(GlowingAccentColour.Lighten(0.1f), 40, Easing.OutQuint)
.Then()
.FadeColour(GlowingAccentColour, 800, Easing.OutQuint);
main.FadeEdgeEffectTo(Color4.White.Opacity(0.1f), 40, Easing.OutQuint)
.Then()
.FadeEdgeEffectTo(GlowColour.Opacity(0.1f), 800, Easing.OutQuint);
}
else
{
main.FadeEdgeEffectTo(GlowColour.Opacity(0), 800, Easing.OutQuint);
main.FadeColour(AccentColour, 800, Easing.OutQuint);
}
}
}
private readonly Bindable<bool> current = new Bindable<bool>();
public Bindable<bool> Current
{
get => current;
set
{
ArgumentNullException.ThrowIfNull(value);
current.UnbindBindings();
current.BindTo(value);
}
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
if (!Glowing)
main.Colour = value;
}
}
private Color4 glowingAccentColour;
public Color4 GlowingAccentColour
{
get => glowingAccentColour;
set
{
glowingAccentColour = value;
if (Glowing)
main.Colour = value;
}
}
private Color4 glowColour;
public Color4 GlowColour
{
get => glowColour;
set
{
glowColour = value;
var effect = main.EdgeEffect;
effect.Colour = Glowing ? value : value.Opacity(0);
main.EdgeEffect = effect;
}
}
private void onCurrentValueChanged(ValueChangedEvent<bool> filled)
{
const double duration = 200;
fill.FadeTo(filled.NewValue ? 1 : 0, duration, Easing.OutQuint);
if (filled.NewValue)
{
main.ResizeWidthTo(1, duration, Easing.OutElasticHalf);
main.TransformTo(nameof(BorderThickness), 8.5f, duration, Easing.OutElasticHalf);
}
else
{
main.ResizeWidthTo(0.75f, duration, Easing.OutQuint);
main.TransformTo(nameof(BorderThickness), BORDER_WIDTH, duration, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,173 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Overlays;
using static osu.Game.Graphics.UserInterface.ShearedNub;
namespace osu.Game.Graphics.UserInterface
{
public partial class ShearedSliderBar<T> : OsuSliderBar<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
protected readonly ShearedNub Nub;
protected readonly Box LeftBox;
protected readonly Box RightBox;
private readonly Container nubContainer;
private readonly HoverClickSounds hoverClickSounds;
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
// We want to slightly darken the colour for the box because the sheared slider has the boxes at the same height as the nub,
// making the nub invisible when not hovered.
LeftBox.Colour = value.Darken(0.1f);
}
}
private Colour4 backgroundColour;
public Color4 BackgroundColour
{
get => backgroundColour;
set
{
backgroundColour = value;
RightBox.Colour = value;
}
}
public ShearedSliderBar()
{
Shear = SHEAR;
Height = HEIGHT;
RangePadding = EXPANDED_SIZE / 2;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Padding = new MarginPadding { Horizontal = 2 },
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Masking = true,
CornerRadius = 5,
Children = new Drawable[]
{
LeftBox = new Box
{
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
RightBox = new Box
{
EdgeSmoothness = new Vector2(0, 0.5f),
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
},
},
},
},
nubContainer = new Container
{
Shear = -SHEAR,
RelativeSizeAxes = Axes.Both,
Child = Nub = new ShearedNub
{
X = -SHEAR.X * HEIGHT / 2f,
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.X,
Current = { Value = true }
},
},
hoverClickSounds = new HoverClickSounds()
};
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
}
protected override void Update()
{
base.Update();
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindDisabledChanged(disabled =>
{
Alpha = disabled ? 0.3f : 1;
hoverClickSounds.Enabled.Value = !disabled;
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateGlow();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateGlow();
base.OnHoverLost(e);
}
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
protected override void OnDragEnd(DragEndEvent e)
{
updateGlow();
base.OnDragEnd(e);
}
private void updateGlow()
{
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2.15f, 0, Math.Max(0, DrawWidth)), 1);
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2.15f, 0, Math.Max(0, DrawWidth)), 1);
}
protected override void UpdateValue(float value)
{
Nub.MoveToX(value, 250, Easing.OutQuint);
}
}
}

View File

@ -10,7 +10,7 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// A slider bar which displays a millisecond time value.
/// </summary>
public partial class TimeSlider : OsuSliderBar<double>
public partial class TimeSlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => $"{Current.Value:N0} ms";
}

View File

@ -315,10 +315,10 @@ namespace osu.Game.Overlays
channelListing.Hide();
textBar.ShowSearch.Value = false;
if (loadedChannels.ContainsKey(newChannel))
if (loadedChannels.TryGetValue(newChannel, out var loadedChannel))
{
currentChannelContainer.Clear(false);
currentChannelContainer.Add(loadedChannels[newChannel]);
currentChannelContainer.Add(loadedChannel);
}
else
{

View File

@ -76,6 +76,7 @@ namespace osu.Game.Overlays.Comments
private GridContainer content = null!;
private VotePill votePill = null!;
private Container<CommentEditor> replyEditorContainer = null!;
private Container repliesButtonContainer = null!;
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
@ -239,10 +240,12 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Top = 10 },
Alpha = 0,
},
new Container
repliesButtonContainer = new Container
{
AutoSizeAxes = Axes.Both,
Alpha = 0,
Children = new Drawable[]
{
showRepliesButton = new ShowRepliesButton(Comment.RepliesCount)
@ -449,6 +452,7 @@ namespace osu.Game.Overlays.Comments
{
if (replyEditorContainer.Count == 0)
{
replyEditorContainer.Show();
replyEditorContainer.Add(new ReplyCommentEditor(Comment)
{
OnPost = comments =>
@ -456,12 +460,14 @@ namespace osu.Game.Overlays.Comments
Comment.RepliesCount += comments.Length;
showRepliesButton.Count = Comment.RepliesCount;
Replies.AddRange(comments);
}
},
OnCancel = toggleReply
});
}
else
{
replyEditorContainer.Clear(true);
replyEditorContainer.ForEach(e => e.Expire());
replyEditorContainer.Hide();
}
}
@ -513,9 +519,11 @@ namespace osu.Game.Overlays.Comments
int loadedRepliesCount = loadedReplies.Count;
bool hasUnloadedReplies = loadedRepliesCount != Comment.RepliesCount;
loadRepliesButton.FadeTo(hasUnloadedReplies && loadedRepliesCount == 0 ? 1 : 0);
showMoreButton.FadeTo(hasUnloadedReplies && loadedRepliesCount > 0 ? 1 : 0);
showRepliesButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);
loadRepliesButton.FadeTo(hasUnloadedReplies && loadedRepliesCount == 0 ? 1 : 0);
repliesButtonContainer.FadeTo(repliesButtonContainer.Any(child => child.Alpha > 0) ? 1 : 0);
showMoreButton.FadeTo(hasUnloadedReplies && loadedRepliesCount > 0 ? 1 : 0);
if (Comment.IsTopLevel)
chevronButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);

View File

@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Game.Online.API;
@ -33,7 +32,6 @@ namespace osu.Game.Overlays.Comments
public ReplyCommentEditor(Comment parent)
{
parentComment = parent;
OnCancel = () => this.FadeOut(200).Expire();
}
protected override void LoadComplete()

View File

@ -107,7 +107,7 @@ namespace osu.Game.Overlays.FirstRunSetup
public override bool? AllowTrackAdjustments => false;
}
private partial class UIScaleSlider : OsuSliderBar<float>
private partial class UIScaleSlider : RoundedSliderBar<float>
{
public override LocalisableString TooltipText => base.TooltipText + "x";
}

View File

@ -35,6 +35,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
CornerRadius = 8;
TooltipText = group.Name;
if (group.IsProbationary)
{
Alpha = 0.6f;
}
}
[BackgroundDependencyLoader]
@ -47,7 +52,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider?.Background6 ?? Colour4.Black
Colour = colourProvider?.Background6 ?? Colour4.Black,
// Normal badges background opacity is 75%, probationary is full opacity as the whole badge gets a bit transparent
// Goal is to match osu-web so this is the most accurate it can be, its a bit scuffed but it is what it is
// Source: https://github.com/ppy/osu-web/blob/master/resources/css/bem/user-group-badge.less#L50
Alpha = group.IsProbationary ? 1 : 0.75f,
},
innerContainer = new FillFlowContainer
{

View File

@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
protected override Drawable CreateControl()
{
var sliderBar = (OsuSliderBar<double>)base.CreateControl();
var sliderBar = (RoundedSliderBar<double>)base.CreateControl();
sliderBar.PlaySamplesOnAdjust = false;
return sliderBar;
}

View File

@ -329,7 +329,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}
}
private partial class UIScaleSlider : OsuSliderBar<float>
private partial class UIScaleSlider : RoundedSliderBar<float>
{
public override LocalisableString TooltipText => base.TooltipText + "x";
}

View File

@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
}
}
public partial class SensitivitySlider : OsuSliderBar<double>
public partial class SensitivitySlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x";
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections
/// <summary>
/// A slider intended to show a "size" multiplier number, where 1x is 1.0.
/// </summary>
public partial class SizeSlider<T> : OsuSliderBar<T>
public partial class SizeSlider<T> : RoundedSliderBar<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible, IFormattable
{
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo);

View File

@ -10,14 +10,14 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public partial class SettingsSlider<T> : SettingsSlider<T, OsuSliderBar<T>>
public partial class SettingsSlider<T> : SettingsSlider<T, RoundedSliderBar<T>>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
}
public partial class SettingsSlider<TValue, TSlider> : SettingsItem<TValue>
where TValue : struct, IEquatable<TValue>, IComparable<TValue>, IConvertible
where TSlider : OsuSliderBar<TValue>, new()
where TSlider : RoundedSliderBar<TValue>, new()
{
protected override Drawable CreateControl() => new TSlider
{

View File

@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Edit
/// </summary>
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) == true;
protected override bool Handle(UIEvent e)
{

View File

@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mods
{
InternalChildren = new Drawable[]
{
new OsuSliderBar<float>
new RoundedSliderBar<float>
{
RelativeSizeAxes = Axes.X,
Current = currentNumber,

View File

@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mods
}
}
public partial class PercentSlider : OsuSliderBar<double>
public partial class PercentSlider : RoundedSliderBar<double>
{
public PercentSlider()
{

View File

@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mods
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
}
public partial class MuteComboSlider : OsuSliderBar<int>
public partial class MuteComboSlider : RoundedSliderBar<int>
{
public override LocalisableString TooltipText => Current.Value == 0 ? "always muted" : base.TooltipText;
}

View File

@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mods
}
}
public partial class HiddenComboSlider : OsuSliderBar<int>
public partial class HiddenComboSlider : RoundedSliderBar<int>
{
public override LocalisableString TooltipText => Current.Value == 0 ? "always hidden" : base.TooltipText;
}

View File

@ -44,6 +44,14 @@ namespace osu.Game.Screens.Play
});
}
public void StopAllSamples()
{
if (!IsLoaded)
return;
pauseLoop.Stop();
}
protected override void PopIn()
{
base.PopIn();

View File

@ -1073,7 +1073,10 @@ namespace osu.Game.Screens.Play
public override bool OnExiting(ScreenExitEvent e)
{
screenSuspension?.RemoveAndDisposeImmediately();
// Eagerly clean these up as disposal of child components is asynchronous and may leave sounds playing beyond user expectations.
failAnimationLayer?.Stop();
PauseOverlay?.StopAllSamples();
if (LoadedBeatmapSuccessfully)
{

View File

@ -15,11 +15,11 @@ namespace osu.Game.Screens.Play.PlayerSettings
public partial class PlayerSliderBar<T> : SettingsSlider<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
public OsuSliderBar<T> Bar => (OsuSliderBar<T>)Control;
public RoundedSliderBar<T> Bar => (RoundedSliderBar<T>)Control;
protected override Drawable CreateControl() => new SliderBar();
protected partial class SliderBar : OsuSliderBar<T>
protected partial class SliderBar : RoundedSliderBar<T>
{
public SliderBar()
{

View File

@ -46,7 +46,8 @@ namespace osu.Game.Screens.Select.Carousel
Children = new Drawable[]
{
Content,
hoverLayer = new HoverLayer()
hoverLayer = new HoverLayer(),
new HeaderSounds(),
}
};
}
@ -91,10 +92,8 @@ namespace osu.Game.Screens.Select.Carousel
}
}
public partial class HoverLayer : HoverSampleDebounceComponent
public partial class HoverLayer : CompositeDrawable
{
private Sample? sampleHover;
private Box box = null!;
public HoverLayer()
@ -103,7 +102,7 @@ namespace osu.Game.Screens.Select.Carousel
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
private void load(OsuColour colours)
{
InternalChild = box = new Box
{
@ -112,8 +111,6 @@ namespace osu.Game.Screens.Select.Carousel
Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both,
};
sampleHover = audio.Samples.Get("UI/default-hover");
}
public bool InsetForBorder
@ -147,6 +144,17 @@ namespace osu.Game.Screens.Select.Carousel
box.FadeOut(1000, Easing.OutQuint);
base.OnHoverLost(e);
}
}
private partial class HeaderSounds : HoverSampleDebounceComponent
{
private Sample? sampleHover;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleHover = audio.Samples.Get("UI/default-hover");
}
public override void PlayHoverSample()
{

View File

@ -18,7 +18,12 @@ namespace osu.Game.Storyboards
public readonly int TotalIterations;
public override double StartTime => LoopStartTime + CommandsStartTime;
public override double EndTime => StartTime + CommandsDuration * TotalIterations;
public override double EndTime =>
// In an ideal world, we would multiply the command duration by TotalIterations here.
// Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro
// sequences for minutes or hours.
StartTime + CommandsDuration;
/// <summary>
/// Construct a new command loop.

View File

@ -84,9 +84,6 @@ namespace osu.Game.Storyboards
[JsonIgnore]
public virtual double EndTime => CommandsEndTime;
[JsonIgnore]
public double Duration => EndTime - StartTime;
[JsonIgnore]
public bool HasCommands
{