From 2cef371a16b76637127e602d4b3c7896c0a90bc2 Mon Sep 17 00:00:00 2001 From: Robbie Lodico Date: Sun, 9 Feb 2025 12:03:46 +0100 Subject: [PATCH 01/30] fix #2528 #2529 Hotbar alignment and Credits scrollbar and alignment --- .../Interface/Debugging/DebugWindow.cs | 74 +++++-- .../Interface/Game/Hotbar/HotBar.cs | 15 +- .../Interface/Game/Hotbar/HotbarItem.cs | 3 +- .../Interface/Menu/CreditsWindow.cs | 91 +++++--- .../Interface/Menu/LoginWindow.cs | 8 +- .../Interface/Menu/MainMenu.cs | 8 +- .../Interface/Menu/MainMenuWindow.Designer.cs | 5 +- .../Interface/Shared/SettingsWindow.cs | 15 +- .../Interface/Shared/VersionLabel.cs | 2 +- .../Interface/Shared/VersionPanel.cs | 2 +- Intersect.Client.Core/Localization/Strings.cs | 5 +- Intersect.Client.Framework/Gwen/Align.cs | 14 +- .../Gwen/Control/Base.cs | 186 +++++++++------- .../Gwen/Control/Button.cs | 23 +- .../Gwen/Control/ComboBox.cs | 21 +- .../Gwen/Control/ComponentStateFilters.cs | 7 +- .../Gwen/Control/GroupBox.cs | 6 +- .../Gwen/Control/ILabel.cs | 4 +- .../Gwen/Control/ITextContainer.cs | 4 +- .../Gwen/Control/ImagePanel.cs | 8 +- .../Gwen/Control/Label.cs | 38 ++-- .../Gwen/Control/LabeledCheckBox.cs | 2 +- .../Gwen/Control/LabeledComboBox.cs | 14 +- .../Gwen/Control/LabeledSlider.cs | 2 +- .../Gwen/Control/Layout/Table.cs | 5 - .../Gwen/Control/Layout/TableRow.cs | 6 +- .../Gwen/Control/Menu.cs | 8 +- .../Gwen/Control/MenuStrip.cs | 1 - .../Gwen/Control/MultilineTextBox.cs | 14 +- .../Gwen/Control/ProgressBar.cs | 2 +- .../Gwen/Control/RichLabel.cs | 205 ++++++++++++------ .../Gwen/Control/ScrollControl.cs | 2 +- .../Gwen/Control/TabButton.cs | 3 +- .../Gwen/Control/TabControl.cs | 2 +- .../Gwen/Control/TabTitleBar.cs | 3 +- .../Gwen/Control/TextBox.cs | 10 +- .../Gwen/Control/TreeControl.cs | 2 +- .../Gwen/Control/TreeNode.cs | 2 +- .../Gwen/Control/WindowControl.cs | 30 ++- .../Gwen/ControlInternal/CategoryButton.cs | 2 +- .../ControlInternal/CategoryHeaderButton.cs | 2 +- .../Gwen/ControlInternal/CloseButton.cs | 3 +- .../Gwen/ControlInternal/Dragger.cs | 10 + .../Gwen/ControlInternal/TabControlInner.cs | 4 +- .../Gwen/ControlInternal/Text.cs | 22 +- .../Gwen/ControlInternal/TreeNodeLabel.cs | 2 +- Intersect.Client.Framework/Gwen/Margin.cs | 14 ++ .../Gwen/Renderer/Base.cs | 10 +- Intersect.Client.Framework/Gwen/Skin/Base.cs | 41 ++-- .../Gwen/Skin/Intersect2021.cs | 13 +- .../Gwen/Skin/IntersectSkin.cs | 43 ++-- .../Gwen/Skin/Simple.cs | 4 +- .../Gwen/Skin/TexturedBase.cs | 71 +++--- .../Gwen/Skin/Texturing/Bordered.cs | 31 ++- .../Gwen/Skin/Texturing/FivePatch.cs | 203 +++++++++++++++++ .../Gwen/Skin/Texturing/UVSquare.cs | 3 + Intersect.Server/Program.cs | 6 +- .../Web/Controllers/Api/RootInfoController.cs | 1 - Intersect.sln.DotSettings | 1 + 59 files changed, 885 insertions(+), 453 deletions(-) create mode 100644 Intersect.Client.Framework/Gwen/Skin/Texturing/FivePatch.cs create mode 100644 Intersect.Client.Framework/Gwen/Skin/Texturing/UVSquare.cs diff --git a/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs b/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs index 92688cebc3..3142efe336 100644 --- a/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs +++ b/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs @@ -21,10 +21,12 @@ internal sealed partial class DebugWindow : Window private readonly GameFont? _defaultFont; private bool _wasParentDrawDebugOutlinesEnabled; private bool _drawDebugOutlinesEnabled; + private ComponentStateFilters _componentStateFilters = ComponentStateFilters.IncludeMouseInputDisabled; public DebugWindow(Base parent) : base(parent, Strings.Debug.Title, false, nameof(DebugWindow)) { _generators = []; + DisableResizing(); MinimumSize = new Point(320, 320); Size = new Point(400, 600); @@ -39,6 +41,7 @@ public DebugWindow(Base parent) : base(parent, Strings.Debug.Title, false, nameo TabInfo = Tabs.AddPage(Strings.Debug.TabLabelInfo, nameof(TabInfo)); CheckboxDrawDebugOutlines = CreateInfoCheckboxDrawDebugOutlines(TabInfo.Page); CheckboxEnableLayoutHotReloading = CreateInfoCheckboxEnableLayoutHotReloading(TabInfo.Page); + CheckboxIncludeTextNodesInHover = CreateInfoCheckboxIncludeTextNodesInHover(TabInfo.Page); ButtonShutdownServer = CreateInfoButtonShutdownServer(TabInfo.Page); ButtonShutdownServerAndExit = CreateInfoButtonShutdownServerAndExit(TabInfo.Page); TableDebugStats = CreateInfoTableDebugStats(TabInfo.Page); @@ -144,6 +147,8 @@ private Button CreateAssetsButtonReloadAsset(Table table, SearchableTree assetLi private LabeledCheckBox CheckboxEnableLayoutHotReloading { get; } + private LabeledCheckBox CheckboxIncludeTextNodesInHover { get; } + private Button ButtonShutdownServer { get; } private Button ButtonShutdownServerAndExit { get; } @@ -152,7 +157,7 @@ private Button CreateAssetsButtonReloadAsset(Table table, SearchableTree assetLi protected override void EnsureInitialized() { - // LoadJsonUi(UI.Debug, Graphics.Renderer?.GetResolutionString()); + LoadJsonUi(UI.Debug, Graphics.Renderer?.GetResolutionString()); foreach (var generator in _generators) { @@ -237,7 +242,7 @@ private LabeledCheckBox CreateInfoCheckboxEnableLayoutHotReloading(Base parent) TextColorOverride = Color.Yellow, }; - checkbox.CheckChanged += (sender, args) => Globals.ContentManager.ContentWatcher.Enabled = checkbox.IsChecked; + checkbox.CheckChanged += (_, _) => Globals.ContentManager.ContentWatcher.Enabled = checkbox.IsChecked; checkbox.SetToolTipText(Strings.Internals.ExperimentalFeatureTooltip); checkbox.TooltipFont = Skin.DefaultFont; @@ -245,6 +250,31 @@ private LabeledCheckBox CreateInfoCheckboxEnableLayoutHotReloading(Base parent) return checkbox; } + private LabeledCheckBox CreateInfoCheckboxIncludeTextNodesInHover(Base parent) + { + var checkbox = new LabeledCheckBox(parent, nameof(CheckboxIncludeTextNodesInHover)) + { + Dock = Pos.Top, + Font = _defaultFont, + IsChecked = _componentStateFilters.HasFlag(ComponentStateFilters.IncludeText), + Text = Strings.Debug.IncludeTextNodesInHover, + }; + + checkbox.CheckChanged += (_, _) => + { + if (_componentStateFilters.HasFlag(ComponentStateFilters.IncludeText)) + { + _componentStateFilters &= ~ComponentStateFilters.IncludeText; + } + else + { + _componentStateFilters |= ComponentStateFilters.IncludeText; + } + }; + + return checkbox; + } + private Button CreateInfoButtonShutdownServer(Base parent) { var button = new Button(parent, nameof(ButtonShutdownServer)) @@ -255,7 +285,7 @@ private Button CreateInfoButtonShutdownServer(Base parent) Text = Strings.Debug.ShutdownServer, }; - button.Clicked += (sender, args) => + button.Clicked += (_, _) => { // TODO: Implement }; @@ -273,7 +303,7 @@ private Button CreateInfoButtonShutdownServerAndExit(Base parent) Text = Strings.Debug.ShutdownServerAndExit, }; - button.Clicked += (sender, args) => + button.Clicked += (_, _) => { // TODO: Implement }; @@ -399,12 +429,13 @@ private Table CreateInfoTableDebugStats(Base parent) table.AddRow(Strings.Internals.Color, name: "ColorRow").Listen(controlUnderCursorProvider, 4); table.AddRow(Strings.Internals.ColorOverride, name: "ColorOverrideRow").Listen(controlUnderCursorProvider, 5); table.AddRow(Strings.Internals.InnerBounds, name: "InnerBoundsRow").Listen(controlUnderCursorProvider, 6); - table.AddRow(Strings.Internals.Margin, name: "MarginRow").Listen(controlUnderCursorProvider, 7); - table.AddRow(Strings.Internals.Padding, name: "PaddingRow").Listen(controlUnderCursorProvider, 8); - table.AddRow(Strings.Internals.Dock, name: "Dock").Listen(controlUnderCursorProvider, 9); - table.AddRow(Strings.Internals.Alignment, name: "Alignment").Listen(controlUnderCursorProvider, 10); - table.AddRow(Strings.Internals.TextAlign, name: "TextAlign").Listen(controlUnderCursorProvider, 11); - table.AddRow(Strings.Internals.TextPadding, name: "TextPadding").Listen(controlUnderCursorProvider, 12); + table.AddRow(Strings.Internals.OuterBounds, name: "OuterBounds").Listen(controlUnderCursorProvider, 7); + table.AddRow(Strings.Internals.Margin, name: "MarginRow").Listen(controlUnderCursorProvider, 8); + table.AddRow(Strings.Internals.Padding, name: "PaddingRow").Listen(controlUnderCursorProvider, 9); + table.AddRow(Strings.Internals.Dock, name: "Dock").Listen(controlUnderCursorProvider, 10); + table.AddRow(Strings.Internals.Alignment, name: "Alignment").Listen(controlUnderCursorProvider, 11); + table.AddRow(Strings.Internals.Alignment, name: "Alignment").Listen(controlUnderCursorProvider, 11); + table.AddRow(Strings.Internals.TextAlign, name: "TextAlign").Listen(controlUnderCursorProvider, 12); table.AddRow(Strings.Internals.Font, name: "Font").Listen(controlUnderCursorProvider, 13); table.AddRow(Strings.Internals.AutoSizeToContents, name: "AutoSizeToContents").Listen(controlUnderCursorProvider, 14); _generators.Add(controlUnderCursorProvider.Generator); @@ -425,9 +456,9 @@ private Table CreateInfoTableDebugStats(Base parent) private partial class ControlUnderCursorProvider : ITableDataProvider { - private readonly Base _owner; + private readonly DebugWindow _owner; - public ControlUnderCursorProvider(Base owner) + public ControlUnderCursorProvider(DebugWindow owner) { _owner = owner; Generator = new CancellableGenerator(CreateControlUnderCursorGenerator); @@ -465,8 +496,7 @@ private async Task WaitForOwnerVisible(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); } - private static Base? CreateValue(Task _) => - Interface.FindComponentUnderCursor(ComponentStateFilters.IncludeMouseInputDisabled); + private Base? CreateValue(Task _) => Interface.FindComponentUnderCursor(_owner._componentStateFilters); private void HandleValue(Base? component) { @@ -515,29 +545,29 @@ private void HandleValue(Base? component) ); DataChanged?.Invoke( this, - new TableDataChangedEventArgs(7, 1, default, component?.Margin.ToString() ?? string.Empty) + new TableDataChangedEventArgs(7, 1, default, component?.OuterBounds.ToString() ?? string.Empty) ); DataChanged?.Invoke( this, - new TableDataChangedEventArgs(8, 1, default, component?.Padding.ToString() ?? string.Empty) + new TableDataChangedEventArgs(8, 1, default, component?.Margin.ToString() ?? string.Empty) ); DataChanged?.Invoke( this, - new TableDataChangedEventArgs(9, 1, default, component?.Dock.ToString() ?? string.Empty) + new TableDataChangedEventArgs(9, 1, default, component?.Padding.ToString() ?? string.Empty) ); DataChanged?.Invoke( this, - new TableDataChangedEventArgs(10, 1, default, string.Join(", ", component?.Alignment ?? [])) + new TableDataChangedEventArgs(10, 1, default, component?.Dock.ToString() ?? string.Empty) ); - - var label = component as Label; DataChanged?.Invoke( this, - new TableDataChangedEventArgs(11, 1, default, label?.TextAlign.ToString() ?? string.Empty) + new TableDataChangedEventArgs(11, 1, default, string.Join(", ", component?.Alignment ?? [])) ); + + var label = component as Label; DataChanged?.Invoke( this, - new TableDataChangedEventArgs(12, 1, default, label?.TextPadding.ToString() ?? string.Empty) + new TableDataChangedEventArgs(12, 1, default, label?.TextAlign.ToString() ?? string.Empty) ); DataChanged?.Invoke( this, diff --git a/Intersect.Client.Core/Interface/Game/Hotbar/HotBar.cs b/Intersect.Client.Core/Interface/Game/Hotbar/HotBar.cs index 60d7486c7b..7fdd5f56f9 100644 --- a/Intersect.Client.Core/Interface/Game/Hotbar/HotBar.cs +++ b/Intersect.Client.Core/Interface/Game/Hotbar/HotBar.cs @@ -22,8 +22,9 @@ public HotBarWindow(Canvas gameCanvas) { HotbarWindow = new ImagePanel(gameCanvas, "HotbarWindow") { - AlignmentDistance = new Padding { Top = 4, Right = 4 }, + AlignmentPadding = new Padding { Top = 4, Right = 4 }, Alignment = [Alignments.Top, Alignments.Right], + Padding = Padding.Four, RestrictToParent = true, TextureFilename = "hotbar.png", TextureNinePatchMargin = Margin.Three, @@ -35,20 +36,16 @@ public HotBarWindow(Canvas gameCanvas) return; } - InitHotbarItems(); - HotbarWindow.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); - - HotbarWindow.SizeToChildren(); - } - - private void InitHotbarItems() - { var hotbarSlotCount = Options.Instance.Player.HotbarSlotCount; for (var hotbarSlotIndex = 0; hotbarSlotIndex < hotbarSlotCount; hotbarSlotIndex++) { var hotbarItem = new HotbarItem(hotbarSlotIndex, HotbarWindow); Items.Add(hotbarItem); } + + // HotbarWindow.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + + HotbarWindow.SizeToChildren(); } public void Update() diff --git a/Intersect.Client.Core/Interface/Game/Hotbar/HotbarItem.cs b/Intersect.Client.Core/Interface/Game/Hotbar/HotbarItem.cs index feb3f3c9aa..9fd9d53b09 100644 --- a/Intersect.Client.Core/Interface/Game/Hotbar/HotbarItem.cs +++ b/Intersect.Client.Core/Interface/Game/Hotbar/HotbarItem.cs @@ -65,8 +65,9 @@ public HotbarItem(int hotbarSlotIndex, Base hotbarWindow) Y = 4 + row * 40, Width = 36, Height = 36, + Margin = new Margin(column > 0 ? 4 : 0, row > 0 ? 4 : 0, 0, 0), + RestrictToParent = true, TextureFilename = "hotbaritem.png", - Margin = Margin.Four, }; // Content Panel is layered on top of the container (shows the Item or Spell Icon). diff --git a/Intersect.Client.Core/Interface/Menu/CreditsWindow.cs b/Intersect.Client.Core/Interface/Menu/CreditsWindow.cs index d44946df6c..03b65fd3a7 100644 --- a/Intersect.Client.Core/Interface/Menu/CreditsWindow.cs +++ b/Intersect.Client.Core/Interface/Menu/CreditsWindow.cs @@ -1,5 +1,7 @@ using Intersect.Client.Core; using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.Graphics; +using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.EventArguments; using Intersect.Client.Localization; @@ -8,30 +10,60 @@ namespace Intersect.Client.Interface.Menu; -public partial class CreditsWindow : ImagePanel, IMainMenuWindow +public partial class CreditsWindow : Window, IMainMenuWindow { - private readonly MainMenu _mainMenu; - private readonly RichLabel _richLabel; + private readonly GameFont? _defaultFont; - public CreditsWindow(Canvas parent, MainMenu mainMenu) : base(parent, "CreditsWindow") + private readonly MainMenu _mainMenu; + private readonly RichLabel _credits; + private readonly Button _backButton; + private readonly ScrollControl _creditsScroller; + + public CreditsWindow(Canvas parent, MainMenu mainMenu) : base( + parent, + title: Strings.Credits.Title, + modal: false, + name: nameof(CreditsWindow) + ) { _mainMenu = mainMenu; - _ = new Label(this, "CreditsHeader") + Alignment = [Alignments.Center]; + MinimumSize = new Point(x: 640, y: 400); + IsResizable = false; + IsClosable = false; + + Titlebar.MouseInputEnabled = false; + + TitleLabel.FontSize = 14; + TitleLabel.TextColorOverride = Color.White; + + _defaultFont = GameContentManager.Current.GetFont(name: TitleLabel.FontName, 12); + + _creditsScroller = new ScrollControl(this, nameof(_creditsScroller)) { - Text = Strings.Credits.Title, + Dock = Pos.Fill, }; + _creditsScroller.EnableScroll(false, true); - var creditsContent = new ScrollControl(this, "CreditsScrollview"); - creditsContent.EnableScroll(false, true); - - _richLabel = new RichLabel(creditsContent, "CreditsLabel"); + _credits = new RichLabel(_creditsScroller, nameof(_credits)) + { + Dock = Pos.Fill, + Font = _defaultFont, + Padding = new Padding(16), + }; - var btnBack = new Button(this, "BackButton") + _backButton = new Button(this, nameof(_backButton)) { + Alignment = [Alignments.CenterH], + AutoSizeToContents = true, + Dock = Pos.Bottom | Pos.CenterH, + Font = _defaultFont, + Margin = new Margin(0, 8), + Padding = new Padding(8, 4), Text = Strings.Credits.Back, }; - btnBack.Clicked += BackBtn_Clicked; + _backButton.Clicked += BackBtn_Clicked; LoadJsonUi(GameContentManager.UI.Menu, Graphics.Renderer?.GetResolutionString()); } @@ -42,10 +74,19 @@ private void BackBtn_Clicked(Base sender, MouseButtonState arguments) _mainMenu.Show(); } - public override void Show() + protected override void RecurseLayout(Framework.Gwen.Skin.Base skin) { - base.Show(); - _richLabel.ClearText(); + base.RecurseLayout(skin); + } + + protected override void EnsureInitialized() + { + if (_credits.FormattedLabels.Count > 0) + { + return; + } + + _credits.ClearText(); var credits = new Credits(); var creditsFile = Path.Combine(ClientConfiguration.ResourcesDirectory, "credits.json"); @@ -71,14 +112,12 @@ public override void Show() foreach (var line in credits?.Lines ?? []) { - if (line.Text.Trim().Length == 0) + var lineText = line.Text.Trim(); + if (lineText.Length > 0) { - _richLabel.AddLineBreak(); - } - else - { - _richLabel.AddText( - line.Text, + var lineFont = GameContentManager.Current.GetFont(line.Font, line.Size); + _credits.AddText( + lineText, new Color( line.TextColor?.A ?? 255, line.TextColor?.R ?? 255, @@ -86,13 +125,13 @@ public override void Show() line.TextColor?.B ?? 255 ), line.GetAlignment(), - GameContentManager.Current.GetFont(line.Font, line.Size) + lineFont ); - - _richLabel.AddLineBreak(); } + + _credits.AddLineBreak(); } - _ = _richLabel.SizeToChildren(false, true); + _ = _credits.SizeToChildren(false, true); } } \ No newline at end of file diff --git a/Intersect.Client.Core/Interface/Menu/LoginWindow.cs b/Intersect.Client.Core/Interface/Menu/LoginWindow.cs index bc04c0a659..6bceda4df5 100644 --- a/Intersect.Client.Core/Interface/Menu/LoginWindow.cs +++ b/Intersect.Client.Core/Interface/Menu/LoginWindow.cs @@ -59,16 +59,16 @@ public LoginWindow(Canvas parent, MainMenu mainMenu) : base(parent, "LoginWindow AutoSizeToContents = false, Dock = Pos.Left, Font = _defaultFont, + Padding = new Padding(0, 0, 10, 0), Text = Strings.LoginWindow.Username, TextAlign = Pos.Right | Pos.CenterV, - TextPadding = new Padding(0, 0, 10, 0), }; _usernameInput = new TextBox(_usernamePanel, nameof(_usernameInput)) { Dock = Pos.Fill, Font = _defaultFont, + Padding = new Padding(2, 0), TextAlign = Pos.Left | Pos.CenterV, - TextPadding = new Padding(2, 0), }; _usernameInput.SubmitPressed += (s, e) => TryLogin(); _usernameInput.Clicked += UsernameInputClicked; @@ -90,16 +90,16 @@ public LoginWindow(Canvas parent, MainMenu mainMenu) : base(parent, "LoginWindow AutoSizeToContents = false, Dock = Pos.Left, Font = _defaultFont, + Padding = new Padding(0, 0, 10, 0), Text = Strings.LoginWindow.Password, TextAlign = Pos.Right | Pos.CenterV, - TextPadding = new Padding(0, 0, 10, 0), }; _passwordInput = new TextBoxPassword(_passwordPanel, nameof(_passwordInput)) { Dock = Pos.Fill, Font = _defaultFont, + Padding = new Padding(2, 0), TextAlign = Pos.Left | Pos.CenterV, - TextPadding = new Padding(2, 0), }; _passwordInput.SubmitPressed += (s, e) => TryLogin(); _passwordInput.TextChanged += PasswordInputTextChanged; diff --git a/Intersect.Client.Core/Interface/Menu/MainMenu.cs b/Intersect.Client.Core/Interface/Menu/MainMenu.cs index 6222aef317..b33faf8254 100644 --- a/Intersect.Client.Core/Interface/Menu/MainMenu.cs +++ b/Intersect.Client.Core/Interface/Menu/MainMenu.cs @@ -52,10 +52,14 @@ public MainMenu(Canvas menuCanvas) : base(menuCanvas) _createCharacterWindow = new CreateCharacterWindow(_menuCanvas, this, SelectCharacterWindow); _settingsWindow = new SettingsWindow(_menuCanvas, this, null) { - AlignmentDistance = new Padding(0, 100, 0, 0), + AlignmentTranslation = new Point(0, 140), + IsVisible = false, + }; + _creditsWindow = new CreditsWindow(_menuCanvas, this) + { + AlignmentTranslation = new Point(0, 140), IsVisible = false, }; - _creditsWindow = new CreditsWindow(_menuCanvas, this); } ~MainMenu() diff --git a/Intersect.Client.Core/Interface/Menu/MainMenuWindow.Designer.cs b/Intersect.Client.Core/Interface/Menu/MainMenuWindow.Designer.cs index 408560f8e5..9f07710711 100644 --- a/Intersect.Client.Core/Interface/Menu/MainMenuWindow.Designer.cs +++ b/Intersect.Client.Core/Interface/Menu/MainMenuWindow.Designer.cs @@ -43,14 +43,13 @@ protected override void EnsureInitialized() ); AddAlignment(Framework.Gwen.Alignments.Center); - AlignmentDistance = new Padding(0, 40, 0, 0); + AlignmentPadding = new Padding(0, 40, 0, 0); ProcessAlignments(); TitleLabel.TextColor = Color.White; TitleLabel.FontName = "sourcesansproblack"; TitleLabel.FontSize = 12; TitleLabel.Padding = new Padding(8, 4, 8, 4); - TitleLabel.TextPadding = new Padding(8, 4, 8, 4); TitleLabel.SizeToContents(); Titlebar.SetBounds(0, 0, Width, TitleLabel.Height); @@ -63,9 +62,9 @@ protected override void EnsureInitialized() button.FontName = "sourcesansproblack"; button.FontSize = 12; + button.Padding = new Padding(0, 8, 0, 0); button.TextColor = Color.White; button.TextColorOverride = Color.White; - button.TextPadding = new Padding(0, 8, 0, 0); button.SetHoverSound("octave-tap-resonant.wav"); var buttonName = button.Name; diff --git a/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs b/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs index b9eca11473..6413fac461 100644 --- a/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs +++ b/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs @@ -24,7 +24,7 @@ namespace Intersect.Client.Interface.Shared; using BottomBarItems = (Panel BottomBar, Button RestoreDefaultControlsButton, Button ApplyPendingChangesButton, Button CancelPendingChangesButton); -public partial class SettingsWindow : WindowControl +public partial class SettingsWindow : Window { private readonly GameFont? _defaultFont; @@ -558,8 +558,6 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) : (_bottomBar, _restoreDefaultsButton, _applyPendingChangesButton, _cancelPendingChangesButton) = CreateBottomBar(this); - - LoadJsonUi(stage: UI.Shared, resolution: Graphics.Renderer?.GetResolutionString()); } public override bool IsBlockingInput => _keybindingEditBtn is not null; @@ -582,7 +580,7 @@ private BottomBarItems CreateBottomBar(Base parent) Font = _defaultFont, IsVisible = false, MinimumSize = new Point(x: 96, y: 24), - TextPadding = new Padding(horizontal: 16, vertical: 2), + Padding = new Padding(horizontal: 16, vertical: 2), Text = Strings.Settings.Restore, }; restoreDefaultKeybindingsButton.Clicked += RestoreDefaultKeybindingsButton_Clicked; @@ -595,7 +593,7 @@ private BottomBarItems CreateBottomBar(Base parent) AutoSizeToContents = true, Font = _defaultFont, MinimumSize = new Point(x: 96, y: 24), - TextPadding = new Padding(horizontal: 16, vertical: 2), + Padding = new Padding(horizontal: 16, vertical: 2), Text = Strings.Settings.Apply, }; applyPendingChangesButton.Clicked += SettingsApplyBtn_Clicked; @@ -608,7 +606,7 @@ private BottomBarItems CreateBottomBar(Base parent) AutoSizeToContents = true, Font = _defaultFont, MinimumSize = new Point(x: 96, y: 24), - TextPadding = new Padding(horizontal: 16, vertical: 2), + Padding = new Padding(horizontal: 16, vertical: 2), Text = Strings.Settings.Cancel, }; cancelPendingChangesButton.Clicked += CancelPendingChangesButton_Clicked; @@ -1186,4 +1184,9 @@ private void CancelPendingChangesButton_Clicked(Base sender, MouseButtonState ar // Hide our current window. Hide(); } + + protected override void EnsureInitialized() + { + LoadJsonUi(stage: UI.Shared, resolution: Graphics.Renderer?.GetResolutionString()); + } } diff --git a/Intersect.Client.Core/Interface/Shared/VersionLabel.cs b/Intersect.Client.Core/Interface/Shared/VersionLabel.cs index bc0727e458..6495fd41a4 100644 --- a/Intersect.Client.Core/Interface/Shared/VersionLabel.cs +++ b/Intersect.Client.Core/Interface/Shared/VersionLabel.cs @@ -12,7 +12,7 @@ public VersionLabel(Base parent, string name = nameof(VersionLabel)) : base(pare AutoSizeToContents = true; Dock = Pos.Top; MouseInputEnabled = true; - TextPadding = new Padding(2, 0); + Padding = new Padding(2, 0); } protected override void Layout(Framework.Gwen.Skin.Base skin) diff --git a/Intersect.Client.Core/Interface/Shared/VersionPanel.cs b/Intersect.Client.Core/Interface/Shared/VersionPanel.cs index 6415c57381..6a7eb1940d 100644 --- a/Intersect.Client.Core/Interface/Shared/VersionPanel.cs +++ b/Intersect.Client.Core/Interface/Shared/VersionPanel.cs @@ -13,7 +13,6 @@ public VersionPanel(Base parent, string name = nameof(VersionPanel)) : base(pare { Alignment = [Alignments.Bottom, Alignments.Right]; BackgroundColor = new Color(0x7f, 0, 0, 0); - Padding = new Padding(8, 4); RestrictToParent = true; // TODO: Remove this when showing a game version is added IsVisible = ApplicationContext.CurrentContext.IsDeveloper; @@ -23,6 +22,7 @@ public VersionPanel(Base parent, string name = nameof(VersionPanel)) : base(pare _engineVersionLabel = new VersionLabel(this, name: nameof(_engineVersionLabel)) { Font = font, + Padding = new Padding(8, 4), Text = ApplicationContext.CurrentContext.VersionName, IsVisible = ApplicationContext.CurrentContext.IsDeveloper, }; diff --git a/Intersect.Client.Core/Localization/Strings.cs b/Intersect.Client.Core/Localization/Strings.cs index 8f9d68e422..3a8c2d5eb7 100644 --- a/Intersect.Client.Core/Localization/Strings.cs +++ b/Intersect.Client.Core/Localization/Strings.cs @@ -938,6 +938,9 @@ public partial struct Debug [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public static LocalizedString EnableLayoutHotReloading = @"Enable Experimental Layout Hot Reloading"; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public static LocalizedString IncludeTextNodesInHover = @"Include Text Nodes in Hover"; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public static LocalizedString EntitiesDrawn = @"Entities Drawn"; @@ -1355,7 +1358,7 @@ public partial struct Internals public static LocalizedString TextAlign = @"Text Align"; [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public static LocalizedString TextPadding = @"Text Padding"; + public static LocalizedString OuterBounds = @"Outer Bounds"; [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public static LocalizedString Font = @"Font"; diff --git a/Intersect.Client.Framework/Gwen/Align.cs b/Intersect.Client.Framework/Gwen/Align.cs index 47caf3bb5d..a41aef6507 100644 --- a/Intersect.Client.Framework/Gwen/Align.cs +++ b/Intersect.Client.Framework/Gwen/Align.cs @@ -39,7 +39,7 @@ public static void AlignLeft(Base control) return; } - control.SetPosition(parent.Padding.Left + control.Margin.Left + control.AlignmentDistance.Left, control.Y); + control.SetPosition(parent.Padding.Left + control.Margin.Left + control.AlignmentPadding.Left, control.Y); } /// @@ -54,7 +54,7 @@ public static void AlignRight(Base control) return; } - var offsetRight = control.Width + control.Margin.Right + control.AlignmentDistance.Right + parent.Padding.Right; + var offsetRight = control.Width + control.Margin.Right + control.AlignmentPadding.Right + parent.Padding.Right; control.SetPosition(parent.Width - offsetRight, control.Y); } @@ -70,7 +70,7 @@ public static void AlignTop(Base control) return; } - control.SetPosition(control.X, control.Margin.Top + parent.Padding.Top + control.AlignmentDistance.Top); + control.SetPosition(control.X, control.Margin.Top + parent.Padding.Top + control.AlignmentPadding.Top); } /// @@ -87,7 +87,7 @@ public static void AlignBottom(Base control) var offsetBottom = control.Height + control.Margin.Bottom + - control.AlignmentDistance.Bottom + + control.AlignmentPadding.Bottom + parent.Padding.Bottom; control.SetPosition(control.X, parent.Height - offsetBottom); } @@ -108,7 +108,7 @@ public static void Center(Base control) var parentPaddingH = parentPadding.Left + parentPadding.Right; var parentPaddingV = parentPadding.Top + parentPadding.Bottom; - Point offset = new(control.AlignmentDistance.Left, control.AlignmentDistance.Top); + Point offset = new(control.AlignmentPadding.Left, control.AlignmentPadding.Top); offset.X += parentPadding.Left; offset.Y += parentPadding.Top; @@ -135,7 +135,7 @@ public static void CenterHorizontally(Base control) var parentPadding = parent.Padding; var parentPaddingH = parentPadding.Left + parentPadding.Right; - Point offset = new(control.AlignmentDistance.Left, control.Y); + Point offset = new(control.AlignmentPadding.Left, control.Y); offset.X += parentPadding.Left; var availableWidth = parent.Width - parentPaddingH; @@ -160,7 +160,7 @@ public static void CenterVertically(Base control) var parentPadding = parent.Padding; var parentPaddingV = parentPadding.Top + parentPadding.Bottom; - Point offset = new(control.X, control.AlignmentDistance.Top); + Point offset = new(control.X, control.AlignmentPadding.Top); offset.Y += parentPadding.Top; var availableHeight = parent.Height - parentPaddingV; diff --git a/Intersect.Client.Framework/Gwen/Control/Base.cs b/Intersect.Client.Framework/Gwen/Control/Base.cs index 1c75d9b4ce..38ade78b7f 100644 --- a/Intersect.Client.Framework/Gwen/Control/Base.cs +++ b/Intersect.Client.Framework/Gwen/Control/Base.cs @@ -63,11 +63,9 @@ internal bool InheritParentEnablementProperties /// private Base? mActualParent; - private Padding mAlignmentDistance; - - private List mAlignments = new List(); - - private Point mAlignmentTransform; + private List _alignments = []; + private Padding _alignmentPadding; + private Point _alignmentTranslation; private Rectangle mBounds; private Rectangle mBoundsOnDisk; @@ -117,7 +115,8 @@ internal bool InheritParentEnablementProperties private string? _name; - private bool mNeedsLayout; + private bool _needsAlignment; + private bool _needsLayout; private Padding mPadding; @@ -238,7 +237,7 @@ public Base(Base? parent = default, string? name = default) mPadding = Padding.Zero; mMargin = Margin.Zero; mColor = Color.White; - mAlignmentDistance = Padding.Zero; + _alignmentPadding = Padding.Zero; RestrictToParent = false; @@ -273,7 +272,7 @@ public GameFont? TooltipFont } } - public List CurAlignments => mAlignments; + public List CurAlignments => _alignments; /// /// Returns true if any on click events are set. @@ -333,14 +332,30 @@ public Base Root public Alignments[] Alignment { - get => mAlignments.ToArray(); + get => _alignments.ToArray(); set { - mAlignments = value.ToList(); + _alignments = value.ToList(); ProcessAlignments(); } } + public Point AlignmentTranslation + { + get => _alignmentTranslation; + set => SetAndDoIfChanged(ref _alignmentTranslation, value, InvalidateAlignment); + } + + public void InvalidateAlignment() + { + if (!_needsAlignment) + { + _needsAlignment = true; + } + + Invalidate(); + } + public virtual bool IsBlockingInput => true; /// @@ -499,21 +514,10 @@ public Padding Padding /// /// Alignment Distance - Minimum distance from parent edges when processing alignments. /// - public Padding AlignmentDistance + public Padding AlignmentPadding { - get => mAlignmentDistance; - set - { - if (mAlignmentDistance == value) - { - return; - } - - mAlignmentDistance = value; - ProcessAlignments(); - Invalidate(); - InvalidateParent(); - } + get => _alignmentPadding; + set => SetAndDoIfChanged(ref _alignmentPadding, value, InvalidateAlignment); } /// @@ -878,7 +882,11 @@ public bool DrawDebugOutlines set { mDrawDebugOutlines = value; - foreach (var child in Children) + if (_innerPanel is { } innerPanel) + { + innerPanel.DrawDebugOutlines = value; + } + foreach (var child in mChildren) { child.DrawDebugOutlines = value; } @@ -975,17 +983,17 @@ public virtual void Dispose() public void AddAlignment(Alignments alignment) { - if (mAlignments?.Contains(alignment) ?? true) + if (_alignments?.Contains(alignment) ?? true) { return; } - mAlignments.Add(alignment); + _alignments.Add(alignment); } public void RemoveAlignments() { - mAlignments.Clear(); + _alignments.Clear(); } public virtual string GetJsonUI(bool isRoot = false) @@ -1031,11 +1039,11 @@ public virtual string GetJsonUI(bool isRoot = false) new JProperty(nameof(Bounds), Rectangle.ToString(boundsToWrite)), new JProperty(nameof(Dock), Dock.ToString()), new JProperty(nameof(Padding), Padding.ToString(mPadding)), - new JProperty("AlignmentEdgeDistances", Padding.ToString(mAlignmentDistance)), - new JProperty("AlignmentTransform", mAlignmentTransform.ToString()), + new JProperty("AlignmentEdgeDistances", Padding.ToString(_alignmentPadding)), + new JProperty("AlignmentTransform", _alignmentTranslation.ToString()), new JProperty(nameof(Margin), mMargin.ToString()), new JProperty(nameof(RenderColor), Color.ToString(mColor)), - new JProperty(nameof(Alignments), string.Join(",", mAlignments.ToArray())), + new JProperty(nameof(Alignments), string.Join(",", _alignments.ToArray())), new JProperty("DrawBackground", mDrawBackground), new JProperty(nameof(MinimumSize), _minimumSize.ToString()), new JProperty(nameof(MaximumSize), _maximumSize.ToString()), @@ -1208,12 +1216,12 @@ public virtual void LoadJson(JToken token, bool isRoot = default) if (obj["AlignmentEdgeDistances"] != null) { - mAlignmentDistance = Padding.FromString((string) obj["AlignmentEdgeDistances"]); + _alignmentPadding = Padding.FromString((string) obj["AlignmentEdgeDistances"]); } if (obj["AlignmentTransform"] != null) { - mAlignmentTransform = Point.FromString((string) obj["AlignmentTransform"]); + _alignmentTranslation = Point.FromString((string) obj["AlignmentTransform"]); } if (obj[nameof(Margin)] != null) @@ -1335,12 +1343,7 @@ public virtual void LoadJson(JToken token, bool isRoot = default) public virtual void ProcessAlignments() { - if (this is Label { Text: "Debug" }) - { - this.ToString(); - } - - foreach (var alignment in mAlignments) + foreach (var alignment in _alignments) { switch (alignment) { @@ -1377,7 +1380,7 @@ public virtual void ProcessAlignments() } } - MoveTo(X + mAlignmentTransform.X, Y + mAlignmentTransform.Y, true); + MoveTo(X + _alignmentTranslation.X, Y + _alignmentTranslation.Y, true); foreach (var child in Children) { child?.ProcessAlignments(); @@ -1547,7 +1550,7 @@ public virtual void SetToolTipText(string? text) AutoSizeToContents = true, Font = _tooltipFont ?? GameContentManager.Current?.GetFont("sourcesansproblack", 10), MaximumSize = new Point(300, 0), - TextPadding = new Padding( + Padding = new Padding( 5, 3 ), @@ -1613,9 +1616,9 @@ protected virtual void InvalidateChildren(bool recursive = false) /// public virtual void Invalidate() { - if (!mNeedsLayout) + if (!_needsLayout) { - mNeedsLayout = true; + _needsLayout = true; } if (!mCacheTextureDirty) @@ -2019,10 +2022,10 @@ public virtual void MoveTo(int x, int y, bool aligning = false) { var ownPadding = Parent.Padding; - var alignmentDistanceLeft = aligning ? mAlignmentDistance.Left : 0; - var alignmentDistanceTop = aligning ? mAlignmentDistance.Top : 0; - var alignmentDistanceRight = aligning ? mAlignmentDistance.Right : 0; - var alignmentDistanceBottom = aligning ? mAlignmentDistance.Bottom : 0; + var alignmentDistanceLeft = aligning ? _alignmentPadding.Left : 0; + var alignmentDistanceTop = aligning ? _alignmentPadding.Top : 0; + var alignmentDistanceRight = aligning ? _alignmentPadding.Right : 0; + var alignmentDistanceBottom = aligning ? _alignmentPadding.Bottom : 0; var parentWidth = parent.Width; var parentHeight = parent.Height; @@ -2156,8 +2159,9 @@ public virtual bool SetBounds(int x, int y, int width, int height) }; var maximumSize = MaximumSize; - newBounds.Width = maximumSize.X > 0 ? Math.Min(MaximumSize.X, width) : width; - newBounds.Height = maximumSize.Y > 0 ? Math.Min(MaximumSize.Y, height) : height; + var minimumSize = MinimumSize; + newBounds.Width = Math.Max(maximumSize.X > 0 ? Math.Min(maximumSize.X, width) : width, minimumSize.X); + newBounds.Height = Math.Max(maximumSize.Y > 0 ? Math.Min(maximumSize.Y, height) : height, minimumSize.Y); if (newBounds.Width > 100000 || newBounds.Height > 100000) { @@ -2877,6 +2881,11 @@ protected virtual void OnChildTouched(Base control) } } + if (this is Text) + { + return filters.HasFlag(ComponentStateFilters.IncludeText) ? this : null; + } + // By default, we only return components that are mouse-input enabled, but if the filters include // those components explicitly, we can return them too. This is particularly useful for debugging. if (MouseInputEnabled || filters.HasFlag(ComponentStateFilters.IncludeMouseInputDisabled)) @@ -2938,12 +2947,18 @@ protected virtual void RecurseLayout(Skin.Base skin) Size = expectedSize; } - if (mNeedsLayout) + if (_needsLayout) { - mNeedsLayout = false; + _needsLayout = false; Layout(skin); } + if (_needsAlignment) + { + _needsAlignment = false; + ProcessAlignments(); + } + var remainingBounds = RenderBounds; // Adjust bounds for padding @@ -2954,20 +2969,15 @@ protected virtual void RecurseLayout(Skin.Base skin) var dockChildSpacing = DockChildSpacing; - foreach (var child in children) - { - if (child.ShouldSkipLayout) - { - continue; - } + var directionalDockChildren = + children.Where(child => !child.ShouldSkipLayout && !child.Dock.HasFlag(Pos.Fill)).ToArray(); + var dockFillChildren = + children.Where(child => !child.ShouldSkipLayout && child.Dock.HasFlag(Pos.Fill)).ToArray(); + foreach (var child in directionalDockChildren) + { var childDock = child.Dock; - if (childDock.HasFlag(Pos.Fill)) - { - continue; - } - var childMargin = child.Margin; var childMarginH = childMargin.Left + childMargin.Right; var childMarginV = childMargin.Top + childMargin.Bottom; @@ -3053,9 +3063,16 @@ protected virtual void RecurseLayout(Skin.Base skin) ? child.Width : availableWidth; - var offsetFromBottom = child.Height + childMargin.Bottom + dockChildSpacing.Bottom; + var offsetFromBottom = child.Height + childMargin.Bottom; + var x = remainingBounds.Left + childMargin.Left; + + if (childDock.HasFlag(Pos.CenterH)) + { + x = remainingBounds.Left + (remainingBounds.Width - child.OuterWidth) / 2; + } + child.SetBounds( - remainingBounds.Left + childMargin.Left, + x, remainingBounds.Bottom - offsetFromBottom, width, child.Height @@ -3072,20 +3089,10 @@ protected virtual void RecurseLayout(Skin.Base skin) // // Fill uses the left over space, so do that now. // - foreach (var child in children) + foreach (var child in dockFillChildren) { - if (child.ShouldSkipLayout) - { - continue; - } - var dock = child.Dock; - if (!dock.HasFlag(Pos.Fill)) - { - continue; - } - var childMargin = child.Margin; var childMarginH = childMargin.Left + childMargin.Right; var childMarginV = childMargin.Top + childMargin.Bottom; @@ -3353,14 +3360,33 @@ public virtual bool DragAndDrop_CanAcceptPackage(Package p) /// /// Determines whether to change control's width. /// Determines whether to change control's height. + /// /// True if bounds changed. - public virtual bool SizeToChildren(bool width = true, bool height = true) + public virtual bool SizeToChildren(bool width = true, bool height = true, bool recursive = false) { - var size = GetChildrenSize(); + Base[]? children = null; + if (recursive) + { + children = mChildren.ToArray(); + foreach (var child in children) + { + if (!child.IsVisible) + { + continue; + } + + child.SizeToChildren(width: width, height: height, recursive: recursive); + } + } + + var size = GetChildrenSize(children: children); var padding = Padding; size.X += padding.Right + padding.Left; size.Y += padding.Bottom + padding.Top; + size.X = Math.Max(Math.Min(size.X, _maximumSize.X < 1 ? size.X : _maximumSize.X), _minimumSize.X); + size.Y = Math.Max(Math.Min(size.Y, _maximumSize.Y < 1 ? size.Y : _maximumSize.Y), _minimumSize.Y); + if (!SetSize(width ? size.X : Width, height ? size.Y : Height)) { return false; @@ -3375,16 +3401,16 @@ public virtual bool SizeToChildren(bool width = true, bool height = true) /// Returns the total width and height of all children. /// /// - /// Default implementation returns maximum size of children since the layout is unknown. + /// Default implementation InvalidateAlignmentreturns maximum size of children since the layout is unknown. /// Implement this in derived compound controls to properly return their size. /// /// - public virtual Point GetChildrenSize() + public virtual Point GetChildrenSize(Base[]? children = null) { Point min = new(int.MaxValue, int.MaxValue); Point max = default; - var children = mChildren.ToArray(); + children ??= mChildren.ToArray(); foreach (var child in children) { if (!child.IsVisible) diff --git a/Intersect.Client.Framework/Gwen/Control/Button.cs b/Intersect.Client.Framework/Gwen/Control/Button.cs index f12f37f5e4..1c6e302732 100644 --- a/Intersect.Client.Framework/Gwen/Control/Button.cs +++ b/Intersect.Client.Framework/Gwen/Control/Button.cs @@ -36,14 +36,17 @@ public partial class Button : Label /// Parent control. /// /// - public Button(Base parent, string? name = default, bool disableText = false) : base(parent, name, disableText) + public Button(Base parent, string? name = default, bool disableText = false) : base( + parent: parent, + name: name, + disableText: disableText + ) { AutoSizeToContents = false; - Size = new Point(100, 20); MouseInputEnabled = true; TextAlign = Pos.Center; - TextPadding = new Padding(3, 3, 3, 3); - Name = name; + Size = new Point(100, 20); + Padding = new Padding(3, 3, 3, 3); } /// @@ -365,9 +368,17 @@ public override void UpdateColors() textColor = new Color(r: 255, g: 0, b: 255); } - if ((textColor.ToArgb() & 0xffffff) == 0) + if (textColor != TextColor) { - textColor.ToString(); + ApplicationContext.CurrentContext.Logger.LogTrace( + "Changing TextColor to {TextColor} of {ComponentType} '{ComponentName}' IsDisabled={IsDisabled} IsActive={IsActive} IsHovered={IsHovered}", + GetType().GetName(qualified: true), + CanonicalName, + textColor, + IsDisabled, + IsActive, + IsHovered + ); } TextColor = textColor; diff --git a/Intersect.Client.Framework/Gwen/Control/ComboBox.cs b/Intersect.Client.Framework/Gwen/Control/ComboBox.cs index 03ed30726c..3414d9edb0 100644 --- a/Intersect.Client.Framework/Gwen/Control/ComboBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/ComboBox.cs @@ -255,15 +255,6 @@ protected override void OnFontChanged(Base sender, GameFont? oldFont, GameFont? } } - /// - /// Renders the control using specified skin. - /// - /// Skin to use. - protected override void Render(Skin.Base skin) - { - skin.DrawComboBox(this, IsActive, IsOpen); - } - public override void Disable() { base.Disable(); @@ -346,6 +337,18 @@ protected override void Layout(Skin.Base skin) base.Layout(skin); } + /// + /// Renders the control using specified skin. + /// + /// Skin to use. + protected override void Render(Skin.Base skin) + { + // skin.DrawRectFill(OuterBounds, Color.FromArgb(0x7f, 0xff, 0, 0)); + // skin.DrawRectFill(Bounds with { X = 0, Y = 0 }, Color.FromArgb(0xff, 0, 0xff, 0)); + // skin.DrawRectFill(InnerBounds, Color.FromArgb(0x7f, 0, 0, 0xff)); + skin.DrawComboBox(this, IsActive, IsOpen); + } + /// /// Handler for losing keyboard focus. /// diff --git a/Intersect.Client.Framework/Gwen/Control/ComponentStateFilters.cs b/Intersect.Client.Framework/Gwen/Control/ComponentStateFilters.cs index 808437cbe8..86c8b5901c 100644 --- a/Intersect.Client.Framework/Gwen/Control/ComponentStateFilters.cs +++ b/Intersect.Client.Framework/Gwen/Control/ComponentStateFilters.cs @@ -4,7 +4,8 @@ namespace Intersect.Client.Framework.Gwen.Control; public enum ComponentStateFilters { None, - - IncludeHidden, - IncludeMouseInputDisabled, + + IncludeHidden = 1, + IncludeMouseInputDisabled = 2, + IncludeText = 4, } \ No newline at end of file diff --git a/Intersect.Client.Framework/Gwen/Control/GroupBox.cs b/Intersect.Client.Framework/Gwen/Control/GroupBox.cs index 26cd23626f..eb4e78c2b3 100644 --- a/Intersect.Client.Framework/Gwen/Control/GroupBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/GroupBox.cs @@ -22,11 +22,11 @@ public GroupBox(Base parent) : base(parent) MouseInputEnabled = true; KeyboardInputEnabled = true; - TextPadding = new Padding(10, 0, 10, 0); + Padding = new Padding(10, 0, 10, 0); TextAlign = Pos.Top | Pos.Left; Invalidate(); - _innerPanel = new Base(this); + _innerPanel = new Base(this, name: nameof(_innerPanel)); _innerPanel.Dock = Pos.Fill; _innerPanel.Margin = new Margin(5, TextHeight + 5, 5, 5); @@ -65,7 +65,7 @@ protected virtual bool DoSizeToContents() var sizeChanged = _innerPanel?.SizeToChildren() ?? false; sizeChanged &= SizeToChildren(); - var textWidth = TextWidth + TextPadding.Right + TextPadding.Left; + var textWidth = TextWidth + Padding.Right + Padding.Left; // ReSharper disable once InvertIf if (Width < textWidth) diff --git a/Intersect.Client.Framework/Gwen/Control/ILabel.cs b/Intersect.Client.Framework/Gwen/Control/ILabel.cs index 211465a54d..4270a4db06 100644 --- a/Intersect.Client.Framework/Gwen/Control/ILabel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ILabel.cs @@ -12,12 +12,12 @@ public interface ILabel : IAutoSizeToContents, IColorableText, ITextContainer int FontSize { get; set; } + Padding Padding { get; set; } + Color? TextColor { get; set; } Color? TextColorOverride { get; set; } - Padding TextPadding { get; set; } - /// /// Resizes the component to fit the contents. /// diff --git a/Intersect.Client.Framework/Gwen/Control/ITextContainer.cs b/Intersect.Client.Framework/Gwen/Control/ITextContainer.cs index 029d2e9731..53009e104e 100644 --- a/Intersect.Client.Framework/Gwen/Control/ITextContainer.cs +++ b/Intersect.Client.Framework/Gwen/Control/ITextContainer.cs @@ -2,9 +2,9 @@ namespace Intersect.Client.Framework.Gwen.Control; public interface ITextContainer { - string? Text { get; set; } + Padding Padding { get; set; } - Padding TextPadding { get; set; } + string? Text { get; set; } Color? TextPaddingDebugColor { get; set; } } \ No newline at end of file diff --git a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs index de23670bb9..8dd43860dc 100644 --- a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs @@ -324,10 +324,10 @@ protected override void Render(Skin.Base skin) { _ninepatchRenderer ??= new Bordered( texture, - _uv[0] * texture.Width, - _uv[1] * texture.Height, - _uv[2] * texture.Width, - _uv[3] * texture.Height, + (int)(_uv[0] * texture.Width), + (int)(_uv[1] * texture.Height), + (int)(_uv[2] * texture.Width), + (int)(_uv[3] * texture.Height), textureNinePatchMargin ); diff --git a/Intersect.Client.Framework/Gwen/Control/Label.cs b/Intersect.Client.Framework/Gwen/Control/Label.cs index beefd50a03..4ad43f83c0 100644 --- a/Intersect.Client.Framework/Gwen/Control/Label.cs +++ b/Intersect.Client.Framework/Gwen/Control/Label.cs @@ -313,6 +313,8 @@ public Color? TextColorOverride set => _textElement.ColorOverride = value; } + public Padding TextPadding { get; set; } + private string? _textOverride; private WrappingBehavior _wrappingBehavior; private GameTexture? _tooltipBackground; @@ -370,20 +372,6 @@ public bool AutoSizeToContents } } - /// - /// Text padding. - /// - public Padding TextPadding - { - get => _textPadding; - set - { - _textPadding = value; - Invalidate(); - InvalidateParent(); - } - } - public Color? TextPaddingDebugColor { get; set; } public override JObject? GetJson(bool isRoot = false, bool onlySerializeIfNotEmpty = false) @@ -404,7 +392,6 @@ public Padding TextPadding serializedProperties.Add("ClickedTextColor", mClickedTextColor?.ToString()); serializedProperties.Add("DisabledTextColor", mDisabledTextColor?.ToString()); serializedProperties.Add(nameof(TextAlign), TextAlign.ToString()); - serializedProperties.Add(nameof(TextPadding), _textPadding.ToString()); serializedProperties.Add(nameof(AutoSizeToContents), _autoSizeToContents); serializedProperties.Add(nameof(Font), _fontInfo); serializedProperties.Add("TextScale", _textElement.GetScale()); @@ -450,11 +437,6 @@ public override void LoadJson(JToken obj, bool isRoot = default) TextAlign = (Pos)Enum.Parse(typeof(Pos), (string)obj["TextAlign"]); } - if (obj["TextPadding"] != null) - { - TextPadding = Padding.FromString((string)obj["TextPadding"]); - } - if (obj["AutoSizeToContents"] != null) { _autoSizeToContents = (bool)obj["AutoSizeToContents"]; @@ -647,30 +629,31 @@ protected void AlignTextElement(Text textElement) var align = TextAlign; var textOuterWidth = textElement.OuterWidth; var textOuterHeight = textElement.OuterHeight; - var textPadding = TextPadding; + var textPadding = Padding; var availableWidth = Width - (textPadding.Left + textPadding.Right); var availableHeight = Height - (textPadding.Top + textPadding.Bottom); + Rectangle contentBounds = new Rectangle(textPadding.Left, textPadding.Top, availableWidth, availableHeight); var x = textPadding.Left; var y = textPadding.Top; if (align.HasFlag(Pos.CenterH)) { - x = textPadding.Left + (int)((availableWidth - textOuterWidth) / 2f); + x = textPadding.Left + (int)((contentBounds.Width - textOuterWidth) / 2f); } else if (align.HasFlag(Pos.Right)) { - x = availableWidth - textOuterWidth; + x = contentBounds.Right - textOuterWidth; } if (align.HasFlag(Pos.CenterV)) { - y = textPadding.Top + (int)((availableHeight - textOuterHeight) / 2f); + y = textPadding.Top + (int)((contentBounds.Height - textOuterHeight) / 2f); } else if (align.HasFlag(Pos.Bottom)) { - y = availableHeight - textOuterHeight; + y = contentBounds.Bottom - textOuterHeight; } textElement.SetPosition(x, y); @@ -873,4 +856,9 @@ public virtual void SetTextColor(Color clr, ComponentState state) return null; } } + + public override string ToString() + { + return $"Label (Text='{Text}')"; + } } \ No newline at end of file diff --git a/Intersect.Client.Framework/Gwen/Control/LabeledCheckBox.cs b/Intersect.Client.Framework/Gwen/Control/LabeledCheckBox.cs index b5b79a2dc2..aaa8f14ca4 100644 --- a/Intersect.Client.Framework/Gwen/Control/LabeledCheckBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/LabeledCheckBox.cs @@ -39,8 +39,8 @@ public LabeledCheckBox(Base parent, string? name = default) : base(parent: paren Dock = Pos.Fill | Pos.CenterV, InheritParentEnablementProperties = true, IsTabable = false, + Padding = new Padding(2/*, 0, 0, 0*/), TextAlign = Pos.CenterV | Pos.Left, - TextPadding = new Padding(2/*, 0, 0, 0*/), }; _label.Clicked += delegate(Base control, MouseButtonState _) { diff --git a/Intersect.Client.Framework/Gwen/Control/LabeledComboBox.cs b/Intersect.Client.Framework/Gwen/Control/LabeledComboBox.cs index 5e962d7e81..92ce63926a 100644 --- a/Intersect.Client.Framework/Gwen/Control/LabeledComboBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/LabeledComboBox.cs @@ -26,8 +26,8 @@ public LabeledComboBox(Base parent, string? name = default) : base(parent: paren { Alignment = [Alignments.CenterV], Dock = Pos.Left, - Margin = new Margin(4, 0, 0, 0), - TextPadding = Padding.Two, + Margin = new Margin(8, 0, 0, 0), + Padding = Padding.Two, }; _comboBox.ItemSelected += (_, args) => ItemSelected?.Invoke(this, args); @@ -84,16 +84,16 @@ public bool AutoSizeToContents set => _autoSizeToContents = value; } - public Padding LabelTextPadding + public Padding LabelPadding { - get => _label.TextPadding; - set => _label.TextPadding = value; + get => _label.Padding; + set => _label.Padding = value; } public Padding TextPadding { - get => _comboBox.TextPadding; - set => _comboBox.TextPadding = value; + get => _comboBox.Padding; + set => _comboBox.Padding = value; } protected override void Layout(Skin.Base skin) diff --git a/Intersect.Client.Framework/Gwen/Control/LabeledSlider.cs b/Intersect.Client.Framework/Gwen/Control/LabeledSlider.cs index 7283ff8f00..97be01d90d 100644 --- a/Intersect.Client.Framework/Gwen/Control/LabeledSlider.cs +++ b/Intersect.Client.Framework/Gwen/Control/LabeledSlider.cs @@ -211,7 +211,7 @@ private Point ComputeMinimumSizeForSliderValue(double? value = null) return Skin.Renderer.MeasureText(_sliderValue.Font, valueString) + _sliderValue.Padding + - _sliderValue.TextPadding; + _sliderValue.Padding; } public double Scale diff --git a/Intersect.Client.Framework/Gwen/Control/Layout/Table.cs b/Intersect.Client.Framework/Gwen/Control/Layout/Table.cs index d61259b06c..361f8acec1 100644 --- a/Intersect.Client.Framework/Gwen/Control/Layout/Table.cs +++ b/Intersect.Client.Framework/Gwen/Control/Layout/Table.cs @@ -654,11 +654,6 @@ public void Invalidate(bool invalidateChildren, bool invalidateRecursive = true) protected override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { - if (Bounds.Height == 800) - { - Bounds.ToString(); - } - base.OnBoundsChanged(oldBounds, newBounds); } diff --git a/Intersect.Client.Framework/Gwen/Control/Layout/TableRow.cs b/Intersect.Client.Framework/Gwen/Control/Layout/TableRow.cs index 6cb4f31ba3..d456169d43 100644 --- a/Intersect.Client.Framework/Gwen/Control/Layout/TableRow.cs +++ b/Intersect.Client.Framework/Gwen/Control/Layout/TableRow.cs @@ -257,15 +257,15 @@ protected override void OnMouseEntered() /// public event GwenEventHandler Selected; - public override bool SizeToChildren(bool width = true, bool height = true) + public bool SizeToChildren(bool width = true, bool height = true, bool recursive = false) { var columns = _columns.ToArray(); foreach (var column in columns) { - column.SizeToChildren(width: width, height: height); + column.SizeToChildren(width: width, height: height, recursive: recursive); } - return base.SizeToChildren(width: width, height: height); + return base.SizeToChildren(width: width, height: height, recursive: recursive); } protected virtual void ComputeColumns() diff --git a/Intersect.Client.Framework/Gwen/Control/Menu.cs b/Intersect.Client.Framework/Gwen/Control/Menu.cs index 6c59cd9e45..dd6ccb22aa 100644 --- a/Intersect.Client.Framework/Gwen/Control/Menu.cs +++ b/Intersect.Client.Framework/Gwen/Control/Menu.cs @@ -173,7 +173,7 @@ public virtual MenuItem AddItem( { Font = font, Text = text, - TextPadding = new Padding(8), + Padding = new Padding(8), }; newMenuItem.SetStateTexture(iconTexture, textureFilename, ComponentState.Normal); newMenuItem.SetAccelerator(accelerator); @@ -189,7 +189,7 @@ public virtual MenuItem AddItem( /// Item added. protected virtual void OnAddItem(MenuItem menuItem) { - menuItem.TextPadding = new Padding(IconMarginDisabled ? 8 : 32, 4, 8, 4); + menuItem.Padding = new Padding(IconMarginDisabled ? 8 : 32, 4, 8, 4); menuItem.Dock = Pos.Top; menuItem.SizeToContents(); menuItem.TextAlign = Pos.CenterV | Pos.Left; @@ -293,9 +293,9 @@ public virtual void AddDivider() divider.Margin = new Margin(IconMarginDisabled ? 0 : 24, 0, 4, 0); } - public override bool SizeToChildren(bool width = true, bool height = true) + public override bool SizeToChildren(bool width = true, bool height = true, bool recursive = false) { - base.SizeToChildren(width, height); + base.SizeToChildren(width: width, height: height, recursive: recursive); if (width) { var maxWidth = 0; diff --git a/Intersect.Client.Framework/Gwen/Control/MenuStrip.cs b/Intersect.Client.Framework/Gwen/Control/MenuStrip.cs index 88bfb1ef4c..290f365cd5 100644 --- a/Intersect.Client.Framework/Gwen/Control/MenuStrip.cs +++ b/Intersect.Client.Framework/Gwen/Control/MenuStrip.cs @@ -63,7 +63,6 @@ protected override void Layout(Skin.Base skin) protected override void OnAddItem(MenuItem item) { item.Dock = Pos.Left; - item.TextPadding = new Padding(5, 0, 5, 0); item.Padding = new Padding(10, 0, 10, 0); item.SizeToContents(); item.IsOnStrip = true; diff --git a/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs b/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs index d821dfcc08..e7f1bcbf11 100644 --- a/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs @@ -34,8 +34,8 @@ public MultilineTextBox(Base parent) : base(parent) MouseInputEnabled = true; KeyboardInputEnabled = true; + Padding = new Padding(4, 2, 4, 2); TextAlign = Pos.Left | Pos.Top; - TextPadding = new Padding(4, 2, 4, 2); mCursorPos = new Point(0, 0); mCursorEnd = new Point(0, 0); @@ -46,7 +46,7 @@ public MultilineTextBox(Base parent) : base(parent) IsTabable = false; AcceptTabs = true; - mScrollControl = new ScrollControl(this); + mScrollControl = new ScrollControl(this, name: nameof(_innerPanel)); mScrollControl.Dock = Pos.Fill; mScrollControl.SetOverflow(OverflowBehavior.Auto, OverflowBehavior.Auto); mScrollControl.Margin = Margin.One; @@ -1060,15 +1060,15 @@ protected virtual void MakeCaretVisible() var idealx = (int) (-caretPos + Width * 0.5f); // Don't show too much whitespace to the right - if (idealx + TextWidth < Width - TextPadding.Right - Padding.Right) + if (idealx + TextWidth < Width - Padding.Right) { - idealx = -TextWidth + (Width - TextPadding.Right - Padding.Right); + idealx = -TextWidth + (Width - Padding.Right); } // Or the left - if (idealx > TextPadding.Left + Padding.Left) + if (idealx > Padding.Left) { - idealx = TextPadding.Left + Padding.Left; + idealx = Padding.Left; } SetTextPosition(idealx, TextY); @@ -1145,7 +1145,7 @@ private Point GetCharacterPosition(Point cursorPosition) var p = new Point(Skin.Renderer.MeasureText(Font, currLine).X, Skin.Renderer.MeasureText(Font, sub).Y); - return new Point(p.X + _textElement.X, p.Y + _textElement.Y + TextPadding.Top); + return new Point(p.X + _textElement.X, p.Y + _textElement.Y + Padding.Top); } protected override bool OnMouseWheeled(int delta) diff --git a/Intersect.Client.Framework/Gwen/Control/ProgressBar.cs b/Intersect.Client.Framework/Gwen/Control/ProgressBar.cs index 209e87c0d7..30627b4899 100644 --- a/Intersect.Client.Framework/Gwen/Control/ProgressBar.cs +++ b/Intersect.Client.Framework/Gwen/Control/ProgressBar.cs @@ -22,7 +22,7 @@ public ProgressBar(Base parent) : base(parent) AutoSizeToContents = false; SetSize(128, 32); - TextPadding = Padding.Three; + Padding = Padding.Three; IsHorizontal = true; TextAlign = Pos.Center; diff --git a/Intersect.Client.Framework/Gwen/Control/RichLabel.cs b/Intersect.Client.Framework/Gwen/Control/RichLabel.cs index a279609482..dcad5fe753 100644 --- a/Intersect.Client.Framework/Gwen/Control/RichLabel.cs +++ b/Intersect.Client.Framework/Gwen/Control/RichLabel.cs @@ -11,27 +11,35 @@ namespace Intersect.Client.Framework.Gwen.Control; /// public partial class RichLabel : Base { + private readonly string[] _newlines = new[]{ Environment.NewLine, "\n" }.Distinct().ToArray(); + private readonly List _textBlocks = []; + private readonly List