diff --git a/Intersect.Client.Core/Interface/Game/Admin/BanMuteBox.cs b/Intersect.Client.Core/Interface/Game/Admin/BanMuteBox.cs index c2b10bc919..cd097652bb 100644 --- a/Intersect.Client.Core/Interface/Game/Admin/BanMuteBox.cs +++ b/Intersect.Client.Core/Interface/Game/Admin/BanMuteBox.cs @@ -8,47 +8,23 @@ namespace Intersect.Client.Interface.Game.Admin; public partial class BanMuteBox : WindowControl { private readonly TextBox _textboxReason; - private readonly ComboBox _comboboxDuration; + private readonly ComboBox? _comboboxDuration; private readonly LabeledCheckBox _checkboxIP; - private readonly Dictionary _dayCount = new() + private static readonly List<(string Label, int Days)> _durationOptions = new() { - { - Strings.BanMute.OneDay, 1 - }, - { - Strings.BanMute.TwoDays, 2 - }, - { - Strings.BanMute.ThreeDays, 3 - }, - { - Strings.BanMute.FourDays, 4 - }, - { - Strings.BanMute.FiveDays, 5 - }, - { - Strings.BanMute.OneWeek, 7 - }, - { - Strings.BanMute.TwoWeeks, 14 - }, - { - Strings.BanMute.OneMonth, 30 - }, - { - Strings.BanMute.TwoMonths, 60 - }, - { - Strings.BanMute.SixMonths, 180 - }, - { - Strings.BanMute.OneYear, 365 - }, - { - Strings.BanMute.Forever, 999999 - }, + (Strings.BanMute.OneDay, 1), + (Strings.BanMute.TwoDays, 2), + (Strings.BanMute.ThreeDays, 3), + (Strings.BanMute.FourDays, 4), + (Strings.BanMute.FiveDays, 5), + (Strings.BanMute.OneWeek, 7), + (Strings.BanMute.TwoWeeks, 14), + (Strings.BanMute.OneMonth, 30), + (Strings.BanMute.TwoMonths, 60), + (Strings.BanMute.SixMonths, 180), + (Strings.BanMute.OneYear, 365), + (Strings.BanMute.Forever, 999999), }; public BanMuteBox(string title, string prompt, EventHandler okayHandler) : base( @@ -84,9 +60,9 @@ public BanMuteBox(string title, string prompt, EventHandler okayHandler) : base( // Duration combobox _comboboxDuration = new ComboBox(this, "ComboBoxDuration"); - foreach (var day in _dayCount) + foreach (var option in _durationOptions) { - _ = _comboboxDuration.AddItem(day.Key, userData: day.Value); + _ = _comboboxDuration.AddItem(option.Label, userData: option.Days); } // Include IP checkbox @@ -103,17 +79,23 @@ public BanMuteBox(string title, string prompt, EventHandler okayHandler) : base( buttonOkay.Clicked += (s, e) => { okayHandler?.Invoke(this, EventArgs.Empty); - Dispose(); + Close(); }; var buttonCancel = new Button(this, "ButtonCancel") { Text = Strings.BanMute.Cancel, }; - buttonCancel.Clicked += (s, e) => Dispose(); + buttonCancel.Clicked += (s, e) => Close(); LoadJsonUi(UI.InGame, Graphics.Renderer?.GetResolutionString(), true); + // Set the first element by default after loading the UI (ensures deterministic selection). + if (_comboboxDuration != null && _durationOptions.Count > 0) + { + _ = _comboboxDuration.SelectByUserData(_durationOptions[0].Days); + } + richLabelPrompt.ClearText(); richLabelPrompt.Width = promptContainer.Width - promptContainer.VerticalScrollBar.Width; richLabelPrompt.AddText(prompt, labelPrompt); @@ -122,24 +104,34 @@ public BanMuteBox(string title, string prompt, EventHandler okayHandler) : base( protected override void Dispose(bool disposing) { - Close(); - Interface.GameUi.GameCanvas.RemoveChild(this, false); + Interface.InputBlockingComponents.Remove(this); + + if (_textboxReason != null) + { + Interface.FocusComponents.Remove(_textboxReason); + } base.Dispose(disposing); } public int GetDuration() { - return (int)_comboboxDuration.SelectedItem.UserData; + var selected = _comboboxDuration?.SelectedItem; + + if (selected?.UserData is int days) + { + return days; + } + return 1; // default value should be 1 day for safety } public string GetReason() { - return _textboxReason.Text; + return _textboxReason?.Text ?? string.Empty; } public bool BanIp() { - return _checkboxIP.IsChecked; + return _checkboxIP?.IsChecked ?? false; } } \ No newline at end of file diff --git a/Intersect.Client.Framework/Gwen/Control/Base.cs b/Intersect.Client.Framework/Gwen/Control/Base.cs index b49cb19e8a..7ae04cd14b 100644 --- a/Intersect.Client.Framework/Gwen/Control/Base.cs +++ b/Intersect.Client.Framework/Gwen/Control/Base.cs @@ -1295,23 +1295,27 @@ private static void PropagateDrawDebugOutlinesToChildren(Base @this, bool drawDe /// public void Dispose() { - try + // TODO : GWEN: We need to spend more time on this. + if (_disposed) { - ObjectDisposedException.ThrowIf(_disposed, this); - } - catch - { - throw; + return; } + // This stack trace is only used for diagnostics, so it is included only in DEBUG builds. +#if DEBUG _disposeStack = new StackTrace(fNeedFileInfo: true); +#endif + // We mark that Dispose() has been called to prevent Dispose from being executed again _disposed = true; + // Remove all pending tasks from the thread queue of this control _threadQueue.ClearPending(); + // Virtual Dispose() so that inherited classes can release their resources Dispose(disposing: true); + // Reserve a temporary variable for the texture caching mechanism ICacheToTexture? cache = default; #pragma warning disable CA1031 // Do not catch general exception types @@ -1325,34 +1329,35 @@ public void Dispose() } #pragma warning restore CA1031 // Do not catch general exception types + // If the control was cached in the texture cache, free that memory if (ShouldCacheToTexture && cache != null) { cache.DisposeCachedTexture(this); } + // Clear input indicators if they point to this control if (InputHandler.HoveredControl == this) - { InputHandler.HoveredControl = null; - } - if (InputHandler.KeyboardFocus == this) - { InputHandler.KeyboardFocus = null; - } - if (InputHandler.MouseFocus == this) - { InputHandler.MouseFocus = null; - } DragAndDrop.ControlDeleted(this); ToolTip.ControlDeleted(this); Animation.Cancel(this); + // Remove and release all child controls DisposeChildrenOf(this); + // TODO : check if we need to dispose tooltip + // if (_tooltip != null) + // _tooltip.Dispose(); + + // Prevents the finalizer from restarting for this object GC.SuppressFinalize(this); + // Informs that the object has completed the resource release process _disposeCompleted = true; try @@ -1370,6 +1375,14 @@ public void Dispose() } } + /// + /// Releases resources used by the control. + /// When is true, both managed and unmanaged resources + /// should be released. When false (finalizer), only unmanaged resources should be released. + /// Override this method in derived classes to dispose additional resources and ensure + /// to call the base implementation. + /// + /// True when called from , false when called from finalizer. protected virtual void Dispose(bool disposing) { } @@ -1591,7 +1604,7 @@ public void LoadJsonUi(GameContentManager.UI stage, string? resolution = default } catch (Exception exception) { - //Log JSON UI Loading Error + // Log JSON UI Loading Error throw new Exception("Error loading json ui for " + ParentQualifiedName, exception); } } @@ -1717,30 +1730,30 @@ public virtual void LoadJson(JToken token, bool isRoot = default) if (obj["DrawBackground"] != null) { - ShouldDrawBackground = (bool) obj["DrawBackground"]; + ShouldDrawBackground = (bool)obj["DrawBackground"]; } if (obj["Disabled"] != null) { - IsDisabled = (bool) obj["Disabled"]; + IsDisabled = (bool)obj["Disabled"]; } if (obj["Hidden"] != null) { - IsHidden = (bool) obj["Hidden"]; + IsHidden = (bool)obj["Hidden"]; } if (obj[nameof(RestrictToParent)] != null) { - RestrictToParent = (bool) obj[nameof(RestrictToParent)]; + RestrictToParent = (bool)obj[nameof(RestrictToParent)]; } if (obj[nameof(MouseInputEnabled)] != null) { - MouseInputEnabled = (bool) obj[nameof(MouseInputEnabled)]; + MouseInputEnabled = (bool)obj[nameof(MouseInputEnabled)]; } - if (obj["HideToolTip"] != null && (bool) obj["HideToolTip"]) + if (obj["HideToolTip"] != null && (bool)obj["HideToolTip"]) { _tooltipEnabled = false; SetToolTipText(null);