feat: can dock the monitor widget to the "right"

Added a new preference (`arduino.monitor.dockPanel`) to specify the
location of the application shell where the _Serial Monitor_ widget
resides. The possible values are `"bottom"` and `"right"`. The default\
value is the `"bottom"`.

The dock panel is per application and not per workspace or window.
However, advanced users can create the `./.vscode/settings.json` and
configure per sketch preference.

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
This commit is contained in:
dankeboy36 2023-06-17 13:03:59 +02:00 committed by Akos Kitta
parent 94d2962985
commit f6a43254f5
6 changed files with 316 additions and 240 deletions

View File

@ -1,12 +1,15 @@
import { interfaces } from '@theia/core/shared/inversify';
import { import {
createPreferenceProxy,
PreferenceProxy,
PreferenceService,
PreferenceContribution, PreferenceContribution,
PreferenceProxy,
PreferenceSchema, PreferenceSchema,
PreferenceService,
createPreferenceProxy,
} from '@theia/core/lib/browser/preferences'; } from '@theia/core/lib/browser/preferences';
import { nls } from '@theia/core/lib/common'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { nls } from '@theia/core/lib/common/nls';
import { PreferenceSchemaProperty } from '@theia/core/lib/common/preferences/preference-schema';
import { interfaces } from '@theia/core/shared/inversify';
import { serialMonitorWidgetLabel } from '../common/nls';
import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol'; import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol';
export enum UpdateChannel { export enum UpdateChannel {
@ -31,7 +34,7 @@ export const ErrorRevealStrategyLiterals = [
*/ */
'centerIfOutsideViewport', 'centerIfOutsideViewport',
] as const; ] as const;
export type ErrorRevealStrategy = typeof ErrorRevealStrategyLiterals[number]; export type ErrorRevealStrategy = (typeof ErrorRevealStrategyLiterals)[number];
export namespace ErrorRevealStrategy { export namespace ErrorRevealStrategy {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
export function is(arg: any): arg is ErrorRevealStrategy { export function is(arg: any): arg is ErrorRevealStrategy {
@ -40,9 +43,24 @@ export namespace ErrorRevealStrategy {
export const Default: ErrorRevealStrategy = 'centerIfOutsideViewport'; export const Default: ErrorRevealStrategy = 'centerIfOutsideViewport';
} }
export const ArduinoConfigSchema: PreferenceSchema = { export type MonitorWidgetDockPanel = Extract<
type: 'object', ApplicationShell.Area,
properties: { 'bottom' | 'right'
>;
export const defaultMonitorWidgetDockPanel: MonitorWidgetDockPanel = 'bottom';
export function isMonitorWidgetDockPanel(
arg: unknown
): arg is MonitorWidgetDockPanel {
return arg === 'bottom' || arg === 'right';
}
type StrictPreferenceSchemaProperties<T extends object> = {
[p in keyof T]: PreferenceSchemaProperty;
};
type ArduinoPreferenceSchemaProperties =
StrictPreferenceSchemaProperties<ArduinoConfiguration> & { 'arduino.window.zoomLevel': PreferenceSchemaProperty };
const properties: ArduinoPreferenceSchemaProperties = {
'arduino.language.log': { 'arduino.language.log': {
type: 'boolean', type: 'boolean',
description: nls.localize( description: nls.localize(
@ -258,8 +276,22 @@ export const ArduinoConfigSchema: PreferenceSchema = {
), ),
default: undefined, default: undefined,
}, },
'arduino.monitor.dockPanel': {
type: 'string',
enum: ['bottom', 'right'],
markdownDescription: nls.localize(
'arduino/preferences/monitor/dockPanel',
'The area of the application shell where the _{0}_ widget will reside. It is either "bottom" or "right". It defaults to "{1}".',
serialMonitorWidgetLabel,
defaultMonitorWidgetDockPanel
),
default: defaultMonitorWidgetDockPanel,
}, },
}; };
export const ArduinoConfigSchema: PreferenceSchema = {
type: 'object',
properties,
};
export interface ArduinoConfiguration { export interface ArduinoConfiguration {
'arduino.language.log': boolean; 'arduino.language.log': boolean;
@ -288,6 +320,7 @@ export interface ArduinoConfiguration {
'arduino.cli.daemon.debug': boolean; 'arduino.cli.daemon.debug': boolean;
'arduino.sketch.inoBlueprint': string; 'arduino.sketch.inoBlueprint': string;
'arduino.checkForUpdates': boolean; 'arduino.checkForUpdates': boolean;
'arduino.monitor.dockPanel': MonitorWidgetDockPanel;
} }
export const ArduinoPreferences = Symbol('ArduinoPreferences'); export const ArduinoPreferences = Symbol('ArduinoPreferences');

View File

@ -1,6 +1,10 @@
import * as React from '@theia/core/shared/react'; import * as React from '@theia/core/shared/react';
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
import { AbstractViewContribution, codicon } from '@theia/core/lib/browser'; import {
AbstractViewContribution,
ApplicationShell,
codicon
} from '@theia/core/lib/browser';
import { MonitorWidget } from './monitor-widget'; import { MonitorWidget } from './monitor-widget';
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core'; import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core';
import { import {
@ -13,6 +17,12 @@ import { nls } from '@theia/core/lib/common';
import { Event } from '@theia/core/lib/common/event'; import { Event } from '@theia/core/lib/common/event';
import { MonitorModel } from '../../monitor-model'; import { MonitorModel } from '../../monitor-model';
import { MonitorManagerProxyClient } from '../../../common/protocol'; import { MonitorManagerProxyClient } from '../../../common/protocol';
import {
ArduinoPreferences,
defaultMonitorWidgetDockPanel,
isMonitorWidgetDockPanel
} from '../../arduino-preferences';
import { serialMonitorWidgetLabel } from '../../../common/nls';
export namespace SerialMonitor { export namespace SerialMonitor {
export namespace Commands { export namespace Commands {
@ -51,30 +61,58 @@ export class MonitorViewContribution
MonitorWidget.ID + ':toggle-toolbar'; MonitorWidget.ID + ':toggle-toolbar';
static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset'; static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset';
constructor(
@inject(MonitorModel) @inject(MonitorModel)
protected readonly model: MonitorModel, private readonly model: MonitorModel;
@inject(MonitorManagerProxyClient) @inject(MonitorManagerProxyClient)
protected readonly monitorManagerProxy: MonitorManagerProxyClient private readonly monitorManagerProxy: MonitorManagerProxyClient;
) { @inject(ArduinoPreferences)
private readonly arduinoPreferences: ArduinoPreferences;
private _panel: ApplicationShell.Area;
constructor() {
super({ super({
widgetId: MonitorWidget.ID, widgetId: MonitorWidget.ID,
widgetName: MonitorWidget.LABEL, widgetName: serialMonitorWidgetLabel,
defaultWidgetOptions: { defaultWidgetOptions: {
area: 'bottom', area: defaultMonitorWidgetDockPanel,
}, },
toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR, toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR,
toggleKeybinding: 'CtrlCmd+Shift+M', toggleKeybinding: 'CtrlCmd+Shift+M',
}); });
this._panel = defaultMonitorWidgetDockPanel;
}
@postConstruct()
protected init(): void {
this._panel = this.arduinoPreferences['arduino.monitor.dockPanel'] ?? defaultMonitorWidgetDockPanel;
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset()); this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.preferenceName === 'arduino.monitor.dockPanel' && isMonitorWidgetDockPanel(event.newValue) && event.newValue !== this._panel) {
this._panel = event.newValue;
const widget = this.tryGetWidget();
// reopen at the new position if opened
if (widget) {
widget.close();
this.openView({ activate: true, reveal: true });
}
}
})
}
override get defaultViewOptions(): ApplicationShell.WidgetOptions {
const viewOptions = super.defaultViewOptions;
return {
...viewOptions,
area: this._panel
};
} }
override registerMenus(menus: MenuModelRegistry): void { override registerMenus(menus: MenuModelRegistry): void {
if (this.toggleCommand) { if (this.toggleCommand) {
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
commandId: this.toggleCommand.id, commandId: this.toggleCommand.id,
label: MonitorWidget.LABEL, label: serialMonitorWidgetLabel,
order: '5', order: '5',
}); });
} }

View File

@ -27,13 +27,10 @@ import {
} from '../../../common/protocol'; } from '../../../common/protocol';
import { MonitorModel } from '../../monitor-model'; import { MonitorModel } from '../../monitor-model';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { serialMonitorWidgetLabel } from '../../../common/nls';
@injectable() @injectable()
export class MonitorWidget extends ReactWidget { export class MonitorWidget extends ReactWidget {
static readonly LABEL = nls.localize(
'arduino/common/serialMonitor',
'Serial Monitor'
);
static readonly ID = 'serial-monitor'; static readonly ID = 'serial-monitor';
protected settings: MonitorSettings = {}; protected settings: MonitorSettings = {};
@ -65,7 +62,7 @@ export class MonitorWidget extends ReactWidget {
constructor() { constructor() {
super(); super();
this.id = MonitorWidget.ID; this.id = MonitorWidget.ID;
this.title.label = MonitorWidget.LABEL; this.title.label = serialMonitorWidgetLabel;
this.title.iconClass = 'monitor-tab-icon'; this.title.iconClass = 'monitor-tab-icon';
this.title.closable = true; this.title.closable = true;
this.scrollOptions = undefined; this.scrollOptions = undefined;

View File

@ -20,7 +20,7 @@
.serial-monitor .head { .serial-monitor .head {
display: flex; display: flex;
padding: 0px 5px 5px 0px; padding: 0px 5px 5px 5px;
height: 27px; height: 27px;
background-color: var(--theia-activityBar-background); background-color: var(--theia-activityBar-background);
} }

View File

@ -19,3 +19,8 @@ export const InstallManually = nls.localize(
'arduino/common/installManually', 'arduino/common/installManually',
'Install Manually' 'Install Manually'
); );
export const serialMonitorWidgetLabel = nls.localize(
'arduino/common/serialMonitor',
'Serial Monitor'
);

View File

@ -381,6 +381,9 @@
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.", "language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration", "manualProxy": "Manual proxy configuration",
"monitor": {
"dockPanel": "The area of the application shell where the _{0}_ widget will reside. It is either \"bottom\" or \"right\". It defaults to \"{1}\"."
},
"network": "Network", "network": "Network",
"newSketchbookLocation": "Select new sketchbook location", "newSketchbookLocation": "Select new sketchbook location",
"noCliConfig": "Could not load the CLI configuration", "noCliConfig": "Could not load the CLI configuration",