From 87974850ddcfdbd55cb332b72016c21f880c91a6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 7 Aug 2019 08:42:43 +0300 Subject: [PATCH 01/25] Initial implementation --- .../Online/TestSceneLeaderboardModSelector.cs | 63 ++++++++ .../BeatmapSet/LeaderboardModSelector.cs | 136 ++++++++++++++++++ osu.Game/Overlays/Mods/ModButton.cs | 2 +- osu.Game/Rulesets/Mods/ModType.cs | 3 +- osu.Game/Rulesets/UI/ModIcon.cs | 27 ++-- 5 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs create mode 100644 osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs new file mode 100644 index 0000000000..eb7fe5591d --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.BeatmapSet; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Catch; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Bindables; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneLeaderboardModSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(LeaderboardModSelector), + }; + + public TestSceneLeaderboardModSelector() + { + LeaderboardModSelector modSelector; + FillFlowContainer selectedMods; + Bindable ruleset = new Bindable(); + + Add(selectedMods = new FillFlowContainer + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }); + + Add(modSelector = new LeaderboardModSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Ruleset = { BindTarget = ruleset } + }); + + modSelector.SelectedMods.BindValueChanged(mods => + { + selectedMods.Clear(); + + foreach (var mod in mods.NewValue) + selectedMods.Add(new SpriteText + { + Text = mod.Acronym, + }); + }); + + AddStep("osu mods", () => ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("mania mods", () => ruleset.Value = new ManiaRuleset().RulesetInfo); + AddStep("taiko mods", () => ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("catch mods", () => ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("Deselect all", () => modSelector.DeselectAll()); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs new file mode 100644 index 0000000000..99c51813c5 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -0,0 +1,136 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Framework.Bindables; +using System.Collections.Generic; +using osu.Game.Rulesets; +using osuTK; +using osu.Game.Rulesets.UI; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osuTK.Graphics; +using System; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class LeaderboardModSelector : Container + { + public readonly Bindable> SelectedMods = new Bindable>(); + public readonly Bindable Ruleset = new Bindable(); + + private readonly FillFlowContainer modsContainer; + + public LeaderboardModSelector() + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + Child = modsContainer = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Full, + Spacing = new Vector2(4), + }; + + Ruleset.BindValueChanged(onRulesetChanged); + } + + private void onRulesetChanged(ValueChangedEvent ruleset) + { + SelectedMods.Value = new List(); + + modsContainer.Clear(); + + if (ruleset.NewValue == null) + return; + + modsContainer.Add(new ModButton(new NoMod())); + + foreach (var mod in ruleset.NewValue.CreateInstance().GetAllMods()) + if (mod.Ranked) + modsContainer.Add(new ModButton(mod)); + + foreach (var mod in modsContainer) + mod.OnSelectionChanged += selectionChanged; + } + + private void selectionChanged(Mod mod, bool selected) + { + var mods = SelectedMods.Value.ToList(); + + if (selected) + mods.Add(mod); + else + mods.Remove(mod); + + SelectedMods.Value = mods; + } + + public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); + + private class ModButton : ModIcon + { + private const float mod_scale = 0.4f; + private const int duration = 200; + + public readonly BindableBool Selected = new BindableBool(); + public Action OnSelectionChanged; + + public ModButton(Mod mod) + : base(mod) + { + Scale = new Vector2(mod_scale); + Add(new HoverClickSounds()); + + Selected.BindValueChanged(selected => + { + updateState(); + OnSelectionChanged?.Invoke(mod, selected.NewValue); + }, true); + } + + protected override bool OnClick(ClickEvent e) + { + Selected.Value = !Selected.Value; + return base.OnClick(e); + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateState(); + } + + private void updateState() + { + this.FadeColour(IsHovered || Selected.Value ? Color4.White : Color4.Gray, duration, Easing.OutQuint); + } + } + + private class NoMod : Mod + { + public override string Name => "NoMod"; + + public override string Acronym => "NM"; + + public override double ScoreMultiplier => 1; + + public override IconUsage Icon => FontAwesome.Solid.Ban; + + public override ModType Type => ModType.Custom; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index fa1ee500a8..7b8745cf42 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods } } - foregroundIcon.Highlighted = Selected; + foregroundIcon.Highlighted.Value = Selected; SelectionChanged?.Invoke(SelectedMod); return true; diff --git a/osu.Game/Rulesets/Mods/ModType.cs b/osu.Game/Rulesets/Mods/ModType.cs index e3c82e42f5..1cdc4415ac 100644 --- a/osu.Game/Rulesets/Mods/ModType.cs +++ b/osu.Game/Rulesets/Mods/ModType.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods Conversion, Automation, Fun, - System + System, + Custom } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 86feea09a8..962263adba 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -11,11 +11,14 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osuTK; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.UI { public class ModIcon : Container, IHasTooltip { + public readonly BindableBool Highlighted = new BindableBool(); + private readonly SpriteIcon modIcon; private readonly SpriteIcon background; @@ -96,27 +99,19 @@ namespace osu.Game.Rulesets.UI backgroundColour = colours.Pink; highlightedColour = colours.PinkLight; break; - } - applyStyle(); - } - - private bool highlighted; - - public bool Highlighted - { - get => highlighted; - - set - { - highlighted = value; - applyStyle(); + case ModType.Custom: + backgroundColour = colours.Gray6; + highlightedColour = colours.Gray7; + modIcon.Colour = colours.Yellow; + break; } } - private void applyStyle() + protected override void LoadComplete() { - background.Colour = highlighted ? highlightedColour : backgroundColour; + base.LoadComplete(); + Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); } } } From 0070f6b26072b391e3989eb880261bc632ce41fb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 14:49:08 +0300 Subject: [PATCH 02/25] Use CompositeDrawable as a parent class --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 99c51813c5..399cd4a49a 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -19,7 +19,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Overlays.BeatmapSet { - public class LeaderboardModSelector : Container + public class LeaderboardModSelector : CompositeDrawable { public readonly Bindable> SelectedMods = new Bindable>(); public readonly Bindable Ruleset = new Bindable(); @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.BeatmapSet { AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; - Child = modsContainer = new FillFlowContainer + InternalChild = modsContainer = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 21af39032749fdb8da92fd773a5063f03ab955c6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 14:57:16 +0300 Subject: [PATCH 03/25] Move binding to LoadComplete --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 399cd4a49a..99c1b54467 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -38,8 +38,12 @@ namespace osu.Game.Overlays.BeatmapSet Direction = FillDirection.Full, Spacing = new Vector2(4), }; + } - Ruleset.BindValueChanged(onRulesetChanged); + protected override void LoadComplete() + { + base.LoadComplete(); + Ruleset.BindValueChanged(onRulesetChanged, true); } private void onRulesetChanged(ValueChangedEvent ruleset) From 62a91e4aaab46c458152233ef4208278dc635e4b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 15:20:21 +0300 Subject: [PATCH 04/25] Add the ability to override Highlighted action to the ModIcon --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 14 ++++++++++++-- osu.Game/Rulesets/UI/ModIcon.cs | 13 +++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 99c1b54467..03d2e6ce4b 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -92,11 +92,16 @@ namespace osu.Game.Overlays.BeatmapSet { Scale = new Vector2(mod_scale); Add(new HoverClickSounds()); + } + + protected override void LoadComplete() + { + base.LoadComplete(); Selected.BindValueChanged(selected => { updateState(); - OnSelectionChanged?.Invoke(mod, selected.NewValue); + OnSelectionChanged?.Invoke(Mod, selected.NewValue); }, true); } @@ -120,7 +125,12 @@ namespace osu.Game.Overlays.BeatmapSet private void updateState() { - this.FadeColour(IsHovered || Selected.Value ? Color4.White : Color4.Gray, duration, Easing.OutQuint); + Highlighted.Value = (IsHovered || Selected.Value) ? true : false; + } + + protected override void OnHighlightedChange(ValueChangedEvent highlighted) + { + this.FadeColour(highlighted.NewValue ? Color4.White : Color4.Gray, duration, Easing.OutQuint); } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 962263adba..e713216f35 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -34,9 +34,11 @@ namespace osu.Game.Rulesets.UI public virtual string TooltipText { get; } + protected Mod Mod { get; private set; } + public ModIcon(Mod mod) { - if (mod == null) throw new ArgumentNullException(nameof(mod)); + Mod = mod ?? throw new ArgumentNullException(nameof(mod)); type = mod.Type; @@ -106,12 +108,19 @@ namespace osu.Game.Rulesets.UI modIcon.Colour = colours.Yellow; break; } + + background.Colour = backgroundColour; } protected override void LoadComplete() { base.LoadComplete(); - Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); + Highlighted.BindValueChanged(OnHighlightedChange, true); + } + + protected virtual void OnHighlightedChange(ValueChangedEvent highlighted) + { + background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour; } } } From b71c776e65c291df39a45a175bc8301d6e3661d7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 16:20:36 +0300 Subject: [PATCH 05/25] Add web-like hover behavior --- .../BeatmapSet/LeaderboardModSelector.cs | 39 +++++++++++++++++-- osu.Game/Rulesets/UI/ModIcon.cs | 2 +- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 03d2e6ce4b..1e10c41478 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -57,12 +57,13 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.Add(new ModButton(new NoMod())); - foreach (var mod in ruleset.NewValue.CreateInstance().GetAllMods()) + ruleset.NewValue.CreateInstance().GetAllMods().ForEach(mod => + { if (mod.Ranked) modsContainer.Add(new ModButton(mod)); + }); - foreach (var mod in modsContainer) - mod.OnSelectionChanged += selectionChanged; + modsContainer.ForEach(button => button.OnSelectionChanged += selectionChanged); } private void selectionChanged(Mod mod, bool selected) @@ -74,11 +75,39 @@ namespace osu.Game.Overlays.BeatmapSet else mods.Remove(mod); + if (!mods.Any() && !IsHovered) + modsContainer.ForEach(button => button.Highlighted.Value = true); + SelectedMods.Value = mods; } + protected override bool OnHover(HoverEvent e) + { + if (!SelectedMods.Value.Any()) + dehighlightAll(); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + + if (!SelectedMods.Value.Any()) + modsContainer.ForEach(mod => mod.Highlighted.Value = true); + } + public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); + private void dehighlightAll() + { + modsContainer.ForEach(button => + { + if (!button.IsHovered) + button.Highlighted.Value = false; + }); + } + private class ModButton : ModIcon { private const float mod_scale = 0.4f; @@ -98,11 +127,13 @@ namespace osu.Game.Overlays.BeatmapSet { base.LoadComplete(); + Highlighted.Value = true; + Selected.BindValueChanged(selected => { updateState(); OnSelectionChanged?.Invoke(Mod, selected.NewValue); - }, true); + }); } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index e713216f35..1bcd2dc780 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.UI protected override void LoadComplete() { base.LoadComplete(); - Highlighted.BindValueChanged(OnHighlightedChange, true); + Highlighted.BindValueChanged(OnHighlightedChange); } protected virtual void OnHighlightedChange(ValueChangedEvent highlighted) From 86c9d5251faea12d24347dc68ae1c94ea3c738db Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 16:28:53 +0300 Subject: [PATCH 06/25] Remove unused function --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 1e10c41478..fffbc400b1 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -84,7 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnHover(HoverEvent e) { if (!SelectedMods.Value.Any()) - dehighlightAll(); + modsContainer.ForEach(button => + { + if (!button.IsHovered) + button.Highlighted.Value = false; + }); return base.OnHover(e); } @@ -99,15 +103,6 @@ namespace osu.Game.Overlays.BeatmapSet public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); - private void dehighlightAll() - { - modsContainer.ForEach(button => - { - if (!button.IsHovered) - button.Highlighted.Value = false; - }); - } - private class ModButton : ModIcon { private const float mod_scale = 0.4f; From cf92d6b1b01174666b484580242ad99891a839e0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 16:32:04 +0300 Subject: [PATCH 07/25] Add highlightAll function to avoid duplication --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index fffbc400b1..1489907589 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.BeatmapSet mods.Remove(mod); if (!mods.Any() && !IsHovered) - modsContainer.ForEach(button => button.Highlighted.Value = true); + highlightAll(); SelectedMods.Value = mods; } @@ -98,11 +98,13 @@ namespace osu.Game.Overlays.BeatmapSet base.OnHoverLost(e); if (!SelectedMods.Value.Any()) - modsContainer.ForEach(mod => mod.Highlighted.Value = true); + highlightAll(); } public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); + private void highlightAll() => modsContainer.ForEach(mod => mod.Highlighted.Value = true); + private class ModButton : ModIcon { private const float mod_scale = 0.4f; From 1bfb87fcdd01536874269a16ffcf13d6e7ffcc7c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 12 Aug 2019 16:41:35 +0300 Subject: [PATCH 08/25] Remove redundant conditional ternary expression --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 1489907589..66d78f927a 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -28,8 +28,7 @@ namespace osu.Game.Overlays.BeatmapSet public LeaderboardModSelector() { - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Both; InternalChild = modsContainer = new FillFlowContainer { Anchor = Anchor.Centre, @@ -151,10 +150,7 @@ namespace osu.Game.Overlays.BeatmapSet updateState(); } - private void updateState() - { - Highlighted.Value = (IsHovered || Selected.Value) ? true : false; - } + private void updateState() => Highlighted.Value = IsHovered || Selected.Value; protected override void OnHighlightedChange(ValueChangedEvent highlighted) { From 0cf4db899ff94292bac6945e8296c93931f64769 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 19 Sep 2019 17:03:52 +0300 Subject: [PATCH 09/25] Few cleanups --- .../Visual/Online/TestSceneLeaderboardModSelector.cs | 9 +++++---- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 11 ++++------- osu.Game/Rulesets/UI/ModIcon.cs | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index eb7fe5591d..3a64ac79f6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -53,11 +53,12 @@ namespace osu.Game.Tests.Visual.Online }); }); - AddStep("osu mods", () => ruleset.Value = new OsuRuleset().RulesetInfo); - AddStep("mania mods", () => ruleset.Value = new ManiaRuleset().RulesetInfo); - AddStep("taiko mods", () => ruleset.Value = new TaikoRuleset().RulesetInfo); - AddStep("catch mods", () => ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo); + AddStep("taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo); AddStep("Deselect all", () => modSelector.DeselectAll()); + AddStep("null ruleset", () => ruleset.Value = null); } } } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 66d78f927a..d09464aba6 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -74,10 +74,10 @@ namespace osu.Game.Overlays.BeatmapSet else mods.Remove(mod); + SelectedMods.Value = mods; + if (!mods.Any() && !IsHovered) highlightAll(); - - SelectedMods.Value = mods; } protected override bool OnHover(HoverEvent e) @@ -116,6 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet : base(mod) { Scale = new Vector2(mod_scale); + Highlighted.Value = true; Add(new HoverClickSounds()); } @@ -123,8 +124,6 @@ namespace osu.Game.Overlays.BeatmapSet { base.LoadComplete(); - Highlighted.Value = true; - Selected.BindValueChanged(selected => { updateState(); @@ -152,10 +151,8 @@ namespace osu.Game.Overlays.BeatmapSet private void updateState() => Highlighted.Value = IsHovered || Selected.Value; - protected override void OnHighlightedChange(ValueChangedEvent highlighted) - { + protected override void OnHighlightedChanged(ValueChangedEvent highlighted) => this.FadeColour(highlighted.NewValue ? Color4.White : Color4.Gray, duration, Easing.OutQuint); - } } private class NoMod : Mod diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index d3607757ab..b54ddf8f12 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -114,10 +114,10 @@ namespace osu.Game.Rulesets.UI protected override void LoadComplete() { base.LoadComplete(); - Highlighted.BindValueChanged(OnHighlightedChange); + Highlighted.BindValueChanged(OnHighlightedChanged, true); } - protected virtual void OnHighlightedChange(ValueChangedEvent highlighted) + protected virtual void OnHighlightedChanged(ValueChangedEvent highlighted) { background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour; } From e7118a9272600b7e4ac2ffddef8bbecab336d12d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 20 Sep 2019 23:47:21 +0300 Subject: [PATCH 10/25] Use System mod type for NoMod --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/ModType.cs | 3 +-- osu.Game/Rulesets/UI/ModIcon.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index d09464aba6..4b6fd5bfc2 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -165,7 +165,7 @@ namespace osu.Game.Overlays.BeatmapSet public override IconUsage Icon => FontAwesome.Solid.Ban; - public override ModType Type => ModType.Custom; + public override ModType Type => ModType.System; } } } diff --git a/osu.Game/Rulesets/Mods/ModType.cs b/osu.Game/Rulesets/Mods/ModType.cs index 1cdc4415ac..e3c82e42f5 100644 --- a/osu.Game/Rulesets/Mods/ModType.cs +++ b/osu.Game/Rulesets/Mods/ModType.cs @@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Mods Conversion, Automation, Fun, - System, - Custom + System } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index b54ddf8f12..19211e0c80 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.UI highlightedColour = colours.PinkLight; break; - case ModType.Custom: + case ModType.System: backgroundColour = colours.Gray6; highlightedColour = colours.Gray7; modIcon.Colour = colours.Yellow; From 93954c8da0996307f277473bf4ac894d97d06395 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 10 Nov 2019 23:58:07 +0300 Subject: [PATCH 11/25] Use BindableList for selected mods --- .../Online/TestSceneLeaderboardModSelector.cs | 31 +++++++++++++------ .../BeatmapSet/LeaderboardModSelector.cs | 20 +++++------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index 3a64ac79f6..799528220b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Bindables; using osu.Game.Rulesets; +using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Tests.Visual.Online { @@ -26,10 +27,10 @@ namespace osu.Game.Tests.Visual.Online public TestSceneLeaderboardModSelector() { LeaderboardModSelector modSelector; - FillFlowContainer selectedMods; + FillFlowContainer selectedMods; Bindable ruleset = new Bindable(); - Add(selectedMods = new FillFlowContainer + Add(selectedMods = new FillFlowContainer { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, @@ -42,16 +43,28 @@ namespace osu.Game.Tests.Visual.Online Ruleset = { BindTarget = ruleset } }); - modSelector.SelectedMods.BindValueChanged(mods => + modSelector.SelectedMods.ItemsAdded += mods => { - selectedMods.Clear(); + mods.ForEach(mod => selectedMods.Add(new SpriteText + { + Text = mod.Acronym, + })); + }; - foreach (var mod in mods.NewValue) - selectedMods.Add(new SpriteText + modSelector.SelectedMods.ItemsRemoved += mods => + { + mods.ForEach(mod => + { + foreach (var selected in selectedMods) { - Text = mod.Acronym, - }); - }); + if (selected.Text == mod.Acronym) + { + selectedMods.Remove(selected); + break; + } + } + }); + }; AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo); AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo); diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 4b6fd5bfc2..75c24cb710 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; using osu.Framework.Bindables; -using System.Collections.Generic; using osu.Game.Rulesets; using osuTK; using osu.Game.Rulesets.UI; @@ -21,7 +20,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class LeaderboardModSelector : CompositeDrawable { - public readonly Bindable> SelectedMods = new Bindable>(); + public readonly BindableList SelectedMods = new BindableList(); public readonly Bindable Ruleset = new Bindable(); private readonly FillFlowContainer modsContainer; @@ -47,8 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet private void onRulesetChanged(ValueChangedEvent ruleset) { - SelectedMods.Value = new List(); - + SelectedMods.Clear(); modsContainer.Clear(); if (ruleset.NewValue == null) @@ -67,22 +65,18 @@ namespace osu.Game.Overlays.BeatmapSet private void selectionChanged(Mod mod, bool selected) { - var mods = SelectedMods.Value.ToList(); - if (selected) - mods.Add(mod); + SelectedMods.Add(mod); else - mods.Remove(mod); + SelectedMods.Remove(mod); - SelectedMods.Value = mods; - - if (!mods.Any() && !IsHovered) + if (!SelectedMods.Any() && !IsHovered) highlightAll(); } protected override bool OnHover(HoverEvent e) { - if (!SelectedMods.Value.Any()) + if (!SelectedMods.Any()) modsContainer.ForEach(button => { if (!button.IsHovered) @@ -96,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapSet { base.OnHoverLost(e); - if (!SelectedMods.Value.Any()) + if (!SelectedMods.Any()) highlightAll(); } From a69a4643c950526a0683120799c06f7051f478ba Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 12 Nov 2019 08:45:21 +0300 Subject: [PATCH 12/25] Simplify LINQ expressions --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 75c24cb710..a7128ca157 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -53,12 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new NoMod())); - - ruleset.NewValue.CreateInstance().GetAllMods().ForEach(mod => - { - if (mod.Ranked) - modsContainer.Add(new ModButton(mod)); - }); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); modsContainer.ForEach(button => button.OnSelectionChanged += selectionChanged); } @@ -77,11 +72,7 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnHover(HoverEvent e) { if (!SelectedMods.Any()) - modsContainer.ForEach(button => - { - if (!button.IsHovered) - button.Highlighted.Value = false; - }); + modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = false); return base.OnHover(e); } From 27f721eec232a056e208d9ffcbbac1b8459db063 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 13 Nov 2019 00:24:13 +0300 Subject: [PATCH 13/25] Use = instead of += for OnSelectionChanged Action --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index a7128ca157..09c388a3b0 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.Add(new ModButton(new NoMod())); modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); - modsContainer.ForEach(button => button.OnSelectionChanged += selectionChanged); + modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); } private void selectionChanged(Mod mod, bool selected) From 6715b25ddd767c1a1d2b8891033b827765ce2ac7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 13 Nov 2019 16:04:15 +0300 Subject: [PATCH 14/25] Apply suggestions --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 09c388a3b0..b400141abf 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -118,14 +118,14 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnClick(ClickEvent e) { - Selected.Value = !Selected.Value; - return base.OnClick(e); + Selected.Toggle(); + return true; } protected override bool OnHover(HoverEvent e) { updateState(); - return base.OnHover(e); + return false; } protected override void OnHoverLost(HoverLostEvent e) From 1218d41b50ee6d1e52552c533c110448b312e63d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 15 Nov 2019 11:52:49 +0300 Subject: [PATCH 15/25] Make Ruleset a property --- .../BeatmapSet/LeaderboardModSelector.cs | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index b400141abf..660d957e1a 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -21,7 +21,34 @@ namespace osu.Game.Overlays.BeatmapSet public class LeaderboardModSelector : CompositeDrawable { public readonly BindableList SelectedMods = new BindableList(); - public readonly Bindable Ruleset = new Bindable(); + + private RulesetInfo ruleset; + + public RulesetInfo Ruleset + { + get => ruleset; + set + { + if (ruleset == value) + { + DeselectAll(); + return; + } + + ruleset = value; + + SelectedMods.Clear(); + modsContainer.Clear(); + + if (ruleset == null) + return; + + modsContainer.Add(new ModButton(new NoMod())); + modsContainer.AddRange(ruleset.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); + + modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); + } + } private readonly FillFlowContainer modsContainer; @@ -38,26 +65,6 @@ namespace osu.Game.Overlays.BeatmapSet }; } - protected override void LoadComplete() - { - base.LoadComplete(); - Ruleset.BindValueChanged(onRulesetChanged, true); - } - - private void onRulesetChanged(ValueChangedEvent ruleset) - { - SelectedMods.Clear(); - modsContainer.Clear(); - - if (ruleset.NewValue == null) - return; - - modsContainer.Add(new ModButton(new NoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); - - modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); - } - private void selectionChanged(Mod mod, bool selected) { if (selected) From 2592a0489b05fb4a7d5dfe112adc1802e5c27694 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 15 Nov 2019 11:57:40 +0300 Subject: [PATCH 16/25] Use existing ModNoMod --- .../Online/TestSceneLeaderboardModSelector.cs | 12 +++++------- .../BeatmapSet/LeaderboardModSelector.cs | 16 +--------------- osu.Game/Rulesets/Mods/ModNoMod.cs | 4 ++++ 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index 799528220b..94540eeb5b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -28,7 +28,6 @@ namespace osu.Game.Tests.Visual.Online { LeaderboardModSelector modSelector; FillFlowContainer selectedMods; - Bindable ruleset = new Bindable(); Add(selectedMods = new FillFlowContainer { @@ -40,7 +39,6 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Ruleset = { BindTarget = ruleset } }); modSelector.SelectedMods.ItemsAdded += mods => @@ -66,12 +64,12 @@ namespace osu.Game.Tests.Visual.Online }); }; - AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo); - AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo); - AddStep("taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo); - AddStep("catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("osu ruleset", () => modSelector.Ruleset = new OsuRuleset().RulesetInfo); + AddStep("mania ruleset", () => modSelector.Ruleset = new ManiaRuleset().RulesetInfo); + AddStep("taiko ruleset", () => modSelector.Ruleset = new TaikoRuleset().RulesetInfo); + AddStep("catch ruleset", () => modSelector.Ruleset = new CatchRuleset().RulesetInfo); AddStep("Deselect all", () => modSelector.DeselectAll()); - AddStep("null ruleset", () => ruleset.Value = null); + AddStep("null ruleset", () => modSelector.Ruleset = null); } } } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 660d957e1a..c96840c959 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -14,7 +14,6 @@ using osuTK.Graphics; using System; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics.Sprites; namespace osu.Game.Overlays.BeatmapSet { @@ -43,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet if (ruleset == null) return; - modsContainer.Add(new ModButton(new NoMod())); + modsContainer.Add(new ModButton(new ModNoMod())); modsContainer.AddRange(ruleset.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); @@ -146,18 +145,5 @@ namespace osu.Game.Overlays.BeatmapSet protected override void OnHighlightedChanged(ValueChangedEvent highlighted) => this.FadeColour(highlighted.NewValue ? Color4.White : Color4.Gray, duration, Easing.OutQuint); } - - private class NoMod : Mod - { - public override string Name => "NoMod"; - - public override string Acronym => "NM"; - - public override double ScoreMultiplier => 1; - - public override IconUsage Icon => FontAwesome.Solid.Ban; - - public override ModType Type => ModType.System; - } } } diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 0ddbd2a8cb..487985b2b3 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics.Sprites; + namespace osu.Game.Rulesets.Mods { /// @@ -11,5 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "No Mod"; public override string Acronym => "NM"; public override double ScoreMultiplier => 1; + public override IconUsage Icon => FontAwesome.Solid.Ban; + public override ModType Type => ModType.System; } } From e51fd00d588cd2b7488995b15f15b5533a8d759d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 15 Nov 2019 12:09:31 +0300 Subject: [PATCH 17/25] CI fix --- osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs | 2 -- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index 94540eeb5b..c0c18c2583 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -11,8 +11,6 @@ using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Catch; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Bindables; -using osu.Game.Rulesets; using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Tests.Visual.Online diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index c96840c959..e62a754e92 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.BeatmapSet get => ruleset; set { - if (ruleset == value) + if (ruleset?.Equals(value) ?? false) { DeselectAll(); return; From 737c2bd1c8fa1a66bcf47160cc675fe082985941 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 18:50:29 +0300 Subject: [PATCH 18/25] Remove pointless const --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index e62a754e92..ac8952bebb 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -97,7 +97,6 @@ namespace osu.Game.Overlays.BeatmapSet private class ModButton : ModIcon { - private const float mod_scale = 0.4f; private const int duration = 200; public readonly BindableBool Selected = new BindableBool(); @@ -106,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet public ModButton(Mod mod) : base(mod) { - Scale = new Vector2(mod_scale); + Scale = new Vector2(0.4f); Highlighted.Value = true; Add(new HoverClickSounds()); } From 984ec11a78ca071e70d04cd5899ee7b07cfad140 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 18:56:48 +0300 Subject: [PATCH 19/25] Make Ruleset a bindable --- .../Online/TestSceneLeaderboardModSelector.cs | 14 ++++-- .../BeatmapSet/LeaderboardModSelector.cs | 49 ++++++++----------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index c0c18c2583..ebe233a5f4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -12,6 +12,8 @@ using osu.Game.Rulesets.Catch; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Bindables; +using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.Online { @@ -26,6 +28,7 @@ namespace osu.Game.Tests.Visual.Online { LeaderboardModSelector modSelector; FillFlowContainer selectedMods; + var ruleset = new Bindable(); Add(selectedMods = new FillFlowContainer { @@ -37,6 +40,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Ruleset = { BindTarget = ruleset } }); modSelector.SelectedMods.ItemsAdded += mods => @@ -62,12 +66,12 @@ namespace osu.Game.Tests.Visual.Online }); }; - AddStep("osu ruleset", () => modSelector.Ruleset = new OsuRuleset().RulesetInfo); - AddStep("mania ruleset", () => modSelector.Ruleset = new ManiaRuleset().RulesetInfo); - AddStep("taiko ruleset", () => modSelector.Ruleset = new TaikoRuleset().RulesetInfo); - AddStep("catch ruleset", () => modSelector.Ruleset = new CatchRuleset().RulesetInfo); + AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo); + AddStep("taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo); AddStep("Deselect all", () => modSelector.DeselectAll()); - AddStep("null ruleset", () => modSelector.Ruleset = null); + AddStep("null ruleset", () => ruleset.Value = null); } } } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index ac8952bebb..ba42e8c310 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -20,34 +20,7 @@ namespace osu.Game.Overlays.BeatmapSet public class LeaderboardModSelector : CompositeDrawable { public readonly BindableList SelectedMods = new BindableList(); - - private RulesetInfo ruleset; - - public RulesetInfo Ruleset - { - get => ruleset; - set - { - if (ruleset?.Equals(value) ?? false) - { - DeselectAll(); - return; - } - - ruleset = value; - - SelectedMods.Clear(); - modsContainer.Clear(); - - if (ruleset == null) - return; - - modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); - - modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); - } - } + public readonly Bindable Ruleset = new Bindable(); private readonly FillFlowContainer modsContainer; @@ -64,6 +37,26 @@ namespace osu.Game.Overlays.BeatmapSet }; } + protected override void LoadComplete() + { + base.LoadComplete(); + Ruleset.BindValueChanged(onRulesetChanged, true); + } + + private void onRulesetChanged(ValueChangedEvent ruleset) + { + SelectedMods.Clear(); + modsContainer.Clear(); + + if (ruleset.NewValue == null) + return; + + modsContainer.Add(new ModButton(new ModNoMod())); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); + + modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); + } + private void selectionChanged(Mod mod, bool selected) { if (selected) From 23fc7b198782d79333edfa849a29bfe27f9805cd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 19:02:24 +0300 Subject: [PATCH 20/25] Implement updateHighlighted method --- .../BeatmapSet/LeaderboardModSelector.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index ba42e8c310..0a053c98db 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -64,24 +64,34 @@ namespace osu.Game.Overlays.BeatmapSet else SelectedMods.Remove(mod); - if (!SelectedMods.Any() && !IsHovered) + updateHighlighted(); + } + + private void updateHighlighted() + { + if (SelectedMods.Any()) + return; + + if (IsHovered) + { + modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = false); + } + else + { highlightAll(); + } } protected override bool OnHover(HoverEvent e) { - if (!SelectedMods.Any()) - modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = false); - + updateHighlighted(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - - if (!SelectedMods.Any()) - highlightAll(); + updateHighlighted(); } public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); From e22a71c6b80125e63660b3a0799b7325bce50348 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 19:42:07 +0300 Subject: [PATCH 21/25] Add visual difference between hovered and selected states --- .../BeatmapSet/LeaderboardModSelector.cs | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 0a053c98db..0e9e848e7a 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -72,14 +72,7 @@ namespace osu.Game.Overlays.BeatmapSet if (SelectedMods.Any()) return; - if (IsHovered) - { - modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = false); - } - else - { - highlightAll(); - } + modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.IsActive.Value = !IsHovered); } protected override bool OnHover(HoverEvent e) @@ -94,22 +87,19 @@ namespace osu.Game.Overlays.BeatmapSet updateHighlighted(); } - public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); - - private void highlightAll() => modsContainer.ForEach(mod => mod.Highlighted.Value = true); + public void DeselectAll() => modsContainer.ForEach(mod => mod.Highlighted.Value = false); private class ModButton : ModIcon { private const int duration = 200; - public readonly BindableBool Selected = new BindableBool(); + public readonly BindableBool IsActive = new BindableBool(); public Action OnSelectionChanged; public ModButton(Mod mod) : base(mod) { Scale = new Vector2(0.4f); - Highlighted.Value = true; Add(new HoverClickSounds()); } @@ -117,35 +107,39 @@ namespace osu.Game.Overlays.BeatmapSet { base.LoadComplete(); - Selected.BindValueChanged(selected => + IsActive.BindValueChanged(hovered => { - updateState(); - OnSelectionChanged?.Invoke(Mod, selected.NewValue); - }); + if (Highlighted.Value) + return; + + this.FadeColour(hovered.NewValue ? Color4.White : Color4.DimGray, duration, Easing.OutQuint); + }, true); + } + + protected override void OnHighlightedChanged(ValueChangedEvent highlighted) + { + base.OnHighlightedChanged(highlighted); + OnSelectionChanged?.Invoke(Mod, highlighted.NewValue); + IsActive.TriggerChange(); } protected override bool OnClick(ClickEvent e) { - Selected.Toggle(); + Highlighted.Toggle(); return true; } protected override bool OnHover(HoverEvent e) { - updateState(); + IsActive.Value = true; return false; } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - updateState(); + IsActive.Value = false; } - - private void updateState() => Highlighted.Value = IsHovered || Selected.Value; - - protected override void OnHighlightedChanged(ValueChangedEvent highlighted) => - this.FadeColour(highlighted.NewValue ? Color4.White : Color4.Gray, duration, Easing.OutQuint); } } } From c7c8527f5ff780ccf012b41720545084757486b8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 20:22:15 +0300 Subject: [PATCH 22/25] Remove OnHighlightedChanged function --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 11 +++++------ osu.Game/Rulesets/UI/ModIcon.cs | 9 +-------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 0e9e848e7a..bb6f889c40 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -114,13 +114,12 @@ namespace osu.Game.Overlays.BeatmapSet this.FadeColour(hovered.NewValue ? Color4.White : Color4.DimGray, duration, Easing.OutQuint); }, true); - } - protected override void OnHighlightedChanged(ValueChangedEvent highlighted) - { - base.OnHighlightedChanged(highlighted); - OnSelectionChanged?.Invoke(Mod, highlighted.NewValue); - IsActive.TriggerChange(); + Highlighted.BindValueChanged(highlighted => + { + OnSelectionChanged?.Invoke(Mod, highlighted.NewValue); + IsActive.TriggerChange(); + }, true); } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 19211e0c80..cf1bbadb57 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -107,19 +107,12 @@ namespace osu.Game.Rulesets.UI modIcon.Colour = colours.Yellow; break; } - - background.Colour = backgroundColour; } protected override void LoadComplete() { base.LoadComplete(); - Highlighted.BindValueChanged(OnHighlightedChanged, true); - } - - protected virtual void OnHighlightedChanged(ValueChangedEvent highlighted) - { - background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour; + Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); } } } From 0f1a3d97c8d5c7a6daf9330663e3d81677d6d3f6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 20:34:19 +0300 Subject: [PATCH 23/25] Naming adjustments --- .../BeatmapSet/LeaderboardModSelector.cs | 32 +++++++++---------- osu.Game/Overlays/Mods/ModButton.cs | 2 +- osu.Game/Rulesets/UI/ModIcon.cs | 4 +-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index bb6f889c40..a6f69617d0 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.BeatmapSet if (SelectedMods.Any()) return; - modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.IsActive.Value = !IsHovered); + modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = !IsHovered); } protected override bool OnHover(HoverEvent e) @@ -87,13 +87,13 @@ namespace osu.Game.Overlays.BeatmapSet updateHighlighted(); } - public void DeselectAll() => modsContainer.ForEach(mod => mod.Highlighted.Value = false); + public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); private class ModButton : ModIcon { private const int duration = 200; - public readonly BindableBool IsActive = new BindableBool(); + public readonly BindableBool Highlighted = new BindableBool(); public Action OnSelectionChanged; public ModButton(Mod mod) @@ -107,37 +107,37 @@ namespace osu.Game.Overlays.BeatmapSet { base.LoadComplete(); - IsActive.BindValueChanged(hovered => - { - if (Highlighted.Value) - return; - - this.FadeColour(hovered.NewValue ? Color4.White : Color4.DimGray, duration, Easing.OutQuint); - }, true); - Highlighted.BindValueChanged(highlighted => { - OnSelectionChanged?.Invoke(Mod, highlighted.NewValue); - IsActive.TriggerChange(); + if (Selected.Value) + return; + + this.FadeColour(highlighted.NewValue ? Color4.White : Color4.DimGray, duration, Easing.OutQuint); + }, true); + + Selected.BindValueChanged(selected => + { + OnSelectionChanged?.Invoke(Mod, selected.NewValue); + Highlighted.TriggerChange(); }, true); } protected override bool OnClick(ClickEvent e) { - Highlighted.Toggle(); + Selected.Toggle(); return true; } protected override bool OnHover(HoverEvent e) { - IsActive.Value = true; + Highlighted.Value = true; return false; } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - IsActive.Value = false; + Highlighted.Value = false; } } } diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 8252020e9b..c6b4787ff1 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods } } - foregroundIcon.Highlighted.Value = Selected; + foregroundIcon.Selected.Value = Selected; SelectionChanged?.Invoke(SelectedMod); return true; diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index cf1bbadb57..1be70ff48f 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.UI { public class ModIcon : Container, IHasTooltip { - public readonly BindableBool Highlighted = new BindableBool(); + public readonly BindableBool Selected = new BindableBool(); private readonly SpriteIcon modIcon; private readonly SpriteIcon background; @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.UI protected override void LoadComplete() { base.LoadComplete(); - Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); + Selected.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); } } } From eb2f7c1d0a63f2bd0aa21cc56eeb61f58a066d5a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Nov 2019 20:37:02 +0300 Subject: [PATCH 24/25] Rename forgotten variable --- osu.Game/Rulesets/UI/ModIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 1be70ff48f..945dbe4cc9 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.UI protected override void LoadComplete() { base.LoadComplete(); - Selected.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); + Selected.BindValueChanged(selected => background.Colour = selected.NewValue ? highlightedColour : backgroundColour, true); } } } From e170cd289269f9a028fb699953f2a28551a3e570 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Nov 2019 10:29:16 +0900 Subject: [PATCH 25/25] Move private methods below --- .../BeatmapSet/LeaderboardModSelector.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index a6f69617d0..60fd520681 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -57,6 +57,18 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); } + protected override bool OnHover(HoverEvent e) + { + updateHighlighted(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateHighlighted(); + } + private void selectionChanged(Mod mod, bool selected) { if (selected) @@ -75,18 +87,6 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = !IsHovered); } - protected override bool OnHover(HoverEvent e) - { - updateHighlighted(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - updateHighlighted(); - } - public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false); private class ModButton : ModIcon