Skip to content

Commit b62f3de

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
#714: UX improvements of the Arduino LS in IDE2
- Debounced the connectivity status update. - Silent the output channel for the Arduino LS. - Delay the problem markers update with 500ms. - Do not update the status bar on every `keypress` event. - Debounced the tab-bar toolbar updates when typing in editor. - Fixed electron menu contribution binding. - Aligned the editor widget factory's API to Theia. - Set the zoom level when the app is ready (Closes #1244) - Fixed event listener leak (Closes #1062) Signed-off-by: Akos Kitta <[email protected]>
1 parent 90d2950 commit b62f3de

File tree

12 files changed

+129
-35
lines changed

12 files changed

+129
-35
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class ArduinoFrontendContribution
104104
}
105105
}
106106
});
107-
this.appStateService.reachedState('initialized_layout').then(() =>
107+
this.appStateService.reachedState('ready').then(() =>
108108
this.arduinoPreferences.ready.then(() => {
109109
const webContents = remote.getCurrentWebContents();
110110
const zoomLevel = this.arduinoPreferences.get(

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ import {
5050
ApplicationShell as TheiaApplicationShell,
5151
ShellLayoutRestorer as TheiaShellLayoutRestorer,
5252
CommonFrontendContribution as TheiaCommonFrontendContribution,
53+
DockPanelRenderer as TheiaDockPanelRenderer,
5354
TabBarRendererFactory,
5455
ContextMenuRenderer,
5556
createTreeContainer,
5657
TreeWidget,
5758
} from '@theia/core/lib/browser';
5859
import { MenuContribution } from '@theia/core/lib/common/menu';
59-
import { ApplicationShell } from './theia/core/application-shell';
60+
import {
61+
ApplicationShell,
62+
DockPanelRenderer,
63+
} from './theia/core/application-shell';
6064
import { FrontendApplication } from './theia/core/frontend-application';
6165
import {
6266
BoardsConfigDialog,
@@ -315,6 +319,8 @@ import { OpenBoardsConfig } from './contributions/open-boards-config';
315319
import { SketchFilesTracker } from './contributions/sketch-files-tracker';
316320
import { MonacoThemeServiceIsReady } from './utils/window';
317321
import { Deferred } from '@theia/core/lib/common/promise-util';
322+
import { StatusBarImpl } from './theia/core/status-bar';
323+
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
318324

319325
const registerArduinoThemes = () => {
320326
const themes: MonacoThemeJson[] = [
@@ -583,7 +589,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
583589

584590
// Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times.
585591
bind(EditorManager).toSelf().inSingletonScope();
586-
rebind(TheiaEditorManager).to(EditorManager);
592+
rebind(TheiaEditorManager).toService(EditorManager);
587593

588594
// replace search icon
589595
rebind(TheiaSearchInWorkspaceFactory)
@@ -822,6 +828,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
822828
bind(WidgetManager).toSelf().inSingletonScope();
823829
rebind(TheiaWidgetManager).toService(WidgetManager);
824830

831+
// To avoid running a status bar update on every single `keypress` event from the editor.
832+
bind(StatusBarImpl).toSelf().inSingletonScope();
833+
rebind(TheiaStatusBarImpl).toService(StatusBarImpl);
834+
835+
// Debounced update for the tab-bar toolbar when typing in the editor.
836+
bind(DockPanelRenderer).toSelf();
837+
rebind(TheiaDockPanelRenderer).toService(DockPanelRenderer);
838+
825839
// Preferences
826840
bindArduinoPreferences(bind);
827841

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

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export class InoLanguage extends SketchContribution {
145145
name: name ? `"${name}"` : undefined,
146146
},
147147
realTimeDiagnostics,
148+
silentOutput: true,
148149
}
149150
),
150151
]);

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

+49-16
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,35 @@ import {
1010
import {
1111
ApplicationShell as TheiaApplicationShell,
1212
DockPanel,
13+
DockPanelRenderer as TheiaDockPanelRenderer,
1314
Panel,
15+
TabBar,
1416
Widget,
17+
SHELL_TABBAR_CONTEXT_MENU,
1518
} from '@theia/core/lib/browser';
1619
import { Sketch } from '../../../common/protocol';
1720
import { SaveAsSketch } from '../../contributions/save-as-sketch';
18-
import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
21+
import {
22+
CurrentSketch,
23+
SketchesServiceClientImpl,
24+
} from '../../../common/protocol/sketches-service-client-impl';
1925
import { nls } from '@theia/core/lib/common';
2026
import URI from '@theia/core/lib/common/uri';
27+
import { ToolbarAwareTabBar } from './tab-bars';
2128

2229
@injectable()
2330
export class ApplicationShell extends TheiaApplicationShell {
2431
@inject(CommandService)
25-
protected readonly commandService: CommandService;
32+
private readonly commandService: CommandService;
2633

2734
@inject(MessageService)
28-
protected readonly messageService: MessageService;
35+
private readonly messageService: MessageService;
2936

3037
@inject(SketchesServiceClientImpl)
31-
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
38+
private readonly sketchesServiceClient: SketchesServiceClientImpl;
3239

3340
@inject(ConnectionStatusService)
34-
protected readonly connectionStatusService: ConnectionStatusService;
41+
private readonly connectionStatusService: ConnectionStatusService;
3542

3643
protected override track(widget: Widget): void {
3744
super.track(widget);
@@ -43,7 +50,7 @@ export class ApplicationShell extends TheiaApplicationShell {
4350
this.sketchesServiceClient.currentSketch().then((sketch) => {
4451
if (CurrentSketch.isValid(sketch)) {
4552
if (!this.isSketchFile(widget.editor.uri, sketch.uri)) {
46-
return;
53+
return;
4754
}
4855
if (Sketch.isInSketch(widget.editor.uri, sketch)) {
4956
widget.title.closable = false;
@@ -54,11 +61,11 @@ export class ApplicationShell extends TheiaApplicationShell {
5461
}
5562

5663
private isSketchFile(uri: URI, sketchUriString: string): boolean {
57-
const sketchUri = new URI(sketchUriString);
58-
if (uri.parent.isEqual(sketchUri)) {
59-
return true;
60-
}
61-
return false;
64+
const sketchUri = new URI(sketchUriString);
65+
if (uri.parent.isEqual(sketchUri)) {
66+
return true;
67+
}
68+
return false;
6269
}
6370

6471
override async addWidget(
@@ -120,15 +127,41 @@ export class ApplicationShell extends TheiaApplicationShell {
120127
}
121128
}
122129

130+
export class DockPanelRenderer extends TheiaDockPanelRenderer {
131+
override createTabBar(): TabBar<Widget> {
132+
const renderer = this.tabBarRendererFactory();
133+
// `ToolbarAwareTabBar` is from IDE2 and not from Theia. Check the imports.
134+
const tabBar = new ToolbarAwareTabBar(
135+
this.tabBarToolbarRegistry,
136+
this.tabBarToolbarFactory,
137+
this.breadcrumbsRendererFactory,
138+
{
139+
renderer,
140+
// Scroll bar options
141+
handlers: ['drag-thumb', 'keyboard', 'wheel', 'touch'],
142+
useBothWheelAxes: true,
143+
scrollXMarginOffset: 4,
144+
suppressScrollY: true,
145+
}
146+
);
147+
this.tabBarClasses.forEach((c) => tabBar.addClass(c));
148+
renderer.tabBar = tabBar;
149+
tabBar.disposed.connect(() => renderer.dispose());
150+
renderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU;
151+
tabBar.currentChanged.connect(this.onCurrentTabChanged, this);
152+
return tabBar;
153+
}
154+
}
155+
123156
const originalHandleEvent = DockPanel.prototype.handleEvent;
124157

125158
DockPanel.prototype.handleEvent = function (event) {
126159
switch (event.type) {
127-
case 'p-dragenter':
128-
case 'p-dragleave':
129-
case 'p-dragover':
130-
case 'p-drop':
131-
return;
160+
case 'p-dragenter':
161+
case 'p-dragleave':
162+
case 'p-dragover':
163+
case 'p-drop':
164+
return;
132165
}
133166
originalHandleEvent.bind(this)(event);
134167
};

Diff for: arduino-ide-extension/src/browser/theia/core/connection-status-service.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { ArduinoDaemon } from '../../../common/protocol';
1414
import { NotificationCenter } from '../../notification-center';
1515
import { nls } from '@theia/core/lib/common';
16+
import debounce = require('lodash.debounce');
1617

1718
@injectable()
1819
export class FrontendConnectionStatusService extends TheiaFrontendConnectionStatusService {
@@ -36,10 +37,11 @@ export class FrontendConnectionStatusService extends TheiaFrontendConnectionStat
3637
this.notificationCenter.onDaemonDidStop(
3738
() => (this.connectedPort = undefined)
3839
);
39-
this.wsConnectionProvider.onIncomingMessageActivity(() => {
40+
const refresh = debounce(() => {
4041
this.updateStatus(!!this.connectedPort);
4142
this.schedulePing();
42-
});
43+
}, this.options.offlineTimeout - 10);
44+
this.wsConnectionProvider.onIncomingMessageActivity(() => refresh());
4345
}
4446
}
4547

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { injectable } from '@theia/core/shared/inversify';
2+
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
3+
4+
@injectable()
5+
export class StatusBarImpl extends TheiaStatusBarImpl {
6+
override async removeElement(id: string): Promise<void> {
7+
await this.ready;
8+
if (this.entries.delete(id)) {
9+
// Unlike Theia, IDE2 updates the status bar only if the element to remove was among the entries. Otherwise, it's a NOOP.
10+
this.update();
11+
}
12+
}
13+
}

Diff for: arduino-ide-extension/src/browser/theia/core/tab-bars.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
import { TabBar } from '@theia/core/shared/@phosphor/widgets';
1+
import type { TabBar } from '@theia/core/shared/@phosphor/widgets';
22
import { Saveable } from '@theia/core/lib/browser/saveable';
3-
import { TabBarRenderer as TheiaTabBarRenderer } from '@theia/core/lib/browser/shell/tab-bars';
3+
import {
4+
TabBarRenderer as TheiaTabBarRenderer,
5+
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
6+
} from '@theia/core/lib/browser/shell/tab-bars';
7+
import debounce = require('lodash.debounce');
48

59
export class TabBarRenderer extends TheiaTabBarRenderer {
10+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
611
override createTabClass(data: TabBar.IRenderData<any>): string {
712
let className = super.createTabClass(data);
813
if (!data.title.closable && Saveable.isDirty(data.title.owner)) {
@@ -16,3 +21,16 @@ export class TabBarRenderer extends TheiaTabBarRenderer {
1621
// Context menus are empty, so they have been removed
1722
};
1823
}
24+
25+
export class ToolbarAwareTabBar extends TheiaToolbarAwareTabBar {
26+
protected override async updateBreadcrumbs(): Promise<void> {
27+
// NOOP
28+
// IDE2 does not use breadcrumbs.
29+
}
30+
31+
private readonly doUpdateToolbar = debounce(() => super.updateToolbar(), 500);
32+
protected override updateToolbar(): void {
33+
// Unlike Theia, IDE2 debounces the toolbar updates with 500ms
34+
this.doUpdateToolbar();
35+
}
36+
}

Diff for: arduino-ide-extension/src/browser/theia/editor/editor-widget-factory.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { inject, injectable } from '@theia/core/shared/inversify';
22
import URI from '@theia/core/lib/common/uri';
33
import { EditorWidget } from '@theia/editor/lib/browser';
4-
import { LabelProvider } from '@theia/core/lib/browser';
4+
import type { NavigatableWidgetOptions } from '@theia/core/lib/browser';
55
import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory';
66
import {
77
CurrentSketch,
@@ -13,16 +13,16 @@ import { nls } from '@theia/core/lib/common';
1313
@injectable()
1414
export class EditorWidgetFactory extends TheiaEditorWidgetFactory {
1515
@inject(SketchesService)
16-
protected readonly sketchesService: SketchesService;
16+
private readonly sketchesService: SketchesService;
1717

1818
@inject(SketchesServiceClientImpl)
19-
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
19+
private readonly sketchesServiceClient: SketchesServiceClientImpl;
2020

21-
@inject(LabelProvider)
22-
protected override readonly labelProvider: LabelProvider;
23-
24-
protected override async createEditor(uri: URI): Promise<EditorWidget> {
25-
const widget = await super.createEditor(uri);
21+
protected override async createEditor(
22+
uri: URI,
23+
options: NavigatableWidgetOptions
24+
): Promise<EditorWidget> {
25+
const widget = await super.createEditor(uri, options);
2626
return this.maybeUpdateCaption(widget);
2727
}
2828

Diff for: arduino-ide-extension/src/browser/theia/markers/problem-manager.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
1+
import {
2+
inject,
3+
injectable,
4+
postConstruct,
5+
} from '@theia/core/shared/inversify';
26
import { Diagnostic } from 'vscode-languageserver-types';
37
import URI from '@theia/core/lib/common/uri';
48
import { ILogger } from '@theia/core';
59
import { Marker } from '@theia/markers/lib/common/marker';
610
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
711
import { ConfigService } from '../../../common/protocol/config-service';
12+
import debounce = require('lodash.debounce');
813

914
@injectable()
1015
export class ProblemManager extends TheiaProblemManager {
@@ -37,4 +42,12 @@ export class ProblemManager extends TheiaProblemManager {
3742
}
3843
return super.setMarkers(uri, owner, data);
3944
}
45+
46+
private readonly debouncedFireOnDidChangeMakers = debounce(
47+
(uri: URI) => this.onDidChangeMarkersEmitter.fire(uri),
48+
500
49+
);
50+
protected override fireOnDidChangeMarkers(uri: URI): void {
51+
this.debouncedFireOnDidChangeMakers(uri);
52+
}
4053
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Object.assign(dialogs, {
3838
export default new ContainerModule((bind, unbind, isBound, rebind) => {
3939
bind(ElectronMenuContribution).toSelf().inSingletonScope();
4040
bind(MainMenuManager).toService(ElectronMenuContribution);
41-
rebind(TheiaElectronMenuContribution).to(ElectronMenuContribution);
41+
rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution);
4242
bind(ElectronMainMenuFactory).toSelf().inSingletonScope();
4343
rebind(TheiaElectronMainMenuFactory).toService(ElectronMainMenuFactory);
4444
bind(ElectronWindowService).toSelf().inSingletonScope();

Diff for: electron/build/template-package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
"theiaPluginsDir": "plugins",
142142
"theiaPlugins": {
143143
"vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
144-
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.4.vsix",
144+
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.5.vsix",
145145
"vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
146146
"vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
147147
"cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"theiaPluginsDir": "plugins",
7676
"theiaPlugins": {
7777
"vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
78-
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.4.vsix",
78+
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.5.vsix",
7979
"vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
8080
"vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
8181
"cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",

0 commit comments

Comments
 (0)