mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 01:48:13 +08:00
clean up
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
use crate::messages::portfolio::document::node_graph::utility_types::{BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendNodeType, Transform};
|
||||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendExports, FrontendImport, FrontendNodeToRender, FrontendNodeType, FrontendXY, Transform,
|
|
||||||
};
|
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::WirePathInProgress;
|
use crate::messages::portfolio::document::utility_types::wires::WirePathInProgress;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::HintData;
|
use crate::messages::tool::utility_types::HintData;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
|
use graphene_std::node_graph_overlay::types::{FrontendExports, FrontendImport, FrontendNodeToRender, FrontendXY};
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
use graphene_std::text::{Font, TextAlign};
|
use graphene_std::text::{Font, TextAlign};
|
||||||
@@ -280,6 +279,8 @@ pub enum FrontendMessage {
|
|||||||
// Displays a dashed border around the node
|
// Displays a dashed border around the node
|
||||||
#[serde(rename = "previewedNode")]
|
#[serde(rename = "previewedNode")]
|
||||||
previewed_node: Option<NodeId>,
|
previewed_node: Option<NodeId>,
|
||||||
|
},
|
||||||
|
UpdateNativeNodeGraphRender {
|
||||||
#[serde(rename = "nativeNodeGraphRender")]
|
#[serde(rename = "nativeNodeGraphRender")]
|
||||||
native_node_graph_render: bool,
|
native_node_graph_render: bool,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
|
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
|
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
|
||||||
use graphene_std::vector::style::FillChoice;
|
use graphene_std::vector::style::FillChoice;
|
||||||
use graphite_proc_macros::WidgetBuilder;
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use graph_craft::{
|
||||||
|
concrete,
|
||||||
|
document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork, value::TaggedValue},
|
||||||
|
};
|
||||||
|
use graphene_std::{
|
||||||
|
Context, graphic, memo,
|
||||||
|
node_graph_overlay::{self, types::NodeGraphOverlayData},
|
||||||
|
uuid::NodeId,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData, opacity: f64) -> DocumentNode {
|
||||||
|
// TODO: Implement as Network and implement finer grained caching for the background, nodes, and exports
|
||||||
|
DocumentNode {
|
||||||
|
inputs: vec![
|
||||||
|
NodeInput::value(TaggedValue::None, true),
|
||||||
|
NodeInput::value(TaggedValue::NodeGraphOverlayData(node_graph_overlay_data), true),
|
||||||
|
NodeInput::value(TaggedValue::F64(opacity), true),
|
||||||
|
],
|
||||||
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
|
exports: vec![NodeInput::node(NodeId(0), 0)],
|
||||||
|
nodes: vec![
|
||||||
|
// Merge the overlay on top of the artwork
|
||||||
|
(
|
||||||
|
NodeId(0),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::node(NodeId(2), 0), NodeInput::node(NodeId(1), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
//Wrap Artwork in a table
|
||||||
|
(
|
||||||
|
NodeId(1),
|
||||||
|
DocumentNode {
|
||||||
|
inputs: vec![NodeInput::network(concrete!(Context), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Cache the full node graph so its not rerendered when the artwork changes
|
||||||
|
(
|
||||||
|
NodeId(2),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::node(NodeId(3), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Merge the nodes on top of the dot grid background
|
||||||
|
(
|
||||||
|
NodeId(3),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::node(NodeId(5), 0), NodeInput::node(NodeId(4), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Generate the dot grid background
|
||||||
|
(
|
||||||
|
NodeId(4),
|
||||||
|
DocumentNode {
|
||||||
|
inputs: vec![NodeInput::network(concrete!(Context), 2)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::dot_grid_background::IDENTIFIER),
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Transform the nodes based on the Context
|
||||||
|
(
|
||||||
|
NodeId(5),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::node(NodeId(6), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::transform_nodes::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Cache the nodes
|
||||||
|
(
|
||||||
|
NodeId(6),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::node(NodeId(7), 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Create the nodes
|
||||||
|
(
|
||||||
|
NodeId(7),
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
inputs: vec![NodeInput::network(concrete!(Context), 1)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::generate_nodes::IDENTIFIER),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
call_argument: concrete!(Context),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod document_node_definitions;
|
pub mod document_node_definitions;
|
||||||
|
pub mod generate_node_graph_overlay;
|
||||||
mod node_graph_message;
|
mod node_graph_message;
|
||||||
mod node_graph_message_handler;
|
mod node_graph_message_handler;
|
||||||
pub mod node_properties;
|
pub mod node_properties;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ 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;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, FrontendXY};
|
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction};
|
||||||
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::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
use crate::messages::portfolio::document::utility_types::network_interface::{
|
||||||
@@ -21,9 +21,10 @@ use crate::messages::tool::common_functionality::utility_functions::make_path_ed
|
|||||||
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::math::math_ext::QuadExt;
|
use graphene_std::math::math_ext::QuadExt;
|
||||||
|
use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendXY};
|
||||||
use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath;
|
use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath;
|
||||||
use graphene_std::*;
|
use graphene_std::*;
|
||||||
use kurbo::{DEFAULT_ACCURACY, Shape};
|
use kurbo::{DEFAULT_ACCURACY, Shape};
|
||||||
@@ -875,7 +876,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
};
|
};
|
||||||
let Some(output_connector) = output_connector else { return };
|
let Some(output_connector) = output_connector else { return };
|
||||||
self.wire_in_progress_from_connector = network_interface.output_position(&output_connector, selection_network_path);
|
self.wire_in_progress_from_connector = network_interface.output_position(&output_connector, selection_network_path);
|
||||||
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&network_interface.input_type(clicked_input, breadcrumb_network_path));
|
self.wire_in_progress_type = network_interface.input_type(clicked_input, breadcrumb_network_path).displayed_type();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,7 +887,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
|
|
||||||
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
|
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
|
||||||
let output_type = network_interface.output_type(&clicked_output, breadcrumb_network_path);
|
let output_type = network_interface.output_type(&clicked_output, breadcrumb_network_path);
|
||||||
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&output_type);
|
self.wire_in_progress_type = output_type.displayed_type();
|
||||||
|
|
||||||
self.update_node_graph_hints(responses);
|
self.update_node_graph_hints(responses);
|
||||||
return;
|
return;
|
||||||
@@ -1624,14 +1625,24 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path);
|
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path);
|
||||||
self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect();
|
self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect();
|
||||||
let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
|
let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphRender {
|
if self.native_node_graph_render {
|
||||||
nodes_to_render,
|
let node_graph_render_data = node_graph_overlay::types::NodeGraphOverlayData {
|
||||||
open: graph_view_overlay_open,
|
nodes_to_render,
|
||||||
opacity: graph_fade_artwork_percentage,
|
open: graph_view_overlay_open,
|
||||||
in_selected_network: selection_network_path == breadcrumb_network_path,
|
in_selected_network: selection_network_path == breadcrumb_network_path,
|
||||||
previewed_node,
|
previewed_node,
|
||||||
native_node_graph_render: self.native_node_graph_render,
|
};
|
||||||
});
|
self.node_graph_overlay = Some(super::generate_node_graph_overlay::generate_node_graph_overlay(node_graph_render_data, graph_fade_artwork_percentage));
|
||||||
|
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
||||||
|
} else {
|
||||||
|
responses.add(FrontendMessage::UpdateNodeGraphRender {
|
||||||
|
nodes_to_render,
|
||||||
|
open: graph_view_overlay_open,
|
||||||
|
opacity: graph_fade_artwork_percentage,
|
||||||
|
in_selected_network: selection_network_path == breadcrumb_network_path,
|
||||||
|
previewed_node,
|
||||||
|
});
|
||||||
|
}
|
||||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||||
|
|
||||||
let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path);
|
let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path);
|
||||||
@@ -1784,6 +1795,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleNativeNodeGraphRender => {
|
NodeGraphMessage::ToggleNativeNodeGraphRender => {
|
||||||
self.native_node_graph_render = !self.native_node_graph_render;
|
self.native_node_graph_render = !self.native_node_graph_render;
|
||||||
|
self.node_graph_overlay = None;
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleSelectedLocked => {
|
NodeGraphMessage::ToggleSelectedLocked => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
|
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
|
||||||
use super::utility_types::FrontendGraphDataType;
|
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
@@ -14,6 +13,7 @@ use graph_craft::{Type, concrete};
|
|||||||
use graphene_std::NodeInputDecleration;
|
use graphene_std::NodeInputDecleration;
|
||||||
use graphene_std::animation::RealTimeMode;
|
use graphene_std::animation::RealTimeMode;
|
||||||
use graphene_std::extract_xy::XY;
|
use graphene_std::extract_xy::XY;
|
||||||
|
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
|
||||||
use graphene_std::path_bool::BooleanOperation;
|
use graphene_std::path_bool::BooleanOperation;
|
||||||
use graphene_std::raster::curve::Curve;
|
use graphene_std::raster::curve::Curve;
|
||||||
use graphene_std::raster::{
|
use graphene_std::raster::{
|
||||||
@@ -1996,7 +1996,10 @@ pub struct ParameterWidgetsInfo<'a> {
|
|||||||
impl<'a> ParameterWidgetsInfo<'a> {
|
impl<'a> ParameterWidgetsInfo<'a> {
|
||||||
pub fn new(node_id: NodeId, index: usize, blank_assist: bool, context: &'a mut NodePropertiesContext) -> ParameterWidgetsInfo<'a> {
|
pub fn new(node_id: NodeId, index: usize, blank_assist: bool, context: &'a mut NodePropertiesContext) -> ParameterWidgetsInfo<'a> {
|
||||||
let (name, description) = context.network_interface.displayed_input_name_and_description(&node_id, index, context.selection_network_path);
|
let (name, description) = context.network_interface.displayed_input_name_and_description(&node_id, index, context.selection_network_path);
|
||||||
let input_type = FrontendGraphDataType::displayed_type(&context.network_interface.input_type(&InputConnector::node(node_id, index), context.selection_network_path));
|
let input_type = context
|
||||||
|
.network_interface
|
||||||
|
.input_type(&InputConnector::node(node_id, index), context.selection_network_path)
|
||||||
|
.displayed_type();
|
||||||
let document_node = context.network_interface.document_node(&node_id, context.selection_network_path);
|
let document_node = context.network_interface.document_node(&node_id, context.selection_network_path);
|
||||||
|
|
||||||
ParameterWidgetsInfo {
|
ParameterWidgetsInfo {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graph_craft::document::value::TaggedValue;
|
|
||||||
use graphene_std::Type;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource;
|
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource;
|
||||||
@@ -208,7 +206,7 @@ impl FrontendNodeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct DragStart {
|
pub struct DragStart {
|
||||||
pub start_x: f64,
|
pub start_x: f64,
|
||||||
pub start_y: f64,
|
pub start_y: f64,
|
||||||
|
|||||||
@@ -5297,7 +5297,7 @@ impl InputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an output connector
|
/// Represents an output connector
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum OutputConnector {
|
pub enum OutputConnector {
|
||||||
#[serde(rename = "node")]
|
#[serde(rename = "node")]
|
||||||
Node {
|
Node {
|
||||||
|
|||||||
+2
-2
@@ -136,7 +136,7 @@ impl NodeNetworkInterface {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let input_type = self.input_type(input_connector, network_path);
|
let input_type = self.input_type(input_connector, network_path);
|
||||||
let data_type = FrontendGraphDataType::displayed_type(&input_type);
|
let data_type = input_type.displayed_type();
|
||||||
let resolved_type = input_type.resolved_type_name();
|
let resolved_type = input_type.resolved_type_name();
|
||||||
|
|
||||||
let connected_to = self
|
let connected_to = self
|
||||||
@@ -239,7 +239,7 @@ impl NodeNetworkInterface {
|
|||||||
(import_name, description)
|
(import_name, description)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let data_type = FrontendGraphDataType::displayed_type(&output_type);
|
let data_type = output_type.displayed_type();
|
||||||
let resolved_type = output_type.resolved_type_name();
|
let resolved_type = output_type.resolved_type_name();
|
||||||
let mut connected_to = self
|
let mut connected_to = self
|
||||||
.outward_wires(network_path)
|
.outward_wires(network_path)
|
||||||
|
|||||||
+26
-1
@@ -4,7 +4,7 @@ use graph_craft::{
|
|||||||
ProtoNodeIdentifier, Type, concrete,
|
ProtoNodeIdentifier, Type, concrete,
|
||||||
document::{DocumentNodeImplementation, InlineRust, NodeInput, value::TaggedValue},
|
document::{DocumentNodeImplementation, InlineRust, NodeInput, value::TaggedValue},
|
||||||
};
|
};
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::{node_graph_overlay::types::FrontendGraphDataType, uuid::NodeId};
|
||||||
use interpreted_executor::{
|
use interpreted_executor::{
|
||||||
dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta},
|
dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta},
|
||||||
node_registry::NODE_REGISTRY,
|
node_registry::NODE_REGISTRY,
|
||||||
@@ -45,6 +45,31 @@ pub enum TypeSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TypeSource {
|
impl TypeSource {
|
||||||
|
pub fn displayed_type(&self) -> FrontendGraphDataType {
|
||||||
|
match self.compiled_nested_type() {
|
||||||
|
Some(nested_type) => match TaggedValue::from_type_or_none(nested_type) {
|
||||||
|
TaggedValue::U32(_)
|
||||||
|
| TaggedValue::U64(_)
|
||||||
|
| TaggedValue::F32(_)
|
||||||
|
| TaggedValue::F64(_)
|
||||||
|
| TaggedValue::DVec2(_)
|
||||||
|
| TaggedValue::F64Array4(_)
|
||||||
|
| TaggedValue::VecF64(_)
|
||||||
|
| TaggedValue::VecDVec2(_)
|
||||||
|
| TaggedValue::DAffine2(_) => FrontendGraphDataType::Number,
|
||||||
|
TaggedValue::Artboard(_) => FrontendGraphDataType::Artboard,
|
||||||
|
TaggedValue::Graphic(_) => FrontendGraphDataType::Graphic,
|
||||||
|
TaggedValue::Raster(_) => FrontendGraphDataType::Raster,
|
||||||
|
TaggedValue::Vector(_) => FrontendGraphDataType::Vector,
|
||||||
|
TaggedValue::Color(_) => FrontendGraphDataType::Color,
|
||||||
|
TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => FrontendGraphDataType::Gradient,
|
||||||
|
TaggedValue::String(_) => FrontendGraphDataType::Typography,
|
||||||
|
_ => FrontendGraphDataType::General,
|
||||||
|
},
|
||||||
|
None => FrontendGraphDataType::General,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_compiled_nested_type(self) -> Option<Type> {
|
pub fn into_compiled_nested_type(self) -> Option<Type> {
|
||||||
match self {
|
match self {
|
||||||
TypeSource::Compiled(compiled_type) => Some(compiled_type.into_nested_type()),
|
TypeSource::Compiled(compiled_type) => Some(compiled_type.into_nested_type()),
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ pub struct LayerPanelEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// IMPORTANT: the same node may appear multiple times.
|
/// IMPORTANT: the same node may appear multiple times.
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||||
pub struct SelectedNodes(pub Vec<NodeId>);
|
pub struct SelectedNodes(pub Vec<NodeId>);
|
||||||
|
|
||||||
impl SelectedNodes {
|
impl SelectedNodes {
|
||||||
@@ -172,5 +172,5 @@ impl SelectedNodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||||
pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>);
|
pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
|
||||||
use glam::{DVec2, IVec2};
|
use glam::{DVec2, IVec2};
|
||||||
use graphene_std::vector::misc::dvec2_to_point;
|
use graphene_std::vector::misc::dvec2_to_point;
|
||||||
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
||||||
|
|||||||
@@ -115,10 +115,17 @@ 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 network_hash = document.network_interface.document_network().current_hash();
|
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_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 {
|
||||||
let network = document.network_interface.document_network().clone();
|
|
||||||
self.previous_node_to_inspect = node_to_inspect;
|
self.previous_node_to_inspect = node_to_inspect;
|
||||||
self.node_graph_hash = network_hash;
|
self.node_graph_hash = network_hash;
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,9 @@ export class UpdateNodeGraphRender extends JsMessage {
|
|||||||
readonly inSelectedNetwork!: boolean;
|
readonly inSelectedNetwork!: boolean;
|
||||||
|
|
||||||
readonly previewedNode!: bigint | undefined;
|
readonly previewedNode!: bigint | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateNativeNodeGraphRender extends JsMessage {
|
||||||
readonly nativeNodeGraphRender!: boolean;
|
readonly nativeNodeGraphRender!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateLayerWidths,
|
UpdateLayerWidths,
|
||||||
UpdateNodeGraphRender,
|
UpdateNodeGraphRender,
|
||||||
|
UpdateNativeNodeGraphRender,
|
||||||
UpdateVisibleNodes,
|
UpdateVisibleNodes,
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeThumbnail,
|
UpdateNodeThumbnail,
|
||||||
@@ -118,7 +119,12 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
state.opacity = updateNodeGraphRender.opacity;
|
state.opacity = updateNodeGraphRender.opacity;
|
||||||
state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork;
|
state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork;
|
||||||
state.previewedNode = updateNodeGraphRender.previewedNode;
|
state.previewedNode = updateNodeGraphRender.previewedNode;
|
||||||
state.nativeNodeGraphRender = updateNodeGraphRender.nativeNodeGraphRender;
|
return state;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateNativeNodeGraphRender, (updateNativeNodeGraphRender) => {
|
||||||
|
update((state) => {
|
||||||
|
state.nativeNodeGraphRender = updateNativeNodeGraphRender.nativeNodeGraphRender;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ pub mod logic;
|
|||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod memo;
|
pub mod memo;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
|
pub mod node_graph_overlay;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod raster;
|
pub mod raster;
|
||||||
pub mod raster_types;
|
pub mod raster_types;
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use graphene_core_shaders::{Ctx, color::Color};
|
||||||
|
use kurbo::{BezPath, Point};
|
||||||
|
|
||||||
|
use crate::{ExtractFootprint, table::Table, vector::Vector};
|
||||||
|
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
#[node_macro::node(category(""))]
|
||||||
|
pub fn generate_nodes(_: impl Ctx, _node_graph_overlay_data: types::NodeGraphOverlayData) -> Table<Vector> {
|
||||||
|
Table::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category(""))]
|
||||||
|
pub fn transform_nodes(_ctx: impl Ctx + ExtractFootprint, nodes: Table<Vector>) -> Table<Vector> {
|
||||||
|
nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category(""))]
|
||||||
|
pub fn dot_grid_background(ctx: impl Ctx + ExtractFootprint, opacity: f64) -> Table<Vector> {
|
||||||
|
let Some(footprint) = ctx.try_footprint() else {
|
||||||
|
log::error!("Could not get footprint from context in dot_grid_background");
|
||||||
|
return Table::new();
|
||||||
|
};
|
||||||
|
// From --color-2-mildblack: --color-2-mildblack-rgb: 34, 34, 34;
|
||||||
|
let gray = (34. / 255.) as f32;
|
||||||
|
let Some(bg_color) = Color::from_rgbaf32(gray, gray, gray, opacity as f32) else {
|
||||||
|
log::error!("Could not create color in dot grid background");
|
||||||
|
return Table::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bez_path = BezPath::new();
|
||||||
|
let p0 = Point::new(0., 0.); // bottom-left
|
||||||
|
let p1 = Point::new(footprint.resolution.x as f64, 0.); // bottom-right
|
||||||
|
let p2 = Point::new(footprint.resolution.x as f64, footprint.resolution.y as f64); // top-right
|
||||||
|
let p3 = Point::new(0., footprint.resolution.y as f64); // top-left
|
||||||
|
|
||||||
|
bez_path.move_to(p0);
|
||||||
|
bez_path.line_to(p1);
|
||||||
|
bez_path.line_to(p2);
|
||||||
|
bez_path.line_to(p3);
|
||||||
|
bez_path.close_path();
|
||||||
|
|
||||||
|
let mut vector = Vector::from_bezpath(bez_path);
|
||||||
|
vector.style.fill = crate::vector::style::Fill::Solid(bg_color);
|
||||||
|
|
||||||
|
Table::new_from_element(vector)
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
use crate::uuid::NodeId;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
pub struct NodeGraphOverlayData {
|
||||||
|
pub nodes_to_render: Vec<FrontendNodeToRender>,
|
||||||
|
pub open: bool,
|
||||||
|
pub in_selected_network: bool,
|
||||||
|
// Displays a dashed border around the node
|
||||||
|
pub previewed_node: Option<NodeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, 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)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata that is common to nodes and layers
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
pub struct FrontendXY {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Should be an enum but those are hard to serialize/deserialize to TS
|
||||||
|
// #[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
// pub struct FrontendLayerPosition {
|
||||||
|
// pub absolute: Option<FrontendXY>,
|
||||||
|
// pub stack: Option<u32>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
pub struct FrontendNodeOrLayer {
|
||||||
|
pub node: Option<FrontendNode>,
|
||||||
|
pub layer: Option<FrontendLayer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
pub struct FrontendExport {
|
||||||
|
pub port: FrontendGraphInput,
|
||||||
|
pub wire: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, 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, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
|
pub struct FrontendImport {
|
||||||
|
pub port: FrontendGraphOutput,
|
||||||
|
pub wires: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub enum FrontendGraphDataType {
|
||||||
|
#[default]
|
||||||
|
General,
|
||||||
|
Number,
|
||||||
|
Artboard,
|
||||||
|
Graphic,
|
||||||
|
Raster,
|
||||||
|
Vector,
|
||||||
|
Color,
|
||||||
|
Gradient,
|
||||||
|
Typography,
|
||||||
|
}
|
||||||
@@ -126,7 +126,7 @@ impl std::fmt::Debug for NodeIOTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct ProtoNodeIdentifier {
|
pub struct ProtoNodeIdentifier {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ impl PartialEq for TypeDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Graph runtime type information used for type inference.
|
/// Graph runtime type information used for type inference.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
/// A wrapper for some type variable used within the inference system. Resolved at inference time and replaced with a concrete type.
|
/// A wrapper for some type variable used within the inference system. Resolved at inference time and replaced with a concrete type.
|
||||||
Generic(Cow<'static, str>),
|
Generic(Cow<'static, str>),
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ tagged_value! {
|
|||||||
CentroidType(graphene_core::vector::misc::CentroidType),
|
CentroidType(graphene_core::vector::misc::CentroidType),
|
||||||
BooleanOperation(graphene_path_bool::BooleanOperation),
|
BooleanOperation(graphene_path_bool::BooleanOperation),
|
||||||
TextAlign(graphene_core::text::TextAlign),
|
TextAlign(graphene_core::text::TextAlign),
|
||||||
|
NodeGraphOverlayData(graphene_core::node_graph_overlay::types::NodeGraphOverlayData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaggedValue {
|
impl TaggedValue {
|
||||||
|
|||||||
Reference in New Issue
Block a user