From 9e476ced63eba9e4ad3e7e2f62d74faf67f079ff Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 15 Mar 2022 16:35:46 +0900
Subject: [PATCH] Add `EditorSidebar` component

---
 .../UserInterface/TestSceneEditorSidebar.cs   | 97 +++++++++++++++++++
 .../Screens/Edit/Components/EditorSidebar.cs  | 52 ++++++++++
 .../Edit/Components/EditorSidebarSection.cs   | 73 ++++++++++++++
 3 files changed, 222 insertions(+)
 create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneEditorSidebar.cs
 create mode 100644 osu.Game/Screens/Edit/Components/EditorSidebar.cs
 create mode 100644 osu.Game/Screens/Edit/Components/EditorSidebarSection.cs

diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneEditorSidebar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneEditorSidebar.cs
new file mode 100644
index 0000000000..7e2b5e0bad
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneEditorSidebar.cs
@@ -0,0 +1,97 @@
+// 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 System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Overlays;
+using osu.Game.Screens.Edit.Components;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+    public class TestSceneEditorSidebar : OsuTestScene
+    {
+        [Cached]
+        private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
+        [Test]
+        public void TestSidebars()
+        {
+            AddStep("Add sidebars", () =>
+            {
+                Children = new Drawable[]
+                {
+                    new GridContainer
+                    {
+                        RelativeSizeAxes = Axes.Both,
+                        ColumnDimensions = new[]
+                        {
+                            new Dimension(GridSizeMode.AutoSize),
+                            new Dimension(),
+                            new Dimension(GridSizeMode.AutoSize),
+                        },
+                        Content = new[]
+                        {
+                            new Drawable[]
+                            {
+                                new EditorSidebar
+                                {
+                                    Children = new[]
+                                    {
+                                        new EditorSidebarSection("Section 1")
+                                        {
+                                            Child = new FillFlowContainer
+                                            {
+                                                RelativeSizeAxes = Axes.X,
+                                                AutoSizeAxes = Axes.Y,
+                                                Direction = FillDirection.Full,
+                                                Spacing = new Vector2(3),
+                                                ChildrenEnumerable = Enumerable.Range(0, 10).Select(_ => new Box
+                                                {
+                                                    Colour = Color4.White,
+                                                    Size = new Vector2(32),
+                                                })
+                                            },
+                                        },
+                                        new EditorSidebarSection("Section 2")
+                                        {
+                                            Child = new FillFlowContainer
+                                            {
+                                                RelativeSizeAxes = Axes.X,
+                                                AutoSizeAxes = Axes.Y,
+                                                Direction = FillDirection.Full,
+                                                Spacing = new Vector2(3),
+                                                ChildrenEnumerable = Enumerable.Range(0, 400).Select(_ => new Box
+                                                {
+                                                    Colour = Color4.Gray,
+                                                    Size = new Vector2(32),
+                                                })
+                                            },
+                                        },
+                                    },
+                                },
+                                new Container
+                                {
+                                    RelativeSizeAxes = Axes.Both,
+                                },
+                                new EditorSidebar
+                                {
+                                    Children = new[]
+                                    {
+                                        new EditorSidebarSection("Section 1"),
+                                        new EditorSidebarSection("Section 2"),
+                                    },
+                                },
+                            }
+                        }
+                    }
+                };
+            });
+        }
+    }
+}
diff --git a/osu.Game/Screens/Edit/Components/EditorSidebar.cs b/osu.Game/Screens/Edit/Components/EditorSidebar.cs
new file mode 100644
index 0000000000..cd7ef98401
--- /dev/null
+++ b/osu.Game/Screens/Edit/Components/EditorSidebar.cs
@@ -0,0 +1,52 @@
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Containers;
+using osu.Game.Overlays;
+
+namespace osu.Game.Screens.Edit.Components
+{
+    /// <summary>
+    /// A sidebar area that can be attached to the left or right edge of the screen.
+    /// Houses scrolling sectionised content.
+    /// </summary>
+    internal class EditorSidebar : Container<EditorSidebarSection>
+    {
+        private readonly Box background;
+
+        protected override Container<EditorSidebarSection> Content { get; }
+
+        public EditorSidebar()
+        {
+            Width = 250;
+            RelativeSizeAxes = Axes.Y;
+
+            InternalChildren = new Drawable[]
+            {
+                background = new Box
+                {
+                    RelativeSizeAxes = Axes.Both,
+                },
+                new OsuScrollContainer
+                {
+                    Padding = new MarginPadding { Left = 20 },
+                    ScrollbarOverlapsContent = false,
+                    RelativeSizeAxes = Axes.Both,
+                    Child = Content = new FillFlowContainer<EditorSidebarSection>
+                    {
+                        RelativeSizeAxes = Axes.X,
+                        AutoSizeAxes = Axes.Y,
+                        Direction = FillDirection.Vertical,
+                    },
+                }
+            };
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OverlayColourProvider colourProvider)
+        {
+            background.Colour = colourProvider.Background5;
+        }
+    }
+}
diff --git a/osu.Game/Screens/Edit/Components/EditorSidebarSection.cs b/osu.Game/Screens/Edit/Components/EditorSidebarSection.cs
new file mode 100644
index 0000000000..5c000471a6
--- /dev/null
+++ b/osu.Game/Screens/Edit/Components/EditorSidebarSection.cs
@@ -0,0 +1,73 @@
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Localisation;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays;
+using osuTK;
+
+namespace osu.Game.Screens.Edit.Components
+{
+    public class EditorSidebarSection : Container
+    {
+        protected override Container<Drawable> Content { get; }
+
+        public EditorSidebarSection(LocalisableString sectionName)
+        {
+            RelativeSizeAxes = Axes.X;
+            AutoSizeAxes = Axes.Y;
+
+            InternalChild = new FillFlowContainer
+            {
+                RelativeSizeAxes = Axes.X,
+                AutoSizeAxes = Axes.Y,
+                Direction = FillDirection.Vertical,
+                Children = new Drawable[]
+                {
+                    new SectionHeader(sectionName),
+                    Content = new FillFlowContainer
+                    {
+                        RelativeSizeAxes = Axes.X,
+                        AutoSizeAxes = Axes.Y,
+                        Direction = FillDirection.Vertical,
+                    },
+                }
+            };
+        }
+
+        public class SectionHeader : CompositeDrawable
+        {
+            private readonly LocalisableString text;
+
+            public SectionHeader(LocalisableString text)
+            {
+                this.text = text;
+
+                Margin = new MarginPadding { Vertical = 10, Horizontal = 5 };
+
+                AutoSizeAxes = Axes.Both;
+            }
+
+            [BackgroundDependencyLoader]
+            private void load(OverlayColourProvider colourProvider)
+            {
+                InternalChildren = new Drawable[]
+                {
+                    new OsuSpriteText
+                    {
+                        Text = text,
+                        Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold),
+                    },
+                    new Circle
+                    {
+                        Y = 18,
+                        Colour = colourProvider.Highlight1,
+                        Size = new Vector2(28, 2),
+                    }
+                };
+            }
+        }
+    }
+}
\ No newline at end of file