Add Context Menu to Bwoink Window (#9374)

* Clean up EntityListDisplay

* Rename EntityListDisplay to ListContainer

* Rename stuff

* Rework ListContainer

* Add tests

* Replace IControlData with record ListData

* Make _data non-nullable

* Fix test record items being duplicates

* Split filter method from populate

* Rename UpdateList to DirtyList

* Replace _count with _data.Count

* Clarify local variable toRemove

* Cleanup

* Add data selection to ListContainer

* Add selection test

* Fix comments and test name

* Fix ListContainer layout hiding items when scaled

* Add test for ListContainer top item

* Toggle fix

* Ensure buttons are re-generated

* Update unread count on select

* a

* Fix toggle test

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
This commit is contained in:
Jacob Tong
2022-09-14 17:03:13 -07:00
committed by GitHub
parent db1dfc8958
commit 09c6a5b252
9 changed files with 793 additions and 374 deletions

View File

@@ -1,9 +1,9 @@
#nullable enable
using System.Linq;
using System.Text;
using System.Threading;
using Content.Client.Administration.Managers;
using Content.Client.Administration.Systems;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.Administration.UI.Tabs.AdminTab;
using Content.Client.Stylesheets;
using Content.Shared.Administration;
@@ -14,6 +14,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Network;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Client.Administration.UI
@@ -48,13 +49,29 @@ namespace Content.Client.Administration.UI
Title = $"{sel.CharacterName} / {sel.Username}";
}
foreach (var li in ChannelSelector.PlayerItemList)
li.Text = FormatTabTitle(li);
ChannelSelector.PlayerListContainer.DirtyList();
};
ChannelSelector.DecoratePlayer += (PlayerInfo pl, ItemList.Item li) =>
ChannelSelector.OverrideText += (info, text) =>
{
li.Text = FormatTabTitle(li, pl);
var sb = new StringBuilder();
sb.Append(info.Connected ? '●' : '○');
sb.Append(' ');
if (_bwoinkSystem.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0)
{
if (panel.Unread < 11)
sb.Append(new Rune('➀' + (panel.Unread-1)));
else
sb.Append(new Rune(0x2639)); // ☹
sb.Append(' ');
}
if (info.Antag)
sb.Append(new Rune(0x1F5E1)); // 🗡
sb.AppendFormat("\"{0}\"", text);
return sb.ToString();
};
ChannelSelector.Comparison = (a, b) =>
@@ -121,18 +138,16 @@ namespace Content.Client.Administration.UI
public void OnBwoink(NetUserId channel)
{
ChannelSelector.RefreshDecorators();
ChannelSelector.Sort();
ChannelSelector.PopulateList();
}
public void SelectChannel(NetUserId channel)
{
var pi = ChannelSelector
.PlayerItemList
.FirstOrDefault(i => ((PlayerInfo) i.Metadata!).SessionId == channel);
if (pi is not null)
pi.Selected = true;
if (!ChannelSelector.PlayerInfo.TryFirstOrDefault(
i => i.SessionId == channel, out var info))
return;
ChannelSelector.PopulateList();
ChannelSelector.PlayerListContainer.Select(new PlayerListData(info));
}
private void FixButtons()

View File

@@ -1,14 +1,18 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:controls="using:Content.Client.UserInterface.Controls"
Orientation="Vertical">
<Control MinSize="0 5" />
<LineEdit Name="FilterLineEdit"
MinSize="100 0"
HorizontalExpand="True"
PlaceHolder="{Loc Filter}"/>
<ItemList Name="PlayerItemList"
Access="Public"
SelectMode="Single"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="100 100" />
<PanelContainer Name="BackgroundPanel"
VerticalExpand="True"
HorizontalExpand="True">
<controls:ListContainer Name="PlayerListContainer"
Access="Public"
Toggle="True"
Group="True"
MinSize="100 0"/>
</PanelContainer>
</BoxContainer>

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.Administration.Systems;
using Content.Client.UserInterface.Controls;
using Content.Client.Verbs;
using Content.Shared.Administration;
using Content.Shared.Input;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Input;
namespace Content.Client.Administration.UI.CustomControls
{
@@ -15,81 +16,107 @@ namespace Content.Client.Administration.UI.CustomControls
public sealed partial class PlayerListControl : BoxContainer
{
private readonly AdminSystem _adminSystem;
private readonly VerbSystem _verbSystem;
private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
public event Action<PlayerInfo?>? OnSelectionChanged;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
public Action<PlayerInfo, ItemList.Item>? DecoratePlayer;
public Func<PlayerInfo, string, string>? OverrideText;
public Comparison<PlayerInfo>? Comparison;
public PlayerListControl()
{
_adminSystem = EntitySystem.Get<AdminSystem>();
_verbSystem = EntitySystem.Get<VerbSystem>();
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
// Fill the Option data
PopulateList();
PlayerItemList.OnItemSelected += PlayerItemListOnOnItemSelected;
PlayerItemList.OnItemDeselected += PlayerItemListOnOnItemDeselected;
FilterLineEdit.OnTextChanged += FilterLineEditOnOnTextEntered;
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.GenerateItem += GenerateButton;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
}
private void FilterLineEditOnOnTextEntered(LineEdit.LineEditEventArgs obj)
private void PlayerListItemPressed(BaseButton.ButtonEventArgs args, ListData data)
{
PopulateList();
}
private void PlayerItemListOnOnItemSelected(ItemList.ItemListSelectedEventArgs obj)
{
var selectedPlayer = (PlayerInfo) obj.ItemList[obj.ItemIndex].Metadata!;
OnSelectionChanged?.Invoke(selectedPlayer);
}
private void PlayerItemListOnOnItemDeselected(ItemList.ItemListDeselectedEventArgs obj)
{
OnSelectionChanged?.Invoke(null);
}
public void RefreshDecorators()
{
foreach (var item in PlayerItemList)
if (data is not PlayerListData {Info: var selectedPlayer})
return;
if (args.Event.Function == EngineKeyFunctions.UIClick)
{
DecoratePlayer?.Invoke((PlayerInfo) item.Metadata!, item);
OnSelectionChanged?.Invoke(selectedPlayer);
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
else if (args.Event.Function == ContentKeyFunctions.OpenContextMenu)
{
_verbSystem.VerbMenu.OpenVerbMenu(selectedPlayer.EntityUid);
}
}
public void Sort()
private void FilterList()
{
if(Comparison != null)
PlayerItemList.Sort((a, b) => Comparison((PlayerInfo) a.Metadata!, (PlayerInfo) b.Metadata!));
}
private void PopulateList(IReadOnlyList<PlayerInfo> _ = null!)
{
PlayerItemList.Clear();
foreach (var info in _adminSystem.PlayerList)
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text) &&
!displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
{
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
}
var item = new ItemList.Item(PlayerItemList)
{
Metadata = info,
Text = displayName
};
DecoratePlayer?.Invoke(info, item);
PlayerItemList.Add(item);
_sortedPlayerList.Add(info);
}
Sort();
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
FilterList();
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
button.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label
{
ClipText = true,
Text = GetText(info)
}
}
});
button.EnableAllKeybinds = true;
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
}
public record PlayerListData(PlayerInfo Info) : ListData;
}