diff --git a/SettingsLib/Settings/CollapsibleSetting.cs b/SettingsLib/Settings/CollapsibleSetting.cs index 449bc96..b37b6a9 100644 --- a/SettingsLib/Settings/CollapsibleSetting.cs +++ b/SettingsLib/Settings/CollapsibleSetting.cs @@ -23,6 +23,8 @@ public virtual List GetSettings() return settings; } + public virtual void OnToggled(bool IsExpanded) { } + public override void ApplyValue() { foreach (Setting setting in GetSettings()) diff --git a/SettingsLib/Settings/UI/CollapsibleSettingUI.cs b/SettingsLib/Settings/UI/CollapsibleSettingUI.cs index dbbfefd..727aede 100644 --- a/SettingsLib/Settings/UI/CollapsibleSettingUI.cs +++ b/SettingsLib/Settings/UI/CollapsibleSettingUI.cs @@ -1,5 +1,6 @@ using TMPro; using UnityEngine; +using UnityEngine.Localization; using UnityEngine.UI; using Zorro.Core; using Zorro.Localization; @@ -11,17 +12,16 @@ public class CollapsibleSettingUI : SettingInputUICell { // Track expanded state private bool _expanded = false; - private GameObject titleObject = new GameObject("SettingTitle"); + private CollapsibleSetting? _collapsibleSetting; + + // We no longer have a persistent titleObject; we create title objects on demand. private GameObject settingObject = new GameObject("SettingCell"); - private string collapseButtonText - { - get => _expanded ? "▼ Collapse" : "► Expand"; - } + private string collapseButtonText => _expanded ? "▼ Collapse" : "► Expand"; public CollapsibleSettingUI() : base() { - SetUpTitleObject(); + // Removed SetUpTitleObject call – title objects are now created as needed. SetUpSettingObject(); } @@ -29,14 +29,19 @@ public override void Setup(Setting setting, ISettingHandler settingHandler) { if (setting is CollapsibleSetting group) { + _collapsibleSetting = group; + ApplyEnclosingStyling(); AddCollapseButton(); - // Run setup for all settings, except non-shown conditionals + // Run setup for all settings, except non-shown conditionals. foreach (var subsetting in group.GetSettings()) + { if (!(subsetting is IConditionalSetting conditional) || conditional.CanShow()) + { AddSettingBlock(subsetting, settingHandler); - + } + } SetVisibility(); } } @@ -62,30 +67,31 @@ private void OnToggled() { _expanded = !_expanded; SetVisibility(); + _collapsibleSetting?.OnToggled(_expanded); } private void SetVisibility() { - // Skip the first child, as it is the collapse button. + // Skip the first child (collapse button) when toggling visibility. for (var i = 1; i < transform.childCount; i++) { var child = transform.GetChild(i); child.gameObject.SetActive(_expanded); } - // Trigger all ContentSizeFitters + // Trigger a layout update. gameObject.SendMessageUpwards("SetLayoutHorizontal"); gameObject.SendMessageUpwards("SetLayoutVertical"); } private void ApplyEnclosingStyling() { - // Only run for a top-level CollapsibleSettingUICell + // Only run for a top-level CollapsibleSettingUICell. if (!transform.parent.parent.gameObject.TryGetComponent(out var comp)) { return; } - // Make the top level setting box's background and text ignore the Layout + // Make the top-level setting box's background and text ignore the layout. int idx = transform.parent.GetSiblingIndex(); for (int i = 0; i < transform.parent.parent.childCount; i++) { @@ -95,7 +101,7 @@ private void ApplyEnclosingStyling() child.gameObject.AddComponent().ignoreLayout = true; } - // Breaks without this + // This is needed to force a layout. transform.parent.gameObject.AddComponent(); MakeSettingBoxAdaptable(); @@ -103,41 +109,62 @@ private void ApplyEnclosingStyling() private void MakeSettingBoxAdaptable() { - // Make the setting box as tall as necessary + // Make the setting box as tall as necessary. var vlg = transform.parent.parent.gameObject.AddComponent(); vlg.childControlWidth = false; vlg.spacing = 10; vlg.childAlignment = TextAnchor.MiddleRight; - // Give the bounding box some breathing room + // Give the bounding box some breathing room. vlg.padding.top = 15; vlg.padding.bottom = 15; transform.parent.parent.gameObject.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; } + /// + /// Adds a new setting block. If a title exists, it is added here. + /// private void AddSettingBlock(Setting setting, ISettingHandler settingHandler) { var block = UnityEngine.Object.Instantiate(settingObject, transform); - AddSettingTitle(setting, block.transform); + // Check if the setting has a title. + LocalizedString? titleText = null; + if (setting is IExposedSetting exposedSetting) + { + titleText = exposedSetting.GetDisplayName(); + } + + // Only add a title if it is non-null and non-empty. + if (titleText != null && !titleText.IsEmpty) + { + AddSettingTitle(titleText, block.transform); + } + AddSetting(setting, block.transform, settingHandler); } - private void AddSettingTitle(Setting setting, Transform transform) + /// + /// Instantiates a title object and attaches it to the given parent. + /// + private void AddSettingTitle(LocalizedString titleText, Transform parentTransform) { - if (setting is IExposedSetting exposedSetting) - { - transform - .GetComponentInChildren() - ?.SetString(exposedSetting.GetDisplayName()); - } + var titleObj = new GameObject("SettingTitle"); + titleObj.transform.SetParent(parentTransform, false); + var textMesh = titleObj.AddComponent(); + textMesh.enableAutoSizing = true; + textMesh.alignment = TextAlignmentOptions.Center; + var localizeUIText = titleObj.AddComponent(); + localizeUIText.SetString(titleText); + var layout = titleObj.AddComponent(); + layout.preferredHeight = 20; } private void AddSetting(Setting setting, Transform transform, ISettingHandler settingHandler) { var ui = UnityEngine.Object.Instantiate(setting.GetSettingUICell(), transform); - // Give the field a height so it doesn't get destroyed by the layout + // Ensure the element retains a preferred height so it isn’t collapsed by the layout. var layoutElement = ui.GetComponent(); if (layoutElement is null) { @@ -155,25 +182,10 @@ private void AddSetting(Setting setting, Transform transform, ISettingHandler se ui.GetComponent().Setup(setting, settingHandler); } - private void SetUpTitleObject() - { - var textMesh = titleObject.AddComponent(); - textMesh.enableAutoSizing = true; - textMesh.alignment = TextAlignmentOptions.Center; - var text = titleObject.AddComponent(); - text.String = null; - var layout = titleObject.AddComponent(); - layout.preferredHeight = 20; - - UnityEngine.Object.DontDestroyOnLoad(titleObject); - } - private void SetUpSettingObject() { - titleObject.transform.SetParent(settingObject.transform, false); - var layout = settingObject.AddComponent(); - // Little breathing room + // Add a little breathing room. layout.spacing = 2; layout.padding.top = 13; settingObject.AddComponent().verticalFit = ContentSizeFitter diff --git a/SettingsLib/Settings/UI/DynamicSettingListUI.cs b/SettingsLib/Settings/UI/DynamicSettingListUI.cs index fa9a03f..9ae2ba6 100644 --- a/SettingsLib/Settings/UI/DynamicSettingListUI.cs +++ b/SettingsLib/Settings/UI/DynamicSettingListUI.cs @@ -1,5 +1,6 @@ using TMPro; using UnityEngine; +using UnityEngine.Localization; using UnityEngine.UI; using Zorro.Localization; using Zorro.Settings; @@ -10,19 +11,14 @@ public class DynamicSettingListUI : SettingInputUICell { // Track expanded state private bool _expanded = false; - private GameObject titleObject = new GameObject("SettingTitle"); private GameObject settingObject = new GameObject("SettingCell"); private DynamicSettingList? _setting; private ISettingHandler _settingHandler = GameHandler.Instance.SettingsHandler; - private string collapseButtonText - { - get => _expanded ? "▼ Collapse" : "► Expand"; - } + private string collapseButtonText => _expanded ? "▼ Collapse" : "► Expand"; public DynamicSettingListUI() : base() { - SetUpTitleObject(); SetUpSettingObject(); } @@ -65,21 +61,23 @@ private void AddChildren() if (_setting == null) return; - // Run setup for all settings, except non-shown conditionals + // Run setup for all settings, except non-shown conditionals. foreach (var subsetting in _setting.GetSettings()) + { if (!(subsetting is IConditionalSetting conditional) || conditional.CanShow()) AddSettingBlock(subsetting, _settingHandler); + } } private void ApplyEnclosingStyling() { - // Only run for a top-level DynamicSettingListUICell + // Only run for a top-level DynamicSettingListUI. if (!transform.parent.parent.gameObject.TryGetComponent(out var comp)) { return; } - // Make the top level setting box's background and text ignore the Layout + // Make the top level setting box's background and text ignore the layout. int idx = transform.parent.GetSiblingIndex(); for (int i = 0; i < transform.parent.parent.childCount; i++) { @@ -89,7 +87,7 @@ private void ApplyEnclosingStyling() child.gameObject.AddComponent().ignoreLayout = true; } - // Breaks without this + // This is required to force the layout. transform.parent.gameObject.AddComponent(); MakeSettingBoxAdaptable(); @@ -97,12 +95,12 @@ private void ApplyEnclosingStyling() private void MakeSettingBoxAdaptable() { - // Make the setting box as tall as necessary + // Make the setting box as tall as necessary. var vlg = transform.parent.parent.gameObject.AddComponent(); vlg.childControlWidth = false; vlg.spacing = 10; vlg.childAlignment = TextAnchor.MiddleRight; - // Give the bounding box some breathing room + // Give the bounding box some breathing room. vlg.padding.top = 15; vlg.padding.bottom = 15; transform.parent.parent.gameObject.AddComponent().verticalFit = @@ -111,27 +109,44 @@ private void MakeSettingBoxAdaptable() private void AddSettingBlock(Setting setting, ISettingHandler settingHandler) { + // Instantiate a new container using settingObject as template. var block = UnityEngine.Object.Instantiate(settingObject, transform); - AddSettingTitle(setting, block.transform); + // Check if the setting defines a title. + if (setting is IExposedSetting exposedSetting) + { + LocalizedString titleText = exposedSetting.GetDisplayName(); + if (titleText != null && !titleText.IsEmpty) + { + AddSettingTitle(titleText, block.transform); + } + } + AddSetting(setting, block.transform, settingHandler); } - private void AddSettingTitle(Setting setting, Transform transform) + private void AddSettingTitle(LocalizedString titleText, Transform parentTransform) { - if (setting is IExposedSetting exposedSetting) - { - transform - .GetComponentInChildren() - ?.SetString(exposedSetting.GetDisplayName()); - } + var titleObj = new GameObject("SettingTitle"); + titleObj.transform.SetParent(parentTransform, false); + + var textMesh = titleObj.AddComponent(); + textMesh.enableAutoSizing = true; + textMesh.alignment = TextAlignmentOptions.Center; + + var localizeUIText = titleObj.AddComponent(); + localizeUIText.SetString(titleText); + + var layout = titleObj.AddComponent(); + layout.preferredHeight = 20; } - private void AddSetting(Setting setting, Transform transform, ISettingHandler settingHandler) + private void AddSetting(Setting setting, Transform parent, ISettingHandler settingHandler) { - var ui = UnityEngine.Object.Instantiate(setting.GetSettingUICell(), transform); + var ui = UnityEngine.Object.Instantiate(setting.GetSettingUICell(), parent); - // Give the field a height so it doesn't get destroyed by the layout + // Ensure the element gets a preferred height so it isn’t collapsed by the + // layout. var layoutElement = ui.GetComponent(); if (layoutElement is null) { @@ -149,25 +164,10 @@ private void AddSetting(Setting setting, Transform transform, ISettingHandler se ui.GetComponent().Setup(setting, settingHandler); } - private void SetUpTitleObject() - { - var textMesh = titleObject.AddComponent(); - textMesh.enableAutoSizing = true; - textMesh.alignment = TextAlignmentOptions.Center; - var text = titleObject.AddComponent(); - text.String = null; - var layout = titleObject.AddComponent(); - layout.preferredHeight = 20; - - UnityEngine.Object.DontDestroyOnLoad(titleObject); - } - private void SetUpSettingObject() { - titleObject.transform.SetParent(settingObject.transform, false); - var layout = settingObject.AddComponent(); - // Little breathing room + // Little breathing room. layout.spacing = 2; layout.padding.top = 13; settingObject.AddComponent().verticalFit = ContentSizeFitter