Update KTK, Swap Dropdowns to EnumDropdowns

This commit is contained in:
Zeffuro
2026-01-23 14:49:33 +01:00
parent 0fee7d0954
commit 26b3de8a1d
14 changed files with 141 additions and 77 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
using KamiToolKit.Premade.GenericSearchListItemNodes; using KamiToolKit.Premade.GenericListItemNodes;
namespace AetherBags.Addons; namespace AetherBags.Addons;
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using KamiToolKit.Classes; using KamiToolKit.Classes;
@@ -84,6 +85,9 @@ public enum ToggleFilterState
public enum PluginFilterMode public enum PluginFilterMode
{ {
[Description("Create New Categories")]
Categorize = 0, Categorize = 0,
[Description("Apply Highlight Only")]
Highlight = 1, Highlight = 1,
} }
@@ -1,3 +1,5 @@
using System.ComponentModel;
namespace AetherBags.Configuration; namespace AetherBags.Configuration;
public class GeneralSettings public class GeneralSettings
@@ -21,12 +23,18 @@ public class GeneralSettings
public enum InventoryStackMode : byte public enum InventoryStackMode : byte
{ {
[Description("Split Stacks (Game Default)")]
NaturalStacks = 0, NaturalStacks = 0,
[Description("Merge Stacks (By Item ID)")]
AggregateByItemId = 1, AggregateByItemId = 1,
} }
public enum SearchMode : byte public enum SearchMode : byte
{ {
[Description("Filter (Hide non-matches)")]
Filter = 0, Filter = 0,
[Description("Highlight (Dim non-matches)")]
Highlight = 1, Highlight = 1,
} }
+52
View File
@@ -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<DescriptionAttribute>();
return attribute?.Description ?? enumValue.ToString();
}
}
extension<T>(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<T, byte>(flag, enable); break;
case 2: flagValue.SetFlag<T, ushort>(flag, enable); break;
case 4: flagValue.SetFlag<T, uint>(flag, enable); break;
case 8: flagValue.SetFlag<T, ulong>(flag, enable); break;
default: throw new NotSupportedException("Unsupported enum size");
}
}
private void SetFlag<TUnderlying>(T flag, bool enable) where TUnderlying : unmanaged, IBinaryInteger<TUnderlying> {
ref var value = ref Unsafe.As<T, TUnderlying>(ref flagValue);
var mask = Unsafe.As<T, TUnderlying>(ref flag);
if (enable)
value |= mask;
else
value &= ~mask;
}
}
}
@@ -80,24 +80,21 @@ public sealed class CategoryGeneralConfigurationNode : TabbedVerticalListNode
bool bisBuddyReady = System.IPC.BisBuddy?.IsReady ?? false; bool bisBuddyReady = System.IPC.BisBuddy?.IsReady ?? false;
LabeledDropdownNode? bbModeDropdown = new LabeledDropdownNode LabeledDropdownNode<PluginFilterMode>? bbModeDropdown = new LabeledDropdownNode<PluginFilterMode>
{ {
Size = new Vector2(300, 20), Size = new Vector2(500, 20),
LabelText = "Filter Display Mode", LabelText = "Filter Display Mode",
LabelTextFlags = TextFlags.AutoAdjustNodeSize, LabelTextFlags = TextFlags.AutoAdjustNodeSize,
IsEnabled = config.BisBuddyEnabled && bisBuddyReady, IsEnabled = config.BisBuddyEnabled && bisBuddyReady,
Options = Enum.GetNames(typeof(PluginFilterMode)).ToList(), Options = Enum.GetValues<PluginFilterMode>().ToList(),
SelectedOption = config.BisBuddyMode.ToString(), SelectedOption = config.BisBuddyMode,
OnOptionSelected = selected => OnOptionSelected = selected =>
{ {
if (Enum.TryParse<PluginFilterMode>(selected, out var parsed)) config.BisBuddyMode = selected;
{ if (selected == PluginFilterMode.Categorize)
config.BisBuddyMode = parsed; HighlightState.ClearFilter(HighlightSource.AllaganTools);
if (parsed == PluginFilterMode.Categorize)
HighlightState.ClearFilter(HighlightSource.AllaganTools);
RefreshInventory(); RefreshInventory();
}
} }
}; };
@@ -121,24 +118,23 @@ public sealed class CategoryGeneralConfigurationNode : TabbedVerticalListNode
bool allaganReady = System.IPC.AllaganTools?.IsReady ?? false; bool allaganReady = System.IPC.AllaganTools?.IsReady ?? false;
LabeledDropdownNode? atModeDropdown = new LabeledDropdownNode LabeledDropdownNode<PluginFilterMode>? atModeDropdown = new LabeledDropdownNode<PluginFilterMode>
{ {
Size = new Vector2(300, 20), Size = new Vector2(500, 20),
LabelText = "Filter Display Mode", LabelText = "Filter Display Mode",
LabelTextFlags = TextFlags.AutoAdjustNodeSize, LabelTextFlags = TextFlags.AutoAdjustNodeSize,
IsEnabled = config.AllaganToolsCategoriesEnabled && allaganReady, IsEnabled = config.AllaganToolsCategoriesEnabled && allaganReady,
Options = Enum.GetNames(typeof(PluginFilterMode)).ToList(), Options = Enum.GetValues<PluginFilterMode>().ToList(),
SelectedOption = config.AllaganToolsFilterMode.ToString(), SelectedOption = config.AllaganToolsFilterMode,
OnOptionSelected = selected => OnOptionSelected = selected =>
{ {
if (Enum.TryParse<PluginFilterMode>(selected, out var parsed)) config.AllaganToolsFilterMode = selected;
if (selected == PluginFilterMode.Categorize)
{ {
config.AllaganToolsFilterMode = parsed; HighlightState.ClearFilter(HighlightSource.AllaganTools);
if (parsed == PluginFilterMode.Categorize)
HighlightState.ClearFilter(HighlightSource.AllaganTools);
RefreshInventory();
} }
RefreshInventory();
} }
}; };
@@ -3,6 +3,7 @@ using System.Numerics;
using AetherBags.Configuration; using AetherBags.Configuration;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes.Configuration.Category; namespace AetherBags.Nodes.Configuration.Category;
@@ -14,9 +15,9 @@ public sealed class RangeFilterRow : VerticalListNode
public Action<bool, int, int>? OnFilterChanged { get; set; } public Action<bool, int, int>? 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"; init => _enabledCheckbox.String = $"{value} Filter";
} }
@@ -113,9 +114,9 @@ public sealed class RangeFilterRowUint : VerticalListNode
public Action<bool, uint, uint>? OnFilterChanged { get; set; } public Action<bool, uint, uint>? 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"; init => _enabledCheckbox.String = $"{value} Filter";
} }
@@ -4,6 +4,7 @@ using System.Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes; using KamiToolKit.Classes;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes.Configuration.Category; namespace AetherBags.Nodes.Configuration.Category;
@@ -16,12 +17,11 @@ public sealed class StringListEditorNode : VerticalListNode
private readonly LabelTextNode _headerLabel; private readonly LabelTextNode _headerLabel;
private readonly VerticalListNode _itemsContainer; private readonly VerticalListNode _itemsContainer;
private readonly HorizontalListNode _addRow;
private readonly TextInputNode _addInput; private readonly TextInputNode _addInput;
public Action? OnChanged { get; set; } public Action? OnChanged { get; set; }
public required string Label public required ReadOnlySeString Label
{ {
get => _headerLabel.String; get => _headerLabel.String;
init => _headerLabel.String = value; init => _headerLabel.String = value;
@@ -49,7 +49,7 @@ public sealed class StringListEditorNode : VerticalListNode
}; };
AddNode(_itemsContainer); AddNode(_itemsContainer);
_addRow = new HorizontalListNode var addRow = new HorizontalListNode
{ {
Size = new Vector2(LabelWidth + 40f, RowHeight), Size = new Vector2(LabelWidth + 40f, RowHeight),
ItemSpacing = 4.0f, ItemSpacing = 4.0f,
@@ -61,7 +61,7 @@ public sealed class StringListEditorNode : VerticalListNode
PlaceholderString = "Add new...", PlaceholderString = "Add new...",
OnInputComplete = _ => AddCurrentValue(), OnInputComplete = _ => AddCurrentValue(),
}; };
_addRow.AddNode(_addInput); addRow.AddNode(_addInput);
var addButton = new TextButtonNode var addButton = new TextButtonNode
{ {
@@ -69,9 +69,9 @@ public sealed class StringListEditorNode : VerticalListNode
String = "Add", String = "Add",
OnClick = AddCurrentValue, OnClick = AddCurrentValue,
}; };
_addRow.AddNode(addButton); addRow.AddNode(addButton);
AddNode(_addRow); AddNode(addRow);
} }
public void SetList(List<string> newList) public void SetList(List<string> newList)
@@ -82,7 +82,7 @@ public sealed class StringListEditorNode : VerticalListNode
private void AddCurrentValue() private void AddCurrentValue()
{ {
var value = _addInput.String; var value = _addInput.String.ExtractText();
if (!string.IsNullOrWhiteSpace(value) && !_list.Contains(value)) if (!string.IsNullOrWhiteSpace(value) && !_list.Contains(value))
{ {
_list.Add(value); _list.Add(value);
@@ -4,6 +4,7 @@ using System.Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes; using KamiToolKit.Classes;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes.Configuration.Category; namespace AetherBags.Nodes.Configuration.Category;
@@ -16,13 +17,12 @@ public sealed class UintListEditorNode : VerticalListNode
private readonly LabelTextNode _headerLabel; private readonly LabelTextNode _headerLabel;
private readonly VerticalListNode _itemsContainer; private readonly VerticalListNode _itemsContainer;
private readonly HorizontalListNode _addRow;
private readonly NumericInputNode _addInput; private readonly NumericInputNode _addInput;
public Func<uint, string>? LabelResolver { get; init; } public Func<uint, string>? LabelResolver { get; init; }
public Action? OnChanged { get; set; } public Action? OnChanged { get; set; }
public required string Label public required ReadOnlySeString Label
{ {
get => _headerLabel.String; get => _headerLabel.String;
init => _headerLabel.String = value; init => _headerLabel.String = value;
@@ -50,7 +50,7 @@ public sealed class UintListEditorNode : VerticalListNode
}; };
AddNode(_itemsContainer); AddNode(_itemsContainer);
_addRow = new HorizontalListNode var addRow = new HorizontalListNode
{ {
Size = new Vector2(LabelWidth + 40f, RowHeight), Size = new Vector2(LabelWidth + 40f, RowHeight),
ItemSpacing = 4.0f, ItemSpacing = 4.0f,
@@ -63,7 +63,7 @@ public sealed class UintListEditorNode : VerticalListNode
Max = int.MaxValue, Max = int.MaxValue,
Value = 0, Value = 0,
}; };
_addRow.AddNode(_addInput); addRow.AddNode(_addInput);
var addButton = new TextButtonNode var addButton = new TextButtonNode
{ {
@@ -71,9 +71,9 @@ public sealed class UintListEditorNode : VerticalListNode
String = "Add", String = "Add",
OnClick = AddCurrentValue, OnClick = AddCurrentValue,
}; };
_addRow.AddNode(addButton); addRow.AddNode(addButton);
AddNode(_addRow); AddNode(addRow);
} }
public void SetList(List<uint> newList) public void SetList(List<uint> newList)
@@ -14,7 +14,7 @@ internal sealed class FunctionalConfigurationNode : TabbedVerticalListNode
private readonly CheckboxNode _hideDefaultBagsCheckboxNode; private readonly CheckboxNode _hideDefaultBagsCheckboxNode;
private readonly CheckboxNode _hideSaddlebagsCheckboxNode; private readonly CheckboxNode _hideSaddlebagsCheckboxNode;
private readonly CheckboxNode _hideRetainerbagsCheckboxNode; private readonly CheckboxNode _hideRetainerbagsCheckboxNode;
private readonly LabeledDropdownNode _stackDropDown; private readonly LabeledDropdownNode<InventoryStackMode> _stackDropDown;
public FunctionalConfigurationNode() public FunctionalConfigurationNode()
{ {
@@ -139,39 +139,33 @@ internal sealed class FunctionalConfigurationNode : TabbedVerticalListNode
Height = 6 Height = 6
}); });
var searchModeDropDown = new LabeledDropdownNode var searchModeDropDown = new LabeledDropdownNode<SearchMode>
{ {
Size = new Vector2(300, 20), Size = new Vector2(500, 20),
LabelText = "Search Mode", LabelText = "Search Mode",
LabelTextFlags = TextFlags.AutoAdjustNodeSize, LabelTextFlags = TextFlags.AutoAdjustNodeSize,
Options = Enum.GetNames(typeof(SearchMode)).ToList(), Options = Enum.GetValues<SearchMode>().ToList(),
SelectedOption = config.SearchMode.ToString(), SelectedOption = config.SearchMode,
OnOptionSelected = selected => OnOptionSelected = selected =>
{ {
if (Enum.TryParse<SearchMode>(selected, out var parsed)) config.SearchMode = selected;
{ InventoryOrchestrator.RefreshAll(updateMaps: false);
config.SearchMode = parsed;
InventoryOrchestrator.RefreshAll(updateMaps: false);
}
} }
}; };
AddNode(searchModeDropDown); AddNode(searchModeDropDown);
_stackDropDown = new LabeledDropdownNode _stackDropDown = new LabeledDropdownNode<InventoryStackMode>
{ {
Size = new Vector2(300, 20), Size = new Vector2(500, 20),
IsEnabled = true, IsEnabled = true,
LabelText = "Stack Mode", LabelText = "Stack Mode",
LabelTextFlags = TextFlags.AutoAdjustNodeSize, LabelTextFlags = TextFlags.AutoAdjustNodeSize,
Options = Enum.GetNames(typeof(InventoryStackMode)).ToList(), Options = Enum.GetValues<InventoryStackMode>().ToList(),
SelectedOption = config.StackMode.ToString(), SelectedOption = config.StackMode,
OnOptionSelected = selected => OnOptionSelected = selected =>
{ {
if (Enum.TryParse<InventoryStackMode>(selected, out var parsed)) config.StackMode = selected;
{ InventoryOrchestrator.RefreshAll(updateMaps: true);
config.StackMode = parsed;
InventoryOrchestrator.RefreshAll(updateMaps: true);
}
} }
}; };
AddNode(_stackDropDown); AddNode(_stackDropDown);
+20 -12
View File
@@ -2,13 +2,14 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes.Input; namespace AetherBags.Nodes.Input;
public class LabeledDropdownNode : SimpleComponentNode { public class LabeledDropdownNode<T> : SimpleComponentNode where T : Enum {
private readonly GridNode _gridNode; private readonly GridNode _gridNode;
private readonly TextNode _labelNode; private readonly TextNode _labelNode;
private readonly TextDropDownNode _dropDownNode; private readonly EnumDropDownNode<T> _dropDownNode;
public LabeledDropdownNode() { public LabeledDropdownNode() {
_gridNode = new GridNode { _gridNode = new GridNode {
@@ -17,12 +18,12 @@ public class LabeledDropdownNode : SimpleComponentNode {
_gridNode.AttachNode(this); _gridNode.AttachNode(this);
_labelNode = new LabelTextNode { _labelNode = new LabelTextNode {
String = String.Empty, String = string.Empty,
}; };
_labelNode.AttachNode(_gridNode[0, 0]); _labelNode.AttachNode(_gridNode[0, 0]);
_dropDownNode = new TextDropDownNode { _dropDownNode = new EnumDropDownNode<T> {
Options = new List<string>(), Options = new List<T>(),
}; };
_dropDownNode.AttachNode(_gridNode[1, 0]); _dropDownNode.AttachNode(_gridNode[1, 0]);
} }
@@ -36,25 +37,32 @@ public class LabeledDropdownNode : SimpleComponentNode {
_dropDownNode.Size = _gridNode[1, 0].Size; _dropDownNode.Size = _gridNode[1, 0].Size;
} }
public required string LabelText public required ReadOnlySeString LabelText
{ {
get => _labelNode.String; get => _labelNode.String;
set => _labelNode.String = value; set => _labelNode.String = value;
} }
public Action<string>? OnOptionSelected public Action<T>? OnOptionSelected
{ {
get => _dropDownNode.OnOptionSelected; get => _dropDownNode.OnOptionSelected;
set => _dropDownNode.OnOptionSelected = value; set => _dropDownNode.OnOptionSelected = value;
} }
public string? SelectedOption public T? SelectedOption
{ {
get => _dropDownNode.SelectedOption; get => _dropDownNode.OptionListNode.SelectedOption;
set => _dropDownNode.SelectedOption = value; set
{
_dropDownNode.OptionListNode.SelectedOption = value;
if (value != null)
{
_dropDownNode.LabelNode.String = value.Description;
}
}
} }
public required List<string> Options public required List<T> Options
{ {
get => _dropDownNode.Options!; get => _dropDownNode.Options!;
set => _dropDownNode.Options = value; set => _dropDownNode.Options = value;
@@ -65,4 +73,4 @@ public class LabeledDropdownNode : SimpleComponentNode {
get => _labelNode.TextFlags; get => _labelNode.TextFlags;
set => _labelNode.TextFlags = value; set => _labelNode.TextFlags = value;
} }
} }
@@ -49,7 +49,7 @@ public class TextInputWithButtonNode : SimpleComponentNode {
} }
public ReadOnlySeString SearchString { public ReadOnlySeString SearchString {
get => _textInputNode.SeString; get => _textInputNode.String;
set => _textInputNode.SeString = value; set => _textInputNode.String = value;
} }
} }
@@ -5,7 +5,7 @@ using AetherBags.Nodes.Currency;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes; using KamiToolKit.Classes;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
using static AetherBags.Inventory.State.InventoryStateBase; using static AetherBags.Inventory.State.InventoryStateBase;
namespace AetherBags.Nodes.Inventory; namespace AetherBags.Nodes.Inventory;
@@ -60,7 +60,7 @@ public sealed class InventoryFooterNode : SimpleComponentNode
}); });
} }
public string SlotAmountText public ReadOnlySeString SlotAmountText
{ {
get => _slotAmountTextNode.String; get => _slotAmountTextNode.String;
set => _slotAmountTextNode.String = value; set => _slotAmountTextNode.String = value;
@@ -74,8 +74,8 @@ public sealed class InventoryNotificationNode : SimpleComponentNode
{ {
field = value; field = value;
titleTextNode.SeString = value.Title; titleTextNode.String = value.Title;
messageTextNode.SeString = value.Message; messageTextNode.String = value.Message;
if (value.Title.IsEmpty && value.Message.IsEmpty) if (value.Title.IsEmpty && value.Message.IsEmpty)
{ {
@@ -1,6 +1,7 @@
using System. Numerics; using System. Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes.Inventory; namespace AetherBags.Nodes.Inventory;
@@ -23,7 +24,7 @@ public class SaddleBagFooterNode : SimpleComponentNode
_slotCounterNode.AttachNode(this); _slotCounterNode.AttachNode(this);
} }
public string SlotAmountText public ReadOnlySeString SlotAmountText
{ {
get => _slotCounterNode.String; get => _slotCounterNode.String;
set => _slotCounterNode.String = $"Slots: {value}"; set => _slotCounterNode.String = $"Slots: {value}";