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:
mfish33 2021-12-04 16:49:52 -08:00 committed by Keavon Chambers
parent 5f248cd176
commit 9904021744
6 changed files with 56 additions and 12 deletions

View File

@ -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" />

View File

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

View File

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

View File

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

View File

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

View File

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