Skip to content

Commit

Permalink
Collapsible ghost roles menu (#32717)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
MilenVolf authored Nov 4, 2024
1 parent a0ef431 commit cc3a19c
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,17 @@
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Content.Shared.Ghost.Roles;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;

namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{
Expand Down Expand Up @@ -77,35 +76,46 @@ public override void HandleState(EuiStateBase state)

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>();
var sysManager = entityManager.EntitySysManager;
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();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,20 +15,86 @@ public sealed partial class GhostRolesWindow : DefaultWindow
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);
}
}
}
}
1 change: 1 addition & 0 deletions Resources/Locale/en-US/ghost/ghost-gui.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down

0 comments on commit cc3a19c

Please sign in to comment.