mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 05:38:13 +08:00
Rework wasm initialization and reduce global state (#379)
* wasm: do the async initialization only once This allows the rest of the app to access wasm synchronously. This allows removing of a global. * provide the wasm via vue provide/inject. There's still code directly accessing the wasm. That will be changed later. * MenuBarInput: use injected wasm instead of the global instance * Let the App handle event listeners * move stateful modules into state/ * state/fullscreen: create per instance * App: load the initial document list on mount. This got lost a few commits ago. Now it's back. * state/dialog: create per instance * util/input: remove dependency on global dialog instance * state/documents: create per instance * reponse-handler: move into EditorWasm * comingSoon: move into dialog * wasm: allow instantiating multiple editors * input handlers: do not look at canvases outside the mounted App * input: listen on the container instead of the window when possible * - removed proxy from wasm-loader - integrated with js-dispatcher - state functions to classes - integrated some upstream changes * fix errors caused by merge * Getting closer: - added global state to track all instances - fix fullscreen close trigger - wasm-loader is statefull - panic across instanes * - fix outline while using editor - removed circular import rule - added editorInstance to js message constructor * - changed input handler to a class - still need a better way of handeling it in App.vue * - fixed single instance of inputManager to weakmap * - fix no-explicit-any in a few places - removed global state from input.ts * simplified two long lines * removed global state * removed $data from App * add mut self to functions in api.rs * Update Workspace.vue remove outdated import * fixed missing import * Changes throughout code review; note this causes some bugs to be fixed in a later commit * PR review round 1 * - fix coming soon bugs - changed folder structure * moved declaration to .d.ts * - changed from classes to functions - moved decs back to app.vue * removed need to export js function to rust * changed folder structure * fixed indentation breaking multiline strings * Fix eslint rule to whitelist @/../ * Simplify strip-indents implementation * replace type assertions with better annotations or proper runtime checks * Small tweaks and code rearranging improvements after second code review pass * maybe fix mouse events * Add back preventDefault for mouse scroll * code review round 2 * Comment improvements * -removed runtime checks - fixed layers not showing * - extened proxy to cover classes - stopped multiple panics from logging - Stop wasm-bindgen from mut ref counting our struct * cleaned up messageConstructors exports * Fix input and fullscreen regressions Co-authored-by: Max Fisher <maxmfishernj@gmail.com> Co-authored-by: mfish33 <32677537+mfish33@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Max Fisher
mfish33
Keavon Chambers
parent
37be856884
commit
448e72fe02
@@ -0,0 +1,136 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import { reactive, readonly } from "vue";
|
||||
|
||||
import { DialogState } from "@/state/dialog";
|
||||
import { download, upload } from "@/utilities/files";
|
||||
import { EditorState } from "@/state/wasm-loader";
|
||||
import {
|
||||
DisplayConfirmationToCloseAllDocuments,
|
||||
DisplayConfirmationToCloseDocument,
|
||||
ExportDocument,
|
||||
OpenDocumentBrowse,
|
||||
SaveDocument,
|
||||
SetActiveDocument,
|
||||
UpdateOpenDocumentsList,
|
||||
} from "@/dispatcher/js-messages";
|
||||
|
||||
class DocumentSaveState {
|
||||
readonly displayName: string;
|
||||
|
||||
constructor(readonly name: string, readonly isSaved: boolean) {
|
||||
this.displayName = `${name}${isSaved ? "" : "*"}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function createDocumentsState(editor: EditorState, dialogState: DialogState) {
|
||||
const state = reactive({
|
||||
unsaved: false,
|
||||
documents: [] as DocumentSaveState[],
|
||||
activeDocumentIndex: 0,
|
||||
});
|
||||
|
||||
const selectDocument = (tabIndex: number) => {
|
||||
editor.instance.select_document(tabIndex);
|
||||
};
|
||||
|
||||
const closeDocumentWithConfirmation = (tabIndex: number) => {
|
||||
// Close automatically if it's already saved, no confirmation is needed
|
||||
const targetDocument = state.documents[tabIndex];
|
||||
if (targetDocument.isSaved) {
|
||||
editor.instance.close_document(tabIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to the document that's being prompted to close
|
||||
selectDocument(tabIndex);
|
||||
|
||||
// Show the close confirmation prompt
|
||||
dialogState.createDialog("File", "Save changes before closing?", targetDocument.displayName, [
|
||||
{
|
||||
kind: "TextButton",
|
||||
callback: () => {
|
||||
editor.instance.save_document();
|
||||
dialogState.dismissDialog();
|
||||
},
|
||||
props: { label: "Save", emphasized: true, minWidth: 96 },
|
||||
},
|
||||
{
|
||||
kind: "TextButton",
|
||||
callback: () => {
|
||||
editor.instance.close_document(tabIndex);
|
||||
dialogState.dismissDialog();
|
||||
},
|
||||
props: { label: "Discard", minWidth: 96 },
|
||||
},
|
||||
{
|
||||
kind: "TextButton",
|
||||
callback: () => {
|
||||
dialogState.dismissDialog();
|
||||
},
|
||||
props: { label: "Cancel", minWidth: 96 },
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const closeAllDocumentsWithConfirmation = () => {
|
||||
dialogState.createDialog("Copy", "Close all documents?", "Unsaved work will be lost!", [
|
||||
{
|
||||
kind: "TextButton",
|
||||
callback: () => {
|
||||
editor.instance.close_all_documents();
|
||||
dialogState.dismissDialog();
|
||||
},
|
||||
props: { label: "Discard All", minWidth: 96 },
|
||||
},
|
||||
{
|
||||
kind: "TextButton",
|
||||
callback: () => {
|
||||
dialogState.dismissDialog();
|
||||
},
|
||||
props: { label: "Cancel", minWidth: 96 },
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
// Set up message subscriptions on creation
|
||||
editor.dispatcher.subscribeJsMessage(UpdateOpenDocumentsList, (updateOpenDocumentList) => {
|
||||
state.documents = updateOpenDocumentList.open_documents.map(({ name, isSaved }) => new DocumentSaveState(name, isSaved));
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(SetActiveDocument, (setActiveDocument) => {
|
||||
state.activeDocumentIndex = setActiveDocument.document_index;
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(DisplayConfirmationToCloseDocument, (displayConfirmationToCloseDocument) => {
|
||||
closeDocumentWithConfirmation(displayConfirmationToCloseDocument.document_index);
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(DisplayConfirmationToCloseAllDocuments, () => {
|
||||
closeAllDocumentsWithConfirmation();
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(OpenDocumentBrowse, async () => {
|
||||
const extension = editor.rawWasm.file_save_suffix();
|
||||
const data = await upload(extension);
|
||||
editor.instance.open_document_file(data.filename, data.content);
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(ExportDocument, (exportDocument) => {
|
||||
download(exportDocument.name, exportDocument.document);
|
||||
});
|
||||
|
||||
editor.dispatcher.subscribeJsMessage(SaveDocument, (saveDocument) => {
|
||||
download(saveDocument.name, saveDocument.document);
|
||||
});
|
||||
|
||||
// Get the initial documents
|
||||
editor.instance.get_open_documents_list();
|
||||
|
||||
return {
|
||||
state: readonly(state),
|
||||
selectDocument,
|
||||
closeDocumentWithConfirmation,
|
||||
closeAllDocumentsWithConfirmation,
|
||||
};
|
||||
}
|
||||
export type DocumentsState = ReturnType<typeof createDocumentsState>;
|
||||
Reference in New Issue
Block a user