Confirm when the browser window is closed and there is unsaved work (#397)
* - browser confirmation on page exit - prompt to save a document when closed will only trigger when unsaved * add back select document for close prompt * - name -> displayname - add preventPropigation to middle click
This commit is contained in:
parent
5f248cd176
commit
9904021744
|
|
@ -4,7 +4,7 @@
|
|||
<MenuBarInput v-if="platform !== ApplicationPlatform.Mac" />
|
||||
</div>
|
||||
<div class="header-third">
|
||||
<WindowTitle :title="`${documents.activeDocument} - Graphite`" />
|
||||
<WindowTitle :title="`${documents.documents[documents.activeDocumentIndex].displayName} - Graphite`" />
|
||||
</div>
|
||||
<div class="header-third">
|
||||
<WindowButtonsWindows :maximized="maximized" v-if="platform === ApplicationPlatform.Windows || platform === ApplicationPlatform.Linux" />
|
||||
|
|
|
|||
|
|
@ -7,11 +7,26 @@
|
|||
:class="{ active: tabIndex === tabActiveIndex }"
|
||||
v-for="(tabLabel, tabIndex) in tabLabels"
|
||||
:key="tabIndex"
|
||||
@click.middle="closeDocumentWithConfirmation(tabIndex)"
|
||||
@click.middle="
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
closeDocumentWithConfirmation(tabIndex);
|
||||
}
|
||||
"
|
||||
@click="panelType === 'Document' && selectDocument(tabIndex)"
|
||||
>
|
||||
<span>{{ tabLabel }}</span>
|
||||
<IconButton :action="() => closeDocumentWithConfirmation(tabIndex)" :icon="'CloseX'" :size="16" v-if="tabCloseButtons" />
|
||||
<IconButton
|
||||
:action="
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
closeDocumentWithConfirmation(tabIndex);
|
||||
}
|
||||
"
|
||||
:icon="'CloseX'"
|
||||
:size="16"
|
||||
v-if="tabCloseButtons"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<PopoverButton :icon="PopoverButtonIcon.VerticalEllipsis">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<LayoutRow class="workspace-grid-subdivision">
|
||||
<LayoutCol class="workspace-grid-subdivision" style="flex-grow: 1597">
|
||||
<Panel :panelType="'Document'" :tabCloseButtons="true" :tabMinWidths="true" :tabLabels="documents.documents" :tabActiveIndex="documents.activeDocumentIndex" />
|
||||
<Panel
|
||||
:panelType="'Document'"
|
||||
:tabCloseButtons="true"
|
||||
:tabMinWidths="true"
|
||||
:tabLabels="documents.documents.map((doc) => doc.displayName)"
|
||||
:tabActiveIndex="documents.activeDocumentIndex"
|
||||
/>
|
||||
</LayoutCol>
|
||||
<LayoutCol class="workspace-grid-resize-gutter"></LayoutCol>
|
||||
<LayoutCol class="workspace-grid-subdivision" style="flex-grow: 319">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createApp } from "vue";
|
||||
|
||||
import { fullscreenModeChanged } from "@/utilities/fullscreen";
|
||||
import { onKeyUp, onKeyDown, onMouseMove, onMouseDown, onMouseUp, onMouseScroll, onWindowResize } from "@/utilities/input";
|
||||
import { onKeyUp, onKeyDown, onMouseMove, onMouseDown, onMouseUp, onMouseScroll, onWindowResize, onBeforeUnload } from "@/utilities/input";
|
||||
import "@/utilities/errors";
|
||||
import App from "@/App.vue";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
|
|
@ -21,6 +21,8 @@ const wasm = import("@/../wasm/pkg").then(panicProxy);
|
|||
window.addEventListener("resize", onWindowResize);
|
||||
onWindowResize();
|
||||
|
||||
window.addEventListener("beforeunload", onBeforeUnload);
|
||||
|
||||
document.addEventListener("contextmenu", (e) => e.preventDefault());
|
||||
document.addEventListener("fullscreenchange", () => fullscreenModeChanged());
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,17 @@ import { panicProxy } from "@/utilities/panic-proxy";
|
|||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
class DocumentState {
|
||||
readonly displayName: string;
|
||||
|
||||
constructor(readonly name: string, readonly isSaved: boolean) {
|
||||
this.displayName = `${name}${isSaved ? "" : "*"}`;
|
||||
}
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
documents: [] as string[],
|
||||
documents: [] as DocumentState[],
|
||||
activeDocumentIndex: 0,
|
||||
get activeDocument() {
|
||||
return this.documents[this.activeDocumentIndex];
|
||||
},
|
||||
});
|
||||
|
||||
export async function selectDocument(tabIndex: number) {
|
||||
|
|
@ -29,9 +34,16 @@ export async function selectDocument(tabIndex: number) {
|
|||
}
|
||||
|
||||
export async function closeDocumentWithConfirmation(tabIndex: number) {
|
||||
selectDocument(tabIndex);
|
||||
const targetDocument = state.documents[tabIndex];
|
||||
if (targetDocument.isSaved) {
|
||||
(await wasm).close_document(tabIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
const tabLabel = state.documents[tabIndex];
|
||||
// Show the document is being prompted to close
|
||||
await selectDocument(tabIndex);
|
||||
|
||||
const tabLabel = targetDocument.displayName;
|
||||
|
||||
createDialog("File", "Save changes before closing?", tabLabel, [
|
||||
{
|
||||
|
|
@ -84,7 +96,7 @@ export default readonly(state);
|
|||
|
||||
registerResponseHandler(ResponseType.UpdateOpenDocumentsList, (responseData: Response) => {
|
||||
const documentListData = responseData as UpdateOpenDocumentsList;
|
||||
state.documents = documentListData.open_documents.map(({ name, isSaved }) => `${name}${isSaved ? "" : "*"}`);
|
||||
state.documents = documentListData.open_documents.map(({ name, isSaved }) => new DocumentState(name, isSaved));
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.SetActiveDocument, (responseData: Response) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { toggleFullscreen } from "@/utilities/fullscreen";
|
||||
import { dialogIsVisible, dismissDialog, submitDialog } from "@/utilities/dialog";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import documents from "./documents";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
|
|
@ -125,6 +126,14 @@ export async function onWindowResize() {
|
|||
if (boundsOfViewports.length > 0) (await wasm).bounds_of_viewports(data);
|
||||
}
|
||||
|
||||
export function onBeforeUnload(event: BeforeUnloadEvent) {
|
||||
const allDocumentsSaved = documents.documents.reduce((acc, doc) => doc.isSaved && acc, true);
|
||||
if (!allDocumentsSaved) {
|
||||
event.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
export function makeModifiersBitfield(e: MouseEvent | KeyboardEvent): number {
|
||||
return Number(e.ctrlKey) | (Number(e.shiftKey) << 1) | (Number(e.altKey) << 2);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue