// MIT License - Copyright (c) 2025 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Core.Attributes { using System; using UnityEngine; /// /// Draws enum and dropdown-backed fields as a toolbar of toggle buttons. /// Designed to mirror Odin Inspector's EnumToggleButtons experience while remaining editor-only and dependency free. /// /// /// /// When applied to a field backed by a [Flags] enum, each discrete flag is rendered as an individual toggle button. /// This makes it significantly easier to reason about composite bitmasks in the inspector compared to Unity's built-in mask field. /// /// /// Throughout the documentation this workflow is often referred to as WEnumToggleFlags; the attribute name remains /// but no additional setup is required—simply annotate the flagged enum field. /// /// /// The attribute can also be combined with , , or /// to render those curated lists as toggle buttons instead of a popup. /// In this mode only a single option is active at a time, but the visual presentation becomes much clearer for small choice sets. /// /// /// Because everything is driven through serialized fields and reflection, the attribute remains editor-safe and transparent to build players. /// No runtime components are introduced and the backing value on the component or asset stays unchanged. /// /// /// Large option sets automatically paginate based on the Unity Helpers project settings. /// Set to or supply a custom to override that behaviour. /// /// /// /// Flag-based enums: /// /// [System.Flags] /// public enum MovementCapabilities /// { /// None = 0, /// Walk = 1 << 0, /// Jump = 1 << 1, /// Swim = 1 << 2, /// Fly = 1 << 3, /// } /// /// [WEnumToggleButtons(ButtonsPerRow = 3)] /// public MovementCapabilities unlockedAbilities; /// /// Dropdown-backed fields: /// /// [WEnumToggleButtons(ShowSelectNone = false)] /// [StringInList("Low", "Medium", "High")] /// public string difficulty; /// /// [WEnumToggleButtons(ButtonsPerRow = 4)] /// [IntDropDown(15, 30, 45, 60)] /// public int targetFrameRate; /// /// [WEnumToggleButtons] /// [WValueDropDown(typeof(LocalizationCatalogue), nameof(LocalizationCatalogue.GetKnownKeys), typeof(string))] /// public string localizationKey; /// /// public sealed class WEnumToggleButtonsAttribute : PropertyAttribute { private string _colorKey; /// /// Initializes the attribute with automatic row sizing. /// public WEnumToggleButtonsAttribute() : this( buttonsPerRow: 0, showSelectAll: true, showSelectNone: true, enablePagination: true, pageSize: 0, colorKey: null ) { } /// /// Initializes the attribute with a fixed number of buttons per row. /// /// /// Number of buttons to render per row. Values below one fall back to automatic sizing. /// Use zero to keep the automatic layout behaviour while still enabling the optional toolbar controls. /// /// Thrown when is negative. public WEnumToggleButtonsAttribute(int buttonsPerRow) : this( buttonsPerRow: buttonsPerRow, showSelectAll: true, showSelectNone: true, enablePagination: true, pageSize: 0, colorKey: null ) { } /// /// Initializes the attribute with full control over all configuration options. /// /// /// Number of buttons to render per row. Values below one fall back to automatic sizing. /// Use zero to keep the automatic layout behaviour while still enabling the optional toolbar controls. /// /// /// Whether a quick action button for selecting every flag should be displayed. /// Only meaningful for [Flags] enums. /// /// /// Whether a quick action button for clearing every flag should be displayed. /// Only meaningful for [Flags] enums. /// /// /// Whether pagination may be applied when the option count exceeds the configured threshold. /// /// /// The maximum number of options displayed per page before pagination occurs. /// Values less than or equal to zero defer to the project-wide default. /// /// /// An optional palette key used to resolve theming for the toggle buttons. /// /// Thrown when is negative. public WEnumToggleButtonsAttribute( int buttonsPerRow = 0, bool showSelectAll = true, bool showSelectNone = true, bool enablePagination = true, int pageSize = 0, string colorKey = null ) { if (buttonsPerRow < 0) { throw new ArgumentOutOfRangeException( nameof(buttonsPerRow), "Value cannot be negative." ); } ButtonsPerRow = buttonsPerRow; ShowSelectAll = showSelectAll; ShowSelectNone = showSelectNone; EnablePagination = enablePagination; PageSize = pageSize; ColorKey = colorKey; } /// /// Gets the desired number of buttons per row. /// A value of zero indicates that the drawer should determine a sensible layout automatically. /// public int ButtonsPerRow { get; } /// /// Gets or sets a value indicating whether a quick action button for selecting every flag should be displayed. /// Only meaningful for [Flags] enums. /// public bool ShowSelectAll { get; set; } /// /// Gets or sets a value indicating whether a quick action button for clearing every flag should be displayed. /// Only meaningful for [Flags] enums. /// public bool ShowSelectNone { get; set; } /// /// Gets or sets a value indicating whether pagination may be applied when the option count exceeds the configured threshold. /// Disable when all options should always be visible regardless of their count. /// public bool EnablePagination { get; set; } /// /// Gets or sets the maximum number of options displayed per page before pagination occurs. /// Values less than or equal to zero defer to the project-wide default stored in UnityHelpersSettings. /// public int PageSize { get; set; } /// /// Gets or sets an optional palette key used to resolve theming for the toggle buttons. /// public string ColorKey { get => _colorKey; set => _colorKey = string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } } }