Polishing: standardize binding JS resize event (fixes non-integer AA rendering); remove "X" to delete; cleanup

This commit is contained in:
Keavon Chambers 2022-02-10 02:29:45 -08:00
parent a4d3c343a0
commit 77dc125095
10 changed files with 63 additions and 49 deletions

View File

@ -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))),

View File

@ -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 },

View File

@ -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]},

View File

@ -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 } => {

View File

@ -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)]

View File

@ -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();

View File

@ -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 = [

View File

@ -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,

View File

@ -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;

View File

@ -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,
}; };
} }