Initial release: HSUI v1.0.0.0 - HUD replacement with configurable hotbars
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Utility;
|
||||
using HSUI.Config.Attributes;
|
||||
using HSUI.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public delegate void ConfigObjectResetEventHandler(BaseNode sender);
|
||||
|
||||
public class BaseNode : Node
|
||||
{
|
||||
public event ConfigObjectResetEventHandler? ConfigObjectResetEvent;
|
||||
|
||||
private Dictionary<Type, ConfigPageNode> _configPageNodesMap;
|
||||
|
||||
public bool NeedsSave = false;
|
||||
public string? SelectedOptionName = null;
|
||||
|
||||
private List<Node> _extraNodes = new List<Node>();
|
||||
private List<Node> _nodes = new List<Node>();
|
||||
|
||||
public IReadOnlyCollection<Node> Sections => _nodes.AsReadOnly();
|
||||
|
||||
private float _scale => ImGuiHelpers.GlobalScale;
|
||||
|
||||
public BaseNode()
|
||||
{
|
||||
_configPageNodesMap = new Dictionary<Type, ConfigPageNode>();
|
||||
}
|
||||
|
||||
public void AddExtraSectionNode(SectionNode node)
|
||||
{
|
||||
_extraNodes.Add(node);
|
||||
|
||||
_nodes.Clear();
|
||||
CreateNodesIfNeeded();
|
||||
}
|
||||
|
||||
public T? GetConfigObject<T>() where T : PluginConfigObject
|
||||
{
|
||||
var pageNode = GetConfigPageNode<T>();
|
||||
|
||||
return pageNode != null ? (T)pageNode.ConfigObject : null;
|
||||
}
|
||||
|
||||
public void RemoveConfigObject<T>() where T : PluginConfigObject
|
||||
{
|
||||
if (_configPageNodesMap.ContainsKey(typeof(T)))
|
||||
{
|
||||
_configPageNodesMap.Remove(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigPageNode? GetConfigPageNode<T>() where T : PluginConfigObject
|
||||
{
|
||||
if (_configPageNodesMap.TryGetValue(typeof(T), out var node))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
var configPageNode = GetOrAddConfig<T>();
|
||||
|
||||
if (configPageNode != null && configPageNode.ConfigObject != null)
|
||||
{
|
||||
_configPageNodesMap.Add(typeof(T), configPageNode);
|
||||
|
||||
return configPageNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetConfigPageNode(ConfigPageNode configPageNode)
|
||||
{
|
||||
if (configPageNode.ConfigObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_configPageNodesMap[configPageNode.ConfigObject.GetType()] = configPageNode;
|
||||
}
|
||||
|
||||
public bool SetConfigObject(PluginConfigObject configObject)
|
||||
{
|
||||
if (_configPageNodesMap.TryGetValue(configObject.GetType(), out ConfigPageNode? configPageNode))
|
||||
{
|
||||
configPageNode.ConfigObject = configObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool PushStyles()
|
||||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ScrollbarRounding, 1); //Scrollbar Radius
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.TabRounding, 1); //Tabs Radius Radius
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 1); //Intractable Elements Radius
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.GrabRounding, 1); //Gradable Elements Radius
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.PopupRounding, 1); //Popup Radius
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ScrollbarSize, 10); //Popup Radius
|
||||
|
||||
if (ConfigurationManager.Instance.OverrideDalamudStyle)
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(46f / 255f, 45f / 255f, 46f / 255f, 1f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .2f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .2f));
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Separator, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .4f));
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.ScrollbarBg, new Vector4(20f / 255f, 21f / 255f, 20f / 255f, .7f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ScrollbarGrab, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .7f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ScrollbarGrabActive, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .7f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ScrollbarGrabHovered, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .7f));
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Tab, new Vector4(46f / 255f, 45f / 255f, 46f / 255f, 1f));
|
||||
ImGui.PushStyleColor(ImGuiCol.TabActive, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .7f));
|
||||
ImGui.PushStyleColor(ImGuiCol.TabHovered, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .2f));
|
||||
ImGui.PushStyleColor(ImGuiCol.TabUnfocused, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .2f));
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Border, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, 1f));
|
||||
ImGui.PushStyleColor(ImGuiCol.CheckMark, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, 1f));
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.TableBorderStrong, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, 1f));
|
||||
ImGui.PushStyleColor(ImGuiCol.TableBorderLight, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .4f));
|
||||
ImGui.PushStyleColor(ImGuiCol.TableHeaderBg, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, .2f));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PopStyles(bool popColors)
|
||||
{
|
||||
if (popColors)
|
||||
{
|
||||
ImGui.PopStyleColor(17);
|
||||
}
|
||||
|
||||
ImGui.PopStyleVar(6);
|
||||
}
|
||||
|
||||
public void CreateNodesIfNeeded()
|
||||
{
|
||||
if (_nodes.Count > 0) { return; }
|
||||
|
||||
_nodes.AddRange(_children);
|
||||
_nodes.AddRange(_extraNodes);
|
||||
}
|
||||
|
||||
public void RefreshSelectedNode()
|
||||
{
|
||||
if (_nodes.Count == 0) { return; }
|
||||
|
||||
foreach (SectionNode node in _nodes.FindAll(x => x is SectionNode))
|
||||
{
|
||||
node.Selected = node.Name == SelectedOptionName;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(float alpha)
|
||||
{
|
||||
CreateNodesIfNeeded();
|
||||
if (_nodes.Count == 0) { return; }
|
||||
|
||||
bool changed = false;
|
||||
bool didReset = false;
|
||||
|
||||
bool popColors = PushStyles();
|
||||
|
||||
ImGui.BeginGroup(); // Middle section
|
||||
{
|
||||
ImGui.BeginGroup(); // Left
|
||||
{
|
||||
// banner
|
||||
IDalamudTextureWrap? delvUiBanner = Plugin.BannerTexture?.GetWrapOrDefault();
|
||||
if (delvUiBanner != null)
|
||||
{
|
||||
ImGui.SetCursorPos(new Vector2(15 + 150 * _scale / 2f - delvUiBanner.Width / 2f, 5));
|
||||
ImGui.Image(delvUiBanner.Handle, new Vector2(delvUiBanner.Width, delvUiBanner.Height));
|
||||
}
|
||||
|
||||
// version
|
||||
ImGui.SetCursorPos(new Vector2(60 * _scale, 35));
|
||||
ImGui.Text($"v{Plugin.Version}");
|
||||
|
||||
|
||||
// section list
|
||||
if (ImGui.BeginChild("left pane", new Vector2(150 * _scale, -10), true, ImGuiWindowFlags.NoScrollbar))
|
||||
{
|
||||
|
||||
// if no section is selected, select the first
|
||||
if (_nodes.Any() && _nodes.Find(o => o is SectionNode sectionNode && sectionNode.Selected) == null)
|
||||
{
|
||||
SectionNode? selectedSection = (SectionNode?)_nodes.Find(o => o is SectionNode sectionNode && sectionNode.Name == SelectedOptionName);
|
||||
if (selectedSection != null)
|
||||
{
|
||||
selectedSection.Selected = true;
|
||||
SelectedOptionName = selectedSection.Name;
|
||||
}
|
||||
else if (_nodes.Count > 0)
|
||||
{
|
||||
SectionNode node = (SectionNode)_nodes[0];
|
||||
node.Selected = true;
|
||||
SelectedOptionName = node.Name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SectionNode selectionNode in _nodes)
|
||||
{
|
||||
if (ImGui.Selectable(selectionNode.Name, selectionNode.Selected))
|
||||
{
|
||||
selectionNode.Selected = true;
|
||||
SelectedOptionName = selectionNode.Name;
|
||||
|
||||
foreach (SectionNode otherNode in _nodes.FindAll(x => x != selectionNode))
|
||||
{
|
||||
otherNode.Selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
DrawExportResetContextMenu(selectionNode, selectionNode.Name);
|
||||
}
|
||||
|
||||
// changelog button
|
||||
float y = ImGui.GetWindowHeight() - (30 * _scale);
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(255f / 255f, 255f / 255f, 255f / 255f, alpha));
|
||||
ImGui.SetCursorPos(new Vector2(19 * _scale, y));
|
||||
if (ImGui.Button(FontAwesomeIcon.List.ToIconString(), new Vector2(24 * _scale, 24 * _scale)))
|
||||
{
|
||||
ConfigurationManager.Instance.OpenChangelogWindow();
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip("Changelog");
|
||||
|
||||
// discord button
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(114f / 255f, 137f / 255f, 218f / 255f, alpha));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(114f / 255f, 137f / 255f, 218f / 255f, alpha * .85f));
|
||||
ImGui.SetCursorPos(new Vector2(48 * _scale, y));
|
||||
if (ImGui.Button(FontAwesomeIcon.Link.ToIconString(), new Vector2(24 * _scale, 24 * _scale)))
|
||||
{
|
||||
Utils.OpenUrl("https://discord.gg/xzde5qQayh");
|
||||
}
|
||||
ImGui.PopStyleColor(2);
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip("HSUI Discord");
|
||||
|
||||
// discord button
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(255f / 255f, 94f / 255f, 91f / 255f, alpha));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(255f / 255f, 94f / 255f, 91f / 255f, alpha * .85f));
|
||||
ImGui.SetCursorPos(new Vector2(77 * _scale, y));
|
||||
if (ImGui.Button(FontAwesomeIcon.MugHot.ToIconString(), new Vector2(24 * _scale, 24 * _scale)))
|
||||
{
|
||||
Utils.OpenUrl("https://ko-fi.com/Tischel");
|
||||
}
|
||||
ImGui.PopStyleColor(2);
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip("Tip the developer at ko-fi.com");
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.EndGroup(); // Left
|
||||
|
||||
|
||||
didReset |= DrawResetModal();
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
ImGui.BeginGroup(); // Right
|
||||
{
|
||||
foreach (SectionNode selectionNode in _nodes)
|
||||
{
|
||||
didReset |= selectionNode.Draw(ref changed, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndGroup(); // Right
|
||||
}
|
||||
|
||||
ImGui.EndGroup(); // Middle section
|
||||
|
||||
// close button
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, alpha));
|
||||
ImGui.SetCursorPos(new Vector2(ImGui.GetWindowWidth() - 28 * _scale, 5 * _scale));
|
||||
if (ImGui.Button(FontAwesomeIcon.Times.ToIconString(), new Vector2(22 * _scale, 22 * _scale)))
|
||||
{
|
||||
ConfigurationManager.Instance.CloseConfigWindow();
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip("Close");
|
||||
|
||||
// unlock button
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, alpha));
|
||||
ImGui.SetCursorPos(new Vector2(ImGui.GetWindowWidth() - 60 * _scale, 5 * _scale));
|
||||
string lockString = ConfigurationManager.Instance.LockHUD ? FontAwesomeIcon.Lock.ToIconString() : FontAwesomeIcon.LockOpen.ToIconString();
|
||||
if (ImGui.Button(lockString, new Vector2(22 * _scale, 22 * _scale)))
|
||||
{
|
||||
ConfigurationManager.Instance.LockHUD = !ConfigurationManager.Instance.LockHUD;
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip("Unlock HUD");
|
||||
|
||||
// hide button
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(229f / 255f, 57f / 255f, 57f / 255f, alpha));
|
||||
ImGui.SetCursorPos(new Vector2(ImGui.GetWindowWidth() - 88 * _scale, 5 * _scale));
|
||||
string hideString = ConfigurationManager.Instance.ShowHUD ? FontAwesomeIcon.Eye.ToIconString() : FontAwesomeIcon.EyeSlash.ToIconString();
|
||||
if (ImGui.Button(hideString, new Vector2(26 * _scale, 22 * _scale)))
|
||||
{
|
||||
ConfigurationManager.Instance.ShowHUD = !ConfigurationManager.Instance.ShowHUD;
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopFont();
|
||||
ImGuiHelper.SetTooltip(ConfigurationManager.Instance.ShowHUD ? "Hide HUD" : "Show HUD");
|
||||
|
||||
PopStyles(popColors);
|
||||
|
||||
if (didReset)
|
||||
{
|
||||
ConfigObjectResetEvent?.Invoke(this);
|
||||
}
|
||||
|
||||
NeedsSave |= changed | didReset;
|
||||
}
|
||||
|
||||
public ConfigPageNode? GetOrAddConfig<T>() where T : PluginConfigObject
|
||||
{
|
||||
object[] attributes = typeof(T).GetCustomAttributes(true);
|
||||
|
||||
foreach (object attribute in attributes)
|
||||
{
|
||||
if (attribute is SectionAttribute sectionAttribute)
|
||||
{
|
||||
foreach (SectionNode sectionNode in _children)
|
||||
{
|
||||
if (sectionNode.Name == sectionAttribute.SectionName)
|
||||
{
|
||||
return sectionNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
|
||||
SectionNode newNode = new();
|
||||
newNode.Name = sectionAttribute.SectionName;
|
||||
newNode.ForceAllowExport = sectionAttribute.ForceAllowExport;
|
||||
_children.Add(newNode);
|
||||
|
||||
return newNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
throw new ArgumentException("The provided configuration object does not specify a section: " + type.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
using Dalamud.Logging;
|
||||
using HSUI.Config.Attributes;
|
||||
using HSUI.Config.Profiles;
|
||||
using HSUI.Helpers;
|
||||
using HSUI.Interface;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public class ConfigPageNode : SubSectionNode
|
||||
{
|
||||
private PluginConfigObject _configObject = null!;
|
||||
private List<ConfigNode>? _drawList = null;
|
||||
private Dictionary<string, ConfigPageNode> _nestedConfigPageNodes = null!;
|
||||
|
||||
public PluginConfigObject ConfigObject
|
||||
{
|
||||
get => _configObject;
|
||||
set
|
||||
{
|
||||
_configObject = value;
|
||||
GenerateNestedConfigPageNodes();
|
||||
_drawList = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override List<T> GetObjects<T>()
|
||||
{
|
||||
return _configObject.GetObjects<T>();
|
||||
}
|
||||
|
||||
private void GenerateNestedConfigPageNodes()
|
||||
{
|
||||
_nestedConfigPageNodes = new Dictionary<string, ConfigPageNode>();
|
||||
|
||||
FieldInfo[] fields = _configObject.GetType().GetFields();
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
foreach (var attribute in field.GetCustomAttributes(true))
|
||||
{
|
||||
if (attribute is not NestedConfigAttribute nestedConfigAttribute)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = field.GetValue(_configObject);
|
||||
|
||||
if (value is not PluginConfigObject nestedConfig)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigPageNode configPageNode = new();
|
||||
configPageNode.ConfigObject = nestedConfig;
|
||||
configPageNode.Name = nestedConfigAttribute.friendlyName;
|
||||
|
||||
if (nestedConfig.Disableable)
|
||||
{
|
||||
configPageNode.Name += "##" + nestedConfig.GetHashCode();
|
||||
}
|
||||
|
||||
_nestedConfigPageNodes.Add(field.Name, configPageNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string? GetBase64String()
|
||||
{
|
||||
if (!AllowShare())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ImportExportHelper.GenerateExportString(ConfigObject);
|
||||
}
|
||||
|
||||
protected override bool AllowExport()
|
||||
{
|
||||
return ConfigObject.Exportable;
|
||||
}
|
||||
|
||||
protected override bool AllowShare()
|
||||
{
|
||||
return ConfigObject.Shareable;
|
||||
}
|
||||
|
||||
protected override bool AllowReset()
|
||||
{
|
||||
return ConfigObject.Resettable;
|
||||
}
|
||||
|
||||
public override bool Draw(ref bool changed) { return DrawWithID(ref changed); }
|
||||
|
||||
private bool DrawWithID(ref bool changed, string? ID = null)
|
||||
{
|
||||
bool didReset = false;
|
||||
|
||||
// Only do this stuff the first time the config page is loaded
|
||||
if (_drawList is null)
|
||||
{
|
||||
_drawList = GenerateDrawList();
|
||||
}
|
||||
|
||||
if (_drawList is not null)
|
||||
{
|
||||
foreach (var fieldNode in _drawList)
|
||||
{
|
||||
didReset |= fieldNode.Draw(ref changed);
|
||||
}
|
||||
}
|
||||
|
||||
didReset |= DrawPortableSection();
|
||||
|
||||
ImGui.NewLine(); // fixes some long pages getting cut off
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
private List<ConfigNode> GenerateDrawList(string? ID = null)
|
||||
{
|
||||
Dictionary<string, ConfigNode> fieldMap = new Dictionary<string, ConfigNode>();
|
||||
|
||||
FieldInfo[] fields = ConfigObject.GetType().GetFields();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (ConfigObject.DisableParentSettings != null && ConfigObject.DisableParentSettings.Contains(field.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (object attribute in field.GetCustomAttributes(true))
|
||||
{
|
||||
if (attribute is NestedConfigAttribute nestedConfigAttribute && _nestedConfigPageNodes.TryGetValue(field.Name, out ConfigPageNode? node))
|
||||
{
|
||||
var newNodes = node.GenerateDrawList(node.Name);
|
||||
foreach (var newNode in newNodes)
|
||||
{
|
||||
newNode.Position = nestedConfigAttribute.pos;
|
||||
newNode.Separator = nestedConfigAttribute.separator;
|
||||
newNode.Spacing = nestedConfigAttribute.spacing;
|
||||
newNode.ParentName = nestedConfigAttribute.collapseWith;
|
||||
newNode.Nest = nestedConfigAttribute.nest;
|
||||
newNode.CollapsingHeader = nestedConfigAttribute.collapsingHeader;
|
||||
fieldMap.Add($"{node.Name}_{newNode.Name}", newNode);
|
||||
}
|
||||
}
|
||||
else if (attribute is OrderAttribute orderAttribute)
|
||||
{
|
||||
var fieldNode = new FieldNode(field, ConfigObject, ID);
|
||||
fieldNode.Position = orderAttribute.pos;
|
||||
fieldNode.ParentName = orderAttribute.collapseWith;
|
||||
if (fieldMap.TryGetValue(field.Name, out var existing) && existing is FieldNode existingFn &&
|
||||
existingFn.Field.DeclaringType != null && field.DeclaringType != null &&
|
||||
existingFn.Field.DeclaringType.IsAssignableFrom(field.DeclaringType) &&
|
||||
existingFn.Field.DeclaringType != field.DeclaringType)
|
||||
{
|
||||
fieldMap[field.Name] = fieldNode;
|
||||
}
|
||||
else if (!fieldMap.ContainsKey(field.Name))
|
||||
{
|
||||
fieldMap.Add(field.Name, fieldNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var manualDrawMethods = ConfigObject.GetType().GetMethods().Where(m => Attribute.IsDefined(m, typeof(ManualDrawAttribute), false));
|
||||
foreach (var method in manualDrawMethods)
|
||||
{
|
||||
string id = $"ManualDraw##{method.GetHashCode()}";
|
||||
fieldMap.Add(id, new ManualDrawNode(method, ConfigObject, id));
|
||||
}
|
||||
|
||||
foreach (var configNode in fieldMap.Values)
|
||||
{
|
||||
if (configNode.ParentName is not null &&
|
||||
fieldMap.TryGetValue(configNode.ParentName, out ConfigNode? parentNode))
|
||||
{
|
||||
if (!ConfigObject.Disableable &&
|
||||
parentNode.Name.Equals("Enabled") &&
|
||||
parentNode.ID is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parentNode is FieldNode parentFieldNode)
|
||||
{
|
||||
parentFieldNode.CollapseControl = true;
|
||||
parentFieldNode.AddChild(configNode.Position, configNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fieldNodes = fieldMap.Values.ToList();
|
||||
fieldNodes.RemoveAll(f => f.IsChild);
|
||||
fieldNodes.Sort((x, y) => x.Position - y.Position);
|
||||
return fieldNodes;
|
||||
}
|
||||
|
||||
private bool DrawPortableSection()
|
||||
{
|
||||
if (!AllowExport())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiHelper.DrawSeparator(2, 1);
|
||||
|
||||
const float buttonWidth = 120;
|
||||
|
||||
ImGui.BeginGroup();
|
||||
|
||||
float width = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
||||
ImGui.SetCursorPos(new Vector2(width / 2f - buttonWidth - 5, ImGui.GetCursorPosY()));
|
||||
|
||||
if (ImGui.Button("Export", new Vector2(120, 24)))
|
||||
{
|
||||
var exportString = ImportExportHelper.GenerateExportString(ConfigObject);
|
||||
ImGui.SetClipboardText(exportString);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Reset", new Vector2(120, 24)))
|
||||
{
|
||||
_nodeToReset = this;
|
||||
_nodeToResetName = Utils.UserFriendlyConfigName(ConfigObject.GetType().Name);
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ImGui.EndGroup();
|
||||
|
||||
return DrawResetModal();
|
||||
}
|
||||
|
||||
public override void Save(string path)
|
||||
{
|
||||
string[] splits = path.Split("\\", StringSplitOptions.RemoveEmptyEntries);
|
||||
string directory = path.Replace(splits.Last(), "");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
string finalPath = path + ".json";
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(
|
||||
finalPath,
|
||||
JsonConvert.SerializeObject(
|
||||
ConfigObject,
|
||||
Formatting.Indented,
|
||||
new JsonSerializerSettings { TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, TypeNameHandling = TypeNameHandling.Objects }
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Plugin.Logger.Error("Error when saving config object: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(string path)
|
||||
{
|
||||
if (ConfigObject is not PluginConfigObject) { return; }
|
||||
|
||||
FileInfo finalPath = new(path + ".json");
|
||||
|
||||
// Use reflection to call the LoadForType method, this allows us to specify a type at runtime.
|
||||
// While in general use this is important as the conversion from the superclass 'PluginConfigObject' to a specific subclass (e.g. 'BlackMageHudConfig') would
|
||||
// be handled by Json.NET, when the plugin is reloaded with a different assembly (as is the case when using LivePluginLoader, or updating the plugin in-game)
|
||||
// it fails. In order to fix this we need to specify the specific subclass, in order to do this during runtime we must use reflection to set the generic.
|
||||
MethodInfo? methodInfo = ConfigObject.GetType().GetMethod("Load");
|
||||
MethodInfo? function = methodInfo?.MakeGenericMethod(ConfigObject.GetType());
|
||||
|
||||
object?[] args = new object?[] { finalPath };
|
||||
PluginConfigObject? config = (PluginConfigObject?)function?.Invoke(ConfigObject, args);
|
||||
|
||||
ConfigObject = config ?? ConfigObject;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Type type = ConfigObject.GetType();
|
||||
ImportData? importData = ProfilesManager.Instance?.DefaultImportData(type);
|
||||
|
||||
if (importData == null)
|
||||
{
|
||||
Plugin.Logger.Error("Error finding default import data for type " + type.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
PluginConfigObject? config = importData.GetObject();
|
||||
if (config == null)
|
||||
{
|
||||
Plugin.Logger.Error("Error importing default import data for type " + type.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigObject = config;
|
||||
}
|
||||
|
||||
public override ConfigPageNode? GetOrAddConfig<T>() => this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using HSUI.Config.Attributes;
|
||||
using HSUI.Helpers;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public abstract class ConfigNode
|
||||
{
|
||||
public bool CollapseControl { get; set; }
|
||||
|
||||
public bool IsChild { get; set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public bool Nest { get; set; } = true;
|
||||
|
||||
public string? ParentName { get; set; }
|
||||
|
||||
public int Position { get; set; } = Int32.MaxValue;
|
||||
|
||||
public bool Separator { get; set; }
|
||||
|
||||
public bool Spacing { get; set; }
|
||||
|
||||
public bool CollapsingHeader { get; set; }
|
||||
|
||||
public string? ID { get; private set; }
|
||||
|
||||
protected PluginConfigObject ConfigObject { get; set; }
|
||||
|
||||
public ConfigNode(PluginConfigObject configObject, string? id, string name)
|
||||
{
|
||||
ConfigObject = configObject;
|
||||
ID = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public abstract bool Draw(ref bool changed, int depth = 0);
|
||||
|
||||
protected void DrawSeparatorOrSpacing()
|
||||
{
|
||||
if (Separator)
|
||||
{
|
||||
ImGuiHelper.DrawSeparator(1, 1);
|
||||
}
|
||||
|
||||
if (Spacing)
|
||||
{
|
||||
ImGuiHelper.DrawSpacing(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void DrawNestIndicator(int depth)
|
||||
{
|
||||
// This draws the L shaped symbols and padding to the left of config items collapsible under a checkbox.
|
||||
// Shift cursor to the right to pad for children with depth more than 1.
|
||||
// 26 is an arbitrary value I found to be around half the width of a checkbox
|
||||
ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(26, 0) * Math.Max((depth - 1), 0));
|
||||
|
||||
if (ConfigurationManager.Instance.OverrideDalamudStyle)
|
||||
{
|
||||
ImGui.TextColored(new Vector4(229f / 255f, 57f / 255f, 57f / 255f, 1f), "\u2514");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.Text("\u2514");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
protected static ConfigAttribute? GetConfigAttribute(FieldInfo field)
|
||||
{
|
||||
return field.GetCustomAttributes(true).Where(a => a is ConfigAttribute).FirstOrDefault() as ConfigAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldNode : ConfigNode
|
||||
{
|
||||
private SortedDictionary<int, ConfigNode> _childNodes;
|
||||
private ConfigAttribute? _configAttribute;
|
||||
private FieldInfo _mainField;
|
||||
|
||||
public FieldInfo Field => _mainField;
|
||||
|
||||
public FieldNode(FieldInfo mainField, PluginConfigObject configObject, string? id) : base(configObject, id, mainField.Name)
|
||||
{
|
||||
_mainField = mainField;
|
||||
_childNodes = new SortedDictionary<int, ConfigNode>();
|
||||
|
||||
_configAttribute = GetConfigAttribute(mainField);
|
||||
if (_configAttribute is not null)
|
||||
{
|
||||
Separator = _configAttribute.separator;
|
||||
Spacing = _configAttribute.spacing;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddChild(int position, ConfigNode field)
|
||||
{
|
||||
field.IsChild = true;
|
||||
|
||||
while (_childNodes.ContainsKey(position))
|
||||
{
|
||||
position++;
|
||||
}
|
||||
|
||||
_childNodes.Add(position, field);
|
||||
}
|
||||
|
||||
public override bool Draw(ref bool changed, int depth = 0)
|
||||
{
|
||||
bool reset = false;
|
||||
DrawSeparatorOrSpacing();
|
||||
|
||||
if (!Nest)
|
||||
{
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
if (depth > 0)
|
||||
{
|
||||
DrawNestIndicator(depth);
|
||||
}
|
||||
|
||||
bool collapsing = CollapsingHeader && ConfigObject.Disableable;
|
||||
|
||||
// Draw the ConfigAttribute
|
||||
if (!collapsing)
|
||||
{
|
||||
DrawConfigAttribute(ref changed, _mainField);
|
||||
}
|
||||
|
||||
bool enabled = _mainField.GetValue(ConfigObject) as bool? ?? false;
|
||||
|
||||
// Draw children
|
||||
if (CollapseControl && Attribute.IsDefined(_mainField, typeof(CheckboxAttribute)))
|
||||
{
|
||||
if (collapsing)
|
||||
{
|
||||
if (ImGui.CollapsingHeader(ID + "##CollapsingHeader"))
|
||||
{
|
||||
DrawNestIndicator(depth);
|
||||
DrawConfigAttribute(ref changed, _mainField);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
reset |= DrawChildren(ref changed, depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!collapsing && enabled)
|
||||
{
|
||||
ImGui.BeginGroup();
|
||||
reset |= DrawChildren(ref changed, depth);
|
||||
ImGui.EndGroup();
|
||||
}
|
||||
}
|
||||
|
||||
return reset;
|
||||
}
|
||||
|
||||
private bool DrawChildren(ref bool changed, int depth)
|
||||
{
|
||||
bool reset = false;
|
||||
|
||||
int childDepth = depth + 1;
|
||||
foreach (ConfigNode child in _childNodes.Values)
|
||||
{
|
||||
if (child.Separator)
|
||||
{
|
||||
childDepth = 0;
|
||||
}
|
||||
|
||||
reset |= child.Draw(ref changed, childDepth);
|
||||
}
|
||||
|
||||
return reset;
|
||||
}
|
||||
|
||||
private void DrawConfigAttribute(ref bool changed, FieldInfo field)
|
||||
{
|
||||
if (_configAttribute is not null)
|
||||
{
|
||||
changed |= _configAttribute.Draw(field, ConfigObject, ID, CollapsingHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ManualDrawNode : ConfigNode
|
||||
{
|
||||
private MethodInfo _drawMethod;
|
||||
|
||||
public ManualDrawNode(MethodInfo method, PluginConfigObject configObject, string? id) : base(configObject, id, id ?? "")
|
||||
{
|
||||
_drawMethod = method;
|
||||
}
|
||||
|
||||
public override bool Draw(ref bool changed, int depth = 0)
|
||||
{
|
||||
object[] args = new object[] { false };
|
||||
bool? result = (bool?)_drawMethod.Invoke(ConfigObject, args);
|
||||
|
||||
bool arg = (bool)args[0];
|
||||
changed |= arg;
|
||||
return result ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using HSUI.Helpers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public abstract class Node
|
||||
{
|
||||
protected List<Node> _children = new List<Node>();
|
||||
public IReadOnlyList<Node> Children => _children.AsReadOnly();
|
||||
|
||||
public void Add(Node node)
|
||||
{
|
||||
_children.Add(node);
|
||||
}
|
||||
|
||||
#region reset
|
||||
protected Node? _nodeToReset = null;
|
||||
protected string? _nodeToResetName = null;
|
||||
|
||||
public virtual List<T> GetObjects<T>()
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
|
||||
foreach (Node node in _children)
|
||||
{
|
||||
list.AddRange(node.GetObjects<T>());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
protected void DrawExportResetContextMenu(Node node, string name)
|
||||
{
|
||||
if (_nodeToReset != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool allowExport = node.AllowExport();
|
||||
bool allowReset = node.AllowReset();
|
||||
if (!allowExport && !allowReset)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_nodeToReset = ImGuiHelper.DrawExportResetContextMenu(node, allowExport, allowReset);
|
||||
_nodeToResetName = name;
|
||||
}
|
||||
|
||||
protected virtual bool AllowExport()
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
if (child.AllowExport())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool AllowShare()
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
if (child.AllowShare())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool AllowReset()
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
if (child.AllowReset())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool DrawResetModal()
|
||||
{
|
||||
if (_nodeToReset == null || _nodeToResetName == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] lines = new string[] { "Are you sure you want to reset \"" + _nodeToResetName + "\"?" };
|
||||
var (didReset, didClose) = ImGuiHelper.DrawConfirmationModal("Reset?", lines);
|
||||
|
||||
if (didReset)
|
||||
{
|
||||
_nodeToReset.Reset();
|
||||
_nodeToReset = null;
|
||||
}
|
||||
else if (didClose)
|
||||
{
|
||||
_nodeToReset = null;
|
||||
}
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
child.Reset();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region save and load
|
||||
public virtual void Save(string path)
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
child.Save(path);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(string path)
|
||||
{
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
child.Load(path);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region export
|
||||
public virtual string? GetBase64String()
|
||||
{
|
||||
if (_children == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
string base64String = "";
|
||||
|
||||
foreach (Node child in _children)
|
||||
{
|
||||
string? childString = child.GetBase64String();
|
||||
|
||||
if (childString != null && childString.Length > 0)
|
||||
{
|
||||
base64String += "|" + childString;
|
||||
}
|
||||
}
|
||||
|
||||
return base64String;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using HSUI.Config.Attributes;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public class SectionNode : Node
|
||||
{
|
||||
public bool Selected;
|
||||
public string Name = null!;
|
||||
public bool ForceAllowExport = false;
|
||||
public string? ForceSelectedTabName = null;
|
||||
|
||||
public SectionNode() { }
|
||||
|
||||
protected override bool AllowExport()
|
||||
{
|
||||
if (ForceAllowExport) { return true; }
|
||||
|
||||
return base.AllowExport();
|
||||
}
|
||||
|
||||
public bool Draw(ref bool changed, float alpha)
|
||||
{
|
||||
if (!Selected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool didReset = false;
|
||||
|
||||
ImGui.NewLine();
|
||||
|
||||
if (ImGui.BeginChild(
|
||||
"DelvU_Settings_Tab",
|
||||
new Vector2(0, -10),
|
||||
false,
|
||||
ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse
|
||||
))
|
||||
{
|
||||
bool popColors = false;
|
||||
if (ConfigurationManager.Instance.OverrideDalamudStyle)
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Tab, new Vector4(45f / 255f, 45f / 255f, 45f / 255f, alpha));
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(45f / 255f, 45f / 255f, 45f / 255f, alpha));
|
||||
popColors = true;
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabBar("##Tabs", ImGuiTabBarFlags.None))
|
||||
{
|
||||
foreach (SubSectionNode subSectionNode in _children)
|
||||
{
|
||||
if (ForceSelectedTabName != null)
|
||||
{
|
||||
bool a = subSectionNode.Name == ForceSelectedTabName; // no idea how this works
|
||||
ImGuiTabItemFlags flag = subSectionNode.Name == ForceSelectedTabName ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None;
|
||||
|
||||
if (!ImGui.BeginTabItem(subSectionNode.Name, ref a, flag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ImGui.BeginTabItem(subSectionNode.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DrawExportResetContextMenu(subSectionNode, subSectionNode.Name);
|
||||
|
||||
ImGui.BeginChild("subconfig value", new Vector2(0, 0), true);
|
||||
didReset |= subSectionNode.Draw(ref changed);
|
||||
ImGui.EndChild();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
ForceSelectedTabName = null;
|
||||
}
|
||||
|
||||
if (popColors)
|
||||
{
|
||||
ImGui.PopStyleColor(2);
|
||||
}
|
||||
|
||||
didReset |= DrawResetModal();
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
public override void Save(string path)
|
||||
{
|
||||
foreach (SubSectionNode child in _children)
|
||||
{
|
||||
child.Save(Path.Combine(path, Name));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(string path)
|
||||
{
|
||||
foreach (SubSectionNode child in _children)
|
||||
{
|
||||
child.Load(Path.Combine(path, Name));
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigPageNode? GetOrAddConfig<T>() where T : PluginConfigObject
|
||||
{
|
||||
object[] attributes = typeof(T).GetCustomAttributes(true);
|
||||
|
||||
foreach (object attribute in attributes)
|
||||
{
|
||||
if (attribute is SubSectionAttribute subSectionAttribute)
|
||||
{
|
||||
foreach (SubSectionNode subSectionNode in _children)
|
||||
{
|
||||
if (subSectionNode.Name == subSectionAttribute.SubSectionName)
|
||||
{
|
||||
return subSectionNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
|
||||
if (subSectionAttribute.Depth == 0)
|
||||
{
|
||||
NestedSubSectionNode newNode = new();
|
||||
newNode.Name = subSectionAttribute.SubSectionName;
|
||||
newNode.Depth = 0;
|
||||
_children.Add(newNode);
|
||||
|
||||
return newNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
throw new ArgumentException("The provided configuration object does not specify a sub-section: " + type.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
using HSUI.Config.Attributes;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace HSUI.Config.Tree
|
||||
{
|
||||
public abstract class SubSectionNode : Node
|
||||
{
|
||||
public string Name = null!;
|
||||
public int Depth;
|
||||
public string? ForceSelectedTabName = null;
|
||||
|
||||
public abstract bool Draw(ref bool changed);
|
||||
|
||||
public abstract ConfigPageNode? GetOrAddConfig<T>() where T : PluginConfigObject;
|
||||
}
|
||||
|
||||
public class NestedSubSectionNode : SubSectionNode
|
||||
{
|
||||
public NestedSubSectionNode() { }
|
||||
|
||||
public override bool Draw(ref bool changed)
|
||||
{
|
||||
bool didReset = false;
|
||||
|
||||
if (_children.Count > 1)
|
||||
{
|
||||
ImGui.BeginChild(
|
||||
"DelvUI_Tabs_" + Depth,
|
||||
new Vector2(0, ImGui.GetWindowHeight() - 22),
|
||||
false,
|
||||
ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse
|
||||
); // Leave room for 1 line below us
|
||||
|
||||
if (ImGui.BeginTabBar("##tabs" + Depth, ImGuiTabBarFlags.None))
|
||||
{
|
||||
didReset |= DrawSubConfig(ref changed);
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.BeginChild("item" + Depth + " view", new Vector2(0, ImGui.GetWindowHeight() - 20)); // Leave room for 1 line below us
|
||||
|
||||
didReset |= DrawSubConfig(ref changed);
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
public bool DrawSubConfig(ref bool changed)
|
||||
{
|
||||
bool didReset = false;
|
||||
|
||||
foreach (SubSectionNode subSectionNode in _children)
|
||||
{
|
||||
if (subSectionNode is NestedSubSectionNode)
|
||||
{
|
||||
if (ForceSelectedTabName != null)
|
||||
{
|
||||
bool a = subSectionNode.Name == ForceSelectedTabName; // no idea how this works
|
||||
ImGuiTabItemFlags flag = subSectionNode.Name == ForceSelectedTabName ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None;
|
||||
|
||||
if (!ImGui.BeginTabItem(subSectionNode.Name, ref a, flag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ImGui.BeginTabItem(subSectionNode.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DrawExportResetContextMenu(subSectionNode, subSectionNode.Name);
|
||||
|
||||
ImGui.BeginChild("subconfig" + Depth + " value", new Vector2(0, ImGui.GetWindowHeight()));
|
||||
didReset |= subSectionNode.Draw(ref changed);
|
||||
ImGui.EndChild();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
else
|
||||
{
|
||||
didReset |= subSectionNode.Draw(ref changed);
|
||||
}
|
||||
}
|
||||
|
||||
ForceSelectedTabName = null;
|
||||
|
||||
didReset |= DrawResetModal();
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
public override void Save(string path)
|
||||
{
|
||||
foreach (SubSectionNode child in _children)
|
||||
{
|
||||
child.Save(Path.Combine(path, Name));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(string path)
|
||||
{
|
||||
foreach (SubSectionNode child in _children)
|
||||
{
|
||||
child.Load(Path.Combine(path, Name));
|
||||
}
|
||||
}
|
||||
|
||||
public override ConfigPageNode? GetOrAddConfig<T>()
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (type == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
object[] attributes = type.GetCustomAttributes(true);
|
||||
|
||||
foreach (object attribute in attributes)
|
||||
{
|
||||
if (attribute is SubSectionAttribute subSectionAttribute)
|
||||
{
|
||||
if (subSectionAttribute.Depth != Depth + 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (SubSectionNode subSectionNode in _children)
|
||||
{
|
||||
if (subSectionNode.Name == subSectionAttribute.SubSectionName)
|
||||
{
|
||||
return subSectionNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
|
||||
NestedSubSectionNode nestedSubSectionNode = new();
|
||||
nestedSubSectionNode.Name = subSectionAttribute.SubSectionName;
|
||||
nestedSubSectionNode.Depth = Depth + 1;
|
||||
_children.Add(nestedSubSectionNode);
|
||||
|
||||
return nestedSubSectionNode.GetOrAddConfig<T>();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SubSectionNode subSectionNode in _children)
|
||||
{
|
||||
if (subSectionNode.Name == type.FullName && subSectionNode is ConfigPageNode node)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigPageNode configPageNode = new();
|
||||
configPageNode.ConfigObject = ConfigurationManager.GetDefaultConfigObjectForType(type);
|
||||
configPageNode.Name = type.FullName!;
|
||||
_children.Add(configPageNode);
|
||||
|
||||
return configPageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user