using Dalamud.Interface; using Dalamud.Interface.Utility; using HSUI.Enums; using HSUI.Helpers; using HSUI.Interface.GeneralElements; using Dalamud.Bindings.ImGui; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Reflection; namespace HSUI.Config.Attributes { #region class attributes [AttributeUsage(AttributeTargets.Class)] public class ExportableAttribute : Attribute { public bool exportable; public ExportableAttribute(bool exportable) { this.exportable = exportable; } } public class ShareableAttribute : Attribute { public bool shareable; public ShareableAttribute(bool shareable) { this.shareable = shareable; } } public class ResettableAttribute : Attribute { public bool resettable; public ResettableAttribute(bool resettable) { this.resettable = resettable; } } [AttributeUsage(AttributeTargets.Class)] public class DisableableAttribute : Attribute { public bool disableable; public DisableableAttribute(bool disableable) { this.disableable = disableable; } } [AttributeUsage(AttributeTargets.Class)] public class DisableParentSettingsAttribute : Attribute { public readonly string[] DisabledFields; public DisableParentSettingsAttribute(params string[] fields) { this.DisabledFields = fields; } } #endregion #region method attributes [AttributeUsage(AttributeTargets.Method)] public class ManualDrawAttribute : Attribute { } #endregion #region field attributes [AttributeUsage(AttributeTargets.Field)] public abstract class ConfigAttribute : Attribute { public string friendlyName; public bool isMonitored = false; public bool separator = false; public bool spacing = false; public string? help = null; public ConfigAttribute(string friendlyName) { this.friendlyName = friendlyName; } public bool Draw(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader = false) { bool result = DrawField(field, config, ID, collapsingHeader); if (help != null && ImGui.IsItemHovered()) { ImGui.SetTooltip(help); } return result; } public abstract bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader = false); protected string IDText(string? ID) => ID != null ? " ##" + ID : ""; protected void TriggerChangeEvent(PluginConfigObject config, string fieldName, object value, ChangeType type = ChangeType.None) { if (!isMonitored || config is not IOnChangeEventArgs eventObject) { return; } eventObject.OnValueChanged(new OnChangeEventArgs(fieldName, (T)value, type)); } } [AttributeUsage(AttributeTargets.Field)] public class CheckboxAttribute : ConfigAttribute { public CheckboxAttribute(string friendlyName) : base(friendlyName) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { var disableable = config.Disableable; if (!disableable && friendlyName == "Enabled") { if (ID != null) { ImGui.Text(ID); } return false; } bool? fieldVal = (bool?)field.GetValue(config); bool boolVal = fieldVal.HasValue ? fieldVal.Value : false; if (ImGui.Checkbox(ID != null && friendlyName == "Enabled" && !collapsingHeader ? ID : friendlyName + IDText(ID), ref boolVal)) { field.SetValue(config, boolVal); TriggerChangeEvent(config, field.Name, boolVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class RadioSelector : ConfigAttribute { private string[] _options; public string? Label { get; set; } public RadioSelector(params string[] options) : base(string.Join("_", options)) { _options = options; } public RadioSelector(Type enumType) : this(enumType.IsEnum ? Enum.GetNames(enumType) : Array.Empty()) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { bool changed = false; object? fieldVal = field.GetValue(config); int intVal = 0; if (fieldVal != null) { intVal = (int)fieldVal; } string? label = Label ?? friendlyName; if (!string.IsNullOrEmpty(label) && label != string.Join("_", _options)) { ImGui.TextUnformatted(label); ImGui.SameLine(); } for (int i = 0; i < _options.Length; i++) { changed |= ImGui.RadioButton(_options[i], ref intVal, i); if (i < _options.Length - 1) { ImGui.SameLine(); } } if (changed) { field.SetValue(config, intVal); TriggerChangeEvent(config, field.Name, intVal); } return changed; } } [AttributeUsage(AttributeTargets.Field)] public class DragFloatAttribute : ConfigAttribute { public float min; public float max; public float velocity; public DragFloatAttribute(string friendlyName) : base(friendlyName) { min = 1f; max = 1000f; velocity = 1f; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { float? fieldVal = (float?)field.GetValue(config); float floatVal = fieldVal.HasValue ? fieldVal.Value : 0; if (ImGui.DragFloat(friendlyName + IDText(ID), ref floatVal, velocity, min, max)) { field.SetValue(config, floatVal); TriggerChangeEvent(config, field.Name, floatVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class DragIntAttribute : ConfigAttribute { public int min; public int max; public float velocity; public DragIntAttribute(string friendlyName) : base(friendlyName) { min = 1; max = 1000; velocity = 1; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { int? fieldVal = (int?)field.GetValue(config); int intVal = fieldVal.HasValue ? fieldVal.Value : 0; if (ImGui.DragInt(friendlyName + IDText(ID), ref intVal, velocity, min, max)) { field.SetValue(config, intVal); TriggerChangeEvent(config, field.Name, intVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class DragFloat2Attribute : ConfigAttribute { public float min; public float max; public float velocity; public DragFloat2Attribute(string friendlyName) : base(friendlyName) { min = 1f; max = 1000f; velocity = 1f; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { Vector2? fieldVal = (Vector2?)field.GetValue(config); Vector2 vectorVal = fieldVal.HasValue ? fieldVal.Value : Vector2.Zero; if (ImGui.DragFloat2(friendlyName + IDText(ID), ref vectorVal, velocity, min, max)) { field.SetValue(config, vectorVal); TriggerChangeEvent(config, field.Name, vectorVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class DragInt2Attribute : ConfigAttribute { public int min; public int max; public int velocity; public DragInt2Attribute(string friendlyName) : base(friendlyName) { min = 1; max = 1000; velocity = 1; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { Vector2? fieldVal = (Vector2?)field.GetValue(config); Vector2 vectorVal = fieldVal.HasValue ? fieldVal.Value : Vector2.Zero; if (ImGui.DragFloat2(friendlyName + IDText(ID), ref vectorVal, velocity, min, max)) { field.SetValue(config, vectorVal); TriggerChangeEvent(config, field.Name, vectorVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class InputTextAttribute : ConfigAttribute { public int maxLength; public bool formattable = true; private string _searchText = ""; public InputTextAttribute(string friendlyName) : base(friendlyName) { this.friendlyName = friendlyName; maxLength = 999; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { string? fieldVal = (string?)field.GetValue(config); string stringVal = fieldVal ?? ""; string? finalValue = null; string popupId = ID != null ? "DelvUI_TextTagsList " + ID : "DelvUI_TextTagsList ##" + friendlyName; if (!formattable) { if (ImGui.InputText(friendlyName + IDText(ID), ref stringVal)) { finalValue = stringVal; } } else { float scale = ImGuiHelpers.GlobalScale; float width = ImGui.CalcItemWidth(); float height = Math.Max(24 * scale, ImGui.CalcTextSize(stringVal, false, width).Y + 6 * scale); Vector2 size = new Vector2(width, height); if (ImGui.InputTextMultiline(friendlyName + IDText(ID), ref stringVal, maxLength, size, ImGuiInputTextFlags.AllowTabInput)) { finalValue = stringVal; } // text tags ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Pen.ToIconString() + "##" + ID)) { ImGui.OpenPopup(popupId); } ImGui.PopFont(); ImGuiHelper.SetTooltip("Text Tags"); } var selectedTag = ImGuiHelper.DrawTextTagsList(popupId, ref _searchText); if (selectedTag != null) { finalValue = stringVal + selectedTag; } if (finalValue != null) { field.SetValue(config, finalValue); TriggerChangeEvent(config, field.Name, finalValue); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class ColorEdit4Attribute : ConfigAttribute { public ColorEdit4Attribute(string friendlyName) : base(friendlyName) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { PluginConfigColor? colorVal = (PluginConfigColor?)field.GetValue(config); Vector4 vector = (colorVal != null ? colorVal.Vector : Vector4.Zero); if (ImGui.ColorEdit4(friendlyName + IDText(ID), ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar)) { if (colorVal is null) { return false; } colorVal.Vector = vector; field.SetValue(config, colorVal); TriggerChangeEvent(config, field.Name, colorVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class ComboAttribute : ConfigAttribute { public string[] options; public ComboAttribute(string friendlyName, params string[] options) : base(friendlyName) { this.options = options; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { object? fieldVal = field.GetValue(config); int intVal = 0; if (fieldVal != null) { intVal = (int)fieldVal; } if (ImGui.Combo(friendlyName + IDText(ID), ref intVal, options, 4)) { field.SetValue(config, intVal); TriggerChangeEvent(config, field.Name, intVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class DragDropHorizontalAttribute : ConfigAttribute { public string[] names; public DragDropHorizontalAttribute(string friendlyName, params string[] names) : base(friendlyName) { this.names = names; } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { ImGui.Text(friendlyName); int[]? fieldVal = (int[]?)field.GetValue(config); int[] order = fieldVal ?? Array.Empty(); for (int i = 0; i < order.Length; i++) { ImGui.SameLine(); ImGui.Button(names[order[i]], new Vector2(100, 25)); if (ImGui.IsItemActive()) { float drag_dx = ImGui.GetMouseDragDelta(ImGuiMouseButton.Left).X; if ((drag_dx > 80.0f && i < order.Length - 1)) { var _curri = order[i]; order[i] = order[i + 1]; order[i + 1] = _curri; field.SetValue(config, order); ImGui.ResetMouseDragDelta(); } else if ((drag_dx < -80.0f && i > 0)) { var _curri = order[i]; order[i] = order[i - 1]; order[i - 1] = _curri; field.SetValue(config, order); ImGui.ResetMouseDragDelta(); } TriggerChangeEvent(config, field.Name, order); return true; } } return false; } } [AttributeUsage(AttributeTargets.Field)] public class FontAttribute : ConfigAttribute { public FontAttribute(string friendlyName = "Font and Size") : base(friendlyName) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { var fontsConfig = ConfigurationManager.Instance.GetConfigObject(); if (fontsConfig == null) { return false; } string? stringVal = (string?)field.GetValue(config); int index = stringVal == null || stringVal.Length == 0 || !fontsConfig.Fonts.ContainsKey(stringVal) ? -1 : fontsConfig.Fonts.IndexOfKey(stringVal); if (index == -1) { if (fontsConfig.Fonts.ContainsKey(FontsConfig.DefaultBigFontKey)) { index = fontsConfig.Fonts.IndexOfKey(FontsConfig.DefaultBigFontKey); } else { index = 0; } } var options = fontsConfig.Fonts.Values.Select(fontData => fontData.Name + " " + fontData.Size.ToString()).ToArray(); if (ImGui.Combo(friendlyName + IDText(ID), ref index, options, 4)) { stringVal = fontsConfig.Fonts.Keys[index]; field.SetValue(config, stringVal); TriggerChangeEvent(config, field.Name, stringVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class BarTextureAttribute : ConfigAttribute { public BarTextureAttribute(string friendlyName = "Bar Texture") : base(friendlyName) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { if (BarTexturesManager.Instance == null) { return false; } List textures = BarTexturesManager.Instance.BarTextureNames.ToList(); string? stringVal = (string?)field.GetValue(config); int index = 0; if (stringVal != null && stringVal.Length > 0 && textures.Contains(stringVal)) { index = textures.IndexOf(stringVal); } string[] options = textures.ToArray(); if (ImGui.Combo(friendlyName + IDText(ID), ref index, options, 10)) { stringVal = options[index]; field.SetValue(config, stringVal); TriggerChangeEvent(config, field.Name, stringVal); return true; } return false; } } [AttributeUsage(AttributeTargets.Field)] public class AnchorAttribute : ComboAttribute { public AnchorAttribute(string friendlyName) : base(friendlyName, new string[] { "Center", "Left", "Right", "Top", "TopLeft", "TopRight", "Bottom", "BottomLeft", "BottomRight" }) { } } [AttributeUsage(AttributeTargets.Field)] public class BarTextureDrawModeAttribute : ComboAttribute { public BarTextureDrawModeAttribute(string friendlyName) : base(friendlyName, new string[] { "Stretch", "Repeat Horizontal", "Repeat Vertical", "Repeat" }) { } } [AttributeUsage(AttributeTargets.Field)] public class StrataLevelAttribute : ConfigAttribute { private string[] options = { "Lowest", "Low", "Mid-Low", "Mid", "Mid-High", "High", "Highest" }; public StrataLevelAttribute(string friendlyName) : base(friendlyName) { } public override bool DrawField(FieldInfo field, PluginConfigObject config, string? ID, bool collapsingHeader) { object? fieldVal = field.GetValue(config); int intVal = 0; if (fieldVal != null) { intVal = (int)fieldVal; } if (ImGui.Combo(friendlyName + IDText(ID), ref intVal, options, 4)) { field.SetValue(config, (StrataLevel?)intVal); TriggerChangeEvent(config, field.Name, intVal); ConfigurationManager.Instance?.OnStrataLevelChanged(config); return true; } return false; } } #endregion #region field ordering attributes [AttributeUsage(AttributeTargets.Field)] public class OrderAttribute : Attribute { public int pos; public string? collapseWith = "Enabled"; public OrderAttribute(int pos) { this.pos = pos; } } [AttributeUsage(AttributeTargets.Field)] public class NestedConfigAttribute : OrderAttribute { public string friendlyName; public bool separator = false; public bool spacing = true; public bool nest = true; public bool collapsingHeader = true; public NestedConfigAttribute(string friendlyName, int pos) : base(pos) { this.friendlyName = friendlyName; } } #endregion }