mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-28 02:18:11 +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,
|
open: active_document.graph_view_overlay_open,
|
||||||
in_selected_network: &active_document.selection_network_path == breadcrumb_network_path,
|
in_selected_network: &active_document.selection_network_path == breadcrumb_network_path,
|
||||||
previewed_node,
|
previewed_node,
|
||||||
|
thumbnails: active_document.node_graph_handler.thumbnails.clone()
|
||||||
};
|
};
|
||||||
let opacity = active_document.graph_fade_artwork_percentage;
|
let opacity = active_document.graph_fade_artwork_percentage;
|
||||||
let node_graph_overlay_node = generate_node_graph_overlay(node_graph_render_data, opacity);
|
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 EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP: u32 = 120;
|
||||||
pub const IMPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
|
pub const IMPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
|
||||||
pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120;
|
pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120;
|
||||||
pub const TOOLTIP_DELAY: u32 = 800;
|
pub const INPUT_TOOLTIP_DELAY: u64 = 800;
|
||||||
|
|
||||||
// VIEWPORT
|
// VIEWPORT
|
||||||
pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = (1. / 600.) * 3.;
|
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] = &[
|
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
|
||||||
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(EventMessageDiscriminant::AnimationFrame)),
|
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(EventMessageDiscriminant::AnimationFrame)),
|
||||||
MessageDiscriminant::Animation(AnimationMessageDiscriminant::IncrementFrameCounter),
|
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.
|
// 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"];
|
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
use std::{
|
use std::collections::BTreeMap;
|
||||||
collections::BTreeMap,
|
|
||||||
ops::Bound,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct DeferMessageContext<'a> {
|
pub struct DeferMessageContext<'a> {
|
||||||
pub portfolio: &'a PortfolioMessageHandler,
|
pub portfolio: &'a PortfolioMessageHandler,
|
||||||
pub time: Instant,
|
pub time: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, ExtractField)]
|
#[derive(Debug, Default, ExtractField)]
|
||||||
@@ -17,7 +13,7 @@ pub struct DeferMessageHandler {
|
|||||||
after_graph_run: HashMap<DocumentId, Vec<(u64, Message)>>,
|
after_graph_run: HashMap<DocumentId, Vec<(u64, Message)>>,
|
||||||
after_viewport_resize: Vec<Message>,
|
after_viewport_resize: Vec<Message>,
|
||||||
current_graph_submission_id: u64,
|
current_graph_submission_id: u64,
|
||||||
after_time_elapsed: BTreeMap<Instant, Message>,
|
after_time_elapsed: BTreeMap<u64, Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
@@ -58,11 +54,11 @@ impl MessageHandler<DeferMessage, DeferMessageContext<'_>> for DeferMessageHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeferMessage::RequestDeferredMessage { timeout, message } => {
|
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 => {
|
DeferMessage::CheckDeferredMessages => {
|
||||||
let after_current_time = self.after_time_elapsed.split_off((Bound::Unbounded, Bound::Excluded(context.time)));
|
let after_current_time = self.after_time_elapsed.split_off(&context.time);
|
||||||
for (_, message) in std::mem::replace(self.after_time_elapsed, after_current_time) {
|
for (_, message) in std::mem::replace(&mut self.after_time_elapsed, after_current_time) {
|
||||||
responses.add(message);
|
responses.add(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,10 +175,6 @@ pub enum FrontendMessage {
|
|||||||
#[serde(rename = "exportIndex")]
|
#[serde(rename = "exportIndex")]
|
||||||
index: Option<usize>,
|
index: Option<usize>,
|
||||||
},
|
},
|
||||||
UpdateLayerWidths {
|
|
||||||
#[serde(rename = "layerWidths")]
|
|
||||||
layer_widths: HashMap<NodeId, u32>,
|
|
||||||
},
|
|
||||||
UpdateDialogButtons {
|
UpdateDialogButtons {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
@@ -269,7 +265,6 @@ pub enum FrontendMessage {
|
|||||||
UpdateMouseCursor {
|
UpdateMouseCursor {
|
||||||
cursor: MouseCursorIcon,
|
cursor: MouseCursorIcon,
|
||||||
},
|
},
|
||||||
RequestNativeNodeGraphRender,
|
|
||||||
UpdateNativeNodeGraphSVG {
|
UpdateNativeNodeGraphSVG {
|
||||||
#[serde(rename = "svgString")]
|
#[serde(rename = "svgString")]
|
||||||
svg_string: String,
|
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::portfolio::utility_types::KeyboardPlatformLayout;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct InputPreprocessorMessageContext {
|
pub struct InputPreprocessorMessageContext {
|
||||||
@@ -14,7 +14,7 @@ pub struct InputPreprocessorMessageContext {
|
|||||||
#[derive(Debug, Default, ExtractField)]
|
#[derive(Debug, Default, ExtractField)]
|
||||||
pub struct InputPreprocessorMessageHandler {
|
pub struct InputPreprocessorMessageHandler {
|
||||||
pub frame_time: FrameTimeInfo,
|
pub frame_time: FrameTimeInfo,
|
||||||
pub time: Instant,
|
pub time: u64,
|
||||||
pub keyboard: KeyStates,
|
pub keyboard: KeyStates,
|
||||||
pub mouse: MouseState,
|
pub mouse: MouseState,
|
||||||
pub viewport_bounds: ViewportBounds,
|
pub viewport_bounds: ViewportBounds,
|
||||||
@@ -43,6 +43,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
|
||||||
}
|
}
|
||||||
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
@@ -114,7 +115,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
}
|
}
|
||||||
InputPreprocessorMessage::CurrentTime { timestamp } => {
|
InputPreprocessorMessage::CurrentTime { timestamp } => {
|
||||||
responses.add(AnimationMessage::SetTime { time: timestamp as f64 });
|
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));
|
self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
|
||||||
}
|
}
|
||||||
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
|
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);
|
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
|
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
|
||||||
}
|
}
|
||||||
DocumentMessage::Escape => {
|
DocumentMessage::Escape => {
|
||||||
if self.node_graph_handler.drag_start.is_some() {
|
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(NodeGraphMessage::SendGraph);
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
|
responses.add(NodeGraphMessage::UpdateNodeGraphTopRight);
|
||||||
}
|
}
|
||||||
DocumentMessage::FlipSelectedLayers { flip_axis } => {
|
DocumentMessage::FlipSelectedLayers { flip_axis } => {
|
||||||
let scale = match 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::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
|
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::IVec2;
|
use glam::{DVec2, IVec2};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput};
|
use graph_craft::document::{NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
@@ -112,6 +112,7 @@ pub enum NodeGraphMessage {
|
|||||||
PointerOutsideViewport {
|
PointerOutsideViewport {
|
||||||
shift: Key,
|
shift: Key,
|
||||||
},
|
},
|
||||||
|
UpdateNodeGraphTopRight,
|
||||||
ShakeNode,
|
ShakeNode,
|
||||||
RemoveImport {
|
RemoveImport {
|
||||||
import_index: usize,
|
import_index: usize,
|
||||||
@@ -215,7 +216,9 @@ pub enum NodeGraphMessage {
|
|||||||
SetLockedOrVisibilitySideEffects {
|
SetLockedOrVisibilitySideEffects {
|
||||||
node_ids: Vec<NodeId>,
|
node_ids: Vec<NodeId>,
|
||||||
},
|
},
|
||||||
TryDisplayTooltip,
|
TryDisplayTooltip {
|
||||||
|
initial_position: DVec2,
|
||||||
|
},
|
||||||
UpdateBoxSelection,
|
UpdateBoxSelection,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateLayerPanel,
|
UpdateLayerPanel,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart};
|
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart};
|
||||||
use super::{document_node_definitions, node_properties};
|
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::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||||
@@ -30,6 +30,7 @@ use graphene_std::*;
|
|||||||
use kurbo::{DEFAULT_ACCURACY, Shape};
|
use kurbo::{DEFAULT_ACCURACY, Shape};
|
||||||
use renderer::Quad;
|
use renderer::Quad;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug, ExtractField)]
|
#[derive(Debug, ExtractField)]
|
||||||
pub struct NodeGraphMessageContext<'a> {
|
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),
|
.unwrap_or_else(|| modify_import_export.reorder_imports_exports.input_ports().count() + 1),
|
||||||
);
|
);
|
||||||
responses.add(FrontendMessage::UpdateExportReorderIndex { index: self.end_index });
|
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() {
|
if network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
|
||||||
self.hovering_input = true;
|
|
||||||
responses.add(DeferMessage::RequestDeferredMessage {
|
responses.add(DeferMessage::RequestDeferredMessage {
|
||||||
message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()),
|
message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()),
|
||||||
timeout: Duration::from_millis(TOOLTIP_DELAY),
|
timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY),
|
||||||
});
|
});
|
||||||
}
|
} else if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
|
||||||
if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() {
|
|
||||||
self.hovering_output = true;
|
|
||||||
responses.add(DeferMessage::RequestDeferredMessage {
|
responses.add(DeferMessage::RequestDeferredMessage {
|
||||||
message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()),
|
message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()),
|
||||||
timeout: Duration::from_millis(TOOLTIP_DELAY),
|
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 {
|
} else if self.hovering_input {
|
||||||
@@ -1174,16 +1177,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
self.hovering_output = false;
|
self.hovering_output = false;
|
||||||
responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() });
|
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 {
|
} 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;
|
self.hovering_node = false;
|
||||||
responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() });
|
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);
|
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 => {
|
NodeGraphMessage::ShakeNode => {
|
||||||
let Some(drag_start) = &self.drag_start else {
|
let Some(drag_start) = &self.drag_start else {
|
||||||
log::error!("Drag start should be initialized when shaking a node");
|
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::SetVisibility { node_id, visible });
|
||||||
responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] });
|
responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] });
|
||||||
}
|
}
|
||||||
NodeGraphMessage::TryDisplayTooltip => {
|
NodeGraphMessage::TryDisplayTooltip { initial_position } => {
|
||||||
if let Some(input) = network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path) {
|
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);
|
let text = network_interface.input_tooltip_text(&input, breadcrumb_network_path);
|
||||||
if let Some(position) = network_interface.input_position(&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 {
|
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,
|
y: position.y as i32,
|
||||||
};
|
};
|
||||||
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
|
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) {
|
} 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);
|
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,
|
y: position.y as i32,
|
||||||
};
|
};
|
||||||
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
|
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) {
|
} 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);
|
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(position) = network_interface.position(&node_id, breadcrumb_network_path) else {
|
||||||
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
|
log::error!("Could not get position from node: {node_id}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let position = network_metadata
|
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
|
||||||
.persistent_metadata
|
return;
|
||||||
.navigation_metadata
|
};
|
||||||
.node_graph_to_viewport
|
let position = network_metadata
|
||||||
.transform_point2(DVec2::new(position.x as f64 * 24., position.y as f64 * 24.));
|
.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 {
|
let xy = FrontendXY {
|
||||||
x: position.x as i32,
|
x: position.x as i32,
|
||||||
y: position.y as i32,
|
y: position.y as i32,
|
||||||
};
|
};
|
||||||
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
|
responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text });
|
||||||
}
|
self.hovering_node = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetPinned { node_id, pinned } => {
|
NodeGraphMessage::SetPinned { node_id, pinned } => {
|
||||||
@@ -2655,6 +2661,9 @@ impl Default for NodeGraphMessageHandler {
|
|||||||
reordering_import: None,
|
reordering_import: None,
|
||||||
end_index: None,
|
end_index: None,
|
||||||
thumbnails: HashMap::new(),
|
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 std::borrow::Cow;
|
||||||
|
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource;
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
#[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)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendNodeType {
|
pub struct FrontendNodeType {
|
||||||
|
|||||||
@@ -2675,6 +2675,16 @@ impl NodeNetworkInterface {
|
|||||||
self.unload_modify_import_export(network_path);
|
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) {
|
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 {
|
let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else {
|
||||||
log::error!("Could not get node in vector_modification");
|
log::error!("Could not get node in vector_modification");
|
||||||
@@ -5853,7 +5863,7 @@ pub enum LayerClickTargetTypes {
|
|||||||
// Preview,
|
// Preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct NavigationMetadata {
|
pub struct NavigationMetadata {
|
||||||
/// The current pan, and zoom state of the viewport's view of the node graph.
|
/// 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.
|
/// 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
|
// 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.
|
/// Transform from node graph space to viewport space.
|
||||||
pub node_graph_to_viewport: DAffine2,
|
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)]
|
#[serde(default)]
|
||||||
pub node_graph_top_right: DVec2,
|
pub node_graph_width: f64,
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PartialEq required by message handlers
|
// PartialEq required by message handlers
|
||||||
|
|||||||
+23
-20
@@ -1,19 +1,19 @@
|
|||||||
use glam::{DVec2, IVec2};
|
use glam::{DVec2, IVec2};
|
||||||
use graph_craft::proto::GraphErrors;
|
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 kurbo::BezPath;
|
||||||
|
|
||||||
use crate::{
|
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},
|
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::{
|
messages::portfolio::document::utility_types::{
|
||||||
node_graph::utility_types::{
|
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
|
||||||
FrontendExport, FrontendExports, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer,
|
wires::{GraphWireStyle, build_vector_wire},
|
||||||
FrontendNodeToRender, FrontendXY,
|
|
||||||
},
|
|
||||||
utility_types::{
|
|
||||||
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
|
|
||||||
wires::{GraphWireStyle, build_vector_wire},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ impl NodeNetworkInterface {
|
|||||||
(
|
(
|
||||||
wire,
|
wire,
|
||||||
self.wire_is_thick(&InputConnector::node(node_id, input_index), network_path),
|
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();
|
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 {
|
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 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 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 viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width;
|
||||||
let target_viewport_top_right = DVec2::new(
|
|
||||||
viewport_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64,
|
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);
|
||||||
viewport_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64,
|
|
||||||
);
|
|
||||||
|
|
||||||
// An offset from the right edge in viewport pixels
|
// 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);
|
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
|
// 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(
|
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.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),
|
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();
|
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 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};
|
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[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>)>,
|
saved_selection_before_handle_drag: HashMap<LayerNodeIdentifier, (HashSet<ManipulatorPointId>, HashSet<SegmentId>)>,
|
||||||
handle_drag_toggle: bool,
|
handle_drag_toggle: bool,
|
||||||
saved_points_before_anchor_convert_smooth_sharp: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>,
|
saved_points_before_anchor_convert_smooth_sharp: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>,
|
||||||
last_click_time: Instant,
|
last_click_time: u64,
|
||||||
dragging_state: DraggingState,
|
dragging_state: DraggingState,
|
||||||
angle: f64,
|
angle: f64,
|
||||||
pivot_gizmo: PivotGizmo,
|
pivot_gizmo: PivotGizmo,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use graphene_std::text::FontCache;
|
|||||||
use graphene_std::transform::Footprint;
|
use graphene_std::transform::Footprint;
|
||||||
use graphene_std::vector::Vector;
|
use graphene_std::vector::Vector;
|
||||||
use graphene_std::wasm_application_io::RenderOutputType;
|
use graphene_std::wasm_application_io::RenderOutputType;
|
||||||
|
use graphene_std::Graphic;
|
||||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
|
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
|
||||||
|
|
||||||
mod runtime_io;
|
mod runtime_io;
|
||||||
@@ -121,14 +122,7 @@ impl NodeGraphExecutor {
|
|||||||
|
|
||||||
/// Update the cached network if necessary.
|
/// 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> {
|
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();
|
let 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_hash = network.current_hash();
|
let network_hash = network.current_hash();
|
||||||
// Refresh the graph when it changes or the inspect node changes
|
// 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 {
|
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-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24}
|
||||||
style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index}
|
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)" />
|
<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}
|
{: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)" />
|
<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: 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
|
// 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 inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
|
||||||
const inGraphOverlay = get(document).graphViewOverlayOpen;
|
if (!viewportPointerInteractionOngoing && inFloatingMenu) return;
|
||||||
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
|
|
||||||
|
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
if (detectShake(e)) editor.handle.onMouseShake(e.clientX, e.clientY, e.buttons, modifiers);
|
if (detectShake(e)) editor.handle.onMouseShake(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
|
|||||||
+24
-45
@@ -127,17 +127,16 @@ export class UpdateNodeThumbnail extends JsMessage {
|
|||||||
|
|
||||||
readonly value!: string;
|
readonly value!: string;
|
||||||
}
|
}
|
||||||
|
export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
|
@Type(() => OpenDocument)
|
||||||
|
readonly openDocuments!: OpenDocument[];
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateTooltip extends JsMessage {
|
export class UpdateTooltip extends JsMessage {
|
||||||
readonly position!: XY | undefined;
|
readonly position!: XY | undefined;
|
||||||
readonly text!: string;
|
readonly text!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateOpenDocumentsList extends JsMessage {
|
|
||||||
@Type(() => FrontendDocumentDetails)
|
|
||||||
readonly openDocuments!: FrontendDocumentDetails[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WirePathInProgress {
|
export class WirePathInProgress {
|
||||||
readonly wire!: string;
|
readonly wire!: string;
|
||||||
readonly thick!: boolean;
|
readonly thick!: boolean;
|
||||||
@@ -148,35 +147,28 @@ export class UpdateWirePathInProgress extends JsMessage {
|
|||||||
readonly wirePathInProgress!: WirePathInProgress | undefined;
|
readonly wirePathInProgress!: WirePathInProgress | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows the auto save system to use a string for the id rather than a BigInt.
|
export class OpenDocument {
|
||||||
// IndexedDb does not allow for BigInts as primary keys.
|
readonly id!: bigint;
|
||||||
// TypeScript does not allow subclasses to change the type of class variables in subclasses.
|
@Type(() => DocumentDetails)
|
||||||
// It is an abstract class to point out that it should not be instantiated directly.
|
readonly details!: DocumentDetails;
|
||||||
export abstract class DocumentDetails {
|
|
||||||
|
get displayName(): string {
|
||||||
|
return this.details.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocumentDetails {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
readonly isAutoSaved!: boolean;
|
readonly isAutoSaved!: boolean;
|
||||||
|
|
||||||
readonly isSaved!: boolean;
|
readonly isSaved!: boolean;
|
||||||
|
|
||||||
// This field must be provided by the subclass implementation
|
|
||||||
// readonly id!: bigint | string;
|
|
||||||
|
|
||||||
get displayName(): string {
|
get displayName(): string {
|
||||||
return `${this.name}${this.isSaved ? "" : "*"}`;
|
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 type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color";
|
||||||
|
|
||||||
export class FrontendGraphInput {
|
export class FrontendGraphInput {
|
||||||
@@ -198,11 +190,7 @@ export class FrontendGraphOutput {
|
|||||||
|
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
readonly description!: string;
|
readonly connected!: boolean;
|
||||||
|
|
||||||
readonly resolvedType!: string;
|
|
||||||
|
|
||||||
readonly connectedTo!: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FrontendExport {
|
export class FrontendExport {
|
||||||
@@ -322,21 +310,20 @@ export class WireUpdate {
|
|||||||
readonly wirePathUpdate!: WirePath | undefined;
|
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())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
id!: string;
|
documentId!: string;
|
||||||
}
|
|
||||||
|
|
||||||
export class TriggerIndexedDbWriteDocument extends JsMessage {
|
|
||||||
document!: string;
|
document!: string;
|
||||||
|
|
||||||
@Type(() => IndexedDbDocumentDetails)
|
@Type(() => DocumentDetails)
|
||||||
details!: IndexedDbDocumentDetails;
|
details!: DocumentDetails;
|
||||||
|
|
||||||
version!: string;
|
version!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerIndexedDbRemoveDocument extends JsMessage {
|
export class TriggerPersistenceRemoveDocument extends JsMessage {
|
||||||
// Use a string since IndexedDB can not use BigInts for keys
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
documentId!: string;
|
documentId!: string;
|
||||||
@@ -1469,7 +1456,6 @@ export class WidgetDiffUpdate extends JsMessage {
|
|||||||
layoutTarget!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
|
|
||||||
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
|
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
|
||||||
diff!: WidgetDiff[];
|
diff!: WidgetDiff[];
|
||||||
}
|
}
|
||||||
@@ -1504,7 +1490,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
|
|||||||
return targetLayout;
|
return targetLayout;
|
||||||
}
|
}
|
||||||
// This is a path traversal so we can assume from the backend that it exists
|
// This is a path traversal so we can assume from the backend that it exists
|
||||||
|
|
||||||
if (targetLayout && "action" in targetLayout) return targetLayout.children![index];
|
if (targetLayout && "action" in targetLayout) return targetLayout.children![index];
|
||||||
|
|
||||||
return targetLayout?.[index];
|
return targetLayout?.[index];
|
||||||
@@ -1525,7 +1510,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
|
|||||||
diffObject.length = 0;
|
diffObject.length = 0;
|
||||||
}
|
}
|
||||||
// Remove all of the keys from the old object
|
// Remove all of the keys from the old object
|
||||||
|
|
||||||
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
|
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
|
||||||
|
|
||||||
// Assign keys to the new object
|
// 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
|
// Unpacking rust types to more usable type in the frontend
|
||||||
|
|
||||||
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
||||||
return diffs.map((diff) => {
|
return diffs.map((diff) => {
|
||||||
const { widgetPath, newValue } = diff;
|
const { widgetPath, newValue } = diff;
|
||||||
@@ -1577,7 +1560,6 @@ function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unpacking a layout group
|
// Unpacking a layout group
|
||||||
|
|
||||||
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
||||||
if (layoutGroup.column) {
|
if (layoutGroup.column) {
|
||||||
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
|
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
|
||||||
@@ -1635,7 +1617,6 @@ export class UpdateMenuBarLayout extends JsMessage {
|
|||||||
layoutTarget!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
|
|
||||||
@Transform(({ value }: { value: any }) => createMenuLayout(value))
|
@Transform(({ value }: { value: any }) => createMenuLayout(value))
|
||||||
layout!: MenuBarEntry[];
|
layout!: MenuBarEntry[];
|
||||||
}
|
}
|
||||||
@@ -1658,7 +1639,6 @@ function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] {
|
|||||||
children: createMenuLayoutRecursive(entry.children),
|
children: createMenuLayoutRecursive(entry.children),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
|
function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
|
||||||
return children.map((groups) =>
|
return children.map((groups) =>
|
||||||
groups.map((entry) => ({
|
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
|
// `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 JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
|
||||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||||
|
|
||||||
@@ -1691,8 +1670,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerFontLoad,
|
TriggerFontLoad,
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerPersistenceRemoveDocument,
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ import {
|
|||||||
type FrontendSelectionBox,
|
type FrontendSelectionBox,
|
||||||
type FrontendClickTargets,
|
type FrontendClickTargets,
|
||||||
type ContextMenuInformation,
|
type ContextMenuInformation,
|
||||||
type FrontendNodeToRender,
|
|
||||||
type FrontendNodeType,
|
type FrontendNodeType,
|
||||||
type WirePathInProgress,
|
type WirePathInProgress,
|
||||||
|
type XY,
|
||||||
SendUIMetadata,
|
SendUIMetadata,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
UpdateImportReorderIndex,
|
UpdateImportReorderIndex,
|
||||||
UpdateExportReorderIndex,
|
UpdateExportReorderIndex,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateLayerWidths,
|
|
||||||
UpdateNativeNodeGraphSVG,
|
UpdateNativeNodeGraphSVG,
|
||||||
UpdateNodeThumbnail,
|
UpdateNodeThumbnail,
|
||||||
UpdateWirePathInProgress,
|
UpdateWirePathInProgress,
|
||||||
UpdateNodeGraphSelectionBox,
|
UpdateNodeGraphSelectionBox,
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
|
UpdateTooltip,
|
||||||
} from "@graphite/messages";
|
} from "@graphite/messages";
|
||||||
|
|
||||||
export function createNodeGraphState(editor: Editor) {
|
export function createNodeGraphState(editor: Editor) {
|
||||||
@@ -37,11 +37,8 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
nodeTypes: [] as FrontendNodeType[],
|
nodeTypes: [] as FrontendNodeType[],
|
||||||
nodeDescriptions: new Map<string, string>(),
|
nodeDescriptions: new Map<string, string>(),
|
||||||
|
|
||||||
// Data that will be moved into the node graph to be rendered natively
|
tooltipPosition: undefined as XY | undefined,
|
||||||
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
|
tooltipText: "test",
|
||||||
opacity: 0.8,
|
|
||||||
inSelectedNetwork: true,
|
|
||||||
previewedNode: undefined as bigint | undefined,
|
|
||||||
|
|
||||||
// Data that will be passed in the context
|
// Data that will be passed in the context
|
||||||
thumbnails: new Map<bigint, string>(),
|
thumbnails: new Map<bigint, string>(),
|
||||||
@@ -117,7 +114,13 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateTooltip, (updateTooltip) => {
|
||||||
|
update((state) => {
|
||||||
|
state.tooltipPosition = updateTooltip.position;
|
||||||
|
state.tooltipText = updateTooltip.text;
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,11 +5,8 @@
|
|||||||
// on the dispatcher messaging system and more complex Rust data types.
|
// on the dispatcher messaging system and more complex Rust data types.
|
||||||
//
|
//
|
||||||
use crate::helpers::translate_key;
|
use crate::helpers::translate_key;
|
||||||
#[cfg(not(feature = "native"))]
|
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
|
||||||
use crate::wasm_node_graph_ui_executor::WasmNodeGraphUIExecutor;
|
|
||||||
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER, WASM_NODE_GRAPH_EXECUTOR};
|
|
||||||
use editor::consts::FILE_EXTENSION;
|
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_keyboard::ModifierKeys;
|
||||||
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds};
|
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds};
|
||||||
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
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 {
|
pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self {
|
||||||
let editor = Editor::new();
|
let editor = Editor::new();
|
||||||
let editor_handle = EditorHandle { frontend_message_handler_callback };
|
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() {
|
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");
|
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() {
|
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");
|
log::error!("Attempted to initialize the editor handle more than once");
|
||||||
}
|
}
|
||||||
@@ -202,12 +195,28 @@ impl EditorHandle {
|
|||||||
#[cfg(not(feature = "native"))]
|
#[cfg(not(feature = "native"))]
|
||||||
fn dispatch<T: Into<Message>>(&self, message: T) {
|
fn dispatch<T: Into<Message>>(&self, message: T) {
|
||||||
// Process no further messages after a crash to avoid spamming the console
|
// Process no further messages after a crash to avoid spamming the console
|
||||||
|
|
||||||
|
use crate::MESSAGE_BUFFER;
|
||||||
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
|
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
|
||||||
return;
|
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")]
|
#[cfg(feature = "native")]
|
||||||
@@ -220,37 +229,6 @@ impl EditorHandle {
|
|||||||
crate::native_communcation::send_message_to_cef(serialized_message)
|
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
|
// Add additional JS -> Rust wrapper functions below as needed for calling
|
||||||
// the backend from the web frontend.
|
// the backend from the web frontend.
|
||||||
@@ -280,20 +258,6 @@ impl EditorHandle {
|
|||||||
#[cfg(not(feature = "native"))]
|
#[cfg(not(feature = "native"))]
|
||||||
wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation());
|
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) {
|
if !EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
|
||||||
handle(|handle| {
|
handle(|handle| {
|
||||||
// Process all messages that have been queued up
|
// Process all messages that have been queued up
|
||||||
@@ -496,6 +460,7 @@ impl EditorHandle {
|
|||||||
document_is_saved,
|
document_is_saved,
|
||||||
document_serialized_content,
|
document_serialized_content,
|
||||||
to_front,
|
to_front,
|
||||||
|
select_after_open: false,
|
||||||
};
|
};
|
||||||
self.dispatch(message);
|
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.
|
/// Provides access to the `Editor` by calling the given closure with it as an argument.
|
||||||
#[cfg(not(feature = "native"))]
|
#[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| {
|
EDITOR.with(|editor| {
|
||||||
let mut guard = editor.try_lock();
|
let mut guard = editor.try_lock();
|
||||||
let Ok(Some(editor)) = guard.as_deref_mut() else {
|
let Ok(Some(editor)) = guard.as_deref_mut() else {
|
||||||
return Err(());
|
log::error!("Failed to borrow editor");
|
||||||
};
|
return T::default();
|
||||||
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(());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(callback(executor))
|
callback(editor)
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides access to the `Editor` and its `EditorHandle` by calling the given closure with them as arguments.
|
/// Provides access to the `Editor` and its `EditorHandle` by calling the given closure with them as arguments.
|
||||||
#[cfg(not(feature = "native"))]
|
#[cfg(not(feature = "native"))]
|
||||||
pub(crate) fn editor_and_handle(callback: impl FnOnce(&mut Editor, &mut EditorHandle)) {
|
pub(crate) fn editor_and_handle(callback: impl FnOnce(&mut Editor, &mut EditorHandle)) {
|
||||||
let _ = handle(|editor_handle| {
|
handle(|editor_handle| {
|
||||||
let _ = editor(|editor| {
|
editor(|editor| {
|
||||||
// Call the closure with the editor and its handle
|
// Call the closure with the editor and its handle
|
||||||
callback(editor, editor_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);
|
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
|
// If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auto_save_all_documents() {
|
fn auto_save_all_documents() {
|
||||||
// Process no further messages after a crash to avoid spamming the console
|
// Process no further messages after a crash to avoid spamming the console
|
||||||
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
|
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ pub fn panic_hook(info: &panic::PanicHookInfo) {
|
|||||||
/text>"#
|
/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.
|
// 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();
|
.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| {
|
EDITOR_HANDLE.with(|editor_handle| {
|
||||||
let mut guard = editor_handle.lock();
|
let mut guard = editor_handle.lock();
|
||||||
if let Ok(Some(handle)) = guard.as_deref_mut() {
|
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) => {
|
Ok(messages) => {
|
||||||
let callback = move |handle: &mut EditorHandle| {
|
let callback = move |handle: &mut EditorHandle| {
|
||||||
for message in messages {
|
for message in messages {
|
||||||
handle.send_frontend_message_to_js_rust_proxy(message);
|
handle.send_frontend_message_to_js(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
editor_api::handle(callback);
|
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));
|
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);
|
node_table.push(node_text_row);
|
||||||
|
|
||||||
// Add black clipping path to view text in node
|
// 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);
|
ports_table.push(row);
|
||||||
}
|
}
|
||||||
if let Some(primary_output) = &frontend_node.primary_output {
|
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.));
|
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12.));
|
||||||
ports_table.push(row);
|
ports_table.push(row);
|
||||||
}
|
}
|
||||||
for (index, secondary_output) in frontend_node.secondary_outputs.iter().enumerate() {
|
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));
|
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12. + GRID_SIZE * (index + 1) as f64));
|
||||||
ports_table.push(row);
|
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 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);
|
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);
|
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.));
|
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);
|
ports_table.push(top_port);
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ pub struct FrontendGraphOutput {
|
|||||||
#[serde(rename = "dataType")]
|
#[serde(rename = "dataType")]
|
||||||
pub data_type: FrontendGraphDataType,
|
pub data_type: FrontendGraphDataType,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub connected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[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::RasterGPU(table) => table.to_graphic(),
|
||||||
Graphic::Color(table) => table.to_graphic(),
|
Graphic::Color(table) => table.to_graphic(),
|
||||||
Graphic::Gradient(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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|||||||
Reference in New Issue
Block a user