Deploy from GitHub repository
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .cargo/config.toml +12 -0
- .devcontainer/devcontainer.json +32 -0
- .editorconfig +7 -0
- .envrc +1 -0
- .gitattributes +9 -0
- .nix/flake.lock +99 -0
- .nix/flake.nix +115 -0
- .nvmrc +1 -0
- .prettierrc +18 -0
- .vscode/extensions.json +20 -0
- .vscode/launch.json +48 -0
- .vscode/settings.json +56 -0
- Cargo.lock +0 -0
- Cargo.toml +179 -0
- LICENSE.txt +201 -0
- README.md +14 -4
- about.hbs +27 -0
- about.toml +36 -0
- app.py +29 -0
- demo-artwork/changing-seasons.graphite +0 -0
- demo-artwork/isometric-fountain.graphite +0 -0
- demo-artwork/marbled-mandelbrot.graphite +1 -0
- demo-artwork/painted-dreams.graphite +0 -0
- demo-artwork/parametric-dunescape.graphite +0 -0
- demo-artwork/procedural-string-lights.graphite +0 -0
- demo-artwork/red-dress.graphite +0 -0
- demo-artwork/valley-of-spires.graphite +0 -0
- deny.toml +194 -0
- editor/Cargo.toml +71 -0
- editor/build.rs +24 -0
- editor/src/application.rs +57 -0
- editor/src/consts.rs +151 -0
- editor/src/dispatcher.rs +562 -0
- editor/src/generate_ts_types.rs +34 -0
- editor/src/lib.rs +17 -0
- editor/src/macros.rs +56 -0
- editor/src/messages/animation/animation_message.rs +17 -0
- editor/src/messages/animation/animation_message_handler.rs +133 -0
- editor/src/messages/animation/mod.rs +9 -0
- editor/src/messages/broadcast/broadcast_event.rs +12 -0
- editor/src/messages/broadcast/broadcast_message.rs +19 -0
- editor/src/messages/broadcast/broadcast_message_handler.rs +27 -0
- editor/src/messages/broadcast/mod.rs +9 -0
- editor/src/messages/debug/debug_message.rs +10 -0
- editor/src/messages/debug/debug_message_handler.rs +49 -0
- editor/src/messages/debug/mod.rs +9 -0
- editor/src/messages/debug/utility_types.rs +7 -0
- editor/src/messages/dialog/dialog_message.rs +38 -0
- editor/src/messages/dialog/dialog_message_handler.rs +118 -0
- editor/src/messages/dialog/export_dialog/export_dialog_message.rs +13 -0
.cargo/config.toml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[target.wasm32-unknown-unknown]
|
2 |
+
rustflags = [
|
3 |
+
# Currently disabled because of https://github.com/GraphiteEditor/Graphite/issues/1262
|
4 |
+
# The current simd implementation leads to undefined behavior
|
5 |
+
#"-C",
|
6 |
+
#"target-feature=+simd128",
|
7 |
+
"-C",
|
8 |
+
"target-feature=+bulk-memory",
|
9 |
+
"-C",
|
10 |
+
"link-arg=--max-memory=4294967296",
|
11 |
+
"--cfg=web_sys_unstable_apis",
|
12 |
+
]
|
.devcontainer/devcontainer.json
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"image": "mcr.microsoft.com/devcontainers/base:debian",
|
3 |
+
"features": {
|
4 |
+
"ghcr.io/devcontainers/features/rust:1": {
|
5 |
+
"profile": "default"
|
6 |
+
},
|
7 |
+
"ghcr.io/devcontainers/features/node:1": {}
|
8 |
+
},
|
9 |
+
"onCreateCommand": "cargo install cargo-watch wasm-pack cargo-about && cargo install -f [email protected]",
|
10 |
+
"customizations": {
|
11 |
+
"vscode": {
|
12 |
+
// NOTE: Keep this in sync with `.vscode/extensions.json`
|
13 |
+
"extensions": [
|
14 |
+
// Rust
|
15 |
+
"rust-lang.rust-analyzer",
|
16 |
+
"tamasfe.even-better-toml",
|
17 |
+
// Web
|
18 |
+
"dbaeumer.vscode-eslint",
|
19 |
+
"svelte.svelte-vscode",
|
20 |
+
"vitaliymaz.vscode-svg-previewer",
|
21 |
+
// Code quality
|
22 |
+
"wayou.vscode-todo-highlight",
|
23 |
+
"streetsidesoftware.code-spell-checker",
|
24 |
+
// Helpful
|
25 |
+
"mhutchie.git-graph",
|
26 |
+
"waderyan.gitblame",
|
27 |
+
"qezhu.gitlink",
|
28 |
+
"wmaurer.change-case"
|
29 |
+
]
|
30 |
+
}
|
31 |
+
}
|
32 |
+
}
|
.editorconfig
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[*.{rs,js,ts,svelte,json,toml,svg,html,css,scss}]
|
2 |
+
indent_style = tab
|
3 |
+
indent_size = 4
|
4 |
+
end_of_line = lf
|
5 |
+
trim_trailing_whitespace = true
|
6 |
+
insert_final_newline = true
|
7 |
+
max_line_length = 200
|
.envrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
use flake .nix
|
.gitattributes
CHANGED
@@ -33,3 +33,12 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
frontend/src-tauri/icons/icon.icns filter=lfs diff=lfs merge=lfs -text
|
37 |
+
node-graph/graphene-cli/test_files/cat.jpg filter=lfs diff=lfs merge=lfs -text
|
38 |
+
node-graph/graphene-cli/test_files/cow_transparent.png filter=lfs diff=lfs merge=lfs -text
|
39 |
+
node-graph/graphene-cli/test_files/duck.jpg filter=lfs diff=lfs merge=lfs -text
|
40 |
+
node-graph/graphene-cli/test_files/football.jpg filter=lfs diff=lfs merge=lfs -text
|
41 |
+
node-graph/graphene-cli/test_files/mansion.jpg filter=lfs diff=lfs merge=lfs -text
|
42 |
+
node-graph/graphene-cli/test_files/paper.jpg filter=lfs diff=lfs merge=lfs -text
|
43 |
+
node-graph/graphene-cli/test_files/pizza.jpg filter=lfs diff=lfs merge=lfs -text
|
44 |
+
node-graph/graphene-cli/test_files/pizza_transparent.png filter=lfs diff=lfs merge=lfs -text
|
.nix/flake.lock
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"nodes": {
|
3 |
+
"flake-utils": {
|
4 |
+
"inputs": {
|
5 |
+
"systems": "systems"
|
6 |
+
},
|
7 |
+
"locked": {
|
8 |
+
"lastModified": 1731533236,
|
9 |
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
10 |
+
"owner": "numtide",
|
11 |
+
"repo": "flake-utils",
|
12 |
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
13 |
+
"type": "github"
|
14 |
+
},
|
15 |
+
"original": {
|
16 |
+
"owner": "numtide",
|
17 |
+
"repo": "flake-utils",
|
18 |
+
"type": "github"
|
19 |
+
}
|
20 |
+
},
|
21 |
+
"nixpkgs": {
|
22 |
+
"locked": {
|
23 |
+
"lastModified": 1748190013,
|
24 |
+
"narHash": "sha256-R5HJFflOfsP5FBtk+zE8FpL8uqE7n62jqOsADvVshhE=",
|
25 |
+
"owner": "nixos",
|
26 |
+
"repo": "nixpkgs",
|
27 |
+
"rev": "62b852f6c6742134ade1abdd2a21685fd617a291",
|
28 |
+
"type": "github"
|
29 |
+
},
|
30 |
+
"original": {
|
31 |
+
"owner": "nixos",
|
32 |
+
"ref": "nixos-unstable",
|
33 |
+
"repo": "nixpkgs",
|
34 |
+
"type": "github"
|
35 |
+
}
|
36 |
+
},
|
37 |
+
"nixpkgs-unstable": {
|
38 |
+
"locked": {
|
39 |
+
"lastModified": 1748190013,
|
40 |
+
"narHash": "sha256-R5HJFflOfsP5FBtk+zE8FpL8uqE7n62jqOsADvVshhE=",
|
41 |
+
"owner": "nixos",
|
42 |
+
"repo": "nixpkgs",
|
43 |
+
"rev": "62b852f6c6742134ade1abdd2a21685fd617a291",
|
44 |
+
"type": "github"
|
45 |
+
},
|
46 |
+
"original": {
|
47 |
+
"owner": "nixos",
|
48 |
+
"ref": "nixos-unstable",
|
49 |
+
"repo": "nixpkgs",
|
50 |
+
"type": "github"
|
51 |
+
}
|
52 |
+
},
|
53 |
+
"root": {
|
54 |
+
"inputs": {
|
55 |
+
"flake-utils": "flake-utils",
|
56 |
+
"nixpkgs": "nixpkgs",
|
57 |
+
"nixpkgs-unstable": "nixpkgs-unstable",
|
58 |
+
"rust-overlay": "rust-overlay"
|
59 |
+
}
|
60 |
+
},
|
61 |
+
"rust-overlay": {
|
62 |
+
"inputs": {
|
63 |
+
"nixpkgs": [
|
64 |
+
"nixpkgs"
|
65 |
+
]
|
66 |
+
},
|
67 |
+
"locked": {
|
68 |
+
"lastModified": 1748399823,
|
69 |
+
"narHash": "sha256-kahD8D5hOXOsGbNdoLLnqCL887cjHkx98Izc37nDjlA=",
|
70 |
+
"owner": "oxalica",
|
71 |
+
"repo": "rust-overlay",
|
72 |
+
"rev": "d68a69dc71bc19beb3479800392112c2f6218159",
|
73 |
+
"type": "github"
|
74 |
+
},
|
75 |
+
"original": {
|
76 |
+
"owner": "oxalica",
|
77 |
+
"repo": "rust-overlay",
|
78 |
+
"type": "github"
|
79 |
+
}
|
80 |
+
},
|
81 |
+
"systems": {
|
82 |
+
"locked": {
|
83 |
+
"lastModified": 1681028828,
|
84 |
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
85 |
+
"owner": "nix-systems",
|
86 |
+
"repo": "default",
|
87 |
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
88 |
+
"type": "github"
|
89 |
+
},
|
90 |
+
"original": {
|
91 |
+
"owner": "nix-systems",
|
92 |
+
"repo": "default",
|
93 |
+
"type": "github"
|
94 |
+
}
|
95 |
+
}
|
96 |
+
},
|
97 |
+
"root": "root",
|
98 |
+
"version": 7
|
99 |
+
}
|
.nix/flake.nix
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This is a helper file for people using NixOS as their operating system.
|
2 |
+
# If you don't know what this file does, you can safely ignore it.
|
3 |
+
# This file defines both the development environment for the project.
|
4 |
+
#
|
5 |
+
# Development Environment:
|
6 |
+
# - Provides all necessary tools for Rust/WASM development
|
7 |
+
# - Includes Tauri dependencies for desktop app development
|
8 |
+
# - Sets up profiling and debugging tools
|
9 |
+
# - Configures mold as the default linker for faster builds
|
10 |
+
#
|
11 |
+
#
|
12 |
+
# Usage:
|
13 |
+
# - Development shell: `nix develop`
|
14 |
+
# - Run in dev shell with direnv: add `use flake` to .envrc
|
15 |
+
{
|
16 |
+
description = "Development environment and build configuration";
|
17 |
+
|
18 |
+
inputs = {
|
19 |
+
# This url should be changed to match your system packages if you work on tauri because you need to use the same graphics library versions as the ones used by your system
|
20 |
+
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
21 |
+
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
22 |
+
rust-overlay = {
|
23 |
+
url = "github:oxalica/rust-overlay";
|
24 |
+
inputs.nixpkgs.follows = "nixpkgs";
|
25 |
+
};
|
26 |
+
flake-utils.url = "github:numtide/flake-utils";
|
27 |
+
};
|
28 |
+
|
29 |
+
outputs = { nixpkgs, nixpkgs-unstable, rust-overlay, flake-utils, ... }:
|
30 |
+
flake-utils.lib.eachDefaultSystem (system:
|
31 |
+
let
|
32 |
+
overlays = [ (import rust-overlay) ];
|
33 |
+
pkgs = import nixpkgs {
|
34 |
+
inherit system overlays;
|
35 |
+
};
|
36 |
+
pkgs-unstable = import nixpkgs-unstable {
|
37 |
+
inherit system overlays;
|
38 |
+
};
|
39 |
+
|
40 |
+
rustc-wasm = pkgs.rust-bin.stable.latest.default.override {
|
41 |
+
targets = [ "wasm32-unknown-unknown" ];
|
42 |
+
extensions = [ "rust-src" "rust-analyzer" "clippy" "cargo" ];
|
43 |
+
};
|
44 |
+
|
45 |
+
# Shared build inputs - system libraries that need to be in LD_LIBRARY_PATH
|
46 |
+
buildInputs = with pkgs; [
|
47 |
+
# System libraries
|
48 |
+
openssl
|
49 |
+
vulkan-loader
|
50 |
+
mesa
|
51 |
+
libraw
|
52 |
+
|
53 |
+
|
54 |
+
# Tauri dependencies: keep in sync with https://v2.tauri.app/start/prerequisites/#system-dependencies (under the NixOS tab)
|
55 |
+
at-spi2-atk
|
56 |
+
atkmm
|
57 |
+
cairo
|
58 |
+
gdk-pixbuf
|
59 |
+
glib
|
60 |
+
gtk3
|
61 |
+
harfbuzz
|
62 |
+
librsvg
|
63 |
+
libsoup_3
|
64 |
+
pango
|
65 |
+
webkitgtk_4_1
|
66 |
+
openssl
|
67 |
+
];
|
68 |
+
|
69 |
+
# Development tools that don't need to be in LD_LIBRARY_PATH
|
70 |
+
buildTools = [
|
71 |
+
rustc-wasm
|
72 |
+
pkgs.nodejs
|
73 |
+
pkgs.nodePackages.npm
|
74 |
+
pkgs.binaryen
|
75 |
+
pkgs.wasm-bindgen-cli
|
76 |
+
pkgs-unstable.wasm-pack
|
77 |
+
pkgs.pkg-config
|
78 |
+
pkgs.git
|
79 |
+
pkgs.gobject-introspection
|
80 |
+
pkgs-unstable.cargo-tauri
|
81 |
+
pkgs-unstable.cargo-about
|
82 |
+
|
83 |
+
# Linker
|
84 |
+
pkgs.mold
|
85 |
+
];
|
86 |
+
# Development tools that don't need to be in LD_LIBRARY_PATH
|
87 |
+
devTools = with pkgs; [
|
88 |
+
cargo-watch
|
89 |
+
cargo-nextest
|
90 |
+
cargo-expand
|
91 |
+
|
92 |
+
# Profiling tools
|
93 |
+
gnuplot
|
94 |
+
samply
|
95 |
+
cargo-flamegraph
|
96 |
+
|
97 |
+
];
|
98 |
+
in
|
99 |
+
{
|
100 |
+
# Development shell configuration
|
101 |
+
devShells.default = pkgs.mkShell {
|
102 |
+
packages = buildInputs ++ buildTools ++ devTools;
|
103 |
+
|
104 |
+
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
105 |
+
GIO_MODULE_DIR="${pkgs.glib-networking}/lib/gio/modules/";
|
106 |
+
XDG_DATA_DIRS="${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS";
|
107 |
+
|
108 |
+
|
109 |
+
shellHook = ''
|
110 |
+
alias cargo='mold --run cargo'
|
111 |
+
'';
|
112 |
+
};
|
113 |
+
}
|
114 |
+
);
|
115 |
+
}
|
.nvmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
20
|
.prettierrc
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"singleQuote": false,
|
3 |
+
"useTabs": true,
|
4 |
+
"tabWidth": 4,
|
5 |
+
"printWidth": 200,
|
6 |
+
"overrides": [
|
7 |
+
{
|
8 |
+
"files": [
|
9 |
+
"*.yml",
|
10 |
+
"*.yaml"
|
11 |
+
],
|
12 |
+
"options": {
|
13 |
+
"useTabs": false,
|
14 |
+
"tabWidth": 2
|
15 |
+
}
|
16 |
+
}
|
17 |
+
]
|
18 |
+
}
|
.vscode/extensions.json
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
// NOTE: Keep this in sync with `.devcontainer/devcontainer.json`
|
3 |
+
"recommendations": [
|
4 |
+
// Rust
|
5 |
+
"rust-lang.rust-analyzer",
|
6 |
+
"tamasfe.even-better-toml",
|
7 |
+
// Web
|
8 |
+
"dbaeumer.vscode-eslint",
|
9 |
+
"svelte.svelte-vscode",
|
10 |
+
"vitaliymaz.vscode-svg-previewer",
|
11 |
+
// Code quality
|
12 |
+
"wayou.vscode-todo-highlight",
|
13 |
+
"streetsidesoftware.code-spell-checker",
|
14 |
+
// Helpful
|
15 |
+
"mhutchie.git-graph",
|
16 |
+
"waderyan.gitblame",
|
17 |
+
"qezhu.gitlink",
|
18 |
+
"wmaurer.change-case"
|
19 |
+
]
|
20 |
+
}
|
.vscode/launch.json
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
// Use IntelliSense to learn about possible attributes.
|
3 |
+
// Hover to view descriptions of existing attributes.
|
4 |
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
5 |
+
"version": "0.2.0",
|
6 |
+
"configurations": [
|
7 |
+
{
|
8 |
+
"type": "lldb",
|
9 |
+
"request": "launch",
|
10 |
+
"name": "Graphite debug executable",
|
11 |
+
"cargo": {
|
12 |
+
"args": [
|
13 |
+
"build",
|
14 |
+
"--bin=graphite",
|
15 |
+
"--package=graphite",
|
16 |
+
],
|
17 |
+
"filter": {
|
18 |
+
"name": "graphite",
|
19 |
+
"kind": "bin",
|
20 |
+
},
|
21 |
+
},
|
22 |
+
"args": [],
|
23 |
+
"cwd": "${workspaceFolder}",
|
24 |
+
"env": {
|
25 |
+
"RUST_LOG": "error",
|
26 |
+
},
|
27 |
+
},
|
28 |
+
{
|
29 |
+
"type": "lldb",
|
30 |
+
"request": "launch",
|
31 |
+
"name": "Debug unit tests in executable 'graphite'",
|
32 |
+
"cargo": {
|
33 |
+
"args": [
|
34 |
+
"test",
|
35 |
+
"--no-run",
|
36 |
+
"--bin=graphite",
|
37 |
+
"--package=graphite",
|
38 |
+
],
|
39 |
+
"filter": {
|
40 |
+
"name": "graphite",
|
41 |
+
"kind": "bin",
|
42 |
+
},
|
43 |
+
},
|
44 |
+
"args": [],
|
45 |
+
"cwd": "${workspaceFolder}",
|
46 |
+
},
|
47 |
+
],
|
48 |
+
}
|
.vscode/settings.json
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
// Rust: save on format
|
3 |
+
"[rust]": {
|
4 |
+
"editor.formatOnSave": true,
|
5 |
+
"editor.formatOnPaste": true,
|
6 |
+
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
7 |
+
},
|
8 |
+
// Web: save on format
|
9 |
+
"[javascript][typescript][svelte]": {
|
10 |
+
"editor.codeActionsOnSave": {
|
11 |
+
"source.fixAll.eslint": "explicit"
|
12 |
+
},
|
13 |
+
"editor.formatOnSave": true,
|
14 |
+
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
15 |
+
},
|
16 |
+
"[scss]": {
|
17 |
+
"editor.codeActionsOnSave": {
|
18 |
+
"source.fixAll.eslint": "explicit"
|
19 |
+
},
|
20 |
+
"editor.formatOnSave": true,
|
21 |
+
// Configured in `.prettierrc`
|
22 |
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
23 |
+
},
|
24 |
+
"[json][jsonc][yaml][github-actions-workflow]": {
|
25 |
+
"editor.formatOnSave": true,
|
26 |
+
// Configured in `.prettierrc`
|
27 |
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
28 |
+
},
|
29 |
+
// Handlebars: don't save on format
|
30 |
+
// (`about.hbs` is used by Cargo About to encode license information)
|
31 |
+
"[handlebars]": {
|
32 |
+
"editor.formatOnSave": false
|
33 |
+
},
|
34 |
+
// Rust Analyzer config
|
35 |
+
"rust-analyzer.cargo.allTargets": false,
|
36 |
+
// ESLint config
|
37 |
+
"eslint.format.enable": true,
|
38 |
+
"eslint.workingDirectories": ["./frontend", "./website/other/bezier-rs-demos", "./website"],
|
39 |
+
"eslint.validate": ["javascript", "typescript", "svelte"],
|
40 |
+
// Svelte config
|
41 |
+
"svelte.plugin.svelte.compilerWarnings": {
|
42 |
+
// NOTICE: Keep this list in sync with the list in `frontend/vite.config.ts`
|
43 |
+
"css-unused-selector": "ignore",
|
44 |
+
"vite-plugin-svelte-css-no-scopable-elements": "ignore",
|
45 |
+
"a11y-no-static-element-interactions": "ignore",
|
46 |
+
"a11y-no-noninteractive-element-interactions": "ignore",
|
47 |
+
"a11y-click-events-have-key-events": "ignore"
|
48 |
+
},
|
49 |
+
// VS Code config
|
50 |
+
"html.format.wrapLineLength": 200,
|
51 |
+
"files.eol": "\n",
|
52 |
+
"files.insertFinalNewline": true,
|
53 |
+
"files.associations": {
|
54 |
+
"*.graphite": "json"
|
55 |
+
}
|
56 |
+
}
|
Cargo.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|
Cargo.toml
ADDED
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[workspace]
|
2 |
+
members = [
|
3 |
+
"editor",
|
4 |
+
"proc-macros",
|
5 |
+
"frontend/wasm",
|
6 |
+
"frontend/src-tauri",
|
7 |
+
"node-graph/gapplication-io",
|
8 |
+
"node-graph/gbrush",
|
9 |
+
"node-graph/gcore",
|
10 |
+
"node-graph/gstd",
|
11 |
+
"node-graph/gmath-nodes",
|
12 |
+
"node-graph/gpath-bool",
|
13 |
+
"node-graph/graph-craft",
|
14 |
+
"node-graph/graphene-cli",
|
15 |
+
"node-graph/graster-nodes",
|
16 |
+
"node-graph/gsvg-renderer",
|
17 |
+
"node-graph/interpreted-executor",
|
18 |
+
"node-graph/node-macro",
|
19 |
+
"node-graph/preprocessor",
|
20 |
+
"libraries/dyn-any",
|
21 |
+
"libraries/path-bool",
|
22 |
+
"libraries/bezier-rs",
|
23 |
+
"libraries/math-parser",
|
24 |
+
"website/other/bezier-rs-demos/wasm",
|
25 |
+
]
|
26 |
+
default-members = [
|
27 |
+
"editor",
|
28 |
+
"frontend/wasm",
|
29 |
+
"node-graph/gbrush",
|
30 |
+
"node-graph/gcore",
|
31 |
+
"node-graph/gstd",
|
32 |
+
"node-graph/gmath-nodes",
|
33 |
+
"node-graph/gpath-bool",
|
34 |
+
"node-graph/graph-craft",
|
35 |
+
"node-graph/graphene-cli",
|
36 |
+
"node-graph/graster-nodes",
|
37 |
+
"node-graph/gsvg-renderer",
|
38 |
+
"node-graph/interpreted-executor",
|
39 |
+
"node-graph/node-macro",
|
40 |
+
]
|
41 |
+
resolver = "2"
|
42 |
+
|
43 |
+
[workspace.dependencies]
|
44 |
+
# Local dependencies
|
45 |
+
bezier-rs = { path = "libraries/bezier-rs", features = ["dyn-any", "serde"] }
|
46 |
+
dyn-any = { path = "libraries/dyn-any", features = ["derive", "glam", "reqwest", "log-bad-types", "rc"] }
|
47 |
+
preprocessor = { path = "node-graph/preprocessor"}
|
48 |
+
math-parser = { path = "libraries/math-parser" }
|
49 |
+
path-bool = { path = "libraries/path-bool" }
|
50 |
+
graphene-application-io = { path = "node-graph/gapplication-io" }
|
51 |
+
graphene-brush = { path = "node-graph/gbrush" }
|
52 |
+
graphene-core = { path = "node-graph/gcore" }
|
53 |
+
graphene-math-nodes = { path = "node-graph/gmath-nodes" }
|
54 |
+
graphene-path-bool = { path = "node-graph/gpath-bool" }
|
55 |
+
graph-craft = { path = "node-graph/graph-craft" }
|
56 |
+
graphene-raster-nodes = { path = "node-graph/graster-nodes" }
|
57 |
+
graphene-std = { path = "node-graph/gstd" }
|
58 |
+
graphene-svg-renderer = { path = "node-graph/gsvg-renderer" }
|
59 |
+
interpreted-executor = { path = "node-graph/interpreted-executor" }
|
60 |
+
node-macro = { path = "node-graph/node-macro" }
|
61 |
+
wgpu-executor = { path = "node-graph/wgpu-executor" }
|
62 |
+
graphite-proc-macros = { path = "proc-macros" }
|
63 |
+
|
64 |
+
# Workspace dependencies
|
65 |
+
rustc-hash = "2.0"
|
66 |
+
bytemuck = { version = "1.13", features = ["derive"] }
|
67 |
+
serde = { version = "1.0", features = ["derive", "rc"] }
|
68 |
+
serde_json = "1.0"
|
69 |
+
serde-wasm-bindgen = "0.6"
|
70 |
+
reqwest = { version = "0.12", features = ["blocking", "rustls-tls", "json"] }
|
71 |
+
futures = "0.3"
|
72 |
+
env_logger = "0.11"
|
73 |
+
log = "0.4"
|
74 |
+
bitflags = { version = "2.4", features = ["serde"] }
|
75 |
+
ctor = "0.2"
|
76 |
+
convert_case = "0.7"
|
77 |
+
derivative = "2.2"
|
78 |
+
thiserror = "2"
|
79 |
+
anyhow = "1.0"
|
80 |
+
proc-macro2 = { version = "1", features = [ "span-locations" ] }
|
81 |
+
quote = "1.0"
|
82 |
+
axum = "0.8"
|
83 |
+
chrono = "0.4"
|
84 |
+
ron = "0.8"
|
85 |
+
fastnoise-lite = "1.1"
|
86 |
+
wgpu = { version = "23", features = [
|
87 |
+
# We don't have wgpu on multiple threads (yet) https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#wgpu-types-now-send-sync-on-wasm
|
88 |
+
"fragile-send-sync-non-atomic-wasm",
|
89 |
+
"spirv",
|
90 |
+
"strict_asserts",
|
91 |
+
] }
|
92 |
+
once_cell = "1.13" # Remove when `core::cell::LazyCell` (<https://doc.rust-lang.org/core/cell/struct.LazyCell.html>) is stabilized in Rust 1.80 and we bump our MSRV
|
93 |
+
wasm-bindgen = "=0.2.100" # NOTICE: ensure this stays in sync with the `wasm-bindgen-cli` version in `website/content/volunteer/guide/project-setup/_index.md`. We pin this version because wasm-bindgen upgrades may break various things.
|
94 |
+
wasm-bindgen-futures = "0.4"
|
95 |
+
js-sys = "=0.3.77"
|
96 |
+
web-sys = { version = "=0.3.77", features = [
|
97 |
+
"Document",
|
98 |
+
"DomRect",
|
99 |
+
"Element",
|
100 |
+
"HtmlCanvasElement",
|
101 |
+
"CanvasRenderingContext2d",
|
102 |
+
"CanvasPattern",
|
103 |
+
"OffscreenCanvas",
|
104 |
+
"OffscreenCanvasRenderingContext2d",
|
105 |
+
"TextMetrics",
|
106 |
+
"Window",
|
107 |
+
"IdleRequestOptions",
|
108 |
+
"ImageData",
|
109 |
+
"Navigator",
|
110 |
+
"Gpu",
|
111 |
+
"HtmlImageElement",
|
112 |
+
"ImageBitmapRenderingContext",
|
113 |
+
] }
|
114 |
+
winit = "0.29"
|
115 |
+
url = "2.5"
|
116 |
+
tokio = { version = "1.29", features = ["fs", "macros", "io-std", "rt"] }
|
117 |
+
vello = { git = "https://github.com/linebender/vello.git", rev = "3275ec8" } # TODO switch back to stable when a release is made
|
118 |
+
resvg = "0.44"
|
119 |
+
usvg = "0.44"
|
120 |
+
rand = { version = "0.9", default-features = false, features = ["std_rng"] }
|
121 |
+
rand_chacha = "0.9"
|
122 |
+
glam = { version = "0.29", default-features = false, features = ["serde", "scalar-math", "debug-glam-assert"] }
|
123 |
+
base64 = "0.22"
|
124 |
+
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp"] }
|
125 |
+
parley = "0.5.0"
|
126 |
+
skrifa = "0.32.0"
|
127 |
+
pretty_assertions = "1.4.1"
|
128 |
+
fern = { version = "0.7", features = ["colored"] }
|
129 |
+
num_enum = "0.7"
|
130 |
+
num-derive = "0.4"
|
131 |
+
num-traits = { version = "0.2", default-features = false, features = ["i128"] }
|
132 |
+
specta = { version = "2.0.0-rc.22", features = [
|
133 |
+
"glam",
|
134 |
+
"derive",
|
135 |
+
# "typescript",
|
136 |
+
] }
|
137 |
+
syn = { version = "2.0", default-features = false, features = [
|
138 |
+
"full",
|
139 |
+
"derive",
|
140 |
+
"parsing",
|
141 |
+
"printing",
|
142 |
+
"visit-mut",
|
143 |
+
"visit",
|
144 |
+
"clone-impls",
|
145 |
+
"extra-traits",
|
146 |
+
"proc-macro",
|
147 |
+
] }
|
148 |
+
kurbo = { version = "0.11.0", features = ["serde"] }
|
149 |
+
petgraph = { version = "0.7.1", default-features = false, features = [
|
150 |
+
"graphmap",
|
151 |
+
] }
|
152 |
+
half = { version = "2.4.1", default-features = false, features = ["bytemuck", "serde"] }
|
153 |
+
tinyvec = { version = "1", features = ["std"] }
|
154 |
+
criterion = { version = "0.5", features = ["html_reports"] }
|
155 |
+
iai-callgrind = { version = "0.12.3" }
|
156 |
+
ndarray = "0.16.1"
|
157 |
+
|
158 |
+
[profile.dev]
|
159 |
+
opt-level = 1
|
160 |
+
|
161 |
+
[profile.dev.package]
|
162 |
+
graphite-editor = { opt-level = 1 }
|
163 |
+
graphene-core = { opt-level = 1 }
|
164 |
+
graphene-std = { opt-level = 1 }
|
165 |
+
interpreted-executor = { opt-level = 1 } # This is a mitigation for https://github.com/rustwasm/wasm-pack/issues/981 which is needed because the node_registry function is too large
|
166 |
+
graphite-proc-macros = { opt-level = 1 }
|
167 |
+
image = { opt-level = 2 }
|
168 |
+
rustc-hash = { opt-level = 3 }
|
169 |
+
serde_derive = { opt-level = 1 }
|
170 |
+
specta-macros = { opt-level = 1 }
|
171 |
+
syn = { opt-level = 1 }
|
172 |
+
|
173 |
+
[profile.release]
|
174 |
+
lto = "thin"
|
175 |
+
debug = true
|
176 |
+
|
177 |
+
[profile.profiling]
|
178 |
+
inherits = "release"
|
179 |
+
debug = true
|
LICENSE.txt
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright [yyyy] [name of copyright owner]
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
README.md
CHANGED
@@ -1,12 +1,22 @@
|
|
1 |
---
|
2 |
title: Graphite2
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 5.35.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
title: Graphite2
|
3 |
+
emoji: 🚀
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
sdk: gradio
|
7 |
+
sdk_version: "5.35.0"
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
12 |
+
# Graphite2
|
13 |
+
|
14 |
+
<a href="https://graphite.rs/">
|
15 |
+
|
16 |
+
Deployed from: https://github.com/seawolf2357/Graphite
|
17 |
+
|
18 |
+
## Features
|
19 |
+
This Space provides a Gradio interface for the repository's main functionality.
|
20 |
+
The app.py was automatically generated based on repository analysis.
|
21 |
+
|
22 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
about.hbs
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{{!
|
2 |
+
Be careful to prevent auto-formatting from breaking this file's indentation.
|
3 |
+
Replace this file with JSON output once this is resolved: https://github.com/EmbarkStudios/cargo-about/issues/73
|
4 |
+
|
5 |
+
The `GENERATED_BY_CARGO_ABOUT` prefix is a JS labeled statement
|
6 |
+
(<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label>)
|
7 |
+
used so the reader of the generated file can verify the file does indeed start with that string,
|
8 |
+
while remaining valid JS for subsequent parsing.
|
9 |
+
}}
|
10 |
+
GENERATED_BY_CARGO_ABOUT: [
|
11 |
+
{{#each licenses}}
|
12 |
+
{
|
13 |
+
licenseName: `{{name}}`,
|
14 |
+
licenseText: `{{text}}`,
|
15 |
+
packages: [
|
16 |
+
{{#each used_by}}
|
17 |
+
{
|
18 |
+
name: `{{crate.name}}`,
|
19 |
+
version: `{{crate.version}}`,
|
20 |
+
author: `{{crate.authors}}`,
|
21 |
+
repository: `{{crate.repository}}`,
|
22 |
+
},
|
23 |
+
{{/each}}
|
24 |
+
],
|
25 |
+
},
|
26 |
+
{{/each}}
|
27 |
+
]
|
about.toml
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Keep this list in sync with those in `/deny.toml` and `/frontend/vite.config.ts`.
|
2 |
+
accepted = [
|
3 |
+
"Apache-2.0 WITH LLVM-exception",
|
4 |
+
"Apache-2.0",
|
5 |
+
"BSD-2-Clause",
|
6 |
+
"BSD-3-Clause",
|
7 |
+
"BSL-1.0",
|
8 |
+
"CC0-1.0",
|
9 |
+
"ISC",
|
10 |
+
"MIT-0",
|
11 |
+
"MIT",
|
12 |
+
"MPL-2.0",
|
13 |
+
"OpenSSL",
|
14 |
+
"Unicode-3.0",
|
15 |
+
"Unicode-DFS-2016",
|
16 |
+
"Zlib",
|
17 |
+
]
|
18 |
+
workarounds = ["ring"]
|
19 |
+
ignore-build-dependencies = true
|
20 |
+
ignore-dev-dependencies = true
|
21 |
+
# Clearly Defined's API would occasionally (every few months) return errors for at least a full day (maybe some weird rate limiting?), but we can just disable to perform local checking (see #1653)
|
22 |
+
no-clearly-defined = true
|
23 |
+
|
24 |
+
# https://raw.githubusercontent.com/briansmith/webpki/main/LICENSE
|
25 |
+
# is the ISC license but test code within the repo is BSD-3-Clause, but is not compiled into the crate when we use it
|
26 |
+
[webpki.clarify]
|
27 |
+
license = "ISC"
|
28 |
+
[[webpki.clarify.files]]
|
29 |
+
path = "LICENSE"
|
30 |
+
checksum = "5b698ca13897be3afdb7174256fa1574f8c6892b8bea1a66dd6469d3fe27885a"
|
31 |
+
|
32 |
+
[rustls-webpki.clarify]
|
33 |
+
license = "ISC"
|
34 |
+
[[rustls-webpki.clarify.files]]
|
35 |
+
path = "LICENSE"
|
36 |
+
checksum = "5b698ca13897be3afdb7174256fa1574f8c6892b8bea1a66dd6469d3fe27885a"
|
app.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
# Function to simulate the Graphite tool functionality
|
4 |
+
|
5 |
+
def create_graphics_tool(api_key, description):
|
6 |
+
if not api_key:
|
7 |
+
return "Error: API key is required. Please enter a valid API key."
|
8 |
+
if not description:
|
9 |
+
return "Error: Description is required. Please provide a description for the graphics tool."
|
10 |
+
# Simulate some processing (this would be replaced with actual functionality)
|
11 |
+
return f"Graphics tool created with description: '{description}'!"
|
12 |
+
|
13 |
+
# Gradio interface setup
|
14 |
+
|
15 |
+
def main():
|
16 |
+
with gr.Blocks() as demo:
|
17 |
+
gr.Markdown("# Graphite Graphics Tool\n\nCreate your own graphics tool using Graphite!\n\n## Instructions:\n1. Enter your API key (required).\n2. Provide a description for your graphics tool (required).\n3. Click 'Create Tool' to simulate the creation of a graphics tool.")
|
18 |
+
|
19 |
+
api_key = gr.Textbox(label="API Key", placeholder="Enter your API key here...")
|
20 |
+
description = gr.Textbox(label="Tool Description", placeholder="Describe your graphics tool...")
|
21 |
+
create_button = gr.Button("Create Tool")
|
22 |
+
output = gr.Textbox(label="Output")
|
23 |
+
|
24 |
+
create_button.click(create_graphics_tool, inputs=[api_key, description], outputs=output)
|
25 |
+
|
26 |
+
demo.launch()
|
27 |
+
|
28 |
+
if __name__ == '__main__':
|
29 |
+
main()
|
demo-artwork/changing-seasons.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/isometric-fountain.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/marbled-mandelbrot.graphite
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"network_interface":{"network":{"exports":[{"Node":{"node_id":12241147352993594415,"output_index":0,"lambda":false}}],"nodes":[[4388711862172196665,{"inputs":[],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_std::raster::MandelbrotNode"}},"visible":true,"skip_deduplication":false}],[3606681156406984991,{"inputs":[{"Node":{"node_id":6323350524796370485,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GradientStops":[[0.3237704918032787,{"red":1.0,"green":0.0,"blue":1.0,"alpha":0.1}],[0.5655737704918032,{"red":0.8901961,"green":0.8117647,"blue":0.39215687,"alpha":1.0}],[0.8155737704918032,{"red":0.0,"green":1.0,"blue":1.0,"alpha":0.5}]]},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::raster::adjustments::GradientMapNode"}},"visible":true,"skip_deduplication":false}],[6029481207635803402,{"inputs":[{"Node":{"node_id":4388711862172196665,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F64":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1000.0,1000.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Concrete":{"name":"graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>","alias":null}},"import_index":0}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}],[1,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"import_type":{"Concrete":{"name":"glam::f64::dvec2::DVec2","alias":null}},"import_index":1}},{"Network":{"import_type":{"Concrete":{"name":"f64","alias":null}},"import_index":2}},{"Network":{"import_type":{"Concrete":{"name":"glam::f64::dvec2::DVec2","alias":null}},"import_index":3}},{"Network":{"import_type":{"Concrete":{"name":"glam::f64::dvec2::DVec2","alias":null}},"import_index":4}},{"Network":{"import_type":{"Concrete":{"name":"glam::f64::dvec2::DVec2","alias":null}},"import_index":5}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::transform_nodes::TransformNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false}],[7624113397561636853,{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Node":{"node_id":3606681156406984991,"output_index":0,"lambda":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":3,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":1}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToElementNode"}},"visible":true,"skip_deduplication":false}],[3,{"inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Reflection":"DocumentNodePath"}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::LayerNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}],[1,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":0}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToGroupNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false}],[80924370013313595,{"inputs":[{"Node":{"node_id":6029481207635803402,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GradientStops":[[0.0,{"red":0.19607843,"green":0.0,"blue":0.21176471,"alpha":1.0}],[0.12704918032786883,{"red":0.0627451,"green":0.19215687,"blue":0.39607844,"alpha":1.0}],[0.5,{"red":0.8901961,"green":0.8117647,"blue":0.39215687,"alpha":1.0}],[1.0,{"red":0.58431375,"green":0.92941177,"blue":0.92941177,"alpha":0.01}]]},"exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::raster::adjustments::GradientMapNode"}},"visible":true,"skip_deduplication":false}],[6323350524796370485,{"inputs":[{"Value":{"tagged_value":"None","exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}},{"Value":{"tagged_value":{"U32":0},"exposed":false}},{"Value":{"tagged_value":{"F64":35.0},"exposed":false}},{"Value":{"tagged_value":{"NoiseType":"OpenSimplex2"},"exposed":false}},{"Value":{"tagged_value":{"DomainWarpType":"OpenSimplex2"},"exposed":false}},{"Value":{"tagged_value":{"F64":100.0},"exposed":false}},{"Value":{"tagged_value":{"FractalType":"PingPong"},"exposed":false}},{"Value":{"tagged_value":{"U32":3},"exposed":false}},{"Value":{"tagged_value":{"F64":2.0},"exposed":false}},{"Value":{"tagged_value":{"F64":0.5},"exposed":false}},{"Value":{"tagged_value":{"F64":0.0},"exposed":false}},{"Value":{"tagged_value":{"F64":2.0},"exposed":false}},{"Value":{"tagged_value":{"CellularDistanceFunction":"Hybrid"},"exposed":false}},{"Value":{"tagged_value":{"CellularReturnType":"CellValue"},"exposed":false}},{"Value":{"tagged_value":{"F64":1.0},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_std::raster::NoisePatternNode"}},"visible":true,"skip_deduplication":false}],[6565638614909771142,{"inputs":[{"Node":{"node_id":7624113397561636853,"output_index":0,"lambda":false}},{"Node":{"node_id":80924370013313595,"output_index":0,"lambda":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":3,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":1}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToElementNode"}},"visible":true,"skip_deduplication":false}],[3,{"inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Reflection":"DocumentNodePath"}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::LayerNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}],[1,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":0}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToGroupNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false}],[12241147352993594415,{"inputs":[{"Value":{"tagged_value":{"ArtboardGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Node":{"node_id":6565638614909771142,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"IVec2":[0,0]},"exposed":false}},{"Value":{"tagged_value":{"IVec2":[1000,1000]},"exposed":false}},{"Value":{"tagged_value":{"Color":{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":2,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Concrete":{"name":"graph_craft::document::value::TaggedValue","alias":null}},"import_index":1}},{"Value":{"tagged_value":{"String":"Artboard"},"exposed":false}},{"Network":{"import_type":{"Concrete":{"name":"graph_craft::document::value::TaggedValue","alias":null}},"import_index":2}},{"Network":{"import_type":{"Concrete":{"name":"graph_craft::document::value::TaggedValue","alias":null}},"import_index":3}},{"Network":{"import_type":{"Concrete":{"name":"graph_craft::document::value::TaggedValue","alias":null}},"import_index":4}},{"Network":{"import_type":{"Concrete":{"name":"graph_craft::document::value::TaggedValue","alias":null}},"import_index":5}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToArtboardNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Network":{"import_type":{"Fn":[{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},{"Concrete":{"name":"graphene_core::instances::Instances<graphene_core::graphic_element::Artboard>","alias":null}}]},"import_index":0}},{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Reflection":"DocumentNodePath"}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::AppendArtboardNode"}},"visible":true,"skip_deduplication":false}],[1,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]},"network_metadata":{"persistent_metadata":{"node_metadata":[[6029481207635803402,{"persistent_metadata":{"reference":"Transform","display_name":"","input_properties":[{"input_data":{"input_name":"Vector Data"},"widget_override":null},{"input_data":{"x":"X","input_name":"Translation","unit":" px","y":"Y"},"widget_override":"vec2"},{"input_data":{"input_name":"Rotation"},"widget_override":"transform_rotation"},{"input_data":{"x":"W","unit":"x","y":"H","input_name":"Scale"},"widget_override":"vec2"},{"input_data":{"input_name":"Skew"},"widget_override":"hidden"},{"input_data":{"input_name":"Pivot"},"widget_override":"hidden"}],"output_names":["Data"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":{"persistent_metadata":{"node_metadata":[[0,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}],[1,{"persistent_metadata":{"reference":null,"display_name":"Transform","input_properties":[{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[7624113397561636853,{"persistent_metadata":{"reference":"Merge","display_name":"Swirly Noise","input_properties":[{"input_data":{"input_name":"Graphical Data"},"widget_override":null},{"input_data":{"input_name":"Over"},"widget_override":null}],"output_names":["Out"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Layer":{"position":{"Stack":0}}},"network_metadata":{"persistent_metadata":{"node_metadata":[[2,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-7,-1]}}},"network_metadata":null}}],[0,{"persistent_metadata":{"reference":null,"display_name":"To Element","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-1]}}},"network_metadata":null}}],[3,{"persistent_metadata":{"reference":null,"display_name":"Layer","input_properties":[{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[1,-3]}}},"network_metadata":null}}],[1,{"persistent_metadata":{"reference":null,"display_name":"To Group","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-3]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[4388711862172196665,{"persistent_metadata":{"reference":"Mandelbrot","display_name":"","input_properties":[],"output_names":["Raster"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":null}}],[6323350524796370485,{"persistent_metadata":{"reference":"Noise Pattern","display_name":"","input_properties":[{"input_data":{"input_name":"Spacer"},"widget_override":null},{"input_data":{"input_name":"Clip"},"widget_override":null},{"input_data":{"input_name":"Seed"},"widget_override":null},{"input_data":{"input_name":"Scale"},"widget_override":"noise_properties_scale"},{"input_data":{"input_name":"Noise Type"},"widget_override":"noise_properties_noise_type"},{"input_data":{"input_name":"Domain Warp Type"},"widget_override":"noise_properties_domain_warp_type"},{"input_data":{"input_name":"Domain Warp Amplitude"},"widget_override":"noise_properties_domain_warp_amplitude"},{"input_data":{"input_name":"Fractal Type"},"widget_override":"noise_properties_fractal_type"},{"input_data":{"input_name":"Fractal Octaves"},"widget_override":"noise_properties_fractal_octaves"},{"input_data":{"input_name":"Fractal Lacunarity"},"widget_override":"noise_properties_fractal_lacunarity"},{"input_data":{"input_name":"Fractal Gain"},"widget_override":"noise_properties_fractal_gain"},{"input_data":{"input_name":"Fractal Weighted Strength"},"widget_override":"noise_properties_fractal_weighted_strength"},{"input_data":{"input_name":"Fractal Ping Pong Strength"},"widget_override":"noise_properties_ping_pong_strength"},{"input_data":{"input_name":"Cellular Distance Function"},"widget_override":"noise_properties_cellular_distance_function"},{"input_data":{"input_name":"Cellular Return Type"},"widget_override":"noise_properties_cellular_return_type"},{"input_data":{"input_name":"Cellular Jitter"},"widget_override":"noise_properties_cellular_jitter"}],"output_names":["Image"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":{"persistent_metadata":{"node_metadata":[[0,{"persistent_metadata":{"reference":null,"display_name":"Noise Pattern","input_properties":[],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}],[1,{"persistent_metadata":{"reference":null,"display_name":"Cull","input_properties":[],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[12241147352993594415,{"persistent_metadata":{"reference":"Artboard","display_name":"","input_properties":[{"input_data":{"input_name":"Artboards"},"widget_override":null},{"input_data":{"input_name":"Contents"},"widget_override":"hidden"},{"input_data":{"input_name":"Location","x":"X","y":"Y","unit":" px"},"widget_override":"vec2"},{"input_data":{"input_name":"Dimensions","unit":" px","x":"W","y":"H"},"widget_override":"vec2"},{"input_data":{"input_name":"Background"},"widget_override":"artboard_background"},{"input_data":{"input_name":"Clip"},"widget_override":null}],"output_names":["Out"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Layer":{"position":{"Absolute":[-8,3]}}},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-2,-3]}}},"network_metadata":null}}],[0,{"persistent_metadata":{"reference":null,"display_name":"To Artboard","input_properties":[{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-10,-3]}}},"network_metadata":null}}],[2,{"persistent_metadata":{"reference":null,"display_name":"Append Artboards","input_properties":[{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[6,-4]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[6565638614909771142,{"persistent_metadata":{"reference":"Merge","display_name":"Mandelbrot Set","input_properties":[{"input_data":{"input_name":"Graphical Data"},"widget_override":null},{"input_data":{"input_name":"Over"},"widget_override":null}],"output_names":["Out"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Layer":{"position":{"Absolute":[-16,6]}}},"network_metadata":{"persistent_metadata":{"node_metadata":[[0,{"persistent_metadata":{"reference":null,"display_name":"To Element","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-1]}}},"network_metadata":null}}],[2,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-7,-1]}}},"network_metadata":null}}],[1,{"persistent_metadata":{"reference":null,"display_name":"To Group","input_properties":[{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-3]}}},"network_metadata":null}}],[3,{"persistent_metadata":{"reference":null,"display_name":"Layer","input_properties":[{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null},{"input_data":{"input_name":""},"widget_override":null}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[1,-3]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[3606681156406984991,{"persistent_metadata":{"reference":"Gradient Map","display_name":"","input_properties":[{"input_data":{"input_name":"Image"},"widget_override":null},{"input_data":{"input_name":"Gradient"},"widget_override":null},{"input_data":{"input_name":"Reverse"},"widget_override":null}],"output_names":["Image"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":null}}],[80924370013313595,{"persistent_metadata":{"reference":"Gradient Map","display_name":"","input_properties":[{"input_data":{"input_name":"Image"},"widget_override":null},{"input_data":{"input_name":"Gradient"},"widget_override":null},{"input_data":{"input_name":"Reverse"},"widget_override":null}],"output_names":["Image"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[345.0,-187.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,1336.0,394.0],"node_graph_top_right":[1468.796875,0.0]},"selection_undo_history":[[12241147352993594415]],"selection_redo_history":[]}}},"collapsed":[],"name":"marbled-mandelbrot.graphite","commit_hash":"f6ffa45a8180183d70a67d3e41249934a8fcacc9","document_ptz":{"pan":[-339.3903349049215,-502.62390663267854],"tilt":0.0,"zoom":4.0,"flip":false},"document_mode":"DesignMode","view_mode":"Normal","overlays_visibility_settings":{"all":true,"artboard_name":true,"compass_rose":true,"quick_measurement":true,"transform_measurement":true,"transform_cage":true,"hover_outline":true,"selection_outline":true,"pivot":true,"path":true,"anchors":true,"handles":true},"rulers_visible":true,"snapping_state":{"snapping_enabled":true,"grid_snapping":false,"artboards":true,"tolerance":8.0,"bounding_box":{"center_point":true,"corner_point":true,"edge_midpoint":true,"align_with_edges":true,"distribute_evenly":true},"path":{"anchor_point":true,"line_midpoint":true,"along_path":true,"normal_to_path":true,"tangent_to_path":true,"path_intersection_point":true,"align_with_anchor_point":true,"perpendicular_from_endpoint":true},"grid":{"origin":[0.0,0.0],"grid_type":{"Rectangular":{"spacing":[1.0,1.0]}},"rectangular_spacing":[1.0,1.0],"isometric_y_spacing":1.0,"isometric_angle_a":30.0,"isometric_angle_b":30.0,"grid_color":{"red":0.6038274,"green":0.6038274,"blue":0.6038274,"alpha":1.0},"dot_display":false}},"graph_view_overlay_open":false,"graph_fade_artwork_percentage":80.0}
|
demo-artwork/painted-dreams.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/parametric-dunescape.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/procedural-string-lights.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/red-dress.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
demo-artwork/valley-of-spires.graphite
ADDED
The diff for this file is too large to render.
See raw diff
|
|
deny.toml
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This template contains all of the possible sections and their default values
|
2 |
+
|
3 |
+
# Note that all fields that take a lint level have these possible values:
|
4 |
+
# * deny - An error will be produced and the check will fail
|
5 |
+
# * warn - A warning will be produced, but the check will not fail
|
6 |
+
# * allow - No warning or error will be produced, though in some cases a note
|
7 |
+
# will be
|
8 |
+
|
9 |
+
# The values provided in this template are the default values that will be used
|
10 |
+
# when any section or field is not specified in your own configuration
|
11 |
+
|
12 |
+
[graph]
|
13 |
+
# If 1 or more target triples (and optionally, target_features) are specified,
|
14 |
+
# only the specified targets will be checked when running `cargo deny check`.
|
15 |
+
# This means, if a particular package is only ever used as a target specific
|
16 |
+
# dependency, such as, for example, the `nix` crate only being used via the
|
17 |
+
# `target_family = "unix"` configuration, that only having windows targets in
|
18 |
+
# this list would mean the nix crate, as well as any of its exclusive
|
19 |
+
# dependencies not shared by any other crates, would be ignored, as the target
|
20 |
+
# list here is effectively saying which targets you are building for.
|
21 |
+
targets = [
|
22 |
+
# The triple can be any string, but only the target triples built in to
|
23 |
+
# rustc (as of 1.40) can be checked against actual config expressions
|
24 |
+
#{ triple = "x86_64-unknown-linux-musl" },
|
25 |
+
# You can also specify which target_features you promise are enabled for a
|
26 |
+
# particular target. target_features are currently not validated against
|
27 |
+
# the actual valid features supported by the target architecture.
|
28 |
+
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
|
29 |
+
]
|
30 |
+
|
31 |
+
# Tauri produces too many nonsense warnings.
|
32 |
+
exclude = ["tauri", "tauri-build"]
|
33 |
+
|
34 |
+
|
35 |
+
# This section is considered when running `cargo deny check advisories`
|
36 |
+
# More documentation for the advisories section can be found here:
|
37 |
+
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
|
38 |
+
[advisories]
|
39 |
+
# The path where the advisory database is cloned/fetched into
|
40 |
+
db-path = "~/.cargo/advisory-db"
|
41 |
+
# The url(s) of the advisory databases to use
|
42 |
+
db-urls = ["https://github.com/rustsec/advisory-db"]
|
43 |
+
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
44 |
+
# output a note when they are encountered.
|
45 |
+
ignore = [
|
46 |
+
"RUSTSEC-2024-0370", # Unmaintained but still fully functional crate `proc-macro-error`
|
47 |
+
"RUSTSEC-2024-0388", # Unmaintained but still fully functional crate `derivative`
|
48 |
+
"RUSTSEC-2025-0007", # Unmaintained but still fully functional crate `ring`
|
49 |
+
"RUSTSEC-2024-0436", # Unmaintained but still fully functional crate `paste`
|
50 |
+
"RUSTSEC-2025-0014", # Unmaintained but still fully functional crate `humantime`
|
51 |
+
]
|
52 |
+
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
53 |
+
# lower than the range specified will be ignored. Note that ignored advisories
|
54 |
+
# will still output a note when they are encountered.
|
55 |
+
# * None - CVSS Score 0.0
|
56 |
+
# * Low - CVSS Score 0.1 - 3.9
|
57 |
+
# * Medium - CVSS Score 4.0 - 6.9
|
58 |
+
# * High - CVSS Score 7.0 - 8.9
|
59 |
+
# * Critical - CVSS Score 9.0 - 10.0
|
60 |
+
#severity-threshold =
|
61 |
+
|
62 |
+
# This section is considered when running `cargo deny check licenses`
|
63 |
+
# More documentation for the licenses section can be found here:
|
64 |
+
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
|
65 |
+
[licenses]
|
66 |
+
# List of explicitly allowed licenses
|
67 |
+
# See https://spdx.org/licenses/ for list of possible licenses
|
68 |
+
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
69 |
+
#
|
70 |
+
# Keep this list in sync with those in `/about.toml` and `/frontend/vite.config.ts`.
|
71 |
+
allow = [
|
72 |
+
"Apache-2.0 WITH LLVM-exception",
|
73 |
+
"Apache-2.0",
|
74 |
+
"BSD-2-Clause",
|
75 |
+
"BSD-3-Clause",
|
76 |
+
"BSL-1.0",
|
77 |
+
"CC0-1.0",
|
78 |
+
"ISC",
|
79 |
+
"MIT",
|
80 |
+
"MPL-2.0",
|
81 |
+
"OpenSSL",
|
82 |
+
"Unicode-3.0",
|
83 |
+
"Zlib",
|
84 |
+
"NCSA",
|
85 |
+
]
|
86 |
+
# The confidence threshold for detecting a license from license text.
|
87 |
+
# The higher the value, the more closely the license text must be to the
|
88 |
+
# canonical license text of a valid SPDX license file.
|
89 |
+
# [possible values: any between 0.0 and 1.0].
|
90 |
+
confidence-threshold = 0.8
|
91 |
+
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
92 |
+
# aren't accepted for every possible crate as with the normal allow list
|
93 |
+
exceptions = [
|
94 |
+
# Each entry is the crate and version constraint, and its specific allow
|
95 |
+
# list
|
96 |
+
#{ allow = ["Zlib"], name = "adler32", version = "*" },
|
97 |
+
]
|
98 |
+
|
99 |
+
# Some crates don't have (easily) machine readable licensing information,
|
100 |
+
# adding a clarification entry for it allows you to manually specify the
|
101 |
+
# licensing information
|
102 |
+
[[licenses.clarify]]
|
103 |
+
# The name of the crate the clarification applies to
|
104 |
+
name = "ring"
|
105 |
+
# The optional version constraint for the crate
|
106 |
+
#version = "*"
|
107 |
+
# The SPDX expression for the license requirements of the crate
|
108 |
+
expression = "MIT AND ISC AND OpenSSL"
|
109 |
+
# One or more files in the crate's source used as the "source of truth" for
|
110 |
+
# the license expression. If the contents match, the clarification will be used
|
111 |
+
# when running the license check, otherwise the clarification will be ignored
|
112 |
+
# and the crate will be checked normally, which may produce warnings or errors
|
113 |
+
# depending on the rest of your configuration
|
114 |
+
license-files = [
|
115 |
+
# Each entry is a crate relative path, and the (opaque) hash of its contents
|
116 |
+
{ path = "LICENSE", hash = 0xbd0eed23 },
|
117 |
+
]
|
118 |
+
|
119 |
+
[licenses.private]
|
120 |
+
# If true, ignores workspace crates that aren't published, or are only
|
121 |
+
# published to private registries
|
122 |
+
ignore = false
|
123 |
+
# One or more private registries that you might publish crates to, if a crate
|
124 |
+
# is only published to private registries, and ignore is true, the crate will
|
125 |
+
# not have its license(s) checked
|
126 |
+
registries = [
|
127 |
+
#"https://sekretz.com/registry
|
128 |
+
]
|
129 |
+
|
130 |
+
# This section is considered when running `cargo deny check bans`.
|
131 |
+
# More documentation about the 'bans' section can be found here:
|
132 |
+
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
|
133 |
+
[bans]
|
134 |
+
# Lint level for when multiple versions of the same crate are detected
|
135 |
+
multiple-versions = "allow"
|
136 |
+
|
137 |
+
# Lint level for when a crate version requirement is `*`
|
138 |
+
wildcards = "allow"
|
139 |
+
# The graph highlighting used when creating dotgraphs for crates
|
140 |
+
# with multiple versions
|
141 |
+
# * lowest-version - The path to the lowest versioned duplicate is highlighted
|
142 |
+
# * simplest-path - The path to the version with the fewest edges is highlighted
|
143 |
+
# * all - Both lowest-version and simplest-path are used
|
144 |
+
highlight = "all"
|
145 |
+
# List of crates that are allowed. Use with care!
|
146 |
+
allow = [
|
147 |
+
#{ name = "ansi_term", version = "=0.11.0" },
|
148 |
+
]
|
149 |
+
# List of crates to deny
|
150 |
+
deny = [
|
151 |
+
# Each entry the name of a crate and a version range. If version is
|
152 |
+
# not specified, all versions will be matched.
|
153 |
+
#{ name = "ansi_term", version = "=0.11.0" },
|
154 |
+
#
|
155 |
+
# Wrapper crates can optionally be specified to allow the crate when it
|
156 |
+
# is a direct dependency of the otherwise banned crate
|
157 |
+
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
|
158 |
+
]
|
159 |
+
# Certain crates/versions that will be skipped when doing duplicate detection.
|
160 |
+
skip = [
|
161 |
+
#{ name = "ansi_term", version = "=0.11.0" },
|
162 |
+
#{ name = "cfg-if", version = "=0.1.10" },
|
163 |
+
]
|
164 |
+
# Similarly to `skip` allows you to skip certain crates during duplicate
|
165 |
+
# detection. Unlike skip, it also includes the entire tree of transitive
|
166 |
+
# dependencies starting at the specified crate, up to a certain depth, which is
|
167 |
+
# by default infinite
|
168 |
+
skip-tree = [
|
169 |
+
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
|
170 |
+
]
|
171 |
+
|
172 |
+
# This section is considered when running `cargo deny check sources`.
|
173 |
+
# More documentation about the 'sources' section can be found here:
|
174 |
+
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
|
175 |
+
[sources]
|
176 |
+
# Lint level for what to happen when a crate from a crate registry that is not
|
177 |
+
# in the allow list is encountered
|
178 |
+
unknown-registry = "warn"
|
179 |
+
# Lint level for what to happen when a crate from a git repository that is not
|
180 |
+
# in the allow list is encountered
|
181 |
+
unknown-git = "warn"
|
182 |
+
# List of URLs for allowed crate registries. Defaults to the crates.io index
|
183 |
+
# if not specified. If it is specified but empty, no registries are allowed.
|
184 |
+
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
185 |
+
# List of URLs for allowed Git repositories
|
186 |
+
allow-git = []
|
187 |
+
|
188 |
+
[sources.allow-org]
|
189 |
+
# 1 or more github.com organizations to allow git sources for
|
190 |
+
github = ["linebender", "Rust-GPU", "specta-rs"]
|
191 |
+
# 1 or more gitlab.com organizations to allow git sources for
|
192 |
+
#gitlab = [""]
|
193 |
+
# 1 or more bitbucket.org organizations to allow git sources for
|
194 |
+
#bitbucket = [""]
|
editor/Cargo.toml
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[package]
|
2 |
+
name = "graphite-editor"
|
3 |
+
publish = false
|
4 |
+
version = "0.0.0"
|
5 |
+
rust-version = "1.85"
|
6 |
+
authors = ["Graphite Authors <[email protected]>"]
|
7 |
+
edition = "2024"
|
8 |
+
readme = "../README.md"
|
9 |
+
homepage = "https://graphite.rs"
|
10 |
+
repository = "https://github.com/GraphiteEditor/Graphite"
|
11 |
+
license = "Apache-2.0"
|
12 |
+
|
13 |
+
[features]
|
14 |
+
default = ["wasm"]
|
15 |
+
wasm = ["wasm-bindgen", "graphene-std/wasm", "wasm-bindgen-futures"]
|
16 |
+
gpu = ["interpreted-executor/gpu", "wgpu-executor"]
|
17 |
+
tauri = ["ron", "decouple-execution"]
|
18 |
+
decouple-execution = []
|
19 |
+
resvg = ["graphene-std/resvg"]
|
20 |
+
vello = ["graphene-std/vello", "resvg"]
|
21 |
+
ron = ["dep:ron"]
|
22 |
+
|
23 |
+
[dependencies]
|
24 |
+
# Local dependencies
|
25 |
+
graphite-proc-macros = { workspace = true }
|
26 |
+
graph-craft = { workspace = true }
|
27 |
+
interpreted-executor = { workspace = true }
|
28 |
+
graphene-std = { workspace = true }
|
29 |
+
preprocessor = { workspace = true }
|
30 |
+
|
31 |
+
# Workspace dependencies
|
32 |
+
js-sys = { workspace = true }
|
33 |
+
log = { workspace = true }
|
34 |
+
bitflags = { workspace = true }
|
35 |
+
thiserror = { workspace = true }
|
36 |
+
serde = { workspace = true }
|
37 |
+
serde_json = { workspace = true }
|
38 |
+
bezier-rs = { workspace = true }
|
39 |
+
kurbo = { workspace = true }
|
40 |
+
futures = { workspace = true }
|
41 |
+
glam = { workspace = true }
|
42 |
+
derivative = { workspace = true }
|
43 |
+
specta = { workspace = true }
|
44 |
+
dyn-any = { workspace = true }
|
45 |
+
num_enum = { workspace = true }
|
46 |
+
usvg = { workspace = true }
|
47 |
+
once_cell = { workspace = true }
|
48 |
+
web-sys = { workspace = true }
|
49 |
+
|
50 |
+
# Required dependencies
|
51 |
+
spin = "0.9.8"
|
52 |
+
|
53 |
+
# Optional local dependencies
|
54 |
+
wgpu-executor = { workspace = true, optional = true }
|
55 |
+
|
56 |
+
# Optional workspace dependencies
|
57 |
+
wasm-bindgen = { workspace = true, optional = true }
|
58 |
+
wasm-bindgen-futures = { workspace = true, optional = true }
|
59 |
+
ron = { workspace = true, optional = true }
|
60 |
+
|
61 |
+
[dev-dependencies]
|
62 |
+
# Workspace dependencies
|
63 |
+
env_logger = { workspace = true }
|
64 |
+
futures = { workspace = true }
|
65 |
+
tokio = { workspace = true }
|
66 |
+
|
67 |
+
[lints.rust]
|
68 |
+
# TODO: figure out why we check these features when they do not exist
|
69 |
+
unexpected_cfgs = { level = "warn", check-cfg = [
|
70 |
+
'cfg(feature, values("resvg", "vello"))',
|
71 |
+
] }
|
editor/build.rs
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use std::process::Command;
|
2 |
+
|
3 |
+
const GRAPHITE_RELEASE_SERIES: &str = "Alpha 4";
|
4 |
+
|
5 |
+
fn main() {
|
6 |
+
// Execute a Git command for its stdout. Early exit if it fails for any of the possible reasons.
|
7 |
+
let try_git_command = |args: &[&str]| -> Option<String> {
|
8 |
+
let git_output = Command::new("git").args(args).output().ok()?;
|
9 |
+
let maybe_empty = String::from_utf8(git_output.stdout).ok()?;
|
10 |
+
let command_result = (!maybe_empty.is_empty()).then_some(maybe_empty)?;
|
11 |
+
Some(command_result)
|
12 |
+
};
|
13 |
+
// Execute a Git command for its output. Return "unknown" if it fails for any of the possible reasons.
|
14 |
+
let git_command = |args| -> String { try_git_command(args).unwrap_or_else(|| String::from("unknown")) };
|
15 |
+
|
16 |
+
// Rather than printing to any terminal, these commands set environment variables in the Cargo toolchain.
|
17 |
+
// They are accessed with the `env!("...")` macro in the codebase.
|
18 |
+
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_DATE={}", git_command(&["log", "-1", "--format=%cd"]));
|
19 |
+
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_HASH={}", git_command(&["rev-parse", "HEAD"]));
|
20 |
+
let branch = std::env::var("GITHUB_HEAD_REF").unwrap_or_default();
|
21 |
+
let branch = if branch.is_empty() { git_command(&["name-rev", "--name-only", "HEAD"]) } else { branch };
|
22 |
+
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_BRANCH={branch}");
|
23 |
+
println!("cargo:rustc-env=GRAPHITE_RELEASE_SERIES={GRAPHITE_RELEASE_SERIES}");
|
24 |
+
}
|
editor/src/application.rs
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::dispatcher::Dispatcher;
|
2 |
+
use crate::messages::prelude::*;
|
3 |
+
pub use graphene_std::uuid::*;
|
4 |
+
|
5 |
+
// TODO: serialize with serde to save the current editor state
|
6 |
+
pub struct Editor {
|
7 |
+
pub dispatcher: Dispatcher,
|
8 |
+
}
|
9 |
+
|
10 |
+
impl Editor {
|
11 |
+
/// Construct the editor.
|
12 |
+
/// Remember to provide a random seed with `editor::set_uuid_seed(seed)` before any editors can be used.
|
13 |
+
pub fn new() -> Self {
|
14 |
+
Self { dispatcher: Dispatcher::new() }
|
15 |
+
}
|
16 |
+
|
17 |
+
#[cfg(test)]
|
18 |
+
pub(crate) fn new_local_executor() -> (Self, crate::node_graph_executor::NodeRuntime) {
|
19 |
+
let (runtime, executor) = crate::node_graph_executor::NodeGraphExecutor::new_with_local_runtime();
|
20 |
+
let dispatcher = Dispatcher::with_executor(executor);
|
21 |
+
(Self { dispatcher }, runtime)
|
22 |
+
}
|
23 |
+
|
24 |
+
pub fn handle_message<T: Into<Message>>(&mut self, message: T) -> Vec<FrontendMessage> {
|
25 |
+
self.dispatcher.handle_message(message, true);
|
26 |
+
|
27 |
+
std::mem::take(&mut self.dispatcher.responses)
|
28 |
+
}
|
29 |
+
|
30 |
+
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
31 |
+
self.dispatcher.poll_node_graph_evaluation(responses)
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
impl Default for Editor {
|
36 |
+
fn default() -> Self {
|
37 |
+
Self::new()
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
pub const GRAPHITE_RELEASE_SERIES: &str = env!("GRAPHITE_RELEASE_SERIES");
|
42 |
+
pub const GRAPHITE_GIT_COMMIT_DATE: &str = env!("GRAPHITE_GIT_COMMIT_DATE");
|
43 |
+
pub const GRAPHITE_GIT_COMMIT_HASH: &str = env!("GRAPHITE_GIT_COMMIT_HASH");
|
44 |
+
pub const GRAPHITE_GIT_COMMIT_BRANCH: &str = env!("GRAPHITE_GIT_COMMIT_BRANCH");
|
45 |
+
|
46 |
+
pub fn commit_info_localized(localized_commit_date: &str) -> String {
|
47 |
+
format!(
|
48 |
+
"Release Series: {}\n\
|
49 |
+
Branch: {}\n\
|
50 |
+
Commit: {}\n\
|
51 |
+
{}",
|
52 |
+
GRAPHITE_RELEASE_SERIES,
|
53 |
+
GRAPHITE_GIT_COMMIT_BRANCH,
|
54 |
+
&GRAPHITE_GIT_COMMIT_HASH[..8],
|
55 |
+
localized_commit_date
|
56 |
+
)
|
57 |
+
}
|
editor/src/consts.rs
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// GRAPH
|
2 |
+
pub const GRID_SIZE: u32 = 24;
|
3 |
+
pub const EXPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
|
4 |
+
pub const EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP: u32 = 120;
|
5 |
+
pub const IMPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72;
|
6 |
+
pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120;
|
7 |
+
|
8 |
+
// VIEWPORT
|
9 |
+
pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = (1. / 600.) * 3.;
|
10 |
+
pub const VIEWPORT_ZOOM_MOUSE_RATE: f64 = 1. / 400.;
|
11 |
+
pub const VIEWPORT_ZOOM_SCALE_MIN: f64 = 0.000_000_1;
|
12 |
+
pub const VIEWPORT_ZOOM_SCALE_MAX: f64 = 10_000.;
|
13 |
+
pub const VIEWPORT_ZOOM_MIN_FRACTION_COVER: f64 = 0.01;
|
14 |
+
pub const VIEWPORT_ZOOM_LEVELS: [f64; 74] = [
|
15 |
+
0.0001, 0.000125, 0.00016, 0.0002, 0.00025, 0.00032, 0.0004, 0.0005, 0.00064, 0.0008, 0.001, 0.0016, 0.002, 0.0025, 0.0032, 0.004, 0.005, 0.0064, 0.008, 0.01, 0.01125, 0.015, 0.02, 0.025, 0.03,
|
16 |
+
0.04, 0.05, 0.06, 0.08, 0.1, 0.125, 0.15, 0.2, 0.25, 0.33333333, 0.4, 0.5, 0.66666666, 0.8, 1., 1.25, 1.6, 2., 2.5, 3.2, 4., 5., 6.4, 8., 10., 12.5, 16., 20., 25., 32., 40., 50., 64., 80., 100.,
|
17 |
+
128., 160., 200., 256., 320., 400., 512., 640., 800., 1024., 1280., 1600., 2048., 2560.,
|
18 |
+
];
|
19 |
+
/// Higher values create a steeper curve (a faster zoom rate change)
|
20 |
+
pub const VIEWPORT_ZOOM_WHEEL_RATE_CHANGE: f64 = 3.;
|
21 |
+
|
22 |
+
/// Helps push values that end in approximately half, plus or minus some floating point imprecision, towards the same side of the round() function.
|
23 |
+
pub const VIEWPORT_GRID_ROUNDING_BIAS: f64 = 0.002;
|
24 |
+
|
25 |
+
pub const VIEWPORT_SCROLL_RATE: f64 = 0.6;
|
26 |
+
|
27 |
+
pub const VIEWPORT_ROTATE_SNAP_INTERVAL: f64 = 15.;
|
28 |
+
|
29 |
+
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f64 = 0.95;
|
30 |
+
|
31 |
+
pub const DRAG_BEYOND_VIEWPORT_MAX_OVEREXTENSION_PIXELS: f64 = 50.;
|
32 |
+
pub const DRAG_BEYOND_VIEWPORT_SPEED_FACTOR: f64 = 20.;
|
33 |
+
|
34 |
+
// SNAPPING POINT
|
35 |
+
pub const SNAP_POINT_TOLERANCE: f64 = 5.;
|
36 |
+
/// These are layers whose bounding boxes are used for alignment.
|
37 |
+
pub const MAX_ALIGNMENT_CANDIDATES: usize = 100;
|
38 |
+
/// These are layers that are used for the layer snapper.
|
39 |
+
pub const MAX_SNAP_CANDIDATES: usize = 10;
|
40 |
+
/// These are points (anchors and bounding box corners etc.) in the layer snapper.
|
41 |
+
pub const MAX_LAYER_SNAP_POINTS: usize = 100;
|
42 |
+
|
43 |
+
pub const DRAG_THRESHOLD: f64 = 1.;
|
44 |
+
|
45 |
+
// TRANSFORMING LAYER
|
46 |
+
pub const ROTATE_INCREMENT: f64 = 15.;
|
47 |
+
pub const SCALE_INCREMENT: f64 = 0.1;
|
48 |
+
pub const SLOWING_DIVISOR: f64 = 10.;
|
49 |
+
pub const NUDGE_AMOUNT: f64 = 1.;
|
50 |
+
pub const BIG_NUDGE_AMOUNT: f64 = 10.;
|
51 |
+
|
52 |
+
// TOOLS
|
53 |
+
pub const DEFAULT_STROKE_WIDTH: f64 = 2.;
|
54 |
+
|
55 |
+
// SELECT TOOL
|
56 |
+
pub const SELECTION_TOLERANCE: f64 = 5.;
|
57 |
+
pub const DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD: f64 = 15.;
|
58 |
+
pub const SELECTION_DRAG_ANGLE: f64 = 90.;
|
59 |
+
|
60 |
+
// PIVOT
|
61 |
+
pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.;
|
62 |
+
pub const PIVOT_CROSSHAIR_LENGTH: f64 = 9.;
|
63 |
+
pub const PIVOT_DIAMETER: f64 = 5.;
|
64 |
+
|
65 |
+
// COMPASS ROSE
|
66 |
+
pub const COMPASS_ROSE_RING_INNER_DIAMETER: f64 = 13.;
|
67 |
+
pub const COMPASS_ROSE_MAIN_RING_DIAMETER: f64 = 15.;
|
68 |
+
pub const COMPASS_ROSE_HOVER_RING_DIAMETER: f64 = 23.;
|
69 |
+
pub const COMPASS_ROSE_ARROW_SIZE: f64 = 5.;
|
70 |
+
// Angle to either side of the compass arrows where they are targetted by the cursor (in degrees, must be less than 45°)
|
71 |
+
pub const COMPASS_ROSE_ARROW_CLICK_TARGET_ANGLE: f64 = 20.;
|
72 |
+
|
73 |
+
// TRANSFORM OVERLAY
|
74 |
+
pub const ANGLE_MEASURE_RADIUS_FACTOR: f64 = 0.04;
|
75 |
+
pub const ARC_MEASURE_RADIUS_FACTOR_RANGE: (f64, f64) = (0.05, 0.15);
|
76 |
+
|
77 |
+
// TRANSFORM CAGE
|
78 |
+
pub const RESIZE_HANDLE_SIZE: f64 = 6.;
|
79 |
+
pub const BOUNDS_SELECT_THRESHOLD: f64 = 10.;
|
80 |
+
pub const BOUNDS_ROTATE_THRESHOLD: f64 = 20.;
|
81 |
+
pub const MIN_LENGTH_FOR_MIDPOINT_VISIBILITY: f64 = 20.;
|
82 |
+
pub const MIN_LENGTH_FOR_CORNERS_VISIBILITY: f64 = 12.;
|
83 |
+
/// The width or height that the transform cage needs to be (at least) before the corner resize handle click targets take up their full surroundings. Otherwise, when less than this value, the interior edge resize handle takes precedence so the corner handles don't eat into the edge area, making it harder to resize the cage from its edges.
|
84 |
+
pub const MIN_LENGTH_FOR_EDGE_RESIZE_PRIORITY_OVER_CORNERS: f64 = 10.;
|
85 |
+
/// When the width or height of the transform cage is less than this value, only the exterior of the bounding box will act as a click target for resizing.
|
86 |
+
pub const MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR: f64 = 40.;
|
87 |
+
/// When dragging the edge of a cage with Alt, it centers around the pivot.
|
88 |
+
/// However if the pivot is on or near the same edge you are dragging, we should avoid scaling by a massive factor caused by the small denominator.
|
89 |
+
///
|
90 |
+
/// The motion of the user's cursor by an `x` pixel offset results in `x * scale_factor` pixels of offset on the other side.
|
91 |
+
pub const MAXIMUM_ALT_SCALE_FACTOR: f64 = 25.;
|
92 |
+
/// The width or height that the transform cage needs before it is considered to have no width or height.
|
93 |
+
pub const MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT: f64 = 1e-4;
|
94 |
+
|
95 |
+
// SKEW TRIANGLES
|
96 |
+
pub const SKEW_TRIANGLE_SIZE: f64 = 7.;
|
97 |
+
pub const SKEW_TRIANGLE_OFFSET: f64 = 4.;
|
98 |
+
pub const MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY: f64 = 48.;
|
99 |
+
|
100 |
+
// PATH TOOL
|
101 |
+
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.;
|
102 |
+
pub const SELECTION_THRESHOLD: f64 = 10.;
|
103 |
+
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
104 |
+
pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
105 |
+
pub const SEGMENT_INSERTION_DISTANCE: f64 = 5.;
|
106 |
+
pub const SEGMENT_OVERLAY_SIZE: f64 = 10.;
|
107 |
+
pub const HANDLE_LENGTH_FACTOR: f64 = 0.5;
|
108 |
+
|
109 |
+
// PEN TOOL
|
110 |
+
pub const CREATE_CURVE_THRESHOLD: f64 = 5.;
|
111 |
+
|
112 |
+
// SPLINE TOOL
|
113 |
+
pub const PATH_JOIN_THRESHOLD: f64 = 5.;
|
114 |
+
|
115 |
+
// LINE TOOL
|
116 |
+
pub const LINE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
117 |
+
|
118 |
+
// BRUSH TOOL
|
119 |
+
pub const BRUSH_SIZE_CHANGE_KEYBOARD: f64 = 5.;
|
120 |
+
pub const DEFAULT_BRUSH_SIZE: f64 = 20.;
|
121 |
+
|
122 |
+
// GIZMOS
|
123 |
+
pub const POINT_RADIUS_HANDLE_SNAP_THRESHOLD: f64 = 8.;
|
124 |
+
pub const POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD: f64 = 7.9;
|
125 |
+
pub const NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION: f64 = 1.2;
|
126 |
+
pub const NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH: f64 = 10.;
|
127 |
+
pub const GIZMO_HIDE_THRESHOLD: f64 = 20.;
|
128 |
+
|
129 |
+
// SCROLLBARS
|
130 |
+
pub const SCROLLBAR_SPACING: f64 = 0.1;
|
131 |
+
pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
|
132 |
+
pub const SCALE_EFFECT: f64 = 0.5;
|
133 |
+
|
134 |
+
// COLORS
|
135 |
+
pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff";
|
136 |
+
pub const COLOR_OVERLAY_BLUE_50: &str = "rgba(0, 168, 255, 0.5)";
|
137 |
+
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
|
138 |
+
pub const COLOR_OVERLAY_GREEN: &str = "#63ce63";
|
139 |
+
pub const COLOR_OVERLAY_RED: &str = "#ef5454";
|
140 |
+
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
|
141 |
+
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
|
142 |
+
pub const COLOR_OVERLAY_LABEL_BACKGROUND: &str = "#000000cc";
|
143 |
+
|
144 |
+
// DOCUMENT
|
145 |
+
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
146 |
+
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
147 |
+
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
148 |
+
pub const AUTO_SAVE_TIMEOUT_SECONDS: u64 = 15;
|
149 |
+
|
150 |
+
// INPUT
|
151 |
+
pub const DOUBLE_CLICK_MILLISECONDS: u64 = 500;
|
editor/src/dispatcher.rs
ADDED
@@ -0,0 +1,562 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
2 |
+
use crate::messages::dialog::DialogMessageData;
|
3 |
+
use crate::messages::portfolio::document::node_graph::document_node_definitions;
|
4 |
+
use crate::messages::prelude::*;
|
5 |
+
|
6 |
+
#[derive(Debug, Default)]
|
7 |
+
pub struct Dispatcher {
|
8 |
+
buffered_queue: Option<Vec<VecDeque<Message>>>,
|
9 |
+
message_queues: Vec<VecDeque<Message>>,
|
10 |
+
pub responses: Vec<FrontendMessage>,
|
11 |
+
pub message_handlers: DispatcherMessageHandlers,
|
12 |
+
}
|
13 |
+
|
14 |
+
#[derive(Debug, Default)]
|
15 |
+
pub struct DispatcherMessageHandlers {
|
16 |
+
animation_message_handler: AnimationMessageHandler,
|
17 |
+
broadcast_message_handler: BroadcastMessageHandler,
|
18 |
+
debug_message_handler: DebugMessageHandler,
|
19 |
+
dialog_message_handler: DialogMessageHandler,
|
20 |
+
globals_message_handler: GlobalsMessageHandler,
|
21 |
+
input_preprocessor_message_handler: InputPreprocessorMessageHandler,
|
22 |
+
key_mapping_message_handler: KeyMappingMessageHandler,
|
23 |
+
layout_message_handler: LayoutMessageHandler,
|
24 |
+
pub portfolio_message_handler: PortfolioMessageHandler,
|
25 |
+
preferences_message_handler: PreferencesMessageHandler,
|
26 |
+
tool_message_handler: ToolMessageHandler,
|
27 |
+
workspace_message_handler: WorkspaceMessageHandler,
|
28 |
+
}
|
29 |
+
|
30 |
+
impl DispatcherMessageHandlers {
|
31 |
+
pub fn with_executor(executor: crate::node_graph_executor::NodeGraphExecutor) -> Self {
|
32 |
+
Self {
|
33 |
+
portfolio_message_handler: PortfolioMessageHandler::with_executor(executor),
|
34 |
+
..Default::default()
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/// For optimization, these are messages guaranteed to be redundant when repeated.
|
40 |
+
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
41 |
+
/// In addition, these messages do not change any state in the backend (aside from caches).
|
42 |
+
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
43 |
+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
44 |
+
PropertiesPanelMessageDiscriminant::Refresh,
|
45 |
+
))),
|
46 |
+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
|
47 |
+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))),
|
48 |
+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderRulers)),
|
49 |
+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderScrollbars)),
|
50 |
+
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
|
51 |
+
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
|
52 |
+
];
|
53 |
+
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame))];
|
54 |
+
// 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.
|
55 |
+
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];
|
56 |
+
|
57 |
+
impl Dispatcher {
|
58 |
+
pub fn new() -> Self {
|
59 |
+
Self::default()
|
60 |
+
}
|
61 |
+
|
62 |
+
pub fn with_executor(executor: crate::node_graph_executor::NodeGraphExecutor) -> Self {
|
63 |
+
Self {
|
64 |
+
message_handlers: DispatcherMessageHandlers::with_executor(executor),
|
65 |
+
..Default::default()
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
// If the deepest queues (higher index in queues list) are now empty (after being popped from) then remove them
|
70 |
+
fn cleanup_queues(&mut self, leave_last: bool) {
|
71 |
+
while self.message_queues.last().filter(|queue| queue.is_empty()).is_some() {
|
72 |
+
if leave_last && self.message_queues.len() == 1 {
|
73 |
+
break;
|
74 |
+
}
|
75 |
+
self.message_queues.pop();
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
/// Add a message to a queue so that it can be executed.
|
80 |
+
/// If `process_after_all_current` is set, all currently queued messages (including children) will be processed first.
|
81 |
+
/// If not set, it (and its children) will be processed as soon as possible.
|
82 |
+
pub fn schedule_execution(message_queues: &mut Vec<VecDeque<Message>>, process_after_all_current: bool, messages: impl IntoIterator<Item = Message>) {
|
83 |
+
match message_queues.first_mut() {
|
84 |
+
// If there are currently messages being processed and we are processing after them, add to the end of the first queue
|
85 |
+
Some(queue) if process_after_all_current => queue.extend(messages),
|
86 |
+
// In all other cases, make a new inner queue and add our message there
|
87 |
+
_ => message_queues.push(VecDeque::from_iter(messages)),
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
|
92 |
+
let message = message.into();
|
93 |
+
// Add all additional messages to the buffer if it exists (except from the end buffer message)
|
94 |
+
if !matches!(message, Message::EndBuffer(_)) {
|
95 |
+
if let Some(buffered_queue) = &mut self.buffered_queue {
|
96 |
+
Self::schedule_execution(buffered_queue, true, [message]);
|
97 |
+
|
98 |
+
return;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
// If we are not maintaining the buffer, simply add to the current queue
|
103 |
+
Self::schedule_execution(&mut self.message_queues, process_after_all_current, [message]);
|
104 |
+
|
105 |
+
while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) {
|
106 |
+
// Skip processing of this message if it will be processed later (at the end of the shallowest level queue)
|
107 |
+
if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) {
|
108 |
+
let already_in_queue = self.message_queues.first().filter(|queue| queue.contains(&message)).is_some();
|
109 |
+
if already_in_queue {
|
110 |
+
self.log_deferred_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
111 |
+
self.cleanup_queues(false);
|
112 |
+
continue;
|
113 |
+
} else if self.message_queues.len() > 1 {
|
114 |
+
self.log_deferred_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
115 |
+
self.cleanup_queues(true);
|
116 |
+
self.message_queues[0].add(message);
|
117 |
+
continue;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
// Print the message at a verbosity level of `info`
|
122 |
+
self.log_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
123 |
+
|
124 |
+
// Create a new queue for the child messages
|
125 |
+
let mut queue = VecDeque::new();
|
126 |
+
|
127 |
+
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
|
128 |
+
match message {
|
129 |
+
Message::StartBuffer => {
|
130 |
+
self.buffered_queue = Some(std::mem::take(&mut self.message_queues));
|
131 |
+
}
|
132 |
+
Message::EndBuffer(render_metadata) => {
|
133 |
+
// Assign the message queue to the currently buffered queue
|
134 |
+
if let Some(buffered_queue) = self.buffered_queue.take() {
|
135 |
+
self.cleanup_queues(false);
|
136 |
+
assert!(self.message_queues.is_empty(), "message queues are always empty when ending a buffer");
|
137 |
+
self.message_queues = buffered_queue;
|
138 |
+
};
|
139 |
+
|
140 |
+
let graphene_std::renderer::RenderMetadata {
|
141 |
+
upstream_footprints: footprints,
|
142 |
+
local_transforms,
|
143 |
+
click_targets,
|
144 |
+
clip_targets,
|
145 |
+
} = render_metadata;
|
146 |
+
|
147 |
+
// Run these update state messages immediately
|
148 |
+
let messages = [
|
149 |
+
DocumentMessage::UpdateUpstreamTransforms {
|
150 |
+
upstream_footprints: footprints,
|
151 |
+
local_transforms,
|
152 |
+
},
|
153 |
+
DocumentMessage::UpdateClickTargets { click_targets },
|
154 |
+
DocumentMessage::UpdateClipTargets { clip_targets },
|
155 |
+
];
|
156 |
+
Self::schedule_execution(&mut self.message_queues, false, messages.map(Message::from));
|
157 |
+
}
|
158 |
+
Message::NoOp => {}
|
159 |
+
Message::Init => {
|
160 |
+
// Load persistent data from the browser database
|
161 |
+
queue.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument);
|
162 |
+
queue.add(FrontendMessage::TriggerLoadPreferences);
|
163 |
+
|
164 |
+
// Display the menu bar at the top of the window
|
165 |
+
queue.add(MenuBarMessage::SendLayout);
|
166 |
+
|
167 |
+
// Send the information for tooltips and categories for each node/input.
|
168 |
+
queue.add(FrontendMessage::SendUIMetadata {
|
169 |
+
node_descriptions: document_node_definitions::collect_node_descriptions(),
|
170 |
+
node_types: document_node_definitions::collect_node_types(),
|
171 |
+
});
|
172 |
+
|
173 |
+
// Finish loading persistent data from the browser database
|
174 |
+
queue.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
|
175 |
+
}
|
176 |
+
Message::Animation(message) => {
|
177 |
+
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
|
178 |
+
}
|
179 |
+
Message::Batched(messages) => {
|
180 |
+
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
|
181 |
+
}
|
182 |
+
Message::Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()),
|
183 |
+
Message::Debug(message) => {
|
184 |
+
self.message_handlers.debug_message_handler.process_message(message, &mut queue, ());
|
185 |
+
}
|
186 |
+
Message::Dialog(message) => {
|
187 |
+
let data = DialogMessageData {
|
188 |
+
portfolio: &self.message_handlers.portfolio_message_handler,
|
189 |
+
preferences: &self.message_handlers.preferences_message_handler,
|
190 |
+
};
|
191 |
+
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, data);
|
192 |
+
}
|
193 |
+
Message::Frontend(message) => {
|
194 |
+
// Handle these messages immediately by returning early
|
195 |
+
if let FrontendMessage::TriggerFontLoad { .. } = message {
|
196 |
+
self.responses.push(message);
|
197 |
+
self.cleanup_queues(false);
|
198 |
+
|
199 |
+
// Return early to avoid running the code after the match block
|
200 |
+
return;
|
201 |
+
} else {
|
202 |
+
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
|
203 |
+
self.responses.push(message);
|
204 |
+
}
|
205 |
+
}
|
206 |
+
Message::Globals(message) => {
|
207 |
+
self.message_handlers.globals_message_handler.process_message(message, &mut queue, ());
|
208 |
+
}
|
209 |
+
Message::InputPreprocessor(message) => {
|
210 |
+
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
211 |
+
|
212 |
+
self.message_handlers
|
213 |
+
.input_preprocessor_message_handler
|
214 |
+
.process_message(message, &mut queue, InputPreprocessorMessageData { keyboard_platform });
|
215 |
+
}
|
216 |
+
Message::KeyMapping(message) => {
|
217 |
+
let input = &self.message_handlers.input_preprocessor_message_handler;
|
218 |
+
let actions = self.collect_actions();
|
219 |
+
|
220 |
+
self.message_handlers
|
221 |
+
.key_mapping_message_handler
|
222 |
+
.process_message(message, &mut queue, KeyMappingMessageData { input, actions });
|
223 |
+
}
|
224 |
+
Message::Layout(message) => {
|
225 |
+
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.key_mapping_message_handler.action_input_mapping(action_to_find);
|
226 |
+
|
227 |
+
self.message_handlers.layout_message_handler.process_message(message, &mut queue, action_input_mapping);
|
228 |
+
}
|
229 |
+
Message::Portfolio(message) => {
|
230 |
+
let ipp = &self.message_handlers.input_preprocessor_message_handler;
|
231 |
+
let preferences = &self.message_handlers.preferences_message_handler;
|
232 |
+
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
|
233 |
+
let message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
|
234 |
+
let reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
|
235 |
+
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
236 |
+
let animation = &self.message_handlers.animation_message_handler;
|
237 |
+
|
238 |
+
self.message_handlers.portfolio_message_handler.process_message(
|
239 |
+
message,
|
240 |
+
&mut queue,
|
241 |
+
PortfolioMessageData {
|
242 |
+
ipp,
|
243 |
+
preferences,
|
244 |
+
current_tool,
|
245 |
+
message_logging_verbosity,
|
246 |
+
reset_node_definitions_on_open,
|
247 |
+
timing_information,
|
248 |
+
animation,
|
249 |
+
},
|
250 |
+
);
|
251 |
+
}
|
252 |
+
Message::Preferences(message) => {
|
253 |
+
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
|
254 |
+
}
|
255 |
+
Message::Tool(message) => {
|
256 |
+
let document_id = self.message_handlers.portfolio_message_handler.active_document_id().unwrap();
|
257 |
+
let Some(document) = self.message_handlers.portfolio_message_handler.documents.get_mut(&document_id) else {
|
258 |
+
warn!("Called ToolMessage without an active document.\nGot {message:?}");
|
259 |
+
return;
|
260 |
+
};
|
261 |
+
|
262 |
+
let data = ToolMessageData {
|
263 |
+
document_id,
|
264 |
+
document,
|
265 |
+
input: &self.message_handlers.input_preprocessor_message_handler,
|
266 |
+
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
267 |
+
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
268 |
+
preferences: &self.message_handlers.preferences_message_handler,
|
269 |
+
};
|
270 |
+
|
271 |
+
self.message_handlers.tool_message_handler.process_message(message, &mut queue, data);
|
272 |
+
}
|
273 |
+
Message::Workspace(message) => {
|
274 |
+
self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ());
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
// If there are child messages, append the queue to the list of queues
|
279 |
+
if !queue.is_empty() {
|
280 |
+
self.message_queues.push(queue);
|
281 |
+
}
|
282 |
+
|
283 |
+
self.cleanup_queues(false);
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
pub fn collect_actions(&self) -> ActionList {
|
288 |
+
// TODO: Reduce the number of heap allocations
|
289 |
+
let mut list = Vec::new();
|
290 |
+
list.extend(self.message_handlers.dialog_message_handler.actions());
|
291 |
+
list.extend(self.message_handlers.animation_message_handler.actions());
|
292 |
+
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
293 |
+
list.extend(self.message_handlers.key_mapping_message_handler.actions());
|
294 |
+
list.extend(self.message_handlers.debug_message_handler.actions());
|
295 |
+
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() {
|
296 |
+
if !document.graph_view_overlay_open {
|
297 |
+
list.extend(self.message_handlers.tool_message_handler.actions());
|
298 |
+
}
|
299 |
+
}
|
300 |
+
list.extend(self.message_handlers.portfolio_message_handler.actions());
|
301 |
+
list
|
302 |
+
}
|
303 |
+
|
304 |
+
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
305 |
+
self.message_handlers.portfolio_message_handler.poll_node_graph_evaluation(responses)
|
306 |
+
}
|
307 |
+
|
308 |
+
/// Create the tree structure for logging the messages as a tree
|
309 |
+
fn create_indents(queues: &[VecDeque<Message>]) -> String {
|
310 |
+
String::from_iter(queues.iter().enumerate().skip(1).map(|(index, queue)| {
|
311 |
+
if index == queues.len() - 1 {
|
312 |
+
if queue.is_empty() { "└── " } else { "├── " }
|
313 |
+
} else if queue.is_empty() {
|
314 |
+
" "
|
315 |
+
} else {
|
316 |
+
"│ "
|
317 |
+
}
|
318 |
+
}))
|
319 |
+
}
|
320 |
+
|
321 |
+
/// Logs a message that is about to be executed, either as a tree
|
322 |
+
/// with a discriminant or the entire payload (depending on settings)
|
323 |
+
fn log_message(&self, message: &Message, queues: &[VecDeque<Message>], message_logging_verbosity: MessageLoggingVerbosity) {
|
324 |
+
let discriminant = MessageDiscriminant::from(message);
|
325 |
+
let is_blocked = DEBUG_MESSAGE_BLOCK_LIST.contains(&discriminant) || DEBUG_MESSAGE_ENDING_BLOCK_LIST.iter().any(|blocked_name| discriminant.local_name().ends_with(blocked_name));
|
326 |
+
|
327 |
+
if !is_blocked {
|
328 |
+
match message_logging_verbosity {
|
329 |
+
MessageLoggingVerbosity::Off => {}
|
330 |
+
MessageLoggingVerbosity::Names => {
|
331 |
+
info!("{}{:?}", Self::create_indents(queues), message.to_discriminant());
|
332 |
+
}
|
333 |
+
MessageLoggingVerbosity::Contents => {
|
334 |
+
if !(matches!(message, Message::InputPreprocessor(_))) {
|
335 |
+
info!("Message: {}{:?}", Self::create_indents(queues), message);
|
336 |
+
}
|
337 |
+
}
|
338 |
+
}
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
/// Logs into the tree that the message is in the side effect free messages and its execution will be deferred
|
343 |
+
fn log_deferred_message(&self, message: &Message, queues: &[VecDeque<Message>], message_logging_verbosity: MessageLoggingVerbosity) {
|
344 |
+
if let MessageLoggingVerbosity::Names = message_logging_verbosity {
|
345 |
+
info!("{}Deferred \"{:?}\" because it's a SIDE_EFFECT_FREE_MESSAGE", Self::create_indents(queues), message.to_discriminant());
|
346 |
+
}
|
347 |
+
}
|
348 |
+
}
|
349 |
+
|
350 |
+
#[cfg(test)]
|
351 |
+
mod test {
|
352 |
+
pub use crate::test_utils::test_prelude::*;
|
353 |
+
|
354 |
+
/// Create an editor with three layers
|
355 |
+
/// 1. A red rectangle
|
356 |
+
/// 2. A blue shape
|
357 |
+
/// 3. A green ellipse
|
358 |
+
async fn create_editor_with_three_layers() -> EditorTestUtils {
|
359 |
+
let mut editor = EditorTestUtils::create();
|
360 |
+
|
361 |
+
editor.new_document().await;
|
362 |
+
|
363 |
+
editor.select_primary_color(Color::RED).await;
|
364 |
+
editor.draw_rect(100., 200., 300., 400.).await;
|
365 |
+
|
366 |
+
editor.select_primary_color(Color::BLUE).await;
|
367 |
+
editor.draw_polygon(10., 1200., 1300., 400.).await;
|
368 |
+
|
369 |
+
editor.select_primary_color(Color::GREEN).await;
|
370 |
+
editor.draw_ellipse(104., 1200., 1300., 400.).await;
|
371 |
+
|
372 |
+
editor
|
373 |
+
}
|
374 |
+
|
375 |
+
/// - create rect, shape and ellipse
|
376 |
+
/// - copy
|
377 |
+
/// - paste
|
378 |
+
/// - assert that ellipse was copied
|
379 |
+
#[tokio::test]
|
380 |
+
async fn copy_paste_single_layer() {
|
381 |
+
let mut editor = create_editor_with_three_layers().await;
|
382 |
+
|
383 |
+
let layers_before_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
384 |
+
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }).await;
|
385 |
+
editor
|
386 |
+
.handle_message(PortfolioMessage::PasteIntoFolder {
|
387 |
+
clipboard: Clipboard::Internal,
|
388 |
+
parent: LayerNodeIdentifier::ROOT_PARENT,
|
389 |
+
insert_index: 0,
|
390 |
+
})
|
391 |
+
.await;
|
392 |
+
|
393 |
+
let layers_after_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
394 |
+
|
395 |
+
assert_eq!(layers_before_copy.len(), 3);
|
396 |
+
assert_eq!(layers_after_copy.len(), 4);
|
397 |
+
|
398 |
+
// Existing layers are unaffected
|
399 |
+
for i in 0..=2 {
|
400 |
+
assert_eq!(layers_before_copy[i], layers_after_copy[i + 1]);
|
401 |
+
}
|
402 |
+
}
|
403 |
+
|
404 |
+
#[cfg_attr(miri, ignore)]
|
405 |
+
/// - create rect, shape and ellipse
|
406 |
+
/// - select shape
|
407 |
+
/// - copy
|
408 |
+
/// - paste
|
409 |
+
/// - assert that shape was copied
|
410 |
+
#[tokio::test]
|
411 |
+
async fn copy_paste_single_layer_from_middle() {
|
412 |
+
let mut editor = create_editor_with_three_layers().await;
|
413 |
+
|
414 |
+
let layers_before_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
415 |
+
let shape_id = editor.active_document().metadata().all_layers().nth(1).unwrap();
|
416 |
+
|
417 |
+
editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![shape_id.to_node()] }).await;
|
418 |
+
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }).await;
|
419 |
+
editor
|
420 |
+
.handle_message(PortfolioMessage::PasteIntoFolder {
|
421 |
+
clipboard: Clipboard::Internal,
|
422 |
+
parent: LayerNodeIdentifier::ROOT_PARENT,
|
423 |
+
insert_index: 0,
|
424 |
+
})
|
425 |
+
.await;
|
426 |
+
|
427 |
+
let layers_after_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
428 |
+
|
429 |
+
assert_eq!(layers_before_copy.len(), 3);
|
430 |
+
assert_eq!(layers_after_copy.len(), 4);
|
431 |
+
|
432 |
+
// Existing layers are unaffected
|
433 |
+
for i in 0..=2 {
|
434 |
+
assert_eq!(layers_before_copy[i], layers_after_copy[i + 1]);
|
435 |
+
}
|
436 |
+
}
|
437 |
+
|
438 |
+
#[cfg_attr(miri, ignore)]
|
439 |
+
/// - create rect, shape and ellipse
|
440 |
+
/// - select ellipse and rect
|
441 |
+
/// - copy
|
442 |
+
/// - delete
|
443 |
+
/// - create another rect
|
444 |
+
/// - paste
|
445 |
+
/// - paste
|
446 |
+
#[tokio::test]
|
447 |
+
async fn copy_paste_deleted_layers() {
|
448 |
+
let mut editor = create_editor_with_three_layers().await;
|
449 |
+
assert_eq!(editor.active_document().metadata().all_layers().count(), 3);
|
450 |
+
|
451 |
+
let layers_before_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
452 |
+
let rect_id = layers_before_copy[0];
|
453 |
+
let shape_id = layers_before_copy[1];
|
454 |
+
let ellipse_id = layers_before_copy[2];
|
455 |
+
|
456 |
+
editor
|
457 |
+
.handle_message(NodeGraphMessage::SelectedNodesSet {
|
458 |
+
nodes: vec![rect_id.to_node(), ellipse_id.to_node()],
|
459 |
+
})
|
460 |
+
.await;
|
461 |
+
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }).await;
|
462 |
+
editor.handle_message(NodeGraphMessage::DeleteSelectedNodes { delete_children: true }).await;
|
463 |
+
editor.draw_rect(0., 800., 12., 200.).await;
|
464 |
+
editor
|
465 |
+
.handle_message(PortfolioMessage::PasteIntoFolder {
|
466 |
+
clipboard: Clipboard::Internal,
|
467 |
+
parent: LayerNodeIdentifier::ROOT_PARENT,
|
468 |
+
insert_index: 0,
|
469 |
+
})
|
470 |
+
.await;
|
471 |
+
editor
|
472 |
+
.handle_message(PortfolioMessage::PasteIntoFolder {
|
473 |
+
clipboard: Clipboard::Internal,
|
474 |
+
parent: LayerNodeIdentifier::ROOT_PARENT,
|
475 |
+
insert_index: 0,
|
476 |
+
})
|
477 |
+
.await;
|
478 |
+
|
479 |
+
let layers_after_copy = editor.active_document().metadata().all_layers().collect::<Vec<_>>();
|
480 |
+
|
481 |
+
assert_eq!(layers_before_copy.len(), 3);
|
482 |
+
assert_eq!(layers_after_copy.len(), 6);
|
483 |
+
|
484 |
+
println!("{:?} {:?}", layers_after_copy, layers_before_copy);
|
485 |
+
|
486 |
+
assert_eq!(layers_after_copy[5], shape_id);
|
487 |
+
}
|
488 |
+
|
489 |
+
#[tokio::test]
|
490 |
+
/// This test will fail when you make changes to the underlying serialization format for a document.
|
491 |
+
async fn check_if_demo_art_opens() {
|
492 |
+
use crate::messages::layout::utility_types::widget_prelude::*;
|
493 |
+
|
494 |
+
let print_problem_to_terminal_on_failure = |value: &String| {
|
495 |
+
println!();
|
496 |
+
println!("-------------------------------------------------");
|
497 |
+
println!("Failed test due to receiving a DisplayDialogError while loading a Graphite demo file.");
|
498 |
+
println!();
|
499 |
+
println!("NOTE:");
|
500 |
+
println!("Document upgrading isn't performed in tests like when opening in the actual editor.");
|
501 |
+
println!("You may need to open and re-save a document in the editor to apply its migrations.");
|
502 |
+
println!();
|
503 |
+
println!("DisplayDialogError details:");
|
504 |
+
println!();
|
505 |
+
println!("Description:");
|
506 |
+
println!("{value}");
|
507 |
+
println!("-------------------------------------------------");
|
508 |
+
println!();
|
509 |
+
|
510 |
+
panic!()
|
511 |
+
};
|
512 |
+
|
513 |
+
let mut editor = EditorTestUtils::create();
|
514 |
+
|
515 |
+
// UNCOMMENT THIS FOR RUNNING UNDER MIRI
|
516 |
+
//
|
517 |
+
// let files = [
|
518 |
+
// include_str!("../../demo-artwork/changing-seasons.graphite"),
|
519 |
+
// include_str!("../../demo-artwork/isometric-fountain.graphite"),
|
520 |
+
// include_str!("../../demo-artwork/painted-dreams.graphite"),
|
521 |
+
// include_str!("../../demo-artwork/procedural-string-lights.graphite"),
|
522 |
+
// include_str!("../../demo-artwork/parametric-dunescape.graphite"),
|
523 |
+
// include_str!("../../demo-artwork/red-dress.graphite"),
|
524 |
+
// include_str!("../../demo-artwork/valley-of-spires.graphite"),
|
525 |
+
// ];
|
526 |
+
// for (id, document_serialized_content) in files.iter().enumerate() {
|
527 |
+
// let document_name = format!("document {id}");
|
528 |
+
|
529 |
+
for (document_name, _, file_name) in crate::messages::dialog::simple_dialogs::ARTWORK {
|
530 |
+
let document_serialized_content = std::fs::read_to_string(format!("../demo-artwork/{file_name}")).unwrap();
|
531 |
+
|
532 |
+
assert_eq!(
|
533 |
+
document_serialized_content.lines().count(),
|
534 |
+
1,
|
535 |
+
"Demo artwork '{document_name}' has more than 1 line (remember to open and re-save it in Graphite)",
|
536 |
+
);
|
537 |
+
|
538 |
+
let responses = editor.editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
539 |
+
document_name: document_name.into(),
|
540 |
+
document_serialized_content,
|
541 |
+
});
|
542 |
+
|
543 |
+
// Check if the graph renders
|
544 |
+
if let Err(e) = editor.eval_graph().await {
|
545 |
+
print_problem_to_terminal_on_failure(&format!("Failed to evaluate the graph for document '{document_name}':\n{e}"));
|
546 |
+
}
|
547 |
+
|
548 |
+
for response in responses {
|
549 |
+
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
550 |
+
if let FrontendMessage::UpdateDialogColumn1 { layout_target: _, diff } = response {
|
551 |
+
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
552 |
+
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
553 |
+
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
554 |
+
print_problem_to_terminal_on_failure(value);
|
555 |
+
}
|
556 |
+
}
|
557 |
+
}
|
558 |
+
}
|
559 |
+
}
|
560 |
+
}
|
561 |
+
}
|
562 |
+
}
|
editor/src/generate_ts_types.rs
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/// Running this test will generate a `types.ts` file at the root of the repo,
|
2 |
+
/// containing every type annotated with `specta::Type`
|
3 |
+
// #[cfg(all(test, feature = "specta-export"))]
|
4 |
+
#[ignore]
|
5 |
+
#[test]
|
6 |
+
fn generate_ts_types() {
|
7 |
+
// TODO: Un-comment this out when we figure out how to reenable the "typescript` Specta feature flag
|
8 |
+
|
9 |
+
// use crate::messages::prelude::FrontendMessage;
|
10 |
+
// use specta::ts::{export_named_datatype, BigIntExportBehavior, ExportConfig};
|
11 |
+
// use specta::{NamedType, TypeMap};
|
12 |
+
// use std::fs::File;
|
13 |
+
// use std::io::Write;
|
14 |
+
|
15 |
+
// let config = ExportConfig::new().bigint(BigIntExportBehavior::Number);
|
16 |
+
|
17 |
+
// let mut type_map = TypeMap::default();
|
18 |
+
|
19 |
+
// let datatype = FrontendMessage::definition_named_data_type(&mut type_map);
|
20 |
+
|
21 |
+
// let mut export = String::new();
|
22 |
+
|
23 |
+
// export += &export_named_datatype(&config, &datatype, &type_map).unwrap();
|
24 |
+
|
25 |
+
// type_map
|
26 |
+
// .iter()
|
27 |
+
// .map(|(_, v)| v)
|
28 |
+
// .flat_map(|v| export_named_datatype(&config, v, &type_map))
|
29 |
+
// .for_each(|e| export += &format!("\n\n{e}"));
|
30 |
+
|
31 |
+
// let mut file = File::create("../types.ts").unwrap();
|
32 |
+
|
33 |
+
// write!(file, "{export}").ok();
|
34 |
+
}
|
editor/src/lib.rs
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
extern crate graphite_proc_macros;
|
2 |
+
|
3 |
+
// `macro_use` puts these macros into scope for all descendant code files
|
4 |
+
#[macro_use]
|
5 |
+
mod macros;
|
6 |
+
mod generate_ts_types;
|
7 |
+
#[macro_use]
|
8 |
+
extern crate log;
|
9 |
+
|
10 |
+
pub mod application;
|
11 |
+
pub mod consts;
|
12 |
+
pub mod dispatcher;
|
13 |
+
pub mod messages;
|
14 |
+
pub mod node_graph_executor;
|
15 |
+
#[cfg(test)]
|
16 |
+
pub mod test_utils;
|
17 |
+
pub mod utility_traits;
|
editor/src/macros.rs
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/// Syntax sugar for initializing an `ActionList`
|
2 |
+
///
|
3 |
+
/// # Example
|
4 |
+
///
|
5 |
+
/// ```ignore
|
6 |
+
/// actions!(DocumentMessage::Undo, DocumentMessage::Redo);
|
7 |
+
/// ```
|
8 |
+
///
|
9 |
+
/// expands to:
|
10 |
+
/// ```ignore
|
11 |
+
/// vec![vec![DocumentMessage::Undo, DocumentMessage::Redo]];
|
12 |
+
/// ```
|
13 |
+
///
|
14 |
+
/// and
|
15 |
+
/// ```ignore
|
16 |
+
/// actions!(DocumentMessage;
|
17 |
+
/// Undo,
|
18 |
+
/// Redo,
|
19 |
+
/// );
|
20 |
+
/// ```
|
21 |
+
///
|
22 |
+
/// expands to:
|
23 |
+
/// ```ignore
|
24 |
+
/// vec![vec![DocumentMessage::Undo, DocumentMessage::Redo]];
|
25 |
+
/// ```
|
26 |
+
///
|
27 |
+
macro_rules! actions {
|
28 |
+
($($v:expr_2021),* $(,)?) => {{
|
29 |
+
vec![$(vec![$v.into()]),*]
|
30 |
+
}};
|
31 |
+
|
32 |
+
($name:ident; $($v:ident),* $(,)?) => {{
|
33 |
+
vec![vec![$(($name::$v).into()),*]]
|
34 |
+
}};
|
35 |
+
}
|
36 |
+
|
37 |
+
/// Does the same thing as the `actions!` macro but wraps everything in:
|
38 |
+
///
|
39 |
+
/// ```ignore
|
40 |
+
/// fn actions(&self) -> ActionList {
|
41 |
+
/// actions!(…)
|
42 |
+
/// }
|
43 |
+
/// ```
|
44 |
+
macro_rules! advertise_actions {
|
45 |
+
($($v:expr_2021),* $(,)?) => {
|
46 |
+
fn actions(&self) -> $crate::utility_traits::ActionList {
|
47 |
+
actions!($($v),*)
|
48 |
+
}
|
49 |
+
};
|
50 |
+
|
51 |
+
($name:ident; $($v:ident),* $(,)?) => {
|
52 |
+
fn actions(&self) -> $crate::utility_traits::ActionList {
|
53 |
+
actions!($name; $($v),*)
|
54 |
+
}
|
55 |
+
}
|
56 |
+
}
|
editor/src/messages/animation/animation_message.rs
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
use super::animation_message_handler::AnimationTimeMode;
|
4 |
+
|
5 |
+
#[impl_message(Message, Animation)]
|
6 |
+
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
7 |
+
pub enum AnimationMessage {
|
8 |
+
ToggleLivePreview,
|
9 |
+
EnableLivePreview,
|
10 |
+
DisableLivePreview,
|
11 |
+
RestartAnimation,
|
12 |
+
SetFrameIndex(f64),
|
13 |
+
SetTime(f64),
|
14 |
+
UpdateTime,
|
15 |
+
IncrementFrameCounter,
|
16 |
+
SetAnimationTimeMode(AnimationTimeMode),
|
17 |
+
}
|
editor/src/messages/animation/animation_message_handler.rs
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use std::time::Duration;
|
2 |
+
|
3 |
+
use crate::messages::prelude::*;
|
4 |
+
|
5 |
+
use super::TimingInformation;
|
6 |
+
|
7 |
+
#[derive(PartialEq, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
|
8 |
+
pub enum AnimationTimeMode {
|
9 |
+
#[default]
|
10 |
+
TimeBased,
|
11 |
+
FrameBased,
|
12 |
+
}
|
13 |
+
|
14 |
+
#[derive(Default, Debug, Clone, PartialEq)]
|
15 |
+
enum AnimationState {
|
16 |
+
#[default]
|
17 |
+
Stopped,
|
18 |
+
Playing {
|
19 |
+
start: f64,
|
20 |
+
},
|
21 |
+
Paused {
|
22 |
+
start: f64,
|
23 |
+
pause_time: f64,
|
24 |
+
},
|
25 |
+
}
|
26 |
+
|
27 |
+
#[derive(Default, Debug, Clone, PartialEq)]
|
28 |
+
pub struct AnimationMessageHandler {
|
29 |
+
/// Used to re-send the UI on the next frame after playback starts
|
30 |
+
live_preview_recently_zero: bool,
|
31 |
+
timestamp: f64,
|
32 |
+
frame_index: f64,
|
33 |
+
animation_state: AnimationState,
|
34 |
+
fps: f64,
|
35 |
+
animation_time_mode: AnimationTimeMode,
|
36 |
+
}
|
37 |
+
impl AnimationMessageHandler {
|
38 |
+
pub(crate) fn timing_information(&self) -> TimingInformation {
|
39 |
+
let animation_time = self.timestamp - self.animation_start();
|
40 |
+
let animation_time = match self.animation_time_mode {
|
41 |
+
AnimationTimeMode::TimeBased => Duration::from_millis(animation_time as u64),
|
42 |
+
AnimationTimeMode::FrameBased => Duration::from_secs((self.frame_index / self.fps) as u64),
|
43 |
+
};
|
44 |
+
TimingInformation { time: self.timestamp, animation_time }
|
45 |
+
}
|
46 |
+
|
47 |
+
pub(crate) fn animation_start(&self) -> f64 {
|
48 |
+
match self.animation_state {
|
49 |
+
AnimationState::Stopped => self.timestamp,
|
50 |
+
AnimationState::Playing { start } => start,
|
51 |
+
AnimationState::Paused { start, pause_time } => start + self.timestamp - pause_time,
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
pub fn is_playing(&self) -> bool {
|
56 |
+
matches!(self.animation_state, AnimationState::Playing { .. })
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
61 |
+
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
62 |
+
match message {
|
63 |
+
AnimationMessage::ToggleLivePreview => match self.animation_state {
|
64 |
+
AnimationState::Stopped => responses.add(AnimationMessage::EnableLivePreview),
|
65 |
+
AnimationState::Playing { .. } => responses.add(AnimationMessage::DisableLivePreview),
|
66 |
+
AnimationState::Paused { .. } => responses.add(AnimationMessage::EnableLivePreview),
|
67 |
+
},
|
68 |
+
AnimationMessage::EnableLivePreview => {
|
69 |
+
self.animation_state = AnimationState::Playing { start: self.animation_start() };
|
70 |
+
|
71 |
+
// Update the restart and pause/play buttons
|
72 |
+
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
73 |
+
}
|
74 |
+
AnimationMessage::DisableLivePreview => {
|
75 |
+
match self.animation_state {
|
76 |
+
AnimationState::Stopped => (),
|
77 |
+
AnimationState::Playing { start } => self.animation_state = AnimationState::Paused { start, pause_time: self.timestamp },
|
78 |
+
AnimationState::Paused { .. } => (),
|
79 |
+
}
|
80 |
+
|
81 |
+
// Update the restart and pause/play buttons
|
82 |
+
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
83 |
+
}
|
84 |
+
AnimationMessage::SetFrameIndex(frame) => {
|
85 |
+
self.frame_index = frame;
|
86 |
+
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
87 |
+
// Update the restart and pause/play buttons
|
88 |
+
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
89 |
+
}
|
90 |
+
AnimationMessage::SetTime(time) => {
|
91 |
+
self.timestamp = time;
|
92 |
+
responses.add(AnimationMessage::UpdateTime);
|
93 |
+
}
|
94 |
+
AnimationMessage::IncrementFrameCounter => {
|
95 |
+
if self.is_playing() {
|
96 |
+
self.frame_index += 1.;
|
97 |
+
responses.add(AnimationMessage::UpdateTime);
|
98 |
+
}
|
99 |
+
}
|
100 |
+
AnimationMessage::UpdateTime => {
|
101 |
+
if self.is_playing() {
|
102 |
+
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
103 |
+
|
104 |
+
if self.live_preview_recently_zero {
|
105 |
+
// Update the restart and pause/play buttons
|
106 |
+
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
107 |
+
self.live_preview_recently_zero = false;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
}
|
111 |
+
AnimationMessage::RestartAnimation => {
|
112 |
+
self.frame_index = 0.;
|
113 |
+
self.animation_state = match self.animation_state {
|
114 |
+
AnimationState::Playing { .. } => AnimationState::Playing { start: self.timestamp },
|
115 |
+
_ => AnimationState::Stopped,
|
116 |
+
};
|
117 |
+
self.live_preview_recently_zero = true;
|
118 |
+
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
119 |
+
// Update the restart and pause/play buttons
|
120 |
+
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
121 |
+
}
|
122 |
+
AnimationMessage::SetAnimationTimeMode(animation_time_mode) => {
|
123 |
+
self.animation_time_mode = animation_time_mode;
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
advertise_actions!(AnimationMessageDiscriminant;
|
129 |
+
ToggleLivePreview,
|
130 |
+
SetFrameIndex,
|
131 |
+
RestartAnimation,
|
132 |
+
);
|
133 |
+
}
|
editor/src/messages/animation/mod.rs
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
mod animation_message;
|
2 |
+
mod animation_message_handler;
|
3 |
+
|
4 |
+
#[doc(inline)]
|
5 |
+
pub use animation_message::{AnimationMessage, AnimationMessageDiscriminant};
|
6 |
+
#[doc(inline)]
|
7 |
+
pub use animation_message_handler::AnimationMessageHandler;
|
8 |
+
|
9 |
+
pub use graphene_std::application_io::TimingInformation;
|
editor/src/messages/broadcast/broadcast_event.rs
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
4 |
+
#[impl_message(Message, BroadcastMessage, TriggerEvent)]
|
5 |
+
pub enum BroadcastEvent {
|
6 |
+
/// Triggered by requestAnimationFrame in JS
|
7 |
+
AnimationFrame,
|
8 |
+
CanvasTransformed,
|
9 |
+
ToolAbort,
|
10 |
+
SelectionChanged,
|
11 |
+
WorkingColorChanged,
|
12 |
+
}
|
editor/src/messages/broadcast/broadcast_message.rs
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
#[impl_message(Message, Broadcast)]
|
4 |
+
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
5 |
+
pub enum BroadcastMessage {
|
6 |
+
// Sub-messages
|
7 |
+
#[child]
|
8 |
+
TriggerEvent(BroadcastEvent),
|
9 |
+
|
10 |
+
// Messages
|
11 |
+
SubscribeEvent {
|
12 |
+
on: BroadcastEvent,
|
13 |
+
send: Box<Message>,
|
14 |
+
},
|
15 |
+
UnsubscribeEvent {
|
16 |
+
on: BroadcastEvent,
|
17 |
+
message: Box<Message>,
|
18 |
+
},
|
19 |
+
}
|
editor/src/messages/broadcast/broadcast_message_handler.rs
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
#[derive(Debug, Clone, Default)]
|
4 |
+
pub struct BroadcastMessageHandler {
|
5 |
+
listeners: HashMap<BroadcastEvent, Vec<Message>>,
|
6 |
+
}
|
7 |
+
|
8 |
+
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
|
9 |
+
fn process_message(&mut self, message: BroadcastMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
10 |
+
match message {
|
11 |
+
// Sub-messages
|
12 |
+
BroadcastMessage::TriggerEvent(event) => {
|
13 |
+
for message in self.listeners.entry(event).or_default() {
|
14 |
+
responses.add_front(message.clone())
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
// Messages
|
19 |
+
BroadcastMessage::SubscribeEvent { on, send } => self.listeners.entry(on).or_default().push(*send),
|
20 |
+
BroadcastMessage::UnsubscribeEvent { on, message } => self.listeners.entry(on).or_default().retain(|msg| *msg != *message),
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
fn actions(&self) -> ActionList {
|
25 |
+
actions!(BroadcastEventDiscriminant;)
|
26 |
+
}
|
27 |
+
}
|
editor/src/messages/broadcast/mod.rs
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
mod broadcast_message;
|
2 |
+
mod broadcast_message_handler;
|
3 |
+
|
4 |
+
pub mod broadcast_event;
|
5 |
+
|
6 |
+
#[doc(inline)]
|
7 |
+
pub use broadcast_message::{BroadcastMessage, BroadcastMessageDiscriminant};
|
8 |
+
#[doc(inline)]
|
9 |
+
pub use broadcast_message_handler::BroadcastMessageHandler;
|
editor/src/messages/debug/debug_message.rs
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
#[impl_message(Message, Debug)]
|
4 |
+
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
5 |
+
pub enum DebugMessage {
|
6 |
+
ToggleTraceLogs,
|
7 |
+
MessageOff,
|
8 |
+
MessageNames,
|
9 |
+
MessageContents,
|
10 |
+
}
|
editor/src/messages/debug/debug_message_handler.rs
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use super::utility_types::MessageLoggingVerbosity;
|
2 |
+
use crate::messages::prelude::*;
|
3 |
+
|
4 |
+
#[derive(Debug, Default)]
|
5 |
+
pub struct DebugMessageHandler {
|
6 |
+
pub message_logging_verbosity: MessageLoggingVerbosity,
|
7 |
+
}
|
8 |
+
|
9 |
+
impl MessageHandler<DebugMessage, ()> for DebugMessageHandler {
|
10 |
+
fn process_message(&mut self, message: DebugMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
11 |
+
match message {
|
12 |
+
DebugMessage::ToggleTraceLogs => {
|
13 |
+
if log::max_level() == log::LevelFilter::Debug {
|
14 |
+
log::set_max_level(log::LevelFilter::Trace);
|
15 |
+
} else {
|
16 |
+
log::set_max_level(log::LevelFilter::Debug);
|
17 |
+
}
|
18 |
+
|
19 |
+
// Refresh the checkmark beside the menu entry for this
|
20 |
+
responses.add(MenuBarMessage::SendLayout);
|
21 |
+
}
|
22 |
+
DebugMessage::MessageOff => {
|
23 |
+
self.message_logging_verbosity = MessageLoggingVerbosity::Off;
|
24 |
+
|
25 |
+
// Refresh the checkmark beside the menu entry for this
|
26 |
+
responses.add(MenuBarMessage::SendLayout);
|
27 |
+
}
|
28 |
+
DebugMessage::MessageNames => {
|
29 |
+
self.message_logging_verbosity = MessageLoggingVerbosity::Names;
|
30 |
+
|
31 |
+
// Refresh the checkmark beside the menu entry for this
|
32 |
+
responses.add(MenuBarMessage::SendLayout);
|
33 |
+
}
|
34 |
+
DebugMessage::MessageContents => {
|
35 |
+
self.message_logging_verbosity = MessageLoggingVerbosity::Contents;
|
36 |
+
|
37 |
+
// Refresh the checkmark beside the menu entry for this
|
38 |
+
responses.add(MenuBarMessage::SendLayout);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
advertise_actions!(DebugMessageDiscriminant;
|
44 |
+
ToggleTraceLogs,
|
45 |
+
MessageOff,
|
46 |
+
MessageNames,
|
47 |
+
MessageContents,
|
48 |
+
);
|
49 |
+
}
|
editor/src/messages/debug/mod.rs
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
mod debug_message;
|
2 |
+
mod debug_message_handler;
|
3 |
+
|
4 |
+
pub mod utility_types;
|
5 |
+
|
6 |
+
#[doc(inline)]
|
7 |
+
pub use debug_message::{DebugMessage, DebugMessageDiscriminant};
|
8 |
+
#[doc(inline)]
|
9 |
+
pub use debug_message_handler::DebugMessageHandler;
|
editor/src/messages/debug/utility_types.rs
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
|
2 |
+
pub enum MessageLoggingVerbosity {
|
3 |
+
#[default]
|
4 |
+
Off,
|
5 |
+
Names,
|
6 |
+
Contents,
|
7 |
+
}
|
editor/src/messages/dialog/dialog_message.rs
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::prelude::*;
|
2 |
+
|
3 |
+
#[impl_message(Message, Dialog)]
|
4 |
+
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
5 |
+
pub enum DialogMessage {
|
6 |
+
// Sub-messages
|
7 |
+
#[child]
|
8 |
+
ExportDialog(ExportDialogMessage),
|
9 |
+
#[child]
|
10 |
+
NewDocumentDialog(NewDocumentDialogMessage),
|
11 |
+
#[child]
|
12 |
+
PreferencesDialog(PreferencesDialogMessage),
|
13 |
+
|
14 |
+
// Messages
|
15 |
+
CloseAllDocumentsWithConfirmation,
|
16 |
+
CloseDialogAndThen {
|
17 |
+
followups: Vec<Message>,
|
18 |
+
},
|
19 |
+
DisplayDialogError {
|
20 |
+
title: String,
|
21 |
+
description: String,
|
22 |
+
},
|
23 |
+
RequestAboutGraphiteDialog,
|
24 |
+
RequestAboutGraphiteDialogWithLocalizedCommitDate {
|
25 |
+
localized_commit_date: String,
|
26 |
+
localized_commit_year: String,
|
27 |
+
},
|
28 |
+
RequestComingSoonDialog {
|
29 |
+
issue: Option<u32>,
|
30 |
+
},
|
31 |
+
RequestDemoArtworkDialog,
|
32 |
+
RequestExportDialog,
|
33 |
+
RequestLicensesDialogWithLocalizedCommitDate {
|
34 |
+
localized_commit_year: String,
|
35 |
+
},
|
36 |
+
RequestNewDocumentDialog,
|
37 |
+
RequestPreferencesDialog,
|
38 |
+
}
|
editor/src/messages/dialog/dialog_message_handler.rs
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
2 |
+
use crate::messages::layout::utility_types::widget_prelude::*;
|
3 |
+
use crate::messages::prelude::*;
|
4 |
+
|
5 |
+
pub struct DialogMessageData<'a> {
|
6 |
+
pub portfolio: &'a PortfolioMessageHandler,
|
7 |
+
pub preferences: &'a PreferencesMessageHandler,
|
8 |
+
}
|
9 |
+
|
10 |
+
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
|
11 |
+
#[derive(Debug, Default, Clone)]
|
12 |
+
pub struct DialogMessageHandler {
|
13 |
+
export_dialog: ExportDialogMessageHandler,
|
14 |
+
new_document_dialog: NewDocumentDialogMessageHandler,
|
15 |
+
preferences_dialog: PreferencesDialogMessageHandler,
|
16 |
+
}
|
17 |
+
|
18 |
+
impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandler {
|
19 |
+
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, data: DialogMessageData) {
|
20 |
+
let DialogMessageData { portfolio, preferences } = data;
|
21 |
+
|
22 |
+
match message {
|
23 |
+
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageData { portfolio }),
|
24 |
+
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
|
25 |
+
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageData { preferences }),
|
26 |
+
|
27 |
+
DialogMessage::CloseAllDocumentsWithConfirmation => {
|
28 |
+
let dialog = simple_dialogs::CloseAllDocumentsDialog {
|
29 |
+
unsaved_document_names: portfolio.unsaved_document_names(),
|
30 |
+
};
|
31 |
+
dialog.send_dialog_to_frontend(responses);
|
32 |
+
}
|
33 |
+
DialogMessage::CloseDialogAndThen { followups } => {
|
34 |
+
for message in followups.into_iter() {
|
35 |
+
responses.add(message);
|
36 |
+
}
|
37 |
+
|
38 |
+
// This come after followups, so that the followups (which can cause the dialog to open) happen first, then we close it afterwards.
|
39 |
+
// If it comes before, the dialog reopens (and appears to not close at all).
|
40 |
+
responses.add(FrontendMessage::DisplayDialogDismiss);
|
41 |
+
}
|
42 |
+
DialogMessage::DisplayDialogError { title, description } => {
|
43 |
+
let dialog = simple_dialogs::ErrorDialog { title, description };
|
44 |
+
dialog.send_dialog_to_frontend(responses);
|
45 |
+
}
|
46 |
+
DialogMessage::RequestAboutGraphiteDialog => {
|
47 |
+
responses.add(FrontendMessage::TriggerAboutGraphiteLocalizedCommitDate {
|
48 |
+
commit_date: env!("GRAPHITE_GIT_COMMIT_DATE").into(),
|
49 |
+
});
|
50 |
+
}
|
51 |
+
DialogMessage::RequestAboutGraphiteDialogWithLocalizedCommitDate {
|
52 |
+
localized_commit_date,
|
53 |
+
localized_commit_year,
|
54 |
+
} => {
|
55 |
+
let dialog = AboutGraphiteDialog {
|
56 |
+
localized_commit_date,
|
57 |
+
localized_commit_year,
|
58 |
+
};
|
59 |
+
|
60 |
+
dialog.send_dialog_to_frontend(responses);
|
61 |
+
}
|
62 |
+
DialogMessage::RequestComingSoonDialog { issue } => {
|
63 |
+
let dialog = ComingSoonDialog { issue };
|
64 |
+
dialog.send_dialog_to_frontend(responses);
|
65 |
+
}
|
66 |
+
DialogMessage::RequestDemoArtworkDialog => {
|
67 |
+
let dialog = DemoArtworkDialog;
|
68 |
+
dialog.send_dialog_to_frontend(responses);
|
69 |
+
}
|
70 |
+
DialogMessage::RequestExportDialog => {
|
71 |
+
if let Some(document) = portfolio.active_document() {
|
72 |
+
let artboards = document
|
73 |
+
.metadata()
|
74 |
+
.all_layers()
|
75 |
+
.filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
76 |
+
.map(|layer| {
|
77 |
+
let name = document
|
78 |
+
.network_interface
|
79 |
+
.node_metadata(&layer.to_node(), &[])
|
80 |
+
.map(|node| node.persistent_metadata.display_name.clone())
|
81 |
+
.and_then(|name| if name.is_empty() { None } else { Some(name) })
|
82 |
+
.unwrap_or_else(|| "Artboard".to_string());
|
83 |
+
(layer, name)
|
84 |
+
})
|
85 |
+
.collect();
|
86 |
+
|
87 |
+
self.export_dialog.artboards = artboards;
|
88 |
+
self.export_dialog.has_selection = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().is_some();
|
89 |
+
self.export_dialog.send_dialog_to_frontend(responses);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
DialogMessage::RequestLicensesDialogWithLocalizedCommitDate { localized_commit_year } => {
|
93 |
+
let dialog = LicensesDialog { localized_commit_year };
|
94 |
+
|
95 |
+
dialog.send_dialog_to_frontend(responses);
|
96 |
+
}
|
97 |
+
DialogMessage::RequestNewDocumentDialog => {
|
98 |
+
self.new_document_dialog = NewDocumentDialogMessageHandler {
|
99 |
+
name: portfolio.generate_new_document_name(),
|
100 |
+
infinite: false,
|
101 |
+
dimensions: glam::UVec2::new(1920, 1080),
|
102 |
+
};
|
103 |
+
self.new_document_dialog.send_dialog_to_frontend(responses);
|
104 |
+
}
|
105 |
+
DialogMessage::RequestPreferencesDialog => {
|
106 |
+
self.preferences_dialog = PreferencesDialogMessageHandler {};
|
107 |
+
self.preferences_dialog.send_dialog_to_frontend(responses, preferences);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
advertise_actions!(DialogMessageDiscriminant;
|
113 |
+
CloseAllDocumentsWithConfirmation,
|
114 |
+
RequestExportDialog,
|
115 |
+
RequestNewDocumentDialog,
|
116 |
+
RequestPreferencesDialog,
|
117 |
+
);
|
118 |
+
}
|
editor/src/messages/dialog/export_dialog/export_dialog_message.rs
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
2 |
+
use crate::messages::prelude::*;
|
3 |
+
|
4 |
+
#[impl_message(Message, DialogMessage, ExportDialog)]
|
5 |
+
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
6 |
+
pub enum ExportDialogMessage {
|
7 |
+
FileType(FileType),
|
8 |
+
ScaleFactor(f64),
|
9 |
+
TransparentBackground(bool),
|
10 |
+
ExportBounds(ExportBounds),
|
11 |
+
|
12 |
+
Submit,
|
13 |
+
}
|