mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-26 06:48:12 +08:00
Refactor the TypeScript data flow for full type safety and auto-generation of Rust types (#3865)
* Migrate Specta to Tsify to auto-generate messages.ts, working except colors and widgets * Adopt the generated FillColor/Color/GradientStops * Fix widget typing * Separate WidgetGroup enum variants into wrapper structs * Small rename * Simplify widgets further * Clean up message type references * Switch type imports to the auto-generated file * Remove lowercase serde rename * Fix FillChoice deserialization * Fix small regression from #3837 * Improve type safety * Make WidgetSpan type-safe * More cleanup and type safety * More type safety * More type safety * Get the rest to type-check without errors; improve widget builder macro to have optional icons; improve Svelte 5 configs * Cargo fmt * Fix imports * Update outdated readme info * Fix lint command rename references * Fix typos * One more typos fix * Remove unnecessary dep: prefix from the edited Cargo.toml files * Remove excess parts from Cargo.toml * Fix compiling on desktop * Revert "Remove excess parts from Cargo.toml" This reverts commit 6b711117b3a5d5d8a3ee20f36a43bc74930b7c82. * Update dev docs with simpler, more accurate instructions
This commit is contained in:
@@ -128,7 +128,7 @@ impl DataPanelMessageHandler {
|
||||
}
|
||||
|
||||
if !widgets.is_empty() {
|
||||
layout.0.insert(0, LayoutGroup::Row { widgets });
|
||||
layout.0.insert(0, LayoutGroup::row(widgets));
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
@@ -185,7 +185,7 @@ fn column_headings(value: &[&str]) -> Vec<WidgetInstance> {
|
||||
|
||||
fn label(x: impl Into<String>) -> Vec<LayoutGroup> {
|
||||
let error = vec![TextLabel::new(x).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets: error }]
|
||||
vec![LayoutGroup::row(error)]
|
||||
}
|
||||
|
||||
trait TableRowLayout {
|
||||
@@ -234,7 +234,7 @@ impl<T: TableRowLayout> TableRowLayout for Vec<T> {
|
||||
|
||||
rows.insert(0, column_headings(&["", "element"]));
|
||||
|
||||
vec![LayoutGroup::Table { rows, unstyled: false }]
|
||||
vec![LayoutGroup::table(rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
|
||||
|
||||
rows.insert(0, column_headings(&["", "element", "transform", "alpha_blending", "source_node_id"]));
|
||||
|
||||
vec![LayoutGroup::Table { rows, unstyled: false }]
|
||||
vec![LayoutGroup::table(rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ impl TableRowLayout for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
vec![LayoutGroup::Row { widgets: table_tabs }, LayoutGroup::Table { rows: table_rows, unstyled: false }]
|
||||
vec![LayoutGroup::row(table_tabs), LayoutGroup::table(table_rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ impl TableRowLayout for Raster<CPU> {
|
||||
|
||||
if raster.width == 0 || raster.height == 0 {
|
||||
let widgets = vec![TextLabel::new("Image has no area").widget_instance()];
|
||||
return vec![LayoutGroup::Row { widgets }];
|
||||
return vec![LayoutGroup::row(widgets)];
|
||||
}
|
||||
|
||||
let base64_string = raster.base64_string.clone().unwrap_or_else(|| {
|
||||
@@ -519,7 +519,7 @@ impl TableRowLayout for Raster<CPU> {
|
||||
});
|
||||
|
||||
let widgets = vec![ImageLabel::new(base64_string).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ impl TableRowLayout for Raster<GPU> {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new("Raster is a texture on the GPU and cannot currently be displayed here").widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ impl TableRowLayout for Color {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![self.element_widget(0)];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ impl TableRowLayout for GradientStops {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![self.element_widget(0)];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ impl TableRowLayout for f64 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ impl TableRowLayout for u32 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +611,7 @@ impl TableRowLayout for u64 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +624,7 @@ impl TableRowLayout for bool {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +643,7 @@ impl TableRowLayout for String {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextAreaInput::new(self.to_string()).disabled(true).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ impl TableRowLayout for Option<f64> {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("{self:?}")).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ impl TableRowLayout for DVec2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("({}, {})", self.x, self.y)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ impl TableRowLayout for Vec2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("({}, {})", self.x, self.y)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ impl TableRowLayout for DAffine2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format_transform_matrix(self)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +709,7 @@ impl TableRowLayout for Affine2 {
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let matrix = DAffine2::from_cols_array(&self.to_cols_array().map(|x| x as f64));
|
||||
let widgets = vec![TextLabel::new(format_transform_matrix(&matrix)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::node_graph::document_node_definitions;
|
||||
use super::node_graph::utility_types::Transform;
|
||||
use super::utility_types::error::EditorError;
|
||||
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
|
||||
use super::utility_types::network_interface::{self, NodeNetworkInterface, TransactionStatus};
|
||||
@@ -848,7 +847,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
document_id,
|
||||
name: format!("{}.{}", self.name.clone(), FILE_EXTENSION),
|
||||
path: self.path.clone(),
|
||||
content: self.serialize_document().into_bytes(),
|
||||
content: self.serialize_document().into_bytes().into(),
|
||||
})
|
||||
}
|
||||
DocumentMessage::SavedDocument { path } => {
|
||||
@@ -1332,11 +1331,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
|
||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||
transform: Transform {
|
||||
scale: transform.matrix2.x_axis.x,
|
||||
x: transform.translation.x,
|
||||
y: transform.translation.y,
|
||||
},
|
||||
translation: transform.translation.into(),
|
||||
scale: transform.matrix2.x_axis.x,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2216,256 +2212,222 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
PopoverButton::new()
|
||||
.popover_layout(Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Overlays").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("General").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.artboard_name)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::ArtboardName),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Artboard Name".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("G/R/S Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Select Tool").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.quick_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::QuickMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Quick Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_cage)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformCage),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Cage".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.compass_rose)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::CompassRose),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Dial".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Pivot),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Pivot".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Origin),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.hover_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::HoverOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Hover Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.selection_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::SelectionOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Selection Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.layer_origin_cross)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::LayerOriginCross),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Layer Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Pen & Path Tools").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.path)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Path),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Path".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Anchors),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Anchors".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.handles)
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Handles),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Handles".to_string())
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.for_checkbox(checkbox_id)
|
||||
.widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Overlays").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new("General").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.artboard_name)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::ArtboardName),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Artboard Name".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("G/R/S Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row(vec![TextLabel::new("Select Tool").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.quick_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::QuickMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Quick Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_cage)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformCage),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Cage".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.compass_rose)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::CompassRose),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Dial".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Pivot),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Pivot".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.origin)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Origin),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.hover_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::HoverOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Hover Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.selection_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::SelectionOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Selection Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.layer_origin_cross)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::LayerOriginCross),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Layer Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row(vec![TextLabel::new("Pen & Path Tools").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.path)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Path),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Path".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Anchors),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Anchors".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.handles)
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Handles),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Handles".to_string())
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.for_checkbox(checkbox_id)
|
||||
.widget_instance(),
|
||||
]
|
||||
}),
|
||||
]))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
@@ -2484,16 +2446,12 @@ impl DocumentMessageHandler {
|
||||
PopoverButton::new()
|
||||
.popover_layout(Layout(
|
||||
[
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Snapping").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Snapping").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).widget_instance()]),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, description)| {
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(*closure(&mut snapping_state))
|
||||
@@ -2510,13 +2468,11 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
})
|
||||
}))
|
||||
.chain([LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_instance()],
|
||||
}])
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
.chain([LayoutGroup::row(vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_instance()])])
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, description)| {
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(*closure(&mut snapping_state2))
|
||||
@@ -2533,7 +2489,7 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
})
|
||||
}))
|
||||
.collect(),
|
||||
))
|
||||
@@ -2630,8 +2586,8 @@ impl DocumentMessageHandler {
|
||||
widgets.extend([
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into()))
|
||||
.hover_icon(Some((if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" }).into()))
|
||||
.icon(if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" })
|
||||
.hover_icon(if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" })
|
||||
.tooltip_label(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
@@ -2639,7 +2595,7 @@ impl DocumentMessageHandler {
|
||||
]);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout(vec![LayoutGroup::Row { widgets }]),
|
||||
layout: Layout(vec![LayoutGroup::row(widgets)]),
|
||||
layout_target: LayoutTarget::DocumentBar,
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
@@ -2777,25 +2733,25 @@ impl DocumentMessageHandler {
|
||||
.tooltip_label("Fill")
|
||||
.widget_instance(),
|
||||
];
|
||||
let layers_panel_control_bar_left = Layout(vec![LayoutGroup::Row { widgets }]);
|
||||
let layers_panel_control_bar_left = Layout(vec![LayoutGroup::row(widgets)]);
|
||||
|
||||
let widgets = vec![
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.hover_icon(if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" })
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_instance(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.hover_icon(if selection_all_visible { "EyeHide" } else { "EyeShow" })
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_instance(),
|
||||
];
|
||||
let layers_panel_control_bar_right = Layout(vec![LayoutGroup::Row { widgets }]);
|
||||
let layers_panel_control_bar_right = Layout(vec![LayoutGroup::row(widgets)]);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: layers_panel_control_bar_left,
|
||||
@@ -2821,7 +2777,7 @@ impl DocumentMessageHandler {
|
||||
|
||||
let widgets = vec![
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.icon("Node")
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.disabled(!has_selection || has_multiple_selection)
|
||||
@@ -2849,7 +2805,7 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
@@ -2875,7 +2831,7 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
];
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout(vec![LayoutGroup::Row { widgets }]),
|
||||
layout: Layout(vec![LayoutGroup::row(widgets)]),
|
||||
layout_target: LayoutTarget::LayersPanelBottomBar,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ impl NodePropertiesContext<'_> {
|
||||
/// The key used to access definitions for a network node or proto node.
|
||||
/// For proto nodes, this is their [`ProtoNodeIdentifier`].
|
||||
/// For network nodes, it doesn't necessarily have to be the same as the network's display name, but it often is.
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum DefinitionIdentifier {
|
||||
ProtoNode(ProtoNodeIdentifier),
|
||||
@@ -2191,9 +2192,10 @@ fn static_input_properties() -> InputProperties {
|
||||
true
|
||||
});
|
||||
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::number_widget(ParameterWidgetsInfo::new(node_id, index, blank_assist, context), number_input),
|
||||
}])
|
||||
Ok(vec![LayoutGroup::row(node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, blank_assist, context),
|
||||
number_input,
|
||||
))])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2227,10 +2229,12 @@ fn static_input_properties() -> InputProperties {
|
||||
number_input = number_input.step(number_step);
|
||||
}
|
||||
};
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
// NOTE: The bool input MUST be at the input index directly before the f64 input!
|
||||
widgets: node_properties::optional_f64_widget(ParameterWidgetsInfo::new(node_id, index, false, context), index - 1, number_input),
|
||||
}])
|
||||
// NOTE: The bool input MUST be at the input index directly before the f64 input!
|
||||
Ok(vec![LayoutGroup::row(node_properties::optional_f64_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, false, context),
|
||||
index - 1,
|
||||
number_input,
|
||||
))])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2298,7 +2302,7 @@ fn static_input_properties() -> InputProperties {
|
||||
"noise_properties_noise_type".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let noise_type_row = enum_choice::<NoiseType>().for_socket(ParameterWidgetsInfo::new(node_id, index, true, context)).property_row();
|
||||
Ok(vec![noise_type_row, LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![noise_type_row, LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2320,7 +2324,7 @@ fn static_input_properties() -> InputProperties {
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
|
||||
);
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2408,7 +2412,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.range_max(Some(10.))
|
||||
.disabled(!ping_pong_active || !coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||
);
|
||||
Ok(vec![fractal_ping_pong_strength.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![fractal_ping_pong_strength.into(), LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2504,7 +2508,7 @@ fn static_input_properties() -> InputProperties {
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(vec![LayoutGroup::Row { widgets }])
|
||||
Ok(vec![LayoutGroup::row(widgets)])
|
||||
}),
|
||||
);
|
||||
// Skew has a custom override that maps to degrees
|
||||
@@ -2548,24 +2552,20 @@ fn static_input_properties() -> InputProperties {
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(vec![LayoutGroup::Row { widgets }])
|
||||
Ok(vec![LayoutGroup::row(widgets)])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
"text_area".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::text_area_widget(ParameterWidgetsInfo::new(node_id, index, true, context)),
|
||||
}])
|
||||
}),
|
||||
Box::new(|node_id, index, context| Ok(vec![LayoutGroup::row(node_properties::text_area_widget(ParameterWidgetsInfo::new(node_id, index, true, context)))])),
|
||||
);
|
||||
map.insert(
|
||||
"text_font".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (font, style) = node_properties::font_inputs(ParameterWidgetsInfo::new(node_id, index, true, context));
|
||||
let mut result = vec![LayoutGroup::Row { widgets: font }];
|
||||
let mut result = vec![LayoutGroup::row(font)];
|
||||
if let Some(style) = style {
|
||||
result.push(LayoutGroup::Row { widgets: style });
|
||||
result.push(LayoutGroup::row(style));
|
||||
}
|
||||
Ok(result)
|
||||
}),
|
||||
|
||||
@@ -847,7 +847,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
};
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
context_menu_coordinates: (node_graph_point + node_graph_shift).as_ivec2(),
|
||||
context_menu_coordinates: (node_graph_point + node_graph_shift).as_ivec2().into(),
|
||||
context_menu_data,
|
||||
});
|
||||
|
||||
@@ -1280,7 +1280,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string();
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
context_menu_coordinates: (point + node_graph_shift).as_ivec2(),
|
||||
context_menu_coordinates: (point + node_graph_shift).as_ivec2().into(),
|
||||
context_menu_data: ContextMenuData::CreateNode { compatible_type },
|
||||
});
|
||||
|
||||
@@ -2050,8 +2050,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(FrontendMessage::UpdateImportsExports {
|
||||
imports,
|
||||
exports,
|
||||
import_position,
|
||||
export_position,
|
||||
import_position: import_position.into(),
|
||||
export_position: export_position.into(),
|
||||
add_import_export,
|
||||
});
|
||||
}
|
||||
@@ -2181,7 +2181,7 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
let mut widgets = vec![
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.icon("Node")
|
||||
.tooltip_label("New Node")
|
||||
.tooltip_description("To add a node at the pointer location, perform the shortcut in an open area of the graph.")
|
||||
.tooltip_shortcut(action_shortcut_manual!(Key::MouseRight))
|
||||
@@ -2222,7 +2222,7 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
//
|
||||
@@ -2252,14 +2252,14 @@ impl NodeGraphMessageHandler {
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
//
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.hover_icon(if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" })
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection || !selection_includes_layers)
|
||||
.widget_instance(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.hover_icon(if selection_all_visible { "EyeHide" } else { "EyeShow" })
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||
@@ -2286,7 +2286,7 @@ impl NodeGraphMessageHandler {
|
||||
// If only one node is selected then show the preview or stop previewing button
|
||||
if let Some(node_id) = previewing {
|
||||
let button = TextButton::new("End Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.icon("FrameAll")
|
||||
.tooltip_description("Restore preview to the graph output.")
|
||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||
.widget_instance();
|
||||
@@ -2298,7 +2298,7 @@ impl NodeGraphMessageHandler {
|
||||
.any(|export| matches!(export, NodeInput::Node { node_id: export_node_id, .. } if *export_node_id == node_id));
|
||||
if selection_is_not_already_the_output && no_other_selections {
|
||||
let button = TextButton::new("Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.icon("FrameAll")
|
||||
.tooltip_label("Preview")
|
||||
.tooltip_description("Temporarily set the graph output to the selected node or layer. Perform the shortcut on a node or layer for quick access.")
|
||||
.tooltip_shortcut(action_shortcut_manual!(Key::Alt, Key::MouseLeft))
|
||||
@@ -2323,7 +2323,7 @@ impl NodeGraphMessageHandler {
|
||||
]);
|
||||
}
|
||||
|
||||
self.widgets[0] = LayoutGroup::Row { widgets };
|
||||
self.widgets[0] = LayoutGroup::row(widgets);
|
||||
}
|
||||
|
||||
fn update_graph_bar_right(
|
||||
@@ -2357,15 +2357,15 @@ impl NodeGraphMessageHandler {
|
||||
widgets.extend([
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some("GraphViewOpen".into()))
|
||||
.hover_icon(Some("GraphViewClosed".into()))
|
||||
.icon("GraphViewOpen")
|
||||
.hover_icon("GraphViewClosed")
|
||||
.tooltip_label("Hide Node Graph")
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
.widget_instance(),
|
||||
]);
|
||||
|
||||
self.widgets[1] = LayoutGroup::Row { widgets };
|
||||
self.widgets[1] = LayoutGroup::row(widgets);
|
||||
}
|
||||
|
||||
/// Collate the properties panel sections for a node graph
|
||||
@@ -2407,25 +2407,23 @@ impl NodeGraphMessageHandler {
|
||||
let mut properties = Vec::new();
|
||||
|
||||
if let [node_id] = *nodes.as_slice() {
|
||||
properties.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected node.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
});
|
||||
properties.push(LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected node.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
]));
|
||||
}
|
||||
|
||||
properties.extend(selected_nodes);
|
||||
@@ -2435,18 +2433,16 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
// TODO: Display properties for encapsulating node when no nodes are selected in a nested network
|
||||
// This may require store a separate path for the properties panel
|
||||
let mut properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("File").tooltip_description("Name of the current document.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.document_name)
|
||||
.tooltip_description("Name of the current document.")
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
}];
|
||||
let mut properties = vec![LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("File").tooltip_description("Name of the current document.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.document_name)
|
||||
.tooltip_description("Name of the current document.")
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
])];
|
||||
|
||||
let Some(network) = context.network_interface.nested_network(context.selection_network_path) else {
|
||||
warn!("No network in collate_properties");
|
||||
@@ -2482,50 +2478,48 @@ impl NodeGraphMessageHandler {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut layer_properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected layer.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id: layer,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer, 1), &[])
|
||||
.and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string());
|
||||
let mut layer_properties = vec![LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected layer.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id: layer,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
PopoverButton::new()
|
||||
.icon("Node")
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer, 1), &[])
|
||||
.and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string());
|
||||
|
||||
let mut node_chooser = NodeCatalog::new();
|
||||
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||
let mut node_chooser = NodeCatalog::new();
|
||||
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||
|
||||
let node_chooser = node_chooser
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
}];
|
||||
let node_chooser = node_chooser
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
])];
|
||||
|
||||
// Iterate through all the upstream nodes, but stop when we reach another layer (since that's a point where we switch from horizontal to vertical flow)
|
||||
let node_properties = context
|
||||
@@ -2651,7 +2645,7 @@ impl NodeGraphMessageHandler {
|
||||
exposed_outputs,
|
||||
primary_output_connected_to_layer,
|
||||
primary_input_connected_to_layer,
|
||||
position,
|
||||
position: position.into(),
|
||||
previewed,
|
||||
visible,
|
||||
locked,
|
||||
@@ -2695,6 +2689,7 @@ impl NodeGraphMessageHandler {
|
||||
if network_interface.is_layer(&error_node, breadcrumb_network_path) {
|
||||
position += IVec2::new(12, -12)
|
||||
}
|
||||
let position = position.into();
|
||||
|
||||
Some(NodeGraphErrorDiagnostic { position, error })
|
||||
}
|
||||
@@ -2841,7 +2836,7 @@ impl Default for NodeGraphMessageHandler {
|
||||
Self {
|
||||
network: Vec::new(),
|
||||
has_selection: false,
|
||||
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: Vec::new() }],
|
||||
widgets: [LayoutGroup::row(Vec::new()), LayoutGroup::row(Vec::new())],
|
||||
drag_start: None,
|
||||
begin_dragging: false,
|
||||
node_has_moved_in_drag: false,
|
||||
|
||||
@@ -31,7 +31,7 @@ use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops, Gra
|
||||
|
||||
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
|
||||
let widget = TextLabel::new(text).widget_instance();
|
||||
vec![LayoutGroup::Row { widgets: vec![widget] }]
|
||||
vec![LayoutGroup::row(vec![widget])]
|
||||
}
|
||||
|
||||
fn optionally_update_value<T>(value: impl Fn(&T) -> Option<TaggedValue> + 'static + Send + Sync, node_id: NodeId, input_index: usize) -> impl Fn(&T) -> Message + 'static + Send + Sync {
|
||||
@@ -538,11 +538,7 @@ pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
|
||||
);
|
||||
}
|
||||
|
||||
let widgets = [
|
||||
LayoutGroup::Row { widgets: location_widgets },
|
||||
LayoutGroup::Row { widgets: scale_widgets },
|
||||
LayoutGroup::Row { widgets: resolution_widgets },
|
||||
];
|
||||
let widgets = [LayoutGroup::row(location_widgets), LayoutGroup::row(scale_widgets), LayoutGroup::row(resolution_widgets)];
|
||||
let (last, rest) = widgets.split_last().expect("Footprint widget should return multiple rows");
|
||||
*extra_widgets = rest.to_vec();
|
||||
last.clone()
|
||||
@@ -651,13 +647,9 @@ pub fn transform_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
|
||||
.widget_instance(),
|
||||
]);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: location_widgets },
|
||||
LayoutGroup::Row { widgets: rotation_widgets },
|
||||
LayoutGroup::Row { widgets: scale_widgets },
|
||||
]
|
||||
vec![LayoutGroup::row(location_widgets), LayoutGroup::row(rotation_widgets), LayoutGroup::row(scale_widgets)]
|
||||
} else {
|
||||
vec![LayoutGroup::Row { widgets: location_widgets }]
|
||||
vec![LayoutGroup::row(location_widgets)]
|
||||
};
|
||||
|
||||
if let Some((last, rest)) = widgets.split_last() {
|
||||
@@ -676,7 +668,7 @@ pub fn vec2_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &st
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
match input.as_non_exposed_value() {
|
||||
Some(&TaggedValue::DVec2(dvec2)) => {
|
||||
@@ -730,7 +722,7 @@ pub fn vec2_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &st
|
||||
_ => {}
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn array_of_number_widget(parameter_widgets_info: ParameterWidgetsInfo, text_input: TextInput) -> Vec<WidgetInstance> {
|
||||
@@ -1101,7 +1093,7 @@ pub fn blend_mode_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Layout
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
if let Some(&TaggedValue::BlendMode(blend_mode)) = input.as_non_exposed_value() {
|
||||
let entries = BlendMode::list_svg_subset()
|
||||
@@ -1126,7 +1118,7 @@ pub fn blend_mode_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Layout
|
||||
.widget_instance(),
|
||||
]);
|
||||
}
|
||||
LayoutGroup::Row { widgets }.with_tooltip_description("Formula used for blending.")
|
||||
LayoutGroup::row(widgets).with_tooltip_description("Formula used for blending.")
|
||||
}
|
||||
|
||||
pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button: ColorInput) -> LayoutGroup {
|
||||
@@ -1137,7 +1129,7 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
// Return early with just the label if the input is exposed to the graph, meaning we don't want to show the color picker widget in the Properties panel
|
||||
let NodeInput::Value { tagged_value, exposed: false } = &document_node.inputs[index] else {
|
||||
return LayoutGroup::Row { widgets };
|
||||
return LayoutGroup::row(widgets);
|
||||
};
|
||||
|
||||
// Add a separator
|
||||
@@ -1176,7 +1168,7 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
x => warn!("Color {x:?}"),
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn font_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup {
|
||||
@@ -1192,7 +1184,7 @@ pub fn curve_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
if let Some(TaggedValue::Curve(curve)) = &input.as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
@@ -1203,7 +1195,7 @@ pub fn curve_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
|
||||
.widget_instance(),
|
||||
])
|
||||
}
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn get_document_node<'a>(node_id: NodeId, context: &'a NodePropertiesContext<'a>) -> Result<&'a DocumentNode, String> {
|
||||
@@ -1306,10 +1298,10 @@ pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut Node
|
||||
.range_max(Some(100.)),
|
||||
);
|
||||
|
||||
let mut layout = vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }];
|
||||
let mut layout = vec![LayoutGroup::row(brightness), LayoutGroup::row(contrast)];
|
||||
if includes_use_classic {
|
||||
// TODO: When we no longer use this function in the temporary "Brightness/Contrast Classic" node, remove this conditional pushing and just always include this
|
||||
layout.push(LayoutGroup::Row { widgets: use_classic });
|
||||
layout.push(LayoutGroup::row(use_classic));
|
||||
}
|
||||
|
||||
layout
|
||||
@@ -1358,18 +1350,13 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
|
||||
let constant = number_widget(ParameterWidgetsInfo::new(node_id, constant_output_index, true, context), number_input);
|
||||
|
||||
// Monochrome
|
||||
let mut layout = vec![LayoutGroup::Row { widgets: is_monochrome }];
|
||||
let mut layout = vec![LayoutGroup::row(is_monochrome)];
|
||||
// Output channel choice
|
||||
if !is_monochrome_value {
|
||||
layout.push(output_channel);
|
||||
}
|
||||
// Channel values
|
||||
layout.extend([
|
||||
LayoutGroup::Row { widgets: red },
|
||||
LayoutGroup::Row { widgets: green },
|
||||
LayoutGroup::Row { widgets: blue },
|
||||
LayoutGroup::Row { widgets: constant },
|
||||
]);
|
||||
layout.extend([LayoutGroup::row(red), LayoutGroup::row(green), LayoutGroup::row(blue), LayoutGroup::row(constant)]);
|
||||
layout
|
||||
}
|
||||
|
||||
@@ -1422,10 +1409,10 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp
|
||||
// Colors choice
|
||||
colors,
|
||||
// CMYK
|
||||
LayoutGroup::Row { widgets: cyan },
|
||||
LayoutGroup::Row { widgets: magenta },
|
||||
LayoutGroup::Row { widgets: yellow },
|
||||
LayoutGroup::Row { widgets: black },
|
||||
LayoutGroup::row(cyan),
|
||||
LayoutGroup::row(magenta),
|
||||
LayoutGroup::row(yellow),
|
||||
LayoutGroup::row(black),
|
||||
// Mode
|
||||
mode,
|
||||
]
|
||||
@@ -1458,12 +1445,10 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
widgets.push(spacing);
|
||||
}
|
||||
GridType::Isometric => {
|
||||
let spacing = LayoutGroup::Row {
|
||||
widgets: number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context),
|
||||
NumberInput::default().label("H").min(0.).unit(" px"),
|
||||
),
|
||||
};
|
||||
let spacing = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context),
|
||||
NumberInput::default().label("H").min(0.).unit(" px"),
|
||||
));
|
||||
let angles = vec2_widget(ParameterWidgetsInfo::new(node_id, AnglesInput::INDEX, true, context), "", "", "°", None, false);
|
||||
widgets.extend([spacing, angles]);
|
||||
}
|
||||
@@ -1473,7 +1458,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
let columns = number_widget(ParameterWidgetsInfo::new(node_id, ColumnsInput::INDEX, true, context), NumberInput::default().min(1.));
|
||||
let rows = number_widget(ParameterWidgetsInfo::new(node_id, RowsInput::INDEX, true, context), NumberInput::default().min(1.));
|
||||
|
||||
widgets.extend([LayoutGroup::Row { widgets: columns }, LayoutGroup::Row { widgets: rows }]);
|
||||
widgets.extend([LayoutGroup::row(columns), LayoutGroup::row(rows)]);
|
||||
|
||||
widgets
|
||||
}
|
||||
@@ -1487,7 +1472,7 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
let turns = number_widget(ParameterWidgetsInfo::new(node_id, TurnsInput::INDEX, true, context), NumberInput::default().min(0.1));
|
||||
let start_angle = number_widget(ParameterWidgetsInfo::new(node_id, StartAngleInput::INDEX, true, context), NumberInput::default().unit("°"));
|
||||
|
||||
let mut widgets = vec![spiral_type, LayoutGroup::Row { widgets: turns }, LayoutGroup::Row { widgets: start_angle }];
|
||||
let mut widgets = vec![spiral_type, LayoutGroup::row(turns), LayoutGroup::row(start_angle)];
|
||||
|
||||
let document_node = match get_document_node(node_id, context) {
|
||||
Ok(document_node) => document_node,
|
||||
@@ -1504,24 +1489,28 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
if let Some(&TaggedValue::SpiralType(spiral_type)) = spiral_type_input.as_non_exposed_value() {
|
||||
match spiral_type {
|
||||
SpiralType::Archimedean => {
|
||||
let inner_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context), NumberInput::default().min(0.).unit(" px")),
|
||||
};
|
||||
let inner_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.).unit(" px"),
|
||||
));
|
||||
|
||||
let outer_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context), NumberInput::default().unit(" px")),
|
||||
};
|
||||
let outer_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().unit(" px"),
|
||||
));
|
||||
|
||||
widgets.extend([inner_radius, outer_radius]);
|
||||
}
|
||||
SpiralType::Logarithmic => {
|
||||
let inner_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context), NumberInput::default().min(0.).unit(" px")),
|
||||
};
|
||||
let inner_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.).unit(" px"),
|
||||
));
|
||||
|
||||
let outer_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context), NumberInput::default().min(0.1).unit(" px")),
|
||||
};
|
||||
let outer_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.1).unit(" px"),
|
||||
));
|
||||
|
||||
widgets.extend([inner_radius, outer_radius]);
|
||||
}
|
||||
@@ -1533,7 +1522,7 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
NumberInput::default().min(1.).max(180.).unit("°"),
|
||||
);
|
||||
|
||||
widgets.push(LayoutGroup::Row { widgets: angular_resolution });
|
||||
widgets.push(LayoutGroup::row(angular_resolution));
|
||||
|
||||
widgets
|
||||
}
|
||||
@@ -1574,13 +1563,13 @@ pub(crate) fn sample_polyline_properties(node_id: NodeId, context: &mut NodeProp
|
||||
vec![
|
||||
spacing.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SPACING),
|
||||
match current_spacing {
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::Row { widgets: separation }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::Row { widgets: quantity }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_QUANTITY),
|
||||
_ => LayoutGroup::Row { widgets: vec![] },
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::row(separation).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::row(quantity).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_QUANTITY),
|
||||
_ => LayoutGroup::row(vec![]),
|
||||
},
|
||||
LayoutGroup::Row { widgets: start_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET),
|
||||
LayoutGroup::Row { widgets: stop_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET),
|
||||
LayoutGroup::Row { widgets: adaptive_spacing }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING),
|
||||
LayoutGroup::row(start_offset).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET),
|
||||
LayoutGroup::row(stop_offset).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET),
|
||||
LayoutGroup::row(adaptive_spacing).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1594,11 +1583,7 @@ pub(crate) fn exposure_properties(node_id: NodeId, context: &mut NodePropertiesC
|
||||
NumberInput::default().min(0.01).max(9.99).increment_step(0.1),
|
||||
);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: exposure },
|
||||
LayoutGroup::Row { widgets: offset },
|
||||
LayoutGroup::Row { widgets: gamma_correction },
|
||||
]
|
||||
vec![LayoutGroup::row(exposure), LayoutGroup::row(offset), LayoutGroup::row(gamma_correction)]
|
||||
}
|
||||
|
||||
pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
@@ -1722,11 +1707,11 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
let clamped = bool_widget(ParameterWidgetsInfo::new(node_id, ClampedInput::INDEX, true, context), CheckboxInput::default());
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: size_x },
|
||||
LayoutGroup::Row { widgets: size_y },
|
||||
LayoutGroup::Row { widgets: corner_radius_row_1 },
|
||||
LayoutGroup::Row { widgets: corner_radius_row_2 },
|
||||
LayoutGroup::Row { widgets: clamped },
|
||||
LayoutGroup::row(size_x),
|
||||
LayoutGroup::row(size_y),
|
||||
LayoutGroup::row(corner_radius_row_1),
|
||||
LayoutGroup::row(corner_radius_row_2),
|
||||
LayoutGroup::row(clamped),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1825,14 +1810,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
|
||||
let visible = context.network_interface.is_visible(&node_id, context.selection_network_path);
|
||||
let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path);
|
||||
|
||||
LayoutGroup::Section {
|
||||
name,
|
||||
description,
|
||||
visible,
|
||||
pinned,
|
||||
id: node_id.0,
|
||||
layout: Layout(layout),
|
||||
}
|
||||
LayoutGroup::section(name, description, visible, pinned, node_id.0, Layout(layout))
|
||||
}
|
||||
|
||||
/// Fill Node Widgets LayoutGroup
|
||||
@@ -1856,7 +1834,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
) {
|
||||
(fill, backup_color, backup_gradient)
|
||||
} else {
|
||||
return vec![LayoutGroup::Row { widgets: widgets_first_row }];
|
||||
return vec![LayoutGroup::row(widgets_first_row)];
|
||||
};
|
||||
let fill2 = fill.clone();
|
||||
let backup_color_fill: Fill = backup_color.clone().into();
|
||||
@@ -1899,7 +1877,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
);
|
||||
let mut widgets = vec![LayoutGroup::Row { widgets: widgets_first_row }];
|
||||
let mut widgets = vec![LayoutGroup::row(widgets_first_row)];
|
||||
|
||||
let fill_type_switch = {
|
||||
let mut row = vec![TextLabel::new("").widget_instance()];
|
||||
@@ -1942,7 +1920,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
RadioInput::new(entries).selected_index(Some(if fill.as_gradient().is_some() { 1 } else { 0 })).widget_instance(),
|
||||
]);
|
||||
|
||||
LayoutGroup::Row { widgets: row }
|
||||
LayoutGroup::row(row)
|
||||
};
|
||||
widgets.push(fill_type_switch);
|
||||
|
||||
@@ -2011,7 +1989,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
RadioInput::new(entries).selected_index(Some(gradient.gradient_type as u32)).widget_instance(),
|
||||
]);
|
||||
|
||||
widgets.push(LayoutGroup::Row { widgets: row });
|
||||
widgets.push(LayoutGroup::row(row));
|
||||
}
|
||||
|
||||
widgets
|
||||
@@ -2069,14 +2047,14 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
|
||||
|
||||
vec![
|
||||
color,
|
||||
LayoutGroup::Row { widgets: weight },
|
||||
LayoutGroup::row(weight),
|
||||
align,
|
||||
cap,
|
||||
join,
|
||||
LayoutGroup::Row { widgets: miter_limit },
|
||||
LayoutGroup::row(miter_limit),
|
||||
paint_order,
|
||||
LayoutGroup::Row { widgets: dash_lengths },
|
||||
LayoutGroup::Row { widgets: dash_offset },
|
||||
LayoutGroup::row(dash_lengths),
|
||||
LayoutGroup::row(dash_offset),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2106,7 +2084,7 @@ pub fn offset_path_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
});
|
||||
let miter_limit = number_widget(ParameterWidgetsInfo::new(node_id, MiterLimitInput::INDEX, true, context), number_input);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: distance }, join, LayoutGroup::Row { widgets: miter_limit }]
|
||||
vec![LayoutGroup::row(distance), join, LayoutGroup::row(miter_limit)]
|
||||
}
|
||||
|
||||
pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
@@ -2158,9 +2136,9 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
|
||||
let operand_a_hint = vec![TextLabel::new("(Operand A is the primary input)").widget_instance()];
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: expression }.with_tooltip_description(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"."#),
|
||||
LayoutGroup::Row { widgets: operand_b }.with_tooltip_description(r#"The value of "B" when calculating the expression."#),
|
||||
LayoutGroup::Row { widgets: operand_a_hint }.with_tooltip_description(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected."#),
|
||||
LayoutGroup::row(expression).with_tooltip_description(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"."#),
|
||||
LayoutGroup::row(operand_b).with_tooltip_description(r#"The value of "B" when calculating the expression."#),
|
||||
LayoutGroup::row(operand_a_hint).with_tooltip_description(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected."#),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2348,14 +2326,14 @@ pub mod choice {
|
||||
let ParameterWidgetsInfo { document_node, node_id, index, .. } = self.parameter_info;
|
||||
let Some(document_node) = document_node else {
|
||||
log::error!("Could not get document node when building property row for node {node_id:?}");
|
||||
return LayoutGroup::Row { widgets: Vec::new() };
|
||||
return LayoutGroup::row(Vec::new());
|
||||
};
|
||||
|
||||
let mut widgets = super::start_widgets(self.parameter_info);
|
||||
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
|
||||
let input: Option<W::Value> = input.as_non_exposed_value().and_then(|v| <&W::Value as TryFrom<&TaggedValue>>::try_from(v).ok()).cloned();
|
||||
@@ -2367,7 +2345,7 @@ pub mod choice {
|
||||
widgets.extend_from_slice(&[Separator::new(SeparatorStyle::Unrelated).widget_instance(), widget]);
|
||||
}
|
||||
|
||||
let mut row = LayoutGroup::Row { widgets };
|
||||
let mut row = LayoutGroup::row(widgets);
|
||||
if let Some(desc) = self.widget_factory.description() {
|
||||
row = row.with_tooltip_description(desc);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use glam::IVec2;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::Type;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FrontendGraphDataType {
|
||||
#[default]
|
||||
General,
|
||||
@@ -42,7 +42,8 @@ impl FrontendGraphDataType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendGraphInput {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
@@ -57,21 +58,23 @@ pub struct FrontendGraphInput {
|
||||
pub connected_to: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendGraphOutput {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[serde(rename = "resolvedType")]
|
||||
pub resolved_type: String,
|
||||
pub description: String,
|
||||
/// If connected to an export, it is "export index {index}".
|
||||
/// If connected to a node, it is "{node name} input {input_index}".
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendNode {
|
||||
pub id: graph_craft::document::NodeId,
|
||||
#[serde(rename = "isLayer")]
|
||||
@@ -95,13 +98,14 @@ pub struct FrontendNode {
|
||||
pub primary_input_connected_to_layer: bool,
|
||||
#[serde(rename = "primaryOutputConnectedToLayer")]
|
||||
pub primary_output_connected_to_layer: bool,
|
||||
pub position: IVec2,
|
||||
pub position: (i32, i32),
|
||||
pub previewed: bool,
|
||||
pub visible: bool,
|
||||
pub locked: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendNodeType {
|
||||
pub identifier: String,
|
||||
pub name: String,
|
||||
@@ -110,7 +114,8 @@ pub struct FrontendNodeType {
|
||||
pub input_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DragStart {
|
||||
pub start_x: f64,
|
||||
pub start_y: f64,
|
||||
@@ -118,14 +123,8 @@ pub struct DragStart {
|
||||
pub round_y: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct Transform {
|
||||
pub scale: f64,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BoxSelection {
|
||||
#[serde(rename = "startX")]
|
||||
pub start_x: u32,
|
||||
@@ -137,7 +136,8 @@ pub struct BoxSelection {
|
||||
pub end_y: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum ContextMenuData {
|
||||
ModifyNode {
|
||||
@@ -158,22 +158,25 @@ pub enum ContextMenuData {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ContextMenuInformation {
|
||||
// Stores whether the context menu is open and its position in graph coordinates
|
||||
#[serde(rename = "contextMenuCoordinates")]
|
||||
pub context_menu_coordinates: IVec2,
|
||||
pub context_menu_coordinates: (i32, i32),
|
||||
#[serde(rename = "contextMenuData")]
|
||||
pub context_menu_data: ContextMenuData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NodeGraphErrorDiagnostic {
|
||||
pub position: IVec2,
|
||||
pub position: (i32, i32),
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendClickTargets {
|
||||
#[serde(rename = "nodeClickTargets")]
|
||||
pub node_click_targets: Vec<String>,
|
||||
@@ -189,7 +192,8 @@ pub struct FrontendClickTargets {
|
||||
pub modify_import_export: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
|
||||
@@ -228,42 +228,38 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
})
|
||||
};
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Grid").bold(true).widget_instance()],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![TextLabel::new("Grid").bold(true).widget_instance()]));
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Type").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("rectangular").label("Rectangular").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Isometric { y_axis_spacing, angle_a, angle_b } = grid.grid_type {
|
||||
grid.isometric_y_spacing = y_axis_spacing;
|
||||
grid.isometric_angle_a = angle_a;
|
||||
grid.isometric_angle_b = angle_b;
|
||||
}
|
||||
grid.grid_type = GridType::Rectangular { spacing: grid.rectangular_spacing };
|
||||
})),
|
||||
RadioEntryData::new("isometric").label("Isometric").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Rectangular { spacing } = grid.grid_type {
|
||||
grid.rectangular_spacing = spacing;
|
||||
}
|
||||
grid.grid_type = GridType::Isometric {
|
||||
y_axis_spacing: grid.isometric_y_spacing,
|
||||
angle_a: grid.isometric_angle_a,
|
||||
angle_b: grid.isometric_angle_b,
|
||||
};
|
||||
})),
|
||||
])
|
||||
.min_width(200)
|
||||
.selected_index(Some(match grid.grid_type {
|
||||
GridType::Rectangular { .. } => 0,
|
||||
GridType::Isometric { .. } => 1,
|
||||
}))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Type").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("rectangular").label("Rectangular").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Isometric { y_axis_spacing, angle_a, angle_b } = grid.grid_type {
|
||||
grid.isometric_y_spacing = y_axis_spacing;
|
||||
grid.isometric_angle_a = angle_a;
|
||||
grid.isometric_angle_b = angle_b;
|
||||
}
|
||||
grid.grid_type = GridType::Rectangular { spacing: grid.rectangular_spacing };
|
||||
})),
|
||||
RadioEntryData::new("isometric").label("Isometric").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Rectangular { spacing } = grid.grid_type {
|
||||
grid.rectangular_spacing = spacing;
|
||||
}
|
||||
grid.grid_type = GridType::Isometric {
|
||||
y_axis_spacing: grid.isometric_y_spacing,
|
||||
angle_a: grid.isometric_angle_a,
|
||||
angle_b: grid.isometric_angle_b,
|
||||
};
|
||||
})),
|
||||
])
|
||||
.min_width(200)
|
||||
.selected_index(Some(match grid.grid_type {
|
||||
GridType::Rectangular { .. } => 0,
|
||||
GridType::Isometric { .. } => 1,
|
||||
}))
|
||||
.widget_instance(),
|
||||
]));
|
||||
|
||||
let mut color_widgets = vec![
|
||||
TextLabel::new("Display").table_align(true).widget_instance(),
|
||||
@@ -288,80 +284,72 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
}))
|
||||
.widget_instance(),
|
||||
);
|
||||
widgets.push(LayoutGroup::Row { widgets: color_widgets });
|
||||
widgets.push(LayoutGroup::row(color_widgets));
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Origin").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.y)))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Origin").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.y)))
|
||||
.widget_instance(),
|
||||
]));
|
||||
|
||||
match grid.grid_type {
|
||||
GridType::Rectangular { spacing } => widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Spacing").table_align(true).widget_instance(),
|
||||
GridType::Rectangular { spacing } => widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(spacing.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(spacing.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.y)))
|
||||
.widget_instance(),
|
||||
])),
|
||||
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Y Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(spacing.x))
|
||||
.label("X")
|
||||
NumberInput::new(Some(y_axis_spacing))
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(200)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.isometric_y_spacing()))
|
||||
.widget_instance(),
|
||||
]));
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Angles").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(angle_a))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.x)))
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_a()))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(spacing.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
NumberInput::new(Some(angle_b))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.y)))
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_b()))
|
||||
.widget_instance(),
|
||||
],
|
||||
}),
|
||||
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Y Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(y_axis_spacing))
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(200)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.isometric_y_spacing()))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Angles").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(angle_a))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_a()))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(angle_b))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_b()))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ pub enum GizmoEmphasis {
|
||||
|
||||
// TODO Remove duplicated definition of this in `utility_types_web.rs`
|
||||
/// Types of overlays used by DocumentMessage to enable/disable the selected set of viewport overlays.
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum OverlaysType {
|
||||
ArtboardName,
|
||||
CompassRose,
|
||||
@@ -60,7 +61,8 @@ pub enum OverlaysType {
|
||||
}
|
||||
|
||||
// TODO Remove duplicated definition of this in `utility_types_web.rs`
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct OverlaysVisibilitySettings {
|
||||
pub all: bool,
|
||||
@@ -160,11 +162,11 @@ impl OverlaysVisibilitySettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct OverlayContext {
|
||||
// Serde functionality isn't used but is required by the message system macros
|
||||
#[serde(skip)]
|
||||
#[specta(skip)]
|
||||
internal: Arc<Mutex<OverlayContextInternal>>,
|
||||
pub viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
|
||||
@@ -35,7 +35,8 @@ pub enum GizmoEmphasis {
|
||||
}
|
||||
|
||||
/// Types of overlays used by DocumentMessage to enable/disable the selected set of viewport overlays.
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum OverlaysType {
|
||||
ArtboardName,
|
||||
CompassRose,
|
||||
@@ -52,7 +53,8 @@ pub enum OverlaysType {
|
||||
Handles,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct OverlaysVisibilitySettings {
|
||||
pub all: bool,
|
||||
@@ -150,11 +152,11 @@ impl OverlaysVisibilitySettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OverlayContext {
|
||||
// Serde functionality isn't used but is required by the message system macros
|
||||
#[serde(skip, default = "overlay_canvas_context")]
|
||||
#[specta(skip)]
|
||||
pub render_context: web_sys::CanvasRenderingContext2d,
|
||||
pub viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
|
||||
@@ -2,7 +2,8 @@ use super::network_interface::NodeTemplate;
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Clipboard {
|
||||
Internal,
|
||||
Device,
|
||||
|
||||
@@ -234,7 +234,8 @@ impl DocumentMetadata {
|
||||
// ===================
|
||||
|
||||
/// ID of a layer node
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LayerNodeIdentifier(NonZeroU64);
|
||||
|
||||
impl core::fmt::Debug for LayerNodeIdentifier {
|
||||
|
||||
@@ -3,7 +3,8 @@ use glam::DVec2;
|
||||
use std::fmt;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DocumentId(pub u64);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
@@ -12,13 +13,15 @@ pub enum FlipAxis {
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
pub enum AlignAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
pub enum AlignAggregate {
|
||||
Min,
|
||||
Max,
|
||||
|
||||
@@ -5723,14 +5723,16 @@ impl Iterator for FlowIter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ImportOrExport {
|
||||
Import(usize),
|
||||
Export(usize),
|
||||
}
|
||||
|
||||
/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum InputConnector {
|
||||
#[serde(rename = "node")]
|
||||
Node {
|
||||
@@ -5770,7 +5772,8 @@ impl InputConnector {
|
||||
}
|
||||
|
||||
/// Represents an output connector
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum OutputConnector {
|
||||
#[serde(rename = "node")]
|
||||
Node {
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use super::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::frontend::IconName;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use glam::DVec2;
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
|
||||
/// Represents an entry in the layer tree hierarchy, sent to the frontend.
|
||||
/// Each entry contains its layer ID and a list of its visible children.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct LayerStructureEntry {
|
||||
#[serde(rename = "layerId")]
|
||||
pub layer_id: NodeId,
|
||||
pub children: Vec<LayerStructureEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct LayerPanelEntry {
|
||||
pub id: NodeId,
|
||||
#[serde(rename = "implementationName")]
|
||||
pub implementation_name: String,
|
||||
#[serde(rename = "iconName")]
|
||||
pub icon_name: Option<String>,
|
||||
pub icon_name: Option<IconName>,
|
||||
pub alias: String,
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
pub in_selected_network: bool,
|
||||
@@ -47,7 +50,8 @@ pub struct LayerPanelEntry {
|
||||
}
|
||||
|
||||
/// IMPORTANT: the same node may appear multiple times.
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct SelectedNodes(pub Vec<NodeId>);
|
||||
|
||||
impl SelectedNodes {
|
||||
@@ -157,5 +161,6 @@ impl SelectedNodes {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>);
|
||||
|
||||
@@ -3,7 +3,8 @@ use glam::{DVec2, IVec2};
|
||||
use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point};
|
||||
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WirePath {
|
||||
#[serde(rename = "pathString")]
|
||||
pub path_string: String,
|
||||
@@ -13,7 +14,8 @@ pub struct WirePath {
|
||||
pub dashed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WirePathUpdate {
|
||||
pub id: NodeId,
|
||||
#[serde(rename = "inputIndex")]
|
||||
@@ -23,7 +25,8 @@ pub struct WirePathUpdate {
|
||||
pub wire_path_update: Option<WirePath>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GraphWireStyle {
|
||||
#[default]
|
||||
Direct = 0,
|
||||
|
||||
Reference in New Issue
Block a user