Use builder pattern for widgets (#941)
* Use builder pattern for widgets * Arguments to new function
This commit is contained in:
parent
1a6ee7c542
commit
de407f8b23
|
|
@ -447,29 +447,16 @@ impl WidgetHolder {
|
||||||
Self { widget_id: generate_uuid(), widget }
|
Self { widget_id: generate_uuid(), widget }
|
||||||
}
|
}
|
||||||
pub fn unrelated_separator() -> Self {
|
pub fn unrelated_separator() -> Self {
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Unrelated).widget_holder()
|
||||||
separator_type: SeparatorType::Unrelated,
|
|
||||||
direction: SeparatorDirection::Horizontal,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
pub fn related_separator() -> Self {
|
pub fn related_separator() -> Self {
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Related).widget_holder()
|
||||||
separator_type: SeparatorType::Related,
|
|
||||||
direction: SeparatorDirection::Horizontal,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
pub fn text_widget(text: impl Into<String>) -> Self {
|
pub fn text_widget(text: impl Into<String>) -> Self {
|
||||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
TextLabel::new(text).widget_holder()
|
||||||
value: text.into(),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
pub fn bold_text(text: impl Into<String>) -> Self {
|
pub fn bold_text(text: impl Into<String>) -> Self {
|
||||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
TextLabel::new(text).bold(true).widget_holder()
|
||||||
value: text.into(),
|
|
||||||
bold: true,
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
||||||
pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) {
|
pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
pub mod layout_widget;
|
pub mod layout_widget;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
|
pub mod widget_prelude {
|
||||||
|
pub use super::layout_widget::{LayoutGroup, Widget, WidgetHolder, WidgetLayout};
|
||||||
|
pub use super::widgets::assist_widgets::*;
|
||||||
|
pub use super::widgets::button_widgets::*;
|
||||||
|
pub use super::widgets::input_widgets::*;
|
||||||
|
pub use super::widgets::label_widgets::*;
|
||||||
|
pub use super::widgets::menu_widgets::*;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,16 @@ use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||||
use crate::messages::{input_mapper::utility_types::misc::ActionKeys, portfolio::document::node_graph::FrontendGraphDataType};
|
use crate::messages::{input_mapper::utility_types::misc::ActionKeys, portfolio::document::node_graph::FrontendGraphDataType};
|
||||||
|
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct IconButton {
|
pub struct IconButton {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub size: u32, // TODO: Convert to an `IconSize` enum
|
pub size: u32, // TODO: Convert to an `IconSize` enum
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
@ -26,7 +29,7 @@ pub struct IconButton {
|
||||||
pub on_update: WidgetCallback<IconButton>,
|
pub on_update: WidgetCallback<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct PopoverButton {
|
pub struct PopoverButton {
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
|
|
@ -34,9 +37,11 @@ pub struct PopoverButton {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
||||||
// Placeholder popover content heading
|
// Placeholder popover content heading
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub header: String,
|
pub header: String,
|
||||||
|
|
||||||
// Placeholder popover content paragraph
|
// Placeholder popover content paragraph
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
|
||||||
pub tooltip: String,
|
pub tooltip: String,
|
||||||
|
|
@ -45,7 +50,7 @@ pub struct PopoverButton {
|
||||||
pub tooltip_shortcut: Option<ActionKeys>,
|
pub tooltip_shortcut: Option<ActionKeys>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
||||||
pub struct ParameterExposeButton {
|
pub struct ParameterExposeButton {
|
||||||
|
|
@ -65,10 +70,11 @@ pub struct ParameterExposeButton {
|
||||||
pub on_update: WidgetCallback<ParameterExposeButton>,
|
pub on_update: WidgetCallback<ParameterExposeButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
||||||
pub struct TextButton {
|
pub struct TextButton {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub label: String,
|
pub label: String,
|
||||||
|
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
|
|
@ -91,10 +97,11 @@ pub struct TextButton {
|
||||||
pub on_update: WidgetCallback<TextButton>,
|
pub on_update: WidgetCallback<TextButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
|
||||||
pub struct BreadcrumbTrailButtons {
|
pub struct BreadcrumbTrailButtons {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub labels: Vec<String>,
|
pub labels: Vec<String>,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@ use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||||
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||||
|
|
||||||
use document_legacy::{color::Color, layers::layer_info::LayerDataTypeDiscriminant, LayerId};
|
use document_legacy::{color::Color, layers::layer_info::LayerDataTypeDiscriminant, LayerId};
|
||||||
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
|
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct CheckboxInput {
|
pub struct CheckboxInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub checked: bool,
|
pub checked: bool,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
@ -39,9 +41,10 @@ impl Default for CheckboxInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct ColorInput {
|
pub struct ColorInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: Option<Color>,
|
pub value: Option<Color>,
|
||||||
|
|
||||||
// TODO: Add allow_none
|
// TODO: Add allow_none
|
||||||
|
|
@ -62,9 +65,10 @@ pub struct ColorInput {
|
||||||
pub on_update: WidgetCallback<ColorInput>,
|
pub on_update: WidgetCallback<ColorInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct DropdownInput {
|
pub struct DropdownInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub entries: DropdownInputEntries,
|
pub entries: DropdownInputEntries,
|
||||||
|
|
||||||
// This uses `u32` instead of `usize` since it will be serialized as a normal JS number (replace this with `usize` after switching to a Rust-based GUI)
|
// This uses `u32` instead of `usize` since it will be serialized as a normal JS number (replace this with `usize` after switching to a Rust-based GUI)
|
||||||
|
|
@ -90,11 +94,13 @@ pub struct DropdownInput {
|
||||||
|
|
||||||
pub type DropdownInputEntries = Vec<Vec<DropdownEntryData>>;
|
pub type DropdownInputEntries = Vec<Vec<DropdownEntryData>>;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
|
#[widget_builder(not_widget_holder)]
|
||||||
pub struct DropdownEntryData {
|
pub struct DropdownEntryData {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub label: String,
|
pub label: String,
|
||||||
|
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
|
@ -114,13 +120,15 @@ pub struct DropdownEntryData {
|
||||||
pub on_update: WidgetCallback<()>,
|
pub on_update: WidgetCallback<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct FontInput {
|
pub struct FontInput {
|
||||||
#[serde(rename = "fontFamily")]
|
#[serde(rename = "fontFamily")]
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub font_family: String,
|
pub font_family: String,
|
||||||
|
|
||||||
#[serde(rename = "fontStyle")]
|
#[serde(rename = "fontStyle")]
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub font_style: String,
|
pub font_style: String,
|
||||||
|
|
||||||
#[serde(rename = "isStyle")]
|
#[serde(rename = "isStyle")]
|
||||||
|
|
@ -142,7 +150,7 @@ pub struct FontInput {
|
||||||
/// This widget allows for the flexible use of the layout system.
|
/// This widget allows for the flexible use of the layout system.
|
||||||
/// In a custom layout, one can define a widget that is just used to trigger code on the backend.
|
/// In a custom layout, one can define a widget that is just used to trigger code on the backend.
|
||||||
/// This is used in MenuLayout to pipe the triggering of messages from the frontend to backend.
|
/// This is used in MenuLayout to pipe the triggering of messages from the frontend to backend.
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct InvisibleStandinInput {
|
pub struct InvisibleStandinInput {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
|
@ -150,15 +158,18 @@ pub struct InvisibleStandinInput {
|
||||||
pub on_update: WidgetCallback<()>,
|
pub on_update: WidgetCallback<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct LayerReferenceInput {
|
pub struct LayerReferenceInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: Option<Vec<LayerId>>,
|
pub value: Option<Vec<LayerId>>,
|
||||||
|
|
||||||
#[serde(rename = "layerName")]
|
#[serde(rename = "layerName")]
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub layer_name: Option<String>,
|
pub layer_name: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "layerType")]
|
#[serde(rename = "layerType")]
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub layer_type: Option<LayerDataTypeDiscriminant>,
|
pub layer_type: Option<LayerDataTypeDiscriminant>,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
@ -178,7 +189,7 @@ pub struct LayerReferenceInput {
|
||||||
pub on_update: WidgetCallback<LayerReferenceInput>,
|
pub on_update: WidgetCallback<LayerReferenceInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct NumberInput {
|
pub struct NumberInput {
|
||||||
// Label
|
// Label
|
||||||
|
|
@ -193,10 +204,13 @@ pub struct NumberInput {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: Option<f64>,
|
pub value: Option<f64>,
|
||||||
|
|
||||||
|
#[widget_builder(skip)]
|
||||||
pub min: Option<f64>,
|
pub min: Option<f64>,
|
||||||
|
|
||||||
|
#[widget_builder(skip)]
|
||||||
pub max: Option<f64>,
|
pub max: Option<f64>,
|
||||||
|
|
||||||
#[serde(rename = "isInteger")]
|
#[serde(rename = "isInteger")]
|
||||||
|
|
@ -247,9 +261,6 @@ pub struct NumberInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NumberInput {
|
impl NumberInput {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
pub fn int(mut self) -> Self {
|
pub fn int(mut self) -> Self {
|
||||||
self.is_integer = true;
|
self.is_integer = true;
|
||||||
self
|
self
|
||||||
|
|
@ -265,20 +276,8 @@ impl NumberInput {
|
||||||
self.mode = NumberInputMode::Range;
|
self.mode = NumberInputMode::Range;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn unit(mut self, val: impl Into<String>) -> Self {
|
|
||||||
self.unit = val.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn dp(mut self, decimal_places: u32) -> Self {
|
|
||||||
self.display_decimal_places = decimal_places;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn percentage(self) -> Self {
|
pub fn percentage(self) -> Self {
|
||||||
self.min(0.).max(100.).unit("%").dp(2)
|
self.min(0.).max(100.).unit("%").display_decimal_places(2)
|
||||||
}
|
|
||||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
|
||||||
self.disabled = disabled;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,13 +296,15 @@ pub enum NumberInputMode {
|
||||||
Range,
|
Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct OptionalInput {
|
pub struct OptionalInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub checked: bool,
|
pub checked: bool,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
|
||||||
pub tooltip: String,
|
pub tooltip: String,
|
||||||
|
|
@ -317,9 +318,10 @@ pub struct OptionalInput {
|
||||||
pub on_update: WidgetCallback<OptionalInput>,
|
pub on_update: WidgetCallback<OptionalInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct RadioInput {
|
pub struct RadioInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub entries: Vec<RadioEntryData>,
|
pub entries: Vec<RadioEntryData>,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
@ -329,11 +331,13 @@ pub struct RadioInput {
|
||||||
pub selected_index: u32,
|
pub selected_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
|
#[widget_builder(not_widget_holder)]
|
||||||
pub struct RadioEntryData {
|
pub struct RadioEntryData {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub label: String,
|
pub label: String,
|
||||||
|
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
|
@ -349,17 +353,20 @@ pub struct RadioEntryData {
|
||||||
pub on_update: WidgetCallback<()>,
|
pub on_update: WidgetCallback<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct SwatchPairInput {
|
pub struct SwatchPairInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub primary: Color,
|
pub primary: Color,
|
||||||
|
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub secondary: Color,
|
pub secondary: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct TextAreaInput {
|
pub struct TextAreaInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
|
@ -374,9 +381,10 @@ pub struct TextAreaInput {
|
||||||
pub on_update: WidgetCallback<TextAreaInput>,
|
pub on_update: WidgetCallback<TextAreaInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq, Default)]
|
#[derivative(Debug, PartialEq, Default)]
|
||||||
pub struct TextInput {
|
pub struct TextInput {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder)]
|
||||||
pub struct IconLabel {
|
pub struct IconLabel {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
@ -10,29 +12,33 @@ pub struct IconLabel {
|
||||||
pub tooltip: String,
|
pub tooltip: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, WidgetBuilder)]
|
||||||
pub struct Separator {
|
pub struct Separator {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub direction: SeparatorDirection,
|
pub direction: SeparatorDirection,
|
||||||
|
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub separator_type: SeparatorType,
|
pub separator_type: SeparatorType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum SeparatorDirection {
|
pub enum SeparatorDirection {
|
||||||
|
#[default]
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum SeparatorType {
|
pub enum SeparatorType {
|
||||||
Related,
|
Related,
|
||||||
|
#[default]
|
||||||
Unrelated,
|
Unrelated,
|
||||||
Section,
|
Section,
|
||||||
List,
|
List,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Eq, Default)]
|
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Eq, Default, WidgetBuilder)]
|
||||||
pub struct TextLabel {
|
pub struct TextLabel {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
||||||
|
|
@ -51,6 +57,7 @@ pub struct TextLabel {
|
||||||
pub tooltip: String,
|
pub tooltip: String,
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::messages::layout::utility_types::layout_widget::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::layout::utility_types::widgets::{button_widgets::*, input_widgets::*};
|
|
||||||
use crate::messages::portfolio::utility_types::ImaginateServerStatus;
|
use crate::messages::portfolio::utility_types::ImaginateServerStatus;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
|
|
@ -18,32 +17,31 @@ pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::Row { widgets: vec![widget] }]
|
vec![LayoutGroup::Row { widgets: vec![widget] }]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static + Send + Sync>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
|
fn update_value<T>(value: impl Fn(&T) -> TaggedValue + 'static + Send + Sync, node_id: NodeId, input_index: usize) -> impl Fn(&T) -> Message + 'static + Send + Sync {
|
||||||
WidgetCallback::new(move |input_value: &T| {
|
move |input_value: &T| {
|
||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
input_index,
|
input_index,
|
||||||
value: value(input_value),
|
value: value(input_value),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType, exposed: bool) -> WidgetHolder {
|
fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType, exposed: bool) -> WidgetHolder {
|
||||||
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
|
ParameterExposeButton::new()
|
||||||
exposed,
|
.exposed(exposed)
|
||||||
data_type,
|
.data_type(data_type)
|
||||||
tooltip: "Expose this parameter input in node graph".into(),
|
.tooltip("Expose this parameter input in node graph")
|
||||||
on_update: WidgetCallback::new(move |_parameter| {
|
.on_update(move |_parameter| {
|
||||||
NodeGraphMessage::ExposeInput {
|
NodeGraphMessage::ExposeInput {
|
||||||
node_id,
|
node_id,
|
||||||
input_index: index,
|
input_index: index,
|
||||||
new_exposed: !exposed,
|
new_exposed: !exposed,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..Default::default()
|
.widget_holder()
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, data_type: FrontendGraphDataType, blank_assist: bool) -> Vec<WidgetHolder> {
|
fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, data_type: FrontendGraphDataType, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||||
|
|
@ -74,11 +72,9 @@ fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
TextInput::new(x.clone())
|
||||||
value: x.clone(),
|
.on_update(update_value(|x: &TextInput| TaggedValue::String(x.value.clone()), node_id, index))
|
||||||
on_update: update_value(|x: &TextInput| TaggedValue::String(x.value.clone()), node_id, index),
|
.widget_holder(),
|
||||||
..TextInput::default()
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
widgets
|
widgets
|
||||||
|
|
@ -94,11 +90,9 @@ fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::TextAreaInput(TextAreaInput {
|
TextAreaInput::new(x.clone())
|
||||||
value: x.clone(),
|
.on_update(update_value(|x: &TextAreaInput| TaggedValue::String(x.value.clone()), node_id, index))
|
||||||
on_update: update_value(|x: &TextAreaInput| TaggedValue::String(x.value.clone()), node_id, index),
|
.widget_holder(),
|
||||||
..TextAreaInput::default()
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
widgets
|
widgets
|
||||||
|
|
@ -114,11 +108,9 @@ fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
CheckboxInput::new(*x)
|
||||||
checked: *x,
|
.on_update(update_value(|x: &CheckboxInput| TaggedValue::Bool(x.checked), node_id, index))
|
||||||
on_update: update_value(|x: &CheckboxInput| TaggedValue::Bool(x.checked), node_id, index),
|
.widget_holder(),
|
||||||
..CheckboxInput::default()
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
widgets
|
widgets
|
||||||
|
|
@ -134,25 +126,22 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
number_props
|
||||||
value: Some(x),
|
.value(Some(x))
|
||||||
on_update: update_value(|x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index),
|
.on_update(update_value(|x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
||||||
..number_props
|
.widget_holder(),
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
} else if let NodeInput::Value {
|
||||||
if let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::U32(x),
|
tagged_value: TaggedValue::U32(x),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
} = document_node.inputs[index]
|
} = document_node.inputs[index]
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
number_props
|
||||||
value: Some(x as f64),
|
.value(Some(x as f64))
|
||||||
on_update: update_value(|x: &NumberInput| TaggedValue::U32(x.value.unwrap() as u32), node_id, index),
|
.on_update(update_value(|x: &NumberInput| TaggedValue::U32(x.value.unwrap() as u32), node_id, index))
|
||||||
..NumberInput::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
widgets
|
widgets
|
||||||
|
|
@ -161,19 +150,17 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
/// Properties for the input node, with information describing how frames work and a refresh button
|
/// Properties for the input node, with information describing how frames work and a refresh button
|
||||||
pub fn input_properties(_document_node: &DocumentNode, _node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn input_properties(_document_node: &DocumentNode, _node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let information = WidgetHolder::text_widget("The graph's input is the artwork under the frame layer");
|
let information = WidgetHolder::text_widget("The graph's input is the artwork under the frame layer");
|
||||||
let refresh_button = WidgetHolder::new(Widget::TextButton(TextButton {
|
let refresh_button = TextButton::new("Refresh Input")
|
||||||
label: "Refresh Input".to_string(),
|
.tooltip("Refresh the artwork under the frame")
|
||||||
tooltip: "Refresh the artwork under the frame".to_string(),
|
.on_update(|_| DocumentMessage::NodeGraphFrameGenerate.into())
|
||||||
on_update: WidgetCallback::new(|_| DocumentMessage::NodeGraphFrameGenerate.into()),
|
.widget_holder();
|
||||||
..Default::default()
|
|
||||||
}));
|
|
||||||
vec![LayoutGroup::Row { widgets: vec![information] }, LayoutGroup::Row { widgets: vec![refresh_button] }]
|
vec![LayoutGroup::Row { widgets: vec![information] }, LayoutGroup::Row { widgets: vec![refresh_button] }]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let hue_shift = number_widget(document_node, node_id, 1, "Hue Shift", NumberInput::new().min(-180.).max(180.).unit("°"), true);
|
let hue_shift = number_widget(document_node, node_id, 1, "Hue Shift", NumberInput::default().min(-180.).max(180.).unit("°"), true);
|
||||||
let saturation_shift = number_widget(document_node, node_id, 2, "Saturation Shift", NumberInput::new().min(-100.).max(100.).unit("%"), true);
|
let saturation_shift = number_widget(document_node, node_id, 2, "Saturation Shift", NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
let lightness_shift = number_widget(document_node, node_id, 3, "Lightness Shift", NumberInput::new().min(-100.).max(100.).unit("%"), true);
|
let lightness_shift = number_widget(document_node, node_id, 3, "Lightness Shift", NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
LayoutGroup::Row { widgets: hue_shift },
|
LayoutGroup::Row { widgets: hue_shift },
|
||||||
|
|
@ -183,25 +170,26 @@ pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _con
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let brightness = number_widget(document_node, node_id, 1, "Brightness", NumberInput::new().min(-255.).max(255.), true);
|
let brightness = number_widget(document_node, node_id, 1, "Brightness", NumberInput::default().min(-255.).max(255.), true);
|
||||||
let contrast = number_widget(document_node, node_id, 2, "Contrast", NumberInput::new().min(-255.).max(255.), true);
|
let contrast = number_widget(document_node, node_id, 2, "Contrast", NumberInput::default().min(-255.).max(255.), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }]
|
vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let radius = number_widget(document_node, node_id, 1, "Radius", NumberInput::new().min(0.).max(20.).int(), true);
|
let radius = number_widget(document_node, node_id, 1, "Radius", NumberInput::default().min(0.).max(20.).int(), true);
|
||||||
let sigma = number_widget(document_node, node_id, 2, "Sigma", NumberInput::new().min(0.).max(10000.), true);
|
let sigma = number_widget(document_node, node_id, 2, "Sigma", NumberInput::default().min(0.).max(10000.), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: sigma }]
|
vec![LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: sigma }]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let gamma = number_widget(document_node, node_id, 1, "Gamma", NumberInput::new().min(0.01), true);
|
let gamma = number_widget(document_node, node_id, 1, "Gamma", NumberInput::default().min(0.01), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: gamma }]
|
vec![LayoutGroup::Row { widgets: gamma }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gpu")]
|
||||||
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let map = text_widget(document_node, node_id, 1, "Map", true);
|
let map = text_widget(document_node, node_id, 1, "Map", true);
|
||||||
|
|
||||||
|
|
@ -209,32 +197,33 @@ pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _contex
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let gamma = number_widget(document_node, node_id, 1, "Factor", NumberInput::new().min(0.).max(1.), true);
|
let gamma = number_widget(document_node, node_id, 1, "Factor", NumberInput::default().min(0.).max(1.), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: gamma }]
|
vec![LayoutGroup::Row { widgets: gamma }]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn posterize_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn posterize_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let value = number_widget(document_node, node_id, 1, "Levels", NumberInput::new().min(2.).max(255.).int(), true);
|
let value = number_widget(document_node, node_id, 1, "Levels", NumberInput::default().min(2.).max(255.).int(), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: value }]
|
vec![LayoutGroup::Row { widgets: value }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
pub fn quantize_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn quantize_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let value = number_widget(document_node, node_id, 1, "Levels", NumberInput::new().min(1.).max(1000.).int(), true);
|
let value = number_widget(document_node, node_id, 1, "Levels", NumberInput::default().min(1.).max(1000.).int(), true);
|
||||||
let index = number_widget(document_node, node_id, 1, "Fit Fn Index", NumberInput::new().min(0.).max(2.).int(), true);
|
let index = number_widget(document_node, node_id, 1, "Fit Fn Index", NumberInput::default().min(0.).max(2.).int(), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: value }, LayoutGroup::Row { widgets: index }]
|
vec![LayoutGroup::Row { widgets: value }, LayoutGroup::Row { widgets: index }]
|
||||||
}
|
}
|
||||||
pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let value = number_widget(document_node, node_id, 1, "Value", NumberInput::new().min(-3.).max(3.), true);
|
let value = number_widget(document_node, node_id, 1, "Value", NumberInput::default().min(-3.).max(3.), true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: value }]
|
vec![LayoutGroup::Row { widgets: value }]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn add_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let operand = |name: &str, index| {
|
let operand = |name: &str, index| {
|
||||||
let widgets = number_widget(document_node, node_id, index, name, NumberInput::new(), true);
|
let widgets = number_widget(document_node, node_id, index, name, NumberInput::default(), true);
|
||||||
|
|
||||||
LayoutGroup::Row { widgets }
|
LayoutGroup::Row { widgets }
|
||||||
};
|
};
|
||||||
|
|
@ -254,21 +243,17 @@ pub fn _transform_properties(document_node: &DocumentNode, node_id: NodeId, _con
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.x))
|
||||||
value: Some(vec2.x),
|
.label("X")
|
||||||
label: "X".into(),
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
|
||||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(number_input.value.unwrap(), vec2.y)), node_id, index),
|
.widget_holder(),
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.y))
|
||||||
value: Some(vec2.y),
|
.label("Y")
|
||||||
label: "Y".into(),
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
|
||||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, number_input.value.unwrap())), node_id, index),
|
.widget_holder(),
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,15 +272,13 @@ pub fn _transform_properties(document_node: &DocumentNode, node_id: NodeId, _con
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(val.to_degrees()))
|
||||||
value: Some(val.to_degrees()),
|
.unit("°")
|
||||||
unit: "°".into(),
|
.mode(NumberInputMode::Range)
|
||||||
mode: NumberInputMode::Range,
|
.range_min(Some(-180.))
|
||||||
range_min: Some(-180.),
|
.range_max(Some(180.))
|
||||||
range_max: Some(180.),
|
.on_update(update_value(|number_input: &NumberInput| TaggedValue::F64(number_input.value.unwrap().to_radians()), node_id, index))
|
||||||
on_update: update_value(|number_input: &NumberInput| TaggedValue::F64(number_input.value.unwrap().to_radians()), node_id, index),
|
.widget_holder(),
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,21 +297,15 @@ pub fn _transform_properties(document_node: &DocumentNode, node_id: NodeId, _con
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.x))
|
||||||
value: Some(vec2.x),
|
.label("X")
|
||||||
label: "X".into(),
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
|
||||||
unit: "".into(),
|
.widget_holder(),
|
||||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(number_input.value.unwrap(), vec2.y)), node_id, index),
|
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.y))
|
||||||
value: Some(vec2.y),
|
.label("Y")
|
||||||
label: "Y".into(),
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
|
||||||
unit: "".into(),
|
.widget_holder(),
|
||||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, number_input.value.unwrap())), node_id, index),
|
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,23 +354,17 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let widgets = vec![
|
let widgets = vec![
|
||||||
WidgetHolder::text_widget("Server"),
|
WidgetHolder::text_widget("Server"),
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("Settings", 24)
|
||||||
size: 24,
|
.tooltip("Preferences: Imaginate")
|
||||||
icon: "Settings".into(),
|
.on_update(|_| DialogMessage::RequestPreferencesDialog.into())
|
||||||
tooltip: "Preferences: Imaginate".into(),
|
.widget_holder(),
|
||||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestPreferencesDialog.into()),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::bold_text(status),
|
WidgetHolder::bold_text(status),
|
||||||
WidgetHolder::related_separator(),
|
WidgetHolder::related_separator(),
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("Reload", 24)
|
||||||
size: 24,
|
.tooltip("Refresh connection status")
|
||||||
icon: "Reload".into(),
|
.on_update(|_| PortfolioMessage::ImaginateCheckServerStatus.into())
|
||||||
tooltip: "Refresh connection status".into(),
|
.widget_holder(),
|
||||||
on_update: WidgetCallback::new(|_| PortfolioMessage::ImaginateCheckServerStatus.into()),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
];
|
];
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
||||||
};
|
};
|
||||||
|
|
@ -458,70 +429,58 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
match imaginate_status {
|
match imaginate_status {
|
||||||
ImaginateStatus::Beginning | ImaginateStatus::Uploading(_) => {
|
ImaginateStatus::Beginning | ImaginateStatus::Uploading(_) => {
|
||||||
widgets.extend_from_slice(&assist_separators);
|
widgets.extend_from_slice(&assist_separators);
|
||||||
widgets.push(WidgetHolder::new(Widget::TextButton(TextButton {
|
widgets.push(TextButton::new("Beginning...").tooltip("Sending image generation request to the server").disabled(true).widget_holder());
|
||||||
label: "Beginning...".into(),
|
|
||||||
tooltip: "Sending image generation request to the server".into(),
|
|
||||||
disabled: true,
|
|
||||||
..Default::default()
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
ImaginateStatus::Generating => {
|
ImaginateStatus::Generating => {
|
||||||
widgets.extend_from_slice(&assist_separators);
|
widgets.extend_from_slice(&assist_separators);
|
||||||
widgets.push(WidgetHolder::new(Widget::TextButton(TextButton {
|
widgets.push(
|
||||||
label: "Terminate".into(),
|
TextButton::new("Terminate")
|
||||||
tooltip: "Cancel the in-progress image generation and keep the latest progress".into(),
|
.tooltip("Cancel the in-progress image generation and keep the latest progress")
|
||||||
on_update: WidgetCallback::new(move |_| {
|
.on_update(move |_| {
|
||||||
DocumentMessage::NodeGraphFrameImaginateTerminate {
|
DocumentMessage::NodeGraphFrameImaginateTerminate {
|
||||||
layer_path: layer_path.clone(),
|
layer_path: layer_path.clone(),
|
||||||
node_path: imaginate_node.clone(),
|
node_path: imaginate_node.clone(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..Default::default()
|
.widget_holder(),
|
||||||
})));
|
);
|
||||||
}
|
}
|
||||||
ImaginateStatus::Terminating => {
|
ImaginateStatus::Terminating => {
|
||||||
widgets.extend_from_slice(&assist_separators);
|
widgets.extend_from_slice(&assist_separators);
|
||||||
widgets.push(WidgetHolder::new(Widget::TextButton(TextButton {
|
widgets.push(
|
||||||
label: "Terminating...".into(),
|
TextButton::new("Terminating...")
|
||||||
tooltip: "Waiting on the final image generated after termination".into(),
|
.tooltip("Waiting on the final image generated after termination")
|
||||||
disabled: true,
|
.disabled(true)
|
||||||
..Default::default()
|
.widget_holder(),
|
||||||
})));
|
);
|
||||||
}
|
}
|
||||||
ImaginateStatus::Idle | ImaginateStatus::Terminated => widgets.extend_from_slice(&[
|
ImaginateStatus::Idle | ImaginateStatus::Terminated => widgets.extend_from_slice(&[
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("Random", 24)
|
||||||
size: 24,
|
.tooltip("Generate with a new random seed")
|
||||||
icon: "Random".into(),
|
.on_update(move |_| {
|
||||||
tooltip: "Generate with a new random seed".into(),
|
|
||||||
on_update: WidgetCallback::new(move |_| {
|
|
||||||
DocumentMessage::NodeGraphFrameImaginateRandom {
|
DocumentMessage::NodeGraphFrameImaginateRandom {
|
||||||
imaginate_node: imaginate_node.clone(),
|
imaginate_node: imaginate_node.clone(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..Default::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
TextButton::new("Generate")
|
||||||
label: "Generate".into(),
|
.tooltip("Fill layer frame by generating a new image")
|
||||||
tooltip: "Fill layer frame by generating a new image".into(),
|
.on_update(move |_| {
|
||||||
on_update: WidgetCallback::new(move |_| {
|
|
||||||
DocumentMessage::NodeGraphFrameImaginate {
|
DocumentMessage::NodeGraphFrameImaginate {
|
||||||
imaginate_node: imaginate_node_1.clone(),
|
imaginate_node: imaginate_node_1.clone(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..Default::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
WidgetHolder::related_separator(),
|
WidgetHolder::related_separator(),
|
||||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
TextButton::new("Clear")
|
||||||
label: "Clear".into(),
|
.tooltip("Remove generated image from the layer frame")
|
||||||
tooltip: "Remove generated image from the layer frame".into(),
|
.disabled(cached_data.is_none())
|
||||||
disabled: cached_data.is_none(),
|
.on_update(update_value(|_| TaggedValue::RcImage(None), node_id, cached_index))
|
||||||
on_update: update_value(|_| TaggedValue::RcImage(None), node_id, cached_index),
|
.widget_holder(),
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Buttons that control the image generation process")
|
LayoutGroup::Row { widgets }.with_tooltip("Buttons that control the image generation process")
|
||||||
|
|
@ -538,21 +497,16 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("Regenerate", 24)
|
||||||
size: 24,
|
.tooltip("Set a new random seed")
|
||||||
icon: "Regenerate".into(),
|
.on_update(update_value(move |_| TaggedValue::F64((generate_uuid() >> 1) as f64), node_id, seed_index))
|
||||||
tooltip: "Set a new random seed".into(),
|
.widget_holder(),
|
||||||
on_update: update_value(move |_| TaggedValue::F64((generate_uuid() >> 1) as f64), node_id, seed_index),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(seed))
|
||||||
value: Some(seed),
|
.min(0.)
|
||||||
min: Some(0.),
|
.int()
|
||||||
is_integer: true,
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::F64(input.value.unwrap()), node_id, seed_index))
|
||||||
on_update: update_value(move |input: &NumberInput| TaggedValue::F64(input.value.unwrap()), node_id, seed_index),
|
.widget_holder(),
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
// Note: Limited by f64. You cannot even have all the possible u64 values :)
|
// Note: Limited by f64. You cannot even have all the possible u64 values :)
|
||||||
|
|
@ -588,25 +542,21 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let layer_path = context.layer_path.to_vec();
|
let layer_path = context.layer_path.to_vec();
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("Rescale", 24)
|
||||||
size: 24,
|
.tooltip("Set the Node Graph Frame layer dimensions to this resolution")
|
||||||
icon: "Rescale".into(),
|
.on_update(move |_| {
|
||||||
tooltip: "Set the Node Graph Frame layer dimensions to this resolution".into(),
|
|
||||||
on_update: WidgetCallback::new(move |_| {
|
|
||||||
Operation::SetLayerScaleAroundPivot {
|
Operation::SetLayerScaleAroundPivot {
|
||||||
path: layer_path.clone(),
|
path: layer_path.clone(),
|
||||||
new_scale: vec2.into(),
|
new_scale: vec2.into(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..Default::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
CheckboxInput::new(!dimensions_is_auto)
|
||||||
checked: !dimensions_is_auto,
|
.icon("Edit")
|
||||||
icon: "Edit".into(),
|
.tooltip("Set a custom resolution instead of using the frame's rounded dimensions")
|
||||||
tooltip: "Set a custom resolution instead of using the frame's rounded dimensions".into(),
|
.on_update(update_value(
|
||||||
on_update: update_value(
|
|
||||||
move |checkbox_input: &CheckboxInput| {
|
move |checkbox_input: &CheckboxInput| {
|
||||||
if checkbox_input.checked {
|
if checkbox_input.checked {
|
||||||
TaggedValue::OptionalDVec2(Some(vec2))
|
TaggedValue::OptionalDVec2(Some(vec2))
|
||||||
|
|
@ -616,35 +566,30 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
},
|
},
|
||||||
node_id,
|
node_id,
|
||||||
resolution_index,
|
resolution_index,
|
||||||
),
|
))
|
||||||
..CheckboxInput::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.x))
|
||||||
value: Some(vec2.x),
|
.label("W")
|
||||||
label: "W".into(),
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.disabled(dimensions_is_auto)
|
||||||
disabled: dimensions_is_auto,
|
.on_update(update_value(
|
||||||
on_update: update_value(
|
|
||||||
move |number_input: &NumberInput| TaggedValue::OptionalDVec2(round(DVec2::new(number_input.value.unwrap(), vec2.y))),
|
move |number_input: &NumberInput| TaggedValue::OptionalDVec2(round(DVec2::new(number_input.value.unwrap(), vec2.y))),
|
||||||
node_id,
|
node_id,
|
||||||
resolution_index,
|
resolution_index,
|
||||||
),
|
))
|
||||||
..NumberInput::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
WidgetHolder::related_separator(),
|
WidgetHolder::related_separator(),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
NumberInput::new(Some(vec2.y))
|
||||||
value: Some(vec2.y),
|
.label("H")
|
||||||
label: "H".into(),
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.disabled(dimensions_is_auto)
|
||||||
disabled: dimensions_is_auto,
|
.on_update(update_value(
|
||||||
on_update: update_value(
|
|
||||||
move |number_input: &NumberInput| TaggedValue::OptionalDVec2(round(DVec2::new(vec2.x, number_input.value.unwrap()))),
|
move |number_input: &NumberInput| TaggedValue::OptionalDVec2(round(DVec2::new(vec2.x, number_input.value.unwrap()))),
|
||||||
node_id,
|
node_id,
|
||||||
resolution_index,
|
resolution_index,
|
||||||
),
|
))
|
||||||
..NumberInput::default()
|
.widget_holder(),
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
|
|
@ -656,7 +601,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
};
|
};
|
||||||
|
|
||||||
let sampling_steps = {
|
let sampling_steps = {
|
||||||
let widgets = number_widget(document_node, node_id, samples_index, "Sampling Steps", NumberInput::new().min(0.).max(150.).int(), true);
|
let widgets = number_widget(document_node, node_id, samples_index, "Sampling Steps", NumberInput::default().min(0.).max(150.).int(), true);
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Number of iterations to improve the image generation quality, with diminishing returns around 40 when using the Euler A sampling method")
|
LayoutGroup::Row { widgets }.with_tooltip("Number of iterations to improve the image generation quality, with diminishing returns around 40 when using the Euler A sampling method")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -671,28 +616,20 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let sampling_methods = ImaginateSamplingMethod::list();
|
let sampling_methods = ImaginateSamplingMethod::list();
|
||||||
let mut entries = Vec::with_capacity(sampling_methods.len());
|
let mut entries = Vec::with_capacity(sampling_methods.len());
|
||||||
for method in sampling_methods {
|
for method in sampling_methods {
|
||||||
entries.push(DropdownEntryData {
|
entries.push(DropdownEntryData::new(method.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, sampling_method_index)));
|
||||||
label: method.to_string(),
|
|
||||||
on_update: update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, sampling_method_index),
|
|
||||||
..DropdownEntryData::default()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let entries = vec![entries];
|
let entries = vec![entries];
|
||||||
|
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).widget_holder(),
|
||||||
entries,
|
|
||||||
selected_index: Some(sampling_method as u32),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step")
|
LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step")
|
||||||
};
|
};
|
||||||
|
|
||||||
let text_guidance = {
|
let text_guidance = {
|
||||||
let widgets = number_widget(document_node, node_id, text_guidance_index, "Prompt Guidance", NumberInput::new().min(0.).max(30.), true);
|
let widgets = number_widget(document_node, node_id, text_guidance_index, "Prompt Guidance", NumberInput::default().min(0.).max(30.), true);
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
"Amplification of the text prompt's influence over the outcome. At 0, the prompt is entirely ignored.\n\
|
"Amplification of the text prompt's influence over the outcome. At 0, the prompt is entirely ignored.\n\
|
||||||
\n\
|
\n\
|
||||||
|
|
@ -722,7 +659,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Generate an image based upon the bitmap data plugged into this node")
|
LayoutGroup::Row { widgets }.with_tooltip("Generate an image based upon the bitmap data plugged into this node")
|
||||||
};
|
};
|
||||||
let image_creativity = {
|
let image_creativity = {
|
||||||
let props = NumberInput::new().percentage().disabled(!use_base_image);
|
let props = NumberInput::default().percentage().disabled(!use_base_image);
|
||||||
let widgets = number_widget(document_node, node_id, img_creativity_index, "Image Creativity", props, true);
|
let widgets = number_widget(document_node, node_id, img_creativity_index, "Image Creativity", props, true);
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
"Strength of the artistic liberties allowing changes from the input image. The image is unchanged at 0% and completely different at 100%.\n\
|
"Strength of the artistic liberties allowing changes from the input image. The image is unchanged at 0% and completely different at 100%.\n\
|
||||||
|
|
@ -752,14 +689,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
|
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::LayerReferenceInput(LayerReferenceInput {
|
LayerReferenceInput::new(layer_path.clone(), layer_reference_input_layer_name.cloned(), layer_reference_input_layer_type.cloned())
|
||||||
value: layer_path.clone(),
|
.disabled(!use_base_image)
|
||||||
layer_name: layer_reference_input_layer_name.cloned(),
|
.on_update(update_value(|input: &LayerReferenceInput| TaggedValue::LayerPath(input.value.clone()), node_id, mask_index))
|
||||||
layer_type: layer_reference_input_layer_type.cloned(),
|
.widget_holder(),
|
||||||
disabled: !use_base_image,
|
|
||||||
on_update: update_value(|input: &LayerReferenceInput| TaggedValue::LayerPath(input.value.clone()), node_id, mask_index),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
|
|
@ -796,18 +729,14 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::RadioInput(RadioInput {
|
RadioInput::new(
|
||||||
entries: [(true, "Inpaint"), (false, "Outpaint")]
|
[(true, "Inpaint"), (false, "Outpaint")]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(paint, name)| RadioEntryData {
|
.map(|(paint, name)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::Bool(paint), node_id, inpaint_index)))
|
||||||
label: name.to_string(),
|
|
||||||
on_update: update_value(move |_| TaggedValue::Bool(paint), node_id, inpaint_index),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
selected_index: 1 - in_paint as u32,
|
)
|
||||||
..Default::default()
|
.selected_index(1 - in_paint as u32)
|
||||||
})),
|
.widget_holder(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
|
|
@ -820,7 +749,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
};
|
};
|
||||||
|
|
||||||
let blur_radius = {
|
let blur_radius = {
|
||||||
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", NumberInput::new().unit(" px").min(0.).max(25.).int(), true);
|
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", NumberInput::default().unit(" px").min(0.).max(25.).int(), true);
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
|
LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -835,21 +764,13 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let mask_fill_content_modes = ImaginateMaskStartingFill::list();
|
let mask_fill_content_modes = ImaginateMaskStartingFill::list();
|
||||||
let mut entries = Vec::with_capacity(mask_fill_content_modes.len());
|
let mut entries = Vec::with_capacity(mask_fill_content_modes.len());
|
||||||
for mode in mask_fill_content_modes {
|
for mode in mask_fill_content_modes {
|
||||||
entries.push(DropdownEntryData {
|
entries.push(DropdownEntryData::new(mode.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateMaskStartingFill(mode), node_id, mask_fill_index)));
|
||||||
label: mode.to_string(),
|
|
||||||
on_update: update_value(move |_| TaggedValue::ImaginateMaskStartingFill(mode), node_id, mask_fill_index),
|
|
||||||
..DropdownEntryData::default()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let entries = vec![entries];
|
let entries = vec![entries];
|
||||||
|
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
DropdownInput::new(entries).selected_index(Some(starting_fill as u32)).widget_holder(),
|
||||||
entries,
|
|
||||||
selected_index: Some(starting_fill as u32),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip(
|
LayoutGroup::Row { widgets }.with_tooltip(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ mod helper_structs;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod hint;
|
mod hint;
|
||||||
mod transitive_child;
|
mod transitive_child;
|
||||||
|
mod widget_builder;
|
||||||
|
|
||||||
use crate::as_message::derive_as_message_impl;
|
use crate::as_message::derive_as_message_impl;
|
||||||
use crate::combined_message_attrs::combined_message_attrs_impl;
|
use crate::combined_message_attrs::combined_message_attrs_impl;
|
||||||
|
|
@ -12,6 +13,7 @@ use crate::discriminant::derive_discriminant_impl;
|
||||||
use crate::helper_structs::AttrInnerSingleString;
|
use crate::helper_structs::AttrInnerSingleString;
|
||||||
use crate::hint::derive_hint_impl;
|
use crate::hint::derive_hint_impl;
|
||||||
use crate::transitive_child::derive_transitive_child_impl;
|
use crate::transitive_child::derive_transitive_child_impl;
|
||||||
|
use crate::widget_builder::derive_widget_builder_impl;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
|
@ -273,6 +275,11 @@ pub fn edge(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(WidgetBuilder, attributes(widget_builder))]
|
||||||
|
pub fn derive_widget_builder(input_item: TokenStream) -> TokenStream {
|
||||||
|
TokenStream::from(derive_widget_builder_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
use proc_macro2::{Ident, Literal, TokenStream as TokenStream2};
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
use syn::{Attribute, Data, DeriveInput, Field, PathArguments, Type};
|
||||||
|
|
||||||
|
/// Check if a specified `#[widget_builder target]` attribute can be found in the list
|
||||||
|
fn has_attribute(attrs: &[Attribute], target: &str) -> bool {
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path.to_token_stream().to_string() == "widget_builder")
|
||||||
|
.any(|attr| attr.tokens.to_token_stream().to_string() == target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make setting strings easier by allowing all types that `impl Into<String>`
|
||||||
|
///
|
||||||
|
/// Returns the new input type and a conversion to the origional.
|
||||||
|
fn easier_string_assignment(field_ty: &Type, field_ident: &Ident) -> (TokenStream2, TokenStream2) {
|
||||||
|
if let Type::Path(type_path) = field_ty {
|
||||||
|
if let Some(last_segement) = type_path.path.segments.last() {
|
||||||
|
// Check if this type is a `String`
|
||||||
|
// Based on https://stackoverflow.com/questions/66906261/rust-proc-macro-derive-how-do-i-check-if-a-field-is-of-a-primitive-type-like-b
|
||||||
|
if last_segement.ident == Ident::new("String", last_segement.ident.span()) {
|
||||||
|
return (
|
||||||
|
quote::quote_spanned!(type_path.span() => impl Into<String>),
|
||||||
|
quote::quote_spanned!(field_ident.span() => #field_ident.into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(quote::quote_spanned!(field_ty.span() => #field_ty), quote::quote_spanned!(field_ident.span() => #field_ident))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the identifier of the field (which should always be present)
|
||||||
|
fn extract_ident(field: &Field) -> syn::Result<&Ident> {
|
||||||
|
field
|
||||||
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| syn::Error::new_spanned(field, "Constructing a builder not supported for unnamed fields"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the type passed into the builder and the right hand side of the assignment.
|
||||||
|
///
|
||||||
|
/// Applies special behaviour for easier String and WidgetCallback assignment.
|
||||||
|
fn find_type_and_assignment(field: &Field) -> syn::Result<(TokenStream2, TokenStream2)> {
|
||||||
|
let field_ty = &field.ty;
|
||||||
|
let field_ident = extract_ident(field)?;
|
||||||
|
|
||||||
|
let (mut function_input_ty, mut assignment) = easier_string_assignment(field_ty, field_ident);
|
||||||
|
|
||||||
|
// Check if type is `WidgetCallback`
|
||||||
|
if let Type::Path(type_path) = field_ty {
|
||||||
|
if let Some(last_segement) = type_path.path.segments.last() {
|
||||||
|
if let PathArguments::AngleBracketed(generic_args) = &last_segement.arguments {
|
||||||
|
if let Some(first_generic) = generic_args.args.first() {
|
||||||
|
if last_segement.ident == Ident::new("WidgetCallback", last_segement.ident.span()) {
|
||||||
|
// Assign builder pattern to assign the closure directly
|
||||||
|
function_input_ty = quote::quote_spanned!(field_ty.span() => impl Fn(&#first_generic) -> crate::messages::message::Message + 'static + Send + Sync);
|
||||||
|
assignment = quote::quote_spanned!(field_ident.span() => crate::messages::layout::utility_types::layout_widget::WidgetCallback::new(#field_ident));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((function_input_ty, assignment))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a builder function for a specific field in the struct
|
||||||
|
fn construct_builder(field: &Field) -> syn::Result<TokenStream2> {
|
||||||
|
// Check if this field should be skipped with `#[widget_builder(skip)]`
|
||||||
|
if has_attribute(&field.attrs, "(skip)") {
|
||||||
|
return Ok(Default::default());
|
||||||
|
}
|
||||||
|
let field_ident = extract_ident(field)?;
|
||||||
|
|
||||||
|
// Create a doc comment literal describing the behaviour of the function
|
||||||
|
let doc_comment = Literal::string(&format!("Set the `{field_ident}` field using a builder pattern."));
|
||||||
|
|
||||||
|
let (function_input_ty, assignment) = find_type_and_assignment(field)?;
|
||||||
|
|
||||||
|
// Create builder function
|
||||||
|
Ok(quote::quote_spanned!(field.span() =>
|
||||||
|
#[doc = #doc_comment]
|
||||||
|
pub fn #field_ident(mut self, #field_ident: #function_input_ty) -> Self{
|
||||||
|
self.#field_ident = #assignment;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn derive_widget_builder_impl(input_item: TokenStream2) -> syn::Result<TokenStream2> {
|
||||||
|
let input = syn::parse2::<DeriveInput>(input_item)?;
|
||||||
|
|
||||||
|
let struct_name_ident = input.ident;
|
||||||
|
|
||||||
|
// Extract the struct fields
|
||||||
|
let fields = match &input.data {
|
||||||
|
Data::Enum(enum_data) => return Err(syn::Error::new_spanned(enum_data.enum_token, "Derive widget builder is not supported for enums")),
|
||||||
|
Data::Union(union_data) => return Err(syn::Error::new_spanned(union_data.union_token, "Derive widget builder is not supported for unions")),
|
||||||
|
Data::Struct(struct_data) => &struct_data.fields,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create functions based on each field
|
||||||
|
let builder_functions = fields.iter().map(construct_builder).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
// Check if this should not have the `widget_holder()` function due to a `#[widget_builder(not_widget_holder)]` attribute
|
||||||
|
let widget_holder_fn = if !has_attribute(&input.attrs, "(not_widget_holder)") {
|
||||||
|
// A doc comment for the widget_holder function
|
||||||
|
let widget_holder_doc_comment = Literal::string(&format!("Wrap {struct_name_ident} as a WidgetHolder."));
|
||||||
|
|
||||||
|
// Construct the `widget_holder` function
|
||||||
|
quote::quote! {
|
||||||
|
#[doc = #widget_holder_doc_comment]
|
||||||
|
pub fn widget_holder(self) -> crate::messages::layout::utility_types::layout_widget::WidgetHolder{
|
||||||
|
crate::messages::layout::utility_types::layout_widget::WidgetHolder::new( crate::messages::layout::utility_types::layout_widget::Widget::#struct_name_ident(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote::quote!()
|
||||||
|
};
|
||||||
|
|
||||||
|
// The new function takes any fields tagged with `#[widget_builder(constructor)]` as arguments.
|
||||||
|
let new_fn = {
|
||||||
|
// A doc comment for the new function
|
||||||
|
let new_doc_comment = Literal::string(&format!("Create a new {struct_name_ident}, based on default values."));
|
||||||
|
|
||||||
|
let is_constructor = |field: &Field| has_attribute(&field.attrs, "(constructor)");
|
||||||
|
|
||||||
|
let idents = fields.iter().filter(|field| is_constructor(field)).map(extract_ident).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let types_and_assignments = fields.iter().filter(|field| is_constructor(field)).map(find_type_and_assignment).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let (types, assignments): (Vec<_>, Vec<_>) = types_and_assignments.into_iter().unzip();
|
||||||
|
|
||||||
|
let construction = if idents.is_empty() {
|
||||||
|
quote::quote!(Default::default())
|
||||||
|
} else {
|
||||||
|
let default = (idents.len() != fields.len()).then_some(quote::quote!(..Default::default())).unwrap_or_default();
|
||||||
|
quote::quote! {
|
||||||
|
Self {
|
||||||
|
#(#idents: #assignments,)*
|
||||||
|
#default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
#[doc = #new_doc_comment]
|
||||||
|
pub fn new(#(#idents: #types),*) -> Self {
|
||||||
|
#construction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct the code block
|
||||||
|
Ok(quote::quote! {
|
||||||
|
impl #struct_name_ident {
|
||||||
|
#new_fn
|
||||||
|
|
||||||
|
#(#builder_functions)*
|
||||||
|
|
||||||
|
#widget_holder_fn
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue