Skip to content

Commit 07d5bef

Browse files
Keavon0HyperCube
andcommitted
Build the node graph frontend with placeholder graph info (#581)
* Build the node graph frontend * Graph pan and zoom * Graph's dot grid now pans/zooms also * Interactive horisontal to vertical curves * Data types and zooming on wires * Icon definitions code beautification * Add a visibility toggle Co-authored-by: 0hypercube <[email protected]>
1 parent be63515 commit 07d5bef

32 files changed

+820
-139
lines changed

editor/src/communication/dispatcher.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
44
use crate::layout::layout_message_handler::LayoutMessageHandler;
55
use crate::message_prelude::*;
66
use crate::viewport_tools::tool_message_handler::ToolMessageHandler;
7+
use crate::workspace::WorkspaceMessageHandler;
78

89
use std::collections::VecDeque;
910

@@ -27,6 +28,7 @@ struct DispatcherMessageHandlers {
2728
layout_message_handler: LayoutMessageHandler,
2829
portfolio_message_handler: PortfolioMessageHandler,
2930
tool_message_handler: ToolMessageHandler,
31+
workspace_message_handler: WorkspaceMessageHandler,
3032
}
3133

3234
/// For optimization, these are messages guaranteed to be redundant when repeated.
@@ -40,7 +42,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
4042
))),
4143
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::FolderChanged)),
4244
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayer),
43-
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::DisplayDocumentLayerTreeStructure),
45+
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerTreeStructure),
4446
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateOpenDocumentsList),
4547
MessageDiscriminant::Tool(ToolMessageDiscriminant::DocumentIsDirty),
4648
];
@@ -113,6 +115,11 @@ impl Dispatcher {
113115
&mut self.message_queue,
114116
);
115117
}
118+
Workspace(message) => {
119+
self.message_handlers
120+
.workspace_message_handler
121+
.process_action(message, &self.message_handlers.input_preprocessor_message_handler, &mut self.message_queue);
122+
}
116123

117124
#[remain::unsorted]
118125
PopulateBuildMetadata { new } => self.build_metadata = new,

editor/src/communication/message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum Message {
3939
Portfolio(PortfolioMessage),
4040
#[child]
4141
Tool(ToolMessage),
42+
#[child]
43+
Workspace(WorkspaceMessage),
4244

4345
#[remain::unsorted]
4446
PopulateBuildMetadata { new: BuildMetadata },

editor/src/document/document_message_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
846846
DocumentHistoryForward => self.redo(responses).unwrap_or_else(|e| log::warn!("{}", e)),
847847
DocumentStructureChanged => {
848848
let data_buffer: RawBuffer = self.serialize_root().into();
849-
responses.push_back(FrontendMessage::DisplayDocumentLayerTreeStructure { data_buffer }.into())
849+
responses.push_back(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer }.into())
850850
}
851851
DuplicateSelectedLayers => {
852852
self.backup(responses);

editor/src/frontend/frontend_message.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pub enum FrontendMessage {
1616
DisplayDialog { icon: String },
1717
DisplayDialogDismiss,
1818
DisplayDialogPanic { panic_info: String, title: String, description: String },
19-
DisplayDocumentLayerTreeStructure { data_buffer: RawBuffer },
2019
DisplayEditableTextbox { text: String, line_width: Option<f64>, font_size: f64, color: Color },
2120
DisplayRemoveEditableTextbox,
2221

@@ -43,12 +42,14 @@ pub enum FrontendMessage {
4342
UpdateDocumentArtwork { svg: String },
4443
UpdateDocumentBarLayout { layout_target: LayoutTarget, layout: SubLayout },
4544
UpdateDocumentLayer { data: LayerPanelEntry },
45+
UpdateDocumentLayerTreeStructure { data_buffer: RawBuffer },
4646
UpdateDocumentOverlays { svg: String },
4747
UpdateDocumentRulers { origin: (f64, f64), spacing: f64, interval: f64 },
4848
UpdateDocumentScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) },
4949
UpdateImageData { image_data: Vec<FrontendImageData> },
5050
UpdateInputHints { hint_data: HintData },
5151
UpdateMouseCursor { cursor: MouseCursorIcon },
52+
UpdateNodeGraphVisibility { visible: bool },
5253
UpdateOpenDocumentsList { open_documents: Vec<FrontendDocumentDetails> },
5354
UpdatePropertyPanelOptionsLayout { layout_target: LayoutTarget, layout: SubLayout },
5455
UpdatePropertyPanelSectionsLayout { layout_target: LayoutTarget, layout: SubLayout },

editor/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod global;
1111
pub mod input;
1212
pub mod layout;
1313
pub mod viewport_tools;
14+
pub mod workspace;
1415

1516
#[doc(inline)]
1617
pub use graphene::color::Color;
@@ -89,6 +90,7 @@ pub mod message_prelude {
8990
pub use crate::viewport_tools::tools::shape_tool::{ShapeToolMessage, ShapeToolMessageDiscriminant};
9091
pub use crate::viewport_tools::tools::spline_tool::{SplineToolMessage, SplineToolMessageDiscriminant};
9192
pub use crate::viewport_tools::tools::text_tool::{TextMessage, TextMessageDiscriminant};
93+
pub use crate::workspace::{WorkspaceMessage, WorkspaceMessageDiscriminant};
9294
pub use graphite_proc_macros::*;
9395

9496
pub use std::collections::VecDeque;

editor/src/workspace/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod workspace_message;
2+
mod workspace_message_handler;
3+
4+
#[doc(inline)]
5+
pub use workspace_message::{WorkspaceMessage, WorkspaceMessageDiscriminant};
6+
#[doc(inline)]
7+
pub use workspace_message_handler::WorkspaceMessageHandler;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::message_prelude::*;
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
#[remain::sorted]
6+
#[impl_message(Message, Workspace)]
7+
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
8+
pub enum WorkspaceMessage {
9+
// Messages
10+
NodeGraphToggleVisibility,
11+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::input::InputPreprocessorMessageHandler;
2+
use crate::message_prelude::*;
3+
4+
#[derive(Debug, Clone, Default)]
5+
pub struct WorkspaceMessageHandler {
6+
node_graph_visible: bool,
7+
}
8+
9+
impl MessageHandler<WorkspaceMessage, &InputPreprocessorMessageHandler> for WorkspaceMessageHandler {
10+
#[remain::check]
11+
fn process_action(&mut self, message: WorkspaceMessage, _ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
12+
use WorkspaceMessage::*;
13+
14+
#[remain::sorted]
15+
match message {
16+
// Messages
17+
NodeGraphToggleVisibility => {
18+
self.node_graph_visible = !self.node_graph_visible;
19+
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: self.node_graph_visible }.into());
20+
}
21+
}
22+
}
23+
24+
fn actions(&self) -> ActionList {
25+
actions!(WorkspaceMessageDiscriminant;
26+
NodeGraphToggleVisibility,
27+
)
28+
}
29+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

frontend/src/App.vue

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,12 @@
7070
--color-data-general-rgb: 197, 197, 197;
7171
--color-data-vector: #65bbe5;
7272
--color-data-vector-rgb: 101, 187, 229;
73+
--color-data-vector-dim: #4b778c;
74+
--color-data-vector-dim-rgb: 75, 119, 140;
7375
--color-data-raster: #e4bb72;
7476
--color-data-raster-rgb: 228, 187, 114;
77+
--color-data-raster-dim: #8b7752;
78+
--color-data-raster-dim-rgb: 139, 119, 82;
7579
--color-data-mask: #8d85c7;
7680
--color-data-mask-rgb: 141, 133, 199;
7781
--color-data-unused1: #d6536e;
@@ -258,9 +262,10 @@ import { createAutoSaveManager } from "@/lifetime/auto-save";
258262
import { initErrorHandling } from "@/lifetime/errors";
259263
import { createInputManager, InputManager } from "@/lifetime/input";
260264
import { createDialogState, DialogState } from "@/state/dialog";
261-
import { createDocumentsState, DocumentsState } from "@/state/documents";
262265
import { createFullscreenState, FullscreenState } from "@/state/fullscreen";
266+
import { createPortfolioState, PortfolioState } from "@/state/portfolio";
263267
import { createEditorState, EditorState } from "@/state/wasm-loader";
268+
import { createWorkspaceState, WorkspaceState } from "@/state/workspace";
264269
265270
import LayoutCol from "@/components/layout/LayoutCol.vue";
266271
import LayoutRow from "@/components/layout/LayoutRow.vue";
@@ -270,7 +275,8 @@ import MainWindow from "@/components/window/MainWindow.vue";
270275
declare module "@vue/runtime-core" {
271276
interface ComponentCustomProperties {
272277
dialog: DialogState;
273-
documents: DocumentsState;
278+
portfolio: PortfolioState;
279+
workspace: WorkspaceState;
274280
fullscreen: FullscreenState;
275281
editor: EditorState;
276282
// This must be set to optional because there is a time in the lifecycle of the component where inputManager is undefined.
@@ -284,7 +290,8 @@ export default defineComponent({
284290
return {
285291
editor: this.editor,
286292
dialog: this.dialog,
287-
documents: this.documents,
293+
portfolio: this.portfolio,
294+
workspace: this.workspace,
288295
fullscreen: this.fullscreen,
289296
inputManager: this.inputManager,
290297
};
@@ -295,15 +302,17 @@ export default defineComponent({
295302
296303
// Initialize other stateful Vue systems
297304
const dialog = createDialogState(editor);
298-
const documents = createDocumentsState(editor);
305+
const portfolio = createPortfolioState(editor);
306+
const workspace = createWorkspaceState(editor);
299307
const fullscreen = createFullscreenState();
300308
initErrorHandling(editor, dialog);
301-
createAutoSaveManager(editor, documents);
309+
createAutoSaveManager(editor, portfolio);
302310
303311
return {
304312
editor,
305313
dialog,
306-
documents,
314+
portfolio,
315+
workspace,
307316
fullscreen,
308317
showUnsupportedModal: !("BigInt64Array" in window),
309318
inputManager: undefined as undefined | InputManager,
@@ -315,7 +324,7 @@ export default defineComponent({
315324
},
316325
},
317326
mounted() {
318-
this.inputManager = createInputManager(this.editor, this.$el.parentElement, this.dialog, this.documents, this.fullscreen);
327+
this.inputManager = createInputManager(this.editor, this.$el.parentElement, this.dialog, this.portfolio, this.fullscreen);
319328
},
320329
beforeUnmount() {
321330
this.inputManager?.removeListeners();

frontend/src/components/panels/LayerTree.vue

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<LayoutCol class="layer-tree-panel">
2+
<LayoutCol class="layer-tree">
33
<LayoutRow class="options-bar">
44
<DropdownInput
55
v-model:selectedIndex="blendModeSelectedIndex"
@@ -32,7 +32,7 @@
3232
<IconButton :action="createEmptyFolder" :icon="'NodeFolder'" title="New Folder (Ctrl+Shift+N)" :size="24" />
3333
<IconButton :action="deleteSelectedLayers" :icon="'Trash'" title="Delete Selected (Del)" :size="24" />
3434
</LayoutRow>
35-
<LayoutRow class="layer-tree" :scrollableY="true">
35+
<LayoutRow class="layer-tree-rows" :scrollableY="true">
3636
<LayoutCol class="list" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="(e) => draggable && updateInsertLine(e)" @dragend="() => draggable && drop()">
3737
<LayoutRow
3838
class="layer-row"
@@ -70,10 +70,10 @@
7070
:title="`${listing.entry.name}\n${devMode ? 'Layer Path: ' + listing.entry.path.join(' / ') : ''}`.trim() || null"
7171
>
7272
<LayoutRow class="layer-type-icon">
73-
<IconLabel v-if="listing.entry.layer_type === 'Folder'" :icon="'NodeFolder'" title="Folder" />
74-
<IconLabel v-else-if="listing.entry.layer_type === 'Image'" :icon="'NodeImage'" title="Image" />
75-
<IconLabel v-else-if="listing.entry.layer_type === 'Shape'" :icon="'NodeShape'" title="Shape" />
76-
<IconLabel v-else-if="listing.entry.layer_type === 'Text'" :icon="'NodeText'" title="Path" />
73+
<IconLabel v-if="listing.entry.layer_type === 'Folder'" :icon="'NodeFolder'" :style="'node'" title="Folder" />
74+
<IconLabel v-else-if="listing.entry.layer_type === 'Image'" :icon="'NodeImage'" :style="'node'" title="Image" />
75+
<IconLabel v-else-if="listing.entry.layer_type === 'Shape'" :icon="'NodeShape'" :style="'node'" title="Shape" />
76+
<IconLabel v-else-if="listing.entry.layer_type === 'Text'" :icon="'NodeText'" :style="'node'" title="Path" />
7777
</LayoutRow>
7878
<LayoutRow class="layer-name" @dblclick="() => onEditLayerName(listing)">
7979
<input
@@ -98,7 +98,7 @@
9898
</template>
9999

100100
<style lang="scss">
101-
.layer-tree-panel {
101+
.layer-tree {
102102
min-height: 0;
103103
104104
.options-bar {
@@ -117,7 +117,7 @@
117117
}
118118
}
119119
120-
.layer-tree {
120+
.layer-tree-rows {
121121
margin-top: 4px;
122122
// Crop away the 1px border below the bottom layer entry when it uses the full space of this panel
123123
margin-bottom: -1px;
@@ -202,12 +202,6 @@
202202
.layer-type-icon {
203203
flex: 0 0 auto;
204204
margin: 0 4px;
205-
206-
.icon-label {
207-
border-radius: 2px;
208-
background: var(--color-node-background);
209-
fill: var(--color-node-icon);
210-
}
211205
}
212206
213207
.layer-name {
@@ -289,7 +283,7 @@
289283
<script lang="ts">
290284
import { defineComponent } from "vue";
291285
292-
import { BlendMode, DisplayDocumentLayerTreeStructure, UpdateDocumentLayer, LayerPanelEntry } from "@/dispatcher/js-messages";
286+
import { BlendMode, UpdateDocumentLayerTreeStructure, UpdateDocumentLayer, LayerPanelEntry } from "@/dispatcher/js-messages";
293287
294288
import LayoutCol from "@/components/layout/LayoutCol.vue";
295289
import LayoutRow from "@/components/layout/LayoutRow.vue";
@@ -573,14 +567,14 @@ export default defineComponent({
573567
},
574568
},
575569
mounted() {
576-
this.editor.dispatcher.subscribeJsMessage(DisplayDocumentLayerTreeStructure, (displayDocumentLayerTreeStructure) => {
570+
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentLayerTreeStructure, (updateDocumentLayerTreeStructure) => {
577571
const layerWithNameBeingEdited = this.layers.find((layer: LayerListingInfo) => layer.editingName);
578572
const layerPathWithNameBeingEdited = layerWithNameBeingEdited?.entry.path;
579573
const layerIdWithNameBeingEdited = layerPathWithNameBeingEdited?.slice(-1)[0];
580574
const path = [] as bigint[];
581575
this.layers = [] as LayerListingInfo[];
582576
583-
const recurse = (folder: DisplayDocumentLayerTreeStructure, layers: LayerListingInfo[], cache: Map<string, LayerPanelEntry>): void => {
577+
const recurse = (folder: UpdateDocumentLayerTreeStructure, layers: LayerListingInfo[], cache: Map<string, LayerPanelEntry>): void => {
584578
folder.children.forEach((item, index) => {
585579
// TODO: fix toString
586580
const layerId = BigInt(item.layerId.toString());
@@ -603,7 +597,7 @@ export default defineComponent({
603597
});
604598
};
605599
606-
recurse(displayDocumentLayerTreeStructure, this.layers, this.layerCache);
600+
recurse(updateDocumentLayerTreeStructure, this.layers, this.layerCache);
607601
});
608602
609603
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentLayer, (updateDocumentLayer) => {

0 commit comments

Comments
 (0)