mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-23 15:45:05 +00:00
parent
4c6f2c80bd
commit
a07c1a37a8
10 changed files with 54 additions and 31 deletions
|
@ -129,6 +129,7 @@ impl PropertyHolder for MenuBarMessageHandler {
|
|||
MenuEntry {
|
||||
label: "Import…".into(),
|
||||
shortcut: Some(vec![Key::KeyControl, Key::KeyI]),
|
||||
action: MenuEntry::create_action(|_| PortfolioMessage::Import.into()),
|
||||
..MenuEntry::default()
|
||||
},
|
||||
MenuEntry {
|
||||
|
|
|
@ -45,6 +45,7 @@ pub enum PortfolioMessage {
|
|||
data: Vec<u8>,
|
||||
is_default: bool,
|
||||
},
|
||||
Import,
|
||||
LoadFont {
|
||||
font: Font,
|
||||
is_default: bool,
|
||||
|
|
|
@ -267,6 +267,12 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||
responses.push_back(DocumentMessage::RenderDocument.into());
|
||||
}
|
||||
}
|
||||
Import => {
|
||||
// This portfolio message wraps the frontend message so it can be listed as an action, which isn't possible for frontend messages
|
||||
if self.active_document().is_some() {
|
||||
responses.push_back(FrontendMessage::TriggerImport.into());
|
||||
}
|
||||
}
|
||||
LoadFont { font, is_default } => {
|
||||
if !self.font_cache.loaded_font(&font) {
|
||||
responses.push_front(FrontendMessage::TriggerFontLoad { font, is_default }.into());
|
||||
|
@ -292,7 +298,8 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||
}
|
||||
}
|
||||
OpenDocument => {
|
||||
responses.push_back(FrontendMessage::TriggerFileUpload.into());
|
||||
// This portfolio message wraps the frontend message so it can be listed as an action, which isn't possible for frontend messages
|
||||
responses.push_back(FrontendMessage::TriggerOpenDocument.into());
|
||||
}
|
||||
OpenDocumentFile {
|
||||
document_name,
|
||||
|
@ -493,10 +500,12 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||
let mut common = actions!(PortfolioMessageDiscriminant;
|
||||
CloseActiveDocumentWithConfirmation,
|
||||
CloseAllDocuments,
|
||||
Import,
|
||||
NextDocument,
|
||||
PrevDocument,
|
||||
PasteIntoFolder,
|
||||
OpenDocument,
|
||||
Paste,
|
||||
PasteIntoFolder,
|
||||
PrevDocument,
|
||||
);
|
||||
|
||||
if let Some(document) = self.active_document() {
|
||||
|
|
|
@ -24,10 +24,11 @@ pub enum FrontendMessage {
|
|||
// Trigger prefix: cause a browser API to do something
|
||||
TriggerAboutGraphiteLocalizedCommitDate { commit_date: String },
|
||||
TriggerFileDownload { document: String, name: String },
|
||||
TriggerFileUpload,
|
||||
TriggerFontLoad { font: Font, is_default: bool },
|
||||
TriggerImport,
|
||||
TriggerIndexedDbRemoveDocument { document_id: u64 },
|
||||
TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String },
|
||||
TriggerOpenDocument,
|
||||
TriggerPaste,
|
||||
TriggerRasterDownload { document: String, name: String, mime: String, size: (f64, f64) },
|
||||
TriggerRefreshBoundsOfViewports,
|
||||
|
|
|
@ -145,8 +145,6 @@ impl Default for Mapping {
|
|||
entry! {action=ToolMessage::ResetColors, key_down=KeyX, modifiers=[KeyShift, KeyControl]},
|
||||
entry! {action=ToolMessage::SwapColors, key_down=KeyX, modifiers=[KeyShift]},
|
||||
entry! {action=ToolMessage::SelectRandomPrimaryColor, key_down=KeyC, modifiers=[KeyAlt]},
|
||||
// Editor Actions
|
||||
entry! {action=FrontendMessage::TriggerFileUpload, key_down=KeyO, modifiers=[KeyControl]},
|
||||
// Document actions
|
||||
entry! {action=DocumentMessage::Redo, key_down=KeyZ, modifiers=[KeyControl, KeyShift]},
|
||||
entry! {action=DocumentMessage::Undo, key_down=KeyZ, modifiers=[KeyControl]},
|
||||
|
@ -187,6 +185,8 @@ impl Default for Mapping {
|
|||
entry! {action=MovementMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., 1.) }, key_down=KeyPageUp},
|
||||
entry! {action=MovementMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., -1.) }, key_down=KeyPageDown},
|
||||
// Portfolio actions
|
||||
entry! {action=PortfolioMessage::OpenDocument, key_down=KeyO, modifiers=[KeyControl]},
|
||||
entry! {action=PortfolioMessage::Import, key_down=KeyI, modifiers=[KeyControl]},
|
||||
entry! {action=DialogMessage::RequestNewDocumentDialog, key_down=KeyN, modifiers=[KeyControl]},
|
||||
entry! {action=PortfolioMessage::NextDocument, key_down=KeyTab, modifiers=[KeyControl]},
|
||||
entry! {action=PortfolioMessage::PrevDocument, key_down=KeyTab, modifiers=[KeyControl, KeyShift]},
|
||||
|
|
|
@ -255,7 +255,7 @@ export default defineComponent({
|
|||
this.editor.instance.new_document_dialog();
|
||||
},
|
||||
openDocument() {
|
||||
this.editor.instance.open_file_upload();
|
||||
this.editor.instance.document_open();
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { reactive, readonly } from "vue";
|
|||
|
||||
import { download, downloadBlob, upload } from "@/utility-functions/files";
|
||||
import { Editor } from "@/wasm-communication/editor";
|
||||
import { TriggerFileDownload, TriggerRasterDownload, FrontendDocumentDetails, TriggerFileUpload, UpdateActiveDocument, UpdateOpenDocumentsList } from "@/wasm-communication/messages";
|
||||
import { TriggerFileDownload, TriggerRasterDownload, FrontendDocumentDetails, TriggerOpenDocument, TriggerImport, UpdateActiveDocument, UpdateOpenDocumentsList } from "@/wasm-communication/messages";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function createPortfolioState(editor: Editor) {
|
||||
|
@ -22,11 +22,15 @@ export function createPortfolioState(editor: Editor) {
|
|||
const activeId = state.documents.findIndex((doc) => doc.id === updateActiveDocument.document_id);
|
||||
state.activeDocumentIndex = activeId;
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(TriggerFileUpload, async () => {
|
||||
editor.subscriptions.subscribeJsMessage(TriggerOpenDocument, async () => {
|
||||
const extension = editor.instance.file_save_suffix();
|
||||
const data = await upload(extension);
|
||||
const data = await upload(extension, "text");
|
||||
editor.instance.open_document_file(data.filename, data.content);
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(TriggerImport, async () => {
|
||||
const data = await upload("image/*", "data");
|
||||
editor.instance.paste_image(data.type, Uint8Array.from(data.content));
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(TriggerFileDownload, (triggerFileDownload) => {
|
||||
download(triggerFileDownload.name, triggerFileDownload.document);
|
||||
});
|
||||
|
|
|
@ -18,22 +18,24 @@ export function download(filename: string, fileData: string): void {
|
|||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export async function upload(acceptedEextensions: string): Promise<{ filename: string; content: string }> {
|
||||
return new Promise<{ filename: string; content: string }>((resolve, _) => {
|
||||
export async function upload<T extends "text" | "data">(acceptedExtensions: string, textOrData: T): Promise<UploadResult<T>> {
|
||||
return new Promise<UploadResult<T>>((resolve, _) => {
|
||||
const element = document.createElement("input");
|
||||
element.type = "file";
|
||||
element.style.display = "none";
|
||||
element.accept = acceptedEextensions;
|
||||
element.accept = acceptedExtensions;
|
||||
|
||||
element.addEventListener(
|
||||
"change",
|
||||
async () => {
|
||||
if (element.files?.length) {
|
||||
const file = element.files[0];
|
||||
const filename = file.name;
|
||||
const content = await file.text();
|
||||
|
||||
resolve({ filename, content });
|
||||
const filename = file.name;
|
||||
const type = file.type;
|
||||
const content = (textOrData === "text" ? await file.text() : new Uint8Array(await file.arrayBuffer())) as UploadResultType<T>;
|
||||
|
||||
resolve({ filename, type, content });
|
||||
}
|
||||
},
|
||||
{ capture: false, once: true }
|
||||
|
@ -44,3 +46,5 @@ export async function upload(acceptedEextensions: string): Promise<{ filename: s
|
|||
// Once `element` goes out of scope, it has no references so it gets garbage collected along with its event listener, so `removeEventListener` is not needed
|
||||
});
|
||||
}
|
||||
export type UploadResult<T> = { filename: string; type: string; content: UploadResultType<T> };
|
||||
type UploadResultType<T> = T extends "text" ? string : T extends "data" ? Uint8Array : never;
|
||||
|
|
|
@ -206,7 +206,9 @@ export class TriggerFileDownload extends JsMessage {
|
|||
readonly name!: string;
|
||||
}
|
||||
|
||||
export class TriggerFileUpload extends JsMessage {}
|
||||
export class TriggerOpenDocument extends JsMessage {}
|
||||
|
||||
export class TriggerImport extends JsMessage {}
|
||||
|
||||
export class TriggerPaste extends JsMessage {}
|
||||
|
||||
|
@ -808,23 +810,22 @@ type MessageMaker = typeof JsMessage | JSMessageFactory;
|
|||
|
||||
export const messageMakers: Record<string, MessageMaker> = {
|
||||
DisplayDialog,
|
||||
DisplayDialogPanic,
|
||||
UpdateDocumentLayerTreeStructure: newUpdateDocumentLayerTreeStructure,
|
||||
DisplayEditableTextbox,
|
||||
UpdateImageData,
|
||||
DisplayRemoveEditableTextbox,
|
||||
DisplayDialogDismiss,
|
||||
DisplayDialogPanic,
|
||||
DisplayEditableTextbox,
|
||||
DisplayRemoveEditableTextbox,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerOpenDocument,
|
||||
TriggerFileDownload,
|
||||
TriggerFileUpload,
|
||||
TriggerIndexedDbRemoveDocument,
|
||||
TriggerFontLoad,
|
||||
TriggerImport,
|
||||
TriggerIndexedDbRemoveDocument,
|
||||
TriggerIndexedDbWriteDocument,
|
||||
TriggerPaste,
|
||||
TriggerRasterDownload,
|
||||
TriggerRefreshBoundsOfViewports,
|
||||
TriggerTextCommit,
|
||||
TriggerTextCopy,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerViewportResize,
|
||||
TriggerVisitLink,
|
||||
UpdateActiveDocument,
|
||||
|
@ -832,21 +833,23 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdateDocumentArtboards,
|
||||
UpdateDocumentArtwork,
|
||||
UpdateDocumentBarLayout,
|
||||
UpdateToolShelfLayout,
|
||||
UpdateDocumentLayerDetails,
|
||||
UpdateDocumentLayerTreeStructure: newUpdateDocumentLayerTreeStructure,
|
||||
UpdateDocumentModeLayout,
|
||||
UpdateDocumentOverlays,
|
||||
UpdateDocumentRulers,
|
||||
UpdateDocumentScrollbars,
|
||||
UpdateImageData,
|
||||
UpdateInputHints,
|
||||
UpdateLayerTreeOptionsLayout,
|
||||
UpdateMenuBarLayout,
|
||||
UpdateMouseCursor,
|
||||
UpdateNodeGraphVisibility,
|
||||
UpdateOpenDocumentsList,
|
||||
UpdatePropertyPanelOptionsLayout,
|
||||
UpdatePropertyPanelSectionsLayout,
|
||||
UpdateLayerTreeOptionsLayout,
|
||||
UpdateDocumentModeLayout,
|
||||
UpdateToolOptionsLayout,
|
||||
UpdateToolShelfLayout,
|
||||
UpdateWorkingColorsLayout,
|
||||
UpdateMenuBarLayout,
|
||||
} as const;
|
||||
export type JsMessageType = keyof typeof messageMakers;
|
||||
|
|
|
@ -154,8 +154,8 @@ impl JsEditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
pub fn open_file_upload(&self) {
|
||||
let message = FrontendMessage::TriggerFileUpload;
|
||||
pub fn document_open(&self) {
|
||||
let message = PortfolioMessage::OpenDocument;
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue