using System; using System.Collections.Generic; using System.Numerics; using Robust.Client.AutoGenerated; using Robust.Client.Console.Commands; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Input; using Robust.Shared.IoC; using Robust.Shared.Maths; namespace Robust.Client.UserInterface; [GenerateTypedNameReferences] public sealed partial class DevWindowTabUI : Control { [Dependency] private IClydeInternal _clyde = default!; [Dependency] private IInputManager _input = default!; public Control? SelectedControl { get; private set; } private Dictionary ControlMap { get; } = new(); private Control? LastHoveredControl { get; set; } public event Action? SelectedControlChanged; public DevWindowTabUI() { InitializeComponent(); TopbarButtons.PanelOverride = new StyleBoxFlat { BorderThickness = new Thickness(0, 0, 0, 1), BorderColor = Color.Gray, ContentMarginLeftOverride = 3, ContentMarginRightOverride = 3, ContentMarginBottomOverride = 1, ContentMarginTopOverride = 3, }; TreeRootBG.PanelOverride = new StyleBoxFlat { BorderThickness = new Thickness(0, 0, 1, 0), BorderColor = Color.Gray, ContentMarginLeftOverride = 3, ContentMarginBottomOverride = 3, }; } private void InitializeComponent() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); ControlTreeRoot.OnKeyBindDown += ControlTreeRootOnKeyBindDown; RefreshPropertiesButton.OnPressed += _ => Refresh(); } private Control? GetControlUnderMouse() { return UserInterfaceManager.MouseGetControl(_input.MouseScreenPosition); } private void OnMouseMove(MouseMoveEventArgs obj) { if (!ControlPicker.Pressed) return; var controlUnderMouse = GetControlUnderMouse(); if (LastHoveredControl == controlUnderMouse) return; LastHoveredControl = controlUnderMouse; Stack entryStack = new(); DevWindowUITreeEntry? entry = null; var control = controlUnderMouse; while (control != null) { if (ControlMap.TryGetValue(control, out entry)) break; entryStack.Push(control); control = control.Parent; } if (entry == null) return; if (entryStack.Count > 0) entry.Open(); foreach (var subEntry in entryStack) { ControlMap[subEntry].Open(); } SelectControl(controlUnderMouse); } private bool OnUIKeyBindStateChanged(BoundKeyEventArgs arg) { if (arg.Function != EngineKeyFunctions.UIClick) return false; if (!ControlPicker.Pressed) return false; var control = GetControlUnderMouse(); if (control == ControlPicker) return false; ControlPicker.Pressed = false; return true; } private void ControlTreeRootOnKeyBindDown(GUIBoundKeyEventArgs obj) { if (obj.Function != EngineKeyFunctions.UIClick) return; obj.Handle(); SelectControl(null); } protected override void EnteredTree() { base.EnteredTree(); // Load tree roots. foreach (var root in UserInterfaceManager.AllRoots) { var entry = new DevWindowUITreeEntry(this, root); ControlTreeRoot.AddChild(entry); } UserInterfaceManager.OnPostDrawUIRoot += OnPostDrawUIRoot; _clyde.MouseMove += OnMouseMove; _input.UIKeyBindStateChanged += OnUIKeyBindStateChanged; } protected override void ExitedTree() { base.ExitedTree(); // Clear tree children. ControlTreeRoot.RemoveAllChildren(); UserInterfaceManager.OnPostDrawUIRoot -= OnPostDrawUIRoot; _clyde.MouseMove -= OnMouseMove; _input.UIKeyBindStateChanged -= OnUIKeyBindStateChanged; } private void OnPostDrawUIRoot(PostDrawUIRootEventArgs eventArgs) { if (SelectedControl == null || eventArgs.Root != SelectedControl.Root) return; var rect = UIBox2i.FromDimensions(SelectedControl.GlobalPixelPosition, SelectedControl.PixelSize); eventArgs.DrawingHandle.DrawRect(rect, Color.Cyan.WithAlpha(0.35f)); } internal void EntryAdded(DevWindowUITreeEntry entry) { ControlMap[entry.VisControl] = entry; } public void EntryRemoved(DevWindowUITreeEntry entry) { if (SelectedControl == entry.VisControl) SelectControl(null); ControlMap.Remove(entry.VisControl); } public void SelectControl(Control? control) { SelectedControl = control; SelectedControlChanged?.Invoke(); Refresh(); } private void Refresh() { ControlProperties.RemoveAllChildren(); if (SelectedControl == null) return; var props = GuiDumpCommand.PropertyValuesForInheritance(SelectedControl); foreach (var (classname, values) in props) { ControlProperties.AddChild(new PanelContainer { Children = { new Label { Text = classname, Margin = new Thickness(3, 2) }}, PanelOverride = new StyleBoxFlat { BorderThickness = new Thickness(0, 1), BorderColor = Color.Gray, BackgroundColor = Color.FromHex("#555555"), } }); foreach (var (prop, value) in values) { var button = new ContainerButton { Children = { new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, SeparationOverride = 3, Margin = new Thickness(3, 1), Children = { new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, Children = { new Label { Text = $"{prop}", FontColorOverride = Color.GreenYellow }, new Label { Text = ":" }, // this is for the non colored ":", intentional } }, new Label { Text = $"{value}" }, } } } }; button.OnPressed += _ => { // TODO replace with parenting to popup container on WindowRoot UIPopup.Text = GuiDumpCommand.PropertyValuesString(SelectedControl, prop); var box = UIBox2.FromDimensions(UserInterfaceManager.MousePositionScaled.Position - GlobalPosition, Vector2.One); UIPopup.Open(box); }; ControlProperties.AddChild(button); } } } }