using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Utility;
using KamiToolKit.Nodes;
using KamiToolKit.Premade.Widgets;
namespace KamiToolKit.Premade.Nodes;
///
/// A non-owning list node that supports searching, and various callbacks for easily editing a list.
///
/// Data type to display the data for.
/// ListItemNode derived type, for defining the result view.
public class ModifyListNode : SimpleComponentNode where TU : ListItemNode, new() {
private readonly SearchWidget searchWidget;
private readonly ListNode listNode;
private readonly TextButtonNode addButton;
private readonly TextButtonNode editButton;
private readonly TextButtonNode removeButton;
public ModifyListNode() {
searchWidget = new SearchWidget {
OnSortOrderChanged = OnSortOrderChanged,
OnSearchUpdated = OnSearchUpdated,
};
searchWidget.AttachNode(this);
listNode = new ListNode {
OptionsList = [],
OnItemSelected = OnListItemSelected,
};
listNode.AttachNode(this);
addButton = new TextButtonNode {
String = "Add",
OnClick = OnAddClicked,
IsEnabled = false,
};
addButton.AttachNode(this);
editButton = new TextButtonNode {
String = "Edit",
OnClick = OnEditClicked,
IsEnabled = false,
};
editButton.AttachNode(this);
removeButton = new TextButtonNode {
String = "Remove",
OnClick = OnRemoveClicked,
IsEnabled = false,
};
removeButton.AttachNode(this);
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
searchWidget.Size = new Vector2(Width, 65.0f);
searchWidget.Position = Vector2.Zero;
listNode.Size = new Vector2(Width, Height - searchWidget.Height - 40.0f);
listNode.Position = new Vector2(0.0f, searchWidget.Y + searchWidget.Height + 8.0f);
const float buttonPadding = 5.0f;
var buttonWidth = (Width - buttonPadding * 2.0f) / 3.0f;
addButton.Size = new Vector2(buttonWidth, 24.0f);
addButton.Position = new Vector2(0.0f, Height - 24.0f);
editButton.Size = new Vector2(buttonWidth, 24.0f);
editButton.Position = new Vector2(buttonWidth + buttonPadding, Height - 24.0f);
removeButton.Size = new Vector2(buttonWidth, 24.0f);
removeButton.Position = new Vector2(buttonWidth * 2.0f + buttonPadding * 2.0f, Height - 24.0f);
}
public List Options {
get;
set {
field = value;
listNode.OptionsList = value;
}
} = [];
public List? SortOptions {
get => searchWidget.SortingOptions;
set {
searchWidget.SortingOptions = value ?? [];
OnSizeChanged();
if (value is not null && value.Count > 0) {
OnSortOrderChanged(value.First(), false);
}
}
}
public Action? SelectionChanged { get; init; }
public Action? AddNewEntry {
get;
set {
field = value;
addButton.IsEnabled = value is not null;
}
}
public Action? RemoveEntry {
get;
set {
field = value;
removeButton.IsEnabled = value is not null && SelectedOption is not null;
}
}
public Action? EditEntry {
get;
set {
field = value;
editButton.IsEnabled = value is not null && SelectedOption is not null;
}
}
public delegate int ItemCompareDelegate(T left, T right, string sortingMode);
public ItemCompareDelegate? ItemComparer { get; set; }
public delegate bool IsSearchMatchDelegate(T obj, string searchString);
public IsSearchMatchDelegate? IsSearchMatch { get; set; }
public T? SelectedOption { get; private set; }
public float ItemSpacing {
get => listNode.ItemSpacing;
set {
listNode.ItemSpacing = value;
OnSizeChanged();
}
}
private void OnSortOrderChanged(string sortingString, bool reversed) {
if (ItemComparer is null) return;
var listCopy = Options.ToList();
listCopy.Sort((left, right) => ItemComparer.Invoke(left, right, sortingString) * (reversed ? -1 : 1));
listNode.OptionsList = listCopy;
UpdateButtonStates();
}
private void OnSearchUpdated(string searchString) {
if (IsSearchMatch is null) return;
if (searchString.IsNullOrEmpty()) {
listNode.OptionsList = Options;
}
else {
listNode.OptionsList = Options.Where(item => IsSearchMatch(item, searchString)).ToList();
}
}
private void OnListItemSelected(T? obj) {
SelectedOption = obj;
SelectionChanged?.Invoke(SelectedOption);
UpdateButtonStates();
}
private void OnAddClicked() {
AddNewEntry?.Invoke();
RefreshList();
}
private void OnEditClicked() {
if (SelectedOption is null) return;
EditEntry?.Invoke(SelectedOption);
RefreshList();
}
private void OnRemoveClicked() {
if (SelectedOption is null) return;
RemoveEntry?.Invoke(SelectedOption);
RefreshList();
}
private void UpdateButtonStates() {
editButton.IsEnabled = SelectedOption is not null && EditEntry is not null;
removeButton.IsEnabled = SelectedOption is not null && RemoveEntry is not null;
}
///
/// Refreshes the displayed list data.
/// This resets scroll position, so don't spam it.
///
public void RefreshList() {
OnSortOrderChanged(searchWidget.SortMode, searchWidget.IsReversed);
OnSearchUpdated(searchWidget.SearchText);
listNode.FullRebuild();
}
}