From 9a7537cd569c9401efbcda2eca04b4dcbca166a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 19:48:21 +0200 Subject: [PATCH 1/4] Add support for adding new colours to palette --- .../Graphics/UserInterfaceV2/ColourPalette.cs | 131 ++++++++++++------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index d8edd00c16..1956eb045b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -1,12 +1,17 @@ // 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.Collections.Specialized; using System.Linq; 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.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; @@ -36,36 +41,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private FillFlowContainer palette; - private Container placeholder; + private FillFlowContainer palette; + + private IEnumerable colourDisplays => palette.OfType(); [BackgroundDependencyLoader] private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + AutoSizeDuration = fade_duration; + AutoSizeEasing = Easing.OutQuint; - InternalChildren = new Drawable[] + InternalChild = palette = new FillFlowContainer { - palette = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Direction = FillDirection.Full - }, - placeholder = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Text = "(none)", - Font = OsuFont.Default.With(weight: FontWeight.Bold) - } - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Full }; } @@ -73,30 +66,20 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Colours.BindCollectionChanged((_, args) => updatePalette(args), true); + Colours.BindCollectionChanged((_, args) => + { + if (args.Action != NotifyCollectionChangedAction.Replace) + updatePalette(); + }, true); FinishTransforms(true); } private const int fade_duration = 200; - private void updatePalette(NotifyCollectionChangedEventArgs args) + private void updatePalette() { - if (args.Action == NotifyCollectionChangedAction.Replace) - return; - palette.Clear(); - if (Colours.Any()) - { - palette.FadeIn(fade_duration, Easing.OutQuint); - placeholder.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - palette.FadeOut(fade_duration, Easing.OutQuint); - placeholder.FadeIn(fade_duration, Easing.OutQuint); - } - for (int i = 0; i < Colours.Count; ++i) { // copy to avoid accesses to modified closure. @@ -111,6 +94,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); } + palette.Add(new AddColourButton + { + Action = () => Colours.Add(Colour4.White) + }); + reindexItems(); } @@ -118,11 +106,74 @@ namespace osu.Game.Graphics.UserInterfaceV2 { int index = 1; - foreach (var colour in palette) + foreach (var colourDisplay in colourDisplays) { - colour.ColourName = $"{colourNamePrefix} {index}"; + colourDisplay.ColourName = $"{colourNamePrefix} {index}"; index += 1; } } + + private class AddColourButton : CompositeDrawable + { + public Action Action + { + set => circularButton.Action = value; + } + + private readonly OsuClickableContainer circularButton; + + public AddColourButton() + { + AutoSizeAxes = Axes.Y; + Width = 100; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + circularButton = new OsuClickableContainer + { + RelativeSizeAxes = Axes.X, + Height = 100, + CornerRadius = 50, + Masking = true, + BorderThickness = 5, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + AlwaysPresent = true + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Icon = FontAwesome.Solid.Plus + } + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "New" + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + circularButton.BorderColour = colours.BlueDarker; + } + } } } From 3f005886d66e1c4c19e7fd60e5589861ec616cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:02:39 +0200 Subject: [PATCH 2/4] Add support for removing colours from palette --- .../TestSceneLabelledColourPalette.cs | 19 ++-- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 103 +++++++++++------- .../Graphics/UserInterfaceV2/ColourPalette.cs | 3 + 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index d7af47a835..bbb5ebd0d9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; @@ -34,17 +35,21 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - Child = new Container + Child = new OsuContextMenuContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 500, - AutoSizeAxes = Axes.Y, - Child = component = new LabelledColourPalette + RelativeSizeAxes = Axes.Both, + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - ColourNamePrefix = "My colour #" + Width = 500, + AutoSizeAxes = Axes.Y, + Child = component = new LabelledColourPalette + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColourNamePrefix = "My colour #" + } } }; diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 25f89fdcaa..85e18a6865 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 @@ -19,12 +21,17 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue, IHasPopover + public class ColourDisplay : CompositeDrawable, IHasCurrentValue { + /// + /// Invoked when the user has requested the colour corresponding to this + /// to be removed from its palette. + /// + public event Action DeleteRequested; + private readonly BindableWithCurrent current = new BindableWithCurrent(); - private Box fill; - private OsuSpriteText colourHexCode; + private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -63,26 +70,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - new OsuClickableContainer + colourCircle = new ColourCircle { - RelativeSizeAxes = Axes.X, - Height = 100, - CornerRadius = 50, - Masking = true, - Children = new Drawable[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both - }, - colourHexCode = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Default.With(size: 12) - } - }, - Action = this.ShowPopover + Current = { BindTarget = Current }, + DeleteRequested = () => DeleteRequested?.Invoke(this) }, colourName = new OsuSpriteText { @@ -93,26 +84,64 @@ namespace osu.Game.Graphics.UserInterfaceV2 }; } - protected override void LoadComplete() + private class ColourCircle : OsuClickableContainer, IHasPopover, IHasContextMenu { - base.LoadComplete(); + public Bindable Current { get; } = new Bindable(); - current.BindValueChanged(_ => updateColour(), true); - } + public Action DeleteRequested { get; set; } - private void updateColour() - { - fill.Colour = current.Value; - colourHexCode.Text = current.Value.ToHex(); - colourHexCode.Colour = OsuColour.ForegroundTextColourFor(current.Value); - } + private readonly Box fill; + private readonly OsuSpriteText colourHexCode; - public Popover GetPopover() => new OsuPopover(false) - { - Child = new OsuColourPicker + public ColourCircle() { - Current = { BindTarget = Current } + RelativeSizeAxes = Axes.X; + Height = 100; + CornerRadius = 50; + Masking = true; + Action = this.ShowPopover; + + Children = new Drawable[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both + }, + colourHexCode = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Default.With(size: 12) + } + }; } - }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateColour(), true); + } + + private void updateColour() + { + fill.Colour = Current.Value; + colourHexCode.Text = Current.Value.ToHex(); + colourHexCode.Colour = OsuColour.ForegroundTextColourFor(Current.Value); + } + + public Popover GetPopover() => new OsuPopover(false) + { + Child = new OsuColourPicker + { + Current = { BindTarget = Current } + } + }; + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke()) + }; + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 1956eb045b..02d03bf07e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -92,6 +92,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 }); display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); + display.DeleteRequested += colourDeletionRequested; } palette.Add(new AddColourButton @@ -102,6 +103,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 reindexItems(); } + private void colourDeletionRequested(ColourDisplay display) => Colours.RemoveAt(palette.IndexOf(display)); + private void reindexItems() { int index = 1; From 4334121e8ecff738618e04006e89f6dcd61ae549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:14:42 +0200 Subject: [PATCH 3/4] Add testing for colour palette behaviour --- .../TestSceneLabelledColourPalette.cs | 53 ++++++++++++++++++- .../Graphics/UserInterfaceV2/ColourPalette.cs | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index bbb5ebd0d9..6fafb8f87a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -1,17 +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.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLabelledColourPalette : OsuTestScene + public class TestSceneLabelledColourPalette : OsuManualInputManagerTestScene { private LabelledColourPalette component; @@ -31,6 +35,22 @@ namespace osu.Game.Tests.Visual.UserInterface }, 8); } + [Test] + public void TestUserInteractions() + { + createColourPalette(); + assertColourCount(4); + + clickAddColour(); + assertColourCount(5); + + deleteFirstColour(); + assertColourCount(4); + + clickFirstColour(); + AddAssert("colour picker spawned", () => this.ChildrenOfType().Any()); + } + private void createColourPalette(bool hasDescription = false) { AddStep("create component", () => @@ -71,5 +91,36 @@ namespace osu.Game.Tests.Visual.UserInterface RNG.NextSingle(), RNG.NextSingle(), 1); + + private void assertColourCount(int count) => AddAssert($"colour count is {count}", () => component.Colours.Count == count); + + private void clickAddColour() => AddStep("click new colour button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + private void clickFirstColour() => AddStep("click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + private void deleteFirstColour() + { + AddStep("right-click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Right); + }); + + AddUntilStep("wait for menu", () => this.ChildrenOfType().Any()); + + AddStep("click delete", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 02d03bf07e..a966f61b74 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private class AddColourButton : CompositeDrawable + internal class AddColourButton : CompositeDrawable { public Action Action { From 708b50fdba0016e950b1a4446e35534f50bf3720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 00:11:56 +0200 Subject: [PATCH 4/4] Remove unused field --- osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 85e18a6865..5240df74a2 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -31,7 +31,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private readonly BindableWithCurrent current = new BindableWithCurrent(); - private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -70,7 +69,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - colourCircle = new ColourCircle + new ColourCircle { Current = { BindTarget = Current }, DeleteRequested = () => DeleteRequested?.Invoke(this)