Merge branch 'master' into skin-editor-toolbox-ui

This commit is contained in:
Dean Herbert 2022-03-16 22:21:10 +09:00
commit 6df36171b5
13 changed files with 308 additions and 105 deletions

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("reload skin editor", () => AddStep("reload skin editor", () =>
{ {
skinEditor?.Expire(); skinEditor?.Expire();
Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE); Player.ScaleTo(0.8f);
LoadComponentAsync(skinEditor = new SkinEditor(Player), Add); LoadComponentAsync(skinEditor = new SkinEditor(Player), Add);
}); });
} }

View File

@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Skinning.Editor; using osu.Game.Skinning.Editor;
@ -11,13 +13,17 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneSkinEditorComponentsList : SkinnableTestScene public class TestSceneSkinEditorComponentsList : SkinnableTestScene
{ {
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[Test] [Test]
public void TestToggleEditor() public void TestToggleEditor()
{ {
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300) AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre, Origin = Anchor.TopRight,
Width = 0.6f,
})); }));
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Online
private int messageIdCounter; private int messageIdCounter;
[SetUp] [SetUp]
public void Setup() public void Setup() => Schedule(() =>
{ {
if (API is DummyAPIAccess daa) if (API is DummyAPIAccess daa)
{ {
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Online
testContainer.ChatOverlay.Show(); testContainer.ChatOverlay.Show();
}); });
} });
private bool dummyAPIHandleRequest(APIRequest request) private bool dummyAPIHandleRequest(APIRequest request)
{ {

View File

@ -1184,7 +1184,7 @@ namespace osu.Game
BackButton.Hide(); BackButton.Hide();
} }
skinEditor.SetTarget((Screen)newScreen); skinEditor.SetTarget((OsuScreen)newScreen);
} }
private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen); private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen);

View File

@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Chat
if (highlightedMessage.Value == null) if (highlightedMessage.Value == null)
return; return;
var chatLine = chatLines.SingleOrDefault(c => c.Message.Equals(highlightedMessage.Value)); var chatLine = chatLines.FirstOrDefault(c => c.Message.Equals(highlightedMessage.Value));
if (chatLine == null) if (chatLine == null)
return; return;

View File

@ -37,6 +37,7 @@ using osu.Game.Graphics.UserInterface;
using System.Diagnostics; using System.Diagnostics;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Skinning;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
@ -235,6 +236,10 @@ namespace osu.Game.Screens.Select
} }
} }
}, },
new SkinnableTargetContainer(SkinnableTarget.SongSelect)
{
RelativeSizeAxes = Axes.Both,
},
}); });
if (ShowFooter) if (ShowFooter)

View File

@ -70,6 +70,14 @@ namespace osu.Game.Skinning
case SkinnableTargetComponent target: case SkinnableTargetComponent target:
switch (target.Target) switch (target.Target)
{ {
case SkinnableTarget.SongSelect:
var songSelectComponents = new SkinnableTargetComponentsContainer(container =>
{
// do stuff when we need to.
});
return songSelectComponents;
case SkinnableTarget.MainHUDComponents: case SkinnableTarget.MainHUDComponents:
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container =>
{ {

View File

@ -17,18 +17,16 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Components;
using osuTK; using osuTK;
namespace osu.Game.Skinning.Editor namespace osu.Game.Skinning.Editor
{ {
public class SkinComponentToolbox : ScrollingToolboxGroup public class SkinComponentToolbox : EditorSidebarSection
{ {
public const float WIDTH = 200;
public Action<Type> RequestPlacement; public Action<Type> RequestPlacement;
[Cached] [Cached]
@ -41,11 +39,9 @@ namespace osu.Game.Skinning.Editor
[Cached(typeof(HealthProcessor))] [Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
public SkinComponentToolbox(float height) public SkinComponentToolbox()
: base("Components", height) : base("Components")
{ {
RelativeSizeAxes = Axes.None;
Width = WIDTH;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -17,7 +16,8 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface; 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; using osu.Game.Screens.Edit.Components.Menus;
namespace osu.Game.Skinning.Editor namespace osu.Game.Skinning.Editor
@ -43,95 +43,139 @@ namespace osu.Game.Skinning.Editor
[Resolved] [Resolved]
private OsuColour colours { get; set; } private OsuColour colours { get; set; }
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private bool hasBegunMutating; private bool hasBegunMutating;
private Container content; private Container content;
private EditorToolboxGroup settingsToolbox; private EditorSidebarSection settingsToolbox;
public SkinEditor()
{
}
public SkinEditor(Drawable targetScreen) public SkinEditor(Drawable targetScreen)
{ {
RelativeSizeAxes = Axes.Both;
UpdateTargetScreen(targetScreen); UpdateTargetScreen(targetScreen);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
RelativeSizeAxes = Axes.Both;
const float menu_height = 40;
InternalChild = new OsuContextMenuContainer InternalChild = new OsuContextMenuContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Child = new GridContainer
{ {
new Container RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{ {
Name = "Top bar", new Dimension(GridSizeMode.AutoSize),
RelativeSizeAxes = Axes.X, new Dimension(GridSizeMode.AutoSize),
Depth = float.MinValue, new Dimension(),
Height = 40, },
Children = new Drawable[]
Content = new[]
{
new Drawable[]
{ {
new EditorMenuBar new Container
{ {
Anchor = Anchor.CentreLeft, Name = "Menu container",
Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Both, Depth = float.MinValue,
Items = new[] Height = menu_height,
Children = new Drawable[]
{ {
new MenuItem("File") new EditorMenuBar
{ {
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Items = new[] Items = new[]
{ {
new EditorMenuItem("Save", MenuItemType.Standard, Save), new MenuItem("File")
new EditorMenuItem("Revert to default", MenuItemType.Destructive, revert), {
new EditorMenuItemSpacer(), Items = new[]
new EditorMenuItem("Exit", MenuItemType.Standard, Hide), {
}, 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
}, {
headerText = new OsuTextFlowContainer TextAnchor = Anchor.TopRight,
{ Padding = new MarginPadding(5),
TextAnchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Padding = new MarginPadding(5), Origin = Anchor.TopRight,
Anchor = Anchor.TopRight, AutoSizeAxes = Axes.X,
Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X, },
RelativeSizeAxes = Axes.Y, },
}, },
}, },
}, new Drawable[]
new GridContainer
{
RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[]
{ {
new Dimension(GridSizeMode.AutoSize), new SkinEditorSceneLibrary
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
{
new Drawable[]
{ {
new SkinComponentToolbox(600) RelativeSizeAxes = Axes.X,
},
},
new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[]
{ {
Anchor = Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize),
Origin = Anchor.CentreLeft, new Dimension(),
RequestPlacement = placeComponent new Dimension(GridSizeMode.AutoSize),
}, },
content = new Container Content = new[]
{ {
RelativeSizeAxes = Axes.Both, new Drawable[]
}, {
settingsToolbox = new SkinSettingsToolbox new EditorSidebar
{ {
Anchor = Anchor.CentreRight, Children = new[]
Origin = Anchor.CentreRight, {
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); Scheduler.AddOnce(skinChanged);
}, true); }, true);
SelectedComponents.BindCollectionChanged(selectionChanged); SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true);
} }
public void UpdateTargetScreen(Drawable targetScreen) public void UpdateTargetScreen(Drawable targetScreen)
@ -163,15 +207,11 @@ namespace osu.Game.Skinning.Editor
this.targetScreen = targetScreen; this.targetScreen = targetScreen;
SelectedComponents.Clear(); SelectedComponents.Clear();
Scheduler.AddOnce(loadBlueprintContainer);
void loadBlueprintContainer() Scheduler.AddOnce(loadBlueprintContainer);
{ Scheduler.AddOnce(populateSettings);
content.Children = new Drawable[]
{ void loadBlueprintContainer() => content.Child = new SkinBlueprintContainer(targetScreen);
new SkinBlueprintContainer(targetScreen),
};
}
} }
private void skinChanged() private void skinChanged()
@ -224,7 +264,7 @@ namespace osu.Game.Skinning.Editor
SelectedComponents.Add(component); SelectedComponents.Add(component);
} }
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void populateSettings()
{ {
settingsToolbox.Clear(); settingsToolbox.Clear();

View File

@ -3,15 +3,15 @@
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Screens;
namespace osu.Game.Skinning.Editor namespace osu.Game.Skinning.Editor
{ {
@ -21,16 +21,21 @@ namespace osu.Game.Skinning.Editor
/// </summary> /// </summary>
public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler<GlobalAction> public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler<GlobalAction>
{ {
private readonly ScalingContainer target; private readonly ScalingContainer scalingContainer;
[CanBeNull] [CanBeNull]
private SkinEditor skinEditor; private SkinEditor skinEditor;
public const float VISIBLE_TARGET_SCALE = 0.8f; 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; RelativeSizeAxes = Axes.Both;
} }
@ -77,8 +82,8 @@ namespace osu.Game.Skinning.Editor
return; return;
} }
var editor = new SkinEditor(target); var editor = new SkinEditor();
editor.State.BindValueChanged(editorVisibilityChanged); editor.State.BindValueChanged(visibility => updateComponentVisibility());
skinEditor = editor; skinEditor = editor;
@ -95,21 +100,31 @@ namespace osu.Game.Skinning.Editor
return; return;
AddInternal(editor); AddInternal(editor);
SetTarget(lastTargetScreen);
}); });
}); });
} }
private void editorVisibilityChanged(ValueChangedEvent<Visibility> visibility) private void updateComponentVisibility()
{ {
Debug.Assert(skinEditor != null);
const float toolbar_padding_requirement = 0.18f; 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 else
{ {
target.SetCustomRect(null); scalingContainer.SetCustomRect(null);
if (lastTargetScreen?.HideOverlaysOnEnter != true)
game?.Toolbar.Show();
} }
} }
@ -120,17 +135,22 @@ namespace osu.Game.Skinning.Editor
/// <summary> /// <summary>
/// Set a new target screen which will be used to find skinnable components. /// Set a new target screen which will be used to find skinnable components.
/// </summary> /// </summary>
public void SetTarget(Screen screen) public void SetTarget(OsuScreen screen)
{ {
lastTargetScreen = screen;
if (skinEditor == null) return; if (skinEditor == null) return;
skinEditor.Save(); 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. // AddOnce with parameter will ensure the newest target is loaded if there is any overlap.
Scheduler.AddOnce(setTarget, screen); Scheduler.AddOnce(setTarget, screen);
} }
private void setTarget(Screen target) private void setTarget(OsuScreen target)
{ {
Debug.Assert(skinEditor != null); Debug.Assert(skinEditor != null);

View File

@ -0,0 +1,123 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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<RulesetInfo> 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;
}
}
}
}

View File

@ -2,22 +2,26 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Edit; using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Edit.Components;
using osuTK; using osuTK;
namespace osu.Game.Skinning.Editor namespace osu.Game.Skinning.Editor
{ {
internal class SkinSettingsToolbox : ScrollingToolboxGroup internal class SkinSettingsToolbox : EditorSidebarSection
{ {
public const float WIDTH = 200; protected override Container<Drawable> Content { get; }
public SkinSettingsToolbox() public SkinSettingsToolbox()
: base("Settings", 600) : base("Settings")
{ {
RelativeSizeAxes = Axes.None; base.Content.Add(Content = new FillFlowContainer
Width = WIDTH; {
RelativeSizeAxes = Axes.X,
FillFlow.Spacing = new Vector2(10); AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
});
} }
} }
} }

View File

@ -5,6 +5,7 @@ namespace osu.Game.Skinning
{ {
public enum SkinnableTarget public enum SkinnableTarget
{ {
MainHUDComponents MainHUDComponents,
SongSelect
} }
} }