diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 1bc22da8ac..2276b9f9f4 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -54,7 +54,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor for (int i = 0; i < max_sprites; i++) { - parts[i].InvalidationID = 0; + // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node + // This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms + parts[i].InvalidationID = 1; parts[i].WasUpdated = true; } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 590ee4e720..8fe31b7ad6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -84,7 +84,6 @@ namespace osu.Game.Tests.Visual.UserInterface testLocalCursor(); testUserCursorOverride(); testMultipleLocalCursors(); - ReturnUserInput(); } /// diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs new file mode 100644 index 0000000000..dbef7d1686 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs @@ -0,0 +1,194 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneOsuHoverContainer : ManualInputManagerTestScene + { + private OsuHoverTestContainer hoverContainer; + private Box colourContainer; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = hoverContainer = new OsuHoverTestContainer + { + Enabled = { Value = true }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(100), + Child = colourContainer = new Box + { + RelativeSizeAxes = Axes.Both, + }, + }; + + doMoveOut(); + }); + + [Description("Checks IsHovered property value on a container when it is hovered/unhovered.")] + [TestCase(true, TestName = "Enabled_Check_IsHovered")] + [TestCase(false, TestName = "Disabled_Check_IsHovered")] + public void TestIsHoveredHasProperValue(bool isEnabled) + { + setContainerEnabledTo(isEnabled); + + checkNotHovered(); + + moveToText(); + checkHovered(); + + moveOut(); + checkNotHovered(); + + moveToText(); + checkHovered(); + + moveOut(); + checkNotHovered(); + } + + [Test] + [Description("Checks colour fading on an enabled container when it is hovered/unhovered.")] + public void TestTransitionWhileEnabled() + { + enableContainer(); + + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + + moveOut(); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + + moveOut(); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); + } + + [Test] + [Description("Checks colour fading on a disabled container when it is hovered/unhovered.")] + public void TestNoTransitionWhileDisabled() + { + disableContainer(); + + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveOut(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveOut(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + } + + [Test] + [Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")] + public void TestBecomesEnabledTransition() + { + disableContainer(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + enableContainer(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + } + + [Test] + [Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")] + public void TestBecomesDisabledTransition() + { + enableContainer(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + + disableContainer(); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); + } + + [Test] + [Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")] + public void TestDisabledChangesMultipleTimes() + { + enableContainer(); + checkColour(OsuHoverTestContainer.IDLE_COLOUR); + + moveToText(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + + disableContainer(); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); + + enableContainer(); + waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR); + + disableContainer(); + waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR); + } + + private void enableContainer() => setContainerEnabledTo(true); + + private void disableContainer() => setContainerEnabledTo(false); + + private void setContainerEnabledTo(bool newValue) + { + string word = newValue ? "Enable" : "Disable"; + AddStep($"{word} container", () => hoverContainer.Enabled.Value = newValue); + } + + private void moveToText() => AddStep("Move mouse to text", () => InputManager.MoveMouseTo(hoverContainer)); + + private void moveOut() => AddStep("Move out", doMoveOut); + + private void checkHovered() => AddAssert("Check hovered", () => hoverContainer.IsHovered); + + private void checkNotHovered() => AddAssert("Check not hovered", () => !hoverContainer.IsHovered); + + private void checkColour(ColourInfo expectedColour) + => AddAssert($"Check colour to be '{expectedColour}'", () => currentColour.Equals(expectedColour)); + + private void waitUntilColourIs(ColourInfo expectedColour) + => AddUntilStep($"Wait until hover colour is {expectedColour}", () => currentColour.Equals(expectedColour)); + + private ColourInfo currentColour => colourContainer.DrawColourInfo.Colour; + + /// + /// Moves the cursor to top left corner of the screen + /// + private void doMoveOut() + => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.TopLeft.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + + private sealed class OsuHoverTestContainer : OsuHoverContainer + { + public static readonly Color4 HOVER_COLOUR = Color4.Red; + public static readonly Color4 IDLE_COLOUR = Color4.Green; + + public OsuHoverTestContainer() + { + HoverColour = HOVER_COLOUR; + IdleColour = IDLE_COLOUR; + } + } + } +} diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index d479483508..6e162ca95e 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -1,6 +1,10 @@ // 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 System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -12,22 +16,24 @@ using osu.Game.Beatmaps; namespace osu.Game.Audio { - /// - /// A central store for the retrieval of s. - /// public class PreviewTrackManager : Component { private readonly BindableDouble muteBindable = new BindableDouble(); private AudioManager audio; - private ITrackStore trackStore; + private PreviewTrackStore trackStore; private TrackManagerPreviewTrack current; [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { - trackStore = audio.GetTrackStore(new OnlineStore()); + // this is a temporary solution to get around muting ourselves. + // todo: update this once we have a BackgroundTrackManager or similar. + trackStore = new PreviewTrackStore(new OnlineStore()); + + audio.AddItem(trackStore); + trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack); this.audio = audio; @@ -103,5 +109,46 @@ namespace osu.Game.Audio protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); } + + private class PreviewTrackStore : AudioCollectionManager, ITrackStore + { + private readonly IResourceStore store; + + internal PreviewTrackStore(IResourceStore store) + { + this.store = store; + } + + public Track GetVirtual(double length = double.PositiveInfinity) + { + if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}"); + + var track = new TrackVirtual(length); + AddItem(track); + return track; + } + + public Track Get(string name) + { + if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}"); + + if (string.IsNullOrEmpty(name)) return null; + + var dataStream = store.GetStream(name); + + if (dataStream == null) + return null; + + Track track = new TrackBass(dataStream); + AddItem(track); + return track; + } + + public Task GetAsync(string name) => Task.Run(() => Get(name)); + + public Stream GetStream(string name) => store.GetStream(name); + + public IEnumerable GetAvailableResources() => store.GetAvailableResources(); + } } } diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index eb2d926424..67af79c763 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -24,8 +24,13 @@ namespace osu.Game.Graphics.Containers { Enabled.ValueChanged += e => { - if (!e.NewValue) - unhover(); + if (isHovered) + { + if (e.NewValue) + fadeIn(); + else + fadeOut(); + } }; } @@ -33,28 +38,28 @@ namespace osu.Game.Graphics.Containers protected override bool OnHover(HoverEvent e) { + if (isHovered) + return false; + + isHovered = true; + if (!Enabled.Value) return false; - EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); - isHovered = true; + fadeIn(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) - { - unhover(); - base.OnHoverLost(e); - } - - private void unhover() { if (!isHovered) return; isHovered = false; - EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); + fadeOut(); + + base.OnHoverLost(e); } [BackgroundDependencyLoader] @@ -69,5 +74,9 @@ namespace osu.Game.Graphics.Containers base.LoadComplete(); EffectTargets.ForEach(d => d.FadeColour(IdleColour)); } + + private void fadeIn() => EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint)); + + private void fadeOut() => EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint)); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b675a35970..dec58f4c9e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -1,40 +1,40 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -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.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +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.Graphics; +using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods.Sections; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.Mods { public class ModSelectOverlay : WaveOverlayContainer { - private const float content_width = 0.8f; - - protected Color4 LowMultiplierColour, HighMultiplierColour; - protected readonly TriangleButton DeselectAllButton; - protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel; - private readonly FillFlowContainer footerContainer; + protected readonly TriangleButton CloseButton; + + protected readonly OsuSpriteText MultiplierLabel; + protected readonly OsuSpriteText UnrankedLabel; protected override bool BlockNonPositionalInput => false; @@ -46,154 +46,14 @@ namespace osu.Game.Overlays.Mods protected readonly IBindable Ruleset = new Bindable(); - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) - { - LowMultiplierColour = colours.Red; - HighMultiplierColour = colours.Green; - UnrankedLabel.Colour = colours.Blue; + protected Color4 LowMultiplierColour; + protected Color4 HighMultiplierColour; - Ruleset.BindTo(ruleset); - if (mods != null) SelectedMods.BindTo(mods); - - sampleOn = audio.Samples.Get(@"UI/check-on"); - sampleOff = audio.Samples.Get(@"UI/check-off"); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Ruleset.BindValueChanged(rulesetChanged, true); - SelectedMods.BindValueChanged(selectedModsChanged, true); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - Ruleset.UnbindAll(); - SelectedMods.UnbindAll(); - } - - private void rulesetChanged(ValueChangedEvent e) - { - if (e.NewValue == null) return; - - var instance = e.NewValue.CreateInstance(); - - foreach (ModSection section in ModSectionsContainer.Children) - section.Mods = instance.GetModsFor(section.ModType); - - // attempt to re-select any already selected mods. - // this may be the first time we are receiving the ruleset, in which case they will still match. - selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); - - // write the mods back to the SelectedMods bindable in the case a change was not applicable. - // this generally isn't required as the previous line will perform deselection; just here for safety. - refreshSelectedMods(); - } - - private void selectedModsChanged(ValueChangedEvent> e) - { - foreach (ModSection section in ModSectionsContainer.Children) - section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); - - updateMods(); - } - - private void updateMods() - { - double multiplier = 1.0; - bool ranked = true; - - foreach (Mod mod in SelectedMods.Value) - { - multiplier *= mod.ScoreMultiplier; - ranked &= mod.Ranked; - } - - MultiplierLabel.Text = $"{multiplier:N2}x"; - if (multiplier > 1.0) - MultiplierLabel.FadeColour(HighMultiplierColour, 200); - else if (multiplier < 1.0) - MultiplierLabel.FadeColour(LowMultiplierColour, 200); - else - MultiplierLabel.FadeColour(Color4.White, 200); - - UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); - } - - protected override void PopOut() - { - base.PopOut(); - - footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - } - } - - protected override void PopIn() - { - base.PopIn(); - - footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - } - } - - public void DeselectAll() - { - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectAll(); - - refreshSelectedMods(); - } - - /// - /// Deselect one or more mods. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(Type[] modTypes, bool immediate = false) - { - if (modTypes.Length == 0) return; - - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes, immediate); - } + private const float content_width = 0.8f; + private readonly FillFlowContainer footerContainer; private SampleChannel sampleOn, sampleOff; - private void modButtonPressed(Mod selectedMod) - { - if (selectedMod != null) - { - if (State == Visibility.Visible) sampleOn?.Play(); - DeselectTypes(selectedMod.IncompatibleMods, true); - } - else - { - if (State == Visibility.Visible) sampleOff?.Play(); - } - - refreshSelectedMods(); - } - - private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - public ModSelectOverlay() { Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2"); @@ -364,6 +224,16 @@ namespace osu.Game.Overlays.Mods Right = 20 } }, + CloseButton = new TriangleButton + { + Width = 180, + Text = "Close", + Action = Hide, + Margin = new MarginPadding + { + Right = 20 + } + }, new OsuSpriteText { Text = @"Score Multiplier:", @@ -401,5 +271,171 @@ namespace osu.Game.Overlays.Mods }, }; } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) + { + LowMultiplierColour = colours.Red; + HighMultiplierColour = colours.Green; + UnrankedLabel.Colour = colours.Blue; + + Ruleset.BindTo(ruleset); + if (mods != null) SelectedMods.BindTo(mods); + + sampleOn = audio.Samples.Get(@"UI/check-on"); + sampleOff = audio.Samples.Get(@"UI/check-off"); + } + + public void DeselectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.DeselectAll(); + + refreshSelectedMods(); + } + + /// + /// Deselect one or more mods. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(Type[] modTypes, bool immediate = false) + { + if (modTypes.Length == 0) return; + + foreach (var section in ModSectionsContainer.Children) + section.DeselectTypes(modTypes, immediate); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Ruleset.BindValueChanged(rulesetChanged, true); + SelectedMods.BindValueChanged(selectedModsChanged, true); + } + + protected override void PopOut() + { + base.PopOut(); + + footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + + foreach (var section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + } + } + + protected override void PopIn() + { + base.PopIn(); + + footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + + foreach (var section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + } + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + switch (e.Key) + { + case Key.Number1: + DeselectAllButton.Click(); + return true; + + case Key.Number2: + CloseButton.Click(); + return true; + } + + return base.OnKeyDown(e); + } + + private void rulesetChanged(ValueChangedEvent e) + { + if (e.NewValue == null) return; + + var instance = e.NewValue.CreateInstance(); + + foreach (var section in ModSectionsContainer.Children) + section.Mods = instance.GetModsFor(section.ModType); + + // attempt to re-select any already selected mods. + // this may be the first time we are receiving the ruleset, in which case they will still match. + selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); + + // write the mods back to the SelectedMods bindable in the case a change was not applicable. + // this generally isn't required as the previous line will perform deselection; just here for safety. + refreshSelectedMods(); + } + + private void selectedModsChanged(ValueChangedEvent> e) + { + foreach (var section in ModSectionsContainer.Children) + section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); + + updateMods(); + } + + private void updateMods() + { + var multiplier = 1.0; + var ranked = true; + + foreach (var mod in SelectedMods.Value) + { + multiplier *= mod.ScoreMultiplier; + ranked &= mod.Ranked; + } + + MultiplierLabel.Text = $"{multiplier:N2}x"; + if (multiplier > 1.0) + MultiplierLabel.FadeColour(HighMultiplierColour, 200); + else if (multiplier < 1.0) + MultiplierLabel.FadeColour(LowMultiplierColour, 200); + else + MultiplierLabel.FadeColour(Color4.White, 200); + + UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); + } + + private void modButtonPressed(Mod selectedMod) + { + if (selectedMod != null) + { + if (State == Visibility.Visible) sampleOn?.Play(); + DeselectTypes(selectedMod.IncompatibleMods, true); + } + else + { + if (State == Visibility.Visible) sampleOff?.Play(); + } + + refreshSelectedMods(); + } + + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Ruleset.UnbindAll(); + SelectedMods.UnbindAll(); + } + + #endregion } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 8639acfc94..b459afcb49 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -49,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, Spacing = new Vector2(0, 2), }, MoreButton = new ShowMoreButton diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs index a7a7f88ff7..86191609a4 100644 --- a/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/ManualInputManagerTestScene.cs @@ -3,8 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Tests.Visual { @@ -15,21 +20,92 @@ namespace osu.Game.Tests.Visual protected readonly ManualInputManager InputManager; + private readonly TriangleButton buttonTest; + private readonly TriangleButton buttonLocal; + protected ManualInputManagerTestScene() { - base.Content.Add(InputManager = new ManualInputManager + base.Content.AddRange(new Drawable[] { - UseParentInput = true, - Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, + InputManager = new ManualInputManager + { + UseParentInput = true, + Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(5), + CornerRadius = 5, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding(5), + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Input Priority" + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding(5), + Spacing = new Vector2(5), + Direction = FillDirection.Horizontal, + + Children = new Drawable[] + { + buttonLocal = new TriangleButton + { + Text = "local", + Size = new Vector2(50, 30), + Action = returnUserInput + }, + buttonTest = new TriangleButton + { + Text = "test", + Size = new Vector2(50, 30), + Action = returnTestInput + }, + } + }, + } + }, + } + }, }); } - /// - /// Returns input back to the user. - /// - protected void ReturnUserInput() + protected override void Update() { - AddStep("Return user input", () => InputManager.UseParentInput = true); + base.Update(); + + buttonTest.Enabled.Value = InputManager.UseParentInput; + buttonLocal.Enabled.Value = !InputManager.UseParentInput; } + + private void returnUserInput() => + InputManager.UseParentInput = true; + + private void returnTestInput() => + InputManager.UseParentInput = false; } }