Skip to content

Commit 5ffb47b

Browse files
committed
Complete basic UI
1 parent 74c7113 commit 5ffb47b

File tree

10 files changed

+179
-22
lines changed

10 files changed

+179
-22
lines changed

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
17951795
native_node_graph_render: self.native_node_graph_render,
17961796
});
17971797
responses.add(NodeGraphMessage::SendGraph);
1798+
responses.add(MenuBarMessage::SendLayout);
17981799
}
17991800
NodeGraphMessage::ToggleSelectedLocked => {
18001801
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {

frontend/src/components/Editor.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
// Replace usage of `-rgb` variants with CSS color() function to calculate alpha when browsers support it
7575
// See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color() and https://caniuse.com/css-color-function
7676
// Specifically, support for the relative syntax is needed: `color(from var(--color-0-black) srgb r g b / 0.5)` to convert black to 50% alpha
77+
// Keep in sync with node_graph_overlay/consts.rs
7778
--color-0-black: #000;
7879
--color-0-black-rgb: 0, 0, 0;
7980
--color-1-nearblack: #111;

frontend/wasm/src/wasm_node_graph_ui_executor.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,15 @@ impl WasmNodeGraphUIExecutor {
5454
if !self.runtime_busy {
5555
self.runtime_busy = true;
5656
wasm_bindgen_futures::spawn_local(async move {
57-
let Ok(mut runtime) = NODE_UI_RUNTIME.try_lock() else { return };
58-
if let Some(runtime) = runtime.as_mut() {
59-
runtime.compile(compilation_request).await
60-
}
57+
let Ok(mut runtime) = NODE_UI_RUNTIME.try_lock() else {
58+
log::error!("Could not get runtime when evaluating");
59+
return;
60+
};
61+
let Some(runtime) = runtime.as_mut() else {
62+
log::error!("Could not lock runtime when evaluating");
63+
return;
64+
};
65+
runtime.compile(compilation_request).await;
6166
})
6267
} else {
6368
self.queued_compilation = Some(compilation_request);
@@ -66,7 +71,6 @@ impl WasmNodeGraphUIExecutor {
6671

6772
// Evaluates the node graph in a spawned future, and returns responses with the response_sender
6873
fn evaluation_request(&mut self, editor: &Editor) {
69-
self.runtime_busy = true;
7074
if let Some(active_document) = editor.dispatcher.message_handlers.portfolio_message_handler.active_document() {
7175
let Some(network_metadata) = active_document.network_interface.network_metadata(&active_document.breadcrumb_network_path) else {
7276
return;
@@ -84,6 +88,8 @@ impl WasmNodeGraphUIExecutor {
8488
};
8589
let resolution = editor.dispatcher.message_handlers.input_preprocessor_message_handler.viewport_bounds.size().as_uvec2();
8690
let evaluation_request = EvaluationRequest { transform, resolution };
91+
self.runtime_busy = true;
92+
8793
wasm_bindgen_futures::spawn_local(async move {
8894
let Ok(mut runtime) = NODE_UI_RUNTIME.try_lock() else {
8995
log::error!("Could not get runtime when evaluating");
@@ -105,18 +111,24 @@ impl WasmNodeGraphUIExecutor {
105111
let mut responses = Vec::new();
106112
for runtime_response in self.response_receiver.try_iter() {
107113
match runtime_response {
114+
UIRuntimeResponse::RuntimeReady => {
115+
self.runtime_busy = false;
116+
}
108117
UIRuntimeResponse::OverlaySVG(svg_string) => {
109118
responses.push(FrontendMessage::UpdateNativeNodeGraphSVG { svg_string }.into());
110119
}
111120
UIRuntimeResponse::OverlayTexture(_texture) => todo!(),
112121
}
113122
}
114-
self.runtime_busy = false;
115-
if let Some(compilation_request) = self.queued_compilation.take() {
116-
self.compilation_request(compilation_request);
117-
} else {
118-
self.evaluation_request(editor);
123+
124+
if !self.runtime_busy {
125+
if let Some(compilation_request) = self.queued_compilation.take() {
126+
self.compilation_request(compilation_request);
127+
} else {
128+
self.evaluation_request(editor);
129+
}
119130
}
131+
120132
responses
121133
}
122134
}

node-graph/gcore/src/node_graph_overlay.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,35 @@ use graphene_core_shaders::{Ctx, color::Color};
22
use kurbo::{BezPath, Point};
33

44
use crate::{
5-
node_graph_overlay::ui_context::{UIContext, UIRuntimeResponse},
6-
table::Table,
5+
node_graph_overlay::{
6+
nodes_and_wires::draw_node,
7+
types::NodeGraphOverlayData,
8+
ui_context::{UIContext, UIRuntimeResponse},
9+
},
10+
table::{Table, TableRow},
11+
transform::ApplyTransform,
712
vector::Vector,
813
};
914

15+
pub mod nodes_and_wires;
1016
pub mod types;
1117
pub mod ui_context;
18+
pub mod consts;
1219

1320
#[node_macro::node(skip_impl)]
14-
pub fn generate_nodes(_: impl Ctx, _node_graph_overlay_data: types::NodeGraphOverlayData) -> Table<Vector> {
15-
Table::new()
21+
pub fn generate_nodes(_: impl Ctx, node_graph_overlay_data: NodeGraphOverlayData) -> Table<Vector> {
22+
let mut nodes_and_wires = Table::new();
23+
for node in node_graph_overlay_data.nodes_to_render {
24+
let vector = draw_node(node);
25+
nodes_and_wires.push(TableRow::new_from_element(vector));
26+
}
27+
nodes_and_wires
1628
}
1729

1830
#[node_macro::node(skip_impl)]
19-
pub fn transform_nodes(_ctx: UIContext, nodes: Table<Vector>) -> Table<Vector> {
31+
pub fn transform_nodes(ui_context: UIContext, mut nodes: Table<Vector>) -> Table<Vector> {
32+
let matrix = ui_context.transform.to_daffine2();
33+
nodes.apply_transform(&matrix);
2034
nodes
2135
}
2236

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
pub const GRID_SIZE: f64 = 24.;
2+
3+
// Keep in sync with colors in Editor.svelte
4+
pub const COLOR_0_BLACK: &str = "000000";
5+
pub const COLOR_1_NEARBLACK: &str = "111111";
6+
pub const COLOR_2_MILDBLACK: &str = "222222";
7+
pub const COLOR_3_DARKGRAY: &str = "333333";
8+
pub const COLOR_4_DIMGRAY: &str = "444444";
9+
pub const COLOR_5_DULLGRAY: &str = "555555";
10+
pub const COLOR_6_LOWERGRAY: &str = "666666";
11+
pub const COLOR_7_MIDDLEGRAY: &str = "777777";
12+
pub const COLOR_8_UPPERGRAY: &str = "888888";
13+
pub const COLOR_9_PALEGRAY: &str = "999999";
14+
pub const COLOR_A_SOFTGRAY: &str = "AAAAAA";
15+
pub const COLOR_B_LIGHTGRAY: &str = "BBBBBB";
16+
pub const COLOR_C_BRIGHTGRAY: &str = "CCCCCC";
17+
pub const COLOR_D_MILDWHITE: &str = "DDDDDD";
18+
pub const COLOR_E_NEARWHITE: &str = "EEEEEE";
19+
pub const COLOR_F_WHITE: &str = "FFFFFF";
20+
21+
pub const COLOR_ERROR_RED: &str = "D6536E";
22+
pub const COLOR_WARNING_YELLOW: &str = "D5AA43";
23+
24+
pub const COLOR_DATA_GENERAL: &str = "CFCFCF";
25+
pub const COLOR_DATA_GENERAL_DIM: &str = "8A8A8A";
26+
pub const COLOR_DATA_NUMBER: &str = "C9A699";
27+
pub const COLOR_DATA_NUMBER_DIM: &str = "886B60";
28+
pub const COLOR_DATA_ARTBOARD: &str = "FBF9EB";
29+
pub const COLOR_DATA_ARTBOARD_DIM: &str = "B9B9A9";
30+
pub const COLOR_DATA_GRAPHIC: &str = "68C587";
31+
pub const COLOR_DATA_GRAPHIC_DIM: &str = "37754C";
32+
pub const COLOR_DATA_RASTER: &str = "E4BB72";
33+
pub const COLOR_DATA_RASTER_DIM: &str = "9A7B43";
34+
pub const COLOR_DATA_VECTOR: &str = "65BBE5";
35+
pub const COLOR_DATA_VECTOR_DIM: &str = "417892";
36+
pub const COLOR_DATA_COLOR: &str = "CE6EA7";
37+
pub const COLOR_DATA_COLOR_DIM: &str = "924071";
38+
pub const COLOR_DATA_GRADIENT: &str = "AF81EB";
39+
pub const COLOR_DATA_GRADIENT_DIM: &str = "6C489B";
40+
pub const COLOR_DATA_TYPOGRAPHY: &str = "EEA7A7";
41+
pub const COLOR_DATA_TYPOGRAPHY_DIM: &str = "955252";
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use graphene_core_shaders::color::{AlphaMut, Color};
2+
use kurbo::{RoundedRect, Shape};
3+
4+
use crate::{
5+
node_graph_overlay::{
6+
consts::{self, GRID_SIZE},
7+
types::{FrontendNodeToRender, NodeOrLayer},
8+
},
9+
vector::Vector,
10+
};
11+
12+
pub fn draw_node(node: FrontendNodeToRender) -> Vector {
13+
let node_or_layer = node.node_or_layer.to_enum();
14+
15+
match &node_or_layer {
16+
NodeOrLayer::Node(frontend_node) => {
17+
let x0 = frontend_node.position.x as f64 * GRID_SIZE;
18+
let y0 = frontend_node.position.y as f64 * GRID_SIZE + GRID_SIZE / 2.;
19+
let w = GRID_SIZE * 5.0;
20+
let height = 1 + frontend_node.inputs.iter().skip(1).filter(|x| x.is_some()).count();
21+
let h = height as f64 * GRID_SIZE;
22+
23+
let rect = RoundedRect::new(x0, y0, x0 + w, y0 + h, 2.);
24+
let bez_path = rect.to_path(0.1);
25+
let mut vector = Vector::from_bezpath(bez_path);
26+
let border_color = Color::from_rgb_str(consts::COLOR_5_DULLGRAY).unwrap();
27+
vector.style.stroke = Some(crate::vector::style::Stroke::new(Some(border_color), 1.));
28+
let mut background = if node.metadata.selected {
29+
Color::from_rgb_str(consts::COLOR_5_DULLGRAY).unwrap()
30+
} else {
31+
Color::from_rgb_str(consts::COLOR_2_MILDBLACK).unwrap()
32+
};
33+
background.set_alpha(0.33);
34+
vector.style.fill = crate::vector::style::Fill::Solid(background);
35+
vector
36+
}
37+
NodeOrLayer::Layer(frontend_layer) => {
38+
let chain_width = if frontend_layer.chain_width > 0 {
39+
frontend_layer.chain_width as f64 * GRID_SIZE + 0.5 * GRID_SIZE
40+
} else {
41+
0.
42+
};
43+
let x0 = frontend_layer.position.x as f64 * GRID_SIZE - chain_width + 0.5 * GRID_SIZE;
44+
let y0 = frontend_layer.position.y as f64 * GRID_SIZE;
45+
let h = 2. * GRID_SIZE;
46+
let w = chain_width + 8. * GRID_SIZE;
47+
48+
let rect = RoundedRect::new(x0, y0, x0 + w, y0 + h, 8.);
49+
let bez_path = rect.to_path(0.1);
50+
let mut vector = Vector::from_bezpath(bez_path);
51+
let border_color = Color::from_rgb_str(consts::COLOR_5_DULLGRAY).unwrap();
52+
vector.style.stroke = Some(crate::vector::style::Stroke::new(Some(border_color), 1.));
53+
let mut background = if node.metadata.selected {
54+
Color::from_rgb_str(consts::COLOR_5_DULLGRAY).unwrap()
55+
} else {
56+
Color::from_rgb_str(consts::COLOR_2_MILDBLACK).unwrap()
57+
};
58+
background.set_alpha(0.33);
59+
vector.style.fill = crate::vector::style::Fill::Solid(background);
60+
vector
61+
}
62+
}
63+
}

node-graph/gcore/src/node_graph_overlay/types.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use glam::{DAffine2, DVec2};
2+
13
use crate::uuid::NodeId;
24
use std::hash::{Hash, Hasher};
35

@@ -8,6 +10,12 @@ pub struct NodeGraphTransform {
810
pub y: f64,
911
}
1012

13+
impl NodeGraphTransform {
14+
pub fn to_daffine2(&self) -> DAffine2 {
15+
DAffine2::from_scale_angle_translation(DVec2::splat(self.scale), 0.0, DVec2::new(self.x, self.y))
16+
}
17+
}
18+
1119
impl Hash for NodeGraphTransform {
1220
fn hash<H: Hasher>(&self, state: &mut H) {
1321
self.scale.to_bits().hash(state);
@@ -104,12 +112,31 @@ pub struct FrontendXY {
104112
// pub stack: Option<u32>,
105113
// }
106114

115+
// Should be an enum but those are hard to serialize/deserialize to TS
107116
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
108117
pub struct FrontendNodeOrLayer {
109118
pub node: Option<FrontendNode>,
110119
pub layer: Option<FrontendLayer>,
111120
}
112121

122+
impl FrontendNodeOrLayer {
123+
pub fn to_enum(self) -> NodeOrLayer {
124+
let node_or_layer = if let Some(node) = self.node {
125+
Some(NodeOrLayer::Node(node))
126+
} else if let Some(layer) = self.layer {
127+
Some(NodeOrLayer::Layer(layer))
128+
} else {
129+
None
130+
};
131+
node_or_layer.unwrap()
132+
}
133+
}
134+
135+
pub enum NodeOrLayer {
136+
Node(FrontendNode),
137+
Layer(FrontendLayer),
138+
}
139+
113140
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
114141
pub struct FrontendGraphInput {
115142
#[serde(rename = "dataType")]

node-graph/gcore/src/node_graph_overlay/ui_context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct UIContextImpl {
1616

1717
#[derive(Debug, Clone, dyn_any::DynAny)]
1818
pub enum UIRuntimeResponse {
19+
RuntimeReady,
1920
OverlaySVG(String),
2021
OverlayTexture(wgpu::Texture),
2122
// OverlayClickTargets(NodeId, ClickTarget)

node-graph/gstd/src/wasm_application_io.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use glam::DAffine2;
1+
use glam::DVec2;
22
use graph_craft::document::value::RenderOutput;
33
pub use graph_craft::document::value::RenderOutputType;
44
pub use graph_craft::wasm_application_io::*;
55
use graphene_application_io::{ApplicationIo, ExportFormat, RenderConfig};
66
use graphene_core::Artboard;
7-
use graphene_core::bounds::RenderBoundingBox;
87
use graphene_core::gradient::GradientStops;
98
#[cfg(target_family = "wasm")]
109
use graphene_core::math::bbox::Bbox;
@@ -367,13 +366,9 @@ async fn render_node_graph_ui<T: Render + WasmNotSend>(
367366
data: impl Node<UIContext, Output = T>,
368367
) -> String {
369368
let data = data.eval(ui_context.clone()).await;
370-
let RenderBoundingBox::Rectangle(bbox) = data.bounding_box(DAffine2::default(), false) else {
371-
log::error!("Could not get bounding box for node graph data");
372-
return String::new();
373-
};
374369
let render_params = RenderParams::default();
375370
let mut render = SvgRender::new();
376371
data.render_svg(&mut render, &render_params);
377-
render.format_svg(bbox[0], bbox[1]);
372+
render.format_svg(DVec2::ZERO, ui_context.resolution.as_dvec2());
378373
render.svg.to_svg_string()
379374
}

node-graph/interpreted-executor/src/ui_runtime.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl NodeGraphUIRuntime {
2929
log::error!("Error compiling node graph ui network: {e:?}");
3030
}
3131
};
32+
let _ = self.response_sender.send(UIRuntimeResponse::RuntimeReady);
3233
}
3334

3435
pub async fn evaluate(&mut self, evaluation_request: EvaluationRequest) {
@@ -40,6 +41,7 @@ impl NodeGraphUIRuntime {
4041
response_sender: self.response_sender.clone(),
4142
});
4243
let _ = (&self.executor).execute(ui_context).await;
44+
let _ = self.response_sender.send(UIRuntimeResponse::RuntimeReady);
4345
}
4446
}
4547

0 commit comments

Comments
 (0)