diff --git a/Content.Client/Administration/UI/BanList/BanListEui.cs b/Content.Client/Administration/UI/BanList/BanListEui.cs index 2fca1dee52..00b27cd173 100644 --- a/Content.Client/Administration/UI/BanList/BanListEui.cs +++ b/Content.Client/Administration/UI/BanList/BanListEui.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System.Linq; +using System.Numerics; using Content.Client.Administration.UI.BanList.Bans; using Content.Client.Administration.UI.BanList.RoleBans; using Content.Client.Eui; @@ -73,7 +74,7 @@ public sealed class BanListEui : BaseEui return date.ToString("MM/dd/yyyy h:mm tt"); } - public static void SetData(IBanListLine line, SharedServerBan ban) where T : SharedServerBan + public static void SetData(IBanListLine line, SharedBan ban) where T : SharedBan { line.Reason.Text = ban.Reason; line.BanTime.Text = FormatDate(ban.BanTime); @@ -94,20 +95,20 @@ public sealed class BanListEui : BaseEui line.BanningAdmin.Text = ban.BanningAdminName; } - private void OnLineIdsClicked(IBanListLine line) where T : SharedServerBan + private void OnLineIdsClicked(IBanListLine line) where T : SharedBan { _popup?.Close(); _popup = null; var ban = line.Ban; var id = ban.Id == null ? string.Empty : Loc.GetString("ban-list-id", ("id", ban.Id.Value)); - var ip = ban.Address == null + var ip = ban.Addresses.Length == 0 ? string.Empty - : Loc.GetString("ban-list-ip", ("ip", ban.Address.Value.address)); - var hwid = ban.HWId == null ? string.Empty : Loc.GetString("ban-list-hwid", ("hwid", ban.HWId)); - var guid = ban.UserId == null + : Loc.GetString("ban-list-ip", ("ip", string.Join(',', ban.Addresses.Select(a => a.address)))); + var hwid = ban.HWIds.Length == 0 ? string.Empty : Loc.GetString("ban-list-hwid", ("hwid", string.Join(',', ban.HWIds))); + var guid = ban.UserIds.Length == 0 ? string.Empty - : Loc.GetString("ban-list-guid", ("guid", ban.UserId.Value.ToString())); + : Loc.GetString("ban-list-guid", ("guid", string.Join(',', ban.UserIds))); _popup = new BanListIdsPopup(id, ip, hwid, guid); diff --git a/Content.Client/Administration/UI/BanList/Bans/BanListControl.xaml.cs b/Content.Client/Administration/UI/BanList/Bans/BanListControl.xaml.cs index 431087568a..a79fc4a137 100644 --- a/Content.Client/Administration/UI/BanList/Bans/BanListControl.xaml.cs +++ b/Content.Client/Administration/UI/BanList/Bans/BanListControl.xaml.cs @@ -16,7 +16,7 @@ public sealed partial class BanListControl : Control RobustXamlLoader.Load(this); } - public void SetBans(List bans) + public void SetBans(List bans) { for (var i = Bans.ChildCount - 1; i >= 1; i--) { diff --git a/Content.Client/Administration/UI/BanList/Bans/BanListLine.xaml.cs b/Content.Client/Administration/UI/BanList/Bans/BanListLine.xaml.cs index 0c4e6e60d0..f1320ef7b9 100644 --- a/Content.Client/Administration/UI/BanList/Bans/BanListLine.xaml.cs +++ b/Content.Client/Administration/UI/BanList/Bans/BanListLine.xaml.cs @@ -7,13 +7,13 @@ using static Robust.Client.UserInterface.Controls.BaseButton; namespace Content.Client.Administration.UI.BanList.Bans; [GenerateTypedNameReferences] -public sealed partial class BanListLine : BoxContainer, IBanListLine +public sealed partial class BanListLine : BoxContainer, IBanListLine { - public SharedServerBan Ban { get; } + public SharedBan Ban { get; } public event Action? IdsClicked; - public BanListLine(SharedServerBan ban) + public BanListLine(SharedBan ban) { RobustXamlLoader.Load(this); diff --git a/Content.Client/Administration/UI/BanList/IBanListLine.cs b/Content.Client/Administration/UI/BanList/IBanListLine.cs index 097bae15df..565e707218 100644 --- a/Content.Client/Administration/UI/BanList/IBanListLine.cs +++ b/Content.Client/Administration/UI/BanList/IBanListLine.cs @@ -3,7 +3,7 @@ using Robust.Client.UserInterface.Controls; namespace Content.Client.Administration.UI.BanList; -public interface IBanListLine where T : SharedServerBan +public interface IBanListLine where T : SharedBan { T Ban { get; } Label Reason { get; } diff --git a/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListControl.xaml.cs b/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListControl.xaml.cs index 1ea751deb7..f217dec5e6 100644 --- a/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListControl.xaml.cs +++ b/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListControl.xaml.cs @@ -16,7 +16,7 @@ public sealed partial class RoleBanListControl : Control RobustXamlLoader.Load(this); } - public void SetRoleBans(List bans) + public void SetRoleBans(List bans) { for (var i = RoleBans.ChildCount - 1; i >= 1; i--) { diff --git a/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListLine.xaml.cs b/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListLine.xaml.cs index 4f77d662e1..ca0d214e31 100644 --- a/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListLine.xaml.cs +++ b/Content.Client/Administration/UI/BanList/RoleBans/RoleBanListLine.xaml.cs @@ -7,13 +7,13 @@ using static Robust.Client.UserInterface.Controls.BaseButton; namespace Content.Client.Administration.UI.BanList.RoleBans; [GenerateTypedNameReferences] -public sealed partial class RoleBanListLine : BoxContainer, IBanListLine +public sealed partial class RoleBanListLine : BoxContainer, IBanListLine { - public SharedServerRoleBan Ban { get; } + public SharedBan Ban { get; } public event Action? IdsClicked; - public RoleBanListLine(SharedServerRoleBan ban) + public RoleBanListLine(SharedBan ban) { RobustXamlLoader.Load(this); @@ -21,7 +21,7 @@ public sealed partial class RoleBanListLine : BoxContainer, IBanListLine?)Note.BannedRoles ?? [new BanRoleDef("what", "You should not be seeing this")]); + + var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {rolesText} "); return FormatBanMessageCommon(banMessage); } diff --git a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml.cs b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml.cs index 18a5003158..e82b85acb6 100644 --- a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml.cs +++ b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml.cs @@ -32,9 +32,9 @@ public sealed partial class AdminNotesLinePopup : Popup IdLabel.Text = Loc.GetString("admin-notes-id", ("id", note.Id)); TypeLabel.Text = Loc.GetString("admin-notes-type", ("type", note.NoteType)); SeverityLabel.Text = Loc.GetString("admin-notes-severity", ("severity", note.NoteSeverity ?? NoteSeverity.None)); - RoundIdLabel.Text = note.Round == null + RoundIdLabel.Text = note.Rounds.Length == 0 ? Loc.GetString("admin-notes-round-id-unknown") - : Loc.GetString("admin-notes-round-id", ("id", note.Round)); + : Loc.GetString("admin-notes-round-id", ("id", string.Join(',', note.Rounds))); CreatedByLabel.Text = Loc.GetString("admin-notes-created-by", ("author", note.CreatedByName)); CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"))); EditedByLabel.Text = Loc.GetString("admin-notes-last-edited-by", ("author", note.EditedByName)); diff --git a/Content.Client/Body/Systems/MetabolizerSystem.cs b/Content.Client/Body/Systems/MetabolizerSystem.cs deleted file mode 100644 index 2a0ba4606d..0000000000 --- a/Content.Client/Body/Systems/MetabolizerSystem.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Content.Shared.Body.Systems; - -namespace Content.Client.Body.Systems; - -/// -public sealed class MetabolizerSystem : SharedMetabolizerSystem; diff --git a/Content.Client/Body/VisualBodySystem.cs b/Content.Client/Body/VisualBodySystem.cs index 724dd22017..fba936ee58 100644 --- a/Content.Client/Body/VisualBodySystem.cs +++ b/Content.Client/Body/VisualBodySystem.cs @@ -232,6 +232,9 @@ public sealed class VisualBodySystem : SharedVisualBodySystem private void OnMarkingsChangedVisibility(Entity ent, ref BodyRelayedEvent args) { + if (!ent.Comp.HideableLayers.Contains(args.Args.Layer)) + return; + foreach (var markings in ent.Comp.Markings.Values) { foreach (var marking in markings) @@ -239,7 +242,7 @@ public sealed class VisualBodySystem : SharedVisualBodySystem if (!_marking.TryGetMarking(marking, out var proto)) continue; - if (proto.BodyPart != args.Args.Layer) + if (proto.BodyPart != args.Args.Layer && !(ent.Comp.DependentHidingLayers.TryGetValue(args.Args.Layer, out var dependent) && dependent.Contains(proto.BodyPart))) continue; foreach (var sprite in proto.Sprites) diff --git a/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs index 3bd220bfad..9cd614de14 100644 --- a/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs +++ b/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs @@ -70,9 +70,9 @@ namespace Content.Client.Cargo.BUI _menu.OnClose += Close; - _menu.OnItemSelected += (args) => + _menu.OnItemSelected += (row) => { - if (args.Button.Parent is not CargoProductRow row) + if (row == null) return; description.Clear(); @@ -175,23 +175,23 @@ namespace Content.Client.Cargo.BUI return true; } - private void RemoveOrder(ButtonEventArgs args) + private void RemoveOrder(CargoOrderData? order) { - if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null) + if (order == null) return; - SendMessage(new CargoConsoleRemoveOrderMessage(row.Order.OrderId)); + SendMessage(new CargoConsoleRemoveOrderMessage(order.OrderId)); } - private void ApproveOrder(ButtonEventArgs args) + private void ApproveOrder(CargoOrderData? order) { - if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null) + if (order == null) return; if (OrderCount >= OrderCapacity) return; - SendMessage(new CargoConsoleApproveOrderMessage(row.Order.OrderId)); + SendMessage(new CargoConsoleApproveOrderMessage(order.OrderId)); } } } diff --git a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml index 940352dc48..3ecfad94aa 100644 --- a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml +++ b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml @@ -1,86 +1,226 @@ - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -18,9 +29,13 @@ diff --git a/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs b/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs index 64d1f78d3c..66530eba91 100644 --- a/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs +++ b/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using Content.Client.Decals.UI; using Content.Client.Stylesheets; using Content.Shared.Decals; using Robust.Client.AutoGenerated; @@ -8,6 +8,8 @@ using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; +using System.Linq; +using System.Numerics; namespace Content.Client.SprayPainter.UI; @@ -21,6 +23,9 @@ public sealed partial class SprayPainterDecals : Control public Action? OnColorChanged; public Action? OnAngleChanged; public Action? OnSnapChanged; + public Action? OnColorPickerToggled; + + private PaletteColorPicker? _palette; private SpriteSystem? _sprite; private string _selectedDecal = string.Empty; @@ -30,14 +35,17 @@ public sealed partial class SprayPainterDecals : Control { RobustXamlLoader.Load(this); - AddAngleButton.OnButtonUp += _ => AngleSpinBox.Value += 90; - SubAngleButton.OnButtonUp += _ => AngleSpinBox.Value -= 90; + AddAngleButton.OnButtonUp += _ => AngleSpinBox.Value = (AngleSpinBox.Value + 90) % 360; + SubAngleButton.OnButtonUp += _ => AngleSpinBox.Value = (AngleSpinBox.Value - 90) % 360; SetZeroAngleButton.OnButtonUp += _ => AngleSpinBox.Value = 0; AngleSpinBox.ValueChanged += args => OnAngleChanged?.Invoke(args.Value); UseCustomColorCheckBox.OnPressed += UseCustomColorCheckBoxOnOnPressed; SnapToTileCheckBox.OnPressed += SnapToTileCheckBoxOnOnPressed; ColorSelector.OnColorChanged += OnColorSelected; + + ColorPalette.OnPressed += ColorPaletteOnPressed; + ColorPicker.OnPressed += args => OnColorPickerToggled?.Invoke(args.Button.Pressed); } private void UseCustomColorCheckBoxOnOnPressed(BaseButton.ButtonEventArgs _) @@ -147,6 +155,7 @@ public sealed partial class SprayPainterDecals : Control public void SetSelectedDecal(string name) { _selectedDecal = name; + SelectedDecalName.Text = name; if (_sprite is null) return; @@ -171,4 +180,35 @@ public sealed partial class SprayPainterDecals : Control { SnapToTileCheckBox.Pressed = snap; } + + private void ColorPaletteOnPressed(BaseButton.ButtonEventArgs _) + { + // Code copied from other implementations of `PaletteColorPicker`. + if (_palette is null) + { + _palette = new PaletteColorPicker(); + _palette.OpenCenteredLeft(); + _palette.PaletteList.OnItemSelected += args => + { + var color = (args.ItemList.GetSelected().First().Metadata as Color?)!.Value; + ColorSelector.Color = color; + OnColorSelected(color); + }; + return; + } + + if (_palette.IsOpen) + { + _palette.Close(); + } + else + { + _palette.Open(); + } + } + + public void SetColorPicker(bool enabled) + { + ColorPicker.Pressed = enabled; + } } diff --git a/Content.Client/SprayPainter/UI/SprayPainterWindow.xaml.cs b/Content.Client/SprayPainter/UI/SprayPainterWindow.xaml.cs index eb1218ad67..2f72796043 100644 --- a/Content.Client/SprayPainter/UI/SprayPainterWindow.xaml.cs +++ b/Content.Client/SprayPainter/UI/SprayPainterWindow.xaml.cs @@ -30,6 +30,7 @@ public sealed partial class SprayPainterWindow : DefaultWindow public event Action? OnDecalColorChanged; public event Action? OnDecalAngleChanged; public event Action? OnDecalSnapChanged; + public event Action? OnDecalColorPickerToggled; // Pipe color data private ItemList _colorList = default!; @@ -195,6 +196,7 @@ public sealed partial class SprayPainterWindow : DefaultWindow _sprayPainterDecals.OnColorChanged += color => OnDecalColorChanged?.Invoke(color); _sprayPainterDecals.OnAngleChanged += angle => OnDecalAngleChanged?.Invoke(angle); _sprayPainterDecals.OnSnapChanged += snap => OnDecalSnapChanged?.Invoke(snap); + _sprayPainterDecals.OnColorPickerToggled += toggle => OnDecalColorPickerToggled?.Invoke(toggle); Tabs.AddChild(_sprayPainterDecals); TabContainer.SetTabTitle(_sprayPainterDecals, Loc.GetString("spray-painter-tab-category-decals")); @@ -298,7 +300,12 @@ public sealed partial class SprayPainterWindow : DefaultWindow if (_sprayPainterDecals != null) _sprayPainterDecals.SetSnap(snap); } - # endregion + + public void SetDecalColorPicker(bool colorPickerEnabled) + { + _sprayPainterDecals?.SetColorPicker(colorPickerEnabled); + } + #endregion } public record SpriteListData(string Group, string Style, EntProtoId Prototype, int SelectedIndex) : ListData; diff --git a/Content.Client/Stylesheets/CommonStylesheet.cs b/Content.Client/Stylesheets/CommonStylesheet.cs index f8eae88b38..72b58e5648 100644 --- a/Content.Client/Stylesheets/CommonStylesheet.cs +++ b/Content.Client/Stylesheets/CommonStylesheet.cs @@ -6,7 +6,7 @@ namespace Content.Client.Stylesheets; public abstract class CommonStylesheet : PalettedStylesheet, IButtonConfig, IWindowConfig, IIconConfig, ITabContainerConfig, ISliderConfig, IRadialMenuConfig, IPlaceholderConfig, ITooltipConfig, IPanelConfig, INanoHeadingConfig, - ILineEditConfig, IStripebackConfig, ICheckboxConfig + ILineEditConfig, IStripebackConfig, ICheckboxConfig, ISwitchButtonConfig { /// /// This constructor will not access any virtual or abstract properties, so you can set them from your config. @@ -73,4 +73,11 @@ public abstract class CommonStylesheet : PalettedStylesheet, IButtonConfig, IWin ColorPalette IButtonConfig.ButtonPalette => PrimaryPalette with { PressedElement = PositivePalette.PressedElement }; ColorPalette IButtonConfig.PositiveButtonPalette => PositivePalette; ColorPalette IButtonConfig.NegativeButtonPalette => NegativePalette; + + ResPath ISwitchButtonConfig.SwitchButtonTrackFillPath => new("switchbutton_track_fill.svg.96dpi.png"); + ResPath ISwitchButtonConfig.SwitchButtonTrackOutlinePath => new("switchbutton_track_outline.svg.96dpi.png"); + ResPath ISwitchButtonConfig.SwitchButtonThumbFillPath => new("switchbutton_thumb_fill.svg.96dpi.png"); + ResPath ISwitchButtonConfig.SwitchButtonThumbOutlinePath => new("switchbutton_thumb_outline.svg.96dpi.png"); + ResPath ISwitchButtonConfig.SwitchButtonSymbolOffPath => new("switchbutton_symbol_off.svg.96dpi.png"); + ResPath ISwitchButtonConfig.SwitchButtonSymbolOnPath => new("switchbutton_symbol_on.svg.96dpi.png"); } diff --git a/Content.Client/Stylesheets/SheetletConfigs/ISwitchButtonConfig.cs b/Content.Client/Stylesheets/SheetletConfigs/ISwitchButtonConfig.cs new file mode 100644 index 0000000000..0697bc9626 --- /dev/null +++ b/Content.Client/Stylesheets/SheetletConfigs/ISwitchButtonConfig.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Utility; + +namespace Content.Client.Stylesheets.SheetletConfigs; + +public interface ISwitchButtonConfig +{ + public ResPath SwitchButtonTrackFillPath { get; } + public ResPath SwitchButtonTrackOutlinePath { get; } + public ResPath SwitchButtonThumbFillPath { get; } + public ResPath SwitchButtonThumbOutlinePath { get; } + public ResPath SwitchButtonSymbolOffPath { get; } + public ResPath SwitchButtonSymbolOnPath { get; } +} + diff --git a/Content.Client/Stylesheets/Sheetlets/ButtonSheetlet.cs b/Content.Client/Stylesheets/Sheetlets/ButtonSheetlet.cs index 51ba206392..8b92e1410a 100644 --- a/Content.Client/Stylesheets/Sheetlets/ButtonSheetlet.cs +++ b/Content.Client/Stylesheets/Sheetlets/ButtonSheetlet.cs @@ -19,6 +19,7 @@ public sealed class ButtonSheetlet : Sheetlet where T : PalettedStylesheet var crossTex = sheet.GetTextureOr(iconCfg.CrossIconPath, NanotrasenStylesheet.TextureRoot); var refreshTex = sheet.GetTextureOr(iconCfg.RefreshIconPath, NanotrasenStylesheet.TextureRoot); + var helpTex = sheet.GetTextureOr(iconCfg.HelpIconPath, NanotrasenStylesheet.TextureRoot); var rules = new List { @@ -56,6 +57,11 @@ public sealed class ButtonSheetlet : Sheetlet where T : PalettedStylesheet .Class(StyleClass.RefreshButton) .Prop(TextureButton.StylePropertyTexture, refreshTex), + // Help button + E() + .Class(StyleClass.HelpButton) + .Prop(TextureButton.StylePropertyTexture, helpTex), + // Ensure labels in buttons are aligned. E