Skip to content

Commit 40425d4

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Unified the sketch close and the app quit logic.
Signed-off-by: Akos Kitta <[email protected]>
1 parent 0c87fa9 commit 40425d4

File tree

5 files changed

+113
-64
lines changed

5 files changed

+113
-64
lines changed

Diff for: arduino-ide-extension/src/browser/contributions/close.ts

+98-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { injectable } from '@theia/core/shared/inversify';
22
import * as remote from '@theia/core/electron-shared/@electron/remote';
33
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
4+
import type { MaybePromise } from '@theia/core/lib/common/types';
5+
import type {
6+
FrontendApplication,
7+
OnWillStopAction,
8+
} from '@theia/core/lib/browser/frontend-application';
9+
import { nls } from '@theia/core/lib/common/nls';
10+
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
411
import { ArduinoMenus } from '../menu/arduino-menus';
512
import {
613
SketchContribution,
@@ -11,17 +18,21 @@ import {
1118
Sketch,
1219
URI,
1320
} from './contribution';
14-
import { nls } from '@theia/core/lib/common';
1521
import { Dialog } from '@theia/core/lib/browser/dialogs';
1622
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
1723
import { SaveAsSketch } from './save-as-sketch';
18-
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
1924

2025
/**
2126
* Closes the `current` closeable editor, or any closeable current widget from the main area, or the current sketch window.
2227
*/
2328
@injectable()
2429
export class Close extends SketchContribution {
30+
private shell: ApplicationShell | undefined;
31+
32+
override onStart(app: FrontendApplication): MaybePromise<void> {
33+
this.shell = app.shell;
34+
}
35+
2536
override registerCommands(registry: CommandRegistry): void {
2637
registry.registerCommand(Close.Commands.CLOSE, {
2738
execute: () => remote.getCurrentWindow().close(),
@@ -46,20 +57,41 @@ export class Close extends SketchContribution {
4657
// `FrontendApplicationContribution#onWillStop`
4758
onWillStop(): OnWillStopAction {
4859
return {
49-
reason: 'temp-sketch',
60+
reason: 'save-sketch',
5061
action: () => {
51-
return this.showSaveTempSketchDialog();
62+
return this.showSaveSketchDialog();
5263
},
5364
};
5465
}
5566

56-
private async showSaveTempSketchDialog(): Promise<boolean> {
57-
const sketch = await this.sketchServiceClient.currentSketch();
58-
if (!CurrentSketch.isValid(sketch)) {
59-
return true;
60-
}
61-
const isTemp = await this.sketchService.isTemp(sketch);
62-
if (!isTemp) {
67+
/**
68+
* If returns with `true`, IDE2 will close. Otherwise, it won't.
69+
*/
70+
private async showSaveSketchDialog(): Promise<boolean> {
71+
const sketch = await this.isCurrentSketchTemp();
72+
if (!sketch) {
73+
// Normal close workflow: if there are dirty editors prompt the user.
74+
if (!this.shell) {
75+
console.error(
76+
`Could not get the application shell. Something went wrong.`
77+
);
78+
return true;
79+
}
80+
if (this.shell.canSaveAll()) {
81+
const prompt = await this.prompt(false);
82+
switch (prompt) {
83+
case Prompt.DoNotSave:
84+
return true;
85+
case Prompt.Cancel:
86+
return false;
87+
case Prompt.Save: {
88+
await this.shell.saveAll();
89+
return true;
90+
}
91+
default:
92+
throw new Error(`Unexpected prompt: ${prompt}`);
93+
}
94+
}
6395
return true;
6496
}
6597

@@ -71,11 +103,36 @@ export class Close extends SketchContribution {
71103
return true;
72104
}
73105

74-
const messageBoxResult = await remote.dialog.showMessageBox(
106+
const prompt = await this.prompt(true);
107+
switch (prompt) {
108+
case Prompt.DoNotSave:
109+
return true;
110+
case Prompt.Cancel:
111+
return false;
112+
case Prompt.Save: {
113+
// If `save as` was canceled by user, the result will be `undefined`, otherwise the new URI.
114+
const result = await this.commandService.executeCommand(
115+
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
116+
{
117+
execOnlyIfTemp: false,
118+
openAfterMove: false,
119+
wipeOriginal: true,
120+
markAsRecentlyOpened: true,
121+
}
122+
);
123+
return !!result;
124+
}
125+
default:
126+
throw new Error(`Unexpected prompt: ${prompt}`);
127+
}
128+
}
129+
130+
private async prompt(isTemp: boolean): Promise<Prompt> {
131+
const { response } = await remote.dialog.showMessageBox(
75132
remote.getCurrentWindow(),
76133
{
77134
message: nls.localize(
78-
'arduino/sketch/saveTempSketch',
135+
'arduino/sketch/saveSketch',
79136
'Save your sketch to open it again later.'
80137
),
81138
title: nls.localize(
@@ -84,24 +141,32 @@ export class Close extends SketchContribution {
84141
),
85142
type: 'question',
86143
buttons: [
87-
Dialog.CANCEL,
88-
nls.localizeByDefault('Save As...'),
89144
nls.localizeByDefault("Don't Save"),
145+
Dialog.CANCEL,
146+
nls.localizeByDefault(isTemp ? 'Save As...' : 'Save'),
90147
],
148+
defaultId: 2, // `Save`/`Save As...` button index is the default.
91149
}
92150
);
93-
const result = messageBoxResult.response;
94-
if (result === 2) {
95-
return true;
96-
} else if (result === 1) {
97-
return !!(await this.commandService.executeCommand(
98-
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
99-
{
100-
execOnlyIfTemp: false,
101-
openAfterMove: false,
102-
wipeOriginal: true,
103-
}
104-
));
151+
switch (response) {
152+
case 0:
153+
return Prompt.DoNotSave;
154+
case 1:
155+
return Prompt.Cancel;
156+
case 2:
157+
return Prompt.Save;
158+
default:
159+
throw new Error(`Unexpected response: ${response}`);
160+
}
161+
}
162+
163+
private async isCurrentSketchTemp(): Promise<false | Sketch> {
164+
const currentSketch = await this.sketchServiceClient.currentSketch();
165+
if (CurrentSketch.isValid(currentSketch)) {
166+
const isTemp = await this.sketchService.isTemp(currentSketch);
167+
if (isTemp) {
168+
return currentSketch;
169+
}
105170
}
106171
return false;
107172
}
@@ -128,6 +193,12 @@ export class Close extends SketchContribution {
128193
}
129194
}
130195

196+
enum Prompt {
197+
Save,
198+
DoNotSave,
199+
Cancel,
200+
}
201+
131202
export namespace Close {
132203
export namespace Commands {
133204
export const CLOSE: Command = {

Diff for: arduino-ide-extension/src/browser/contributions/save-as-sketch.ts

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class SaveAsSketch extends SketchContribution {
5757
execOnlyIfTemp,
5858
openAfterMove,
5959
wipeOriginal,
60+
markAsRecentlyOpened,
6061
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
6162
): Promise<boolean> {
6263
const sketch = await this.sketchServiceClient.currentSketch();
@@ -102,6 +103,9 @@ export class SaveAsSketch extends SketchContribution {
102103
});
103104
if (workspaceUri) {
104105
await this.saveOntoCopiedSketch(sketch.mainFileUri, sketch.uri, workspaceUri);
106+
if (markAsRecentlyOpened) {
107+
this.sketchService.markAsRecentlyOpened(workspaceUri);
108+
}
105109
}
106110
if (workspaceUri && openAfterMove) {
107111
this.windowService.setSafeToShutDown();
@@ -171,12 +175,14 @@ export namespace SaveAsSketch {
171175
* Ignored if `openAfterMove` is `false`.
172176
*/
173177
readonly wipeOriginal?: boolean;
178+
readonly markAsRecentlyOpened?: boolean;
174179
}
175180
export namespace Options {
176181
export const DEFAULT: Options = {
177182
execOnlyIfTemp: false,
178183
openAfterMove: true,
179184
wipeOriginal: false,
185+
markAsRecentlyOpened: false,
180186
};
181187
}
182188
}

Diff for: arduino-ide-extension/src/browser/theia/core/application-shell.ts

+3-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { injectable, inject } from '@theia/core/shared/inversify';
22
import { EditorWidget } from '@theia/editor/lib/browser';
3-
import { CommandService } from '@theia/core/lib/common/command';
43
import { MessageService } from '@theia/core/lib/common/message-service';
54
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
65
import {
@@ -15,9 +14,9 @@ import {
1514
TabBar,
1615
Widget,
1716
SHELL_TABBAR_CONTEXT_MENU,
17+
SaveOptions,
1818
} from '@theia/core/lib/browser';
1919
import { Sketch } from '../../../common/protocol';
20-
import { SaveAsSketch } from '../../contributions/save-as-sketch';
2120
import {
2221
CurrentSketch,
2322
SketchesServiceClientImpl,
@@ -28,9 +27,6 @@ import { ToolbarAwareTabBar } from './tab-bars';
2827

2928
@injectable()
3029
export class ApplicationShell extends TheiaApplicationShell {
31-
@inject(CommandService)
32-
private readonly commandService: CommandService;
33-
3430
@inject(MessageService)
3531
private readonly messageService: MessageService;
3632

@@ -106,7 +102,7 @@ export class ApplicationShell extends TheiaApplicationShell {
106102
return topPanel;
107103
}
108104

109-
override async saveAll(): Promise<void> {
105+
override async saveAll(options?: SaveOptions): Promise<void> {
110106
if (
111107
this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE
112108
) {
@@ -118,12 +114,7 @@ export class ApplicationShell extends TheiaApplicationShell {
118114
);
119115
return; // Theia does not reject on failed save: https://github.com/eclipse-theia/theia/pull/8803
120116
}
121-
await super.saveAll();
122-
const options = { execOnlyIfTemp: true, openAfterMove: true };
123-
await this.commandService.executeCommand(
124-
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
125-
options
126-
);
117+
return super.saveAll(options);
127118
}
128119
}
129120

Diff for: arduino-ide-extension/src/browser/theia/core/common-frontend-contribution.ts

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
CommonCommands,
66
} from '@theia/core/lib/browser/common-frontend-contribution';
77
import { CommandRegistry } from '@theia/core/lib/common/command';
8+
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
89

910
@injectable()
1011
export class CommonFrontendContribution extends TheiaCommonFrontendContribution {
@@ -48,4 +49,9 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
4849
registry.unregisterMenuAction(command);
4950
}
5051
}
52+
53+
override onWillStop(): OnWillStopAction | undefined {
54+
// This is NOOP here. All window close and app quit requests are handled in the `Close` contribution.
55+
return undefined;
56+
}
5157
}

Diff for: arduino-ide-extension/src/electron-browser/theia/core/electron-menu-module.ts

-25
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,6 @@ import { ElectronMenuContribution as TheiaElectronMenuContribution } from '@thei
44
import { MainMenuManager } from '../../../common/main-menu-manager';
55
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
66
import { ElectronMenuContribution } from './electron-menu-contribution';
7-
import { nls } from '@theia/core/lib/common/nls';
8-
9-
import * as remote from '@theia/core/electron-shared/@electron/remote';
10-
import * as dialogs from '@theia/core/lib/browser/dialogs';
11-
12-
Object.assign(dialogs, {
13-
confirmExit: async () => {
14-
const messageBoxResult = await remote.dialog.showMessageBox(
15-
remote.getCurrentWindow(),
16-
{
17-
message: nls.localize(
18-
'theia/core/quitMessage',
19-
'Any unsaved changes will not be saved.'
20-
),
21-
title: nls.localize(
22-
'theia/core/quitTitle',
23-
'Are you sure you want to quit?'
24-
),
25-
type: 'question',
26-
buttons: [dialogs.Dialog.CANCEL, dialogs.Dialog.YES],
27-
}
28-
);
29-
return messageBoxResult.response === 1;
30-
},
31-
});
327

338
export default new ContainerModule((bind, unbind, isBound, rebind) => {
349
bind(ElectronMenuContribution).toSelf().inSingletonScope();

0 commit comments

Comments
 (0)