Fix misc errors and cleanup after rebases

This commit is contained in:
Adam
2025-09-09 16:53:07 -07:00
parent c994cdeced
commit 4c92c48cc3
25 changed files with 206 additions and 436 deletions

View File

@@ -50,6 +50,7 @@ impl Editor {
open: active_document.graph_view_overlay_open,
in_selected_network: &active_document.selection_network_path == breadcrumb_network_path,
previewed_node,
thumbnails: active_document.node_graph_handler.thumbnails.clone()
};
let opacity = active_document.graph_fade_artwork_percentage;
let node_graph_overlay_node = generate_node_graph_overlay(node_graph_render_data, opacity);

View File

@@ -4,7 +4,7 @@ pub const EXPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
pub const EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP: u32 = 120;
pub const IMPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120;
pub const TOOLTIP_DELAY: u32 = 800;
pub const INPUT_TOOLTIP_DELAY: u64 = 800;
// VIEWPORT
pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = (1. / 600.) * 3.;

View File

@@ -59,6 +59,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(EventMessageDiscriminant::AnimationFrame)),
MessageDiscriminant::Animation(AnimationMessageDiscriminant::IncrementFrameCounter),
MessageDiscriminant::Defer(DeferMessageDiscriminant::CheckDeferredMessages),
];
// TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best.
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];

View File

@@ -1,15 +1,11 @@
use std::{
collections::BTreeMap,
ops::Bound,
time::{Duration, Instant},
};
use std::collections::BTreeMap;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct DeferMessageContext<'a> {
pub portfolio: &'a PortfolioMessageHandler,
pub time: Instant,
pub time: u64,
}
#[derive(Debug, Default, ExtractField)]
@@ -17,7 +13,7 @@ pub struct DeferMessageHandler {
after_graph_run: HashMap<DocumentId, Vec<(u64, Message)>>,
after_viewport_resize: Vec<Message>,
current_graph_submission_id: u64,
after_time_elapsed: BTreeMap<Instant, Message>,
after_time_elapsed: BTreeMap<u64, Message>,
}
#[message_handler_data]
@@ -58,11 +54,11 @@ impl MessageHandler<DeferMessage, DeferMessageContext<'_>> for DeferMessageHandl
}
}
DeferMessage::RequestDeferredMessage { timeout, message } => {
self.after_time_elapsed.insert(context.time + timeout, *message);
self.after_time_elapsed.insert(context.time + timeout.as_millis() as u64, *message);
}
DeferMessage::CheckDeferredMessages => {
let after_current_time = self.after_time_elapsed.split_off((Bound::Unbounded, Bound::Excluded(context.time)));
for (_, message) in std::mem::replace(self.after_time_elapsed, after_current_time) {
let after_current_time = self.after_time_elapsed.split_off(&context.time);
for (_, message) in std::mem::replace(&mut self.after_time_elapsed, after_current_time) {
responses.add(message);
}
}

View File

@@ -175,10 +175,6 @@ pub enum FrontendMessage {
#[serde(rename = "exportIndex")]
index: Option<usize>,
},
UpdateLayerWidths {
#[serde(rename = "layerWidths")]
layer_widths: HashMap<NodeId, u32>,
},
UpdateDialogButtons {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
@@ -269,7 +265,6 @@ pub enum FrontendMessage {
UpdateMouseCursor {
cursor: MouseCursorIcon,
},
RequestNativeNodeGraphRender,
UpdateNativeNodeGraphSVG {
#[serde(rename = "svgString")]
svg_string: String,

View File

@@ -4,7 +4,7 @@ use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*;
use glam::DVec2;
use std::time::{Duration, Instant};
use std::time::Duration;
#[derive(ExtractField)]
pub struct InputPreprocessorMessageContext {
@@ -14,7 +14,7 @@ pub struct InputPreprocessorMessageContext {
#[derive(Debug, Default, ExtractField)]
pub struct InputPreprocessorMessageHandler {
pub frame_time: FrameTimeInfo,
pub time: Instant,
pub time: u64,
pub keyboard: KeyStates,
pub mouse: MouseState,
pub viewport_bounds: ViewportBounds,
@@ -43,6 +43,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
.into(),
],
});
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
}
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
@@ -114,7 +115,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
}
InputPreprocessorMessage::CurrentTime { timestamp } => {
responses.add(AnimationMessage::SetTime { time: timestamp as f64 });
self.time = Instant::from(timestamp);
self.time = timestamp;
self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
}
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {

View File

@@ -476,6 +476,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
}
DocumentMessage::Escape => {
if self.node_graph_handler.drag_start.is_some() {
@@ -504,6 +505,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
}
responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
}
DocumentMessage::FlipSelectedLayers { flip_axis } => {
let scale = match flip_axis {

View File

@@ -3,7 +3,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
use crate::messages::prelude::*;
use glam::IVec2;
use glam::{DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
@@ -112,6 +112,7 @@ pub enum NodeGraphMessage {
PointerOutsideViewport {
shift: Key,
},
UpdateNodeGraphTopRight,
ShakeNode,
RemoveImport {
import_index: usize,
@@ -215,7 +216,9 @@ pub enum NodeGraphMessage {
SetLockedOrVisibilitySideEffects {
node_ids: Vec<NodeId>,
},
TryDisplayTooltip,
TryDisplayTooltip {
initial_position: DVec2,
},
UpdateBoxSelection,
UpdateImportsExports,
UpdateLayerPanel,

View File

@@ -1,6 +1,6 @@
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart};
use super::{document_node_definitions, node_properties};
use crate::consts::{GRID_SIZE, TOOLTIP_DELAY};
use crate::consts::*;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
@@ -30,6 +30,7 @@ use graphene_std::*;
use kurbo::{DEFAULT_ACCURACY, Shape};
use renderer::Quad;
use std::cmp::Ordering;
use std::time::Duration;
#[derive(Debug, ExtractField)]
pub struct NodeGraphMessageContext<'a> {
@@ -1149,19 +1150,21 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
.unwrap_or_else(|| modify_import_export.reorder_imports_exports.input_ports().count() + 1),
);
responses.add(FrontendMessage::UpdateExportReorderIndex { index: self.end_index });
} else if !self.hovering_input && !self.hovering_output {
} else if !self.hovering_input && !self.hovering_output && !self.hovering_node {
if network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
self.hovering_input = true;
responses.add(DeferMessage::RequestDeferredMessage {
message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()),
timeout: Duration::from_millis(TOOLTIP_DELAY),
message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()),
timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY),
});
}
if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
self.hovering_output = true;
} else if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
responses.add(DeferMessage::RequestDeferredMessage {
message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()),
timeout: Duration::from_millis(TOOLTIP_DELAY),
message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()),
timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY),
})
} else if network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
responses.add(DeferMessage::RequestDeferredMessage {
message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()),
timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY),
})
}
} else if self.hovering_input {
@@ -1174,16 +1177,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.hovering_output = false;
responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() });
}
} else if !self.hovering_node {
if network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) {
self.hovering_node = true;
responses.add(DeferMessage::RequestDeferredMessage {
message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()),
timeout: Duration::from_millis(TOOLTIP_DELAY),
})
}
} else if self.hovering_node {
if !network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) {
if !network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
self.hovering_node = false;
responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() });
}
@@ -1449,6 +1444,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.auto_panning.stop(&messages, responses);
}
}
NodeGraphMessage::UpdateNodeGraphTopRight => {
network_interface.set_node_graph_width(ipp.viewport_bounds.size().x, breadcrumb_network_path);
responses.add(NodeGraphMessage::UpdateImportsExports);
}
NodeGraphMessage::ShakeNode => {
let Some(drag_start) = &self.drag_start else {
log::error!("Drag start should be initialized when shaking a node");
@@ -1849,8 +1848,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] });
}
NodeGraphMessage::TryDisplayTooltip => {
if let Some(input) = network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path) {
NodeGraphMessage::TryDisplayTooltip { initial_position } => {
if initial_position != ipp.mouse.position {
return;
} else if let Some(input) = network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path) {
let text = network_interface.input_tooltip_text(&input, breadcrumb_network_path);
if let Some(position) = network_interface.input_position(&input, breadcrumb_network_path) {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
@@ -1862,6 +1863,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
y: position.y as i32,
};
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
self.hovering_input = true;
}
} else if let Some(output) = network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path) {
let text = network_interface.output_tooltip_text(&output, breadcrumb_network_path);
@@ -1876,25 +1878,29 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
y: position.y as i32,
};
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
self.hovering_output = true;
}
} else if let Some(node_id) = network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) {
let text = network_interface.node_tooltip_text(&node_id, breadcrumb_network_path);
if let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return;
};
let position = network_metadata
.persistent_metadata
.navigation_metadata
.node_graph_to_viewport
.transform_point2(DVec2::new(position.x as f64 * 24., position.y as f64 * 24.));
let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) else {
log::error!("Could not get position from node: {node_id}");
return;
};
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return;
};
let position = network_metadata
.persistent_metadata
.navigation_metadata
.node_graph_to_viewport
.transform_point2(DVec2::new(position.x as f64 * 24., position.y as f64 * 24.));
let xy = FrontendXY {
x: position.x as i32,
y: position.y as i32,
};
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
}
let xy = FrontendXY {
x: position.x as i32,
y: position.y as i32,
};
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
self.hovering_node = true;
}
}
NodeGraphMessage::SetPinned { node_id, pinned } => {
@@ -2655,6 +2661,9 @@ impl Default for NodeGraphMessageHandler {
reordering_import: None,
end_index: None,
thumbnails: HashMap::new(),
hovering_input: false,
hovering_output: false,
hovering_node: false,
}
}
}

View File

@@ -1,185 +1,6 @@
use graph_craft::document::NodeId;
use std::borrow::Cow;
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType {
#[default]
General,
Number,
Artboard,
Graphic,
Raster,
Vector,
Color,
Gradient,
Typography,
}
impl FrontendGraphDataType {
pub fn from_type(input: &Type) -> Self {
match TaggedValue::from_type_or_none(input) {
TaggedValue::U32(_)
| TaggedValue::U64(_)
| TaggedValue::F32(_)
| TaggedValue::F64(_)
| TaggedValue::DVec2(_)
| TaggedValue::F64Array4(_)
| TaggedValue::VecF64(_)
| TaggedValue::VecDVec2(_)
| TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::Artboard(_) => Self::Artboard,
TaggedValue::Graphic(_) => Self::Graphic,
TaggedValue::Raster(_) => Self::Raster,
TaggedValue::Vector(_) => Self::Vector,
TaggedValue::Color(_) => Self::Color,
TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => Self::Gradient,
TaggedValue::String(_) => Self::Typography,
_ => Self::General,
}
}
pub fn displayed_type(type_source: &TypeSource) -> Self {
match type_source.compiled_nested_type() {
Some(nested_type) => Self::from_type(&nested_type),
None => Self::General,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendXY {
pub x: i32,
pub y: i32,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphInput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
#[serde(rename = "resolvedType")]
pub resolved_type: String,
pub name: String,
pub description: String,
/// Either "nothing", "import index {index}", or "{node name} output {output_index}".
#[serde(rename = "connectedToString")]
pub connected_to: String,
/// Used to render the upstream node once this node is rendered
#[serde(rename = "connectedToNode")]
pub connected_to_node: Option<NodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphOutput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: 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)]
pub struct FrontendExport {
pub port: FrontendGraphInput,
pub wire: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExports {
/// If the primary export is not visible, then it is None.
pub exports: Vec<Option<FrontendExport>>,
#[serde(rename = "previewWire")]
pub preview_wire: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendImport {
pub port: FrontendGraphOutput,
pub wires: Vec<String>,
}
// Metadata that is common to nodes and layers
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeMetadata {
#[serde(rename = "nodeId")]
pub node_id: NodeId,
// TODO: Remove and replace with popup manager system
#[serde(rename = "canBeLayer")]
pub can_be_layer: bool,
#[serde(rename = "displayName")]
pub display_name: String,
pub selected: bool,
// Used to get the description, which is stored in a global hashmap
pub reference: Option<String>,
// Reduces opacity of node/hidden eye icon
pub visible: bool,
// The svg string for each input
// pub wires: Vec<Option<String>>,
pub errors: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNode {
// pub position: FrontendNodePosition,
pub position: FrontendXY,
pub inputs: Vec<Option<FrontendGraphInput>>,
pub outputs: Vec<Option<FrontendGraphOutput>>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendLayer {
#[serde(rename = "bottomInput")]
pub bottom_input: FrontendGraphInput,
#[serde(rename = "sideInput")]
pub side_input: Option<FrontendGraphInput>,
pub output: FrontendGraphOutput,
// pub position: FrontendLayerPosition,
pub position: FrontendXY,
pub locked: bool,
#[serde(rename = "chainWidth")]
pub chain_width: u32,
#[serde(rename = "layerHasLeftBorderGap")]
pub layer_has_left_border_gap: bool,
#[serde(rename = "primaryInputConnectedToLayer")]
pub primary_input_connected_to_layer: bool,
#[serde(rename = "primaryOutputConnectedToLayer")]
pub primary_output_connected_to_layer: bool,
}
// // Should be an enum but those are hard to serialize/deserialize to TS
// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
// pub struct FrontendNodePosition {
// pub absolute: Option<FrontendXY>,
// pub chain: Option<bool>,
// }
// // Should be an enum but those are hard to serialize/deserialize to TS
// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
// pub struct FrontendLayerPosition {
// pub absolute: Option<FrontendXY>,
// pub stack: Option<u32>,
// }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeOrLayer {
pub node: Option<FrontendNode>,
pub layer: Option<FrontendLayer>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeToRender {
pub metadata: FrontendNodeMetadata,
#[serde(rename = "nodeOrLayer")]
pub node_or_layer: FrontendNodeOrLayer,
//TODO: Remove
pub wires: Vec<(String, bool, FrontendGraphDataType)>,
}
use graphene_std::uuid::NodeId;
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType {

View File

@@ -2675,6 +2675,16 @@ impl NodeNetworkInterface {
self.unload_modify_import_export(network_path);
}
pub fn set_node_graph_width(&mut self, node_graph_width: f64, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network in set_transform");
return;
};
network_metadata.persistent_metadata.navigation_metadata.node_graph_width = node_graph_width;
self.unload_import_export_ports(network_path);
self.unload_modify_import_export(network_path);
}
pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) {
let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else {
log::error!("Could not get node in vector_modification");
@@ -5853,7 +5863,7 @@ pub enum LayerClickTargetTypes {
// Preview,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct NavigationMetadata {
/// The current pan, and zoom state of the viewport's view of the node graph.
/// Ensure `DocumentMessage::UpdateDocumentTransform` is called when the pan, zoom, or transform changes.
@@ -5861,21 +5871,10 @@ pub struct NavigationMetadata {
// TODO: Remove and replace with calculate_offset_transform from the node_graph_ptz. This will be difficult since it requires both the navigation message handler and the IPP
/// Transform from node graph space to viewport space.
pub node_graph_to_viewport: DAffine2,
/// Top right of the node graph in viewport space
// TODO: Eventually replace with footprint
/// The width of the node graph in viewport space
#[serde(default)]
pub node_graph_top_right: DVec2,
}
impl Default for NavigationMetadata {
fn default() -> NavigationMetadata {
// Default PTZ and transform
NavigationMetadata {
node_graph_ptz: PTZ::default(),
node_graph_to_viewport: DAffine2::IDENTITY,
// TODO: Eventually replace with footprint
node_graph_top_right: DVec2::ZERO,
}
}
pub node_graph_width: f64,
}
// PartialEq required by message handlers

View File

@@ -1,19 +1,19 @@
use glam::{DVec2, IVec2};
use graph_craft::proto::GraphErrors;
use graphene_std::uuid::NodeId;
use graphene_std::{
node_graph_overlay::types::{
FrontendExport, FrontendExports, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendNodeToRender,
FrontendXY,
},
uuid::NodeId,
};
use kurbo::BezPath;
use crate::{
consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP},
messages::portfolio::document::{
node_graph::utility_types::{
FrontendExport, FrontendExports, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer,
FrontendNodeToRender, FrontendXY,
},
utility_types::{
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
wires::{GraphWireStyle, build_vector_wire},
},
messages::portfolio::document::utility_types::{
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
wires::{GraphWireStyle, build_vector_wire},
},
};
@@ -123,7 +123,7 @@ impl NodeNetworkInterface {
(
wire,
self.wire_is_thick(&InputConnector::node(node_id, input_index), network_path),
FrontendGraphDataType::displayed_type(&self.input_type(&InputConnector::node(node_id, input_index), network_path)),
self.input_type(&InputConnector::node(node_id, input_index), network_path).displayed_type(),
)
})
})
@@ -210,8 +210,12 @@ impl NodeNetworkInterface {
}
}
};
let connected = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(output_connector))
.is_some_and(|downstream| downstream.len() > 0);
let data_type = output_type.displayed_type();
Some(FrontendGraphOutput { data_type, name })
Some(FrontendGraphOutput { data_type, name, connected })
}
pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 {
@@ -386,19 +390,18 @@ impl NodeNetworkInterface {
let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y));
let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.);
let viewport_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right;
let target_viewport_top_right = DVec2::new(
viewport_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64,
viewport_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64,
);
let viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width;
let target_viewport_top_right = DVec2::new(viewport_width - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64);
// An offset from the right edge in viewport pixels
let node_graph_pixel_offset_top_right = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_right);
// A 5x5 grid offset from the right corner
let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(viewport_top_right) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(DVec2::new(viewport_width, 0.)) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
// The inner bound of the export is the highest/furthest right of the two offsets
// The inner bound of the export is the highest/furthest right of the two offsets.
// When zoomed out this keeps it a constant grid space away from the edge, but when zoomed in it prevents the exports from getting too far in
let top_right_inner_bound = DVec2::new(
node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x),
node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y),
@@ -561,6 +564,6 @@ impl NodeNetworkInterface {
return String::new();
};
format!("{display_name}\nReference: {reference}\n\n{description}")
format!("{display_name}\n\nReference: {reference:?}\n\n{description}")
}
}

View File

@@ -1,5 +1,5 @@
use glam::{DVec2, IVec2};
use graphene_std::vector::misc::dvec2_to_point;
use graphene_std::{node_graph_overlay::types::FrontendGraphDataType, vector::misc::dvec2_to_point};
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]

View File

@@ -562,7 +562,7 @@ struct PathToolData {
saved_selection_before_handle_drag: HashMap<LayerNodeIdentifier, (HashSet<ManipulatorPointId>, HashSet<SegmentId>)>,
handle_drag_toggle: bool,
saved_points_before_anchor_convert_smooth_sharp: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>,
last_click_time: Instant,
last_click_time: u64,
dragging_state: DraggingState,
angle: f64,
pivot_gizmo: PivotGizmo,

View File

@@ -12,6 +12,7 @@ use graphene_std::text::FontCache;
use graphene_std::transform::Footprint;
use graphene_std::vector::Vector;
use graphene_std::wasm_application_io::RenderOutputType;
use graphene_std::Graphic;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
mod runtime_io;
@@ -121,14 +122,7 @@ impl NodeGraphExecutor {
/// Update the cached network if necessary.
fn update_node_graph(&mut self, document: &mut DocumentMessageHandler, node_to_inspect: Option<NodeId>, ignore_hash: bool) -> Result<(), String> {
let mut network = document.network_interface.document_network().clone();
if let Some(mut node_graph_overlay_node) = document.node_graph_handler.node_graph_overlay.clone() {
let node_graph_overlay_id = NodeId::new();
let new_export = NodeInput::node(node_graph_overlay_id, 0);
let old_export = std::mem::replace(&mut network.exports[0], new_export);
node_graph_overlay_node.inputs[0] = old_export;
network.nodes.insert(node_graph_overlay_id, node_graph_overlay_node);
}
let network = document.network_interface.document_network().clone();
let network_hash = network.current_hash();
// Refresh the graph when it changes or the inspect node changes
if network_hash != self.node_graph_hash || self.previous_node_to_inspect != node_to_inspect || ignore_hash {

View File

@@ -299,7 +299,7 @@
style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24}
style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index}
>
{#if frontendOutput.connectedTo.length > 0}
{#if frontendOutput.connected}
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color)" />
{:else}
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color-dim)" />

View File

@@ -157,8 +157,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
// TODO: This would allow it to properly decide to act on removing hover focus from something that was hovered in the canvas before moving over the GUI.
// TODO: Further explanation: https://github.com/GraphiteEditor/Graphite/pull/623#discussion_r866436197
const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
const inGraphOverlay = get(document).graphViewOverlayOpen;
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
if (!viewportPointerInteractionOngoing && inFloatingMenu) return;
const modifiers = makeKeyboardModifiersBitfield(e);
if (detectShake(e)) editor.handle.onMouseShake(e.clientX, e.clientY, e.buttons, modifiers);

View File

@@ -127,17 +127,16 @@ export class UpdateNodeThumbnail extends JsMessage {
readonly value!: string;
}
export class UpdateOpenDocumentsList extends JsMessage {
@Type(() => OpenDocument)
readonly openDocuments!: OpenDocument[];
}
export class UpdateTooltip extends JsMessage {
readonly position!: XY | undefined;
readonly text!: string;
}
export class UpdateOpenDocumentsList extends JsMessage {
@Type(() => FrontendDocumentDetails)
readonly openDocuments!: FrontendDocumentDetails[];
}
export class WirePathInProgress {
readonly wire!: string;
readonly thick!: boolean;
@@ -148,35 +147,28 @@ export class UpdateWirePathInProgress extends JsMessage {
readonly wirePathInProgress!: WirePathInProgress | undefined;
}
// Allows the auto save system to use a string for the id rather than a BigInt.
// IndexedDb does not allow for BigInts as primary keys.
// TypeScript does not allow subclasses to change the type of class variables in subclasses.
// It is an abstract class to point out that it should not be instantiated directly.
export abstract class DocumentDetails {
export class OpenDocument {
readonly id!: bigint;
@Type(() => DocumentDetails)
readonly details!: DocumentDetails;
get displayName(): string {
return this.details.displayName;
}
}
export class DocumentDetails {
readonly name!: string;
readonly isAutoSaved!: boolean;
readonly isSaved!: boolean;
// This field must be provided by the subclass implementation
// readonly id!: bigint | string;
get displayName(): string {
return `${this.name}${this.isSaved ? "" : "*"}`;
}
}
export class FrontendDocumentDetails extends DocumentDetails {
readonly id!: bigint;
}
=======
export class FrontendDocumentDetails extends DocumentDetails {
readonly id!: bigint;
}
>>>>>>> 17a1a3d5 (Complete separating node rendering from imports/exports)
export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color";
export class FrontendGraphInput {
@@ -198,11 +190,7 @@ export class FrontendGraphOutput {
readonly name!: string;
readonly description!: string;
readonly resolvedType!: string;
readonly connectedTo!: string[];
readonly connected!: boolean;
}
export class FrontendExport {
@@ -322,21 +310,20 @@ export class WireUpdate {
readonly wirePathUpdate!: WirePath | undefined;
}
export class IndexedDbDocumentDetails extends DocumentDetails {
export class TriggerPersistenceWriteDocument extends JsMessage {
// Use a string since IndexedDB can not use BigInts for keys
@Transform(({ value }: { value: bigint }) => value.toString())
id!: string;
}
documentId!: string;
export class TriggerIndexedDbWriteDocument extends JsMessage {
document!: string;
@Type(() => IndexedDbDocumentDetails)
details!: IndexedDbDocumentDetails;
@Type(() => DocumentDetails)
details!: DocumentDetails;
version!: string;
}
export class TriggerIndexedDbRemoveDocument extends JsMessage {
export class TriggerPersistenceRemoveDocument extends JsMessage {
// Use a string since IndexedDB can not use BigInts for keys
@Transform(({ value }: { value: bigint }) => value.toString())
documentId!: string;
@@ -1469,7 +1456,6 @@ export class WidgetDiffUpdate extends JsMessage {
layoutTarget!: unknown;
// TODO: Replace `any` with correct typing
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
diff!: WidgetDiff[];
}
@@ -1504,7 +1490,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
return targetLayout;
}
// This is a path traversal so we can assume from the backend that it exists
if (targetLayout && "action" in targetLayout) return targetLayout.children![index];
return targetLayout?.[index];
@@ -1525,7 +1510,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
diffObject.length = 0;
}
// Remove all of the keys from the old object
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
// Assign keys to the new object
@@ -1558,7 +1542,6 @@ export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSect
}
// Unpacking rust types to more usable type in the frontend
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
return diffs.map((diff) => {
const { widgetPath, newValue } = diff;
@@ -1577,7 +1560,6 @@ function createWidgetDiff(diffs: any[]): WidgetDiff[] {
}
// Unpacking a layout group
function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.column) {
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
@@ -1635,7 +1617,6 @@ export class UpdateMenuBarLayout extends JsMessage {
layoutTarget!: unknown;
// TODO: Replace `any` with correct typing
@Transform(({ value }: { value: any }) => createMenuLayout(value))
layout!: MenuBarEntry[];
}
@@ -1658,7 +1639,6 @@ function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] {
children: createMenuLayoutRecursive(entry.children),
}));
}
function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
return children.map((groups) =>
groups.map((entry) => ({
@@ -1671,7 +1651,6 @@ function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
}
// `any` is used since the type of the object should be known from the Rust side
type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
type MessageMaker = typeof JsMessage | JSMessageFactory;
@@ -1691,8 +1670,8 @@ export const messageMakers: Record<string, MessageMaker> = {
TriggerFetchAndOpenDocument,
TriggerFontLoad,
TriggerImport,
TriggerIndexedDbRemoveDocument,
TriggerIndexedDbWriteDocument,
TriggerPersistenceRemoveDocument,
TriggerPersistenceWriteDocument,
TriggerLoadFirstAutoSaveDocument,
TriggerLoadPreferences,
TriggerLoadRestAutoSaveDocuments,

View File

@@ -5,21 +5,21 @@ import {
type FrontendSelectionBox,
type FrontendClickTargets,
type ContextMenuInformation,
type FrontendNodeToRender,
type FrontendNodeType,
type WirePathInProgress,
type XY,
SendUIMetadata,
UpdateClickTargets,
UpdateContextMenuInformation,
UpdateImportReorderIndex,
UpdateExportReorderIndex,
UpdateImportsExports,
UpdateLayerWidths,
UpdateNativeNodeGraphSVG,
UpdateNodeThumbnail,
UpdateWirePathInProgress,
UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform,
UpdateTooltip,
} from "@graphite/messages";
export function createNodeGraphState(editor: Editor) {
@@ -37,11 +37,8 @@ export function createNodeGraphState(editor: Editor) {
nodeTypes: [] as FrontendNodeType[],
nodeDescriptions: new Map<string, string>(),
// Data that will be moved into the node graph to be rendered natively
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
opacity: 0.8,
inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined,
tooltipPosition: undefined as XY | undefined,
tooltipText: "test",
// Data that will be passed in the context
thumbnails: new Map<bigint, string>(),
@@ -117,7 +114,13 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateTooltip, (updateTooltip) => {
update((state) => {
state.tooltipPosition = updateTooltip.position;
state.tooltipText = updateTooltip.text;
return state;
});
});
return {
subscribe,
};

View File

@@ -5,11 +5,8 @@
// on the dispatcher messaging system and more complex Rust data types.
//
use crate::helpers::translate_key;
#[cfg(not(feature = "native"))]
use crate::wasm_node_graph_ui_executor::WasmNodeGraphUIExecutor;
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER, WASM_NODE_GRAPH_EXECUTOR};
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
use editor::consts::FILE_EXTENSION;
use editor::dispatcher::EditorOutput;
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds};
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -175,13 +172,9 @@ impl EditorHandle {
pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self {
let editor = Editor::new();
let editor_handle = EditorHandle { frontend_message_handler_callback };
let node_graph_executor = WasmNodeGraphUIExecutor::new();
if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() {
log::error!("Attempted to initialize the editor more than once");
}
if WASM_NODE_GRAPH_EXECUTOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(node_graph_executor))).is_none() {
log::error!("Attempted to initialize the editor more than once");
}
if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() {
log::error!("Attempted to initialize the editor handle more than once");
}
@@ -202,12 +195,28 @@ impl EditorHandle {
#[cfg(not(feature = "native"))]
fn dispatch<T: Into<Message>>(&self, message: T) {
// Process no further messages after a crash to avoid spamming the console
use crate::MESSAGE_BUFFER;
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
return;
}
let _ = editor(|editor| {
self.process_messages(std::iter::once(message.into()), editor);
// Get the editor, dispatch the message, and store the `FrontendMessage` queue response
let frontend_messages = EDITOR.with(|editor| {
let mut guard = editor.try_lock();
let Ok(Some(editor)) = guard.as_deref_mut() else {
// Enqueue messages which can't be procssed currently
MESSAGE_BUFFER.with_borrow_mut(|buffer| buffer.push(message.into()));
return vec![];
};
editor.handle_message(message)
});
// Send each `FrontendMessage` to the JavaScript frontend
for message in frontend_messages.into_iter() {
self.send_frontend_message_to_js(message);
}
}
#[cfg(feature = "native")]
@@ -220,37 +229,6 @@ impl EditorHandle {
crate::native_communcation::send_message_to_cef(serialized_message)
}
// Messages can come from the runtime, browser, or a timed callback. This processes them in the editor and does all the side effects
// Like updating the frontend and node graph ui network. Some side effects are deduplicated and produce other side effects.
fn process_messages(&self, messages: impl IntoIterator<Item = Message>, editor_param: &mut Editor) {
// Get the editor, dispatch the message, and store the `FrontendMessage` queue response
for output in messages.into_iter().flat_map(|message| editor_param.handle_message(message)).collect::<Vec<_>>() {
match output {
EditorOutput::RequestNativeNodeGraphRender { compilation_request } => {
let res = executor(|executor| executor.compilation_request(compilation_request));
if let Err(_) = res {
log::error!("Could not borrow executor in process_messages_in_editor");
}
}
EditorOutput::RequestDeferredMessage { message, timeout } => {
let callback = Closure::once_into_js(move || {
editor_and_handle(|editor, handle| {
handle.process_messages(std::iter::once(*message), editor);
});
});
window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(callback.as_ref().unchecked_ref(), timeout.as_millis() as i32)
.unwrap();
}
EditorOutput::FrontendMessage { frontend_message } => {
self.send_frontend_message_to_js(frontend_message);
}
}
}
}
// ========================================================================
// Add additional JS -> Rust wrapper functions below as needed for calling
// the backend from the web frontend.
@@ -280,20 +258,6 @@ impl EditorHandle {
#[cfg(not(feature = "native"))]
wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation());
// Poll the UI node graph
#[cfg(not(feature = "native"))]
let result = editor(|editor| {
let node_graph_response = executor(|executor| executor.poll_node_graph_ui_evaluation(editor));
match node_graph_response {
Ok(node_graph_ui_messages) => handle(|handle| handle.process_messages(node_graph_ui_messages, editor)),
Err(_) => log::error!("Could not get executor in frame loop"),
}
});
if let Err(_) = result {
log::error!("Could not get editor in frame loop");
}
if !EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
handle(|handle| {
// Process all messages that have been queued up
@@ -496,6 +460,7 @@ impl EditorHandle {
document_is_saved,
document_serialized_content,
to_front,
select_after_open: false,
};
self.dispatch(message);
}
@@ -1000,49 +965,39 @@ fn set_timeout(f: &Closure<dyn FnMut()>, delay: Duration) {
/// Provides access to the `Editor` by calling the given closure with it as an argument.
#[cfg(not(feature = "native"))]
fn editor<T>(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> Result<T, ()> {
fn editor<T: Default>(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> T {
EDITOR.with(|editor| {
let mut guard = editor.try_lock();
let Ok(Some(editor)) = guard.as_deref_mut() else {
return Err(());
};
Ok(callback(editor))
})
}
#[cfg(not(feature = "native"))]
fn executor<T>(callback: impl FnOnce(&mut WasmNodeGraphUIExecutor) -> T) -> Result<T, ()> {
WASM_NODE_GRAPH_EXECUTOR.with(|executor| {
let mut guard = executor.try_lock();
let Ok(Some(executor)) = guard.as_deref_mut() else {
return Err(());
log::error!("Failed to borrow editor");
return T::default();
};
Ok(callback(executor))
})
}
/// Provides access to the `EditorHandle` by calling the given closure with them as arguments.
pub(crate) fn handle(callback: impl FnOnce(&mut EditorHandle)) {
EDITOR_HANDLE.with(|editor_handle| {
let mut guard = editor_handle.try_lock();
let Ok(Some(editor_handle)) = guard.as_deref_mut() else {
return log::error!("Failed to borrow handle");
};
// Call the closure with the editor and its handle
callback(editor_handle)
callback(editor)
})
}
/// Provides access to the `Editor` and its `EditorHandle` by calling the given closure with them as arguments.
#[cfg(not(feature = "native"))]
pub(crate) fn editor_and_handle(callback: impl FnOnce(&mut Editor, &mut EditorHandle)) {
let _ = handle(|editor_handle| {
let _ = editor(|editor| {
handle(|editor_handle| {
editor(|editor| {
// Call the closure with the editor and its handle
callback(editor, editor_handle);
});
})
});
}
/// Provides access to the `EditorHandle` by calling the given closure with them as arguments.
pub(crate) fn handle(callback: impl FnOnce(&mut EditorHandle)) {
EDITOR_HANDLE.with(|editor_handle| {
let mut guard = editor_handle.try_lock();
let Ok(Some(editor_handle)) = guard.as_deref_mut() else {
log::error!("Failed to borrow editor handle");
return;
};
// Call the closure with the editor and its handle
callback(editor_handle);
});
}
@@ -1071,11 +1026,15 @@ async fn poll_node_graph_evaluation() {
crate::NODE_GRAPH_ERROR_DISPLAYED.store(false, Ordering::SeqCst);
}
handle.process_messages(messages, editor);
// Send each `FrontendMessage` to the JavaScript frontend
for response in messages.into_iter().flat_map(|message| editor.handle_message(message)) {
handle.send_frontend_message_to_js(response);
}
// If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches
});
}
fn auto_save_all_documents() {
// Process no further messages after a crash to avoid spamming the console
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {

View File

@@ -61,7 +61,7 @@ pub fn panic_hook(info: &panic::PanicHookInfo) {
/text>"#
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
.to_string();
handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::UpdateDocumentArtwork { svg: error });
handle.send_frontend_message_to_js(FrontendMessage::UpdateDocumentArtwork { svg: error });
});
}
@@ -75,7 +75,7 @@ pub fn panic_hook(info: &panic::PanicHookInfo) {
EDITOR_HANDLE.with(|editor_handle| {
let mut guard = editor_handle.lock();
if let Ok(Some(handle)) = guard.as_deref_mut() {
handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() });
handle.send_frontend_message_to_js(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() });
}
});
}

View File

@@ -11,7 +11,7 @@ pub fn receive_native_message(buffer: ArrayBuffer) {
Ok(messages) => {
let callback = move |handle: &mut EditorHandle| {
for message in messages {
handle.send_frontend_message_to_js_rust_proxy(message);
handle.send_frontend_message_to_js(message);
}
};
editor_api::handle(callback);

View File

@@ -162,13 +162,7 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
}
}
// for text_row in node_text.iter_mut() {
// text_row.element.style.fill = Fill::Solid(Color::WHITE);
// }
let node_text_row = TableRow::new_from_element(Graphic::Vector(node_text));
// node_text_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x + 8., y + 8.)));
// log::debug!("node_text_row {:?}", node_text_row.transform);
node_table.push(node_text_row);
// Add black clipping path to view text in node
@@ -193,12 +187,12 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
ports_table.push(row);
}
if let Some(primary_output) = &frontend_node.primary_output {
let mut row = port_row(&primary_output.data_type, true);
let mut row = port_row(&primary_output.data_type, primary_output.connected);
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12.));
ports_table.push(row);
}
for (index, secondary_output) in frontend_node.secondary_outputs.iter().enumerate() {
let mut row = port_row(&secondary_output.data_type, true);
let mut row = port_row(&secondary_output.data_type, secondary_output.connected);
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12. + GRID_SIZE * (index + 1) as f64));
ports_table.push(row);
}
@@ -345,7 +339,12 @@ pub fn draw_layers(nodes: &mut NodeGraphOverlayData) -> (Table<Graphic>, Table<G
}
let top_port = BezPath::from_svg("M0,6.953l2.521,-1.694a2.649,2.649,0,0,1,2.959,0l2.52,1.694v5.047h-8z").unwrap();
let mut vector = Vector::from_bezpath(top_port);
vector.style.fill = Fill::Solid(frontend_layer.output.data_type.data_color());
vector.style.fill = if frontend_layer.output.connected {
Fill::Solid(frontend_layer.output.data_type.data_color())
} else {
Fill::Solid(frontend_layer.output.data_type.data_color_dim())
};
let mut top_port = TableRow::new_from_element(vector);
top_port.transform = DAffine2::from_translation(DVec2::new(frontend_layer.position.x as f64 * 24. + GRID_SIZE * 2. - 4., layer_position.y - 12.));
ports_table.push(top_port);

View File

@@ -181,6 +181,7 @@ pub struct FrontendGraphOutput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
pub connected: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]

View File

@@ -302,6 +302,7 @@ impl Render for Graphic {
Graphic::RasterGPU(table) => table.to_graphic(),
Graphic::Color(table) => table.to_graphic(),
Graphic::Gradient(table) => table.to_graphic(),
Graphic::Typography(table) => table.to_graphic(),
}
}
@@ -1696,6 +1697,10 @@ impl Render for Table<Typography> {
}
}
}
fn to_graphic(self) -> Graphic {
Graphic::Typography(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]