Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions client/web/src/components/widgets/inputs/MenuBarInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ const menuEntries: MenuListEntries = [
},
],
[
{ label: "Close", shortcut: ["Ctrl", "W"] },
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"] },
{ label: "Close", shortcut: ["Ctrl", "W"], action: async () => (await wasm).close_active_document_with_confirmation() },
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"], action: async () => (await wasm).close_all_documents_with_confirmation() },
],
[
{ label: "Save", shortcut: ["Ctrl", "S"] },
Expand Down Expand Up @@ -154,6 +154,7 @@ export default defineComponent({
window.open("https://www.graphite.design", "_blank");
},
actionNotImplemented() {
// eslint-disable-next-line no-alert
alert("This action is not yet implemented");
},
},
Expand Down
41 changes: 27 additions & 14 deletions client/web/src/components/workspace/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
:class="{ active: tabIndex === tabActiveIndex }"
v-for="(tabLabel, tabIndex) in tabLabels"
:key="tabLabel"
@click.middle="closeTab(tabIndex)"
@click.middle="handleTabClose(tabIndex)"
@click="handleTabClick(tabIndex)"
>
<span>{{ tabLabel }}</span>
<IconButton :icon="'CloseX'" :size="16" v-if="tabCloseButtons" @click.stop="closeTab(tabIndex)" />
<IconButton :icon="'CloseX'" :size="16" v-if="tabCloseButtons" @click.stop="handleTabClose(tabIndex)" />
</div>
</div>
<PopoverButton :icon="PopoverButtonIcon.VerticalEllipsis">
Expand Down Expand Up @@ -150,7 +150,7 @@ import Minimap from "../panels/Minimap.vue";
import IconButton from "../widgets/buttons/IconButton.vue";
import PopoverButton, { PopoverButtonIcon } from "../widgets/buttons/PopoverButton.vue";
import { MenuDirection } from "../widgets/floating-menus/FloatingMenu.vue";
import { ResponseType, registerResponseHandler, Response } from "../../utilities/response-handler";
import { ResponseType, registerResponseHandler, Response, PromptConfirmationToCloseDocument } from "../../utilities/response-handler";

const wasm = import("../../../wasm/pkg");

Expand All @@ -164,24 +164,37 @@ export default defineComponent({
PopoverButton,
},
methods: {
async handleTabClick(tabIndex: number) {
if (this.panelType !== "Document") return;

handleTabClick(tabIndex: number) {
if (this.panelType === "Document") this.selectDocument(tabIndex);
},
handleTabClose(tabIndex: number) {
if (this.panelType === "Document") this.closeDocumentWithConfirmation(tabIndex);
},
async selectDocument(tabIndex: number) {
const { select_document } = await wasm;
select_document(tabIndex);
},
async closeTab(tabIndex: number) {
if (this.panelType !== "Document") return;

const { close_document } = await wasm;
async closeDocumentWithConfirmation(tabIndex: number) {
// eslint-disable-next-line no-alert
const result = window.confirm("Closing this document will permanently discard all work. Continue?");
if (result) close_document(tabIndex);
const userConfirmation = window.confirm("Closing this document will permanently discard all work. Continue?");
if (userConfirmation) (await wasm).close_document(tabIndex);
},
async closeAllDocumentsWithConfirmation() {
// eslint-disable-next-line no-alert
const userConfirmation = window.confirm("Closing all documents will permanently discard all work in each of them. Continue?");
if (userConfirmation) (await wasm).close_all_documents();
},
},
mounted() {
registerResponseHandler(ResponseType.PromptCloseConfirmationModal, (_responseData: Response) => {
this.closeTab(this.tabActiveIndex);
// TODO: Move these somewhere more appropriate to act upon all panels

registerResponseHandler(ResponseType.PromptConfirmationToCloseDocument, (responseData: Response) => {
const promptData = responseData as PromptConfirmationToCloseDocument;
this.closeDocumentWithConfirmation(promptData.document_index);
});

registerResponseHandler(ResponseType.PromptConfirmationToCloseAllDocuments, (_responseData: Response) => {
this.closeAllDocumentsWithConfirmation();
});
},
props: {
Expand Down
17 changes: 6 additions & 11 deletions client/web/src/components/workspace/Workspace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

<script lang="ts">
import { defineComponent } from "vue";
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, NewDocument, CloseDocument } from "../../utilities/response-handler";
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, UpdateOpenDocumentsList } from "../../utilities/response-handler";
import LayoutRow from "../layout/LayoutRow.vue";
import LayoutCol from "../layout/LayoutCol.vue";
import Panel from "./Panel.vue";
Expand All @@ -59,15 +59,10 @@ export default defineComponent({
},

mounted() {
registerResponseHandler(ResponseType.NewDocument, (responseData: Response) => {
const documentData = responseData as NewDocument;
if (documentData) this.documents.push(documentData.document_name);
});

registerResponseHandler(ResponseType.CloseDocument, (responseData: Response) => {
const documentData = responseData as CloseDocument;
if (documentData) {
this.documents.splice(documentData.document_index, 1);
registerResponseHandler(ResponseType.UpdateOpenDocumentsList, (responseData: Response) => {
const documentListData = responseData as UpdateOpenDocumentsList;
if (documentListData) {
this.documents = documentListData.open_documents;
}
});

Expand All @@ -80,7 +75,7 @@ export default defineComponent({
data() {
return {
activeDocument: 0,
documents: ["Untitled Document"],
documents: ["Untitled Document"], // TODO: start as an empty list
};
},
});
Expand Down
38 changes: 21 additions & 17 deletions client/web/src/utilities/response-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export enum ResponseType {
CollapseFolder = "CollapseFolder",
SetActiveTool = "SetActiveTool",
SetActiveDocument = "SetActiveDocument",
NewDocument = "NewDocument",
CloseDocument = "CloseDocument",
UpdateOpenDocumentsList = "UpdateOpenDocumentsList",
UpdateWorkingColors = "UpdateWorkingColors",
PromptCloseConfirmationModal = "PromptCloseConfirmationModal",
SetCanvasZoom = "SetCanvasZoom",
SetRotation = "SetRotation",
PromptConfirmationToCloseDocument = "PromptConfirmationToCloseDocument",
PromptConfirmationToCloseAllDocuments = "PromptConfirmationToCloseAllDocuments",
}

export function registerResponseHandler(responseType: ResponseType, callback: ResponseCallback) {
Expand Down Expand Up @@ -57,10 +57,8 @@ function parseResponse(responseType: string, data: any): Response {
return newSetActiveTool(data.SetActiveTool);
case "SetActiveDocument":
return newSetActiveDocument(data.SetActiveDocument);
case "NewDocument":
return newNewDocument(data.NewDocument);
case "CloseDocument":
return newCloseDocument(data.CloseDocument);
case "UpdateOpenDocumentsList":
return newUpdateOpenDocumentsList(data.UpdateOpenDocumentsList);
case "UpdateCanvas":
return newUpdateCanvas(data.UpdateCanvas);
case "SetCanvasZoom":
Expand All @@ -71,20 +69,22 @@ function parseResponse(responseType: string, data: any): Response {
return newExportDocument(data.ExportDocument);
case "UpdateWorkingColors":
return newUpdateWorkingColors(data.UpdateWorkingColors);
case "PromptCloseConfirmationModal":
return {};
case "PromptConfirmationToCloseDocument":
return newPromptConfirmationToCloseDocument(data.PromptConfirmationToCloseDocument);
case "PromptConfirmationToCloseAllDocuments":
return newPromptConfirmationToCloseAllDocuments(data.PromptConfirmationToCloseAllDocuments);
default:
throw new Error(`Unrecognized origin/responseType pair: ${origin}, '${responseType}'`);
}
}

export type Response = SetActiveTool | UpdateCanvas | DocumentChanged | CollapseFolder | ExpandFolder | UpdateWorkingColors | SetCanvasZoom | SetRotation;

export interface CloseDocument {
document_index: number;
export interface UpdateOpenDocumentsList {
open_documents: Array<string>;
}
function newCloseDocument(input: any): CloseDocument {
return { document_index: input.document_index };
function newUpdateOpenDocumentsList(input: any): UpdateOpenDocumentsList {
return { open_documents: input.open_documents };
}

export interface Color {
Expand Down Expand Up @@ -127,15 +127,19 @@ function newSetActiveDocument(input: any): SetActiveDocument {
};
}

export interface NewDocument {
document_name: string;
export interface PromptConfirmationToCloseDocument {
document_index: number;
}
function newNewDocument(input: any): NewDocument {
function newPromptConfirmationToCloseDocument(input: any): PromptConfirmationToCloseDocument {
return {
document_name: input.document_name,
document_index: input.document_index,
};
}

function newPromptConfirmationToCloseAllDocuments(_input: any): {} {
return {};
}

export interface UpdateCanvas {
document: string;
}
Expand Down
25 changes: 20 additions & 5 deletions client/web/wasm/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,29 @@ pub fn select_document(document: usize) -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SelectDocument(document)).map_err(convert_error))
}

#[wasm_bindgen]
pub fn new_document() -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::NewDocument).map_err(convert_error))
}

#[wasm_bindgen]
pub fn close_document(document: usize) -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::CloseDocument(document)).map_err(convert_error))
}

#[wasm_bindgen]
pub fn new_document() -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::NewDocument).map_err(convert_error))
pub fn close_all_documents() -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::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(DocumentMessage::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(DocumentMessage::CloseAllDocumentsWithConfirmation).map_err(convert_error))
}

// TODO: Call event when the panels are resized
Expand Down Expand Up @@ -196,23 +211,23 @@ pub fn toggle_layer_expansion(path: Vec<LayerId>) -> Result<(), JsValue> {
.map_err(convert_error)
}

/// Renames a layer from the layer list
/// 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
/// 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
/// 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)
Expand Down
8 changes: 4 additions & 4 deletions core/editor/src/document/document_file.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use crate::{consts::ROTATE_SNAP_INTERVAL, frontend::layer_panel::*, EditorError};
use document_core::{document::Document as InteralDocument, layers::Layer, LayerId};
use document_core::{document::Document as InternalDocument, layers::Layer, LayerId};
use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Clone, Debug)]
pub struct Document {
pub document: InteralDocument,
pub document: InternalDocument,
pub name: String,
pub layer_data: HashMap<Vec<LayerId>, LayerData>,
}

impl Default for Document {
fn default() -> Self {
Self {
document: InteralDocument::default(),
document: InternalDocument::default(),
name: String::from("Untitled Document"),
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
}
Expand All @@ -24,7 +24,7 @@ impl Default for Document {
impl Document {
pub fn with_name(name: String) -> Self {
Self {
document: InteralDocument::default(),
document: InternalDocument::default(),
name,
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
}
Expand Down
Loading