]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Collapsible ghost roles menu (#32717)
authorMilenVolf <63782763+MilenVolf@users.noreply.github.com>
Mon, 4 Nov 2024 00:49:42 +0000 (03:49 +0300)
committerGitHub <noreply@github.com>
Mon, 4 Nov 2024 00:49:42 +0000 (01:49 +0100)
* Make ghost roles collapsible

* Save `BodyVisible` state of each `Collapsible` box

* Make ghost role collapsible only when group has more than 1 role

* Make it a little prettier

* Make only ghost role buttons collapsible

* Apply requested changes

* Typo

* Small cleanup

* Store in list, instead of iterating

* Make unique ids more unique

* Move it out of the cycle

* Make _collapsibleBoxes into dictionary and use key instead of Collapsible boxes names

Added TODO. So after the problem will be fixed in `GhostRolesEui`, it should be mirrored and fixed here too.

* Put TODO in GhostRolesEui. I guess Issue must be made for this

* Use HashSet instead of Dictionary as suggested. Invert the HashSet, so being present means it uncollapsed

I decided to invert HashSet to _uncollapsedStates, because players surely will have more collapsed buttons than opened, so we optimise memory usage a little bit.

* Remove extra space from ghost roles window

* Add buttons stretching. Size 3:1

Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleButtonsBox.xaml [new file with mode: 0644]
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleButtonsBox.xaml.cs [moved from Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml.cs with 86% similarity]
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleEntryButtons.xaml
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml [new file with mode: 0644]
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml.cs [new file with mode: 0644]
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml [deleted file]
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEui.cs
Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs
Resources/Locale/en-US/ghost/ghost-gui.ftl

diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleButtonsBox.xaml b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleButtonsBox.xaml
new file mode 100644 (file)
index 0000000..32d611e
--- /dev/null
@@ -0,0 +1,9 @@
+<BoxContainer xmlns="https://spacestation14.io"
+              Orientation="Vertical"
+              Margin="8 0 8 0">
+    <BoxContainer Name="Buttons"
+                  Orientation="Vertical"
+                  SeparationOverride="5">
+        <!-- Buttons are added here by code -->
+    </BoxContainer>
+</BoxContainer>
similarity index 86%
rename from Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml.cs
rename to Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleButtonsBox.xaml.cs
index fc53cc72ae6a8e13ed22c992caea648cf92524bd..7df02434160af2ee24986af10c705adc81d06881 100644 (file)
@@ -10,20 +10,17 @@ using Robust.Shared.Utility;
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 {
     [GenerateTypedNameReferences]
-    public sealed partial class GhostRolesEntry : BoxContainer
+    public sealed partial class GhostRoleButtonsBox : BoxContainer
     {
         private SpriteSystem _spriteSystem;
         public event Action<GhostRoleInfo>? OnRoleSelected;
         public event Action<GhostRoleInfo>? OnRoleFollow;
 
-        public GhostRolesEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
+        public GhostRoleButtonsBox(bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
         {
             RobustXamlLoader.Load(this);
             _spriteSystem = spriteSystem;
 
-            Title.Text = name;
-            Description.SetMessage(description);
-
             foreach (var role in roles)
             {
                 var button = new GhostRoleEntryButtons(role);
index ffde5d69f764d3e858819f733c9f327ed6f03627..05c52deef1643ede8fc995a6f8ba180321b63b6f 100644 (file)
@@ -1,15 +1,15 @@
 <BoxContainer xmlns="https://spacestation14.io"
-              Orientation="Horizontal">
+              Orientation="Horizontal"
+              HorizontalAlignment="Stretch">
     <Button Name="RequestButton"
             Access="Public"
             Text="{Loc 'ghost-roles-window-request-role-button'}"
             StyleClasses="OpenRight"
-            HorizontalAlignment="Left"
-            SetWidth="300"/>
+            HorizontalExpand="True"
+            SizeFlagsStretchRatio="3"/>
     <Button Name="FollowButton"
             Access="Public"
             Text="{Loc 'ghost-roles-window-follow-role-button'}"
             StyleClasses="OpenLeft"
-            HorizontalAlignment="Right"
-            SetWidth="150"/>
+            HorizontalExpand="True"/>
 </BoxContainer>
diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml
new file mode 100644 (file)
index 0000000..e24455b
--- /dev/null
@@ -0,0 +1,8 @@
+<BoxContainer xmlns="https://spacestation14.io"
+              Orientation="Vertical">
+    <Label Name="Title"
+           StyleClasses="LabelKeyText"/>
+    <PanelContainer StyleClasses="HighDivider" />
+    <RichTextLabel Name="Description"
+                   Margin="0 4"/>
+</BoxContainer>
diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRoleInfoBox.xaml.cs
new file mode 100644 (file)
index 0000000..705a9f0
--- /dev/null
@@ -0,0 +1,18 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
+{
+    [GenerateTypedNameReferences]
+    public sealed partial class GhostRoleInfoBox : BoxContainer
+    {
+        public GhostRoleInfoBox(string name, string description)
+        {
+            RobustXamlLoader.Load(this);
+
+            Title.Text = name;
+            Description.SetMessage(description);
+        }
+    }
+}
diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesEntry.xaml
deleted file mode 100644 (file)
index d9ed172..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<BoxContainer xmlns="https://spacestation14.io"
-              Orientation="Vertical"
-              HorizontalExpand="True"
-              Margin="0 0 8 8">
-    <Label Name="Title"
-           StyleClasses="LabelKeyText"/>
-    <PanelContainer StyleClasses="HighDivider" />
-    <RichTextLabel Name="Description"
-                   Margin="0 4"/>
-    <BoxContainer Name="Buttons"
-                  HorizontalAlignment="Left"
-                  Orientation="Vertical"
-                  SeparationOverride="5">
-        <!-- Buttons are added here by code -->
-    </BoxContainer>
-</BoxContainer>
index 6b183362e56e26d8e5f74aff539547dc89c70bc3..1cf1e55103d3c2c8a626cd6202c0420f32fa4288 100644 (file)
@@ -5,7 +5,6 @@ using Content.Shared.Eui;
 using Content.Shared.Ghost.Roles;
 using JetBrains.Annotations;
 using Robust.Client.GameObjects;
-using Robust.Shared.Utility;
 
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 {
@@ -77,6 +76,13 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
 
             if (state is not GhostRolesEuiState ghostState)
                 return;
+
+            // We must save BodyVisible state, so all Collapsible boxes will not close
+            // on adding new ghost role.
+            // Save the current state of each Collapsible box being visible or not
+            _window.SaveCollapsibleBoxesStates();
+
+            // Clearing the container before adding new roles
             _window.ClearEntries();
 
             var entityManager = IoCManager.Resolve<IEntityManager>();
@@ -84,28 +90,32 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
             var spriteSystem = sysManager.GetEntitySystem<SpriteSystem>();
             var requirementsManager = IoCManager.Resolve<JobRequirementsManager>();
 
+            // TODO: role.Requirements value doesn't work at all as an equality key, this must be fixed
+            // Grouping roles
             var groupedRoles = ghostState.GhostRoles.GroupBy(
                 role => (role.Name, role.Description, role.Requirements));
+
+            // Add a new entry for each role group
             foreach (var group in groupedRoles)
             {
                 var name = group.Key.Name;
                 var description = group.Key.Description;
-                bool hasAccess = true;
-                FormattedMessage? reason;
-
-                if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, null, out reason))
-                {
-                    hasAccess = false;
-                }
+                var hasAccess = requirementsManager.CheckRoleRequirements(
+                    group.Key.Requirements,
+                    null,
+                    out var reason);
 
+                // Adding a new role
                 _window.AddEntry(name, description, hasAccess, reason, group, spriteSystem);
             }
 
+            // Restore the Collapsible box state if it is saved
+            _window.RestoreCollapsibleBoxesStates();
+
+            // Close the rules window if it is no longer needed
             var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId);
             if (closeRulesWindow)
-            {
                 _windowRules?.Close();
-            }
         }
     }
 }
index 2e7c99641b71acb87a76ad6a3d87834d97ff65d9..627ecfe987a5ee5ad6b33dd62fe79d2115355f0c 100644 (file)
@@ -1,7 +1,10 @@
+using System.Linq;
 using Content.Shared.Ghost.Roles;
 using Robust.Client.AutoGenerated;
 using Robust.Client.GameObjects;
+using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Utility;
 
 namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
@@ -12,20 +15,86 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
         public event Action<GhostRoleInfo>? OnRoleRequestButtonClicked;
         public event Action<GhostRoleInfo>? OnRoleFollow;
 
+        private Dictionary<(string name, string description), Collapsible> _collapsibleBoxes = new();
+        private HashSet<(string name, string description)> _uncollapsedStates = new();
+
+        public GhostRolesWindow()
+        {
+            RobustXamlLoader.Load(this);
+        }
+
         public void ClearEntries()
         {
             NoRolesMessage.Visible = true;
             EntryContainer.DisposeAllChildren();
+            _collapsibleBoxes.Clear();
+        }
+
+        public void SaveCollapsibleBoxesStates()
+        {
+            _uncollapsedStates.Clear();
+            foreach (var (key, collapsible) in _collapsibleBoxes)
+            {
+                if (collapsible.BodyVisible)
+                {
+                    _uncollapsedStates.Add(key);
+                }
+            }
+        }
+
+        public void RestoreCollapsibleBoxesStates()
+        {
+            foreach (var (key, collapsible) in _collapsibleBoxes)
+            {
+                collapsible.BodyVisible = _uncollapsedStates.Contains(key);
+            }
         }
 
         public void AddEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
         {
             NoRolesMessage.Visible = false;
 
-            var entry = new GhostRolesEntry(name, description, hasAccess, reason, roles, spriteSystem);
-            entry.OnRoleSelected += OnRoleRequestButtonClicked;
-            entry.OnRoleFollow += OnRoleFollow;
-            EntryContainer.AddChild(entry);
+            var ghostRoleInfos = roles.ToList();
+            var rolesCount = ghostRoleInfos.Count;
+
+            var info = new GhostRoleInfoBox(name, description);
+            var buttons = new GhostRoleButtonsBox(hasAccess, reason, ghostRoleInfos, spriteSystem);
+            buttons.OnRoleSelected += OnRoleRequestButtonClicked;
+            buttons.OnRoleFollow += OnRoleFollow;
+
+            EntryContainer.AddChild(info);
+
+            if (rolesCount > 1)
+            {
+                var buttonHeading = new CollapsibleHeading(Loc.GetString("ghost-roles-window-available-button", ("rolesCount", rolesCount)));
+
+                buttonHeading.AddStyleClass(ContainerButton.StyleClassButton);
+                buttonHeading.Label.HorizontalAlignment = HAlignment.Center;
+                buttonHeading.Label.HorizontalExpand = true;
+
+                var body = new CollapsibleBody
+                {
+                    Margin = new Thickness(0, 5, 0, 0),
+                };
+
+                // TODO: Add Requirements to this key when it'll be fixed and work as an equality key in GhostRolesEui
+                var key = (name, description);
+
+                var collapsible = new Collapsible(buttonHeading, body)
+                {
+                    Orientation = BoxContainer.LayoutOrientation.Vertical,
+                    Margin = new Thickness(0, 0, 0, 8),
+                };
+
+                body.AddChild(buttons);
+
+                EntryContainer.AddChild(collapsible);
+                _collapsibleBoxes.Add(key, collapsible);
+            }
+            else
+            {
+                EntryContainer.AddChild(buttons);
+            }
         }
     }
 }
index cd4559e1482ea59d94d9b669bc2bb13b3f0ee4a6..7d3939abb13e696e4f6b7ce13c078f762348facd 100644 (file)
@@ -14,6 +14,7 @@ ghost-target-window-current-button = Warp: {$name}
 ghost-target-window-warp-to-most-followed = Warp to Most Followed
 
 ghost-roles-window-title = Ghost Roles
+ghost-roles-window-available-button = Available ({$rolesCount})
 ghost-roles-window-join-raffle-button = Join raffle
 ghost-roles-window-raffle-in-progress-button =
     Join raffle ({$time} left, { $players ->