// 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();
}
}
}