]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add support for multiple changelog files, add admin changelog (#20849)
authorDrSmugleaf <DrSmugleaf@users.noreply.github.com>
Thu, 12 Oct 2023 22:45:04 +0000 (15:45 -0700)
committerGitHub <noreply@github.com>
Thu, 12 Oct 2023 22:45:04 +0000 (15:45 -0700)
Content.Client/Administration/Managers/ClientAdminManager.cs
Content.Client/Administration/Managers/IClientAdminManager.cs
Content.Client/Changelog/ChangelogManager.cs
Content.Client/Changelog/ChangelogTab.xaml [new file with mode: 0644]
Content.Client/Changelog/ChangelogTab.xaml.cs [new file with mode: 0644]
Content.Client/Changelog/ChangelogWindow.xaml
Content.Client/Changelog/ChangelogWindow.xaml.cs
Content.Client/Stylesheets/StyleSpace.cs
Resources/Changelog/Admin.yml [new file with mode: 0644]
Resources/Locale/en-US/changelog/changelog-window.ftl

index 66c8b8a0630ba94176373c4c7f913b3e5f95bcdb..8978e2fd6dd982f94a20557fc72289955faf6eb3 100644 (file)
@@ -130,5 +130,13 @@ namespace Content.Client.Administration.Managers
 
             return null;
         }
+
+        public AdminData? GetAdminData(bool includeDeAdmin = false)
+        {
+            if (_player.LocalPlayer is { Session: { } session })
+                return GetAdminData(session, includeDeAdmin);
+
+            return null;
+        }
     }
 }
index 46e3a01537b1c05ddfa8c16d3623d76a52d34068..b4b5b48b814888c8f6ef458a36a9bc737f29e688 100644 (file)
@@ -1,5 +1,4 @@
-using System;
-using Content.Shared.Administration;
+using Content.Shared.Administration;
 
 namespace Content.Client.Administration.Managers
 {
@@ -13,6 +12,15 @@ namespace Content.Client.Administration.Managers
         /// </summary>
         event Action AdminStatusUpdated;
 
+        /// <summary>
+        ///     Gets the admin data for the client, if they are an admin.
+        /// </summary>
+        /// <param name="includeDeAdmin">
+        ///     Whether to return admin data for admins that are current de-adminned.
+        /// </param>
+        /// <returns><see langword="null" /> if the player is not an admin.</returns>
+        AdminData? GetAdminData(bool includeDeAdmin = false);
+
         /// <summary>
         ///     Checks whether the local player is an admin.
         /// </summary>
@@ -52,5 +60,17 @@ namespace Content.Client.Administration.Managers
         bool CanAdminMenu();
 
         void Initialize();
+
+        /// <summary>
+        ///     Checks if the client is an admin.
+        /// </summary>
+        /// <param name="includeDeAdmin">
+        ///     Whether to return admin data for admins that are current de-adminned.
+        /// </param>
+        /// <returns>true if the player is an admin, false otherwise.</returns>
+        bool IsAdmin(bool includeDeAdmin = false)
+        {
+            return GetAdminData(includeDeAdmin) != null;
+        }
     }
 }
index 249332c337fa7d33299fc3a2a8e005c1eb4e893d..396af99d2cfa61c1e4a3b60ca7db223f54e04878 100644 (file)
@@ -1,29 +1,29 @@
-using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using Content.Shared.CCVar;
 using Robust.Shared.Configuration;
 using Robust.Shared.ContentPack;
-using Robust.Shared.IoC;
 using Robust.Shared.Serialization;
 using Robust.Shared.Serialization.Manager;
-using Robust.Shared.Serialization.Manager.Attributes;
 using Robust.Shared.Serialization.Markdown;
 using Robust.Shared.Serialization.Markdown.Mapping;
 using Robust.Shared.Utility;
 
-
 namespace Content.Client.Changelog
 {
-    public sealed partial class ChangelogManager
+    public sealed partial class ChangelogManager : IPostInjectInit
     {
+        [Dependency] private readonly ILogManager _logManager = default!;
         [Dependency] private readonly IResourceManager _resource = default!;
         [Dependency] private readonly ISerializationManager _serialization = default!;
         [Dependency] private readonly IConfigurationManager _configManager = default!;
 
+        private const string SawmillName = "changelog";
+        public const string MainChangelogName = "Changelog";
+
+        private ISawmill _sawmill = default!;
+
         public bool NewChangelogEntries { get; private set; }
         public int LastReadId { get; private set; }
         public int MaxId { get; private set; }
@@ -51,17 +51,39 @@ namespace Content.Client.Changelog
         public async void Initialize()
         {
             // Open changelog purely to compare to the last viewed date.
-            var changelog = await LoadChangelog();
+            var changelogs = await LoadChangelog();
+            UpdateChangelogs(changelogs);
+        }
+
+        private void UpdateChangelogs(List<Changelog> changelogs)
+        {
+            if (changelogs.Count == 0)
+            {
+                return;
+            }
+
+            var mainChangelogs = changelogs.Where(c => c.Name == MainChangelogName).ToArray();
+            if (mainChangelogs.Length == 0)
+            {
+                _sawmill.Error($"No changelog file found in Resources/Changelog with name {MainChangelogName}");
+                return;
+            }
 
-            if (changelog.Count == 0)
+            var changelog = changelogs[0];
+            if (mainChangelogs.Length > 1)
+            {
+                _sawmill.Error($"More than one file found in Resource/Changelog with name {MainChangelogName}");
+            }
+
+            if (changelog.Entries.Count == 0)
             {
                 return;
             }
 
-            MaxId = changelog.Max(c => c.Id);
+            MaxId = changelog.Entries.Max(c => c.Id);
 
             var path = new ResPath($"/changelog_last_seen_{_configManager.GetCVar(CCVars.ServerId)}");
-            if(_resource.UserData.TryReadAllText(path, out var lastReadIdText))
+            if (_resource.UserData.TryReadAllText(path, out var lastReadIdText))
             {
                 LastReadId = int.Parse(lastReadIdText);
             }
@@ -71,20 +93,74 @@ namespace Content.Client.Changelog
             NewChangelogEntriesChanged?.Invoke();
         }
 
-        public Task<List<ChangelogEntry>> LoadChangelog()
+        public Task<List<Changelog>> LoadChangelog()
         {
             return Task.Run(() =>
             {
-                var yamlData = _resource.ContentFileReadYaml(new ("/Changelog/Changelog.yml"));
+                var changelogs = new List<Changelog>();
+                var directory = new ResPath("/Changelog");
+                foreach (var file in _resource.ContentFindFiles(new ResPath("/Changelog/")))
+                {
+                    if (file.Directory != directory || file.Extension != "yml")
+                        continue;
+
+                    var yamlData = _resource.ContentFileReadYaml(file);
 
-                if (yamlData.Documents.Count == 0)
-                    return new List<ChangelogEntry>();
+                    if (yamlData.Documents.Count == 0)
+                        continue;
 
-                var node = (MappingDataNode)yamlData.Documents[0].RootNode.ToDataNode();
-                return _serialization.Read<List<ChangelogEntry>>(node["Entries"], notNullableOverride: true);
+                    var node = yamlData.Documents[0].RootNode.ToDataNodeCast<MappingDataNode>();
+                    var changelog = _serialization.Read<Changelog>(node, notNullableOverride: true);
+                    if (string.IsNullOrWhiteSpace(changelog.Name))
+                        changelog.Name = file.FilenameWithoutExtension;
+
+                    changelogs.Add(changelog);
+                }
+
+                changelogs.Sort((a, b) => a.Order.CompareTo(b.Order));
+                return changelogs;
             });
         }
 
+        public void PostInject()
+        {
+            _sawmill = _logManager.GetSawmill(SawmillName);
+        }
+
+        [DataDefinition]
+        public sealed partial class Changelog
+        {
+            /// <summary>
+            ///     The name to use for this changelog.
+            ///     If left unspecified, the name of the file is used instead.
+            ///     Used during localization to find the user-displayed name of this changelog.
+            /// </summary>
+            [DataField("Name")]
+            public string Name = string.Empty;
+
+            /// <summary>
+            ///     The individual entries in this changelog.
+            ///     These are not kept around in memory in the changelog manager.
+            /// </summary>
+            [DataField("Entries")]
+            public List<ChangelogEntry> Entries = new();
+
+            /// <summary>
+            ///     Whether or not this changelog will be displayed as a tab to non-admins.
+            ///     These are still loaded by all clients, but not shown if they aren't an admin,
+            ///     as they do not contain sensitive data and are publicly visible on GitHub.
+            /// </summary>
+            [DataField("AdminOnly")]
+            public bool AdminOnly;
+
+            /// <summary>
+            ///     Used when ordering the changelog tabs for the user to see.
+            ///     Larger numbers are displayed later, smaller numbers are displayed earlier.
+            /// </summary>
+            [DataField("Order")]
+            public int Order;
+        }
+
         [DataDefinition]
         public sealed partial class ChangelogEntry : ISerializationHooks
         {
@@ -108,7 +184,7 @@ namespace Content.Client.Changelog
         }
 
         [DataDefinition]
-        public sealed partial class ChangelogChange : ISerializationHooks
+        public sealed partial class ChangelogChange
         {
             [DataField("type")]
             public ChangelogLineType Type { get; private set; }
diff --git a/Content.Client/Changelog/ChangelogTab.xaml b/Content.Client/Changelog/ChangelogTab.xaml
new file mode 100644 (file)
index 0000000..7c049ef
--- /dev/null
@@ -0,0 +1,9 @@
+<controls:ChangelogTab
+    xmlns="https://spacestation14.io"
+    xmlns:controls="clr-namespace:Content.Client.Changelog">
+    <BoxContainer Orientation="Vertical">
+        <ScrollContainer Margin="5" VerticalExpand="True" HScrollEnabled="False">
+            <BoxContainer Orientation="Vertical" Name="ChangelogBody" />
+        </ScrollContainer>
+    </BoxContainer>
+</controls:ChangelogTab>
diff --git a/Content.Client/Changelog/ChangelogTab.xaml.cs b/Content.Client/Changelog/ChangelogTab.xaml.cs
new file mode 100644 (file)
index 0000000..d1e2bc7
--- /dev/null
@@ -0,0 +1,175 @@
+using System.Linq;
+using System.Numerics;
+using Content.Client.Resources;
+using Content.Client.Stylesheets;
+using Robust.Client.AutoGenerated;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+using static Content.Client.Changelog.ChangelogManager;
+using static Robust.Client.UserInterface.Controls.BoxContainer;
+
+namespace Content.Client.Changelog;
+
+[GenerateTypedNameReferences]
+public sealed partial class ChangelogTab : Control
+{
+    [Dependency] private readonly ChangelogManager _changelog = default!;
+    [Dependency] private readonly IResourceCache _resourceCache = default!;
+
+    public bool AdminOnly;
+
+    public ChangelogTab()
+    {
+        RobustXamlLoader.Load(this);
+        IoCManager.InjectDependencies(this);
+    }
+
+    public void PopulateChangelog(ChangelogManager.Changelog changelog)
+    {
+        var byDay = changelog.Entries
+            .GroupBy(e => e.Time.ToLocalTime().Date)
+            .OrderByDescending(c => c.Key);
+
+        var hasRead = changelog.Name != MainChangelogName ||
+                      _changelog.MaxId <= _changelog.LastReadId;
+
+        foreach (var dayEntries in byDay)
+        {
+            var day = dayEntries.Key;
+
+            var groupedEntries = dayEntries
+                .GroupBy(c => (c.Author, Read: c.Id <= _changelog.LastReadId))
+                .OrderBy(c => c.Key.Read)
+                .ThenBy(c => c.Key.Author);
+
+            string dayNice;
+            var today = DateTime.Today;
+            if (day == today)
+                dayNice = Loc.GetString("changelog-today");
+            else if (day == today.AddDays(-1))
+                dayNice = Loc.GetString("changelog-yesterday");
+            else
+                dayNice = day.ToShortDateString();
+
+            ChangelogBody.AddChild(new Label
+            {
+                Text = dayNice,
+                StyleClasses = { StyleBase.StyleClassLabelHeading },
+                Margin = new Thickness(4, 6, 0, 0)
+            });
+
+            var first = true;
+
+            foreach (var groupedEntry in groupedEntries)
+            {
+                var (author, read) = groupedEntry.Key;
+
+                if (!first)
+                {
+                    ChangelogBody.AddChild(new Control { Margin = new Thickness(4) });
+                }
+
+                if (read && !hasRead)
+                {
+                    hasRead = true;
+
+                    var upArrow =
+                        _resourceCache.GetTexture("/Textures/Interface/Changelog/up_arrow.svg.192dpi.png");
+
+                    var readDivider = new BoxContainer
+                    {
+                        Orientation = LayoutOrientation.Vertical
+                    };
+
+                    var hBox = new BoxContainer
+                    {
+                        Orientation = LayoutOrientation.Horizontal,
+                        HorizontalAlignment = HAlignment.Center,
+                        Children =
+                        {
+                            new TextureRect
+                            {
+                                Texture = upArrow,
+                                ModulateSelfOverride = Color.FromHex("#888"),
+                                TextureScale = new Vector2(0.5f, 0.5f),
+                                Margin = new Thickness(4, 3),
+                                VerticalAlignment = VAlignment.Bottom
+                            },
+                            new Label
+                            {
+                                Align = Label.AlignMode.Center,
+                                Text = Loc.GetString("changelog-new-changes"),
+                                FontColorOverride = Color.FromHex("#888"),
+                            },
+                            new TextureRect
+                            {
+                                Texture = upArrow,
+                                ModulateSelfOverride = Color.FromHex("#888"),
+                                TextureScale = new Vector2(0.5f, 0.5f),
+                                Margin = new Thickness(4, 3),
+                                VerticalAlignment = VAlignment.Bottom
+                            }
+                        }
+                    };
+
+                    readDivider.AddChild(hBox);
+                    readDivider.AddChild(new PanelContainer { StyleClasses = { StyleBase.ClassLowDivider } });
+                    ChangelogBody.AddChild(readDivider);
+
+                    if (first)
+                        readDivider.SetPositionInParent(ChangelogBody.ChildCount - 2);
+                }
+
+                first = false;
+
+                var authorLabel = new RichTextLabel
+                {
+                    Margin = new Thickness(6, 0, 0, 0),
+                };
+                authorLabel.SetMessage(
+                    FormattedMessage.FromMarkup(Loc.GetString("changelog-author-changed", ("author", author))));
+                ChangelogBody.AddChild(authorLabel);
+
+                foreach (var change in groupedEntry.SelectMany(c => c.Changes))
+                {
+                    var text = new RichTextLabel();
+                    text.SetMessage(FormattedMessage.FromMarkup(change.Message));
+                    ChangelogBody.AddChild(new BoxContainer
+                    {
+                        Orientation = LayoutOrientation.Horizontal,
+                        Margin = new Thickness(14, 1, 10, 2),
+                        Children =
+                        {
+                            GetIcon(change.Type),
+                            text
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    private TextureRect GetIcon(ChangelogLineType type)
+    {
+        var (file, color) = type switch
+        {
+            ChangelogLineType.Add => ("plus.svg.192dpi.png", "#6ED18D"),
+            ChangelogLineType.Remove => ("minus.svg.192dpi.png", "#D16E6E"),
+            ChangelogLineType.Fix => ("bug.svg.192dpi.png", "#D1BA6E"),
+            ChangelogLineType.Tweak => ("wrench.svg.192dpi.png", "#6E96D1"),
+            _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
+        };
+
+        return new TextureRect
+        {
+            Texture = _resourceCache.GetTexture(new ResPath($"/Textures/Interface/Changelog/{file}")),
+            VerticalAlignment = VAlignment.Top,
+            TextureScale = new Vector2(0.5f, 0.5f),
+            Margin = new Thickness(2, 4, 6, 2),
+            ModulateSelfOverride = Color.FromHex(color)
+        };
+    }
+}
index 888a8528d91a1e9bde6a177b954ba8adc1bd83c8..355452dbfad3409b3fec9a33f74f4f86903b7c40 100644 (file)
@@ -3,15 +3,10 @@
                     Title="{Loc 'changelog-window-title'}"
                     MinSize="500 400"
                     SetSize="500 400">
-
     <PanelContainer StyleClasses="AngleRect" />
     <BoxContainer Orientation="Vertical">
-
-        <ScrollContainer Margin="5" VerticalExpand="True" HScrollEnabled="False">
-            <BoxContainer Orientation="Vertical" Name="ChangelogBody" />
-        </ScrollContainer>
-
+        <TabContainer Name="Tabs" Access="Public" HorizontalExpand="True" VerticalExpand="True" />
         <PanelContainer StyleClasses="LowDivider" />
-        <Label Name="VersionLabel" HorizontalAlignment="Right" StyleClasses="LabelSubText" Margin="4 0" />
+        <Label Name="VersionLabel" Access="Public" HorizontalAlignment="Right" StyleClasses="LabelSubText" Margin="4 0" />
     </BoxContainer>
 </ui:ChangelogWindow>
index cea5bd9e7c2f5618a6ba3143766b6ad2ad0bb53e..e5f492900c26b14d72757ef5932c548833790f65 100644 (file)
@@ -1,28 +1,22 @@
 using System.Linq;
-using System.Numerics;
-using Content.Client.Resources;
+using Content.Client.Administration.Managers;
 using Content.Client.Stylesheets;
 using Content.Client.UserInterface.Controls;
 using Content.Client.UserInterface.Systems.EscapeMenu;
 using Content.Shared.Administration;
 using JetBrains.Annotations;
 using Robust.Client.AutoGenerated;
-using Robust.Client.ResourceManagement;
 using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Console;
-using Robust.Shared.Utility;
-using static Content.Client.Changelog.ChangelogManager;
-using static Robust.Client.UserInterface.Controls.BoxContainer;
 
 namespace Content.Client.Changelog
 {
     [GenerateTypedNameReferences]
     public sealed partial class ChangelogWindow : FancyWindow
     {
+        [Dependency] private readonly IClientAdminManager _adminManager = default!;
         [Dependency] private readonly ChangelogManager _changelog = default!;
-        [Dependency] private readonly IResourceCache _resourceCache = default!;
 
         public ChangelogWindow()
         {
@@ -39,154 +33,84 @@ namespace Content.Client.Changelog
             PopulateChangelog();
         }
 
+        protected override void EnteredTree()
+        {
+            base.EnteredTree();
+            _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
+        }
+
+        protected override void ExitedTree()
+        {
+            base.ExitedTree();
+            _adminManager.AdminStatusUpdated -= OnAdminStatusUpdated;
+        }
+
+        private void OnAdminStatusUpdated()
+        {
+            TabsUpdated();
+        }
+
         private async void PopulateChangelog()
         {
             // Changelog is not kept in memory so load it again.
-            var changelog = await _changelog.LoadChangelog();
+            var changelogs = await _changelog.LoadChangelog();
 
-            var byDay = changelog
-                .GroupBy(e => e.Time.ToLocalTime().Date)
-                .OrderByDescending(c => c.Key);
+            Tabs.DisposeAllChildren();
 
-            var hasRead = _changelog.MaxId <= _changelog.LastReadId;
-            foreach (var dayEntries in byDay)
+            var i = 0;
+            foreach (var changelog in changelogs)
             {
-                var day = dayEntries.Key;
-
-                var groupedEntries = dayEntries
-                    .GroupBy(c => (c.Author, Read: c.Id <= _changelog.LastReadId))
-                    .OrderBy(c => c.Key.Read)
-                    .ThenBy(c => c.Key.Author);
-
-                string dayNice;
-                var today = DateTime.Today;
-                if (day == today)
-                    dayNice = Loc.GetString("changelog-today");
-                else if (day == today.AddDays(-1))
-                    dayNice = Loc.GetString("changelog-yesterday");
-                else
-                    dayNice = day.ToShortDateString();
-
-                ChangelogBody.AddChild(new Label
-                {
-                    Text = dayNice,
-                    StyleClasses = { StyleBase.StyleClassLabelHeading },
-                    Margin = new Thickness(4, 6, 0, 0)
-                });
+                var tab = new ChangelogTab { AdminOnly = changelog.AdminOnly };
+                tab.PopulateChangelog(changelog);
 
-                var first = true;
-
-                foreach (var groupedEntry in groupedEntries)
-                {
-                    var (author, read) = groupedEntry.Key;
-
-                    if (!first)
-                    {
-                        ChangelogBody.AddChild(new Control { Margin = new Thickness(4) });
-                    }
-
-                    if (read && !hasRead)
-                    {
-                        hasRead = true;
-
-                        var upArrow =
-                            _resourceCache.GetTexture("/Textures/Interface/Changelog/up_arrow.svg.192dpi.png");
-
-                        var readDivider = new BoxContainer
-                        {
-                            Orientation = LayoutOrientation.Vertical
-                        };
-
-                        var hBox = new BoxContainer
-                        {
-                            Orientation = LayoutOrientation.Horizontal,
-                            HorizontalAlignment = HAlignment.Center,
-                            Children =
-                            {
-                                new TextureRect
-                                {
-                                    Texture = upArrow,
-                                    ModulateSelfOverride = Color.FromHex("#888"),
-                                    TextureScale = new Vector2(0.5f, 0.5f),
-                                    Margin = new Thickness(4, 3),
-                                    VerticalAlignment = VAlignment.Bottom
-                                },
-                                new Label
-                                {
-                                    Align = Label.AlignMode.Center,
-                                    Text = Loc.GetString("changelog-new-changes"),
-                                    FontColorOverride = Color.FromHex("#888"),
-                                },
-                                new TextureRect
-                                {
-                                    Texture = upArrow,
-                                    ModulateSelfOverride = Color.FromHex("#888"),
-                                    TextureScale = new Vector2(0.5f, 0.5f),
-                                    Margin = new Thickness(4, 3),
-                                    VerticalAlignment = VAlignment.Bottom
-                                }
-                            }
-                        };
-
-                        readDivider.AddChild(hBox);
-                        readDivider.AddChild(new PanelContainer { StyleClasses = { StyleBase.ClassLowDivider } });
-                        ChangelogBody.AddChild(readDivider);
-
-                        if (first)
-                            readDivider.SetPositionInParent(ChangelogBody.ChildCount - 2);
-                    }
-
-                    first = false;
-
-                    var authorLabel = new RichTextLabel
-                    {
-                        Margin = new Thickness(6, 0, 0, 0),
-                    };
-                    authorLabel.SetMessage(
-                        FormattedMessage.FromMarkup(Loc.GetString("changelog-author-changed", ("author", author))));
-                    ChangelogBody.AddChild(authorLabel);
-
-                    foreach (var change in groupedEntry.SelectMany(c => c.Changes))
-                    {
-                        var text = new RichTextLabel();
-                        text.SetMessage(FormattedMessage.FromMarkup(change.Message));
-                        ChangelogBody.AddChild(new BoxContainer
-                        {
-                            Orientation = LayoutOrientation.Horizontal,
-                            Margin = new Thickness(14, 1, 10, 2),
-                            Children =
-                            {
-                                GetIcon(change.Type),
-                                text
-                            }
-                        });
-                    }
-                }
+                Tabs.AddChild(tab);
+                Tabs.SetTabTitle(i++, Loc.GetString($"changelog-tab-title-{changelog.Name}"));
             }
 
             var version = typeof(ChangelogWindow).Assembly.GetName().Version ?? new Version(1, 0);
             VersionLabel.Text = Loc.GetString("changelog-version-tag", ("version", version.ToString()));
+
+            TabsUpdated();
         }
 
-        private TextureRect GetIcon(ChangelogLineType type)
+        private void TabsUpdated()
         {
-            var (file, color) = type switch
+            var tabs = Tabs.Children.OfType<ChangelogTab>().ToArray();
+            var isAdmin = _adminManager.IsAdmin(true);
+
+            var visibleTabs = 0;
+            int? firstVisible = null;
+            for (var i = 0; i < tabs.Length; i++)
             {
-                ChangelogLineType.Add => ("plus.svg.192dpi.png", "#6ED18D"),
-                ChangelogLineType.Remove => ("minus.svg.192dpi.png", "#D16E6E"),
-                ChangelogLineType.Fix => ("bug.svg.192dpi.png", "#D1BA6E"),
-                ChangelogLineType.Tweak => ("wrench.svg.192dpi.png", "#6E96D1"),
-                _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
-            };
-
-            return new TextureRect
+                var tab = tabs[i];
+
+                if (!tab.AdminOnly || isAdmin)
+                {
+                    Tabs.SetTabVisible(i, true);
+                    tab.Visible = true;
+                    visibleTabs++;
+                    firstVisible ??= i;
+                }
+                else
+                {
+                    Tabs.SetTabVisible(i, false);
+                    tab.Visible = false;
+                }
+            }
+
+            Tabs.TabsVisible = visibleTabs > 1;
+
+            // Current tab became invisible, select the first one that is visible
+            if (!Tabs.GetTabVisible(Tabs.CurrentTab) && firstVisible != null)
             {
-                Texture = _resourceCache.GetTexture(new ResPath($"/Textures/Interface/Changelog/{file}")),
-                VerticalAlignment = VAlignment.Top,
-                TextureScale = new Vector2(0.5f, 0.5f),
-                Margin = new Thickness(2, 4, 6, 2),
-                ModulateSelfOverride = Color.FromHex(color)
-            };
+                Tabs.CurrentTab = firstVisible.Value;
+            }
+
+            // We are only displaying one tab, hide its header
+            if (!Tabs.TabsVisible && firstVisible != null)
+            {
+                Tabs.SetTabVisible(firstVisible.Value, false);
+            }
         }
     }
 
index a82dba65bcc887f6665c3f3a16ccc4b28f0be28f..3bb4e986af54701366e8682e0bdd851f2081b6b7 100644 (file)
@@ -4,7 +4,6 @@ using Robust.Client.Graphics;
 using Robust.Client.ResourceManagement;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
-using Robust.Shared.Maths;
 using static Robust.Client.UserInterface.StylesheetHelpers;
 
 namespace Content.Client.Stylesheets
@@ -62,6 +61,14 @@ namespace Content.Client.Stylesheets
 
             var textureInvertedTriangle = resCache.GetTexture("/Textures/Interface/Nano/inverted_triangle.svg.png");
 
+            var tabContainerPanel = new StyleBoxTexture();
+            tabContainerPanel.SetPatchMargin(StyleBox.Margin.All, 2);
+
+            var tabContainerBoxActive = new StyleBoxFlat {BackgroundColor = new Color(64, 64, 64)};
+            tabContainerBoxActive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
+            var tabContainerBoxInactive = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 32)};
+            tabContainerBoxInactive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
+
             Stylesheet = new Stylesheet(BaseRules.Concat(new StyleRule[]
             {
                 Element<Label>().Class(StyleClassLabelHeading)
@@ -179,6 +186,14 @@ namespace Content.Client.Stylesheets
                 Element<Label>().Class(OptionButton.StyleClassOptionButton)
                     .Prop(Label.StylePropertyAlignMode, Label.AlignMode.Center),
 
+                // TabContainer
+                new StyleRule(new SelectorElement(typeof(TabContainer), null, null, null),
+                    new[]
+                    {
+                        new StyleProperty(TabContainer.StylePropertyPanelStyleBox, tabContainerPanel),
+                        new StyleProperty(TabContainer.StylePropertyTabStyleBox, tabContainerBoxActive),
+                        new StyleProperty(TabContainer.StylePropertyTabStyleBoxInactive, tabContainerBoxInactive),
+                    }),
 
             }).ToList());
         }
diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml
new file mode 100644 (file)
index 0000000..81bc934
--- /dev/null
@@ -0,0 +1,9 @@
+Name: Admin
+AdminOnly: true
+Order: 1
+Entries:
+- author: DrSmugleaf
+  changes:
+  - {message: 'Created the admin changelog.', type: Add}
+  id: 1
+  time: '2023-10-08T04:26:00.0000000+00:00'
index 597a059b0f0bd91b9f50e20631b19985e78e66a0..b7f5d6a2fae94edde57b92898e6b729572f5a929 100644 (file)
@@ -9,3 +9,6 @@ changelog-version-tag = version v{ $version }
 
 changelog-button = Changelog
 changelog-button-new-entries = Changelog (new!)
+
+changelog-tab-title-Changelog = Changelog
+changelog-tab-title-Admin = Admin