Polishing: standardize binding JS resize event (fixes non-integer AA rendering); remove "X" to delete; cleanup
This commit is contained in:
parent
a4d3c343a0
commit
77dc125095
|
|
@ -25,9 +25,9 @@ struct DispatcherMessageHandlers {
|
||||||
tool_message_handler: ToolMessageHandler,
|
tool_message_handler: ToolMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For optimization, these are messages guaranteed to be redundant when repeated.
|
/// For optimization, these are messages guaranteed to be redundant when repeated.
|
||||||
// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
||||||
// In addition, these messages do not change any state in the backend (aside from caches).
|
/// In addition, these messages do not change any state in the backend (aside from caches).
|
||||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Rerender))),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Rerender))),
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ pub enum FrontendMessage {
|
||||||
TriggerIndexedDbRemoveDocument { document_id: u64 },
|
TriggerIndexedDbRemoveDocument { document_id: u64 },
|
||||||
TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String },
|
TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String },
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
|
TriggerViewportResize,
|
||||||
|
|
||||||
// Update prefix: give the frontend a new value or state for it to use
|
// Update prefix: give the frontend a new value or state for it to use
|
||||||
UpdateActiveDocument { document_id: u64 },
|
UpdateActiveDocument { document_id: u64 },
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ impl Default for Mapping {
|
||||||
use Key::*;
|
use Key::*;
|
||||||
|
|
||||||
// WARNING!
|
// WARNING!
|
||||||
// If a new mapping isn't being handled (and perhaps another lower-precedence one is instead), make sure to advertise
|
// If a new mapping you added here isn't working (and perhaps another lower-precedence one is instead), make sure to advertise
|
||||||
// it as an available action in the respective message handler file (such as the bottom of `document_message_handler.rs`).
|
// it as an available action in the respective message handler file (such as the bottom of `document_message_handler.rs`).
|
||||||
|
|
||||||
let mappings = mapping![
|
let mappings = mapping![
|
||||||
|
|
@ -145,7 +145,6 @@ impl Default for Mapping {
|
||||||
entry! {action=DocumentMessage::SelectAllLayers, key_down=KeyA, modifiers=[KeyControl]},
|
entry! {action=DocumentMessage::SelectAllLayers, key_down=KeyA, modifiers=[KeyControl]},
|
||||||
entry! {action=DocumentMessage::CreateEmptyFolder { container_path: vec![] }, key_down=KeyN, modifiers=[KeyControl, KeyShift]},
|
entry! {action=DocumentMessage::CreateEmptyFolder { container_path: vec![] }, key_down=KeyN, modifiers=[KeyControl, KeyShift]},
|
||||||
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyDelete},
|
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyDelete},
|
||||||
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyX},
|
|
||||||
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyBackspace},
|
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyBackspace},
|
||||||
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
|
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
|
||||||
entry! {action=DocumentMessage::SaveDocument, key_down=KeyS, modifiers=[KeyControl]},
|
entry! {action=DocumentMessage::SaveDocument, key_down=KeyS, modifiers=[KeyControl]},
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessorMessageHa
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
responses.push_back(FrontendMessage::TriggerViewportResize.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||||
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
||||||
|
|
||||||
use glam::DVec2;
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
|
|
||||||
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,10 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
// Initialize the Graphite WASM editor instance
|
||||||
const editor = createEditorState();
|
const editor = createEditorState();
|
||||||
|
|
||||||
|
// Initialize other stateful Vue systems
|
||||||
const dialog = createDialogState(editor);
|
const dialog = createDialogState(editor);
|
||||||
const documents = createDocumentsState(editor, dialog);
|
const documents = createDocumentsState(editor, dialog);
|
||||||
const fullscreen = createFullscreenState();
|
const fullscreen = createFullscreenState();
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,11 @@
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
<LayoutCol class="viewport">
|
<LayoutCol class="viewport">
|
||||||
<LayoutRow class="bar-area">
|
<LayoutRow class="bar-area">
|
||||||
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Horizontal'" class="top-ruler" />
|
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Horizontal'" class="top-ruler" ref="rulerHorizontal" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="canvas-area">
|
<LayoutRow class="canvas-area">
|
||||||
<LayoutCol class="bar-area">
|
<LayoutCol class="bar-area">
|
||||||
<CanvasRuler :origin="rulerOrigin.y" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Vertical'" />
|
<CanvasRuler :origin="rulerOrigin.y" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Vertical'" ref="rulerVertical" />
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
<LayoutCol class="canvas-area">
|
<LayoutCol class="canvas-area">
|
||||||
<div class="canvas" data-canvas ref="canvas" :style="{ cursor: canvasCursor }" @pointerdown="(e: PointerEvent) => canvasPointerDown(e)">
|
<div class="canvas" data-canvas ref="canvas" :style="{ cursor: canvasCursor }" @pointerdown="(e: PointerEvent) => canvasPointerDown(e)">
|
||||||
|
|
@ -240,6 +240,7 @@ import {
|
||||||
defaultWidgetLayout,
|
defaultWidgetLayout,
|
||||||
UpdateDocumentBarLayout,
|
UpdateDocumentBarLayout,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
|
TriggerViewportResize,
|
||||||
DisplayRemoveEditableTextbox,
|
DisplayRemoveEditableTextbox,
|
||||||
DisplayEditableTextbox,
|
DisplayEditableTextbox,
|
||||||
} from "@/dispatcher/js-messages";
|
} from "@/dispatcher/js-messages";
|
||||||
|
|
@ -263,7 +264,10 @@ export default defineComponent({
|
||||||
inject: ["editor", "dialog"],
|
inject: ["editor", "dialog"],
|
||||||
methods: {
|
methods: {
|
||||||
viewportResize() {
|
viewportResize() {
|
||||||
|
// Resize the canvas
|
||||||
|
|
||||||
const canvas = this.$refs.canvas as HTMLElement;
|
const canvas = this.$refs.canvas as HTMLElement;
|
||||||
|
|
||||||
// Get the width and height rounded up to the nearest even number because resizing is centered and dividing an odd number by 2 for centering causes antialiasing
|
// Get the width and height rounded up to the nearest even number because resizing is centered and dividing an odd number by 2 for centering causes antialiasing
|
||||||
let width = Math.ceil(parseFloat(getComputedStyle(canvas).width));
|
let width = Math.ceil(parseFloat(getComputedStyle(canvas).width));
|
||||||
if (width % 2 === 1) width += 1;
|
if (width % 2 === 1) width += 1;
|
||||||
|
|
@ -272,6 +276,13 @@ export default defineComponent({
|
||||||
|
|
||||||
this.canvasSvgWidth = `${width}px`;
|
this.canvasSvgWidth = `${width}px`;
|
||||||
this.canvasSvgHeight = `${height}px`;
|
this.canvasSvgHeight = `${height}px`;
|
||||||
|
|
||||||
|
// Resize the rulers
|
||||||
|
|
||||||
|
const rulerHorizontal = this.$refs.rulerHorizontal as typeof CanvasRuler;
|
||||||
|
const rulerVertical = this.$refs.rulerVertical as typeof CanvasRuler;
|
||||||
|
if (rulerHorizontal) rulerHorizontal.handleResize();
|
||||||
|
if (rulerVertical) rulerVertical.handleResize();
|
||||||
},
|
},
|
||||||
translateCanvasX(newValue: number) {
|
translateCanvasX(newValue: number) {
|
||||||
const delta = newValue - this.scrollbarPos.x;
|
const delta = newValue - this.scrollbarPos.x;
|
||||||
|
|
@ -415,9 +426,7 @@ export default defineComponent({
|
||||||
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentBarLayout, (updateDocumentBarLayout) => {
|
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentBarLayout, (updateDocumentBarLayout) => {
|
||||||
this.documentBarLayout = updateDocumentBarLayout;
|
this.documentBarLayout = updateDocumentBarLayout;
|
||||||
});
|
});
|
||||||
|
this.editor.dispatcher.subscribeJsMessage(TriggerViewportResize, this.viewportResize);
|
||||||
window.addEventListener("resize", this.viewportResize);
|
|
||||||
window.addEventListener("DOMContentLoaded", this.viewportResize);
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const documentModeEntries: SectionsOfMenuListEntries = [
|
const documentModeEntries: SectionsOfMenuListEntries = [
|
||||||
|
|
|
||||||
|
|
@ -138,13 +138,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
window.addEventListener("resize", this.handleResize);
|
|
||||||
this.handleResize();
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
window.removeEventListener("resize", this.handleResize);
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
rulerLength: 0,
|
rulerLength: 0,
|
||||||
|
|
|
||||||
|
|
@ -471,41 +471,44 @@ export class DisplayDialogComingSoon extends JsMessage {
|
||||||
|
|
||||||
export class TriggerTextCommit extends JsMessage {}
|
export class TriggerTextCommit extends JsMessage {}
|
||||||
|
|
||||||
|
export class TriggerViewportResize extends JsMessage {}
|
||||||
|
|
||||||
// Any is used since the type of the object should be known from the rust side
|
// Any is used since the type of the object should be known from the rust side
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type JSMessageFactory = (data: any, wasm: WasmInstance, instance: RustEditorInstance) => JsMessage;
|
type JSMessageFactory = (data: any, wasm: WasmInstance, instance: RustEditorInstance) => JsMessage;
|
||||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||||
|
|
||||||
export const messageConstructors: Record<string, MessageMaker> = {
|
export const messageConstructors: Record<string, MessageMaker> = {
|
||||||
UpdateDocumentArtwork,
|
DisplayConfirmationToCloseAllDocuments,
|
||||||
UpdateDocumentOverlays,
|
DisplayConfirmationToCloseDocument,
|
||||||
UpdateDocumentScrollbars,
|
DisplayDialogAboutGraphite,
|
||||||
UpdateDocumentRulers,
|
DisplayDialogComingSoon,
|
||||||
TriggerFileDownload,
|
DisplayDialogError,
|
||||||
TriggerFileUpload,
|
DisplayDialogPanic,
|
||||||
DisplayDocumentLayerTreeStructure: newDisplayDocumentLayerTreeStructure,
|
DisplayDocumentLayerTreeStructure: newDisplayDocumentLayerTreeStructure,
|
||||||
DisplayEditableTextbox,
|
DisplayEditableTextbox,
|
||||||
DisplayRemoveEditableTextbox,
|
DisplayRemoveEditableTextbox,
|
||||||
UpdateDocumentLayer,
|
TriggerFileDownload,
|
||||||
UpdateActiveTool,
|
TriggerFileUpload,
|
||||||
UpdateActiveDocument,
|
|
||||||
UpdateOpenDocumentsList,
|
|
||||||
UpdateInputHints,
|
|
||||||
UpdateWorkingColors,
|
|
||||||
UpdateCanvasZoom,
|
|
||||||
UpdateCanvasRotation,
|
|
||||||
UpdateMouseCursor,
|
|
||||||
DisplayDialogError,
|
|
||||||
DisplayDialogPanic,
|
|
||||||
DisplayConfirmationToCloseDocument,
|
|
||||||
DisplayConfirmationToCloseAllDocuments,
|
|
||||||
DisplayDialogAboutGraphite,
|
|
||||||
TriggerIndexedDbWriteDocument,
|
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerIndexedDbRemoveDocument,
|
||||||
|
TriggerIndexedDbWriteDocument,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
|
TriggerViewportResize,
|
||||||
|
UpdateActiveDocument,
|
||||||
|
UpdateActiveTool,
|
||||||
|
UpdateCanvasRotation,
|
||||||
|
UpdateCanvasZoom,
|
||||||
UpdateDocumentArtboards,
|
UpdateDocumentArtboards,
|
||||||
UpdateToolOptionsLayout,
|
UpdateDocumentArtwork,
|
||||||
DisplayDialogComingSoon,
|
|
||||||
UpdateDocumentBarLayout,
|
UpdateDocumentBarLayout,
|
||||||
|
UpdateDocumentLayer,
|
||||||
|
UpdateDocumentOverlays,
|
||||||
|
UpdateDocumentRulers,
|
||||||
|
UpdateDocumentScrollbars,
|
||||||
|
UpdateInputHints,
|
||||||
|
UpdateMouseCursor,
|
||||||
|
UpdateOpenDocumentsList,
|
||||||
|
UpdateToolOptionsLayout,
|
||||||
|
UpdateWorkingColors,
|
||||||
} as const;
|
} as const;
|
||||||
export type JsMessageType = keyof typeof messageConstructors;
|
export type JsMessageType = keyof typeof messageConstructors;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { JsMessageType } from "@/dispatcher/js-messages";
|
||||||
export type WasmInstance = typeof import("@/../wasm/pkg");
|
export type WasmInstance = typeof import("@/../wasm/pkg");
|
||||||
export type RustEditorInstance = InstanceType<WasmInstance["JsEditorHandle"]>;
|
export type RustEditorInstance = InstanceType<WasmInstance["JsEditorHandle"]>;
|
||||||
|
|
||||||
|
// `wasmImport` starts uninitialized until `initWasm()` is called in `main.ts` before the Vue app is created
|
||||||
let wasmImport: WasmInstance | null = null;
|
let wasmImport: WasmInstance | null = null;
|
||||||
export async function initWasm(): Promise<void> {
|
export async function initWasm(): Promise<void> {
|
||||||
if (wasmImport !== null) return;
|
if (wasmImport !== null) return;
|
||||||
|
|
@ -62,20 +63,24 @@ export function getWasmInstance(): WasmInstance {
|
||||||
throw new Error("Editor WASM backend was not initialized at application startup");
|
throw new Error("Editor WASM backend was not initialized at application startup");
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateEditorStateType = { dispatcher: ReturnType<typeof createJsDispatcher>; rawWasm: WasmInstance; instance: RustEditorInstance };
|
type CreateEditorStateType = {
|
||||||
|
/// Allows subscribing to messages from the WASM backend
|
||||||
|
rawWasm: WasmInstance;
|
||||||
|
/// Bindings to WASM wrapper declarations (generated by wasm-bindgen)
|
||||||
|
dispatcher: ReturnType<typeof createJsDispatcher>;
|
||||||
|
/// WASM wrapper's exported functions (generated by wasm-bindgen)
|
||||||
|
instance: RustEditorInstance;
|
||||||
|
};
|
||||||
export function createEditorState(): CreateEditorStateType {
|
export function createEditorState(): CreateEditorStateType {
|
||||||
const dispatcher = createJsDispatcher();
|
|
||||||
const rawWasm = getWasmInstance();
|
const rawWasm = getWasmInstance();
|
||||||
|
const dispatcher = createJsDispatcher();
|
||||||
const rustCallback = (messageType: JsMessageType, data: Record<string, unknown>): void => {
|
const instance = new rawWasm.JsEditorHandle((messageType: JsMessageType, data: Record<string, unknown>): void => {
|
||||||
dispatcher.handleJsMessage(messageType, data, rawWasm, instance);
|
dispatcher.handleJsMessage(messageType, data, rawWasm, instance);
|
||||||
};
|
});
|
||||||
|
|
||||||
const instance = new rawWasm.JsEditorHandle(rustCallback);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dispatcher,
|
|
||||||
rawWasm,
|
rawWasm,
|
||||||
|
dispatcher,
|
||||||
instance,
|
instance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue