diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index a0602e21b9..9012492028 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("reload skin editor", () => { skinEditor?.Expire(); - Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE); + Player.ScaleTo(0.8f); LoadComponentAsync(skinEditor = new SkinEditor(Player), Add); }); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs index 58c89411c0..5385a9983b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Skinning.Editor; @@ -11,13 +13,17 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneSkinEditorComponentsList : SkinnableTestScene { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + [Test] public void TestToggleEditor() { - AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300) + AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Width = 0.6f, })); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs index 175d2ea36b..2c253650d5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Online private int messageIdCounter; [SetUp] - public void Setup() + public void Setup() => Schedule(() => { if (API is DummyAPIAccess daa) { @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Online testContainer.ChatOverlay.Show(); }); - } + }); private bool dummyAPIHandleRequest(APIRequest request) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ae117d03d2..25bd3d71de 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1184,7 +1184,7 @@ namespace osu.Game BackButton.Hide(); } - skinEditor.SetTarget((Screen)newScreen); + skinEditor.SetTarget((OsuScreen)newScreen); } private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen); diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 632517aa31..161fe1d5be 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Chat if (highlightedMessage.Value == null) return; - var chatLine = chatLines.SingleOrDefault(c => c.Message.Equals(highlightedMessage.Value)); + var chatLine = chatLines.FirstOrDefault(c => c.Message.Equals(highlightedMessage.Value)); if (chatLine == null) return; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f5b11448f8..2d1a2bce4e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -37,6 +37,7 @@ using osu.Game.Graphics.UserInterface; using System.Diagnostics; using osu.Game.Screens.Play; using osu.Game.Database; +using osu.Game.Skinning; namespace osu.Game.Screens.Select { @@ -235,6 +236,10 @@ namespace osu.Game.Screens.Select } } }, + new SkinnableTargetContainer(SkinnableTarget.SongSelect) + { + RelativeSizeAxes = Axes.Both, + }, }); if (ShowFooter) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 951e3f9cc5..7c6d138f4c 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -70,6 +70,14 @@ namespace osu.Game.Skinning case SkinnableTargetComponent target: switch (target.Target) { + case SkinnableTarget.SongSelect: + var songSelectComponents = new SkinnableTargetComponentsContainer(container => + { + // do stuff when we need to. + }); + + return songSelectComponents; + case SkinnableTarget.MainHUDComponents: var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index 98fdc7c93f..483e365e78 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -17,18 +17,16 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Components; using osuTK; namespace osu.Game.Skinning.Editor { - public class SkinComponentToolbox : ScrollingToolboxGroup + public class SkinComponentToolbox : EditorSidebarSection { - public const float WIDTH = 200; - public Action RequestPlacement; [Cached] @@ -41,11 +39,9 @@ namespace osu.Game.Skinning.Editor [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - public SkinComponentToolbox(float height) - : base("Components", height) + public SkinComponentToolbox() + : base("Components") { - RelativeSizeAxes = Axes.None; - Width = WIDTH; } [BackgroundDependencyLoader] diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index ef26682c03..f7e5aeecdf 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,7 +16,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Edit; +using osu.Game.Overlays; +using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; namespace osu.Game.Skinning.Editor @@ -43,95 +43,139 @@ namespace osu.Game.Skinning.Editor [Resolved] private OsuColour colours { get; set; } + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + private bool hasBegunMutating; private Container content; - private EditorToolboxGroup settingsToolbox; + private EditorSidebarSection settingsToolbox; + + public SkinEditor() + { + } public SkinEditor(Drawable targetScreen) { - RelativeSizeAxes = Axes.Both; - UpdateTargetScreen(targetScreen); } [BackgroundDependencyLoader] private void load() { + RelativeSizeAxes = Axes.Both; + + const float menu_height = 40; + InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Child = new GridContainer { - new Container + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - Name = "Top bar", - RelativeSizeAxes = Axes.X, - Depth = float.MinValue, - Height = 40, - Children = new Drawable[] + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + + Content = new[] + { + new Drawable[] { - new EditorMenuBar + new Container { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Items = new[] + Name = "Menu container", + RelativeSizeAxes = Axes.X, + Depth = float.MinValue, + Height = menu_height, + Children = new Drawable[] { - new MenuItem("File") + new EditorMenuBar { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, Items = new[] { - new EditorMenuItem("Save", MenuItemType.Standard, Save), - new EditorMenuItem("Revert to default", MenuItemType.Destructive, revert), - new EditorMenuItemSpacer(), - new EditorMenuItem("Exit", MenuItemType.Standard, Hide), - }, + new MenuItem("File") + { + Items = new[] + { + new EditorMenuItem("Save", MenuItemType.Standard, Save), + new EditorMenuItem("Revert to default", MenuItemType.Destructive, revert), + new EditorMenuItemSpacer(), + new EditorMenuItem("Exit", MenuItemType.Standard, Hide), + }, + }, + } }, - } - }, - headerText = new OsuTextFlowContainer - { - TextAnchor = Anchor.TopRight, - Padding = new MarginPadding(5), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, + headerText = new OsuTextFlowContainer + { + TextAnchor = Anchor.TopRight, + Padding = new MarginPadding(5), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + }, + }, }, }, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + new Drawable[] { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + new SkinEditorSceneLibrary { - new SkinComponentToolbox(600) + RelativeSizeAxes = Axes.X, + }, + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RequestPlacement = placeComponent + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, - content = new Container + Content = new[] { - RelativeSizeAxes = Axes.Both, - }, - settingsToolbox = new SkinSettingsToolbox - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, + new Drawable[] + { + new EditorSidebar + { + Children = new[] + { + new SkinComponentToolbox + { + RequestPlacement = placeComponent + }, + } + }, + content = new Container + { + Depth = float.MaxValue, + RelativeSizeAxes = Axes.Both, + }, + new EditorSidebar + { + Children = new[] + { + settingsToolbox = new SkinSettingsToolbox + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + } + }, + } } } - } + }, } } }; @@ -155,7 +199,7 @@ namespace osu.Game.Skinning.Editor Scheduler.AddOnce(skinChanged); }, true); - SelectedComponents.BindCollectionChanged(selectionChanged); + SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true); } public void UpdateTargetScreen(Drawable targetScreen) @@ -163,15 +207,11 @@ namespace osu.Game.Skinning.Editor this.targetScreen = targetScreen; SelectedComponents.Clear(); - Scheduler.AddOnce(loadBlueprintContainer); - void loadBlueprintContainer() - { - content.Children = new Drawable[] - { - new SkinBlueprintContainer(targetScreen), - }; - } + Scheduler.AddOnce(loadBlueprintContainer); + Scheduler.AddOnce(populateSettings); + + void loadBlueprintContainer() => content.Child = new SkinBlueprintContainer(targetScreen); } private void skinChanged() @@ -224,7 +264,7 @@ namespace osu.Game.Skinning.Editor SelectedComponents.Add(component); } - private void selectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void populateSettings() { settingsToolbox.Clear(); diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 08cdbf0aa9..9fc233d3e3 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -3,15 +3,15 @@ using System.Diagnostics; using JetBrains.Annotations; -using osu.Framework.Bindables; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; +using osu.Game.Screens; namespace osu.Game.Skinning.Editor { @@ -21,16 +21,21 @@ namespace osu.Game.Skinning.Editor /// public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler { - private readonly ScalingContainer target; + private readonly ScalingContainer scalingContainer; [CanBeNull] private SkinEditor skinEditor; public const float VISIBLE_TARGET_SCALE = 0.8f; - public SkinEditorOverlay(ScalingContainer target) + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } + + private OsuScreen lastTargetScreen; + + public SkinEditorOverlay(ScalingContainer scalingContainer) { - this.target = target; + this.scalingContainer = scalingContainer; RelativeSizeAxes = Axes.Both; } @@ -77,8 +82,8 @@ namespace osu.Game.Skinning.Editor return; } - var editor = new SkinEditor(target); - editor.State.BindValueChanged(editorVisibilityChanged); + var editor = new SkinEditor(); + editor.State.BindValueChanged(visibility => updateComponentVisibility()); skinEditor = editor; @@ -95,21 +100,31 @@ namespace osu.Game.Skinning.Editor return; AddInternal(editor); + + SetTarget(lastTargetScreen); }); }); } - private void editorVisibilityChanged(ValueChangedEvent visibility) + private void updateComponentVisibility() { + Debug.Assert(skinEditor != null); + const float toolbar_padding_requirement = 0.18f; - if (visibility.NewValue == Visibility.Visible) + if (skinEditor.State.Value == Visibility.Visible) { - target.SetCustomRect(new RectangleF(toolbar_padding_requirement, 0.1f, 0.8f - toolbar_padding_requirement, 0.7f), true); + scalingContainer.SetCustomRect(new RectangleF(toolbar_padding_requirement, 0.2f, 0.8f - toolbar_padding_requirement, 0.7f), true); + + game?.Toolbar.Hide(); + game?.CloseAllOverlays(); } else { - target.SetCustomRect(null); + scalingContainer.SetCustomRect(null); + + if (lastTargetScreen?.HideOverlaysOnEnter != true) + game?.Toolbar.Show(); } } @@ -120,17 +135,22 @@ namespace osu.Game.Skinning.Editor /// /// Set a new target screen which will be used to find skinnable components. /// - public void SetTarget(Screen screen) + public void SetTarget(OsuScreen screen) { + lastTargetScreen = screen; + if (skinEditor == null) return; skinEditor.Save(); + // ensure the toolbar is re-hidden even if a new screen decides to try and show it. + updateComponentVisibility(); + // AddOnce with parameter will ensure the newest target is loaded if there is any overlap. Scheduler.AddOnce(setTarget, screen); } - private void setTarget(Screen target) + private void setTarget(OsuScreen target) { Debug.Assert(skinEditor != null); diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs new file mode 100644 index 0000000000..5da6147e4c --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs @@ -0,0 +1,123 @@ +// 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.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Rulesets; +using osu.Game.Screens.Play; +using osu.Game.Screens.Select; +using osuTK; + +namespace osu.Game.Skinning.Editor +{ + public class SkinEditorSceneLibrary : CompositeDrawable + { + public const float BUTTON_HEIGHT = 40; + + private const float padding = 10; + + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } + + [Resolved] + private IBindable ruleset { get; set; } + + public SkinEditorSceneLibrary() + { + Height = BUTTON_HEIGHT + padding * 2; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider overlayColourProvider) + { + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = overlayColourProvider.Background6, + }, + new OsuScrollContainer(Direction.Horizontal) + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Name = "Scene library", + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Spacing = new Vector2(padding), + Padding = new MarginPadding(padding), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Scene library", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding(10), + }, + new SceneButton + { + Text = "Song Select", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Action = () => game?.PerformFromScreen(screen => + { + if (screen is SongSelect) + return; + + screen.Push(new PlaySongSelect()); + }, new[] { typeof(SongSelect) }) + }, + new SceneButton + { + Text = "Gameplay", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Action = () => game?.PerformFromScreen(screen => + { + if (screen is Player) + return; + + var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); + if (replayGeneratingMod != null) + screen.Push(new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateReplayScore(beatmap, mods))); + }, new[] { typeof(Player), typeof(SongSelect) }) + }, + } + }, + } + } + }; + } + + private class SceneButton : OsuButton + { + public SceneButton() + { + Width = 100; + Height = BUTTON_HEIGHT; + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + { + BackgroundColour = overlayColourProvider?.Background3 ?? colours.Blue3; + Content.CornerRadius = 5; + } + } + } +} diff --git a/osu.Game/Skinning/Editor/SkinSettingsToolbox.cs b/osu.Game/Skinning/Editor/SkinSettingsToolbox.cs index c0ef8e7316..fc06d3647a 100644 --- a/osu.Game/Skinning/Editor/SkinSettingsToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinSettingsToolbox.cs @@ -2,22 +2,26 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Edit; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Edit.Components; using osuTK; namespace osu.Game.Skinning.Editor { - internal class SkinSettingsToolbox : ScrollingToolboxGroup + internal class SkinSettingsToolbox : EditorSidebarSection { - public const float WIDTH = 200; + protected override Container Content { get; } public SkinSettingsToolbox() - : base("Settings", 600) + : base("Settings") { - RelativeSizeAxes = Axes.None; - Width = WIDTH; - - FillFlow.Spacing = new Vector2(10); + base.Content.Add(Content = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + }); } } } diff --git a/osu.Game/Skinning/SkinnableTarget.cs b/osu.Game/Skinning/SkinnableTarget.cs index 7b1eae126c..09de8a5d71 100644 --- a/osu.Game/Skinning/SkinnableTarget.cs +++ b/osu.Game/Skinning/SkinnableTarget.cs @@ -5,6 +5,7 @@ namespace osu.Game.Skinning { public enum SkinnableTarget { - MainHUDComponents + MainHUDComponents, + SongSelect } }