From 26b3de8a1dde786bc65cf2d82dd58b3cea3f142f Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Fri, 23 Jan 2026 14:49:33 +0100 Subject: [PATCH] Update KTK, Swap Dropdowns to EnumDropdowns --- AetherBags/Addons/CategoryListItemNode.cs | 2 +- AetherBags/Configuration/CategorySettings.cs | 4 ++ AetherBags/Configuration/GeneralSettings.cs | 8 +++ AetherBags/Extensions/EnumExtensions.cs | 52 +++++++++++++++++++ .../CategoryGeneralConfigurationNode.cs | 38 ++++++-------- .../Configuration/Category/RangeFilterRow.cs | 9 ++-- .../Category/StringListEditorNode.cs | 14 ++--- .../Category/UintListEditorNode.cs | 12 ++--- .../General/FunctionalConfigurationNode.cs | 32 +++++------- AetherBags/Nodes/Input/LabeledDropdownNode.cs | 32 +++++++----- .../Nodes/Input/TextInputWithButtonNode.cs | 4 +- .../Nodes/Inventory/InventoryFooterNode.cs | 4 +- .../Inventory/InventoryNotificationNode.cs | 4 +- .../Nodes/Inventory/SaddleBagFooterNode.cs | 3 +- 14 files changed, 141 insertions(+), 77 deletions(-) create mode 100644 AetherBags/Extensions/EnumExtensions.cs diff --git a/AetherBags/Addons/CategoryListItemNode.cs b/AetherBags/Addons/CategoryListItemNode.cs index 2933d7a..b6d6a9c 100644 --- a/AetherBags/Addons/CategoryListItemNode.cs +++ b/AetherBags/Addons/CategoryListItemNode.cs @@ -1,4 +1,4 @@ -using KamiToolKit.Premade.GenericSearchListItemNodes; +using KamiToolKit.Premade.GenericListItemNodes; namespace AetherBags.Addons; diff --git a/AetherBags/Configuration/CategorySettings.cs b/AetherBags/Configuration/CategorySettings.cs index 9d288b8..0dedceb 100644 --- a/AetherBags/Configuration/CategorySettings.cs +++ b/AetherBags/Configuration/CategorySettings.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Numerics; using System.Text.Json.Serialization; using KamiToolKit.Classes; @@ -84,6 +85,9 @@ public enum ToggleFilterState public enum PluginFilterMode { + [Description("Create New Categories")] Categorize = 0, + + [Description("Apply Highlight Only")] Highlight = 1, } \ No newline at end of file diff --git a/AetherBags/Configuration/GeneralSettings.cs b/AetherBags/Configuration/GeneralSettings.cs index 79583f5..a68e97c 100644 --- a/AetherBags/Configuration/GeneralSettings.cs +++ b/AetherBags/Configuration/GeneralSettings.cs @@ -1,3 +1,5 @@ +using System.ComponentModel; + namespace AetherBags.Configuration; public class GeneralSettings @@ -21,12 +23,18 @@ public class GeneralSettings public enum InventoryStackMode : byte { + [Description("Split Stacks (Game Default)")] NaturalStacks = 0, + + [Description("Merge Stacks (By Item ID)")] AggregateByItemId = 1, } public enum SearchMode : byte { + [Description("Filter (Hide non-matches)")] Filter = 0, + + [Description("Highlight (Dim non-matches)")] Highlight = 1, } \ No newline at end of file diff --git a/AetherBags/Extensions/EnumExtensions.cs b/AetherBags/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..8f5a0ee --- /dev/null +++ b/AetherBags/Extensions/EnumExtensions.cs @@ -0,0 +1,52 @@ +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +using Dalamud.Utility; + +namespace KamiToolKit.Extensions; + +internal static class EnumExtensions { + extension(Enum enumValue) { + public string Description => enumValue.GetDescription(); + + private string GetDescription() { + var attribute = enumValue.GetAttribute(); + return attribute?.Description ?? enumValue.ToString(); + } + } + + extension(ref T flagValue) where T : unmanaged, Enum { + public void SetFlags(params T[] flags) { + foreach (var flag in flags) { + flagValue.SetFlag(flag, true); + } + } + + public void ClearFlags(params T[] flags) { + foreach (var flag in flags) { + flagValue.SetFlag(flag, false); + } + } + + private unsafe void SetFlag(T flag, bool enable) { + switch (sizeof(T)) { + case 1: flagValue.SetFlag(flag, enable); break; + case 2: flagValue.SetFlag(flag, enable); break; + case 4: flagValue.SetFlag(flag, enable); break; + case 8: flagValue.SetFlag(flag, enable); break; + default: throw new NotSupportedException("Unsupported enum size"); + } + } + + private void SetFlag(T flag, bool enable) where TUnderlying : unmanaged, IBinaryInteger { + ref var value = ref Unsafe.As(ref flagValue); + var mask = Unsafe.As(ref flag); + + if (enable) + value |= mask; + else + value &= ~mask; + } + } +} diff --git a/AetherBags/Nodes/Configuration/Category/CategoryGeneralConfigurationNode.cs b/AetherBags/Nodes/Configuration/Category/CategoryGeneralConfigurationNode.cs index 39038de..8f90f36 100644 --- a/AetherBags/Nodes/Configuration/Category/CategoryGeneralConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/Category/CategoryGeneralConfigurationNode.cs @@ -80,24 +80,21 @@ public sealed class CategoryGeneralConfigurationNode : TabbedVerticalListNode bool bisBuddyReady = System.IPC.BisBuddy?.IsReady ?? false; - LabeledDropdownNode? bbModeDropdown = new LabeledDropdownNode + LabeledDropdownNode? bbModeDropdown = new LabeledDropdownNode { - Size = new Vector2(300, 20), + Size = new Vector2(500, 20), LabelText = "Filter Display Mode", LabelTextFlags = TextFlags.AutoAdjustNodeSize, IsEnabled = config.BisBuddyEnabled && bisBuddyReady, - Options = Enum.GetNames(typeof(PluginFilterMode)).ToList(), - SelectedOption = config.BisBuddyMode.ToString(), + Options = Enum.GetValues().ToList(), + SelectedOption = config.BisBuddyMode, OnOptionSelected = selected => { - if (Enum.TryParse(selected, out var parsed)) - { - config.BisBuddyMode = parsed; - if (parsed == PluginFilterMode.Categorize) - HighlightState.ClearFilter(HighlightSource.AllaganTools); + config.BisBuddyMode = selected; + if (selected == PluginFilterMode.Categorize) + HighlightState.ClearFilter(HighlightSource.AllaganTools); - RefreshInventory(); - } + RefreshInventory(); } }; @@ -121,24 +118,23 @@ public sealed class CategoryGeneralConfigurationNode : TabbedVerticalListNode bool allaganReady = System.IPC.AllaganTools?.IsReady ?? false; - LabeledDropdownNode? atModeDropdown = new LabeledDropdownNode + LabeledDropdownNode? atModeDropdown = new LabeledDropdownNode { - Size = new Vector2(300, 20), + Size = new Vector2(500, 20), LabelText = "Filter Display Mode", LabelTextFlags = TextFlags.AutoAdjustNodeSize, IsEnabled = config.AllaganToolsCategoriesEnabled && allaganReady, - Options = Enum.GetNames(typeof(PluginFilterMode)).ToList(), - SelectedOption = config.AllaganToolsFilterMode.ToString(), + Options = Enum.GetValues().ToList(), + SelectedOption = config.AllaganToolsFilterMode, OnOptionSelected = selected => { - if (Enum.TryParse(selected, out var parsed)) + config.AllaganToolsFilterMode = selected; + if (selected == PluginFilterMode.Categorize) { - config.AllaganToolsFilterMode = parsed; - if (parsed == PluginFilterMode.Categorize) - HighlightState.ClearFilter(HighlightSource.AllaganTools); - - RefreshInventory(); + HighlightState.ClearFilter(HighlightSource.AllaganTools); } + + RefreshInventory(); } }; diff --git a/AetherBags/Nodes/Configuration/Category/RangeFilterRow.cs b/AetherBags/Nodes/Configuration/Category/RangeFilterRow.cs index 9304a42..a7c32df 100644 --- a/AetherBags/Nodes/Configuration/Category/RangeFilterRow.cs +++ b/AetherBags/Nodes/Configuration/Category/RangeFilterRow.cs @@ -3,6 +3,7 @@ using System.Numerics; using AetherBags.Configuration; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; namespace AetherBags.Nodes.Configuration.Category; @@ -14,9 +15,9 @@ public sealed class RangeFilterRow : VerticalListNode public Action? OnFilterChanged { get; set; } - public required string Label + public required ReadOnlySeString Label { - get => _enabledCheckbox.String.Replace(" Filter", ""); + get => _enabledCheckbox.String.ExtractText().Replace(" Filter", ""); init => _enabledCheckbox.String = $"{value} Filter"; } @@ -113,9 +114,9 @@ public sealed class RangeFilterRowUint : VerticalListNode public Action? OnFilterChanged { get; set; } - public required string Label + public required ReadOnlySeString Label { - get => _enabledCheckbox.String.Replace(" Filter", ""); + get => _enabledCheckbox.String.ExtractText().Replace(" Filter", ""); init => _enabledCheckbox.String = $"{value} Filter"; } diff --git a/AetherBags/Nodes/Configuration/Category/StringListEditorNode.cs b/AetherBags/Nodes/Configuration/Category/StringListEditorNode.cs index 5cbcd65..6238a44 100644 --- a/AetherBags/Nodes/Configuration/Category/StringListEditorNode.cs +++ b/AetherBags/Nodes/Configuration/Category/StringListEditorNode.cs @@ -4,6 +4,7 @@ using System.Numerics; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; namespace AetherBags.Nodes.Configuration.Category; @@ -16,12 +17,11 @@ public sealed class StringListEditorNode : VerticalListNode private readonly LabelTextNode _headerLabel; private readonly VerticalListNode _itemsContainer; - private readonly HorizontalListNode _addRow; private readonly TextInputNode _addInput; public Action? OnChanged { get; set; } - public required string Label + public required ReadOnlySeString Label { get => _headerLabel.String; init => _headerLabel.String = value; @@ -49,7 +49,7 @@ public sealed class StringListEditorNode : VerticalListNode }; AddNode(_itemsContainer); - _addRow = new HorizontalListNode + var addRow = new HorizontalListNode { Size = new Vector2(LabelWidth + 40f, RowHeight), ItemSpacing = 4.0f, @@ -61,7 +61,7 @@ public sealed class StringListEditorNode : VerticalListNode PlaceholderString = "Add new...", OnInputComplete = _ => AddCurrentValue(), }; - _addRow.AddNode(_addInput); + addRow.AddNode(_addInput); var addButton = new TextButtonNode { @@ -69,9 +69,9 @@ public sealed class StringListEditorNode : VerticalListNode String = "Add", OnClick = AddCurrentValue, }; - _addRow.AddNode(addButton); + addRow.AddNode(addButton); - AddNode(_addRow); + AddNode(addRow); } public void SetList(List newList) @@ -82,7 +82,7 @@ public sealed class StringListEditorNode : VerticalListNode private void AddCurrentValue() { - var value = _addInput.String; + var value = _addInput.String.ExtractText(); if (!string.IsNullOrWhiteSpace(value) && !_list.Contains(value)) { _list.Add(value); diff --git a/AetherBags/Nodes/Configuration/Category/UintListEditorNode.cs b/AetherBags/Nodes/Configuration/Category/UintListEditorNode.cs index 458ab75..18612ed 100644 --- a/AetherBags/Nodes/Configuration/Category/UintListEditorNode.cs +++ b/AetherBags/Nodes/Configuration/Category/UintListEditorNode.cs @@ -4,6 +4,7 @@ using System.Numerics; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; namespace AetherBags.Nodes.Configuration.Category; @@ -16,13 +17,12 @@ public sealed class UintListEditorNode : VerticalListNode private readonly LabelTextNode _headerLabel; private readonly VerticalListNode _itemsContainer; - private readonly HorizontalListNode _addRow; private readonly NumericInputNode _addInput; public Func? LabelResolver { get; init; } public Action? OnChanged { get; set; } - public required string Label + public required ReadOnlySeString Label { get => _headerLabel.String; init => _headerLabel.String = value; @@ -50,7 +50,7 @@ public sealed class UintListEditorNode : VerticalListNode }; AddNode(_itemsContainer); - _addRow = new HorizontalListNode + var addRow = new HorizontalListNode { Size = new Vector2(LabelWidth + 40f, RowHeight), ItemSpacing = 4.0f, @@ -63,7 +63,7 @@ public sealed class UintListEditorNode : VerticalListNode Max = int.MaxValue, Value = 0, }; - _addRow.AddNode(_addInput); + addRow.AddNode(_addInput); var addButton = new TextButtonNode { @@ -71,9 +71,9 @@ public sealed class UintListEditorNode : VerticalListNode String = "Add", OnClick = AddCurrentValue, }; - _addRow.AddNode(addButton); + addRow.AddNode(addButton); - AddNode(_addRow); + AddNode(addRow); } public void SetList(List newList) diff --git a/AetherBags/Nodes/Configuration/General/FunctionalConfigurationNode.cs b/AetherBags/Nodes/Configuration/General/FunctionalConfigurationNode.cs index 825af76..cbb3d68 100644 --- a/AetherBags/Nodes/Configuration/General/FunctionalConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/General/FunctionalConfigurationNode.cs @@ -14,7 +14,7 @@ internal sealed class FunctionalConfigurationNode : TabbedVerticalListNode private readonly CheckboxNode _hideDefaultBagsCheckboxNode; private readonly CheckboxNode _hideSaddlebagsCheckboxNode; private readonly CheckboxNode _hideRetainerbagsCheckboxNode; - private readonly LabeledDropdownNode _stackDropDown; + private readonly LabeledDropdownNode _stackDropDown; public FunctionalConfigurationNode() { @@ -139,39 +139,33 @@ internal sealed class FunctionalConfigurationNode : TabbedVerticalListNode Height = 6 }); - var searchModeDropDown = new LabeledDropdownNode + var searchModeDropDown = new LabeledDropdownNode { - Size = new Vector2(300, 20), + Size = new Vector2(500, 20), LabelText = "Search Mode", LabelTextFlags = TextFlags.AutoAdjustNodeSize, - Options = Enum.GetNames(typeof(SearchMode)).ToList(), - SelectedOption = config.SearchMode.ToString(), + Options = Enum.GetValues().ToList(), + SelectedOption = config.SearchMode, OnOptionSelected = selected => { - if (Enum.TryParse(selected, out var parsed)) - { - config.SearchMode = parsed; - InventoryOrchestrator.RefreshAll(updateMaps: false); - } + config.SearchMode = selected; + InventoryOrchestrator.RefreshAll(updateMaps: false); } }; AddNode(searchModeDropDown); - _stackDropDown = new LabeledDropdownNode + _stackDropDown = new LabeledDropdownNode { - Size = new Vector2(300, 20), + Size = new Vector2(500, 20), IsEnabled = true, LabelText = "Stack Mode", LabelTextFlags = TextFlags.AutoAdjustNodeSize, - Options = Enum.GetNames(typeof(InventoryStackMode)).ToList(), - SelectedOption = config.StackMode.ToString(), + Options = Enum.GetValues().ToList(), + SelectedOption = config.StackMode, OnOptionSelected = selected => { - if (Enum.TryParse(selected, out var parsed)) - { - config.StackMode = parsed; - InventoryOrchestrator.RefreshAll(updateMaps: true); - } + config.StackMode = selected; + InventoryOrchestrator.RefreshAll(updateMaps: true); } }; AddNode(_stackDropDown); diff --git a/AetherBags/Nodes/Input/LabeledDropdownNode.cs b/AetherBags/Nodes/Input/LabeledDropdownNode.cs index e2149c5..4c92b89 100644 --- a/AetherBags/Nodes/Input/LabeledDropdownNode.cs +++ b/AetherBags/Nodes/Input/LabeledDropdownNode.cs @@ -2,13 +2,14 @@ using System; using System.Collections.Generic; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; namespace AetherBags.Nodes.Input; -public class LabeledDropdownNode : SimpleComponentNode { +public class LabeledDropdownNode : SimpleComponentNode where T : Enum { private readonly GridNode _gridNode; private readonly TextNode _labelNode; - private readonly TextDropDownNode _dropDownNode; + private readonly EnumDropDownNode _dropDownNode; public LabeledDropdownNode() { _gridNode = new GridNode { @@ -17,12 +18,12 @@ public class LabeledDropdownNode : SimpleComponentNode { _gridNode.AttachNode(this); _labelNode = new LabelTextNode { - String = String.Empty, + String = string.Empty, }; _labelNode.AttachNode(_gridNode[0, 0]); - _dropDownNode = new TextDropDownNode { - Options = new List(), + _dropDownNode = new EnumDropDownNode { + Options = new List(), }; _dropDownNode.AttachNode(_gridNode[1, 0]); } @@ -36,25 +37,32 @@ public class LabeledDropdownNode : SimpleComponentNode { _dropDownNode.Size = _gridNode[1, 0].Size; } - public required string LabelText + public required ReadOnlySeString LabelText { get => _labelNode.String; set => _labelNode.String = value; } - public Action? OnOptionSelected + public Action? OnOptionSelected { get => _dropDownNode.OnOptionSelected; set => _dropDownNode.OnOptionSelected = value; } - public string? SelectedOption + public T? SelectedOption { - get => _dropDownNode.SelectedOption; - set => _dropDownNode.SelectedOption = value; + get => _dropDownNode.OptionListNode.SelectedOption; + set + { + _dropDownNode.OptionListNode.SelectedOption = value; + if (value != null) + { + _dropDownNode.LabelNode.String = value.Description; + } + } } - public required List Options + public required List Options { get => _dropDownNode.Options!; set => _dropDownNode.Options = value; @@ -65,4 +73,4 @@ public class LabeledDropdownNode : SimpleComponentNode { get => _labelNode.TextFlags; set => _labelNode.TextFlags = value; } -} +} \ No newline at end of file diff --git a/AetherBags/Nodes/Input/TextInputWithButtonNode.cs b/AetherBags/Nodes/Input/TextInputWithButtonNode.cs index 8b9d56f..8136840 100644 --- a/AetherBags/Nodes/Input/TextInputWithButtonNode.cs +++ b/AetherBags/Nodes/Input/TextInputWithButtonNode.cs @@ -49,7 +49,7 @@ public class TextInputWithButtonNode : SimpleComponentNode { } public ReadOnlySeString SearchString { - get => _textInputNode.SeString; - set => _textInputNode.SeString = value; + get => _textInputNode.String; + set => _textInputNode.String = value; } } \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryFooterNode.cs b/AetherBags/Nodes/Inventory/InventoryFooterNode.cs index f05aa2f..ef0f8df 100644 --- a/AetherBags/Nodes/Inventory/InventoryFooterNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryFooterNode.cs @@ -5,7 +5,7 @@ using AetherBags.Nodes.Currency; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; - +using Lumina.Text.ReadOnly; using static AetherBags.Inventory.State.InventoryStateBase; namespace AetherBags.Nodes.Inventory; @@ -60,7 +60,7 @@ public sealed class InventoryFooterNode : SimpleComponentNode }); } - public string SlotAmountText + public ReadOnlySeString SlotAmountText { get => _slotAmountTextNode.String; set => _slotAmountTextNode.String = value; diff --git a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs index 68d1a2a..786b4c0 100644 --- a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs @@ -74,8 +74,8 @@ public sealed class InventoryNotificationNode : SimpleComponentNode { field = value; - titleTextNode.SeString = value.Title; - messageTextNode.SeString = value.Message; + titleTextNode.String = value.Title; + messageTextNode.String = value.Message; if (value.Title.IsEmpty && value.Message.IsEmpty) { diff --git a/AetherBags/Nodes/Inventory/SaddleBagFooterNode.cs b/AetherBags/Nodes/Inventory/SaddleBagFooterNode.cs index e3f078e..505bc05 100644 --- a/AetherBags/Nodes/Inventory/SaddleBagFooterNode.cs +++ b/AetherBags/Nodes/Inventory/SaddleBagFooterNode.cs @@ -1,6 +1,7 @@ using System. Numerics; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; namespace AetherBags.Nodes.Inventory; @@ -23,7 +24,7 @@ public class SaddleBagFooterNode : SimpleComponentNode _slotCounterNode.AttachNode(this); } - public string SlotAmountText + public ReadOnlySeString SlotAmountText { get => _slotCounterNode.String; set => _slotCounterNode.String = $"Slots: {value}";