Files
tbd-station-14/Content.Client/Voting/UI/VoteCallMenu.xaml.cs
Brandon Li 545cacbcae StyleNano removal: Palette system and Sheetlets (#29903)
* Apply patch 1777eea9a4..6b32bb2b14

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* make red squiggly line go away

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Add todo list

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Add palette to `TextureButton`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Rename `PalettedButtonSheetlet` to `NTButtonSheetlet` and move useful methods to `ButtonSheetlet`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* migrate `ContextMenu` styles

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Update todo

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* tweak NT colors

* New stylesheet: `InterfaceStylesheet` & `InterfaceTooltipSheetlet`

* Move inheritance of `IButtonConfig` to `NanotransenStylesheet.Buttons`

* move `MenuButtonSheetlet` & actually implement `InterfaceStylesheet` correctly

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* tweak color & update todo

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* chat is this real (update chat palette)

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Update todo

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `SmallButton` and remove some obsolete things from `StyleNano`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* rename `StyleClasses` to `StyleClass` so `Stylesheets.Redux.StyleClasses` syntax is dead

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* replace `ButtonColorGreen` with `Positive`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `Placeholder`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Examine popup buttons

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* move over more things & cleanup `StyleNano` more (under 1000 lines!!!!)

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Remove some more redundant stuff

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Undo style change for chat window

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* paper editing works now

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `OptionButton` styles

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `ListContainer`, move `DefaultWindow` styles (for now) & more cleanup

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* fix `ActionButton` not having highlighting

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* remove imports of `Robust.Client.UserInterface.StylesheetHelpers` & format

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `ButtonBig` and more cleanup

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Move items inheriting from `ISheetletConfig` into their own directory

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Cleanup & move `Label` styles

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Action search box styles

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Moved, stuff is

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* rename `LabelSubtext` to `LabelSubText` & move more stuff (were almost there!!)

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* yap & move over MORE stuff (just like one thing left!!!)

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Change status classes to appropriate existing classes

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* remove remaining references to `StyleNano`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Fix some hardcoding & broken code, `GetFromControl`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Scrollbars!

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* chores

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* clean up `StyleClass.cs`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `ItemListSheetlet` refactor

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* more chores!

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Consistency w/ directory structure

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Move `MainMenuSheetlet`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `ColorPalette`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* whoopsie

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Remove most sheet-specific sheetlets

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* fix warnings, cleanup, & fix scrollbar (this is why we fix warnings boys)

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* yap

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* MASSIVE resharper skill issue

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* actually use `ISheetletConfig`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* have specific sheetlet be specific

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `GetResourceOr`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* cleanup & move / remove `IPalette`s

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* actually do specific stylesheets correctly & fix tooltips

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* cleanup & logging

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* Move `FontKind` and `FontKindExtensions` to their own files

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* rename `InterfaceStylesheet` to `SystemStylesheet`

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* change `ButtonHovered` etc to `PseudoHovered` etc

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* give the palettes fun names

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* `StyleSpace` is no more

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* It should compile now! I am now going to bed (fr) if it fails it fails

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* make squiggly red line go away

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>

* add additional type restrictions to sheetlets

* `CommonStylesheet`

* minor cleanup

* Make `GetSheetletRules` not horrible

* wait this was duplicating style rules. oops!

* move some sheetlets to their associated xamls

* oh wait apparently that was important

* review pass 1

* review pass 2 (font & color stuff)

* review pass 3: remove unused stuff / filename fix

* fix warnings & "replace cast with explicit variable type"

* move `Palette` stuff to its own directory

* tweak colors (they're different now that I actually fixed the OKlab thing)

* review pass 4: little things

* make window close button grey before hovering

* refactor `HLine` to make it less terrible and allow it to be styled

* fix `NanoHeading` (it's been broken for a while whoops) and cleanup hardcoding

* band-aid missing references in `StyleNano`

* move `StyleBox` generating functions out of `IButtonSheetlet` into `StyleBoxHelper`

* remove dictionary field from `IStylesheetManager`

* Add check for unloaded sheetlets

* style tweaks to satisfy OCD

* I somehow missed this: `Caution` styleclass replaced with `negative`, refactor `PowerChargeWindow`

* tweak palettes for like the fourth time

* construct `StyleNano` / `StyleSpace` in `StylesheetManager` and mark them as obsolete

* rename `BackgroundPanel` classes for consistency

* tweak window / `ListContainer`

* oh right you use `///` not `/**`

* font system is bad, make it temporary

* acknowledge Divider funkyness

* remove use of class `Disabled`

* `ColorPalette` allow overriding colors with brace initialization

* review pass again

* tweak disabled button colors

* `StatusPalette` tweaks

* typo

* Make squiggly red line go away

* Delete `Redux`

* Remove all references to `Redux`

* make red less radioactive

* Store stylesheet name inside stylesheet class

* fix merge errors

* use RT's Oklab support instead

* shuffle around `StylesheetManager` fields

* apply stylesheets based off `StylesheetComponent`

* simplify `ColorPalette` construction

* add todo for `SheetletConfigType`

* `OptionButton` has a background color now

* fix disabled buttons

* sigh (red color palette fixed)

* make `ItemList` use primary palette

* Revert "apply stylesheets based off `StylesheetComponent`"

This reverts commit c05b147da845f6e04ff33d1cbd91a18a92c676d7.

* dead code removal

* buttons are green when pressed (we need togglebuttons)

---------

Signed-off-by: Brandon Li <sirbrandonthenerd@gmail.com>
Co-authored-by: Janet Blackquill <uhhadd@gmail.com>
2025-10-19 21:10:44 +00:00

303 lines
11 KiB
C#

using System.Linq;
using System.Numerics;
using Content.Client.Gameplay;
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.Voting;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.State;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Network;
using Robust.Shared.Timing;
namespace Content.Client.Voting.UI
{
[GenerateTypedNameReferences]
public sealed partial class VoteCallMenu : BaseWindow
{
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IVoteManager _voteManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntityNetworkManager _entNetManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IStateManager _state = default!;
private VotingSystem _votingSystem;
public StandardVoteType Type;
public Dictionary<StandardVoteType, CreateVoteOption> AvailableVoteOptions = new Dictionary<StandardVoteType, CreateVoteOption>()
{
{ StandardVoteType.Restart, new CreateVoteOption("ui-vote-type-restart", new(), false, null) },
{ StandardVoteType.Preset, new CreateVoteOption("ui-vote-type-gamemode", new(), false, null) },
{ StandardVoteType.Map, new CreateVoteOption("ui-vote-type-map", new(), false, null) },
{ StandardVoteType.Votekick, new CreateVoteOption("ui-vote-type-votekick", new(), true, 0) }
};
public Dictionary<string, string> VotekickReasons = new Dictionary<string, string>()
{
{ VotekickReasonType.Raiding.ToString(), Loc.GetString("ui-vote-votekick-type-raiding") },
{ VotekickReasonType.Cheating.ToString(), Loc.GetString("ui-vote-votekick-type-cheating") },
{ VotekickReasonType.Spam.ToString(), Loc.GetString("ui-vote-votekick-type-spamming") }
};
public Dictionary<NetUserId, (NetEntity, string)> PlayerList = new();
public OptionButton? _followDropdown = null;
public bool IsAllowedVotekick = false;
public VoteCallMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_votingSystem = _entityManager.System<VotingSystem>();
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSystem;
CloseButton.OnPressed += _ => Close();
VoteNotTrustedLabel.Text = Loc.GetString("ui-vote-trusted-users-notice", ("timeReq", _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime)));
foreach (StandardVoteType voteType in Enum.GetValues<StandardVoteType>())
{
var option = AvailableVoteOptions[voteType];
VoteTypeButton.AddItem(Loc.GetString(option.Name), (int)voteType);
}
_state.OnStateChanged += OnStateChanged;
VoteTypeButton.OnItemSelected += VoteTypeSelected;
CreateButton.OnPressed += CreatePressed;
FollowButton.OnPressed += FollowSelected;
}
protected override void Opened()
{
base.Opened();
_netManager.ClientSendMessage(new MsgVoteMenu());
_voteManager.CanCallVoteChanged += CanCallVoteChanged;
_votingSystem.VotePlayerListResponse += UpdateVotePlayerList;
_votingSystem.RequestVotePlayerList();
}
public override void Close()
{
base.Close();
_voteManager.CanCallVoteChanged -= CanCallVoteChanged;
_votingSystem.VotePlayerListResponse -= UpdateVotePlayerList;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateVoteTimeout();
}
private void OnStateChanged(StateChangedEventArgs obj)
{
if (obj.NewState is not GameplayState)
return;
Close();
}
private void CanCallVoteChanged(bool obj)
{
if (!obj)
Close();
}
private void UpdateVotePlayerList(VotePlayerListResponseEvent msg)
{
Dictionary<string, string> optionsList = new();
Dictionary<NetUserId, (NetEntity, string)> playerList = new();
foreach ((NetUserId, NetEntity, string) player in msg.Players)
{
optionsList.Add(player.Item1.ToString(), player.Item3);
playerList.Add(player.Item1, (player.Item2, player.Item3));
}
if (optionsList.Count == 0)
optionsList.Add(" ", " ");
PlayerList = playerList;
IsAllowedVotekick = !msg.Denied;
var updatedDropdownOption = AvailableVoteOptions[StandardVoteType.Votekick];
updatedDropdownOption.Dropdowns = new List<Dictionary<string, string>>() { optionsList, VotekickReasons };
AvailableVoteOptions[StandardVoteType.Votekick] = updatedDropdownOption;
}
private void CreatePressed(BaseButton.ButtonEventArgs obj)
{
var typeId = VoteTypeButton.SelectedId;
var voteType = AvailableVoteOptions[(StandardVoteType)typeId];
var commandArgs = "";
if (voteType.Dropdowns == null || voteType.Dropdowns.Count == 0)
{
_consoleHost.LocalShell.RemoteExecuteCommand($"createvote {((StandardVoteType)typeId).ToString()}");
}
else
{
int i = 0;
foreach(var dropdowns in VoteOptionsButtonContainer.Children)
{
if (dropdowns is OptionButton optionButton && AvailableVoteOptions[(StandardVoteType)typeId].Dropdowns != null)
{
commandArgs += AvailableVoteOptions[(StandardVoteType)typeId].Dropdowns[i].ElementAt(optionButton.SelectedId).Key + " ";
i++;
}
}
_consoleHost.LocalShell.RemoteExecuteCommand($"createvote {((StandardVoteType)typeId).ToString()} {commandArgs}");
}
Close();
}
private void UpdateVoteTimeout()
{
var typeKey = (StandardVoteType)VoteTypeButton.SelectedId;
var isAvailable = _voteManager.CanCallStandardVote(typeKey, out var timeout);
if (typeKey == StandardVoteType.Votekick && !IsAllowedVotekick)
{
CreateButton.Disabled = true;
}
else
{
CreateButton.Disabled = !isAvailable;
}
VoteTypeTimeoutLabel.Visible = !isAvailable;
if (!isAvailable)
{
if (timeout == TimeSpan.Zero)
{
VoteTypeTimeoutLabel.Text = Loc.GetString("ui-vote-type-not-available");
}
else
{
var remaining = timeout - _gameTiming.RealTime;
VoteTypeTimeoutLabel.Text = Loc.GetString("ui-vote-type-timeout", ("remaining", remaining.ToString("mm\\:ss")));
}
}
}
private static void ButtonSelected(OptionButton.ItemSelectedEventArgs obj)
{
obj.Button.SelectId(obj.Id);
}
private void FollowSelected(Button.ButtonEventArgs obj)
{
if (_followDropdown == null)
return;
if (_followDropdown.SelectedId >= PlayerList.Count)
return;
var netEntity = PlayerList.ElementAt(_followDropdown.SelectedId).Value.Item1;
var msg = new GhostWarpToTargetRequestEvent(netEntity);
_entNetManager.SendSystemNetworkMessage(msg);
}
private void VoteTypeSelected(OptionButton.ItemSelectedEventArgs obj)
{
VoteTypeButton.SelectId(obj.Id);
VoteNotTrustedLabel.Visible = false;
if ((StandardVoteType)obj.Id == StandardVoteType.Votekick)
{
if (!IsAllowedVotekick)
{
VoteNotTrustedLabel.Visible = true;
var updatedDropdownOption = AvailableVoteOptions[StandardVoteType.Votekick];
updatedDropdownOption.Dropdowns = new List<Dictionary<string, string>>();
AvailableVoteOptions[StandardVoteType.Votekick] = updatedDropdownOption;
}
else
{
_votingSystem.RequestVotePlayerList();
}
}
VoteWarningLabel.Visible = AvailableVoteOptions[(StandardVoteType)obj.Id].EnableVoteWarning;
FollowButton.Visible = false;
var voteList = AvailableVoteOptions[(StandardVoteType)obj.Id].Dropdowns;
VoteOptionsButtonContainer.RemoveAllChildren();
if (voteList != null)
{
int i = 0;
foreach (var voteDropdown in voteList)
{
var optionButton = new OptionButton();
int j = 0;
foreach (var (key, value) in voteDropdown)
{
optionButton.AddItem(Loc.GetString(value), j);
j++;
}
VoteOptionsButtonContainer.AddChild(optionButton);
optionButton.Visible = true;
optionButton.OnItemSelected += ButtonSelected;
optionButton.Margin = new Thickness(2, 1);
if (AvailableVoteOptions[(StandardVoteType)obj.Id].FollowDropdownId != null && AvailableVoteOptions[(StandardVoteType)obj.Id].FollowDropdownId == i)
{
_followDropdown = optionButton;
FollowButton.Visible = true;
}
i++;
}
}
}
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
{
return DragMode.Move;
}
}
[UsedImplicitly, AnyCommand]
public sealed class VoteMenuCommand : LocalizedCommands
{
public override string Command => "votemenu";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
new VoteCallMenu().OpenCentered();
}
}
public record struct CreateVoteOption
{
public string Name;
public List<Dictionary<string, string>> Dropdowns;
public bool EnableVoteWarning;
public int? FollowDropdownId; // If set, this will enable the Follow button and use the dropdown matching the ID as input.
public CreateVoteOption(string name, List<Dictionary<string, string>> dropdowns, bool enableVoteWarning, int? followDropdownId)
{
Name = name;
Dropdowns = dropdowns;
EnableVoteWarning = enableVoteWarning;
FollowDropdownId = followDropdownId;
}
}
}