mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
Fix misc errors and cleanup after rebases
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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.;
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 } => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user