using System; using System.Collections.Generic; using System.Numerics; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; using KamiToolKit.Premade.Nodes; namespace KamiToolKit.Premade.Addons; public class ListConfigAddon : NativeAddon where T: class where TV : ConfigNode, new() where TU : ListItemNode, new() { private ModifyListNode? selectionListNode; private VerticalLineNode? separatorLine; private TV? configNode; private TextNode? nothingSelectedTextNode; protected override unsafe void OnSetup(AtkUnitBase* addon) { selectionListNode = new ModifyListNode { Position = ContentStartPosition, Size = new Vector2(250.0f, ContentSize.Y), SortOptions = SortOptions, Options = Options, SelectionChanged = SelectionChanged, AddNewEntry = OnAddClicked, RemoveEntry = OnRemoveClicked, ItemComparer = ItemComparer, IsSearchMatch = OnSearchUpdated, ItemSpacing = ItemSpacing, }; selectionListNode.AttachNode(this); separatorLine = new VerticalLineNode { Position = ContentStartPosition + new Vector2(250.0f + 8.0f, 0.0f), Size = new Vector2(4.0f, ContentSize.Y), }; separatorLine.AttachNode(this); nothingSelectedTextNode = new TextNode { Position = ContentStartPosition + new Vector2(250.0f + 16.0f, 0.0f), Size = ContentSize - new Vector2(250.0f + 16.0f, 0.0f), AlignmentType = AlignmentType.Center, TextFlags = TextFlags.WordWrap | TextFlags.MultiLine, FontSize = 14, LineSpacing = 22, FontType = FontType.Axis, String = "Please select an option on the left", TextColor = ColorHelper.GetColor(1), }; nothingSelectedTextNode.AttachNode(this); configNode = new TV { Position = ContentStartPosition + new Vector2(250.0f + 16.0f, 0.0f), Size = ContentSize - new Vector2(250.0f + 16.0f, 0.0f), OnConfigChanged = option => EditCompleted?.Invoke(option), IsVisible = false, }; configNode.AttachNode(this); } public required ModifyListNode.ItemCompareDelegate? ItemComparer { get; init { field = value; selectionListNode?.ItemComparer = value; } } public required ModifyListNode.IsSearchMatchDelegate? IsSearchMatch { get; init { field = value; selectionListNode?.IsSearchMatch = value; } } private void OnAddClicked() { AddClicked?.Invoke(this); selectionListNode?.RefreshList(); } private void OnRemoveClicked(T listItem) { RemoveClicked?.Invoke(this, listItem); SelectionChanged(null); selectionListNode?.RefreshList(); } private void SelectionChanged(T? listItem) { SetConfigNodeItem(listItem); } private bool OnSearchUpdated(T obj, string searchString) { SelectItem(null); return IsSearchMatch?.Invoke(obj, searchString) ?? false; } private void SetConfigNodeItem(T? configItem) { if (configNode is null) return; if (nothingSelectedTextNode is null) return; configNode.ConfigurationOption = configItem; configNode.IsVisible = configNode.ConfigurationOption is not null; nothingSelectedTextNode.IsVisible = configNode.ConfigurationOption is null; } public void RefreshList() => selectionListNode?.RefreshList(); public void SelectItem(T? listItem) => SelectionChanged(listItem); public List? SortOptions { get; set { field = value; selectionListNode?.SortOptions = value; } } = ["Alphabetical", "Id"]; public required List Options { get; set { field = value; selectionListNode?.Options = value; } } = []; public float ItemSpacing { get; set { field = value; selectionListNode?.ItemSpacing = value; } } public Action>? AddClicked { get; set { field = value; selectionListNode?.AddNewEntry = () => { value?.Invoke(this); }; } } public Action, T>? RemoveClicked { get; set { field = value; selectionListNode?.RemoveEntry = entry => { value?.Invoke(this, entry); }; } } public Action? EditCompleted { get; set; } }