From 11dad7bf746820b56fa426f63f8582062de52a6d Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 10 Oct 2018 16:46:02 +0200 Subject: [PATCH 01/75] filter beatmaps by star range --- .../Visual/TestCaseBeatmapCarousel.cs | 49 +++++++++++++++++++ .../Select/Carousel/CarouselBeatmap.cs | 6 +++ osu.Game/Screens/Select/FilterControl.cs | 40 +++++++++------ osu.Game/Screens/Select/FilterCriteria.cs | 2 + 4 files changed, 81 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index db66c01814..df42ba9d16 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -77,6 +77,7 @@ namespace osu.Game.Tests.Visual testEmptyTraversal(); testHiding(); testSelectingFilteredRuleset(); + testFilterByStarRange(); testCarouselRootIsRandom(); } @@ -245,6 +246,54 @@ namespace osu.Game.Tests.Visual AddAssert("Selection is non-null", () => currentSelection != null); } + /// + /// Test filtering by restricting the desired star range + /// + private void testFilterByStarRange() + { + var manyStarDiffs = createTestBeatmapSet(set_count + 1); + manyStarDiffs.Beatmaps.Clear(); + + for (int i = 0; i < 12; i++) + { + manyStarDiffs.Beatmaps.Add(new BeatmapInfo + { + OnlineBeatmapID = manyStarDiffs.ID * 10 + i, + Path = $"randomDiff{i}.osu", + Version = $"Totally Normal {i}", + StarDifficulty = i, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 5, + } + }); + } + + AddStep("add set with many stars", () => carousel.UpdateBeatmapSet(manyStarDiffs)); + + AddStep("select added set", () => carousel.SelectBeatmap(manyStarDiffs.Beatmaps[0], false)); + + AddStep("Filter to 1-3 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 1, DisplayStarsMaximum = 3 }, false)); + checkVisibleItemCount(diff: false, count: 1); + checkVisibleItemCount(diff: true, count: 3); + + AddStep("Filter to 3-3 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 3, DisplayStarsMaximum = 3 }, false)); + checkVisibleItemCount(diff: false, count: 1); + checkVisibleItemCount(diff: true, count: 1); + + AddStep("Filter to 4-2 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 4, DisplayStarsMaximum = 2 }, false)); + checkVisibleItemCount(diff: false, count: 0); + checkVisibleItemCount(diff: true, count: 0); + + AddStep("remove added set", () => + { + carousel.RemoveBeatmapSet(manyStarDiffs); + manyStarDiffs = null; + }); + + AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + } + /// /// Test random non-repeating algorithm /// diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 272332f1ce..c9ed2f3573 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -26,6 +26,12 @@ namespace osu.Game.Screens.Select.Carousel bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps; + if(criteria.DisplayStarsMinimum.HasValue) + match &= Beatmap.StarDifficulty >= criteria.DisplayStarsMinimum; + + if (criteria.DisplayStarsMaximum.HasValue) + match &= Beatmap.StarDifficulty <= criteria.DisplayStarsMaximum; + if (!string.IsNullOrEmpty(criteria.SearchText)) match &= Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0) || diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index fce7af1400..78b452b6f5 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -32,14 +32,13 @@ namespace osu.Game.Screens.Select public SortMode Sort { - get { return sort; } + get => sort; set { - if (sort != value) - { - sort = value; - FilterChanged?.Invoke(CreateCriteria()); - } + if (sort == value) return; + + sort = value; + FilterChanged?.Invoke(CreateCriteria()); } } @@ -47,14 +46,13 @@ namespace osu.Game.Screens.Select public GroupMode Group { - get { return group; } + get => group; set { - if (group != value) - { - group = value; - FilterChanged?.Invoke(CreateCriteria()); - } + if (group == value) return; + + group = value; + FilterChanged?.Invoke(CreateCriteria()); } } @@ -64,7 +62,9 @@ namespace osu.Game.Screens.Select Sort = sort, SearchText = searchTextBox.Text, AllowConvertedBeatmaps = showConverted, - Ruleset = ruleset.Value + Ruleset = ruleset.Value, + DisplayStarsMinimum = minimumStars, + DisplayStarsMaximum = maximumStars, }; public Action Exit; @@ -168,7 +168,9 @@ namespace osu.Game.Screens.Select private readonly IBindable ruleset = new Bindable(); - private Bindable showConverted; + private readonly Bindable showConverted = new Bindable(); + private readonly Bindable minimumStars = new Bindable(); + private readonly Bindable maximumStars = new Bindable(); public readonly Box Background; @@ -177,8 +179,14 @@ namespace osu.Game.Screens.Select { sortTabs.AccentColour = colours.GreenLight; - showConverted = config.GetBindable(OsuSetting.ShowConvertedBeatmaps); - showConverted.ValueChanged += val => updateCriteria(); + config.BindWith(OsuSetting.ShowConvertedBeatmaps, showConverted); + showConverted.ValueChanged += _ => updateCriteria(); + + config.BindWith(OsuSetting.DisplayStarsMinimum, minimumStars); + minimumStars.ValueChanged += _ => updateCriteria(); + + config.BindWith(OsuSetting.DisplayStarsMaximum, maximumStars); + maximumStars.ValueChanged += _ => updateCriteria(); ruleset.BindTo(parentRuleset); ruleset.BindValueChanged(_ => updateCriteria(), true); diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index bea806f00f..f97eb1bea1 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -13,5 +13,7 @@ namespace osu.Game.Screens.Select public string SearchText; public RulesetInfo Ruleset; public bool AllowConvertedBeatmaps; + public double? DisplayStarsMinimum; + public double? DisplayStarsMaximum; } } From d83ce7e4bb35b352f112d1353c4766b4006281be Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 19 Oct 2018 12:22:05 +0200 Subject: [PATCH 02/75] don't allow null values in FilterCriteria, ensure values in test instead --- .../Visual/TestCaseBeatmapCarousel.cs | 51 +++++++++++-------- .../Select/Carousel/CarouselBeatmap.cs | 7 +-- osu.Game/Screens/Select/FilterCriteria.cs | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index df42ba9d16..a57ef21dd9 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -65,6 +65,10 @@ namespace osu.Game.Tests.Visual carousel.SelectionChanged = s => currentSelection = s; + // not being hooked up to the config leads to the SR filter only showing 0 to 0 SR maps + // thus "filter" once to assume a 0 to 10 range + carousel.Filter(new TestCriteria()); + loadBeatmaps(beatmapSets); testTraversal(); @@ -150,9 +154,7 @@ namespace osu.Game.Tests.Visual private bool selectedBeatmapVisible() { var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected); - if (currentlySelected == null) - return true; - return currentlySelected.Item.Visible; + return currentlySelected?.Item.Visible ?? true; } private void checkInvisibleDifficultiesUnselectable() @@ -165,8 +167,8 @@ namespace osu.Game.Tests.Visual { AddStep("Toggle non-matching filter", () => { - carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false); - carousel.Filter(new FilterCriteria(), false); + carousel.Filter(new TestCriteria { SearchText = "Dingo" }, false); + carousel.Filter(new TestCriteria(), false); eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); } ); @@ -207,7 +209,7 @@ namespace osu.Game.Tests.Visual setSelected(1, 1); - AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false)); + AddStep("Filter", () => carousel.Filter(new TestCriteria { SearchText = "set #3!" }, false)); checkVisibleItemCount(diff: false, count: 1); checkVisibleItemCount(diff: true, count: 3); checkSelected(3, 1); @@ -215,7 +217,7 @@ namespace osu.Game.Tests.Visual advanceSelection(diff: true, count: 4); checkSelected(3, 2); - AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria())); + AddStep("Un-filter (debounce)", () => carousel.Filter(new TestCriteria())); AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce"); checkVisibleItemCount(diff: false, count: set_count); checkVisibleItemCount(diff: true, count: 3); @@ -223,13 +225,13 @@ namespace osu.Game.Tests.Visual // test filtering some difficulties (and keeping current beatmap set selected). setSelected(1, 2); - AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false)); + AddStep("Filter some difficulties", () => carousel.Filter(new TestCriteria { SearchText = "Normal" }, false)); checkSelected(1, 1); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + AddStep("Un-filter", () => carousel.Filter(new TestCriteria(), false)); checkSelected(1, 1); - AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false)); + AddStep("Filter all", () => carousel.Filter(new TestCriteria { SearchText = "Dingo" }, false)); checkVisibleItemCount(false, 0); checkVisibleItemCount(true, 0); @@ -241,7 +243,7 @@ namespace osu.Game.Tests.Visual advanceSelection(false); AddAssert("Selection is null", () => currentSelection == null); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + AddStep("Un-filter", () => carousel.Filter(new TestCriteria(), false)); AddAssert("Selection is non-null", () => currentSelection != null); } @@ -273,15 +275,15 @@ namespace osu.Game.Tests.Visual AddStep("select added set", () => carousel.SelectBeatmap(manyStarDiffs.Beatmaps[0], false)); - AddStep("Filter to 1-3 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 1, DisplayStarsMaximum = 3 }, false)); + AddStep("Filter to 1-3 stars", () => carousel.Filter(new TestCriteria { DisplayStarsMinimum = 1, DisplayStarsMaximum = 3 }, false)); checkVisibleItemCount(diff: false, count: 1); checkVisibleItemCount(diff: true, count: 3); - AddStep("Filter to 3-3 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 3, DisplayStarsMaximum = 3 }, false)); + AddStep("Filter to 3-3 stars", () => carousel.Filter(new TestCriteria { DisplayStarsMinimum = 3, DisplayStarsMaximum = 3 }, false)); checkVisibleItemCount(diff: false, count: 1); checkVisibleItemCount(diff: true, count: 1); - AddStep("Filter to 4-2 stars", () => carousel.Filter(new FilterCriteria { DisplayStarsMinimum = 4, DisplayStarsMaximum = 2 }, false)); + AddStep("Filter to 4-2 stars", () => carousel.Filter(new TestCriteria { DisplayStarsMinimum = 4, DisplayStarsMaximum = 2 }, false)); checkVisibleItemCount(diff: false, count: 0); checkVisibleItemCount(diff: true, count: 0); @@ -291,7 +293,7 @@ namespace osu.Game.Tests.Visual manyStarDiffs = null; }); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + AddStep("Un-filter", () => carousel.Filter(new TestCriteria(), false)); } /// @@ -322,13 +324,13 @@ namespace osu.Game.Tests.Visual AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet)); AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(createTestBeatmapSetWithManyDifficulties(set_count + 1))); - AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false)); + AddStep("Filter Extra", () => carousel.Filter(new TestCriteria { SearchText = "Extra 10" }, false)); checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable(); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + AddStep("Un-filter", () => carousel.Filter(new TestCriteria(), false)); } /// @@ -359,9 +361,9 @@ namespace osu.Game.Tests.Visual /// private void testSorting() { - AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false)); + AddStep("Sort by author", () => carousel.Filter(new TestCriteria { Sort = SortMode.Author }, false)); AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz"); - AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); + AddStep("Sort by artist", () => carousel.Filter(new TestCriteria { Sort = SortMode.Artist }, false)); AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!")); } @@ -451,7 +453,7 @@ namespace osu.Game.Tests.Visual carousel.UpdateBeatmapSet(testMixed); }); AddStep("filter to ruleset 0", () => - carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); + carousel.Filter(new TestCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0])); @@ -581,5 +583,14 @@ namespace osu.Game.Tests.Visual public bool PendingFilterTask => PendingFilter != null; } + + private class TestCriteria : FilterCriteria + { + public TestCriteria() + { + DisplayStarsMinimum = 0; + DisplayStarsMaximum = 10; + } + } } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index c9ed2f3573..2e1adaf66f 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -25,12 +25,7 @@ namespace osu.Game.Screens.Select.Carousel base.Filter(criteria); bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps; - - if(criteria.DisplayStarsMinimum.HasValue) - match &= Beatmap.StarDifficulty >= criteria.DisplayStarsMinimum; - - if (criteria.DisplayStarsMaximum.HasValue) - match &= Beatmap.StarDifficulty <= criteria.DisplayStarsMaximum; + match &= Beatmap.StarDifficulty >= criteria.DisplayStarsMinimum && Beatmap.StarDifficulty <= criteria.DisplayStarsMaximum; if (!string.IsNullOrEmpty(criteria.SearchText)) match &= diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index f97eb1bea1..bd8bb22e1c 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Select public string SearchText; public RulesetInfo Ruleset; public bool AllowConvertedBeatmaps; - public double? DisplayStarsMinimum; - public double? DisplayStarsMaximum; + public double DisplayStarsMinimum; + public double DisplayStarsMaximum; } } From 6500cc967fff845a8e65cd3fc1a7a666be43669e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 06:58:35 +0300 Subject: [PATCH 03/75] Implement SongTicker component --- osu.Game/Screens/Menu/MainMenu.cs | 7 +++ osu.Game/Screens/Menu/SongTicker.cs | 66 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 osu.Game/Screens/Menu/SongTicker.cs diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index b28d572b5c..47c86fc1ae 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -75,6 +75,13 @@ namespace osu.Game.Screens.Menu holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); + AddInternal(new SongTicker + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding { Right = 15 } + }); + if (host.CanExit) { AddInternal(exitConfirmOverlay = new ExitConfirmOverlay diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs new file mode 100644 index 0000000000..504b016019 --- /dev/null +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -0,0 +1,66 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osu.Game.Graphics; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Menu +{ + public class SongTicker : Container + { + private const int duration = 500; + + [Resolved] + private Bindable beatmap { get; set; } + + private readonly Bindable workingBeatmap = new Bindable(); + private readonly OsuSpriteText title, artist; + + public SongTicker() + { + AutoSizeAxes = Axes.Both; + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 3), + Children = new Drawable[] + { + title = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light, italics: true) + }, + artist = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = OsuFont.GetFont(size: 16) + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + workingBeatmap.BindTo(beatmap); + workingBeatmap.BindValueChanged(onBeatmapChanged); + } + + private void onBeatmapChanged(ValueChangedEvent working) + { + title.Text = working.NewValue?.Metadata?.Title; + artist.Text = working.NewValue?.Metadata?.Artist; + + this.FadeIn(duration, Easing.OutQuint).Delay(4000).Then().FadeOut(duration, Easing.OutQuint); + } + } +} From 7716a555ecb71fd5d3ad290bc22d89daf4a281c1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 07:08:00 +0300 Subject: [PATCH 04/75] Move only ButtonSystem on screen changes rather than everything --- osu.Game/Screens/Menu/MainMenu.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 47c86fc1ae..c4465dce17 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Menu private ExitConfirmOverlay exitConfirmOverlay; + private ParallaxContainer buttonsContainer; + [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) { @@ -79,7 +81,7 @@ namespace osu.Game.Screens.Menu { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Margin = new MarginPadding { Right = 15 } + Margin = new MarginPadding { Right = 15, Top = 5 } }); if (host.CanExit) @@ -98,7 +100,7 @@ namespace osu.Game.Screens.Menu AddRangeInternal(new Drawable[] { - new ParallaxContainer + buttonsContainer = new ParallaxContainer { ParallaxAmount = 0.01f, Children = new Drawable[] @@ -197,7 +199,7 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.TopLevel; this.FadeIn(FADE_IN_DURATION, Easing.OutQuint); - this.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint); + buttonsContainer.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint); sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint); } @@ -234,7 +236,7 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; this.FadeOut(FADE_OUT_DURATION, Easing.InSine); - this.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); + buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); sideFlashes.FadeOut(64, Easing.OutQuint); } From d59cae33d3f0ff67fc5f6a38a7a2c02449bcb110 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 07:17:13 +0300 Subject: [PATCH 05/75] Some animation adjustments --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++++- osu.Game/Screens/Menu/SongTicker.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c4465dce17..0b267731d8 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -70,6 +70,7 @@ namespace osu.Game.Screens.Menu private ExitConfirmOverlay exitConfirmOverlay; private ParallaxContainer buttonsContainer; + private SongTicker songTicker; [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) @@ -77,7 +78,7 @@ namespace osu.Game.Screens.Menu holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); - AddInternal(new SongTicker + AddInternal(songTicker = new SongTicker { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -235,6 +236,7 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; + songTicker.Hide(); this.FadeOut(FADE_OUT_DURATION, Easing.InSine); buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); @@ -244,6 +246,7 @@ namespace osu.Game.Screens.Menu public override void OnResuming(IScreen last) { base.OnResuming(last); + songTicker.Hide(); (Background as BackgroundScreenDefault)?.Next(); @@ -272,6 +275,7 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.Exit; this.FadeOut(3000); + songTicker.Hide(); return base.OnExiting(next); } diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index 504b016019..eb7012a150 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Menu { public class SongTicker : Container { - private const int duration = 500; + private const int fade_duration = 800; [Resolved] private Bindable beatmap { get; set; } @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu title.Text = working.NewValue?.Metadata?.Title; artist.Text = working.NewValue?.Metadata?.Artist; - this.FadeIn(duration, Easing.OutQuint).Delay(4000).Then().FadeOut(duration, Easing.OutQuint); + this.FadeIn(fade_duration, Easing.OutQuint).Delay(4000).Then().FadeOut(fade_duration, Easing.OutQuint); } } } From e6210f10b7de81a12ef03148202ae14933a77fb7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 07:32:40 +0300 Subject: [PATCH 06/75] Add unicode metadata support --- osu.Game/Screens/Menu/SongTicker.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index eb7012a150..d9b66d06e9 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -9,6 +9,7 @@ using osuTK; using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Framework.Localisation; namespace osu.Game.Screens.Menu { @@ -57,8 +58,13 @@ namespace osu.Game.Screens.Menu private void onBeatmapChanged(ValueChangedEvent working) { - title.Text = working.NewValue?.Metadata?.Title; - artist.Text = working.NewValue?.Metadata?.Artist; + if (working.NewValue?.Beatmap == null) + return; + + var metadata = working.NewValue?.Metadata; + + title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); + artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); this.FadeIn(fade_duration, Easing.OutQuint).Delay(4000).Then().FadeOut(fade_duration, Easing.OutQuint); } From d25ef1966d32bda80e258da550a6c87768ca0eeb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 17:48:09 +0300 Subject: [PATCH 07/75] Remove unnecessary local bindable --- osu.Game/Screens/Menu/SongTicker.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index d9b66d06e9..f2c861a296 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -20,7 +20,6 @@ namespace osu.Game.Screens.Menu [Resolved] private Bindable beatmap { get; set; } - private readonly Bindable workingBeatmap = new Bindable(); private readonly OsuSpriteText title, artist; public SongTicker() @@ -52,8 +51,7 @@ namespace osu.Game.Screens.Menu protected override void LoadComplete() { base.LoadComplete(); - workingBeatmap.BindTo(beatmap); - workingBeatmap.BindValueChanged(onBeatmapChanged); + beatmap.BindValueChanged(onBeatmapChanged); } private void onBeatmapChanged(ValueChangedEvent working) From 81948744d056acd6ca24d0f6875236fcef3f034a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 17:50:13 +0300 Subject: [PATCH 08/75] remove unnecessary null checks --- osu.Game/Screens/Menu/SongTicker.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index f2c861a296..8eae94ae1d 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -56,10 +56,7 @@ namespace osu.Game.Screens.Menu private void onBeatmapChanged(ValueChangedEvent working) { - if (working.NewValue?.Beatmap == null) - return; - - var metadata = working.NewValue?.Metadata; + var metadata = working.NewValue.Metadata; title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); From bd33687f533a969d7bd855054b50f840614ef754 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 18:27:22 +0300 Subject: [PATCH 09/75] Add AllowUpdates flag to SongTicker --- osu.Game/Screens/Menu/MainMenu.cs | 5 ++++- osu.Game/Screens/Menu/SongTicker.cs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0b267731d8..72b61eb05c 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -237,6 +237,8 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; songTicker.Hide(); + songTicker.AllowUpdates = false; + this.FadeOut(FADE_OUT_DURATION, Easing.InSine); buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); @@ -246,7 +248,8 @@ namespace osu.Game.Screens.Menu public override void OnResuming(IScreen last) { base.OnResuming(last); - songTicker.Hide(); + + songTicker.AllowUpdates = true; (Background as BackgroundScreenDefault)?.Next(); diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index 8eae94ae1d..f858d162d2 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -17,6 +17,8 @@ namespace osu.Game.Screens.Menu { private const int fade_duration = 800; + public bool AllowUpdates { get; set; } = true; + [Resolved] private Bindable beatmap { get; set; } @@ -56,6 +58,9 @@ namespace osu.Game.Screens.Menu private void onBeatmapChanged(ValueChangedEvent working) { + if (!AllowUpdates) + return; + var metadata = working.NewValue.Metadata; title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); From 730cc92bf36ddf5c7a2eb53d5c7042b422708de5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 11 Jan 2020 22:43:07 +0300 Subject: [PATCH 10/75] Fade out instead of insta hiding on menu suspending --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 72b61eb05c..2de31cf399 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -236,7 +236,7 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; - songTicker.Hide(); + songTicker.FadeOut(100, Easing.OutQuint); songTicker.AllowUpdates = false; this.FadeOut(FADE_OUT_DURATION, Easing.InSine); From bd96cf94a6284f1d6b9be30e054fda5cfa6c4c62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 00:53:59 +0900 Subject: [PATCH 11/75] Begin refactoring SelectionBlueprint to handle non-drawable HitObjects --- .../Blueprints/ManiaSelectionBlueprint.cs | 2 +- .../Edit/ManiaBlueprintContainer.cs | 2 +- .../Edit/ManiaSelectionHandler.cs | 5 +- .../Edit/Masks/ManiaSelectionBlueprint.cs | 2 +- .../Edit/Blueprints/OsuSelectionBlueprint.cs | 4 +- .../Edit/OsuBlueprintContainer.cs | 2 +- .../Edit/OverlaySelectionBlueprint.cs | 32 ++++++++++++ osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 35 +++++-------- .../Timelines/Summary/Parts/TimelinePart.cs | 20 +++++--- .../Compose/Components/BlueprintContainer.cs | 38 ++++---------- .../Components/ComposeBlueprintContainer.cs | 2 +- .../Compose/Components/MoveSelectionEvent.cs | 8 +-- .../Compose/Components/SelectionHandler.cs | 4 +- .../Timeline/TimelineHitObjectDisplay.cs | 51 ++++++------------- .../Visual/SelectionBlueprintTestScene.cs | 2 +- 15 files changed, 102 insertions(+), 107 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 3bd7fb2d49..63e3d0fcc1 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class ManiaSelectionBlueprint : SelectionBlueprint + public class ManiaSelectionBlueprint : OverlaySelectionBlueprint { public Vector2 ScreenSpaceDragPosition { get; private set; } public Vector2 DragPosition { get; private set; } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index 5f66ae7491..d744036b4c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Edit { } - public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) + public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) { switch (hitObject) { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 618af3e772..f33fd3c640 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Timing; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.UI; @@ -72,8 +73,10 @@ namespace osu.Game.Rulesets.Mania.Edit if (scrollingInfo.Direction.Value == ScrollingDirection.Down) delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight; - foreach (var b in SelectedBlueprints) + foreach (var selectionBlueprint in SelectedBlueprints) { + var b = (OverlaySelectionBlueprint)selectionBlueprint; + var hitObject = b.DrawableObject; var objectParent = (HitObjectContainer)hitObject.Parent; diff --git a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs index ff8882124f..433db79ae0 100644 --- a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Edit.Masks { - public abstract class ManiaSelectionBlueprint : SelectionBlueprint + public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint { protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index a864257274..b0e13808a5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -7,10 +7,10 @@ using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Edit.Blueprints { - public abstract class OsuSelectionBlueprint : SelectionBlueprint + public abstract class OsuSelectionBlueprint : OverlaySelectionBlueprint where T : OsuHitObject { - protected T HitObject => (T)DrawableObject.HitObject; + protected new T HitObject => (T)DrawableObject.HitObject; protected OsuSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs index 30682616e6..330f34b85c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler(); - public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) + public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) { switch (hitObject) { diff --git a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs new file mode 100644 index 0000000000..4c3898aa04 --- /dev/null +++ b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs @@ -0,0 +1,32 @@ +// 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.Primitives; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Edit +{ + public abstract class OverlaySelectionBlueprint : SelectionBlueprint + { + /// + /// The which this applies to. + /// + public readonly DrawableHitObject DrawableObject; + + protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected; + + protected OverlaySelectionBlueprint(DrawableHitObject drawableObject) + : base(drawableObject.HitObject) + { + DrawableObject = drawableObject; + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos); + + public override Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre; + + public override Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad; + } +} diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index bf99f83e0b..d8952a3932 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Edit /// public abstract class SelectionBlueprint : CompositeDrawable, IStateful { + public readonly HitObject HitObject; + /// /// Invoked when this has been selected. /// @@ -30,22 +32,15 @@ namespace osu.Game.Rulesets.Edit /// public event Action Deselected; - /// - /// The which this applies to. - /// - public readonly DrawableHitObject DrawableObject; - - protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected; public override bool HandlePositionalInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; [Resolved(CanBeNull = true)] private HitObjectComposer composer { get; set; } - protected SelectionBlueprint(DrawableHitObject drawableObject) + protected SelectionBlueprint(HitObject hitObject) { - DrawableObject = drawableObject; - + this.HitObject = hitObject; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; @@ -87,37 +82,35 @@ namespace osu.Game.Rulesets.Edit protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected; /// - /// Selects this , causing it to become visible. + /// Selects this , causing it to become visible. /// public void Select() => State = SelectionState.Selected; /// - /// Deselects this , causing it to become invisible. + /// Deselects this , causing it to become invisible. /// public void Deselect() => State = SelectionState.NotSelected; public bool IsSelected => State == SelectionState.Selected; /// - /// Updates the , invoking and re-processing the beatmap. + /// Updates the , invoking and re-processing the beatmap. /// - protected void UpdateHitObject() => composer?.UpdateHitObject(DrawableObject.HitObject); - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos); + protected void UpdateHitObject() => composer?.UpdateHitObject(HitObject); /// - /// The s to be displayed in the context menu for this . + /// The s to be displayed in the context menu for this . /// public virtual MenuItem[] ContextMenuItems => Array.Empty(); /// - /// The screen-space point that causes this to be selected. + /// The screen-space point that causes this to be selected. /// - public virtual Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre; + public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; /// - /// The screen-space quad that outlines this for selections. + /// The screen-space quad that outlines this for selections. /// - public virtual Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad; + public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 119635ccd5..4a7c3f26bc 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -11,20 +11,24 @@ using osu.Game.Beatmaps; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { + public class TimelinePart : TimelinePart + { + } + /// /// Represents a part of the summary timeline.. /// - public class TimelinePart : Container + public class TimelinePart : Container where T : Drawable { protected readonly IBindable Beatmap = new Bindable(); - private readonly Container timeline; + private readonly Container content; - protected override Container Content => timeline; + protected override Container Content => content; - public TimelinePart() + public TimelinePart(Container content = null) { - AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both }); + AddInternal(this.content = content ?? new Container { RelativeSizeAxes = Axes.Both }); Beatmap.ValueChanged += b => { @@ -44,17 +48,17 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts // the track may not be loaded completely (only has a length once it is). if (!Beatmap.Value.Track.IsLoaded) { - timeline.RelativeChildSize = Vector2.One; + content.RelativeChildSize = Vector2.One; Schedule(updateRelativeChildSize); return; } - timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); + content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); } protected virtual void LoadBeatmap(WorkingBeatmap beatmap) { - timeline.Clear(); + content.Clear(); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 81b0fb247f..e945955db6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { DragBox = CreateDragBox(select), selectionHandler, - selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }, + selectionBlueprints = CreateSelectionBlueprintContainer(), DragBox.CreateProxy().With(p => p.Depth = float.MinValue) }); @@ -67,6 +67,8 @@ namespace osu.Game.Screens.Edit.Compose.Components AddBlueprintFor(obj); } + protected virtual SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override void LoadComplete() { base.LoadComplete(); @@ -118,7 +120,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (clickedBlueprint == null) return false; - adjustableClock?.Seek(clickedBlueprint.DrawableObject.HitObject.StartTime); + adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime); return true; } @@ -208,7 +210,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void removeBlueprintFor(HitObject hitObject) { - var blueprint = selectionBlueprints.SingleOrDefault(m => m.DrawableObject.HitObject == hitObject); + var blueprint = selectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject); if (blueprint == null) return; @@ -346,8 +348,8 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject - movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First(); - screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition); + movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First(); + screenSpaceMovementStartPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct return true; } @@ -365,14 +367,14 @@ namespace osu.Game.Screens.Edit.Compose.Components Debug.Assert(screenSpaceMovementStartPosition != null); Vector2 startPosition = screenSpaceMovementStartPosition.Value; - HitObject draggedObject = movementBlueprint.DrawableObject.HitObject; + HitObject draggedObject = movementBlueprint.HitObject; // The final movement position, relative to screenSpaceMovementStartPosition Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects - if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) + if (!selectionHandler.HandleMovement(new MoveSelectionEvent((OverlaySelectionBlueprint)movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) return true; // Apply the start time at the newly snapped-to position @@ -411,29 +413,9 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private class SelectionBlueprintContainer : Container + protected class SelectionBlueprintContainer : Container { public IEnumerable AliveBlueprints => AliveInternalChildren.Cast(); - - protected override int Compare(Drawable x, Drawable y) - { - if (!(x is SelectionBlueprint xBlueprint) || !(y is SelectionBlueprint yBlueprint)) - return base.Compare(x, y); - - return Compare(xBlueprint, yBlueprint); - } - - public int Compare(SelectionBlueprint x, SelectionBlueprint y) - { - // dpeth is used to denote selected status (we always want selected blueprints to handle input first). - int d = x.Depth.CompareTo(y.Depth); - if (d != 0) - return d; - - // Put earlier hitobjects towards the end of the list, so they handle input first - int i = y.DrawableObject.HitObject.StartTime.CompareTo(x.DrawableObject.HitObject.StartTime); - return i == 0 ? CompareReverseChildID(x, y) : i; - } } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 1576def38e..3c41dead5d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return CreateBlueprintFor(drawable); } - public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; + public virtual OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; protected override void AddBlueprintFor(HitObject hitObject) { diff --git a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs index fe0a47aec8..8662347aeb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs +++ b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs @@ -7,14 +7,14 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { /// - /// An event which occurs when a is moved. + /// An event which occurs when a is moved. /// public class MoveSelectionEvent { /// - /// The that triggered this . + /// The that triggered this . /// - public readonly SelectionBlueprint Blueprint; + public readonly OverlaySelectionBlueprint Blueprint; /// /// The starting screen-space position of the hitobject. @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public readonly Vector2 InstantDelta; - public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition) + public MoveSelectionEvent(OverlaySelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition) { Blueprint = blueprint; ScreenSpaceStartPosition = screenSpaceStartPosition; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index bff94e66ed..907a22b9ce 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Edit.Compose.Components public IEnumerable SelectedBlueprints => selectedBlueprints; private readonly List selectedBlueprints; - public IEnumerable SelectedHitObjects => selectedBlueprints.Select(b => b.DrawableObject.HitObject); + public IEnumerable SelectedHitObjects => selectedBlueprints.Select(b => b.HitObject); private Drawable outline; @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void deleteSelected() { foreach (var h in selectedBlueprints.ToList()) - placementHandler.Delete(h.DrawableObject.HitObject); + placementHandler.Delete(h.HitObject); } #endregion diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index f521d08ada..e1acaa34cb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -2,13 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -19,32 +18,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { internal class TimelineHitObjectDisplay : BlueprintContainer { - private EditorBeatmap beatmap { get; } - - private readonly TimelinePart content; - public TimelineHitObjectDisplay(EditorBeatmap beatmap) { RelativeSizeAxes = Axes.Both; - - this.beatmap = beatmap; - - AddInternal(content = new TimelinePart { RelativeSizeAxes = Axes.Both }); } - [BackgroundDependencyLoader] - private void load() - { - foreach (var h in beatmap.HitObjects) - add(h); + protected override SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - beatmap.HitObjectAdded += add; - beatmap.HitObjectRemoved += remove; - beatmap.StartTimeChanged += h => + protected class TimelineSelectionBlueprintContainer : SelectionBlueprintContainer + { + protected override Container Content { get; } + + public TimelineSelectionBlueprintContainer() { - remove(h); - add(h); - }; + AddInternal(new TimelinePart(Content = new Container { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both }); + } } protected override void LoadComplete() @@ -53,24 +41,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline DragBox.Alpha = 0; } - private void remove(HitObject h) + protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) { - foreach (var d in content.OfType().Where(c => c.HitObject == h)) - d.Expire(); - } + //var yOffset = content.Count(d => d.X == h.StartTime); + var yOffset = 0; - private void add(HitObject h) - { - var yOffset = content.Count(d => d.X == h.StartTime); - - content.Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }); + return new TimelineHitObjectRepresentation(hitObject) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }; } protected override bool OnMouseDown(MouseDownEvent e) { base.OnMouseDown(e); - return false; // tempoerary until we correctly handle selections. + return false; // temporary until we correctly handle selections. } protected override DragBox CreateDragBox(Action performSelect) => new NoDragDragBox(performSelect); @@ -85,15 +68,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool UpdateDrag(MouseButtonEvent e) => false; } - private class TimelineHitObjectRepresentation : CompositeDrawable + private class TimelineHitObjectRepresentation : SelectionBlueprint { public const float THICKNESS = 3; - public readonly HitObject HitObject; - public TimelineHitObjectRepresentation(HitObject hitObject) + : base(hitObject) { - HitObject = hitObject; Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; diff --git a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs index 3233ee160d..6565f98666 100644 --- a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual }); } - protected void AddBlueprint(SelectionBlueprint blueprint) + protected void AddBlueprint(OverlaySelectionBlueprint blueprint) { Add(blueprint.With(d => { From 53fe0ce79086351260002e3162baccf63840c17d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 14:20:52 +0900 Subject: [PATCH 12/75] Use AliveChildren --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index e945955db6..438be6ab54 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -258,7 +258,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered)) return; - foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) + foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveChildren) { if (blueprint.IsHovered) { @@ -415,7 +415,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected class SelectionBlueprintContainer : Container { - public IEnumerable AliveBlueprints => AliveInternalChildren.Cast(); + //todo: remove } } } From 8f16c1cb049813cce2e1dabc07aa52e44e894038 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 14:21:00 +0900 Subject: [PATCH 13/75] Add non-hiding selection state --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 10 +++++++--- .../Components/Timeline/TimelineHitObjectDisplay.cs | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index d8952a3932..d50a9dce16 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both; AlwaysPresent = true; - Alpha = 0; + OnDeselected(); } private SelectionState state; @@ -64,12 +64,12 @@ namespace osu.Game.Rulesets.Edit switch (state) { case SelectionState.Selected: - Show(); + OnSelected(); Selected?.Invoke(this); break; case SelectionState.NotSelected: - Hide(); + OnDeselected(); Deselected?.Invoke(this); break; } @@ -78,6 +78,10 @@ namespace osu.Game.Rulesets.Edit } } + protected virtual void OnDeselected() => Hide(); + + protected virtual void OnSelected() => Show(); + // When not selected, input is only required for the blueprint itself to receive IsHovering protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index e1acaa34cb..5d1c4eeeae 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -70,6 +70,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class TimelineHitObjectRepresentation : SelectionBlueprint { + private Circle circle; + public const float THICKNESS = 3; public TimelineHitObjectRepresentation(HitObject hitObject) @@ -104,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - AddInternal(new Circle + AddInternal(circle = new Circle { Size = new Vector2(16), Anchor = Anchor.CentreLeft, @@ -116,6 +118,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline BorderThickness = THICKNESS, }); } + + protected override void OnSelected() => circle.BorderColour = Color4.Orange; + + protected override void OnDeselected() => circle.BorderColour = Color4.Black; } } } From 195068ba9a9f6d35708159b2276ec74e70568456 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 17:35:36 +0900 Subject: [PATCH 14/75] Cache EditorBeatmap in test --- osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 29575cb42e..e9372bd134 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -40,6 +40,8 @@ namespace osu.Game.Tests.Visual.Editor var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap); + Dependencies.Cache(editorBeatmap); + Children = new Drawable[] { new FillFlowContainer From 24a466ab249e6a88790a9f067f34ce44388e4521 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 17:36:21 +0900 Subject: [PATCH 15/75] Avoid null by calling initial OnDeselected later --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index d50a9dce16..1c82a8287b 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -40,11 +40,15 @@ namespace osu.Game.Rulesets.Edit protected SelectionBlueprint(HitObject hitObject) { - this.HitObject = hitObject; + HitObject = hitObject; RelativeSizeAxes = Axes.Both; + } - AlwaysPresent = true; + [BackgroundDependencyLoader] + private void load() + { OnDeselected(); + AlwaysPresent = true; } private SelectionState state; @@ -116,5 +120,7 @@ namespace osu.Game.Rulesets.Edit /// The screen-space quad that outlines this for selections. /// public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; + + public Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; } } From 79351976d5bc3eed44859e1a2f357d0f1b59f8e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 17:37:05 +0900 Subject: [PATCH 16/75] Allow timeline content to get more localised dependencies --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index aa8d99b517..8967f24185 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Edit private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - private TimelineArea timelineArea; + private Container timelineContainer; [BackgroundDependencyLoader(true)] private void load([CanBeNull] BindableBeatDivisor beatDivisor) @@ -62,11 +62,10 @@ namespace osu.Game.Screens.Edit { new Drawable[] { - new Container + timelineContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = timelineArea = CreateTimelineArea() }, new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } }, @@ -100,14 +99,16 @@ namespace osu.Game.Screens.Edit mainContent.Add(content); content.FadeInFromZero(300, Easing.OutQuint); - LoadComponentAsync(CreateTimelineContent(), timelineArea.Add); + LoadComponentAsync(new TimelineArea + { + RelativeSizeAxes = Axes.Both, + Child = CreateTimelineContent() + }, timelineContainer.Add); }); } protected abstract Drawable CreateMainContent(); protected virtual Drawable CreateTimelineContent() => new Container(); - - protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both }; } } From 353b74b04a81331a9343e5cd0f3ba41ba129a64e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 17:37:35 +0900 Subject: [PATCH 17/75] Handle selection events in timeline --- .../Components/Timeline/TimelineHitObjectDisplay.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 5d1c4eeeae..32efdd42a3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -49,15 +49,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return new TimelineHitObjectRepresentation(hitObject) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }; } - protected override bool OnMouseDown(MouseDownEvent e) - { - base.OnMouseDown(e); - - return false; // temporary until we correctly handle selections. - } - - protected override DragBox CreateDragBox(Action performSelect) => new NoDragDragBox(performSelect); - internal class NoDragDragBox : DragBox { public NoDragDragBox(Action performSelect) @@ -74,6 +65,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public const float THICKNESS = 3; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos); + public TimelineHitObjectRepresentation(HitObject hitObject) : base(hitObject) { From 6dd50572d242993a9c1a174ea4e5cf4344c0996a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 17:37:43 +0900 Subject: [PATCH 18/75] Break mania more --- osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index f33fd3c640..9069a636a8 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Edit // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight; + delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong foreach (var selectionBlueprint in SelectedBlueprints) { From 6187b2e77cad79cb899af96adb66d400081bf9fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 18:00:36 +0900 Subject: [PATCH 19/75] Implement IDistanceSnapProvider in timeline for now --- .../Compose/Components/Timeline/Timeline.cs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index b4f3b1f610..c866fb38c8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -10,11 +10,15 @@ using osu.Framework.Graphics.Audio; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class Timeline : ZoomableScrollContainer + [Cached(typeof(IDistanceSnapProvider))] + public class Timeline : ZoomableScrollContainer, IDistanceSnapProvider { public readonly Bindable WaveformVisible = new Bindable(); public readonly IBindable Beatmap = new Bindable(); @@ -162,5 +166,51 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (trackWasPlaying) adjustableClock.Start(); } + + [Resolved] + private BindableBeatDivisor beatDivisor { get; set; } + + [Resolved] + private EditorBeatmap beatmap { get; set; } + + public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time); + + public float GetBeatSnapDistanceAt(double referenceTime) + { + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(referenceTime); + return (float)(100 * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatDivisor.Value); + } + + public float DurationToDistance(double referenceTime, double duration) + { + double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime)); + } + + public double DistanceToDuration(double referenceTime, float distance) + { + double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength; + } + + public double GetSnappedDurationFromDistance(double referenceTime, float distance) + => beatSnap(referenceTime, DistanceToDuration(referenceTime, distance)); + + public float GetSnappedDistanceFromDistance(double referenceTime, float distance) + => DurationToDistance(referenceTime, beatSnap(referenceTime, DistanceToDuration(referenceTime, distance))); + + /// + /// Snaps a duration to the closest beat of a timing point applicable at the reference time. + /// + /// The time of the timing point which resides in. + /// The duration to snap. + /// A value that represents snapped to the closest beat of the timing point. + private double beatSnap(double referenceTime, double duration) + { + double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + + // A 1ms offset prevents rounding errors due to minute variations in duration + return (int)((duration + 1) / beatLength) * beatLength; + } } } From a888d148b653477e11ea7b5a292faf5a2c63b39d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 18:01:10 +0900 Subject: [PATCH 20/75] Remove remaining cast --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 +- .../Screens/Edit/Compose/Components/MoveSelectionEvent.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 438be6ab54..b792065b43 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -374,7 +374,7 @@ namespace osu.Game.Screens.Edit.Compose.Components (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects - if (!selectionHandler.HandleMovement(new MoveSelectionEvent((OverlaySelectionBlueprint)movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) + if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) return true; // Apply the start time at the newly snapped-to position diff --git a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs index 8662347aeb..48cb702c78 100644 --- a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs +++ b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs @@ -12,9 +12,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public class MoveSelectionEvent { /// - /// The that triggered this . + /// The that triggered this . /// - public readonly OverlaySelectionBlueprint Blueprint; + public readonly SelectionBlueprint Blueprint; /// /// The starting screen-space position of the hitobject. @@ -34,13 +34,13 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public readonly Vector2 InstantDelta; - public MoveSelectionEvent(OverlaySelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition) + public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition) { Blueprint = blueprint; ScreenSpaceStartPosition = screenSpaceStartPosition; ScreenSpacePosition = screenSpacePosition; - InstantDelta = Blueprint.DrawableObject.Parent.ToLocalSpace(ScreenSpacePosition) - Blueprint.DrawableObject.Position; + InstantDelta = Blueprint.GetInstantDelta(ScreenSpacePosition); } } } From f582c42bbd3a248c98d47bcc27470a94c7e7b6d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 18:56:09 +0900 Subject: [PATCH 21/75] Perform deletion directly via EditorBeatmap --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 907a22b9ce..9d9685af8a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Drawable outline; [Resolved(CanBeNull = true)] - private IPlacementHandler placementHandler { get; set; } + private EditorBeatmap editorBeatmap { get; set; } public SelectionHandler() { @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void deleteSelected() { foreach (var h in selectedBlueprints.ToList()) - placementHandler.Delete(h.HitObject); + editorBeatmap.Remove(h.HitObject); } #endregion From 1ce78afa9872008ceed0a428366eedb4da9472d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 19:51:44 +0900 Subject: [PATCH 22/75] Disable y offset for now --- .../Compose/Components/Timeline/TimelineHitObjectDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 32efdd42a3..d83336b21b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -44,9 +44,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) { //var yOffset = content.Count(d => d.X == h.StartTime); - var yOffset = 0; + //var yOffset = 0; - return new TimelineHitObjectRepresentation(hitObject) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }; + return new TimelineHitObjectRepresentation(hitObject); } internal class NoDragDragBox : DragBox From c88bdbd4a0264f766de60f88af736d1da4b6a998 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 20:46:39 +0900 Subject: [PATCH 23/75] Share selected hitobjects across multiple blueprint containers --- .../Compose/Components/BlueprintContainer.cs | 18 ++++++++++++++++++ .../Compose/Components/SelectionHandler.cs | 7 ++++++- osu.Game/Screens/Edit/EditorBeatmap.cs | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index b792065b43..c5414542e4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -41,6 +42,8 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private EditorBeatmap beatmap { get; set; } + private readonly BindableList selectedHitObjects = new BindableList(); + [Resolved(canBeNull: true)] private IDistanceSnapProvider snapProvider { get; set; } @@ -65,6 +68,19 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var obj in beatmap.HitObjects) AddBlueprintFor(obj); + + selectedHitObjects.BindTo(beatmap.SelectedHitObjects); + selectedHitObjects.ItemsAdded += objects => + { + foreach (var o in objects) + selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select(); + }; + + selectedHitObjects.ItemsRemoved += objects => + { + foreach (var o in objects) + selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect(); + }; } protected virtual SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; @@ -315,6 +331,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { selectionHandler.HandleSelected(blueprint); selectionBlueprints.ChangeChildDepth(blueprint, 1); + beatmap.SelectedHitObjects.Add(blueprint.HitObject); SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects); } @@ -323,6 +340,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { selectionHandler.HandleDeselected(blueprint); selectionBlueprints.ChangeChildDepth(blueprint, 0); + beatmap.SelectedHitObjects.Remove(blueprint.HitObject); SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects); } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9d9685af8a..6ca110e518 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -102,7 +102,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Handle a blueprint becoming selected. /// /// The blueprint. - internal void HandleSelected(SelectionBlueprint blueprint) => selectedBlueprints.Add(blueprint); + internal void HandleSelected(SelectionBlueprint blueprint) + { + selectedBlueprints.Add(blueprint); + editorBeatmap.SelectedHitObjects.Add(blueprint.HitObject); + } /// /// Handle a blueprint becoming deselected. @@ -111,6 +115,7 @@ namespace osu.Game.Screens.Edit.Compose.Components internal void HandleDeselected(SelectionBlueprint blueprint) { selectedBlueprints.Remove(blueprint); + editorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject); // We don't want to update visibility if > 0, since we may be deselecting blueprints during drag-selection if (selectedBlueprints.Count == 0) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 6ed74dfdb0..2ff7563a88 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -29,6 +29,8 @@ namespace osu.Game.Screens.Edit /// public event Action StartTimeChanged; + public BindableList SelectedHitObjects { get; } = new BindableList(); + public readonly IBeatmap PlayableBeatmap; private readonly Dictionary> startTimeBindables = new Dictionary>(); From a963d652bc1caee47081b5d4a8dc0ae93134a246 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 20:54:50 +0900 Subject: [PATCH 24/75] Mark readonly --- .../Compose/Components/Timeline/TimelineHitObjectDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index d83336b21b..b2ff9eee12 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class TimelineHitObjectRepresentation : SelectionBlueprint { - private Circle circle; + private readonly Circle circle; public const float THICKNESS = 3; From 83fa4a9bb34d74e362cd7e684f0879c9d385a812 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 22:24:04 +0900 Subject: [PATCH 25/75] Move circle size to a constant --- .../Compose/Components/Timeline/TimelineHitObjectDisplay.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index b2ff9eee12..7932813bbc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -65,6 +65,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public const float THICKNESS = 3; + private const float circle_size = 16; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos); public TimelineHitObjectRepresentation(HitObject hitObject) @@ -101,7 +103,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AddInternal(circle = new Circle { - Size = new Vector2(16), + Size = new Vector2(circle_size), Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, RelativePositionAxes = Axes.X, From 98aaf3864953f31a942184bdc6adce5e3781e782 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Jan 2020 23:58:51 +0900 Subject: [PATCH 26/75] Fix playfield movement regressing --- osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs | 2 ++ osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs index 4c3898aa04..b4ae3f3fba 100644 --- a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs @@ -28,5 +28,7 @@ namespace osu.Game.Rulesets.Edit public override Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre; public override Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad; + + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => DrawableObject.Parent.ToLocalSpace(screenSpacePosition) - DrawableObject.Position; } } diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 1c82a8287b..9998254f76 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -121,6 +121,6 @@ namespace osu.Game.Rulesets.Edit /// public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; - public Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; + public virtual Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; } } From e3a2b20f631532dece1d51a4eabd89cc4c05bdff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 01:32:11 +0900 Subject: [PATCH 27/75] Fix SelectionHandler visibility on remote selection --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 6ca110e518..0aced5f957 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -106,6 +106,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { selectedBlueprints.Add(blueprint); editorBeatmap.SelectedHitObjects.Add(blueprint.HitObject); + + UpdateVisibility(); } /// @@ -144,8 +146,6 @@ namespace osu.Game.Screens.Edit.Compose.Components DeselectAll?.Invoke(); blueprint.Select(); } - - UpdateVisibility(); } private void deleteSelected() From a6775d1bd3a0432e5e2b3b907d3bb14851fc26fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 14:58:15 +0900 Subject: [PATCH 28/75] Implement custom drag box and allow drag seeking once again --- .../Edit/Compose/Components/DragBox.cs | 36 ++++++++++--------- .../Timeline/TimelineHitObjectDisplay.cs | 35 ++++++++++++++++-- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index f522ca356f..adbab1767b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -17,9 +17,9 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public class DragBox : CompositeDrawable { - private readonly Action performSelection; + protected readonly Action PerformSelection; - private Drawable box; + protected Drawable Box; /// /// Creates a new . @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// A delegate that performs drag selection. public DragBox(Action performSelection) { - this.performSelection = performSelection; + PerformSelection = performSelection; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; @@ -37,19 +37,21 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - InternalChild = box = new Container - { - Masking = true, - BorderColour = Color4.White, - BorderThickness = SelectionHandler.BORDER_RADIUS, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - } - }; + InternalChild = Box = CreateBox(); } + protected virtual Drawable CreateBox() => new Container + { + Masking = true, + BorderColour = Color4.White, + BorderThickness = SelectionHandler.BORDER_RADIUS, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + } + }; + /// /// Handle a forwarded mouse event. /// @@ -68,10 +70,10 @@ namespace osu.Game.Screens.Edit.Compose.Components var topLeft = ToLocalSpace(dragRectangle.TopLeft); var bottomRight = ToLocalSpace(dragRectangle.BottomRight); - box.Position = topLeft; - box.Size = bottomRight - topLeft; + Box.Position = topLeft; + Box.Size = bottomRight - topLeft; - performSelection?.Invoke(dragRectangle); + PerformSelection?.Invoke(dragRectangle); return true; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 7932813bbc..e68b088d78 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -21,6 +21,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public TimelineHitObjectDisplay(EditorBeatmap beatmap) { RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Height = 0.4f; + + AddInternal(new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f, + }); } protected override SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; @@ -49,14 +60,32 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return new TimelineHitObjectRepresentation(hitObject); } - internal class NoDragDragBox : DragBox + protected override DragBox CreateDragBox(Action performSelect) => new CustomDragBox(performSelect); + + internal class CustomDragBox : DragBox { - public NoDragDragBox(Action performSelect) + public CustomDragBox(Action performSelect) : base(performSelect) { } - public override bool UpdateDrag(MouseButtonEvent e) => false; + protected override Drawable CreateBox() => new Box + { + RelativeSizeAxes = Axes.Y, + Alpha = 0.3f + }; + + public override bool UpdateDrag(MouseButtonEvent e) + { + float selection1 = e.MouseDownPosition.X; + float selection2 = e.MousePosition.X; + + Box.X = Math.Min(selection1, selection2); + Box.Width = Math.Abs(selection1 - selection2); + + PerformSelection?.Invoke(Box.ScreenSpaceDrawQuad.AABBFloat); + return true; + } } private class TimelineHitObjectRepresentation : SelectionBlueprint From 482409e776cd9def6f45af0106b1fd5367aff221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 15:31:58 +0900 Subject: [PATCH 29/75] Colour extension bars of long objects --- .../Timeline/TimelineHitObjectDisplay.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index e68b088d78..b09c1a5597 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -92,6 +92,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { private readonly Circle circle; + private Container extensionBar; + public const float THICKNESS = 3; private const float circle_size = 16; @@ -109,11 +111,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline X = (float)hitObject.StartTime; RelativePositionAxes = Axes.X; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; if (hitObject is IHasEndTime) { - AddInternal(new Container + AddInternal(extensionBar = new Container { CornerRadius = 2, Masking = true, @@ -143,9 +147,32 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - protected override void OnSelected() => circle.BorderColour = Color4.Orange; + protected override void OnSelected() + { + circle.BorderColour = Color4.Orange; + if (extensionBar != null) + extensionBar.Colour = Color4.Orange; + } - protected override void OnDeselected() => circle.BorderColour = Color4.Black; + protected override void OnDeselected() + { + circle.BorderColour = Color4.Black; + if (extensionBar != null) + extensionBar.Colour = Color4.Black; + } + + public override Quad SelectionQuad + { + get + { + // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself. + var circleQuad = circle.ScreenSpaceDrawQuad; + var actualQuad = ScreenSpaceDrawQuad; + + return new Quad(circleQuad.TopLeft, Vector2.ComponentMax(actualQuad.TopRight, circleQuad.TopRight), + circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight)); + } + } } } } From c4395b1cea45b242e2f19816a92b9aa4df2c727a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 15:42:49 +0900 Subject: [PATCH 30/75] Clean up nested class implementations --- .../Timeline/TimelineHitObjectDisplay.cs | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index b09c1a5597..397c74b193 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -36,33 +36,32 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected class TimelineSelectionBlueprintContainer : SelectionBlueprintContainer - { - protected override Container Content { get; } - - public TimelineSelectionBlueprintContainer() - { - AddInternal(new TimelinePart(Content = new Container { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both }); - } - } - protected override void LoadComplete() { base.LoadComplete(); DragBox.Alpha = 0; } - protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) + protected override bool OnDrag(DragEvent e) { - //var yOffset = content.Count(d => d.X == h.StartTime); - //var yOffset = 0; - - return new TimelineHitObjectRepresentation(hitObject); + lastDragEvent = e; + return base.OnDrag(e); } + protected override void Update() + { + if (IsDragged && lastDragEvent != null) + // trigger every frame so drags continue to update selection while playback is scrolling the timeline. + DragBox.UpdateDrag(lastDragEvent); + + base.Update(); + } + + protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectRepresentation(hitObject); + protected override DragBox CreateDragBox(Action performSelect) => new CustomDragBox(performSelect); - internal class CustomDragBox : DragBox + private class CustomDragBox : DragBox { public CustomDragBox(Action performSelect) : base(performSelect) @@ -88,11 +87,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + protected class TimelineSelectionBlueprintContainer : SelectionBlueprintContainer + { + protected override Container Content { get; } + + public TimelineSelectionBlueprintContainer() + { + AddInternal(new TimelinePart(Content = new Container { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both }); + } + } + private class TimelineHitObjectRepresentation : SelectionBlueprint { private readonly Circle circle; - private Container extensionBar; + private readonly Container extensionBar; public const float THICKNESS = 3; From a8ec4907c410dedb4d8c71bc5348db3c096d73ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 15:58:03 +0900 Subject: [PATCH 31/75] Fix selections while scrolling timeline --- .../Components/Timeline/TimelineHitObjectDisplay.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 397c74b193..d50616a66f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -63,6 +63,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class CustomDragBox : DragBox { + private Vector2 lastMouseDown; + private float localMouseDown; + public CustomDragBox(Action performSelect) : base(performSelect) { @@ -76,7 +79,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool UpdateDrag(MouseButtonEvent e) { - float selection1 = e.MouseDownPosition.X; + // store the original position of the mouse down, as we may be scrolled during selection. + if (lastMouseDown != e.ScreenSpaceMouseDownPosition) + { + lastMouseDown = e.ScreenSpaceMouseDownPosition; + localMouseDown = e.MouseDownPosition.X; + } + + float selection1 = localMouseDown; float selection2 = e.MousePosition.X; Box.X = Math.Min(selection1, selection2); From 89d90fdfa054620811404ab60c694d6cb12098c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 16:33:23 +0900 Subject: [PATCH 32/75] Fix drag not updating until mouse is moved while scrolling timeline --- .../Components/Timeline/TimelineHitObjectDisplay.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index d50616a66f..f812095a35 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -18,6 +18,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { internal class TimelineHitObjectDisplay : BlueprintContainer { + private DragEvent lastDragEvent; + public TimelineHitObjectDisplay(EditorBeatmap beatmap) { RelativeSizeAxes = Axes.Both; @@ -50,9 +52,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override void Update() { - if (IsDragged && lastDragEvent != null) - // trigger every frame so drags continue to update selection while playback is scrolling the timeline. - DragBox.UpdateDrag(lastDragEvent); + // trigger every frame so drags continue to update selection while playback is scrolling the timeline. + if (IsDragged) + OnDrag(lastDragEvent); base.Update(); } From 63cef8b8b7189bb4c9215c26e90d532ffd5d83ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 16:58:33 +0900 Subject: [PATCH 33/75] Rename nested classes to be more appropriate --- .../Components/Timeline/TimelineHitObjectDisplay.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index f812095a35..10b470e4b7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -59,16 +59,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline base.Update(); } - protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectRepresentation(hitObject); + protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject); - protected override DragBox CreateDragBox(Action performSelect) => new CustomDragBox(performSelect); + protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect); - private class CustomDragBox : DragBox + private class TimelineDragBox : DragBox { private Vector2 lastMouseDown; private float localMouseDown; - public CustomDragBox(Action performSelect) + public TimelineDragBox(Action performSelect) : base(performSelect) { } @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private class TimelineHitObjectRepresentation : SelectionBlueprint + private class TimelineHitObjectBlueprint : SelectionBlueprint { private readonly Circle circle; @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos); - public TimelineHitObjectRepresentation(HitObject hitObject) + public TimelineHitObjectBlueprint(HitObject hitObject) : base(hitObject) { Anchor = Anchor.CentreLeft; From f0d810fe2098a94a15ad34e439a98ca36e42b59b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 17:11:37 +0900 Subject: [PATCH 34/75] Follow start time and duration changes --- .../Timeline/TimelineHitObjectDisplay.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 10b470e4b7..a11958c8c9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -115,6 +117,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Container extensionBar; + [UsedImplicitly] + private readonly Bindable startTime; + public const float THICKNESS = 3; private const float circle_size = 16; @@ -127,9 +132,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; - Width = (float)(hitObject.GetEndTime() - hitObject.StartTime); - - X = (float)hitObject.StartTime; + startTime = hitObject.StartTimeBindable.GetBoundCopy(); + startTime.BindValueChanged(time => X = (float)time.NewValue, true); RelativePositionAxes = Axes.X; @@ -168,6 +172,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } + protected override void Update() + { + base.Update(); + + // no bindable so we perform this every update + Width = (float)(HitObject.GetEndTime() - HitObject.StartTime); + } + protected override void OnSelected() { circle.BorderColour = Color4.Orange; From cb6e7425aebc9789ac80af0a21f29fd25bad2205 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 17:54:11 +0900 Subject: [PATCH 35/75] Make dragbox stateful to fix blueprint movement --- .../Compose/Components/BlueprintContainer.cs | 29 +++++++++---------- .../Edit/Compose/Components/DragBox.cs | 26 +++++++++++++++-- .../Timeline/TimelineHitObjectDisplay.cs | 8 ++++- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index c5414542e4..583627f78e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -152,15 +152,16 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return false; - if (!beginSelectionMovement()) - { - if (!DragBox.UpdateDrag(e)) - return false; + if (beginSelectionMovement()) + return true; - DragBox.FadeIn(250, Easing.OutQuint); + if (DragBox.HandleDrag(e)) + { + DragBox.Show(); + return true; } - return true; + return false; } protected override bool OnDrag(DragEvent e) @@ -168,13 +169,10 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return false; - if (!moveCurrentSelection(e)) - { - if (!DragBox.UpdateDrag(e)) - return false; - } + if (DragBox.State == Visibility.Visible) + return DragBox.HandleDrag(e); - return true; + return moveCurrentSelection(e); } protected override bool OnDragEnd(DragEndEvent e) @@ -182,13 +180,14 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return false; - if (!finishSelectionMovement()) + if (DragBox.State == Visibility.Visible) { - DragBox.FadeOut(250, Easing.OutQuint); + DragBox.Hide(); selectionHandler.UpdateVisibility(); + return true; } - return true; + return finishSelectionMovement(); } protected override bool OnKeyDown(KeyDownEvent e) diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index adbab1767b..c5f1bd1575 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// A box that displays the drag selection and provides selection events for users to handle. /// - public class DragBox : CompositeDrawable + public class DragBox : CompositeDrawable, IStateful { protected readonly Action PerformSelection; @@ -57,7 +58,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The mouse event. /// Whether the event should be handled and blocking. - public virtual bool UpdateDrag(MouseButtonEvent e) + public virtual bool HandleDrag(MouseButtonEvent e) { var dragPosition = e.ScreenSpaceMousePosition; var dragStartPosition = e.ScreenSpaceMouseDownPosition; @@ -76,5 +77,26 @@ namespace osu.Game.Screens.Edit.Compose.Components PerformSelection?.Invoke(dragRectangle); return true; } + + private Visibility state; + + public Visibility State + { + get => state; + set + { + if (value == state) return; + + state = value; + this.FadeTo(state == Visibility.Hidden ? 0 : 1, 250, Easing.OutQuint); + StateChanged?.Invoke(state); + } + } + + public override void Hide() => State = Visibility.Hidden; + + public override void Show() => State = Visibility.Visible; + + public event Action StateChanged; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index a11958c8c9..7101fac310 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -52,6 +52,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return base.OnDrag(e); } + protected override bool OnDragEnd(DragEndEvent e) + { + lastDragEvent = null; + return base.OnDragEnd(e); + } + protected override void Update() { // trigger every frame so drags continue to update selection while playback is scrolling the timeline. @@ -81,7 +87,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Alpha = 0.3f }; - public override bool UpdateDrag(MouseButtonEvent e) + public override bool HandleDrag(MouseButtonEvent e) { // store the original position of the mouse down, as we may be scrolled during selection. if (lastMouseDown != e.ScreenSpaceMouseDownPosition) From 53bdf72592ba86418da460560515553e63401437 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 21:37:06 +0900 Subject: [PATCH 36/75] Allow basic timeline selection temporal movement --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 2 +- .../Components/Timeline/TimelineHitObjectDisplay.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index c866fb38c8..21eb4b08b0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private EditorBeatmap beatmap { get; set; } - public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time); + public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, (position.X / Content.DrawWidth) * track.Length); public float GetBeatSnapDistanceAt(double referenceTime) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 7101fac310..5e6403bb05 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -67,6 +67,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline base.Update(); } + protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler(); + + internal class TimelineSelectionHandler : SelectionHandler + { + public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; + } + protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject); protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect); @@ -212,6 +219,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight)); } } + + public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft; } } } From aa1a226ab7522b3207d3c3a949fbec1a905be345 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 21:37:57 +0900 Subject: [PATCH 37/75] Remove unused ScreenSpaceStartPosition field --- .../Edit/Compose/Components/BlueprintContainer.cs | 2 +- .../Edit/Compose/Components/MoveSelectionEvent.cs | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 583627f78e..58cd4d0974 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -391,7 +391,7 @@ namespace osu.Game.Screens.Edit.Compose.Components (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects - if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) + if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) return true; // Apply the start time at the newly snapped-to position diff --git a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs index 48cb702c78..0792d0f80e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs +++ b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs @@ -16,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public readonly SelectionBlueprint Blueprint; - /// - /// The starting screen-space position of the hitobject. - /// - public readonly Vector2 ScreenSpaceStartPosition; - /// /// The expected screen-space position of the hitobject at the current cursor position. /// @@ -29,15 +24,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The distance between and the hitobject's current position, in the coordinate-space of the hitobject's parent. /// - /// - /// This does not use and does not represent the cumulative movement distance. - /// public readonly Vector2 InstantDelta; - public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition) + public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpacePosition) { Blueprint = blueprint; - ScreenSpaceStartPosition = screenSpaceStartPosition; ScreenSpacePosition = screenSpacePosition; InstantDelta = Blueprint.GetInstantDelta(ScreenSpacePosition); From c76f76e5aa441c526278345a706f34487e6c1eaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 21:43:02 +0900 Subject: [PATCH 38/75] Fix being able to drag out of the blueprint intending to be moved --- .../Compose/Components/BlueprintContainer.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 58cd4d0974..ef4a06879e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -109,6 +109,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { beginClickSelection(e); + + prepareSelectionMovement(); + return e.Button == MouseButton.Left; } @@ -144,6 +147,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { // Special case for when a drag happened instead of a click Schedule(() => endClickSelection()); + + finishSelectionMovement(); + return e.Button == MouseButton.Left; } @@ -152,7 +158,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return false; - if (beginSelectionMovement()) + if (movementBlueprint != null) return true; if (DragBox.HandleDrag(e)) @@ -184,10 +190,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { DragBox.Hide(); selectionHandler.UpdateVisibility(); - return true; } - return finishSelectionMovement(); + return true; } protected override bool OnKeyDown(KeyDownEvent e) @@ -348,14 +353,14 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Selection Movement - private Vector2? screenSpaceMovementStartPosition; + private Vector2? movementBlueprintOriginalPosition; private SelectionBlueprint movementBlueprint; /// /// Attempts to begin the movement of any selected blueprints. /// /// Whether movement began. - private bool beginSelectionMovement() + private bool prepareSelectionMovement() { Debug.Assert(movementBlueprint == null); @@ -366,7 +371,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First(); - screenSpaceMovementStartPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct + movementBlueprintOriginalPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct return true; } @@ -381,13 +386,13 @@ namespace osu.Game.Screens.Edit.Compose.Components if (movementBlueprint == null) return false; - Debug.Assert(screenSpaceMovementStartPosition != null); + Debug.Assert(movementBlueprintOriginalPosition != null); - Vector2 startPosition = screenSpaceMovementStartPosition.Value; HitObject draggedObject = movementBlueprint.HitObject; // The final movement position, relative to screenSpaceMovementStartPosition - Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects @@ -411,7 +416,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (movementBlueprint == null) return false; - screenSpaceMovementStartPosition = null; + movementBlueprintOriginalPosition = null; movementBlueprint = null; return true; From 9d2a46df892587185343b8ebb7723823df750c33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Jan 2020 21:46:24 +0900 Subject: [PATCH 39/75] Add beat snapping to timeline movement --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 21eb4b08b0..98fb754931 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -173,7 +173,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private EditorBeatmap beatmap { get; set; } - public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, (position.X / Content.DrawWidth) * track.Length); + public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) + { + var targetTime = (position.X / Content.DrawWidth) * track.Length; + return (position, beatSnap(targetTime, targetTime)); + } public float GetBeatSnapDistanceAt(double referenceTime) { From 477e1b7d278a9b1b490a43d4291c5c0af00b05f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 12:17:46 +0900 Subject: [PATCH 40/75] Rename TimelineHitObjectDisplay to TimelineBlueprintContainer --- .../Visual/Editor/TestSceneEditorComposeTimeline.cs | 4 ++-- ...elineHitObjectDisplay.cs => TimelineBlueprintContainer.cs} | 4 ++-- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Screens/Edit/Compose/Components/Timeline/{TimelineHitObjectDisplay.cs => TimelineBlueprintContainer.cs} (98%) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index e9372bd134..78cdeb3ac8 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Editor public override IReadOnlyList RequiredTypes => new[] { typeof(TimelineArea), - typeof(TimelineHitObjectDisplay), + typeof(TimelineBlueprintContainer), typeof(Timeline), typeof(TimelineButton), typeof(CentreMarker) @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Editor }, new TimelineArea { - Child = new TimelineHitObjectDisplay(editorBeatmap), + Child = new TimelineBlueprintContainer(editorBeatmap), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs similarity index 98% rename from osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs rename to osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 5e6403bb05..c5603c0f67 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -18,11 +18,11 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - internal class TimelineHitObjectDisplay : BlueprintContainer + internal class TimelineBlueprintContainer : BlueprintContainer { private DragEvent lastDragEvent; - public TimelineHitObjectDisplay(EditorBeatmap beatmap) + public TimelineBlueprintContainer(EditorBeatmap beatmap) { RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 1a6aae294a..20e1ef95d3 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); } - protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineHitObjectDisplay(EditorBeatmap); + protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(EditorBeatmap); } } From cb09c2e1449ee26285d14a27a9f40c3fb0f937d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 12:29:32 +0900 Subject: [PATCH 41/75] Add support for dragging outside visible extents --- .../Edit/Compose/Components/Timeline/Timeline.cs | 1 + .../Timeline/TimelineBlueprintContainer.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 98fb754931..804c2f1e65 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -18,6 +18,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { [Cached(typeof(IDistanceSnapProvider))] + [Cached] public class Timeline : ZoomableScrollContainer, IDistanceSnapProvider { public readonly Bindable WaveformVisible = new Bindable(); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index c5603c0f67..16e1793c8c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -3,6 +3,7 @@ using System; using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -20,6 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { internal class TimelineBlueprintContainer : BlueprintContainer { + [Resolved(CanBeNull = true)] + private Timeline timeline { get; set; } + private DragEvent lastDragEvent; public TimelineBlueprintContainer(EditorBeatmap beatmap) @@ -48,6 +52,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool OnDrag(DragEvent e) { + if (timeline != null) + { + var timelineQuad = timeline.ScreenSpaceDrawQuad; + var mouseX = e.ScreenSpaceMousePosition.X; + + // scroll if in a drag and dragging outside visible extents + if (mouseX > timelineQuad.TopRight.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime)); + else if (mouseX < timelineQuad.TopLeft.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime)); + } + lastDragEvent = e; return base.OnDrag(e); } From 56c044c44ac6cc6ea460fef8883d99fb9a88aadc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 13:33:55 +0900 Subject: [PATCH 42/75] Move beat snapping to its own interface --- ...tSceneHitObjectComposerDistanceSnapping.cs | 9 +++- .../Visual/Editor/TestSceneComposeScreen.cs | 2 + .../Editor/TestSceneEditorComposeTimeline.cs | 4 +- .../Editor/TestSceneHitObjectComposer.cs | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 26 +++-------- osu.Game/Rulesets/Edit/IBeatSnapProvider.cs | 30 ++++++++++++ .../Compose/Components/Timeline/Timeline.cs | 46 ++++--------------- osu.Game/Screens/Edit/Editor.cs | 15 +++++- osu.Game/Screens/Edit/EditorBeatmap.cs | 20 +++++++- 9 files changed, 90 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/IBeatSnapProvider.cs diff --git a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs index 2d336bd19c..e825df5a3f 100644 --- a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Edit; @@ -19,7 +20,13 @@ namespace osu.Game.Tests.Editor private TestHitObjectComposer composer; [Cached(typeof(EditorBeatmap))] - private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + [Cached(typeof(IBeatSnapProvider))] + private readonly EditorBeatmap editorBeatmap; + + public TestSceneHitObjectComposerDistanceSnapping() + { + editorBeatmap = new EditorBeatmap(new OsuBeatmap(), BeatDivisor); + } [SetUp] public void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs index 3562689482..a8830824c0 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; @@ -14,6 +15,7 @@ namespace osu.Game.Tests.Visual.Editor public class TestSceneComposeScreen : EditorClockTestScene { [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 78cdeb3ac8..5b0408268f 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; @@ -38,9 +39,10 @@ namespace osu.Game.Tests.Visual.Editor { Beatmap.Value = new WaveformTestBeatmap(audio); - var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap); + var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap, BeatDivisor); Dependencies.Cache(editorBeatmap); + Dependencies.CacheAs(editorBeatmap); Children = new Drawable[] { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs index c001c83877..e41c2427fb 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs @@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.Editor Dependencies.CacheAs(clock); Dependencies.CacheAs(clock); Dependencies.CacheAs(editorBeatmap); + Dependencies.CacheAs(editorBeatmap); Child = new OsuHitObjectComposer(new OsuRuleset()); } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 5eba31d149..50042d1e3b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Edit private IAdjustableClock adjustableClock { get; set; } [Resolved] - private BindableBeatDivisor beatDivisor { get; set; } + private IBeatSnapProvider beatSnapProvider { get; set; } private IBeatmapProcessor beatmapProcessor; @@ -259,40 +259,26 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(double referenceTime) { DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime); - return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatDivisor.Value); + return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatSnapProvider.BeatDivisor); } public override float DurationToDistance(double referenceTime, double duration) { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime, beatSnapProvider.BeatDivisor); return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime)); } public override double DistanceToDuration(double referenceTime, float distance) { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime, beatSnapProvider.BeatDivisor); return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength; } public override double GetSnappedDurationFromDistance(double referenceTime, float distance) - => beatSnap(referenceTime, DistanceToDuration(referenceTime, distance)); + => beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance), beatSnapProvider.BeatDivisor); public override float GetSnappedDistanceFromDistance(double referenceTime, float distance) - => DurationToDistance(referenceTime, beatSnap(referenceTime, DistanceToDuration(referenceTime, distance))); - - /// - /// Snaps a duration to the closest beat of a timing point applicable at the reference time. - /// - /// The time of the timing point which resides in. - /// The duration to snap. - /// A value that represents snapped to the closest beat of the timing point. - private double beatSnap(double referenceTime, double duration) - { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; - - // A 1ms offset prevents rounding errors due to minute variations in duration - return (int)((duration + 1) / beatLength) * beatLength; - } + => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance), beatSnapProvider.BeatDivisor)); protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs new file mode 100644 index 0000000000..ed6e08d054 --- /dev/null +++ b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit +{ + public interface IBeatSnapProvider + { + /// + /// Snaps a duration to the closest beat of a timing point applicable at the reference time. + /// + /// The time of the timing point which resides in. + /// The duration to snap. + /// The divisor to use for snapping purposes. + /// A value that represents snapped to the closest beat of the timing point. + double SnapTime(double referenceTime, double duration, int beatDivisor); + + /// + /// Get the most appropriate beat length at a given time. + /// + /// A reference time used for lookup. + /// The divisor to use for snapping purposes. + /// The most appropriate beat length. + double GetBeatLengthAtTime(double referenceTime, int beatDivisor); + + /// + /// Returns the current beat divisor. + /// + int BeatDivisor { get; } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 804c2f1e65..ec6f0bb923 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; @@ -10,7 +11,6 @@ using osu.Framework.Graphics.Audio; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osuTK; @@ -169,53 +169,25 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } [Resolved] - private BindableBeatDivisor beatDivisor { get; set; } + private EditorBeatmap beatmap { get; set; } [Resolved] - private EditorBeatmap beatmap { get; set; } + private IBeatSnapProvider beatSnapProvider { get; set; } public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) { var targetTime = (position.X / Content.DrawWidth) * track.Length; - return (position, beatSnap(targetTime, targetTime)); + return (position, beatSnapProvider.SnapTime(targetTime, targetTime, beatSnapProvider.BeatDivisor)); } - public float GetBeatSnapDistanceAt(double referenceTime) - { - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(referenceTime); - return (float)(100 * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatDivisor.Value); - } + public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException(); - public float DurationToDistance(double referenceTime, double duration) - { - double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; - return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime)); - } + public float DurationToDistance(double referenceTime, double duration) => throw new NotImplementedException(); - public double DistanceToDuration(double referenceTime, float distance) - { - double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; - return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength; - } + public double DistanceToDuration(double referenceTime, float distance) => throw new NotImplementedException(); - public double GetSnappedDurationFromDistance(double referenceTime, float distance) - => beatSnap(referenceTime, DistanceToDuration(referenceTime, distance)); + public double GetSnappedDurationFromDistance(double referenceTime, float distance) => throw new NotImplementedException(); - public float GetSnappedDistanceFromDistance(double referenceTime, float distance) - => DurationToDistance(referenceTime, beatSnap(referenceTime, DistanceToDuration(referenceTime, distance))); - - /// - /// Snaps a duration to the closest beat of a timing point applicable at the reference time. - /// - /// The time of the timing point which resides in. - /// The duration to snap. - /// A value that represents snapped to the closest beat of the timing point. - private double beatSnap(double referenceTime, double duration) - { - double beatLength = beatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; - - // A 1ms offset prevents rounding errors due to minute variations in duration - return (int)((duration + 1) / beatLength) * beatLength; - } + public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => throw new NotImplementedException(); } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index e212b429b9..26660c427c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -26,6 +26,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; @@ -34,7 +35,8 @@ using osu.Game.Users; namespace osu.Game.Screens.Edit { - public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler + [Cached(typeof(IBeatSnapProvider))] + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IBeatSnapProvider { public override float BackgroundParallaxAmount => 0.1f; @@ -77,11 +79,14 @@ namespace osu.Game.Screens.Edit clock.ChangeSource(sourceClock); playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); - editorBeatmap = new EditorBeatmap(playableBeatmap); + editorBeatmap = new EditorBeatmap(playableBeatmap, beatDivisor); dependencies.CacheAs(clock); dependencies.CacheAs(clock); + + // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); + dependencies.CacheAs(editorBeatmap); EditorMenuBar menuBar; @@ -345,5 +350,11 @@ namespace osu.Game.Screens.Edit saveBeatmap(); beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); } + + public double SnapTime(double referenceTime, double duration, int beatDivisor) => editorBeatmap.SnapTime(referenceTime, duration, beatDivisor); + + public double GetBeatLengthAtTime(double referenceTime, int beatDivisor) => editorBeatmap.GetBeatLengthAtTime(referenceTime, beatDivisor); + + public int BeatDivisor => beatDivisor.Value; } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 2ff7563a88..b7dd94b6c5 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -8,11 +8,12 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit { - public class EditorBeatmap : IBeatmap + public class EditorBeatmap : IBeatmap, IBeatSnapProvider { /// /// Invoked when a is added to this . @@ -33,11 +34,14 @@ namespace osu.Game.Screens.Edit public readonly IBeatmap PlayableBeatmap; + private readonly BindableBeatDivisor beatDivisor; + private readonly Dictionary> startTimeBindables = new Dictionary>(); - public EditorBeatmap(IBeatmap playableBeatmap) + public EditorBeatmap(IBeatmap playableBeatmap, BindableBeatDivisor beatDivisor = null) { PlayableBeatmap = playableBeatmap; + this.beatDivisor = beatDivisor; foreach (var obj in HitObjects) trackStartTime(obj); @@ -123,5 +127,17 @@ namespace osu.Game.Screens.Edit return list.Count - 1; } + + public double SnapTime(double referenceTime, double duration, int beatDivisor) + { + double beatLength = GetBeatLengthAtTime(referenceTime, beatDivisor); + + // A 1ms offset prevents rounding errors due to minute variations in duration + return (int)((duration + 1) / beatLength) * beatLength; + } + + public double GetBeatLengthAtTime(double referenceTime, int beatDivisor) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor; + + public int BeatDivisor => beatDivisor?.Value ?? 1; } } From 9d90799447027b3d74a92363514aed5a6b1911f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 16:20:42 +0900 Subject: [PATCH 43/75] Remove useless container --- .../Edit/Compose/Components/BlueprintContainer.cs | 10 +++------- .../Components/Timeline/TimelineBlueprintContainer.cs | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 4ef8877795..23f62e4eaf 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected DragBox DragBox { get; private set; } - private SelectionBlueprintContainer selectionBlueprints; + private Container selectionBlueprints; private SelectionHandler selectionHandler; @@ -83,7 +83,8 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } - protected virtual SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected virtual Container CreateSelectionBlueprintContainer() => + new Container { RelativeSizeAxes = Axes.Both }; protected override void LoadComplete() { @@ -430,10 +431,5 @@ namespace osu.Game.Screens.Edit.Compose.Components beatmap.HitObjectRemoved -= removeBlueprintFor; } } - - protected class SelectionBlueprintContainer : Container - { - //todo: remove - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index b0d3e2dcde..9fef2998bd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - protected override SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override Container CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; protected override void LoadComplete() { @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - protected class TimelineSelectionBlueprintContainer : SelectionBlueprintContainer + protected class TimelineSelectionBlueprintContainer : Container { protected override Container Content { get; } From 5cadbb1ffb057f71f086b6a6053a65e15807001a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 16:22:43 +0900 Subject: [PATCH 44/75] Move timeline blueprint to own class --- .../Timeline/TimelineBlueprintContainer.cs | 102 --------------- .../Timeline/TimelineHitObjectBlueprint.cs | 116 ++++++++++++++++++ 2 files changed, 116 insertions(+), 102 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 9fef2998bd..c1458480a6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -12,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; using osuTK; using osuTK.Graphics; @@ -139,104 +136,5 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AddInternal(new TimelinePart(Content = new Container { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both }); } } - - private class TimelineHitObjectBlueprint : SelectionBlueprint - { - private readonly Circle circle; - - private readonly Container extensionBar; - - [UsedImplicitly] - private readonly Bindable startTime; - - public const float THICKNESS = 3; - - private const float circle_size = 16; - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos); - - public TimelineHitObjectBlueprint(HitObject hitObject) - : base(hitObject) - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - - startTime = hitObject.StartTimeBindable.GetBoundCopy(); - startTime.BindValueChanged(time => X = (float)time.NewValue, true); - - RelativePositionAxes = Axes.X; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - if (hitObject is IHasEndTime) - { - AddInternal(extensionBar = new Container - { - CornerRadius = 2, - Masking = true, - Size = new Vector2(1, THICKNESS), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.X, - Colour = Color4.Black, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - } - }); - } - - AddInternal(circle = new Circle - { - Size = new Vector2(circle_size), - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.X, - AlwaysPresent = true, - Colour = Color4.White, - BorderColour = Color4.Black, - BorderThickness = THICKNESS, - }); - } - - protected override void Update() - { - base.Update(); - - // no bindable so we perform this every update - Width = (float)(HitObject.GetEndTime() - HitObject.StartTime); - } - - protected override void OnSelected() - { - circle.BorderColour = Color4.Orange; - if (extensionBar != null) - extensionBar.Colour = Color4.Orange; - } - - protected override void OnDeselected() - { - circle.BorderColour = Color4.Black; - if (extensionBar != null) - extensionBar.Colour = Color4.Black; - } - - public override Quad SelectionQuad - { - get - { - // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself. - var circleQuad = circle.ScreenSpaceDrawQuad; - var actualQuad = ScreenSpaceDrawQuad; - - return new Quad(circleQuad.TopLeft, Vector2.ComponentMax(actualQuad.TopRight, circleQuad.TopRight), - circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight)); - } - } - - public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft; - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs new file mode 100644 index 0000000000..2ed5471444 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + public class TimelineHitObjectBlueprint : SelectionBlueprint + { + private readonly Circle circle; + + private readonly Container extensionBar; + + [UsedImplicitly] + private readonly Bindable startTime; + + public const float THICKNESS = 3; + + private const float circle_size = 16; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos); + + public TimelineHitObjectBlueprint(HitObject hitObject) + : base(hitObject) + { + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + startTime = hitObject.StartTimeBindable.GetBoundCopy(); + startTime.BindValueChanged(time => X = (float)time.NewValue, true); + + RelativePositionAxes = Axes.X; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + if (hitObject is IHasEndTime) + { + AddInternal(extensionBar = new Container + { + CornerRadius = 2, + Masking = true, + Size = new Vector2(1, THICKNESS), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.X, + Colour = Color4.Black, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + } + }); + } + + AddInternal(circle = new Circle + { + Size = new Vector2(circle_size), + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + AlwaysPresent = true, + Colour = Color4.White, + BorderColour = Color4.Black, + BorderThickness = THICKNESS, + }); + } + + protected override void Update() + { + base.Update(); + + // no bindable so we perform this every update + Width = (float)(HitObject.GetEndTime() - HitObject.StartTime); + } + + protected override void OnSelected() + { + circle.BorderColour = Color4.Orange; + if (extensionBar != null) + extensionBar.Colour = Color4.Orange; + } + + protected override void OnDeselected() + { + circle.BorderColour = Color4.Black; + if (extensionBar != null) + extensionBar.Colour = Color4.Black; + } + + public override Quad SelectionQuad + { + get + { + // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself. + var circleQuad = circle.ScreenSpaceDrawQuad; + var actualQuad = ScreenSpaceDrawQuad; + + return new Quad(circleQuad.TopLeft, Vector2.ComponentMax(actualQuad.TopRight, circleQuad.TopRight), + circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight)); + } + } + + public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft; + } +} From 5646f7777e77bd6c7a5414debef4bfd9c7c769cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 16:23:42 +0900 Subject: [PATCH 45/75] Add comment about custom SelectionHandler --- .../Components/Timeline/TimelineBlueprintContainer.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index c1458480a6..6bfd323c13 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -82,15 +82,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler(); - internal class TimelineSelectionHandler : SelectionHandler - { - public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - } - protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject); protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect); + internal class TimelineSelectionHandler : SelectionHandler + { + // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation + public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; + } + private class TimelineDragBox : DragBox { private Vector2 lastMouseDown; From f8cb8985161d313490b33fe420cc0b3f931d33bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jan 2020 20:03:52 +0900 Subject: [PATCH 46/75] Improve song select display on ultrawide displays (or when UI scale is set low) --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 117 ++++++++++++++------ 2 files changed, 82 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b0d5be8140..cf49cf0228 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select { private const float shear_width = 36.75f; - private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGED_CONTAINER_SIZE.Y, 0); + private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0); private readonly IBindable ruleset = new Bindable(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bf57def700..bcb828a666 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen, IKeyBindingHandler { - public static readonly Vector2 WEDGED_CONTAINER_SIZE = new Vector2(0.5f, 245); + public static readonly float WEDGE_HEIGHT = 245; protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; @@ -88,13 +88,15 @@ namespace osu.Game.Screens.Select [Resolved(canBeNull: true)] private MusicController music { get; set; } + private const float panel_overflow = 1.2f; + [BackgroundDependencyLoader(true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); - AddRangeInternal(new Drawable[] + AddRangeInternal(new[] { new ParallaxContainer { @@ -107,27 +109,61 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = -150 }, - Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1), + Size = new Vector2(0.5f, 1), } } }, - new Container + new GridContainer // used for max width implementation { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1), - Padding = new MarginPadding + ColumnDimensions = new[] { - Bottom = Footer.HEIGHT, - Top = WEDGED_CONTAINER_SIZE.Y + left_area_padding, - Left = left_area_padding, - Right = left_area_padding * 2, + new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 650), }, - Child = BeatmapDetails = new BeatmapDetailArea + Content = new[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 10, Right = 5 }, + new Drawable[] + { + new Container + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + + Children = new Drawable[] + { + beatmapInfoWedge = new BeatmapInfoWedge + { + Height = WEDGE_HEIGHT, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding + { + Top = left_area_padding, + Right = left_area_padding, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Bottom = Footer.HEIGHT, + Top = WEDGE_HEIGHT + left_area_padding, + Left = left_area_padding, + Right = left_area_padding * 2, + }, + Children = new Drawable[] + { + BeatmapDetails = new BeatmapDetailArea + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 10, Right = 5 }, + }, + } + }, + } + }, + }, } }, new Container @@ -136,13 +172,13 @@ namespace osu.Game.Screens.Select Masking = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Width = 2, //avoid horizontal masking so the panels don't clip when screen stack is pushed. + Width = panel_overflow, //avoid horizontal masking so the panels don't clip when screen stack is pushed. Child = new Container { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Width = 0.5f, + Width = 1 / panel_overflow, Children = new Drawable[] { new Container @@ -153,15 +189,33 @@ namespace osu.Game.Screens.Select Top = FilterControl.HEIGHT, Bottom = Footer.HEIGHT }, - Child = Carousel = new BeatmapCarousel + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(1 - WEDGED_CONTAINER_SIZE.X, 1), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - SelectionChanged = updateSelectedBeatmap, - BeatmapSetsChanged = carouselBeatmapsLoaded, - }, + new GridContainer // used for max width implementation + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 850), + }, + Content = new[] + { + new Drawable[] + { + null, + Carousel = new BeatmapCarousel + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + SelectionChanged = updateSelectedBeatmap, + BeatmapSetsChanged = carouselBeatmapsLoaded, + }, + } + } + }, + } }, FilterControl = new FilterControl { @@ -173,21 +227,12 @@ namespace osu.Game.Screens.Select } }, }, - beatmapInfoWedge = new BeatmapInfoWedge - { - Size = WEDGED_CONTAINER_SIZE, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding - { - Top = left_area_padding, - Right = left_area_padding, - }, - }, new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, Width = 250, - } + }, + beatmapInfoWedge.CreateProxy() }); if (ShowFooter) From 316a764f6f85a91f42b1d11b6bb8b03b47392e73 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Thu, 23 Jan 2020 16:23:53 +0100 Subject: [PATCH 47/75] Minor cleanups for Legacy Storyboard/Beatmap decoder --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 12 ++++++------ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 5 ++++- .../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 14 +++++++------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 447d52d980..a8ec351817 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -239,11 +239,11 @@ namespace osu.Game.Beatmaps.Formats break; case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; + metadata.Source = pair.Value; break; case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + metadata.Tags = pair.Value; break; case @"BeatmapID": @@ -300,13 +300,13 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case LegacyEventType.Background: - string bgFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath(); + string bgFilename = split[2]; + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(bgFilename); ; break; case LegacyEventType.Video: - string videoFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath(); + string videoFilename = split[2]; + beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(videoFilename); break; case LegacyEventType.Break: diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index f55e24245b..0ec80eee41 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; @@ -115,7 +116,7 @@ namespace osu.Game.Beatmaps.Formats protected KeyValuePair SplitKeyVal(string line, char separator = ':') { - var split = line.Trim().Split(new[] { separator }, 2); + var split = line.Split(separator, 2); return new KeyValuePair ( @@ -124,6 +125,8 @@ namespace osu.Game.Beatmaps.Formats ); } + protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath(); + protected enum Section { None, diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index c46634e72f..cbf0f8dfbd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.IO; using osuTK; using osuTK.Graphics; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.IO; using osu.Game.Storyboards; @@ -65,12 +64,14 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(string line) { var depth = 0; + var lineSpan = line.AsSpan(); - while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal)) + while (lineSpan.StartsWith(" ", StringComparison.Ordinal) || lineSpan.StartsWith("_", StringComparison.Ordinal)) { + lineSpan = lineSpan.Slice(1); ++depth; - line = line.Substring(1); } + line = line.Substring(depth); decodeVariables(ref line); @@ -89,7 +90,7 @@ namespace osu.Game.Beatmaps.Formats { var layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); + var path = CleanFilename(split[3]); var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); @@ -101,7 +102,7 @@ namespace osu.Game.Beatmaps.Formats { var layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); + var path = CleanFilename(split[3]); var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); var frameCount = int.Parse(split[6]); @@ -116,7 +117,7 @@ namespace osu.Game.Beatmaps.Formats { var time = double.Parse(split[1], CultureInfo.InvariantCulture); var layer = parseLayer(split[2]); - var path = cleanFilename(split[3]); + var path = CleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume)); break; @@ -332,6 +333,5 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath(); } } From 6658bdb223c15661bf7836d66165dacef2d07eec Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Thu, 23 Jan 2020 16:34:43 +0100 Subject: [PATCH 48/75] Fix CodeFactor issues --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index a8ec351817..965fcc03a3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -301,7 +301,7 @@ namespace osu.Game.Beatmaps.Formats { case LegacyEventType.Background: string bgFilename = split[2]; - beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(bgFilename); ; + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(bgFilename); break; case LegacyEventType.Video: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index cbf0f8dfbd..a7d0360f0d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -332,6 +332,5 @@ namespace osu.Game.Beatmaps.Formats break; } } - } } From 0e9ab8c76b843fcb35bd06d7ffd1c423549ba5f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 13:39:47 +0900 Subject: [PATCH 49/75] Rename test scene to match --- ...mposeTimeline.cs => TestSceneTimelineBlueprintContainer.cs} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename osu.Game.Tests/Visual/Editor/{TestSceneEditorComposeTimeline.cs => TestSceneTimelineBlueprintContainer.cs} (97%) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs similarity index 97% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs rename to osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs index 5b0408268f..34bf671eba 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs @@ -23,12 +23,11 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Editor { [TestFixture] - public class TestSceneEditorComposeTimeline : EditorClockTestScene + public class TestSceneTimelineBlueprintContainer : EditorClockTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(TimelineArea), - typeof(TimelineBlueprintContainer), typeof(Timeline), typeof(TimelineButton), typeof(CentreMarker) From c0a233e8bb7d8a8ce8c2a6b67b6be72c09a8d57b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 15:00:10 +0900 Subject: [PATCH 50/75] Align background wedge with carousel --- osu.Game/Screens/Select/SongSelect.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bcb828a666..30cb66b301 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -105,11 +105,26 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Children = new[] { - new WedgeBackground + new GridContainer // used for max width implementation { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = -150 }, - Size = new Vector2(0.5f, 1), + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 850), + }, + Content = new[] + { + new Drawable[] + { + new WedgeBackground + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = -150 }, + }, + null + } + } } } }, From fdde4a6858301129ac787b4fc4990a421e09cf28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 15:34:15 +0900 Subject: [PATCH 51/75] Tidy up song select hierarchy to be more readable --- osu.Game/Screens/Select/SongSelect.cs | 219 ++++++++++++-------------- 1 file changed, 100 insertions(+), 119 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 30cb66b301..1a29f336ea 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -88,22 +88,22 @@ namespace osu.Game.Screens.Select [Resolved(canBeNull: true)] private MusicController music { get; set; } - private const float panel_overflow = 1.2f; - [BackgroundDependencyLoader(true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); - AddRangeInternal(new[] + AddRangeInternal(new Drawable[] { - new ParallaxContainer + new ResetScrollContainer(() => Carousel.ScrollToSelected()) { - Masking = true, - ParallaxAmount = 0.005f, - RelativeSizeAxes = Axes.Both, - Children = new[] + RelativeSizeAxes = Axes.Y, + Width = 250, + }, + new VerticalMaskingContainer + { + Children = new Drawable[] { new GridContainer // used for max width implementation { @@ -117,44 +117,14 @@ namespace osu.Game.Screens.Select { new Drawable[] { - new WedgeBackground + new ParallaxContainer { + ParallaxAmount = 0.005f, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = -150 }, - }, - null - } - } - } - } - }, - new GridContainer // used for max width implementation - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 650), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - - Children = new Drawable[] - { - beatmapInfoWedge = new BeatmapInfoWedge - { - Height = WEDGE_HEIGHT, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding + Child = new WedgeBackground { - Top = left_area_padding, - Right = left_area_padding, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = -150 }, }, }, new Container @@ -162,92 +132,80 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Bottom = Footer.HEIGHT, - Top = WEDGE_HEIGHT + left_area_padding, - Left = left_area_padding, - Right = left_area_padding * 2, + Top = FilterControl.HEIGHT, + Bottom = Footer.HEIGHT }, + Child = Carousel = new BeatmapCarousel + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + SelectionChanged = updateSelectedBeatmap, + BeatmapSetsChanged = carouselBeatmapsLoaded, + }, + } + }, + } + }, + FilterControl = new FilterControl + { + RelativeSizeAxes = Axes.X, + Height = FilterControl.HEIGHT, + FilterChanged = ApplyFilterToCarousel, + Background = { Width = 2 }, + }, + new GridContainer // used for max width implementation + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 650), + }, + Content = new[] + { + new Drawable[] + { + new Container + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - BeatmapDetails = new BeatmapDetailArea + beatmapInfoWedge = new BeatmapInfoWedge + { + Height = WEDGE_HEIGHT, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding + { + Top = left_area_padding, + Right = left_area_padding, + }, + }, + new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 10, Right = 5 }, + Padding = new MarginPadding + { + Bottom = Footer.HEIGHT, + Top = WEDGE_HEIGHT + left_area_padding, + Left = left_area_padding, + Right = left_area_padding * 2, + }, + Child = BeatmapDetails = new BeatmapDetailArea + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 10, Right = 5 }, + }, }, } }, - } - }, - }, + }, + } + } } }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = panel_overflow, //avoid horizontal masking so the panels don't clip when screen stack is pushed. - Child = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 1 / panel_overflow, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Top = FilterControl.HEIGHT, - Bottom = Footer.HEIGHT - }, - Children = new Drawable[] - { - new GridContainer // used for max width implementation - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 850), - }, - Content = new[] - { - new Drawable[] - { - null, - Carousel = new BeatmapCarousel - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - SelectionChanged = updateSelectedBeatmap, - BeatmapSetsChanged = carouselBeatmapsLoaded, - }, - } - } - }, - } - }, - FilterControl = new FilterControl - { - RelativeSizeAxes = Axes.X, - Height = FilterControl.HEIGHT, - FilterChanged = ApplyFilterToCarousel, - Background = { Width = 2 }, - }, - } - }, - }, - new ResetScrollContainer(() => Carousel.ScrollToSelected()) - { - RelativeSizeAxes = Axes.Y, - Width = 250, - }, - beatmapInfoWedge.CreateProxy() }); if (ShowFooter) @@ -765,6 +723,29 @@ namespace osu.Game.Screens.Select return base.OnKeyDown(e); } + private class VerticalMaskingContainer : Container + { + private const float panel_overflow = 1.2f; + + protected override Container Content { get; } + + public VerticalMaskingContainer() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Width = panel_overflow; //avoid horizontal masking so the panels don't clip when screen stack is pushed. + InternalChild = Content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 1 / panel_overflow, + }; + } + } + private class ResetScrollContainer : Container { private readonly Action onHoverAction; From f9a54dfb1dc59a9e85cec3ccb1c63e46361ea911 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 16:23:41 +0900 Subject: [PATCH 52/75] Tidy up implementation and show on returning with new song --- osu.Game/Screens/Menu/MainMenu.cs | 50 +++++++++++++---------------- osu.Game/Screens/Menu/SongTicker.cs | 7 ++-- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 2de31cf399..27b87e4fa5 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -78,27 +78,6 @@ namespace osu.Game.Screens.Menu holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); - AddInternal(songTicker = new SongTicker - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Margin = new MarginPadding { Right = 15, Top = 5 } - }); - - if (host.CanExit) - { - AddInternal(exitConfirmOverlay = new ExitConfirmOverlay - { - Action = () => - { - if (holdDelay.Value > 0) - confirmAndExit(); - else - this.Exit(); - } - }); - } - AddRangeInternal(new Drawable[] { buttonsContainer = new ParallaxContainer @@ -117,8 +96,28 @@ namespace osu.Game.Screens.Menu } }, sideFlashes = new MenuSideFlashes(), + songTicker = new SongTicker + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding { Right = 15, Top = 5 } + } }); + if (host.CanExit) + { + AddInternal(exitConfirmOverlay = new ExitConfirmOverlay + { + Action = () => + { + if (holdDelay.Value > 0) + confirmAndExit(); + else + this.Exit(); + } + }); + } + buttons.StateChanged += state => { switch (state) @@ -236,9 +235,6 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; - songTicker.FadeOut(100, Easing.OutQuint); - songTicker.AllowUpdates = false; - this.FadeOut(FADE_OUT_DURATION, Easing.InSine); buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); @@ -249,8 +245,6 @@ namespace osu.Game.Screens.Menu { base.OnResuming(last); - songTicker.AllowUpdates = true; - (Background as BackgroundScreenDefault)?.Next(); //we may have consumed our preloaded instance, so let's make another. @@ -277,8 +271,10 @@ namespace osu.Game.Screens.Menu } buttons.State = ButtonSystemState.Exit; - this.FadeOut(3000); + songTicker.Hide(); + + this.FadeOut(3000); return base.OnExiting(next); } diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index f858d162d2..c3569b7ccb 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Allocation; @@ -17,8 +17,6 @@ namespace osu.Game.Screens.Menu { private const int fade_duration = 800; - public bool AllowUpdates { get; set; } = true; - [Resolved] private Bindable beatmap { get; set; } @@ -58,8 +56,7 @@ namespace osu.Game.Screens.Menu private void onBeatmapChanged(ValueChangedEvent working) { - if (!AllowUpdates) - return; + var metadata = beatmap.Value.Metadata; var metadata = working.NewValue.Metadata; From 6f44f8a1ad0a64a142043ea97eb806836519df76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 16:27:49 +0900 Subject: [PATCH 53/75] Ensure only run once when not current screen --- osu.Game/Screens/Menu/SongTicker.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index c3569b7ccb..4323d639c4 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Allocation; @@ -22,6 +22,8 @@ namespace osu.Game.Screens.Menu private readonly OsuSpriteText title, artist; + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public SongTicker() { AutoSizeAxes = Axes.Both; @@ -51,19 +53,20 @@ namespace osu.Game.Screens.Menu protected override void LoadComplete() { base.LoadComplete(); - beatmap.BindValueChanged(onBeatmapChanged); + + beatmap.BindValueChanged(_ => Scheduler.AddOnce(show), true); } - private void onBeatmapChanged(ValueChangedEvent working) + private void show() { var metadata = beatmap.Value.Metadata; - var metadata = working.NewValue.Metadata; - title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); - this.FadeIn(fade_duration, Easing.OutQuint).Delay(4000).Then().FadeOut(fade_duration, Easing.OutQuint); + this.FadeInFromZero(fade_duration) + .Delay(4000) + .Then().FadeOut(fade_duration); } } } From 7495cfa0a95dc4e8e54af3b11c9d299422114d7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 16:32:31 +0900 Subject: [PATCH 54/75] Add a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene You have to have a test scene --- .../Visual/Menus/TestSceneSongTicker.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs b/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs new file mode 100644 index 0000000000..d7f23f5cc0 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs @@ -0,0 +1,36 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + public class TestSceneSongTicker : OsuTestScene + { + [Cached] + private MusicController musicController = new MusicController(); + + public TestSceneSongTicker() + { + AddRange(new Drawable[] + { + musicController, + new SongTicker + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new NowPlayingOverlay + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + State = { Value = Visibility.Visible } + } + }); + } + } +} From 827f48c29bc8a512a1807b9e010899a0f7172c8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 16:32:39 +0900 Subject: [PATCH 55/75] Adjust fade --- osu.Game/Screens/Menu/SongTicker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs index 4323d639c4..c4943e77d5 100644 --- a/osu.Game/Screens/Menu/SongTicker.cs +++ b/osu.Game/Screens/Menu/SongTicker.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Menu title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); - this.FadeInFromZero(fade_duration) + this.FadeInFromZero(fade_duration / 2f) .Delay(4000) .Then().FadeOut(fade_duration); } From f9e93e2a6ae041f0a956f82aaa0768e9cb601a12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Jan 2020 16:34:13 +0900 Subject: [PATCH 56/75] Fix regressed input handling order --- osu.Game/Screens/Menu/MainMenu.cs | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 27b87e4fa5..b03febce31 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -78,7 +78,21 @@ namespace osu.Game.Screens.Menu holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); - AddRangeInternal(new Drawable[] + if (host.CanExit) + { + AddInternal(exitConfirmOverlay = new ExitConfirmOverlay + { + Action = () => + { + if (holdDelay.Value > 0) + confirmAndExit(); + else + this.Exit(); + } + }); + } + + AddRangeInternal(new[] { buttonsContainer = new ParallaxContainer { @@ -101,23 +115,10 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Margin = new MarginPadding { Right = 15, Top = 5 } - } + }, + exitConfirmOverlay.CreateProxy() }); - if (host.CanExit) - { - AddInternal(exitConfirmOverlay = new ExitConfirmOverlay - { - Action = () => - { - if (holdDelay.Value > 0) - confirmAndExit(); - else - this.Exit(); - } - }); - } - buttons.StateChanged += state => { switch (state) From 28727bbafd1c7e5e3fb3bd2aabf73d47e900f24e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 17:30:37 +0900 Subject: [PATCH 57/75] Fix crash when deselecting via ctrl+click --- .../Edit/Compose/Components/BlueprintContainer.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 23f62e4eaf..165fc93b9e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -110,7 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { beginClickSelection(e); - prepareSelectionMovement(); return e.Button == MouseButton.Left; @@ -356,21 +355,19 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Attempts to begin the movement of any selected blueprints. /// - /// Whether movement began. - private bool prepareSelectionMovement() + private void prepareSelectionMovement() { - Debug.Assert(movementBlueprint == null); + if (!selectionHandler.SelectedBlueprints.Any()) + return; // Any selected blueprint that is hovered can begin the movement of the group, however only the earliest hitobject is used for movement // A special case is added for when a click selection occurred before the drag if (!clickSelectionBegan && !selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) - return false; + return; // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First(); movementBlueprintOriginalPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct - - return true; } /// From 6fc6a376ee285700940fcfbebac3da349e312d99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 17:50:09 +0900 Subject: [PATCH 58/75] Fix incorrect slider selection point --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 4fdead512a..c18b3b0ff3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)), }; - public override Vector2 SelectionPoint => HeadBlueprint.SelectionPoint; + public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos); From 811ddb02a4bca157bcb17b5fee4adbeb11415fd0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 17:50:36 +0900 Subject: [PATCH 59/75] General refactoring --- .../Blueprints/Sliders/SliderCircleSelectionBlueprint.cs | 1 + .../Visual/Editor/TestSceneTimelineBlueprintContainer.cs | 2 +- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 6 +++--- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++--- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs index f09279ed73..a0392fe536 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders : base(slider) { this.position = position; + InternalChild = CirclePiece = new HitCirclePiece(); Select(); diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs index 34bf671eba..e7b2508ac7 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Editor }, new TimelineArea { - Child = new TimelineBlueprintContainer(editorBeatmap), + Child = new TimelineBlueprintContainer(), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 165fc93b9e..6b21f56567 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -83,9 +83,6 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } - protected virtual Container CreateSelectionBlueprintContainer() => - new Container { RelativeSizeAxes = Axes.Both }; - protected override void LoadComplete() { base.LoadComplete(); @@ -94,6 +91,9 @@ namespace osu.Game.Screens.Edit.Compose.Components beatmap.HitObjectRemoved += removeBlueprintFor; } + protected virtual Container CreateSelectionBlueprintContainer() => + new Container { RelativeSizeAxes = Axes.Both }; + /// /// Creates a which outlines s and handles movement of selections. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 6bfd323c13..3b9cb1df24 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private DragEvent lastDragEvent; - public TimelineBlueprintContainer(EditorBeatmap beatmap) + public TimelineBlueprintContainer() { RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; @@ -39,14 +39,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - protected override Container CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override void LoadComplete() { base.LoadComplete(); DragBox.Alpha = 0; } + protected override Container CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override void OnDrag(DragEvent e) { if (timeline != null) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 20e1ef95d3..cdea200e10 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); } - protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(EditorBeatmap); + protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(); } } From 027778acc1dff50fcfd04ff116f0b94daf732a25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 17:51:24 +0900 Subject: [PATCH 60/75] Fix slider circles not being selected by default --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 39 ++++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 9998254f76..a972d28480 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -41,14 +41,15 @@ namespace osu.Game.Rulesets.Edit protected SelectionBlueprint(HitObject hitObject) { HitObject = hitObject; + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; } - [BackgroundDependencyLoader] - private void load() + protected override void LoadComplete() { - OnDeselected(); - AlwaysPresent = true; + base.LoadComplete(); + updateState(); } private SelectionState state; @@ -65,23 +66,29 @@ namespace osu.Game.Rulesets.Edit state = value; - switch (state) - { - case SelectionState.Selected: - OnSelected(); - Selected?.Invoke(this); - break; - - case SelectionState.NotSelected: - OnDeselected(); - Deselected?.Invoke(this); - break; - } + if (IsLoaded) + updateState(); StateChanged?.Invoke(state); } } + private void updateState() + { + switch (state) + { + case SelectionState.Selected: + OnSelected(); + Selected?.Invoke(this); + break; + + case SelectionState.NotSelected: + OnDeselected(); + Deselected?.Invoke(this); + break; + } + } + protected virtual void OnDeselected() => Hide(); protected virtual void OnSelected() => Show(); From 9e0e7be8d09b2c8d449a7ebb138e8c4e90a8c6bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 18:57:17 +0900 Subject: [PATCH 61/75] Modernise filter implementation --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- osu.Game/Screens/Select/FilterControl.cs | 10 +++++++--- osu.Game/Screens/Select/FilterCriteria.cs | 3 +-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index d50154485a..2ffb73f226 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select.Carousel match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) || criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode); - match &= Beatmap.StarDifficulty >= criteria.DisplayStarsMinimum && Beatmap.StarDifficulty <= criteria.DisplayStarsMaximum; + match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(Beatmap.StarDifficulty); if (match) { diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 88a48a54fc..c851b201d7 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -42,11 +42,15 @@ namespace osu.Game.Screens.Select Group = groupMode.Value, Sort = sortMode.Value, AllowConvertedBeatmaps = showConverted.Value, - Ruleset = ruleset.Value - DisplayStarsMinimum = minimumStars, - DisplayStarsMaximum = maximumStars, + Ruleset = ruleset.Value, }; + if (!minimumStars.IsDefault) + criteria.UserStarDifficulty.Min = minimumStars.Value; + + if (!maximumStars.IsDefault) + criteria.UserStarDifficulty.Max = maximumStars.Value; + FilterQueryParser.ApplyQueries(criteria, query); return criteria; } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index fb41c1d09a..27d9adc386 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -25,13 +25,12 @@ namespace osu.Game.Screens.Select public OptionalRange OnlineStatus; public OptionalTextFilter Creator; public OptionalTextFilter Artist; + public OptionalRange UserStarDifficulty; public string[] SearchTerms = Array.Empty(); public RulesetInfo Ruleset; public bool AllowConvertedBeatmaps; - public double DisplayStarsMinimum; - public double DisplayStarsMaximum; private string searchText; From 62fa619ad43fc9161f9a733ff2a9d6d057817b75 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:08:14 +0900 Subject: [PATCH 62/75] Display "no limit" for maximum stars --- .../Settings/Sections/Gameplay/SongSelectSettings.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index a5f56ae76e..78f12c0695 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay KeyboardStep = 0.1f, Keywords = new[] { "star", "difficulty" } }, - new SettingsSlider + new SettingsSlider { LabelText = "up to", Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), @@ -49,6 +49,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }; } + private class MaximumStarsSlider : StarSlider + { + public override string TooltipText => Current.IsDefault ? "no limit" : base.TooltipText; + } + private class StarSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.## stars"); From 2643b6fca3b1d3695bcb53731c81b9cc4dfb2e73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:12:48 +0900 Subject: [PATCH 63/75] Add additional keywods --- .../Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index 78f12c0695..b01aa1e297 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -32,14 +32,14 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Display beatmaps from", Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), KeyboardStep = 0.1f, - Keywords = new[] { "star", "difficulty" } + Keywords = new[] { "minimum", "star", "difficulty" } }, new SettingsSlider { LabelText = "up to", Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), KeyboardStep = 0.1f, - Keywords = new[] { "star", "difficulty" } + Keywords = new[] { "maximum", "star", "difficulty" } }, new SettingsEnumDropdown { From ed66ee3ba6904beb011a885e3266dd47f6d81aae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:12:56 +0900 Subject: [PATCH 64/75] Allow 10.0 stars to be selectable --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 7e4ab58e36..4155381790 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -24,7 +24,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowConvertedBeatmaps, true); Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); - Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); + Set(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1); Set(OsuSetting.SongSelectGroupingMode, GroupMode.All); Set(OsuSetting.SongSelectSortingMode, SortMode.Title); From 45a25214ab401b73c5f55e8aa6aefa614fe8c8db Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:39:21 +0900 Subject: [PATCH 65/75] Make upper and lower bounds inclusive --- osu.Game/Screens/Select/FilterCriteria.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 27d9adc386..9fa57af01d 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -25,7 +25,12 @@ namespace osu.Game.Screens.Select public OptionalRange OnlineStatus; public OptionalTextFilter Creator; public OptionalTextFilter Artist; - public OptionalRange UserStarDifficulty; + + public OptionalRange UserStarDifficulty = new OptionalRange + { + IsLowerInclusive = true, + IsUpperInclusive = true + }; public string[] SearchTerms = Array.Empty(); From bb390b4470df1e5c6536da9740edc6698bfd2354 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:40:20 +0900 Subject: [PATCH 66/75] Add test --- .../SongSelect/TestSceneBeatmapCarousel.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 132b104afb..71ae47dc66 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -437,6 +437,53 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1); } + [Test] + public void TestFilteringByUserStarDifficulty() + { + BeatmapSetInfo set = null; + + loadBeatmaps(new List()); + + AddStep("add mixed difficulty set", () => + { + set = createTestBeatmapSet(1); + set.Beatmaps.Clear(); + + for (int i = 1; i <= 15; i++) + { + set.Beatmaps.Add(new BeatmapInfo + { + Version = $"Stars: {i}", + StarDifficulty = i, + }); + } + + carousel.UpdateBeatmapSet(set); + }); + + AddStep("select added set", () => carousel.SelectBeatmap(set.Beatmaps[0], false)); + + AddStep("filter [5..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5 } })); + AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask); + checkVisibleItemCount(true, 11); + + AddStep("filter to [0..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Max = 7 } })); + AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask); + checkVisibleItemCount(true, 7); + + AddStep("filter to [5..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5, Max = 7 } })); + AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask); + checkVisibleItemCount(true, 3); + + AddStep("filter [2..2]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 2, Max = 2 } })); + AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask); + checkVisibleItemCount(true, 1); + + AddStep("filter to [0..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 0 } })); + AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask); + checkVisibleItemCount(true, 15); + } + private void loadBeatmaps(List beatmapSets = null) { createCarousel(); From 75d0fd0bab1ca0a00308bca0f459cb242024ed37 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:43:23 +0900 Subject: [PATCH 67/75] Rename class --- .../Settings/Sections/Gameplay/SongSelectSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index b01aa1e297..de67d55184 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Show converted beatmaps", Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), }, - new SettingsSlider + new SettingsSlider { LabelText = "Display beatmaps from", Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), @@ -49,12 +49,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }; } - private class MaximumStarsSlider : StarSlider + private class MaximumStarsSlider : StarsSlider { public override string TooltipText => Current.IsDefault ? "no limit" : base.TooltipText; } - private class StarSlider : OsuSliderBar + private class StarsSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.## stars"); } From d1684a3c92513e7e0af59d3452a98afffaff79aa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Jan 2020 19:50:16 +0900 Subject: [PATCH 68/75] Duplicate keywords for better UX --- .../Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index de67d55184..e03c247b6c 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -32,14 +32,14 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Display beatmaps from", Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), KeyboardStep = 0.1f, - Keywords = new[] { "minimum", "star", "difficulty" } + Keywords = new[] { "minimum", "maximum", "star", "difficulty" } }, new SettingsSlider { LabelText = "up to", Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), KeyboardStep = 0.1f, - Keywords = new[] { "maximum", "star", "difficulty" } + Keywords = new[] { "minimum", "maximum", "star", "difficulty" } }, new SettingsEnumDropdown { From bc75290655b222d073fc58ea99cd2525dfb72656 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Jan 2020 00:11:20 +0900 Subject: [PATCH 69/75] Ensure min and max stars are correctly ordered --- .../Settings/Sections/Gameplay/SongSelectSettings.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index e03c247b6c..0c42247993 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; @@ -10,11 +12,20 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class SongSelectSettings : SettingsSubsection { + private Bindable minStars; + private Bindable maxStars; + protected override string Header => "Song Select"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) { + minStars = config.GetBindable(OsuSetting.DisplayStarsMinimum); + maxStars = config.GetBindable(OsuSetting.DisplayStarsMaximum); + + minStars.ValueChanged += min => maxStars.Value = Math.Max(min.NewValue, maxStars.Value); + maxStars.ValueChanged += max => minStars.Value = Math.Min(max.NewValue, minStars.Value); + Children = new Drawable[] { new SettingsCheckbox From 219e14baa2e0f7a9a63be5a78f418c5e826d6c71 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Fri, 24 Jan 2020 17:05:27 +0100 Subject: [PATCH 70/75] Address review and fix InspectCode --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 ++---- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 965fcc03a3..009da0656b 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -300,13 +300,11 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case LegacyEventType.Background: - string bgFilename = split[2]; - beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(bgFilename); + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); break; case LegacyEventType.Video: - string videoFilename = split[2]; - beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(videoFilename); + beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]); break; case LegacyEventType.Break: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a7d0360f0d..9ba4d7b05e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -71,6 +71,7 @@ namespace osu.Game.Beatmaps.Formats lineSpan = lineSpan.Slice(1); ++depth; } + line = line.Substring(depth); decodeVariables(ref line); From fa595e07a65907e66cb2e37b31c7e76ab5cb62f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Jan 2020 12:44:48 +0900 Subject: [PATCH 71/75] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2ccba60424..5497a82a7a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9b431e6425..0ea558bbc7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4dc7403553..a215bc65e8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 9a960a60e37fe69cb6140ca9fdb78f949b077b8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Jan 2020 17:08:24 +0900 Subject: [PATCH 72/75] Remove build target from Fastfile --- fastlane/Fastfile | 1 - 1 file changed, 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 28a83fbbae..510b53054b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -111,7 +111,6 @@ platform :ios do souyuz( platform: "ios", - build_target: "osu_iOS", plist_path: "../osu.iOS/Info.plist" ) end From 8aec9e450089d874caf812153c7dd7025621018a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 25 Jan 2020 11:41:26 +0300 Subject: [PATCH 73/75] Fix NullReferenceException on main menu for mobile game hsots --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index b03febce31..cb5ceefb0f 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Menu Origin = Anchor.TopRight, Margin = new MarginPadding { Right = 15, Top = 5 } }, - exitConfirmOverlay.CreateProxy() + exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty() }); buttons.StateChanged += state => From d2a032ca8d84453fc35cf54123dfda7dd8070c39 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sat, 25 Jan 2020 22:16:21 +0100 Subject: [PATCH 74/75] Move reverse-order comparer to ChannelTabControl --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 14 -------------- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 6a7998d5fb..1bfbee4a60 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; @@ -84,14 +83,6 @@ namespace osu.Game.Graphics.UserInterface set => strip.Colour = value; } - protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer - { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.Both, - Depth = -1, - Masking = true - }; - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -283,10 +274,5 @@ namespace osu.Game.Graphics.UserInterface } } } - - private class OsuTabFillFlowContainer : TabFillFlowContainer - { - protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); - } } } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 2e4d8ce729..104495ae01 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -9,6 +9,7 @@ using osuTK; using System; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Chat.Tabs { @@ -113,5 +114,18 @@ namespace osu.Game.Overlays.Chat.Tabs OnRequestLeave?.Invoke(tab.Value); } + + protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.Both, + Depth = -1, + Masking = true + }; + + private class ChannelTabFillFlowContainer : TabFillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); + } } } From eabb5a870175e6f59fb86b9039c7a77d6cd832ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Jan 2020 17:40:38 +0900 Subject: [PATCH 75/75] Use ToString instead of Substring --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 9ba4d7b05e..35576e0f33 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps.Formats ++depth; } - line = line.Substring(depth); + line = lineSpan.ToString(); decodeVariables(ref line);