Skip to content

Sketchbook sidebar state #1102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 4, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ import { CoreErrorHandler } from './contributions/core-error-handler';
import { CompilerErrors } from './contributions/compiler-errors';
import { WidgetManager } from './theia/core/widget-manager';
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
import { EncodedCommandsContribution } from './widgets/sketchbook/encoded-commands-contribution';

MonacoThemingService.register({
id: 'arduino-theme',
Expand Down Expand Up @@ -698,6 +699,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, PlotterFrontendContribution);
Contribution.configure(bind, Format);
Contribution.configure(bind, CompilerErrors);
Contribution.configure(bind, EncodedCommandsContribution);

// Disabled the quick-pick customization from Theia when multiple formatters are available.
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
Expand Down Expand Up @@ -832,6 +834,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {

bind(CloudSketchbookWidget).toSelf();
rebind(SketchbookWidget).toService(CloudSketchbookWidget);
bind(CommandContribution).toService(CloudSketchbookWidget);
bind(CloudSketchbookTreeWidget).toDynamicValue(({ container }) =>
createCloudSketchbookTreeWidget(container)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export class SketchControl extends SketchContribution {
});
}

protected isCloudSketch(uri: string): boolean {
isCloudSketch(uri: string): boolean {
try {
const cloudCacheLocation = this.localCacheFsProvider.from(new URI(uri));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@ import { FrontendApplication } from '@theia/core/lib/browser/frontend-applicatio
import { FocusTracker, Widget } from '@theia/core/lib/browser';
import { DEFAULT_WINDOW_HASH } from '@theia/core/lib/common/window';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import {
WorkspaceInput,
WorkspaceService as TheiaWorkspaceService,
} from '@theia/workspace/lib/browser/workspace-service';
import { ConfigService } from '../../../common/protocol/config-service';
import {
SketchesService,
Sketch,
} from '../../../common/protocol/sketches-service';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { BoardsConfig } from '../../boards/boards-config';
import { Command } from '@theia/core';

interface WorkspaceOptions extends WorkspaceInput {
commands: Command[];
}

@injectable()
export class WorkspaceService extends TheiaWorkspaceService {
Expand Down Expand Up @@ -82,13 +90,68 @@ export class WorkspaceService extends TheiaWorkspaceService {
}
}

protected override openNewWindow(workspacePath: string): void {
/*
This method mostly duplicates super.doOpen and super.openWindow because they didn't let pass any custom
option to openNewWindow
*/
async openWithCommands(uri: URI, options?: WorkspaceOptions): Promise<void> {
const stat = await this.toFileStat(uri);
if (stat) {
if (!stat.isDirectory && !this.isWorkspaceFile(stat)) {
const message = `Not a valid workspace: ${uri.path.toString()}`;
this.messageService.error(message);
throw new Error(message);
}
// The same window has to be preserved too (instead of opening a new one), if the workspace root is not yet available and we are setting it for the first time.
// Option passed as parameter has the highest priority (for api developers), then the preference, then the default.
await this.roots;
const { preserveWindow } = {
preserveWindow:
this.preferences['workspace.preserveWindow'] || !this.opened,
...options,
};
await this.server.setMostRecentlyUsedWorkspace(uri.toString());
if (preserveWindow) {
this._workspace = stat;
}

const workspacePath = stat.resource.path.toString();

if (this.shouldPreserveWindow(options)) {
this.reloadWindow();
} else {
try {
this.openNewWindow(workspacePath, options);
return;
} catch (error) {
// Fall back to reloading the current window in case the browser has blocked the new window
this._workspace = stat;
this.logger.error(error.toString()).then(() => this.reloadWindow());
}
}
}
throw new Error(
'Invalid workspace root URI. Expected an existing directory or workspace file.'
);
}

protected override openNewWindow(
workspacePath: string,
options?: WorkspaceOptions
): void {
const { boardsConfig } = this.boardsServiceProvider;
const url = BoardsConfig.Config.setConfig(
boardsConfig,
new URL(window.location.href)
); // Set the current boards config for the new browser window.
url.hash = workspacePath;
if (options?.commands) {
url.searchParams.set(
'commands',
encodeURIComponent(JSON.stringify(options.commands))
);
}

this.windowService.openNewWindow(url.toString());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import {
} from '@theia/core/lib/browser/preferences/preference-service';
import { ArduinoMenus, PlaceholderMenuNode } from '../../menu/arduino-menus';
import { SketchbookCommands } from '../sketchbook/sketchbook-commands';
import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
import {
CurrentSketch,
SketchesServiceClientImpl,
} from '../../../common/protocol/sketches-service-client-impl';
import { Contribution } from '../../contributions/contribution';
import { ArduinoPreferences } from '../../arduino-preferences';
import { MainMenuManager } from '../../../common/main-menu-manager';
Expand Down Expand Up @@ -61,6 +64,14 @@ export namespace CloudSketchbookCommands {
}
}

export const SHOW_CLOUD_SKETCHBOOK_WIDGET = Command.toLocalizedCommand(
{
id: 'arduino-cloud-sketchbook--show-cloud-sketchbook-widget',
label: 'Show Cloud Sketchbook Widget',
},
'arduino/sketch/showCloudSketchbookWidget'
);

export const TOGGLE_CLOUD_SKETCHBOOK = Command.toLocalizedCommand(
{
id: 'arduino-cloud-sketchbook--disable',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { CloudSketchbookCompositeWidget } from './cloud-sketchbook-composite-widget';
import { SketchbookWidget } from '../sketchbook/sketchbook-widget';
import { ArduinoPreferences } from '../../arduino-preferences';
import { CommandContribution, CommandRegistry } from '@theia/core';
import { ApplicationShell } from '@theia/core/lib/browser';
import { CloudSketchbookCommands } from './cloud-sketchbook-contributions';
import { EditorManager } from '@theia/editor/lib/browser';
import { SketchbookWidgetContribution } from '../sketchbook/sketchbook-widget-contribution';

@injectable()
export class CloudSketchbookWidget extends SketchbookWidget {
export class CloudSketchbookWidget
extends SketchbookWidget
implements CommandContribution
{
@inject(CloudSketchbookCompositeWidget)
protected readonly widget: CloudSketchbookCompositeWidget;
private readonly cloudSketchbookCompositeWidget: CloudSketchbookCompositeWidget;

@inject(ArduinoPreferences)
protected readonly arduinoPreferences: ArduinoPreferences;
private readonly arduinoPreferences: ArduinoPreferences;

@inject(ApplicationShell)
private readonly shell: ApplicationShell;

@inject(SketchbookWidgetContribution)
private readonly sketchbookWidgetContribution: SketchbookWidgetContribution;

@inject(EditorManager)
private readonly editorManager: EditorManager;

@postConstruct()
protected override init(): void {
Expand All @@ -27,7 +48,9 @@ export class CloudSketchbookWidget extends SketchbookWidget {

checkCloudEnabled() {
if (this.arduinoPreferences['arduino.cloud.enabled']) {
this.sketchbookTreesContainer.activateWidget(this.widget);
this.sketchbookTreesContainer.activateWidget(
this.cloudSketchbookCompositeWidget
);
} else {
this.sketchbookTreesContainer.activateWidget(
this.localSketchbookTreeWidget
Expand All @@ -45,7 +68,9 @@ export class CloudSketchbookWidget extends SketchbookWidget {
}

protected override onAfterAttach(msg: any): void {
this.sketchbookTreesContainer.addWidget(this.widget);
this.sketchbookTreesContainer.addWidget(
this.cloudSketchbookCompositeWidget
);
this.setDocumentMode();
this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.preferenceName === 'arduino.cloud.enabled') {
Expand All @@ -54,4 +79,29 @@ export class CloudSketchbookWidget extends SketchbookWidget {
});
super.onAfterAttach(msg);
}

registerCommands(registry: CommandRegistry): void {
this.sketchbookTreesContainer.addWidget(
this.cloudSketchbookCompositeWidget
);
registry.registerCommand(
CloudSketchbookCommands.SHOW_CLOUD_SKETCHBOOK_WIDGET,
{
execute: () => this.showCloudSketchbookWidget(),
}
);
}

showCloudSketchbookWidget(): void {
if (this.arduinoPreferences['arduino.cloud.enabled']) {
this.shell.activateWidget(this.id).then((widget) => {
if (widget instanceof CloudSketchbookWidget) {
widget.activateTreeWidget(this.cloudSketchbookCompositeWidget.id);
}
this.sketchbookWidgetContribution.selectWidgetFileNode(
this.editorManager.currentEditor
);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Command, CommandRegistry, MaybePromise } from '@theia/core';
import { inject, injectable } from '@theia/core/shared/inversify';
import { Contribution } from '../../contributions/contribution';

@injectable()
export class EncodedCommandsContribution extends Contribution {
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;

override onReady(): MaybePromise<void> {
const params = new URLSearchParams(window.location.search);
const encoded = params.get('commands');
if (!encoded) return;

const commands = JSON.parse(decodeURIComponent(encoded));

if (Array.isArray(commands)) {
commands.forEach((c: Command) => {
this.commandRegistry.executeCommand(c.id);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { Command } from '@theia/core/lib/common/command';

export namespace SketchbookCommands {
export const TOGGLE_SKETCHBOOK_WIDGET: Command = {
id: 'arduino-sketchbook-widget:toggle',
};

export const SHOW_SKETCHBOOK_WIDGET = Command.toLocalizedCommand(
{
id: 'arduino-sketchbook--show-sketchbook-widget',
label: 'Show Sketchbook Widget',
},
'arduino/sketch/showSketchbookWidget'
);

export const OPEN_NEW_WINDOW = Command.toLocalizedCommand(
{
id: 'arduino-sketchbook--open-sketch-new-window',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import {
} from '../../../common/protocol/sketches-service-client-impl';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { URI } from '../../contributions/contribution';
import { EditorManager } from '@theia/editor/lib/browser';
import { SketchControl } from '../../contributions/sketch-control';
import { CloudSketchbookCommands } from '../cloud-sketchbook/cloud-sketchbook-contributions';

export const SKETCHBOOK__CONTEXT = ['arduino-sketchbook--context'];

Expand Down Expand Up @@ -67,6 +70,12 @@ export class SketchbookWidgetContribution
@inject(FileService)
protected readonly fileService: FileService;

@inject(EditorManager)
protected readonly editorManager: EditorManager;

@inject(SketchControl)
protected readonly sketchControl: SketchControl;

protected readonly toDisposeBeforeNewContextMenu = new DisposableCollection();

constructor() {
Expand All @@ -77,7 +86,7 @@ export class SketchbookWidgetContribution
area: 'left',
rank: 1,
},
toggleCommandId: 'arduino-sketchbook-widget:toggle',
toggleCommandId: SketchbookCommands.TOGGLE_SKETCHBOOK_WIDGET.id,
toggleKeybinding: 'CtrlCmd+Shift+B',
});
}
Expand All @@ -100,11 +109,11 @@ export class SketchbookWidgetContribution

override registerCommands(registry: CommandRegistry): void {
super.registerCommands(registry);

registry.registerCommand(SketchbookCommands.SHOW_SKETCHBOOK_WIDGET, {
execute: () => this.showLocalSketchbookWidget(),
});
registry.registerCommand(SketchbookCommands.OPEN_NEW_WINDOW, {
execute: async (arg) => {
return this.workspaceService.open(arg.node.uri);
},
execute: (arg) => this.openSketchInNewWindow(arg),
isEnabled: (arg) =>
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
isVisible: (arg) =>
Expand Down Expand Up @@ -192,12 +201,21 @@ export class SketchbookWidgetContribution
});
}

openSketchInNewWindow(arg: any): any {
const openSketchbookCommand = this.sketchControl.isCloudSketch(arg.node.uri)
? CloudSketchbookCommands.SHOW_CLOUD_SKETCHBOOK_WIDGET
: SketchbookCommands.SHOW_SKETCHBOOK_WIDGET;
return this.workspaceService.openWithCommands(arg.node.uri, {
commands: [openSketchbookCommand],
});
}

override registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);

// unregister main menu action
registry.unregisterMenuAction({
commandId: 'arduino-sketchbook-widget:toggle',
commandId: SketchbookCommands.TOGGLE_SKETCHBOOK_WIDGET.id,
});

registry.registerMenuAction(SKETCHBOOK__CONTEXT__MAIN_GROUP, {
Expand Down Expand Up @@ -230,4 +248,15 @@ export class SketchbookWidgetContribution
protected onCurrentWidgetChangedHandler(): void {
this.selectWidgetFileNode(this.shell.currentWidget);
}

protected async showLocalSketchbookWidget(): Promise<void> {
this.widget
.then((widget) => this.shell.activateWidget(widget.id))
.then((widget) => {
if (widget instanceof SketchbookWidget) {
widget.activateTreeWidget(widget.getTreeWidget().id);
this.selectWidgetFileNode(this.editorManager.currentEditor);
}
});
}
}
Loading