Add search filter to the admin menu player tab (#28030)
This commit is contained in:
@@ -16,14 +16,17 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
|
|
||||||
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
||||||
{
|
{
|
||||||
if (sel is not null)
|
if (sel is null)
|
||||||
{
|
{
|
||||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
Title = Loc.GetString("bwoink-none-selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (sel.OverallPlaytime != null)
|
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||||
{
|
|
||||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
if (sel.OverallPlaytime != null)
|
||||||
}
|
{
|
||||||
|
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Content.Client.Administration.UI.CustomControls
|
|||||||
private List<PlayerInfo> _playerList = new();
|
private List<PlayerInfo> _playerList = new();
|
||||||
private readonly List<PlayerInfo> _sortedPlayerList = new();
|
private readonly List<PlayerInfo> _sortedPlayerList = new();
|
||||||
|
|
||||||
public event Action<PlayerInfo>? OnSelectionChanged;
|
public event Action<PlayerInfo?>? OnSelectionChanged;
|
||||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||||
|
|
||||||
public Func<PlayerInfo, string, string>? OverrideText;
|
public Func<PlayerInfo, string, string>? OverrideText;
|
||||||
@@ -41,12 +41,19 @@ namespace Content.Client.Administration.UI.CustomControls
|
|||||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||||
PlayerListContainer.GenerateItem += GenerateButton;
|
PlayerListContainer.GenerateItem += GenerateButton;
|
||||||
|
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
|
||||||
PopulateList(_adminSystem.PlayerList);
|
PopulateList(_adminSystem.PlayerList);
|
||||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||||
_adminSystem.PlayerListChanged += PopulateList;
|
_adminSystem.PlayerListChanged += PopulateList;
|
||||||
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
|
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PlayerListNoItemSelected()
|
||||||
|
{
|
||||||
|
_selectedPlayer = null;
|
||||||
|
OnSelectionChanged?.Invoke(null);
|
||||||
|
}
|
||||||
|
|
||||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||||
{
|
{
|
||||||
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
|
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
<Control xmlns="https://spacestation14.io"
|
<Control xmlns="https://spacestation14.io"
|
||||||
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||||
|
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="PlayerCount" HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
<Label Name="PlayerCount" HorizontalExpand="True" Text="{Loc Player Count}" />
|
||||||
Text="{Loc Player Count}" />
|
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
||||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
PlaceHolder="{Loc player-tab-filter-line-edit-placeholder}" />
|
||||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True"/>
|
<Button Name="ShowDisconnectedButton" HorizontalExpand="True"
|
||||||
<Button Name="OverlayButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
Text="{Loc player-tab-show-disconnected}" ToggleMode="True" />
|
||||||
Text="{Loc player-tab-overlay}" ToggleMode="True"/>
|
<Button Name="OverlayButton" HorizontalExpand="True" Text="{Loc player-tab-overlay}" ToggleMode="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinSize="0 5" />
|
<Control MinSize="0 5"/>
|
||||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
<pt:PlayerTabHeader Name="ListHeader"/>
|
||||||
<BoxContainer Orientation="Vertical" Name="PlayerList">
|
<cc:HSeparator/>
|
||||||
<pt:PlayerTabHeader Name="ListHeader" />
|
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
||||||
<cc:HSeparator />
|
|
||||||
</BoxContainer>
|
|
||||||
</ScrollContainer>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Client.Administration.Systems;
|
using Content.Client.Administration.Systems;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -28,15 +29,14 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
private bool _ascending = true;
|
private bool _ascending = true;
|
||||||
private bool _showDisconnected;
|
private bool _showDisconnected;
|
||||||
|
|
||||||
public event Action<PlayerTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||||
|
|
||||||
public PlayerTab()
|
public PlayerTab()
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_adminSystem = _entManager.System<AdminSystem>();
|
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
RefreshPlayerList(_adminSystem.PlayerList);
|
|
||||||
|
|
||||||
|
_adminSystem = _entManager.System<AdminSystem>();
|
||||||
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
||||||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||||
@@ -46,8 +46,17 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
|
|
||||||
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
||||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||||
|
|
||||||
|
SearchList.SearchBar = SearchLineEdit;
|
||||||
|
SearchList.GenerateItem += GenerateButton;
|
||||||
|
SearchList.DataFilterCondition += DataFilterCondition;
|
||||||
|
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||||
|
|
||||||
|
RefreshPlayerList(_adminSystem.PlayerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Antag Overlay
|
||||||
|
|
||||||
private void OverlayEnabled()
|
private void OverlayEnabled()
|
||||||
{
|
{
|
||||||
OverlayButton.Pressed = true;
|
OverlayButton.Pressed = true;
|
||||||
@@ -70,6 +79,8 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
||||||
{
|
{
|
||||||
_showDisconnected = args.Button.Pressed;
|
_showDisconnected = args.Button.Pressed;
|
||||||
@@ -92,14 +103,10 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region ListContainer
|
||||||
|
|
||||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||||
{
|
{
|
||||||
foreach (var child in PlayerList.Children.ToArray())
|
|
||||||
{
|
|
||||||
if (child is PlayerTabEntry)
|
|
||||||
child.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_players = players;
|
_players = players;
|
||||||
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
||||||
|
|
||||||
@@ -108,29 +115,66 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
|
|
||||||
UpdateHeaderSymbols();
|
UpdateHeaderSymbols();
|
||||||
|
|
||||||
var useAltColor = false;
|
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
||||||
foreach (var player in sortedPlayers)
|
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
||||||
{
|
.ToList());
|
||||||
if (!_showDisconnected && !player.Connected)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var entry = new PlayerTabEntry(player.Username,
|
|
||||||
player.CharacterName,
|
|
||||||
player.IdentityName,
|
|
||||||
player.StartingJob,
|
|
||||||
player.Antag ? "YES" : "NO",
|
|
||||||
new StyleBoxFlat(useAltColor ? _altColor : _defaultColor),
|
|
||||||
player.Connected,
|
|
||||||
player.PlaytimeString);
|
|
||||||
entry.PlayerEntity = player.NetEntity;
|
|
||||||
entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(entry, args);
|
|
||||||
entry.ToolTip = Loc.GetString("player-tab-entry-tooltip");
|
|
||||||
PlayerList.AddChild(entry);
|
|
||||||
|
|
||||||
useAltColor ^= true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GenerateButton(ListData data, ListContainerButton button)
|
||||||
|
{
|
||||||
|
if (data is not PlayerListData { Info: var player})
|
||||||
|
return;
|
||||||
|
|
||||||
|
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
|
||||||
|
button.AddChild(entry);
|
||||||
|
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
|
||||||
|
/// If all characters are lowercase, the comparison ignores case.
|
||||||
|
/// If there is an uppercase character, the comparison is case sensitive.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter"></param>
|
||||||
|
/// <param name="listData"></param>
|
||||||
|
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
|
||||||
|
private bool DataFilterCondition(string filter, ListData listData)
|
||||||
|
{
|
||||||
|
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_showDisconnected && !info.Connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (IsAllLower(filter))
|
||||||
|
{
|
||||||
|
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!playerString.Contains(filter))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAllLower(string input)
|
||||||
|
{
|
||||||
|
foreach (var c in input)
|
||||||
|
{
|
||||||
|
if (char.IsLetter(c) && !char.IsLower(c))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Header
|
||||||
|
|
||||||
private void UpdateHeaderSymbols()
|
private void UpdateHeaderSymbols()
|
||||||
{
|
{
|
||||||
ListHeader.ResetHeaderText();
|
ListHeader.ResetHeaderText();
|
||||||
@@ -174,5 +218,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||||||
|
|
||||||
RefreshPlayerList(_adminSystem.PlayerList);
|
RefreshPlayerList(_adminSystem.PlayerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ContainerButton xmlns="https://spacestation14.io"
|
<PanelContainer xmlns="https://spacestation14.io"
|
||||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||||
<PanelContainer Name="BackgroundColorPanel"/>
|
Name="BackgroundColorPanel">
|
||||||
<BoxContainer Orientation="Horizontal"
|
<BoxContainer Orientation="Horizontal"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
SeparationOverride="4">
|
SeparationOverride="4">
|
||||||
@@ -29,4 +29,4 @@
|
|||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</ContainerButton>
|
</PanelContainer>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Client.AutoGenerated;
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
@@ -6,23 +7,24 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class PlayerTabEntry : ContainerButton
|
public sealed partial class PlayerTabEntry : PanelContainer
|
||||||
{
|
{
|
||||||
public NetEntity? PlayerEntity;
|
public NetEntity? PlayerEntity;
|
||||||
|
|
||||||
public PlayerTabEntry(string username, string character, string identity, string job, string antagonist, StyleBox styleBox, bool connected, string overallPlaytime)
|
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
UsernameLabel.Text = username;
|
UsernameLabel.Text = player.Username;
|
||||||
if (!connected)
|
if (!player.Connected)
|
||||||
UsernameLabel.StyleClasses.Add("Disabled");
|
UsernameLabel.StyleClasses.Add("Disabled");
|
||||||
JobLabel.Text = job;
|
JobLabel.Text = player.StartingJob;
|
||||||
CharacterLabel.Text = character;
|
CharacterLabel.Text = player.CharacterName;
|
||||||
if (identity != character)
|
if (player.IdentityName != player.CharacterName)
|
||||||
CharacterLabel.Text += $" [{identity}]";
|
CharacterLabel.Text += $" [{player.IdentityName}]";
|
||||||
AntagonistLabel.Text = antagonist;
|
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
|
||||||
BackgroundColorPanel.PanelOverride = styleBox;
|
BackgroundColorPanel.PanelOverride = styleBoxFlat;
|
||||||
OverallPlaytimeLabel.Text = overallPlaytime;
|
OverallPlaytimeLabel.Text = player.PlaytimeString;
|
||||||
|
PlayerEntity = player.NetEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"
|
ClipText="True"
|
||||||
Text="{Loc player-tab-playtime}"
|
Text="{Loc player-tab-playtime}"
|
||||||
MouseFilter="Pass"/>
|
MouseFilter="Pass"
|
||||||
|
ToolTip="{Loc player-tab-entry-tooltip}"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ using Robust.Shared.Map;
|
|||||||
|
|
||||||
namespace Content.Client.UserInterface.Controls;
|
namespace Content.Client.UserInterface.Controls;
|
||||||
|
|
||||||
public sealed class ListContainer : Control
|
[Virtual]
|
||||||
|
public class ListContainer : Control
|
||||||
{
|
{
|
||||||
public const string StylePropertySeparation = "separation";
|
public const string StylePropertySeparation = "separation";
|
||||||
public const string StyleClassListContainerButton = "list-container-button";
|
public const string StyleClassListContainerButton = "list-container-button";
|
||||||
@@ -21,9 +22,26 @@ public sealed class ListContainer : Control
|
|||||||
set => _buttonGroup = value ? new ButtonGroup() : null;
|
set => _buttonGroup = value ? new ButtonGroup() : null;
|
||||||
}
|
}
|
||||||
public bool Toggle { get; set; }
|
public bool Toggle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when creating a button on the UI.
|
||||||
|
/// The provided <see cref="ListContainerButton"/> is the generated button that Controls should be parented to.
|
||||||
|
/// </summary>
|
||||||
public Action<ListData, ListContainerButton>? GenerateItem;
|
public Action<ListData, ListContainerButton>? GenerateItem;
|
||||||
public Action<BaseButton.ButtonEventArgs?, ListData?>? ItemPressed;
|
|
||||||
public Action<GUIBoundKeyEventArgs, ListData?>? ItemKeyBindDown;
|
/// <inheritdoc cref="BaseButton.OnPressed"/>
|
||||||
|
public Action<BaseButton.ButtonEventArgs, ListData>? ItemPressed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a KeyBind is pressed on a ListContainerButton.
|
||||||
|
/// </summary>
|
||||||
|
public Action<GUIBoundKeyEventArgs, ListData>? ItemKeyBindDown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the selected item does not exist in the new data when PopulateList is called.
|
||||||
|
/// </summary>
|
||||||
|
public Action? NoItemSelected;
|
||||||
|
|
||||||
public IReadOnlyList<ListData> Data => _data;
|
public IReadOnlyList<ListData> Data => _data;
|
||||||
|
|
||||||
private const int DefaultSeparation = 3;
|
private const int DefaultSeparation = 3;
|
||||||
@@ -72,11 +90,11 @@ public sealed class ListContainer : Control
|
|||||||
_vScrollBar.OnValueChanged += ScrollValueChanged;
|
_vScrollBar.OnValueChanged += ScrollValueChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PopulateList(IReadOnlyList<ListData> data)
|
public virtual void PopulateList(IReadOnlyList<ListData> data)
|
||||||
{
|
{
|
||||||
if ((_itemHeight == 0 || _data is {Count: 0}) && data.Count > 0)
|
if ((_itemHeight == 0 || _data is {Count: 0}) && data.Count > 0)
|
||||||
{
|
{
|
||||||
ListContainerButton control = new(data[0]);
|
ListContainerButton control = new(data[0], 0);
|
||||||
GenerateItem?.Invoke(data[0], control);
|
GenerateItem?.Invoke(data[0], control);
|
||||||
control.Measure(Vector2Helpers.Infinity);
|
control.Measure(Vector2Helpers.Infinity);
|
||||||
_itemHeight = control.DesiredSize.Y;
|
_itemHeight = control.DesiredSize.Y;
|
||||||
@@ -97,7 +115,7 @@ public sealed class ListContainer : Control
|
|||||||
if (_selected != null && !data.Contains(_selected))
|
if (_selected != null && !data.Contains(_selected))
|
||||||
{
|
{
|
||||||
_selected = null;
|
_selected = null;
|
||||||
ItemPressed?.Invoke(null, null);
|
NoItemSelected?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +134,7 @@ public sealed class ListContainer : Control
|
|||||||
if (_buttons.TryGetValue(data, out var button) && Toggle)
|
if (_buttons.TryGetValue(data, out var button) && Toggle)
|
||||||
button.Pressed = true;
|
button.Pressed = true;
|
||||||
_selected = data;
|
_selected = data;
|
||||||
button ??= new ListContainerButton(data);
|
button ??= new ListContainerButton(data, _data.IndexOf(data));
|
||||||
OnItemPressed(new BaseButton.ButtonEventArgs(button,
|
OnItemPressed(new BaseButton.ButtonEventArgs(button,
|
||||||
new GUIBoundKeyEventArgs(EngineKeyFunctions.UIClick, BoundKeyState.Up,
|
new GUIBoundKeyEventArgs(EngineKeyFunctions.UIClick, BoundKeyState.Up,
|
||||||
new ScreenCoordinates(0, 0, WindowId.Main), true, Vector2.Zero, Vector2.Zero)));
|
new ScreenCoordinates(0, 0, WindowId.Main), true, Vector2.Zero, Vector2.Zero)));
|
||||||
@@ -260,7 +278,7 @@ public sealed class ListContainer : Control
|
|||||||
toRemove.Remove(data);
|
toRemove.Remove(data);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
button = new ListContainerButton(data);
|
button = new ListContainerButton(data, i);
|
||||||
button.OnPressed += OnItemPressed;
|
button.OnPressed += OnItemPressed;
|
||||||
button.OnKeyBindDown += args => OnItemKeyBindDown(button, args);
|
button.OnKeyBindDown += args => OnItemKeyBindDown(button, args);
|
||||||
button.ToggleMode = Toggle;
|
button.ToggleMode = Toggle;
|
||||||
@@ -360,11 +378,14 @@ public sealed class ListContainer : Control
|
|||||||
public sealed class ListContainerButton : ContainerButton, IEntityControl
|
public sealed class ListContainerButton : ContainerButton, IEntityControl
|
||||||
{
|
{
|
||||||
public readonly ListData Data;
|
public readonly ListData Data;
|
||||||
|
|
||||||
|
public readonly int Index;
|
||||||
// public PanelContainer Background;
|
// public PanelContainer Background;
|
||||||
|
|
||||||
public ListContainerButton(ListData data)
|
public ListContainerButton(ListData data, int index)
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
|
Index = index;
|
||||||
// AddChild(Background = new PanelContainer
|
// AddChild(Background = new PanelContainer
|
||||||
// {
|
// {
|
||||||
// HorizontalExpand = true,
|
// HorizontalExpand = true,
|
||||||
|
|||||||
68
Content.Client/UserInterface/Controls/SearchListContainer.cs
Normal file
68
Content.Client/UserInterface/Controls/SearchListContainer.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Controls;
|
||||||
|
|
||||||
|
public sealed class SearchListContainer : ListContainer
|
||||||
|
{
|
||||||
|
private LineEdit? _searchBar;
|
||||||
|
private List<ListData> _unfilteredData = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="LineEdit"/> that is used to filter the list data.
|
||||||
|
/// </summary>
|
||||||
|
public LineEdit? SearchBar
|
||||||
|
{
|
||||||
|
get => _searchBar;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_searchBar is not null)
|
||||||
|
_searchBar.OnTextChanged -= FilterList;
|
||||||
|
|
||||||
|
_searchBar = value;
|
||||||
|
|
||||||
|
if (_searchBar is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_searchBar.OnTextChanged += FilterList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs over the ListData to determine if it should pass the filter.
|
||||||
|
/// </summary>
|
||||||
|
public Func<string, ListData, bool>? DataFilterCondition = null;
|
||||||
|
|
||||||
|
public override void PopulateList(IReadOnlyList<ListData> data)
|
||||||
|
{
|
||||||
|
_unfilteredData = data.ToList();
|
||||||
|
FilterList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FilterList(LineEdit.LineEditEventArgs obj)
|
||||||
|
{
|
||||||
|
FilterList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FilterList()
|
||||||
|
{
|
||||||
|
var filterText = SearchBar?.Text;
|
||||||
|
|
||||||
|
if (DataFilterCondition is null || string.IsNullOrEmpty(filterText))
|
||||||
|
{
|
||||||
|
base.PopulateList(_unfilteredData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredData = new List<ListData>();
|
||||||
|
foreach (var data in _unfilteredData)
|
||||||
|
{
|
||||||
|
if (!DataFilterCondition(filterText, data))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
filteredData.Add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.PopulateList(filteredData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -177,12 +177,15 @@ public sealed class AdminUIController : UIController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PlayerTabEntryKeyBindDown(PlayerTabEntry entry, GUIBoundKeyEventArgs args)
|
private void PlayerTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data)
|
||||||
{
|
{
|
||||||
if (entry.PlayerEntity == null)
|
if (data is not PlayerListData {Info: var info})
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var entity = entry.PlayerEntity.Value;
|
if (info.NetEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var entity = info.NetEntity.Value;
|
||||||
var function = args.Function;
|
var function = args.Function;
|
||||||
|
|
||||||
if (function == EngineKeyFunctions.UIClick)
|
if (function == EngineKeyFunctions.UIClick)
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ bwoink-system-typing-indicator = {$players} {$count ->
|
|||||||
} typing...
|
} typing...
|
||||||
|
|
||||||
admin-bwoink-play-sound = Bwoink?
|
admin-bwoink-play-sound = Bwoink?
|
||||||
|
|
||||||
|
bwoink-title-none-selected = None selected
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ player-tab-playtime = Playtime
|
|||||||
player-tab-show-disconnected = Show Disconnected
|
player-tab-show-disconnected = Show Disconnected
|
||||||
player-tab-overlay = Overlay
|
player-tab-overlay = Overlay
|
||||||
player-tab-entry-tooltip = Playtime is displayed in days:hours:minutes.
|
player-tab-entry-tooltip = Playtime is displayed in days:hours:minutes.
|
||||||
|
player-tab-filter-line-edit-placeholder = Filter
|
||||||
|
player-tab-is-antag-yes = YES
|
||||||
|
player-tab-is-antag-no = NO
|
||||||
|
|||||||
Reference in New Issue
Block a user