diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs index 32a7e2b070..61054e9b0f 100644 --- a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs +++ b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs @@ -1,7 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Client.Guidebook.RichText; using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls.FancyTree; +using JetBrains.Annotations; using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.ContentPack; @@ -9,7 +13,7 @@ using Robust.Shared.ContentPack; namespace Content.Client.Guidebook.Controls; [GenerateTypedNameReferences] -public sealed partial class GuidebookWindow : FancyWindow +public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler { [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly DocumentParsingManager _parsingMan = default!; @@ -139,4 +143,20 @@ public sealed partial class GuidebookWindow : FancyWindow return item; } + + public void HandleClick(string link) + { + if (!_entries.TryGetValue(link, out var entry)) + return; + + if (Tree.TryGetIndexFromMetadata(entry, out var index)) + { + Tree.ExpandParentEntries(index.Value); + Tree.SetSelectedIndex(index); + } + else + { + ShowGuide(entry); + } + } } diff --git a/Content.Client/Guidebook/Richtext/TextLinkTag.cs b/Content.Client/Guidebook/Richtext/TextLinkTag.cs new file mode 100644 index 0000000000..b1e8460bb8 --- /dev/null +++ b/Content.Client/Guidebook/Richtext/TextLinkTag.cs @@ -0,0 +1,70 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.RichText; +using Robust.Shared.Input; +using Robust.Shared.Utility; + +namespace Content.Client.Guidebook.RichText; + +[UsedImplicitly] +public sealed class TextLinkTag : IMarkupTag +{ + public string Name => "textlink"; + + public Control? Control; + + /// + public bool TryGetControl(MarkupNode node, [NotNullWhen(true)] out Control? control) + { + if (!node.Value.TryGetString(out var text) + || !node.Attributes.TryGetValue("link", out var linkParameter) + || !linkParameter.TryGetString(out var link)) + { + control = null; + return false; + } + + var label = new Label(); + label.Text = text; + + label.MouseFilter = Control.MouseFilterMode.Stop; + label.FontColorOverride = Color.CornflowerBlue; + label.DefaultCursorShape = Control.CursorShape.Hand; + + label.OnMouseEntered += _ => label.FontColorOverride = Color.LightSkyBlue; + label.OnMouseExited += _ => label.FontColorOverride = Color.CornflowerBlue; + label.OnKeyBindDown += args => OnKeybindDown(args, link); + + control = label; + Control = label; + return true; + } + + private void OnKeybindDown(GUIBoundKeyEventArgs args, string link) + { + if (args.Function != EngineKeyFunctions.UIClick) + return; + + if (Control == null) + return; + + var current = Control; + while (current != null) + { + current = current.Parent; + + if (current is not ILinkClickHandler handler) + continue; + handler.HandleClick(link); + return; + } + Logger.Warning($"Warning! No valid ILinkClickHandler found."); + } +} + +public interface ILinkClickHandler +{ + public void HandleClick(string link); +} diff --git a/Content.Client/UserInterface/Controls/FancyTree/FancyTree.xaml.cs b/Content.Client/UserInterface/Controls/FancyTree/FancyTree.xaml.cs index 1df5864926..658465f245 100644 --- a/Content.Client/UserInterface/Controls/FancyTree/FancyTree.xaml.cs +++ b/Content.Client/UserInterface/Controls/FancyTree/FancyTree.xaml.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Client.Resources; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; @@ -196,13 +197,38 @@ public sealed partial class FancyTree : Control if (depth == 0) return; depth--; - + foreach (var child in item.Body.Children) { RecursiveSetExpanded((TreeItem) child, value, depth); } } + public bool TryGetIndexFromMetadata(object metadata, [NotNullWhen(true)] out int? index) + { + index = null; + foreach (var item in Items) + { + if (item.Metadata?.Equals(metadata) ?? false) + { + index = item.Index; + break; + } + } + return index != null; + } + + public void ExpandParentEntries(int index) + { + Control? current = Items[index]; + while (current != null) + { + if (current is TreeItem item) + item.SetExpanded(true); + current = current.Parent; + } + } + public void Clear() { foreach (var item in Items)