From dd8774a64006792556b357d78756438324a34b37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 13:21:28 +0900 Subject: [PATCH 01/74] Vertically centre the editor osu! playfield --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index aac5f6ffb1..8d93613156 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -65,8 +65,8 @@ namespace osu.Game.Rulesets.Osu.Edit PlayfieldContentContainer.Padding = new MarginPadding { Vertical = 10, - Left = TOOLBOX_CONTRACTED_SIZE_LEFT + 10, - Right = TOOLBOX_CONTRACTED_SIZE_RIGHT + 10, + // Intentionally use the left toolbox size for both sides to vertically centre the playfield. + Horizontal = TOOLBOX_CONTRACTED_SIZE_LEFT + 10, }; LayerBelowRuleset.AddRange(new Drawable[] From 56acc9e3dde3f161c79f7fafc312d9800e0fc420 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:02:48 +0900 Subject: [PATCH 02/74] Change `BeatDivisorControl` to retrive bindable divisor via DI --- .../Visual/Editing/TestSceneBeatDivisorControl.cs | 9 +++++++-- .../Visual/Editing/TestSceneTimelineTickDisplay.cs | 2 +- .../Edit/Compose/Components/BeatDivisorControl.cs | 8 ++------ osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 10 ++-------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 88b959a2a0..f2b3351533 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -22,7 +22,6 @@ namespace osu.Game.Tests.Visual.Editing public partial class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene { private BeatDivisorControl beatDivisorControl = null!; - private BindableBeatDivisor bindableBeatDivisor = null!; private SliderBar tickSliderBar => beatDivisorControl.ChildrenOfType>().Single(); private Triangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); @@ -30,13 +29,19 @@ namespace osu.Game.Tests.Visual.Editing [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + [Cached] + private readonly BindableBeatDivisor bindableBeatDivisor = new BindableBeatDivisor(16); + [SetUp] public void SetUp() => Schedule(() => { + bindableBeatDivisor.ValidDivisors.SetDefault(); + bindableBeatDivisor.SetDefault(); + Child = new PopoverContainer { RelativeSizeAxes = Axes.Both, - Child = beatDivisorControl = new BeatDivisorControl(bindableBeatDivisor = new BindableBeatDivisor(16)) + Child = beatDivisorControl = new BeatDivisorControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs index 1376ba23fb..18bd6d840a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing { BeatDivisor.Value = 4; - Add(new BeatDivisorControl(BeatDivisor) + Add(new BeatDivisorControl { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index f7159f8670..59b0bd1785 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -33,12 +33,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { private int? lastCustomDivisor; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - - public BeatDivisorControl(BindableBeatDivisor beatDivisor) - { - this.beatDivisor.BindTo(beatDivisor); - } + [Resolved] + private BindableBeatDivisor beatDivisor { get; set; } = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b8cbff047e..0c5f28c2d6 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -3,7 +3,6 @@ #nullable disable -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,8 +18,6 @@ namespace osu.Game.Screens.Edit { private const float padding = 10; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - private Container timelineContainer; protected EditorScreenWithTimeline(EditorScreenMode type) @@ -33,11 +30,8 @@ namespace osu.Game.Screens.Edit private LoadingSpinner spinner; [BackgroundDependencyLoader(true)] - private void load(OverlayColourProvider colourProvider, [CanBeNull] BindableBeatDivisor beatDivisor) + private void load(OverlayColourProvider colourProvider) { - if (beatDivisor != null) - this.beatDivisor.BindTo(beatDivisor); - Child = new GridContainer { RelativeSizeAxes = Axes.Both, @@ -82,7 +76,7 @@ namespace osu.Game.Screens.Edit AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Right = 5 }, }, - new BeatDivisorControl(this.beatDivisor) { RelativeSizeAxes = Axes.Both } + new BeatDivisorControl { RelativeSizeAxes = Axes.Both } }, }, RowDimensions = new[] From ebaf63b7644425dd06f9e1ba00e8159070813ccf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:04:12 +0900 Subject: [PATCH 03/74] Apply NRT to timeline related classes --- .../Edit/Compose/Components/Timeline/TimelineArea.cs | 8 +++----- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 12 +++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 0b83258f8b..02c773fd4b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,16 +16,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public partial class TimelineArea : CompositeDrawable { - public Timeline Timeline; + public Timeline Timeline = null!; private readonly Drawable userContent; - public TimelineArea(Drawable content = null) + public TimelineArea(Drawable? content = null) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - userContent = content ?? Drawable.Empty(); + userContent = content ?? Empty(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 0c5f28c2d6..bd57ea42f0 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,17 +16,17 @@ namespace osu.Game.Screens.Edit { private const float padding = 10; - private Container timelineContainer; + private Container timelineContainer = null!; + + private Container mainContent = null!; + + private LoadingSpinner spinner = null!; protected EditorScreenWithTimeline(EditorScreenMode type) : base(type) { } - private Container mainContent; - - private LoadingSpinner spinner; - [BackgroundDependencyLoader(true)] private void load(OverlayColourProvider colourProvider) { From 5b2e70426488a295df83f83345326d7181b83568 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:19:04 +0900 Subject: [PATCH 04/74] Move beat divisor control inside of `TimelineArea` and adjust metrics to match design --- .../Components/Timeline/TimelineArea.cs | 44 +++++++++---------- .../Screens/Edit/EditorScreenWithTimeline.cs | 5 ++- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 02c773fd4b..3969a002db 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -48,14 +48,24 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 140), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.Absolute, 110), + }, Content = new[] { new Drawable[] { new Container { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, Name = @"Toggle controls", Children = new Drawable[] { @@ -92,31 +102,31 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, + Timeline = new Timeline(userContent), new Container { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, Name = @"Zoom controls", + Padding = new MarginPadding { Right = 5 }, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, + Colour = colourProvider.Background2, }, new Container { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, Masking = true, Children = new[] { new TimelineButton { - RelativeSizeAxes = Axes.Y, - Height = 0.5f, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1, 0.5f), Icon = FontAwesome.Solid.SearchPlus, Action = () => Timeline.AdjustZoomRelatively(1) }, @@ -124,8 +134,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Y, - Height = 0.5f, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1, 0.5f), Icon = FontAwesome.Solid.SearchMinus, Action = () => Timeline.AdjustZoomRelatively(-1) }, @@ -133,19 +143,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, - Timeline = new Timeline(userContent), + new BeatDivisorControl { RelativeSizeAxes = Axes.Both } }, }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - }, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - } } }; diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index bd57ea42f0..a2367dd66c 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; -using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components.Timeline; namespace osu.Game.Screens.Edit @@ -30,6 +29,9 @@ namespace osu.Game.Screens.Edit [BackgroundDependencyLoader(true)] private void load(OverlayColourProvider colourProvider) { + // Grid with only two rows. + // First is the timeline area, which should be allowed to expand as required. + // Second is the main editor content, including the playfield and side toolbars (but not the bottom). Child = new GridContainer { RelativeSizeAxes = Axes.Both, @@ -74,7 +76,6 @@ namespace osu.Game.Screens.Edit AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Right = 5 }, }, - new BeatDivisorControl { RelativeSizeAxes = Axes.Both } }, }, RowDimensions = new[] From 1dc293ed619adc285ac5554b4ea6a65e04049f77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:24:53 +0900 Subject: [PATCH 05/74] Allow specifying a custom width for nubs in `OsuCheckbox`es --- osu.Game/Graphics/UserInterface/Nub.cs | 6 +++--- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 8 ++++---- osu.Game/Graphics/UserInterface/RoundedSliderBar.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 28a2eb40c3..4b953718bc 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -20,16 +20,16 @@ namespace osu.Game.Graphics.UserInterface { public const float HEIGHT = 15; - public const float EXPANDED_SIZE = 50; + public const float DEFAULT_EXPANDED_SIZE = 50; private const float border_width = 3; private readonly Box fill; private readonly Container main; - public Nub() + public Nub(float expandedSize = DEFAULT_EXPANDED_SIZE) { - Size = new Vector2(EXPANDED_SIZE, HEIGHT); + Size = new Vector2(expandedSize, HEIGHT); InternalChildren = new[] { diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 160105af1a..b7b405a7e8 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -47,7 +47,7 @@ namespace osu.Game.Graphics.UserInterface private Sample sampleChecked; private Sample sampleUnchecked; - public OsuCheckbox(bool nubOnRight = true) + public OsuCheckbox(bool nubOnRight = true, float nubSize = Nub.DEFAULT_EXPANDED_SIZE) { AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; @@ -61,7 +61,7 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, }, - Nub = new Nub(), + Nub = new Nub(nubSize), new HoverSounds() }; @@ -70,14 +70,14 @@ namespace osu.Game.Graphics.UserInterface Nub.Anchor = Anchor.CentreRight; Nub.Origin = Anchor.CentreRight; Nub.Margin = new MarginPadding { Right = nub_padding }; - LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.DEFAULT_EXPANDED_SIZE + nub_padding * 2 }; } else { Nub.Anchor = Anchor.CentreLeft; Nub.Origin = Anchor.CentreLeft; Nub.Margin = new MarginPadding { Left = nub_padding }; - LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.DEFAULT_EXPANDED_SIZE + nub_padding * 2 }; } Nub.Current.BindTo(Current); diff --git a/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs b/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs index a666b83c05..56af23ff10 100644 --- a/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs @@ -51,7 +51,7 @@ namespace osu.Game.Graphics.UserInterface public RoundedSliderBar() { Height = Nub.HEIGHT; - RangePadding = Nub.EXPANDED_SIZE / 2; + RangePadding = Nub.DEFAULT_EXPANDED_SIZE / 2; Children = new Drawable[] { new Container From 01750dd0914e13b6ce4ee1dda8c878639b7d9972 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:24:40 +0900 Subject: [PATCH 06/74] Update metrics of checkboxes and backgrounds to match design better --- .../Components/Timeline/TimelineArea.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 3969a002db..70322bd3e0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -39,11 +39,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline InternalChildren = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5 - }, new GridContainer { RelativeSizeAxes = Axes.X, @@ -76,24 +71,23 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new FillFlowContainer { - AutoSizeAxes = Axes.Y, - Width = 160, + RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(10), Direction = FillDirection.Vertical, Spacing = new Vector2(0, 4), Children = new[] { - waveformCheckbox = new OsuCheckbox + waveformCheckbox = new OsuCheckbox(nubSize: 30f) { LabelText = EditorStrings.TimelineWaveform, Current = { Value = true }, }, - ticksCheckbox = new OsuCheckbox + ticksCheckbox = new OsuCheckbox(nubSize: 30f) { LabelText = EditorStrings.TimelineTicks, Current = { Value = true }, }, - controlPointsCheckbox = new OsuCheckbox + controlPointsCheckbox = new OsuCheckbox(nubSize: 30f) { LabelText = BeatmapsetsStrings.ShowStatsBpm, Current = { Value = true }, @@ -102,7 +96,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, - Timeline = new Timeline(userContent), + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, + Colour = colourProvider.Background5 + }, + Timeline = new Timeline(userContent), + } + }, new Container { RelativeSizeAxes = Axes.Both, From 6b222cfafdedf6bb7299e189f30b533aa83b659f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 14:29:13 +0900 Subject: [PATCH 07/74] Fix slight misalignment so timeline is now completely centered --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index a2367dd66c..104d9dd58f 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit { public abstract partial class EditorScreenWithTimeline : EditorScreen { - private const float padding = 10; + private const float padding = 15; private Container timelineContainer = null!; @@ -74,7 +74,6 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Right = 5 }, }, }, }, From 00e9746174b842bcec7cb6ea2c08bd2e55932580 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 15:17:24 +0900 Subject: [PATCH 08/74] Implement longer design for timing point piece --- .../Timeline/TimelineControlPointGroup.cs | 2 +- .../Components/Timeline/TopPointPiece.cs | 64 ++++++++++++++----- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs index fc3ef92bf5..c1b6069523 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; - Origin = Anchor.TopCentre; + Origin = Anchor.TopLeft; X = (float)group.Time; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs index 69fb001a66..243cdc6ddd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,24 +8,25 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public partial class TopPointPiece : CompositeDrawable { - private readonly ControlPoint point; + protected readonly ControlPoint Point; - protected OsuSpriteText Label { get; private set; } + protected OsuSpriteText Label { get; private set; } = null!; + + private const float width = 80; public TopPointPiece(ControlPoint point) { - this.point = point; - AutoSizeAxes = Axes.X; + Point = point; + Width = width; Height = 16; - Margin = new MarginPadding(4); - - Masking = true; - CornerRadius = Height / 2; + Margin = new MarginPadding { Vertical = 4 }; Origin = Anchor.TopCentre; Anchor = Anchor.TopCentre; @@ -36,17 +35,52 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { + const float corner_radius = 4; + const float arrow_extension = 3; + const float triangle_portion = 15; + InternalChildren = new Drawable[] { - new Box + // This is a triangle, trust me. + // Doing it this way looks okay. Doing it using Triangle primitive is basically impossible. + new Container { - Colour = point.GetRepresentingColour(colours), - RelativeSizeAxes = Axes.Both, + Colour = Point.GetRepresentingColour(colours), + X = -corner_radius, + Size = new Vector2(triangle_portion * arrow_extension, Height), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Masking = true, + CornerRadius = Height, + CornerExponent = 1.4f, + Children = new Drawable[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + } + }, + new Container + { + RelativeSizeAxes = Axes.Y, + Width = width - triangle_portion, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Point.GetRepresentingColour(colours), + Masking = true, + CornerRadius = corner_radius, + Child = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, }, Label = new OsuSpriteText { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Padding = new MarginPadding(3), Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold), Colour = colours.B5, From 57abb15724e537bee9a97ffdbd810b632b675671 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 15:58:53 +0900 Subject: [PATCH 09/74] Update design of timeline centre marker and adjust surrounding paddings --- .../Components/Timeline/CentreMarker.cs | 20 +++++++------------ .../Components/Timeline/TimelineArea.cs | 20 +++++++++++++------ .../Screens/Edit/EditorScreenWithTimeline.cs | 4 ++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs index aee3cffbfd..74786cc0c9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs @@ -12,17 +12,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public partial class CentreMarker : CompositeDrawable { - private const float triangle_width = 15; - private const float triangle_height = 10; - private const float bar_width = 2; + private const float triangle_width = 8; + + private const float bar_width = 1.6f; public CentreMarker() { RelativeSizeAxes = Axes.Y; Size = new Vector2(triangle_width, 1); - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; InternalChildren = new Drawable[] { @@ -37,22 +37,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, - Size = new Vector2(triangle_width, triangle_height), + Size = new Vector2(triangle_width, triangle_width * 0.8f), Scale = new Vector2(1, -1) }, - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangle_width, triangle_height), - } }; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Colour = colours.RedDark; + Colour = colours.Red1; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 70322bd3e0..fb7ce8e423 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays; @@ -29,10 +30,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, OsuColour colours) { - Masking = true; - OsuCheckbox waveformCheckbox; OsuCheckbox controlPointsCheckbox; OsuCheckbox ticksCheckbox; @@ -51,7 +50,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { new Dimension(GridSizeMode.Absolute, 140), new Dimension(), - new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.Absolute, 35), new Dimension(GridSizeMode.Absolute, 110), }, Content = new[] @@ -102,6 +101,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AutoSizeAxes = Axes.Y, Children = new Drawable[] { + // the out-of-bounds portion of the centre marker. + new Box + { + Width = 24, + Height = EditorScreenWithTimeline.PADDING, + Depth = float.MaxValue, + Colour = colours.Red1, + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + }, new Box { RelativeSizeAxes = Axes.Both, @@ -115,7 +124,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { RelativeSizeAxes = Axes.Both, Name = @"Zoom controls", - Padding = new MarginPadding { Right = 5 }, + Padding = new MarginPadding { Right = 10 }, Children = new Drawable[] { new Box @@ -128,7 +137,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, - Masking = true, Children = new[] { new TimelineButton diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 104d9dd58f..ea2790b50a 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit { public abstract partial class EditorScreenWithTimeline : EditorScreen { - private const float padding = 15; + public const float PADDING = 10; private Container timelineContainer = null!; @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Edit Name = "Timeline content", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = padding, Top = padding }, + Padding = new MarginPadding { Horizontal = PADDING, Top = PADDING }, Child = new GridContainer { RelativeSizeAxes = Axes.X, From e6b8cd0c06abce3e02806c570b4316a484c2d817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 16:55:12 +0900 Subject: [PATCH 10/74] Add editor header --- .../Edit/Components/Menus/EditorMenuBar.cs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index a911b4e1d8..fa4e52d6a6 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -3,6 +3,9 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -14,19 +17,56 @@ namespace osu.Game.Screens.Edit.Components.Menus { public partial class EditorMenuBar : OsuMenu { + private const float heading_area = 114; + public EditorMenuBar() : base(Direction.Horizontal, true) { RelativeSizeAxes = Axes.X; MaskingContainer.CornerRadius = 0; - ItemsContainer.Padding = new MarginPadding { Left = 100 }; + ItemsContainer.Padding = new MarginPadding { Left = heading_area }; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, TextureStore textures) { BackgroundColour = colourProvider.Background3; + + TextFlowContainer text; + + AddRangeInternal(new[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + Width = heading_area, + Padding = new MarginPadding(8), + Children = new Drawable[] + { + new Sprite + { + Size = new Vector2(26), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Texture = textures.Get("Icons/Hexacons/editor"), + }, + text = new TextFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + } + } + }, + }); + + text.AddText("osu!", t => t.Font = OsuFont.TorusAlternate); + text.AddText("editor", t => + { + t.Font = OsuFont.TorusAlternate; + t.Colour = colourProvider.Highlight1; + }); } protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); From fe70f2492538533148fd3ee28a2749901736bf43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jul 2023 17:00:42 +0900 Subject: [PATCH 11/74] Update design of summary timeline current time marker --- .../Timelines/Summary/Parts/MarkerPart.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index d42c02e03d..ff707407dd 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -73,6 +73,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { public MarkerVisualisation() { + const float box_height = 4; + Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; RelativePositionAxes = Axes.X; @@ -80,32 +82,46 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts AutoSizeAxes = Axes.X; InternalChildren = new Drawable[] { + new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(14, box_height), + }, new Triangle { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, Scale = new Vector2(1, -1), Size = new Vector2(10, 5), + Y = box_height, }, new Triangle { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Size = new Vector2(10, 5) + Size = new Vector2(10, 5), + Y = -box_height, + }, + new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(14, box_height), }, new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Width = 2, + Width = 1.4f, EdgeSmoothness = new Vector2(1, 0) } }; } [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Red; + private void load(OsuColour colours) => Colour = colours.Red1; } } } From ce12afde70e437fe1e03ebee24d70258a1dd3063 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 15 Jul 2023 23:38:06 -0700 Subject: [PATCH 12/74] Remove android manifest overlay --- osu.Android.props | 4 ---- osu.Android/Properties/AndroidManifestOverlay.xml | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 osu.Android/Properties/AndroidManifestOverlay.xml diff --git a/osu.Android.props b/osu.Android.props index 49b9de678d..f5d986a458 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -8,14 +8,10 @@ true true - manifestmerger.jar - - - diff --git a/osu.Android/Properties/AndroidManifestOverlay.xml b/osu.Android/Properties/AndroidManifestOverlay.xml deleted file mode 100644 index 815f935383..0000000000 --- a/osu.Android/Properties/AndroidManifestOverlay.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file From 72bf43e297896f6ad4cd76644dc232269a435e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 16 Jul 2023 16:28:44 +0200 Subject: [PATCH 13/74] Add failing test covering exit dialog crash --- .../Navigation/TestSceneScreenNavigation.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index c3b668f591..8e335b2345 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -722,6 +722,33 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); } + [Test] + public void TestForceExitWithOperationInProgress() + { + AddStep("set hold delay to 0", () => Game.LocalConfig.SetValue(OsuSetting.UIHoldActivationDelay, 0.0)); + AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType().SingleOrDefault() != null); + + ProgressNotification progressNotification = null!; + + AddStep("start ongoing operation", () => + { + progressNotification = new ProgressNotification + { + Text = "Something is still running", + Progress = 0.5f, + State = ProgressNotificationState.Active, + }; + Game.Notifications.Post(progressNotification); + }); + + AddStep("attempt exit", () => + { + for (int i = 0; i < 2; ++i) + Game.ScreenStack.CurrentScreen.Exit(); + }); + AddUntilStep("stopped at exit confirm", () => Game.ChildrenOfType().Single().CurrentDialog is ConfirmExitDialog); + } + [Test] public void TestExitGameFromSongSelect() { From d25a03984beba1ba009f4120920ced2820e3e122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 16 Jul 2023 16:29:27 +0200 Subject: [PATCH 14/74] Fix `PerformAction()` potentially crashing when no matching button is found --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index e1e5604e4c..d7316305cc 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Dialog { // Buttons are regularly added in BDL or LoadComplete, so let's schedule to ensure // they are ready to be pressed. - Schedule(() => Buttons.OfType().First().TriggerClick()); + Schedule(() => Buttons.OfType().FirstOrDefault()?.TriggerClick()); } protected override bool OnKeyDown(KeyDownEvent e) From a42992d3fdc4a8ea4762a9f546282aea66e5eb42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 16 Jul 2023 17:10:26 +0200 Subject: [PATCH 15/74] Remove unused local variable --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 8e335b2345..3acc2f0384 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -728,17 +728,14 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("set hold delay to 0", () => Game.LocalConfig.SetValue(OsuSetting.UIHoldActivationDelay, 0.0)); AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType().SingleOrDefault() != null); - ProgressNotification progressNotification = null!; - AddStep("start ongoing operation", () => { - progressNotification = new ProgressNotification + Game.Notifications.Post(new ProgressNotification { Text = "Something is still running", Progress = 0.5f, State = ProgressNotificationState.Active, - }; - Game.Notifications.Post(progressNotification); + }); }); AddStep("attempt exit", () => From a6b1559e1f414a608be1f315bf8527877c219e3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Jul 2023 02:24:51 +0900 Subject: [PATCH 16/74] Update `DiscordRichPresence` to pull in performance fix See https://github.com/Lachee/discord-rpc-csharp/pull/237 --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 16d6a81d40..25f4cff00e 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -26,7 +26,7 @@ - + From cd02a8a9ca599b26304f3ef7b39796bbf2c8a68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 16 Jul 2023 19:43:37 +0200 Subject: [PATCH 17/74] Fix `PopupDialog` potentially accumulating schedules during load --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index d7316305cc..2e9087fdbd 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Dialog { // Buttons are regularly added in BDL or LoadComplete, so let's schedule to ensure // they are ready to be pressed. - Schedule(() => Buttons.OfType().FirstOrDefault()?.TriggerClick()); + Scheduler.AddOnce(() => Buttons.OfType().FirstOrDefault()?.TriggerClick()); } protected override bool OnKeyDown(KeyDownEvent e) From 7fbd47e9eec893409211a4a1f49e9819f0ded742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 16 Jul 2023 19:56:22 +0200 Subject: [PATCH 18/74] Fix `MultiplayerMatchSubScreen` erroneously pushing exit dialog on API failure If `IAPIProvider.State` changes from `Online` at any point when being on an `OnlinePlayScreen`, it will be forcefully exited from. However, `MultiplayerMatchSubScreen` had local logic that suppressed the exit in order to show a confirmation dialog. The problem is, that in the suppression logic, `MultiplayerMatchSubScreen` was checking `MultiplayerClient.IsConnected`, which is a SignalR flag, and was not checking `IAPIAccess.State`, which is maintained separately. Due to differing timeouts and failure thresholds, it is not impossible to have `MultiplayerClient.IsConnected == true` but `IAPIAccess.State != APIState.Online`. In such a case, the match subscreen would wrongly consider itself to be still online and due to that, push useless confirmation dialogs, while being in the process of being forcefully exited. This then caused the dialog to cause a crash, as it was calling `.Exit()` on the screen which would already have been exited by that point, by the force-exit flow. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index ecf38a956d..01f04b44c9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Online; +using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -49,6 +50,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } + [Resolved] + private IAPIProvider api { get; set; } + [Resolved(canBeNull: true)] private OsuGame game { get; set; } @@ -251,10 +255,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool OnExiting(ScreenExitEvent e) { // the room may not be left immediately after a disconnection due to async flow, - // so checking the IsConnected status is also required. - if (client.Room == null || !client.IsConnected.Value) + // so checking the MultiplayerClient / IAPIAccess statuses is also required. + if (client.Room == null || !client.IsConnected.Value || api.State.Value != APIState.Online) { - // room has not been created yet; exit immediately. + // room has not been created yet or we're offline; exit immediately. return base.OnExiting(e); } From 6200e207d292357656ceb4ad29bca093bcad9da1 Mon Sep 17 00:00:00 2001 From: Liam DeVoe Date: Sun, 16 Jul 2023 15:21:15 -0400 Subject: [PATCH 19/74] use fa_download for updates instead of fa_upload --- osu.Game/Updater/NoActionUpdateManager.cs | 4 ++-- osu.Game/Updater/SimpleUpdateManager.cs | 4 ++-- osu.Game/Updater/UpdateManager.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Updater/NoActionUpdateManager.cs b/osu.Game/Updater/NoActionUpdateManager.cs index 97d3275757..f776cd67be 100644 --- a/osu.Game/Updater/NoActionUpdateManager.cs +++ b/osu.Game/Updater/NoActionUpdateManager.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. #nullable disable @@ -47,7 +47,7 @@ namespace osu.Game.Updater { Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + "Check with your package manager / provider to bring osu! up-to-date!", - Icon = FontAwesome.Solid.Upload, + Icon = FontAwesome.Solid.Download, }); return true; diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index 1ecb73a154..bc1b0919b8 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.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. #nullable disable @@ -54,7 +54,7 @@ namespace osu.Game.Updater { Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + "Click here to download the new version, which can be installed over the top of your existing installation", - Icon = FontAwesome.Solid.Upload, + Icon = FontAwesome.Solid.Download, Activated = () => { host.OpenUrlExternally(getBestUrl(latest)); diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 47c2a169ed..190748137a 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -134,7 +134,7 @@ namespace osu.Game.Updater { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Icon = FontAwesome.Solid.Upload, + Icon = FontAwesome.Solid.Download, Size = new Vector2(34), Colour = OsuColour.Gray(0.2f), Depth = float.MaxValue, From 17aac0694eec257217bb8f546806d9021cb97147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 Jul 2023 19:19:03 +0200 Subject: [PATCH 20/74] Re-enable connection retrying on discord connector --- osu.Desktop/DiscordRichPresence.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index b1e11d7a60..caf0a1d9fd 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -54,9 +54,6 @@ namespace osu.Desktop client.OnReady += onReady; - // safety measure for now, until we performance test / improve backoff for failed connections. - client.OnConnectionFailed += (_, _) => client.Deinitialize(); - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); From 871056790b4bfb56fb5d5fad4e3e224673b53987 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 15:01:20 +0900 Subject: [PATCH 21/74] Mark editor tile as non-localisable --- osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index fa4e52d6a6..e958849bb0 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -61,8 +61,8 @@ namespace osu.Game.Screens.Edit.Components.Menus }, }); - text.AddText("osu!", t => t.Font = OsuFont.TorusAlternate); - text.AddText("editor", t => + text.AddText(@"osu!", t => t.Font = OsuFont.TorusAlternate); + text.AddText(@"editor", t => { t.Font = OsuFont.TorusAlternate; t.Colour = colourProvider.Highlight1; From 55a41b0887ff6e7d78502f40dc61204eda4c0306 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 15:13:20 +0900 Subject: [PATCH 22/74] Fix overlap between header text and menu items --- osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index e958849bb0..b9385ff0c3 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -25,7 +25,10 @@ namespace osu.Game.Screens.Edit.Components.Menus RelativeSizeAxes = Axes.X; MaskingContainer.CornerRadius = 0; - ItemsContainer.Padding = new MarginPadding { Left = heading_area }; + ItemsContainer.Padding = new MarginPadding(); + + ContentContainer.Margin = new MarginPadding { Left = heading_area }; + ContentContainer.Masking = true; } [BackgroundDependencyLoader] From 4e4dcc9846cdb1c9c052b021d3d90681ce877bff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 16:32:04 +0900 Subject: [PATCH 23/74] Add test coverage of selection preferring closest objects --- .../Editing/TestSceneComposerSelection.cs | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index d6934a3770..be5dd59206 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -6,21 +6,21 @@ using System; using System.Linq; using NUnit.Framework; -using osu.Framework.Testing; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; -using osu.Game.Tests.Beatmaps; using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Beatmaps; using osuTK; using osuTK.Input; @@ -217,6 +217,75 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); } + [Test] + public void TestNearestSelection() + { + var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 }; + var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject })); + + moveMouseToObject(() => firstObject); + + AddStep("seek near first", () => EditorClock.Seek(100)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + + AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear()); + + AddStep("seek near second", () => EditorClock.Seek(500)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + + AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear()); + + AddStep("seek halfway", () => EditorClock.Seek(300)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + } + + [Test] + public void TestNearestSelectionWithEndTime() + { + var firstObject = new Slider + { + Position = new Vector2(256, 192), + StartTime = 0, + Path = new SliderPath(new[] + { + new PathControlPoint(), + new PathControlPoint(new Vector2(50, 0)), + }) + }; + + var secondObject = new HitCircle + { + Position = new Vector2(256, 192), + StartTime = 600 + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(new HitObject[] { firstObject, secondObject })); + + moveMouseToObject(() => firstObject); + + AddStep("seek near first", () => EditorClock.Seek(100)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + + AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear()); + + AddStep("seek near second", () => EditorClock.Seek(500)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + + AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear()); + + AddStep("seek roughly halfway", () => EditorClock.Seek(350)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + // Slider gets priority due to end time. + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + } + [TestCase(false)] [TestCase(true)] public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag) From 5ade093c5a70f32971ed26422c7c53f29b6529bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 15:55:38 +0900 Subject: [PATCH 24/74] Change editor to always perform selection of closest object --- .../Compose/Components/BlueprintContainer.cs | 27 ++++++++++++++----- .../Components/EditorBlueprintContainer.cs | 5 ++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 56a6b18433..5fdd2634c4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -381,6 +381,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// private bool selectedBlueprintAlreadySelectedOnMouseDown; + protected virtual IEnumerable> ApplySelectionOrder(IEnumerable> blueprints) => blueprints.Reverse(); + /// /// Attempts to select any hovered blueprints. /// @@ -390,15 +392,28 @@ namespace osu.Game.Screens.Edit.Compose.Components { // Iterate from the top of the input stack (blueprints closest to the front of the screen first). // Priority is given to already-selected blueprints. - foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected)) + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Where(b => b.IsSelected)) { - if (!blueprint.IsHovered) continue; + if (runForBlueprint(blueprint)) + return true; + } - selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected; - return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e); + foreach (SelectionBlueprint blueprint in ApplySelectionOrder(SelectionBlueprints.AliveChildren)) + { + if (runForBlueprint(blueprint)) + return true; } return false; + + bool runForBlueprint(SelectionBlueprint blueprint) + { + if (!blueprint.IsHovered) return false; + + selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected; + clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e); + return true; + } } /// @@ -432,13 +447,13 @@ namespace osu.Game.Screens.Edit.Compose.Components // The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front). // For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead. - IEnumerable> cyclingSelectionBlueprints = blueprintMap.Values; + IEnumerable> cyclingSelectionBlueprints = ApplySelectionOrder(blueprintMap.Values); // If there's already a selection, let's start from the blueprint after the selection. cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1); // Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection. - cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected)); + cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(ApplySelectionOrder(blueprintMap.Values).TakeWhile(b => !b.IsSelected)); foreach (SelectionBlueprint blueprint in cyclingSelectionBlueprints) { diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 65797a968d..ad0e8b124b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -129,6 +130,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } + protected override IEnumerable> ApplySelectionOrder(IEnumerable> blueprints) => + base.ApplySelectionOrder(blueprints) + .OrderBy(b => Math.Min(Math.Abs(EditorClock.CurrentTime - b.Item.GetEndTime()), Math.Abs(EditorClock.CurrentTime - b.Item.StartTime))); + protected override Container> CreateSelectionBlueprintContainer() => new HitObjectOrderedSelectionContainer { RelativeSizeAxes = Axes.Both }; protected override SelectionHandler CreateSelectionHandler() => new EditorSelectionHandler(); From 9d46d00294cda3ea10fca9507d008975efcc91d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 16:17:42 +0900 Subject: [PATCH 25/74] Update skin editor cyclic test to match expectations better --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 7b37b6624d..4e5db5d46e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -138,24 +138,28 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestCyclicSelection() { - SkinBlueprint[] blueprints = null!; + List blueprints = new List(); - AddStep("Add big black boxes", () => + AddStep("clear list", () => blueprints.Clear()); + + for (int i = 0; i < 3; i++) { - InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First()); - InputManager.Click(MouseButton.Left); - InputManager.Click(MouseButton.Left); - InputManager.Click(MouseButton.Left); - }); + AddStep("Add big black box", () => + { + InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + AddStep("store box", () => + { + // Add blueprints one-by-one so we have a stable order for testing reverse cyclic selection against. + blueprints.Add(skinEditor.ChildrenOfType().Single(s => s.IsSelected)); + }); + } AddAssert("Three black boxes added", () => targetContainer.Components.OfType().Count(), () => Is.EqualTo(3)); - AddStep("Store black box blueprints", () => - { - blueprints = skinEditor.ChildrenOfType().Where(b => b.Item is BigBlackBox).ToArray(); - }); - - AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); + AddAssert("Selection is last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item)); AddStep("move cursor to black box", () => { @@ -164,13 +168,13 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); - AddAssert("Selection is black box 2", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[1].Item)); + AddAssert("Selection is second last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[1].Item)); AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); - AddAssert("Selection is black box 3", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item)); + AddAssert("Selection is last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); - AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); + AddAssert("Selection is first", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item)); AddStep("select all boxes", () => { From 5ec9cd84b25631ba0bc8ed2c0efcadc331a60afe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 17:13:19 +0900 Subject: [PATCH 26/74] Change offset calibration control to adjust for all difficulties of the current beatmap set --- .../Play/PlayerSettings/BeatmapOffsetControl.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index b542707185..840077eb7f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -162,17 +162,20 @@ namespace osu.Game.Screens.Play.PlayerSettings realmWriteTask = realm.WriteAsync(r => { - var settings = r.Find(beatmap.Value.BeatmapInfo.ID)?.UserSettings; + var setInfo = r.Find(beatmap.Value.BeatmapSetInfo.ID); - if (settings == null) // only the case for tests. + if (setInfo == null) // only the case for tests. return; - double val = Current.Value; + // Apply to all difficulties in a beatmap set for now (they generally always share timing). + foreach (var b in setInfo.Beatmaps) + { + BeatmapUserSettings settings = b.UserSettings; + double val = Current.Value; - if (settings.Offset == val) - return; - - settings.Offset = val; + if (settings.Offset != val) + settings.Offset = val; + } }); } } From d33b174243ef6fe98b3c9ac64cfb6f4ccc91d91c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 17:21:17 +0900 Subject: [PATCH 27/74] Add test coverage of beatmap editor cyclic selection --- .../Editing/TestSceneComposerSelection.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index be5dd59206..d7fb13390f 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -286,6 +286,85 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); } + [Test] + public void TestCyclicSelection() + { + var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 }; + var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 }; + var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject })); + + moveMouseToObject(() => firstObject); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject)); + + // cycle around + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + } + + [Test] + public void TestCyclicSelectionOutwards() + { + var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 }; + var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 }; + var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject })); + + moveMouseToObject(() => firstObject); + + AddStep("seek near first", () => EditorClock.Seek(320)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + + // cycle around + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + } + + [Test] + public void TestCyclicSelectionBackwards() + { + var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 }; + var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 }; + var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject })); + + moveMouseToObject(() => firstObject); + + AddStep("seek near first", () => EditorClock.Seek(600)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject)); + + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject)); + + // cycle around + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject)); + } + [TestCase(false)] [TestCase(true)] public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag) From cf3949c9e21b3fe4f085a8a43d56b60ecdde5ae6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 15:35:40 +0900 Subject: [PATCH 28/74] Fix double-click handling when cyclic selection is enabled Removes the limitations of cyclic selection as a result. --- .../SkinEditor/SkinBlueprintContainer.cs | 2 -- .../Compose/Components/BlueprintContainer.cs | 20 +++++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs b/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs index db27e20010..3f8d9f80d4 100644 --- a/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs +++ b/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs @@ -25,8 +25,6 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private SkinEditor editor { get; set; } = null!; - protected override bool AllowCyclicSelection => true; - public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer) { this.targetContainer = targetContainer; diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 5fdd2634c4..c8df999d37 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -46,15 +46,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected readonly BindableList SelectedItems = new BindableList(); - /// - /// Whether to allow cyclic selection on clicking multiple times. - /// - /// - /// Disabled by default as it does not work well with editors that support double-clicking or other advanced interactions. - /// Can probably be made to work with more thought. - /// - protected virtual bool AllowCyclicSelection => false; - protected BlueprintContainer() { RelativeSizeAxes = Axes.Both; @@ -167,6 +158,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint) return false; + doubleClickHandled = true; return true; } @@ -177,6 +169,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { endClickSelection(e); clickSelectionHandled = false; + doubleClickHandled = false; isDraggingBlueprint = false; wasDragStarted = false; }); @@ -376,6 +369,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// private bool clickSelectionHandled; + /// + /// Whether a blueprint was double-clicked since last mouse down. + /// + private bool doubleClickHandled; + /// /// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case. /// @@ -424,7 +422,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool endClickSelection(MouseButtonEvent e) { // If already handled a selection or drag, we don't want to perform a mouse up / click action. - if (clickSelectionHandled || isDraggingBlueprint) return true; + if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint) return true; if (e.Button != MouseButton.Left) return false; @@ -440,7 +438,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1 && AllowCyclicSelection) + if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1) { // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, // cycle between other blueprints which are also under the cursor. From 94c5b8ed32a322d58b04b894eb65449b81bb9bf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 17:22:25 +0900 Subject: [PATCH 29/74] Apply NRT to `TestSceneComposerSelection` --- .../Visual/Editing/TestSceneComposerSelection.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index d7fb13390f..51e75939fe 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using NUnit.Framework; @@ -82,7 +80,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestNudgeSelection() { - HitCircle[] addedObjects = null; + HitCircle[] addedObjects = null!; AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] { @@ -104,7 +102,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestRotateHotkeys() { - HitCircle[] addedObjects = null; + HitCircle[] addedObjects = null!; AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] { @@ -136,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestGlobalFlipHotkeys() { - HitCircle addedObject = null; + HitCircle addedObject = null!; AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 })); @@ -369,7 +367,7 @@ namespace osu.Game.Tests.Visual.Editing [TestCase(true)] public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag) { - HitCircle[] addedObjects = null; + HitCircle[] addedObjects = null!; AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] { @@ -468,7 +466,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestQuickDeleteRemovesSliderControlPoint() { - Slider slider = null; + Slider slider = null!; PathControlPoint[] points = { From e283845b7144d1e16039841af85f3441a3be2a26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 17:45:07 +0900 Subject: [PATCH 30/74] Adjust legacy skin elements to better align with skinning expectations --- osu.Game/Skinning/LegacyAccuracyCounter.cs | 4 ++-- osu.Game/Skinning/LegacyScoreCounter.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index c99cdba91c..326257c25f 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -17,8 +17,8 @@ namespace osu.Game.Skinning Anchor = Anchor.TopRight; Origin = Anchor.TopRight; - Scale = new Vector2(0.6f); - Margin = new MarginPadding(10); + Scale = new Vector2(0.6f * 0.96f); + Margin = new MarginPadding { Vertical = 9, Horizontal = 17 }; } protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score) diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index d8ee6b21de..d238369be1 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.cs @@ -21,7 +21,7 @@ namespace osu.Game.Skinning Origin = Anchor.TopRight; Scale = new Vector2(0.96f); - Margin = new MarginPadding(10); + Margin = new MarginPadding { Horizontal = 10 }; } protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 79f13686e8..00c18bef3d 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -368,7 +368,7 @@ namespace osu.Game.Skinning { songProgress.Anchor = Anchor.TopRight; songProgress.Origin = Anchor.CentreRight; - songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 10; + songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 20; songProgress.Y = container.ToLocalSpace(accuracy.ScreenSpaceDrawQuad.TopLeft).Y + (accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).Y / 2); } From 61ff3d08d45f2667215c46d1412cd9d3f02e3d5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 17:48:19 +0900 Subject: [PATCH 31/74] Change depth of `LegacySongProgress` to allow "skinning" via health bar background --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 00c18bef3d..b72a757e6c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -397,8 +397,8 @@ namespace osu.Game.Skinning new LegacyComboCounter(), new LegacyScoreCounter(), new LegacyAccuracyCounter(), - new LegacyHealthDisplay(), new LegacySongProgress(), + new LegacyHealthDisplay(), new BarHitErrorMeter(), new DefaultKeyCounterDisplay() } From eb149942e59b9995cfe31d0287289eb4842a0742 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 19:08:32 +0900 Subject: [PATCH 32/74] Add ability to toggle all free mods quickly at multiplayer song select --- .../TestSceneMultiplayerMatchSongSelect.cs | 8 ++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 + .../OnlinePlay/FooterButtonFreeMods.cs | 106 ++++++++++++++---- .../OnlinePlay/OnlinePlaySongSelect.cs | 9 +- 4 files changed, 104 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 947b7e5be6..8dc41cd707 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -67,6 +67,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded); } + [Test] + public void TestSelectFreeMods() + { + AddStep("set some freemods", () => songSelect.FreeMods.Value = new OsuRuleset().GetModsFor(ModType.Fun).ToArray()); + AddStep("set all freemods", () => songSelect.FreeMods.Value = new OsuRuleset().CreateAllMods().ToArray()); + AddStep("set no freemods", () => songSelect.FreeMods.Value = Array.Empty()); + } + [Test] public void TestBeatmapConfirmed() { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c09668850a..ba3f01a688 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -111,6 +111,10 @@ namespace osu.Game.Overlays.Mods private readonly Bindable>> globalAvailableMods = new Bindable>>(); + public IEnumerable AllAvailableAndValidMods => allAvailableMods + .Select(s => s.Mod) + .Where(m => isValidMod(m)); + private IEnumerable allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); private readonly BindableBool customisationVisible = new BindableBool(); diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs index 920aff13a8..56a69be741 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs @@ -1,15 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; using osuTK; @@ -17,28 +23,60 @@ namespace osu.Game.Screens.OnlinePlay { public partial class FooterButtonFreeMods : FooterButton, IHasCurrentValue> { - public Bindable> Current + public Bindable> Current { get; set; } = new BindableWithCurrent>(); + + private OsuSpriteText count = null!; + + private Circle circle = null!; + + private readonly FreeModSelectOverlay freeModSelectOverlay; + + public FooterButtonFreeMods(FreeModSelectOverlay freeModSelectOverlay) { - get => modDisplay.Current; - set => modDisplay.Current = value; + this.freeModSelectOverlay = freeModSelectOverlay; } - private readonly ModDisplay modDisplay; - - public FooterButtonFreeMods() - { - ButtonContentContainer.Add(modDisplay = new ModDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.8f), - ExpansionMode = ExpansionMode.AlwaysContracted, - }); - } + [Resolved] + private OsuColour colours { get; set; } = null!; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { + ButtonContentContainer.AddRange(new[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + circle = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colours.YellowDark, + RelativeSizeAxes = Axes.Both, + }, + count = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(5), + UseFullGlyphHeight = false, + } + } + }, + new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.8f), + Icon = FontAwesome.Solid.Bars, + Action = () => freeModSelectOverlay.ToggleVisibility() + } + }); + SelectedColour = colours.Yellow; DeselectedColour = SelectedColour.Opacity(0.5f); Text = @"freemods"; @@ -49,14 +87,42 @@ namespace osu.Game.Screens.OnlinePlay base.LoadComplete(); Current.BindValueChanged(_ => updateModDisplay(), true); + + // Overwrite any external behaviour as we delegate the main toggle action to a sub-button. + Action = toggleAllFreeMods; + } + + /// + /// Immediately toggle all free mods on/off. + /// + private void toggleAllFreeMods() + { + var availableMods = freeModSelectOverlay.AllAvailableAndValidMods.ToArray(); + + Current.Value = Current.Value.Count == availableMods.Length + ? Array.Empty() + : availableMods; } private void updateModDisplay() { - if (Current.Value?.Count > 0) - modDisplay.FadeIn(); + int current = Current.Value.Count; + + if (current == freeModSelectOverlay.AllAvailableAndValidMods.Count()) + { + count.Text = "all"; + circle.FadeColour(colours.Yellow, 200, Easing.OutQuint); + } + else if (current > 0) + { + count.Text = $"{current} mods"; + circle.FadeColour(colours.YellowDark, 200, Easing.OutQuint); + } else - modDisplay.FadeOut(); + { + count.Text = "off"; + circle.FadeColour(colours.Gray4, 200, Easing.OutQuint); + } } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index e0ae437d49..622ffddba6 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -175,9 +175,12 @@ namespace osu.Game.Screens.OnlinePlay protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons() { - var buttons = base.CreateFooterButtons().ToList(); - buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay)); - return buttons; + var baseButtons = base.CreateFooterButtons().ToList(); + var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods }; + + baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay)); + + return baseButtons; } /// From 18aace177a7fdcd9a6756229326b102f669c7d53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 19:36:51 +0900 Subject: [PATCH 33/74] Fix deadlock when logging out while at the create match screen Closes https://github.com/ppy/osu/issues/24275. --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 9 +++++++++ .../Multiplayer/MultiplayerMatchSubScreen.cs | 14 ++++---------- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 6 ++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 6b68024393..6e126a928a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -23,6 +23,7 @@ using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; +using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -76,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private IAPIProvider api { get; set; } = null!; + [Resolved(canBeNull: true)] protected OnlinePlayScreen ParentScreen { get; private set; } @@ -284,6 +288,8 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved(canBeNull: true)] private IDialogOverlay dialogOverlay { get; set; } + protected virtual bool IsConnected => api.State.Value == APIState.Online; + public override bool OnBackButton() { if (Room.RoomID.Value == null) @@ -356,6 +362,9 @@ namespace osu.Game.Screens.OnlinePlay.Match if (ExitConfirmed) return true; + if (!IsConnected) + return true; + if (dialogOverlay == null || Room.RoomID.Value != null || Room.Playlist.Count == 0) return true; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 01f04b44c9..f5746ca96c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -17,7 +17,6 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Online; -using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -50,9 +49,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - [Resolved] - private IAPIProvider api { get; set; } - [Resolved(canBeNull: true)] private OsuGame game { get; set; } @@ -79,6 +75,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer handleRoomLost(); } + protected override bool IsConnected => base.IsConnected && client.IsConnected.Value; + protected override Drawable CreateMainContent() => new Container { RelativeSizeAxes = Axes.Both, @@ -254,13 +252,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool OnExiting(ScreenExitEvent e) { - // the room may not be left immediately after a disconnection due to async flow, - // so checking the MultiplayerClient / IAPIAccess statuses is also required. - if (client.Room == null || !client.IsConnected.Value || api.State.Value != APIState.Online) - { - // room has not been created yet or we're offline; exit immediately. + // room has not been created yet or we're offline; exit immediately. + if (client.Room == null || !IsConnected) return base.OnExiting(e); - } if (!exitConfirmed && dialogOverlay != null) { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index c7b32131cf..b527bf98a2 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; @@ -15,8 +13,8 @@ namespace osu.Game.Screens.OnlinePlay public virtual string ShortTitle => Title; - [Resolved(CanBeNull = true)] - protected IRoomManager RoomManager { get; private set; } + [Resolved] + protected IRoomManager? RoomManager { get; private set; } protected OnlinePlaySubScreen() { From e47722565ae2c53ee38f156a027aa197e54e3c7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jul 2023 19:39:10 +0900 Subject: [PATCH 34/74] Clarify guard condition in `RoomSubScreen` --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 6e126a928a..75b673cf1b 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -365,7 +365,9 @@ namespace osu.Game.Screens.OnlinePlay.Match if (!IsConnected) return true; - if (dialogOverlay == null || Room.RoomID.Value != null || Room.Playlist.Count == 0) + bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0; + + if (dialogOverlay == null || !hasUnsavedChanges) return true; // if the dialog is already displayed, block exiting until the user explicitly makes a decision. From 764029bde1eda3976982238bf9d1cba6ebe56fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 19 Jul 2023 19:23:08 +0200 Subject: [PATCH 35/74] Fix nullability inspection --- .../OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index dd4f35cdd4..4478179726 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // To work around this, temporarily remove the room and trigger an immediate listing poll. if (e.Last is MultiplayerMatchSubScreen match) { - RoomManager.RemoveRoom(match.Room); + RoomManager?.RemoveRoom(match.Room); ListingPollingComponent.PollImmediately(); } } From 18c5fc689f4dc799ab433eb041ca9c09482668ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Jul 2023 12:58:13 +0900 Subject: [PATCH 36/74] Don't expose such specific information from `ModSelectOverlay` --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 14 +++++--------- .../Screens/OnlinePlay/FooterButtonFreeMods.cs | 8 ++++++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ba3f01a688..7ec108e3ec 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -111,11 +111,7 @@ namespace osu.Game.Overlays.Mods private readonly Bindable>> globalAvailableMods = new Bindable>>(); - public IEnumerable AllAvailableAndValidMods => allAvailableMods - .Select(s => s.Mod) - .Where(m => isValidMod(m)); - - private IEnumerable allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); + public IEnumerable AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); private readonly BindableBool customisationVisible = new BindableBool(); @@ -386,7 +382,7 @@ namespace osu.Game.Overlays.Mods private void filterMods() { - foreach (var modState in allAvailableMods) + foreach (var modState in AllAvailableMods) modState.ValidForSelection.Value = modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod); } @@ -411,7 +407,7 @@ namespace osu.Game.Overlays.Mods bool anyCustomisableModActive = false; bool anyModPendingConfiguration = false; - foreach (var modState in allAvailableMods) + foreach (var modState in AllAvailableMods) { anyCustomisableModActive |= modState.Active.Value && modState.Mod.GetSettingsSourceProperties().Any(); anyModPendingConfiguration |= modState.PendingConfiguration; @@ -468,7 +464,7 @@ namespace osu.Game.Overlays.Mods var newSelection = new List(); - foreach (var modState in allAvailableMods) + foreach (var modState in AllAvailableMods) { var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType()); @@ -495,7 +491,7 @@ namespace osu.Game.Overlays.Mods if (externalSelectionUpdateInProgress) return; - var candidateSelection = allAvailableMods.Where(modState => modState.Active.Value) + var candidateSelection = AllAvailableMods.Where(modState => modState.Active.Value) .Select(modState => modState.Mod) .ToArray(); diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs index 56a69be741..294c80677d 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.OnlinePlay /// private void toggleAllFreeMods() { - var availableMods = freeModSelectOverlay.AllAvailableAndValidMods.ToArray(); + var availableMods = allAvailableAndValidMods.ToArray(); Current.Value = Current.Value.Count == availableMods.Length ? Array.Empty() @@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay { int current = Current.Value.Count; - if (current == freeModSelectOverlay.AllAvailableAndValidMods.Count()) + if (current == allAvailableAndValidMods.Count()) { count.Text = "all"; circle.FadeColour(colours.Yellow, 200, Easing.OutQuint); @@ -124,5 +124,9 @@ namespace osu.Game.Screens.OnlinePlay circle.FadeColour(colours.Gray4, 200, Easing.OutQuint); } } + + private IEnumerable allAvailableAndValidMods => freeModSelectOverlay.AllAvailableMods + .Where(state => state.ValidForSelection.Value) + .Select(state => state.Mod); } } From c93d6a4008639b68a9f6508d6b5e46f9e800df9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Jul 2023 13:04:21 +0900 Subject: [PATCH 37/74] Invert text colour when freemods is enabled for better contrast --- osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs index 294c80677d..3825cf18b9 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs @@ -111,16 +111,19 @@ namespace osu.Game.Screens.OnlinePlay if (current == allAvailableAndValidMods.Count()) { count.Text = "all"; + count.FadeColour(colours.Gray2, 200, Easing.OutQuint); circle.FadeColour(colours.Yellow, 200, Easing.OutQuint); } else if (current > 0) { count.Text = $"{current} mods"; + count.FadeColour(colours.Gray2, 200, Easing.OutQuint); circle.FadeColour(colours.YellowDark, 200, Easing.OutQuint); } else { count.Text = "off"; + count.FadeColour(colours.GrayF, 200, Easing.OutQuint); circle.FadeColour(colours.Gray4, 200, Easing.OutQuint); } } From d9d055361aa956e5233d38c689aa9077033d104a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Jul 2023 17:49:23 +0900 Subject: [PATCH 38/74] More realm analytic disables --- .gitignore | 1 - osu.Game/FodyWeavers.xml | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 osu.Game/FodyWeavers.xml diff --git a/.gitignore b/.gitignore index 0c7a18b437..525b3418cd 100644 --- a/.gitignore +++ b/.gitignore @@ -339,6 +339,5 @@ inspectcode # Fody (pulled in by Realm) - schema file FodyWeavers.xsd -**/FodyWeavers.xml .idea/.idea.osu.Desktop/.idea/misc.xml \ No newline at end of file diff --git a/osu.Game/FodyWeavers.xml b/osu.Game/FodyWeavers.xml new file mode 100644 index 0000000000..7ff486f40c --- /dev/null +++ b/osu.Game/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + From f791f21dcb3bc22c439838f2f7bee5d84310b757 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Jul 2023 20:05:35 +0900 Subject: [PATCH 39/74] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 059c5f26e7..ed97f609cc 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - +