Auto-generate enum type widget boilerplate for radio buttons and dropdown menus (#2589)

* First draft of factoring out the dropdown boilerplate

* Add proc macro for enum boilerplate

* Detect whether to say `crate` or the name

* Clean up the input and naming of the enum macro

* Rename a file

* Do the rename of code too

* Use the attribute-driven selection of radio vs dropdown

* Add a metadata struct and tooltips

* Move the new traits to a better place.

* Use ChoiceType, part 1

* Use ChoiceType, part 2

* Introduce a builder API for choice widgets

* Start using the new new API

* DomainWarpType should be a dropdown still

* Handle the case where a node property can never have a socket

* Rustfmt

* Code review

* Update stable node IDs in test

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
kythyria
2025-05-01 12:14:26 +01:00
committed by GitHub
parent 9303953cf8
commit 9ef9b205d9
18 changed files with 713 additions and 990 deletions

View File

@@ -34,9 +34,11 @@ use spirv_std::num_traits::float::Float;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, Hash)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, Hash, node_macro::ChoiceType)]
#[widget(Dropdown)]
pub enum LuminanceCalculation {
#[default]
#[label("sRGB")]
SRGB,
Perceptual,
AverageChannels,
@@ -44,30 +46,6 @@ pub enum LuminanceCalculation {
MaximumChannels,
}
impl LuminanceCalculation {
pub fn list() -> [LuminanceCalculation; 5] {
[
LuminanceCalculation::SRGB,
LuminanceCalculation::Perceptual,
LuminanceCalculation::AverageChannels,
LuminanceCalculation::MinimumChannels,
LuminanceCalculation::MaximumChannels,
]
}
}
impl core::fmt::Display for LuminanceCalculation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
LuminanceCalculation::SRGB => write!(f, "sRGB"),
LuminanceCalculation::Perceptual => write!(f, "Perceptual"),
LuminanceCalculation::AverageChannels => write!(f, "Average Channels"),
LuminanceCalculation::MinimumChannels => write!(f, "Minimum Channels"),
LuminanceCalculation::MaximumChannels => write!(f, "Maximum Channels"),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, Hash)]
@@ -844,9 +822,11 @@ async fn vibrance<T: Adjust<Color>>(
image
}
/// Color Channel
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[widget(Radio)]
pub enum RedGreenBlue {
#[default]
Red,
@@ -854,19 +834,11 @@ pub enum RedGreenBlue {
Blue,
}
impl core::fmt::Display for RedGreenBlue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RedGreenBlue::Red => write!(f, "Red"),
RedGreenBlue::Green => write!(f, "Green"),
RedGreenBlue::Blue => write!(f, "Blue"),
}
}
}
/// Color Channel
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[widget(Radio)]
pub enum RedGreenBlueAlpha {
#[default]
Red,
@@ -875,24 +847,17 @@ pub enum RedGreenBlueAlpha {
Alpha,
}
impl core::fmt::Display for RedGreenBlueAlpha {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RedGreenBlueAlpha::Red => write!(f, "Red"),
RedGreenBlueAlpha::Green => write!(f, "Green"),
RedGreenBlueAlpha::Blue => write!(f, "Blue"),
RedGreenBlueAlpha::Alpha => write!(f, "Alpha"),
}
}
}
/// Style of noise pattern
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[widget(Dropdown)]
pub enum NoiseType {
#[default]
Perlin,
#[label("OpenSimplex2")]
OpenSimplex2,
#[label("OpenSimplex2S")]
OpenSimplex2S,
Cellular,
ValueCubic,
@@ -900,176 +865,71 @@ pub enum NoiseType {
WhiteNoise,
}
impl core::fmt::Display for NoiseType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NoiseType::Perlin => write!(f, "Perlin"),
NoiseType::OpenSimplex2 => write!(f, "OpenSimplex2"),
NoiseType::OpenSimplex2S => write!(f, "OpenSimplex2S"),
NoiseType::Cellular => write!(f, "Cellular"),
NoiseType::ValueCubic => write!(f, "Value Cubic"),
NoiseType::Value => write!(f, "Value"),
NoiseType::WhiteNoise => write!(f, "White Noise"),
}
}
}
impl NoiseType {
pub fn list() -> &'static [NoiseType; 7] {
&[
NoiseType::Perlin,
NoiseType::OpenSimplex2,
NoiseType::OpenSimplex2S,
NoiseType::Cellular,
NoiseType::ValueCubic,
NoiseType::Value,
NoiseType::WhiteNoise,
]
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
/// Style of layered levels of the noise pattern
pub enum FractalType {
#[default]
None,
#[label("Fractional Brownian Motion")]
FBm,
Ridged,
PingPong,
#[label("Progressive (Domain Warp Only)")]
DomainWarpProgressive,
#[label("Independent (Domain Warp Only)")]
DomainWarpIndependent,
}
impl core::fmt::Display for FractalType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
FractalType::None => write!(f, "None"),
FractalType::FBm => write!(f, "Fractional Brownian Motion"),
FractalType::Ridged => write!(f, "Ridged"),
FractalType::PingPong => write!(f, "Ping Pong"),
FractalType::DomainWarpProgressive => write!(f, "Progressive (Domain Warp Only)"),
FractalType::DomainWarpIndependent => write!(f, "Independent (Domain Warp Only)"),
}
}
}
impl FractalType {
pub fn list() -> &'static [FractalType; 6] {
&[
FractalType::None,
FractalType::FBm,
FractalType::Ridged,
FractalType::PingPong,
FractalType::DomainWarpProgressive,
FractalType::DomainWarpIndependent,
]
}
}
/// Distance function used by the cellular noise
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
pub enum CellularDistanceFunction {
#[default]
Euclidean,
#[label("Euclidean Squared (Faster)")]
EuclideanSq,
Manhattan,
Hybrid,
}
impl core::fmt::Display for CellularDistanceFunction {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CellularDistanceFunction::Euclidean => write!(f, "Euclidean"),
CellularDistanceFunction::EuclideanSq => write!(f, "Euclidean Squared (Faster)"),
CellularDistanceFunction::Manhattan => write!(f, "Manhattan"),
CellularDistanceFunction::Hybrid => write!(f, "Hybrid"),
}
}
}
impl CellularDistanceFunction {
pub fn list() -> &'static [CellularDistanceFunction; 4] {
&[
CellularDistanceFunction::Euclidean,
CellularDistanceFunction::EuclideanSq,
CellularDistanceFunction::Manhattan,
CellularDistanceFunction::Hybrid,
]
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
pub enum CellularReturnType {
CellValue,
#[default]
#[label("Nearest (F1)")]
Nearest,
#[label("Next Nearest (F2)")]
NextNearest,
#[label("Average (F1 / 2 + F2 / 2)")]
Average,
#[label("Difference (F2 - F1)")]
Difference,
#[label("Product (F2 * F1 / 2)")]
Product,
#[label("Division (F1 / F2)")]
Division,
}
impl core::fmt::Display for CellularReturnType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CellularReturnType::CellValue => write!(f, "Cell Value"),
CellularReturnType::Nearest => write!(f, "Nearest (F1)"),
CellularReturnType::NextNearest => write!(f, "Next Nearest (F2)"),
CellularReturnType::Average => write!(f, "Average (F1 / 2 + F2 / 2)"),
CellularReturnType::Difference => write!(f, "Difference (F2 - F1)"),
CellularReturnType::Product => write!(f, "Product (F2 * F1 / 2)"),
CellularReturnType::Division => write!(f, "Division (F1 / F2)"),
}
}
}
impl CellularReturnType {
pub fn list() -> &'static [CellularReturnType; 7] {
&[
CellularReturnType::CellValue,
CellularReturnType::Nearest,
CellularReturnType::NextNearest,
CellularReturnType::Average,
CellularReturnType::Difference,
CellularReturnType::Product,
CellularReturnType::Division,
]
}
}
/// Type of domain warp
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[widget(Dropdown)]
pub enum DomainWarpType {
#[default]
None,
#[label("OpenSimplex2")]
OpenSimplex2,
#[label("OpenSimplex2 Reduced")]
OpenSimplex2Reduced,
BasicGrid,
}
impl core::fmt::Display for DomainWarpType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DomainWarpType::None => write!(f, "None"),
DomainWarpType::OpenSimplex2 => write!(f, "OpenSimplex2"),
DomainWarpType::OpenSimplex2Reduced => write!(f, "OpenSimplex2 Reduced"),
DomainWarpType::BasicGrid => write!(f, "Basic Grid"),
}
}
}
impl DomainWarpType {
pub fn list() -> &'static [DomainWarpType; 4] {
&[DomainWarpType::None, DomainWarpType::OpenSimplex2, DomainWarpType::OpenSimplex2Reduced, DomainWarpType::BasicGrid]
}
}
// Aims for interoperable compatibility with:
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
@@ -1169,26 +1029,18 @@ async fn channel_mixer<T: Adjust<Color>>(
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[widget(Radio)]
pub enum RelativeAbsolute {
#[default]
Relative,
Absolute,
}
impl core::fmt::Display for RelativeAbsolute {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RelativeAbsolute::Relative => write!(f, "Relative"),
RelativeAbsolute::Absolute => write!(f, "Absolute"),
}
}
}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", derive(specta::Type))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
pub enum SelectiveColorChoice {
#[default]
Reds,
@@ -1197,27 +1049,13 @@ pub enum SelectiveColorChoice {
Cyans,
Blues,
Magentas,
#[menu_separator]
Whites,
Neutrals,
Blacks,
}
impl core::fmt::Display for SelectiveColorChoice {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
SelectiveColorChoice::Reds => write!(f, "Reds"),
SelectiveColorChoice::Yellows => write!(f, "Yellows"),
SelectiveColorChoice::Greens => write!(f, "Greens"),
SelectiveColorChoice::Cyans => write!(f, "Cyans"),
SelectiveColorChoice::Blues => write!(f, "Blues"),
SelectiveColorChoice::Magentas => write!(f, "Magentas"),
SelectiveColorChoice::Whites => write!(f, "Whites"),
SelectiveColorChoice::Neutrals => write!(f, "Neutrals"),
SelectiveColorChoice::Blacks => write!(f, "Blacks"),
}
}
}
// Aims for interoperable compatibility with:
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27selc%27%20%3D%20Selective%20color
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=from%20%2D100...100.%20.-,Selective%20Color,-Selective%20Color%20settings