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", () =>
{
skinEditor?.Expire();
Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE);
Player.ScaleTo(0.8f);
LoadComponentAsync(skinEditor = new SkinEditor(Player), Add);
});
}

View File

@ -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,
}));
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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 =>
{

View File

@ -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<Type> 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]

View File

@ -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();

View File

@ -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
/// </summary>
public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler<GlobalAction>
{
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> 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
/// <summary>
/// Set a new target screen which will be used to find skinnable components.
/// </summary>
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);

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.
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<Drawable> 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),
});
}
}
}

View File

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