mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Restructure project directories (#333)
`/client/web` -> `/frontend` `/client/cli` -> *delete for now* `/client/native` -> *delete for now* `/core/editor` -> `/editor` `/core/document` -> `/graphene` `/core/renderer` -> `/charcoal` `/core/proc-macro` -> `/proc-macros` *(now plural)*
This commit is contained in:
parent
434695d578
commit
53ad105f57
239 changed files with 197 additions and 224 deletions
1
frontend/wasm/.gitignore
vendored
Normal file
1
frontend/wasm/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
pkg/
|
43
frontend/wasm/Cargo.toml
Normal file
43
frontend/wasm/Cargo.toml
Normal file
|
@ -0,0 +1,43 @@
|
|||
[package]
|
||||
name = "graphite-wasm"
|
||||
version = "0.1.0"
|
||||
authors = ["Graphite Authors <contact@graphite.design>"]
|
||||
edition = "2018"
|
||||
readme = "../../README.md"
|
||||
homepage = "https://www.graphite.design"
|
||||
repository = "https://github.com/GraphiteEditor/Graphite"
|
||||
license = "Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = { version = "0.1.6", optional = true }
|
||||
editor = { path = "../../editor", package = "graphite-editor" }
|
||||
graphene = { path = "../../graphene", package = "graphite-graphene" }
|
||||
log = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2.73", features = ["serde-serialize"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.22"
|
||||
|
||||
[package.metadata.wasm-pack.profile.dev]
|
||||
wasm-opt = false
|
||||
|
||||
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
|
||||
debug-js-glue = true
|
||||
demangle-name-section = true
|
||||
dwarf-debug-info = true
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ["-Os"]
|
||||
|
||||
[package.metadata.wasm-pack.profile.release.wasm-bindgen]
|
||||
debug-js-glue = false
|
||||
demangle-name-section = false
|
||||
dwarf-debug-info = false
|
313
frontend/wasm/src/document.rs
Normal file
313
frontend/wasm/src/document.rs
Normal file
|
@ -0,0 +1,313 @@
|
|||
use crate::shims::Error;
|
||||
use crate::wrappers::{translate_key, translate_tool, Color};
|
||||
use crate::EDITOR_STATE;
|
||||
use editor::input::input_preprocessor::ModifierKeys;
|
||||
use editor::input::mouse::ScrollDelta;
|
||||
use editor::message_prelude::*;
|
||||
use editor::misc::EditorError;
|
||||
use editor::tool::{tool_options::ToolOptions, tools, ToolType};
|
||||
use editor::{input::mouse::MouseState, LayerId};
|
||||
use graphene::layers::BlendMode;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
fn convert_error(err: editor::EditorError) -> JsValue {
|
||||
Error::new(&err.to_string()).into()
|
||||
}
|
||||
|
||||
/// Modify the currently selected tool in the document state store
|
||||
#[wasm_bindgen]
|
||||
pub fn select_tool(tool: String) -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| match translate_tool(&tool) {
|
||||
Some(tool) => editor.borrow_mut().handle_message(ToolMessage::SelectTool(tool)).map_err(convert_error),
|
||||
None => Err(Error::new(&format!("Couldn't select {} because it was not recognized as a valid tool", tool)).into()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Update the options for a given tool
|
||||
#[wasm_bindgen]
|
||||
pub fn set_tool_options(tool: String, options: &JsValue) -> Result<(), JsValue> {
|
||||
match options.into_serde::<ToolOptions>() {
|
||||
Ok(options) => EDITOR_STATE.with(|editor| match translate_tool(&tool) {
|
||||
Some(tool) => editor.borrow_mut().handle_message(ToolMessage::SetToolOptions(tool, options)).map_err(convert_error),
|
||||
None => Err(Error::new(&format!("Couldn't set options for {} because it was not recognized as a valid tool", tool)).into()),
|
||||
}),
|
||||
Err(err) => Err(Error::new(&format!("Invalid JSON for ToolOptions: {}", err)).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to a given tool
|
||||
#[wasm_bindgen]
|
||||
pub fn send_tool_message(tool: String, message: &JsValue) -> Result<(), JsValue> {
|
||||
let tool_message = match translate_tool(&tool) {
|
||||
Some(tool) => match tool {
|
||||
ToolType::Select => match message.into_serde::<tools::select::SelectMessage>() {
|
||||
Ok(select_message) => Ok(ToolMessage::Select(select_message)),
|
||||
Err(err) => Err(Error::new(&format!("Invalid message for {}: {}", tool, err)).into()),
|
||||
},
|
||||
_ => Err(Error::new(&format!("Tool message sending not implemented for {}", tool)).into()),
|
||||
},
|
||||
None => Err(Error::new(&format!("Couldn't send message for {} because it was not recognized as a valid tool", tool)).into()),
|
||||
};
|
||||
EDITOR_STATE.with(|editor| match tool_message {
|
||||
Ok(tool_message) => editor.borrow_mut().handle_message(tool_message).map_err(convert_error),
|
||||
Err(err) => Err(err),
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn select_document(document: usize) -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::SelectDocument(document)).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_open_documents_list() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::GetOpenDocumentsList).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn new_document() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::NewDocument).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn close_document(document: usize) -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::CloseDocument(document)).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn close_all_documents() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::CloseAllDocuments).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn close_active_document_with_confirmation() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::CloseActiveDocumentWithConfirmation).map_err(convert_error))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn close_all_documents_with_confirmation() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentsMessage::CloseAllDocumentsWithConfirmation).map_err(convert_error))
|
||||
}
|
||||
|
||||
// TODO: Call event when the panels are resized
|
||||
/// Viewport resized
|
||||
#[wasm_bindgen]
|
||||
pub fn viewport_resize(new_width: u32, new_height: u32) -> Result<(), JsValue> {
|
||||
let ev = InputPreprocessorMessage::ViewportResize((new_width, new_height).into());
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
// TODO: When a mouse button is down that started in the viewport, this should trigger even when the mouse is outside the viewport (or even the browser window if the browser supports it)
|
||||
/// Mouse movement within the screenspace bounds of the viewport
|
||||
#[wasm_bindgen]
|
||||
pub fn on_mouse_move(x: u32, y: u32, modifiers: u8) -> Result<(), JsValue> {
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
// TODO: Convert these screenspace viewport coordinates to canvas coordinates based on the current zoom and pan
|
||||
let ev = InputPreprocessorMessage::MouseMove((x, y).into(), mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Mouse scrolling within the screenspace bounds of the viewport
|
||||
#[wasm_bindgen]
|
||||
pub fn on_mouse_scroll(delta_x: i32, delta_y: i32, delta_z: i32, modifiers: u8) -> Result<(), JsValue> {
|
||||
// TODO: Convert these screenspace viewport coordinates to canvas coordinates based on the current zoom and pan
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
let ev = InputPreprocessorMessage::MouseScroll(ScrollDelta::new(delta_x, delta_y, delta_z), mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// A mouse button depressed within screenspace the bounds of the viewport
|
||||
#[wasm_bindgen]
|
||||
pub fn on_mouse_down(x: u32, y: u32, mouse_keys: u8, modifiers: u8) -> Result<(), JsValue> {
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
let ev = InputPreprocessorMessage::MouseDown(MouseState::from_u8_pos(mouse_keys, (x, y).into()), mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// A mouse button released
|
||||
#[wasm_bindgen]
|
||||
pub fn on_mouse_up(x: u32, y: u32, mouse_keys: u8, modifiers: u8) -> Result<(), JsValue> {
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
let ev = InputPreprocessorMessage::MouseUp(MouseState::from_u8_pos(mouse_keys, (x, y).into()), mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// A keyboard button depressed within screenspace the bounds of the viewport
|
||||
#[wasm_bindgen]
|
||||
pub fn on_key_down(name: String, modifiers: u8) -> Result<(), JsValue> {
|
||||
let key = translate_key(&name);
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
log::trace!("key down {:?}, name: {}, modifiers: {:?}", key, name, mods);
|
||||
let ev = InputPreprocessorMessage::KeyDown(key, mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// A keyboard button released
|
||||
#[wasm_bindgen]
|
||||
pub fn on_key_up(name: String, modifiers: u8) -> Result<(), JsValue> {
|
||||
let key = translate_key(&name);
|
||||
let mods = ModifierKeys::from_bits(modifiers).expect("invalid modifier keys");
|
||||
log::trace!("key up {:?}, name: {}, modifiers: {:?}", key, name, mods);
|
||||
let ev = InputPreprocessorMessage::KeyUp(key, mods);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Update primary color
|
||||
#[wasm_bindgen]
|
||||
pub fn update_primary_color(primary_color: Color) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(ToolMessage::SelectPrimaryColor(primary_color.inner())))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Update secondary color
|
||||
#[wasm_bindgen]
|
||||
pub fn update_secondary_color(secondary_color: Color) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(ToolMessage::SelectSecondaryColor(secondary_color.inner())))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Swap primary and secondary color
|
||||
#[wasm_bindgen]
|
||||
pub fn swap_colors() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ToolMessage::SwapColors)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Reset primary and secondary colors to their defaults
|
||||
#[wasm_bindgen]
|
||||
pub fn reset_colors() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ToolMessage::ResetColors)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Undo history one step
|
||||
#[wasm_bindgen]
|
||||
pub fn undo() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::Undo)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Select all layers
|
||||
#[wasm_bindgen]
|
||||
pub fn select_all_layers() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SelectAllLayers)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Deselect all layers
|
||||
#[wasm_bindgen]
|
||||
pub fn deselect_all_layers() -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::DeselectAllLayers))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Reorder selected layer
|
||||
#[wasm_bindgen]
|
||||
pub fn reorder_selected_layers(delta: i32) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::ReorderSelectedLayers(delta)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Set the blend mode for the selected layers
|
||||
#[wasm_bindgen]
|
||||
pub fn set_blend_mode_for_selected_layers(blend_mode_svg_style_name: String) -> Result<(), JsValue> {
|
||||
let blend_mode = match blend_mode_svg_style_name.as_str() {
|
||||
"normal" => BlendMode::Normal,
|
||||
"multiply" => BlendMode::Multiply,
|
||||
"darken" => BlendMode::Darken,
|
||||
"color-burn" => BlendMode::ColorBurn,
|
||||
"screen" => BlendMode::Screen,
|
||||
"lighten" => BlendMode::Lighten,
|
||||
"color-dodge" => BlendMode::ColorDodge,
|
||||
"overlay" => BlendMode::Overlay,
|
||||
"soft-light" => BlendMode::SoftLight,
|
||||
"hard-light" => BlendMode::HardLight,
|
||||
"difference" => BlendMode::Difference,
|
||||
"exclusion" => BlendMode::Exclusion,
|
||||
"hue" => BlendMode::Hue,
|
||||
"saturation" => BlendMode::Saturation,
|
||||
"color" => BlendMode::Color,
|
||||
"luminosity" => BlendMode::Luminosity,
|
||||
_ => return Err(convert_error(EditorError::Misc("UnknownBlendMode".to_string()))),
|
||||
};
|
||||
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SetBlendModeForSelectedLayers(blend_mode)).map_err(convert_error))
|
||||
}
|
||||
|
||||
/// Set the opacity for the selected layers
|
||||
#[wasm_bindgen]
|
||||
pub fn set_opacity_for_selected_layers(opacity_percent: f64) -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| {
|
||||
editor
|
||||
.borrow_mut()
|
||||
.handle_message(DocumentMessage::SetOpacityForSelectedLayers(opacity_percent / 100.))
|
||||
.map_err(convert_error)
|
||||
})
|
||||
}
|
||||
|
||||
/// Export the document
|
||||
#[wasm_bindgen]
|
||||
pub fn export_document() -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::ExportDocument)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Sets the zoom to the value
|
||||
#[wasm_bindgen]
|
||||
pub fn set_zoom(new_zoom: f64) -> Result<(), JsValue> {
|
||||
let ev = MovementMessage::SetCanvasZoom(new_zoom);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Sets the rotation to the new value (in radians)
|
||||
#[wasm_bindgen]
|
||||
pub fn set_rotation(new_radians: f64) -> Result<(), JsValue> {
|
||||
let ev = MovementMessage::SetCanvasRotation(new_radians);
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Update the list of selected layers. The layer paths have to be stored in one array and are separated by LayerId::MAX
|
||||
#[wasm_bindgen]
|
||||
pub fn select_layers(paths: Vec<LayerId>) -> Result<(), JsValue> {
|
||||
let paths = paths.split(|id| *id == LayerId::MAX).map(|path| path.to_vec()).collect();
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SelectLayers(paths)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Toggle visibility of a layer from the layer list
|
||||
#[wasm_bindgen]
|
||||
pub fn toggle_layer_visibility(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::ToggleLayerVisibility(path)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Toggle expansions state of a layer from the layer list
|
||||
#[wasm_bindgen]
|
||||
pub fn toggle_layer_expansion(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::ToggleLayerExpansion(path)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Renames a layer from the layer list
|
||||
#[wasm_bindgen]
|
||||
pub fn rename_layer(path: Vec<LayerId>, new_name: String) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::RenameLayer(path, new_name)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Deletes a layer from the layer list
|
||||
#[wasm_bindgen]
|
||||
pub fn delete_layer(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||
EDITOR_STATE
|
||||
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::DeleteLayer(path)))
|
||||
.map_err(convert_error)
|
||||
}
|
||||
|
||||
/// Requests the backend to add a layer to the layer list
|
||||
#[wasm_bindgen]
|
||||
pub fn add_folder(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::AddFolder(path))).map_err(convert_error)
|
||||
}
|
37
frontend/wasm/src/lib.rs
Normal file
37
frontend/wasm/src/lib.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
pub mod document;
|
||||
mod shims;
|
||||
pub mod utils;
|
||||
pub mod window;
|
||||
pub mod wrappers;
|
||||
|
||||
use editor::{message_prelude::*, Editor};
|
||||
use std::cell::RefCell;
|
||||
use utils::WasmLog;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// the thread_local macro provides a way to initialize static variables with non-constant functions
|
||||
thread_local! { pub static EDITOR_STATE: RefCell<Editor> = RefCell::new(Editor::new(Box::new(handle_response))) }
|
||||
static LOGGER: WasmLog = WasmLog;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn init() {
|
||||
utils::set_panic_hook();
|
||||
log::set_logger(&LOGGER).expect("Failed to set logger");
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = "/../src/utilities/response-handler-binding.ts")]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(catch)]
|
||||
fn handleResponse(responseType: String, responseData: JsValue) -> Result<(), JsValue>;
|
||||
}
|
||||
|
||||
fn handle_response(response: FrontendMessage) {
|
||||
let response_type = response.to_discriminant().local_name();
|
||||
send_response(response_type, response);
|
||||
}
|
||||
|
||||
fn send_response(response_type: String, response_data: FrontendMessage) {
|
||||
let response_data = JsValue::from_serde(&response_data).expect("Failed to serialize response");
|
||||
let _ = handleResponse(response_type, response_data).map_err(|error| log::error!("javascript threw an error: {:?}", error));
|
||||
}
|
10
frontend/wasm/src/shims.rs
Normal file
10
frontend/wasm/src/shims.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(Clone, Debug)]
|
||||
pub type Error;
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(msg: &str) -> Error;
|
||||
}
|
45
frontend/wasm/src/utils.rs
Normal file
45
frontend/wasm/src/utils.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(msg: &str, format: &str);
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn info(msg: &str, format: &str);
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn warn(msg: &str, format: &str);
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn error(msg: &str, format: &str);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WasmLog;
|
||||
|
||||
impl log::Log for WasmLog {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
metadata.level() <= log::Level::Info
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
let (log, name, color): (fn(&str, &str), &str, &str) = match record.level() {
|
||||
log::Level::Trace => (log, "trace", "color:plum"),
|
||||
log::Level::Debug => (log, "debug", "color:cyan"),
|
||||
log::Level::Warn => (warn, "warn", "color:goldenrod"),
|
||||
log::Level::Info => (info, "info", "color:mediumseagreen"),
|
||||
log::Level::Error => (error, "error", "color:red"),
|
||||
};
|
||||
let msg = &format!("%c{}\t{}", name, record.args());
|
||||
log(msg, color)
|
||||
}
|
||||
fn flush(&self) {}
|
||||
}
|
43
frontend/wasm/src/window.rs
Normal file
43
frontend/wasm/src/window.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
type DocumentId = u32;
|
||||
|
||||
/// Modify the active Document in the editor state store
|
||||
#[wasm_bindgen]
|
||||
pub fn set_active_document(document_id: DocumentId) {
|
||||
todo!("set_active_document {}", document_id)
|
||||
}
|
||||
|
||||
/// Query the name of a specific document
|
||||
#[wasm_bindgen]
|
||||
pub fn get_document_name(document_id: DocumentId) -> String {
|
||||
todo!("get_document_name {}", document_id)
|
||||
}
|
||||
|
||||
/// Query the id of the most recently interacted with document
|
||||
#[wasm_bindgen]
|
||||
pub fn get_active_document() -> DocumentId {
|
||||
todo!("get_active_document")
|
||||
}
|
||||
|
||||
type PanelId = u32;
|
||||
/// Notify the editor that the mouse hovers above a panel
|
||||
#[wasm_bindgen]
|
||||
pub fn panel_hover_enter(panel_id: PanelId) {
|
||||
todo!("panel_hover_enter {}", panel_id)
|
||||
}
|
||||
|
||||
/// Query a list of currently available operations
|
||||
#[wasm_bindgen]
|
||||
pub fn get_available_operations() -> Vec<JsValue> {
|
||||
todo!("get_available_operations")
|
||||
// vec!["example1", "example2"].into_iter().map(JsValue::from).collect()
|
||||
}
|
||||
|
||||
/*
|
||||
/// Load a new .gdd file into the editor
|
||||
/// Returns a unique document identifier
|
||||
#[wasm_bindgen]
|
||||
pub fn load_document(raw_data: &[u8]) -> DocumentId {
|
||||
todo!()
|
||||
}*/
|
140
frontend/wasm/src/wrappers.rs
Normal file
140
frontend/wasm/src/wrappers.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use crate::shims::Error;
|
||||
use editor::input::keyboard::Key;
|
||||
use editor::tool::{SelectAppendMode, ToolType};
|
||||
use editor::Color as InnerColor;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Color(InnerColor);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Color {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Result<Color, JsValue> {
|
||||
match InnerColor::from_rgbaf32(red, green, blue, alpha) {
|
||||
Some(v) => Ok(Self(v)),
|
||||
None => Err(Error::new("invalid color").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn inner(&self) -> InnerColor {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! match_string_to_enum {
|
||||
(match ($e:expr) {$($var:ident),* $(,)?}) => {
|
||||
match $e {
|
||||
$(
|
||||
stringify!($var) => Some($var),
|
||||
)*
|
||||
_ => None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn translate_tool(name: &str) -> Option<ToolType> {
|
||||
use ToolType::*;
|
||||
|
||||
match_string_to_enum!(match (name) {
|
||||
Select,
|
||||
Crop,
|
||||
Navigate,
|
||||
Eyedropper,
|
||||
Text,
|
||||
Fill,
|
||||
Gradient,
|
||||
Brush,
|
||||
Heal,
|
||||
Clone,
|
||||
Patch,
|
||||
BlurSharpen,
|
||||
Relight,
|
||||
Path,
|
||||
Pen,
|
||||
Freehand,
|
||||
Spline,
|
||||
Line,
|
||||
Rectangle,
|
||||
Ellipse,
|
||||
Shape
|
||||
})
|
||||
}
|
||||
|
||||
pub fn translate_append_mode(name: &str) -> Option<SelectAppendMode> {
|
||||
use SelectAppendMode::*;
|
||||
|
||||
match_string_to_enum!(match (name) {
|
||||
New,
|
||||
Add,
|
||||
Subtract,
|
||||
Intersect
|
||||
})
|
||||
}
|
||||
|
||||
pub fn translate_key(name: &str) -> Key {
|
||||
log::trace!("pressed key: {}", name);
|
||||
use Key::*;
|
||||
match name.to_lowercase().as_str() {
|
||||
"a" => KeyA,
|
||||
"b" => KeyB,
|
||||
"c" => KeyC,
|
||||
"d" => KeyD,
|
||||
"e" => KeyE,
|
||||
"f" => KeyF,
|
||||
"g" => KeyG,
|
||||
"h" => KeyH,
|
||||
"i" => KeyI,
|
||||
"j" => KeyJ,
|
||||
"k" => KeyK,
|
||||
"l" => KeyL,
|
||||
"m" => KeyM,
|
||||
"n" => KeyN,
|
||||
"o" => KeyO,
|
||||
"p" => KeyP,
|
||||
"q" => KeyQ,
|
||||
"r" => KeyR,
|
||||
"s" => KeyS,
|
||||
"t" => KeyT,
|
||||
"u" => KeyU,
|
||||
"v" => KeyV,
|
||||
"w" => KeyW,
|
||||
"x" => KeyX,
|
||||
"y" => KeyY,
|
||||
"z" => KeyZ,
|
||||
"0" => Key0,
|
||||
"1" => Key1,
|
||||
"2" => Key2,
|
||||
"3" => Key3,
|
||||
"4" => Key4,
|
||||
"5" => Key5,
|
||||
"6" => Key6,
|
||||
"7" => Key7,
|
||||
"8" => Key8,
|
||||
"9" => Key9,
|
||||
"enter" => KeyEnter,
|
||||
"=" => KeyEquals,
|
||||
"+" => KeyPlus,
|
||||
"-" => KeyMinus,
|
||||
"shift" => KeyShift,
|
||||
// When using linux + chrome + the neo keyboard layout, the shift key is recognized as caps
|
||||
"capslock" => KeyShift,
|
||||
"control" => KeyControl,
|
||||
"delete" => KeyDelete,
|
||||
"backspace" => KeyBackspace,
|
||||
"alt" => KeyAlt,
|
||||
"escape" => KeyEscape,
|
||||
"tab" => KeyTab,
|
||||
"arrowup" => KeyArrowUp,
|
||||
"arrowdown" => KeyArrowDown,
|
||||
"arrowleft" => KeyArrowLeft,
|
||||
"arrowright" => KeyArrowRight,
|
||||
"[" => KeyLeftBracket,
|
||||
"]" => KeyRightBracket,
|
||||
"{" => KeyLeftCurlyBracket,
|
||||
"}" => KeyRightCurlyBracket,
|
||||
_ => UnknownKey,
|
||||
}
|
||||
}
|
10
frontend/wasm/tests/web.rs
Normal file
10
frontend/wasm/tests/web.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue