Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SettingsLib/Settings/CollapsibleSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public virtual List<Setting> GetSettings()
return settings;
}

public virtual void OnToggled(bool IsExpanded) { }

public override void ApplyValue()
{
foreach (Setting setting in GetSettings())
Expand Down
92 changes: 52 additions & 40 deletions SettingsLib/Settings/UI/CollapsibleSettingUI.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Localization;
Expand All @@ -11,32 +12,36 @@ 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();
}

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();
}
}
Expand All @@ -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<SettingsUICell>(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++)
{
Expand All @@ -95,49 +101,70 @@ private void ApplyEnclosingStyling()
child.gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
}

// Breaks without this
// This is needed to force a layout.
transform.parent.gameObject.AddComponent<VerticalLayoutGroup>();

MakeSettingBoxAdaptable();
}

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<VerticalLayoutGroup>();
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<ContentSizeFitter>().verticalFit =
ContentSizeFitter.FitMode.PreferredSize;
}

/// <summary>
/// Adds a new setting block. If a title exists, it is added here.
/// </summary>
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)
/// <summary>
/// Instantiates a title object and attaches it to the given parent.
/// </summary>
private void AddSettingTitle(LocalizedString titleText, Transform parentTransform)
{
if (setting is IExposedSetting exposedSetting)
{
transform
.GetComponentInChildren<LocalizeUIText>()
?.SetString(exposedSetting.GetDisplayName());
}
var titleObj = new GameObject("SettingTitle");
titleObj.transform.SetParent(parentTransform, false);
var textMesh = titleObj.AddComponent<TextMeshProUGUI>();
textMesh.enableAutoSizing = true;
textMesh.alignment = TextAlignmentOptions.Center;
var localizeUIText = titleObj.AddComponent<LocalizeUIText>();
localizeUIText.SetString(titleText);
var layout = titleObj.AddComponent<LayoutElement>();
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<ILayoutElement?>();
if (layoutElement is null)
{
Expand All @@ -155,25 +182,10 @@ private void AddSetting(Setting setting, Transform transform, ISettingHandler se
ui.GetComponent<SettingInputUICell>().Setup(setting, settingHandler);
}

private void SetUpTitleObject()
{
var textMesh = titleObject.AddComponent<TextMeshProUGUI>();
textMesh.enableAutoSizing = true;
textMesh.alignment = TextAlignmentOptions.Center;
var text = titleObject.AddComponent<LocalizeUIText>();
text.String = null;
var layout = titleObject.AddComponent<LayoutElement>();
layout.preferredHeight = 20;

UnityEngine.Object.DontDestroyOnLoad(titleObject);
}

private void SetUpSettingObject()
{
titleObject.transform.SetParent(settingObject.transform, false);

var layout = settingObject.AddComponent<VerticalLayoutGroup>();
// Little breathing room
// Add a little breathing room.
layout.spacing = 2;
layout.padding.top = 13;
settingObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter
Expand Down
78 changes: 39 additions & 39 deletions SettingsLib/Settings/UI/DynamicSettingListUI.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.UI;
using Zorro.Localization;
using Zorro.Settings;
Expand All @@ -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();
}

Expand Down Expand Up @@ -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<SettingsUICell>(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++)
{
Expand All @@ -89,20 +87,20 @@ private void ApplyEnclosingStyling()
child.gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
}

// Breaks without this
// This is required to force the layout.
transform.parent.gameObject.AddComponent<VerticalLayoutGroup>();

MakeSettingBoxAdaptable();
}

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<VerticalLayoutGroup>();
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<ContentSizeFitter>().verticalFit =
Expand All @@ -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<LocalizeUIText>()
?.SetString(exposedSetting.GetDisplayName());
}
var titleObj = new GameObject("SettingTitle");
titleObj.transform.SetParent(parentTransform, false);

var textMesh = titleObj.AddComponent<TextMeshProUGUI>();
textMesh.enableAutoSizing = true;
textMesh.alignment = TextAlignmentOptions.Center;

var localizeUIText = titleObj.AddComponent<LocalizeUIText>();
localizeUIText.SetString(titleText);

var layout = titleObj.AddComponent<LayoutElement>();
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<ILayoutElement?>();
if (layoutElement is null)
{
Expand All @@ -149,25 +164,10 @@ private void AddSetting(Setting setting, Transform transform, ISettingHandler se
ui.GetComponent<SettingInputUICell>().Setup(setting, settingHandler);
}

private void SetUpTitleObject()
{
var textMesh = titleObject.AddComponent<TextMeshProUGUI>();
textMesh.enableAutoSizing = true;
textMesh.alignment = TextAlignmentOptions.Center;
var text = titleObject.AddComponent<LocalizeUIText>();
text.String = null;
var layout = titleObject.AddComponent<LayoutElement>();
layout.preferredHeight = 20;

UnityEngine.Object.DontDestroyOnLoad(titleObject);
}

private void SetUpSettingObject()
{
titleObject.transform.SetParent(settingObject.transform, false);

var layout = settingObject.AddComponent<VerticalLayoutGroup>();
// Little breathing room
// Little breathing room.
layout.spacing = 2;
layout.padding.top = 13;
settingObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter
Expand Down