Sketchbook sidebar state (#1102)

* add commands to open sketchbook widgets

add commands to show sketchbook widgets

* enable sending commands via query params

* opening sketch in new window will open sketchbook

* requested changes

* add specific method WorkspaceService to open sketch with commands

* add encoded commands contribution

* try merge show sketchbook commands

* pair session changes.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* i18n fixup.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* minimized scope of hacky code.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* clean up OPEN_NEW_WINDOW command

* add comment on workspace-service.ts

* reveal node with URI

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Alberto Iannaccone
2022-07-04 15:49:25 +02:00
committed by GitHub
parent 5da558dfd9
commit 087cab177b
7 changed files with 226 additions and 10 deletions

View File

@@ -1,6 +1,14 @@
import { Command } from '@theia/core/lib/common/command';
export namespace SketchbookCommands {
export const TOGGLE_SKETCHBOOK_WIDGET: Command = {
id: 'arduino-sketchbook-widget:toggle',
};
export const REVEAL_SKETCH_NODE: Command = {
id: 'arduino-sketchbook--reveal-sketch-node',
};
export const OPEN_NEW_WINDOW = Command.toLocalizedCommand(
{
id: 'arduino-sketchbook--open-sketch-new-window',

View File

@@ -29,6 +29,7 @@ import {
} from '../../../common/protocol/sketches-service-client-impl';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { URI } from '../../contributions/contribution';
import { WorkspaceInput } from '@theia/workspace/lib/browser';
export const SKETCHBOOK__CONTEXT = ['arduino-sketchbook--context'];
@@ -77,7 +78,7 @@ export class SketchbookWidgetContribution
area: 'left',
rank: 1,
},
toggleCommandId: 'arduino-sketchbook-widget:toggle',
toggleCommandId: SketchbookCommands.TOGGLE_SKETCHBOOK_WIDGET.id,
toggleKeybinding: 'CtrlCmd+Shift+B',
});
}
@@ -100,11 +101,12 @@ export class SketchbookWidgetContribution
override registerCommands(registry: CommandRegistry): void {
super.registerCommands(registry);
registry.registerCommand(SketchbookCommands.REVEAL_SKETCH_NODE, {
execute: (treeWidgetId: string, nodeUri: string) =>
this.revealSketchNode(treeWidgetId, nodeUri),
});
registry.registerCommand(SketchbookCommands.OPEN_NEW_WINDOW, {
execute: async (arg) => {
return this.workspaceService.open(arg.node.uri);
},
execute: (arg) => this.openNewWindow(arg.node),
isEnabled: (arg) =>
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
isVisible: (arg) =>
@@ -197,7 +199,7 @@ export class SketchbookWidgetContribution
// unregister main menu action
registry.unregisterMenuAction({
commandId: 'arduino-sketchbook-widget:toggle',
commandId: SketchbookCommands.TOGGLE_SKETCHBOOK_WIDGET.id,
});
registry.registerMenuAction(SKETCHBOOK__CONTEXT__MAIN_GROUP, {
@@ -207,6 +209,28 @@ export class SketchbookWidgetContribution
});
}
private openNewWindow(node: SketchbookTree.SketchDirNode): void {
const widget = this.tryGetWidget();
if (widget) {
const treeWidgetId = widget.activeTreeWidgetId();
if (!treeWidgetId) {
console.warn(`Could not retrieve active sketchbook tree ID.`);
return;
}
const nodeUri = node.uri.toString();
const options: WorkspaceInput = {};
Object.assign(options, {
tasks: [
{
command: SketchbookCommands.REVEAL_SKETCH_NODE.id,
args: [treeWidgetId, nodeUri],
},
],
});
return this.workspaceService.open(node.uri, options);
}
}
/**
* Reveals and selects node in the file navigator to which given widget is related.
* Does nothing if given widget undefined or doesn't have related resource.
@@ -230,4 +254,17 @@ export class SketchbookWidgetContribution
protected onCurrentWidgetChangedHandler(): void {
this.selectWidgetFileNode(this.shell.currentWidget);
}
private async revealSketchNode(
treeWidgetId: string,
nodeUIri: string
): Promise<void> {
return this.widget
.then((widget) => this.shell.activateWidget(widget.id))
.then((widget) => {
if (widget instanceof SketchbookWidget) {
return widget.revealSketchNode(treeWidgetId, nodeUIri);
}
});
}
}

View File

@@ -1,4 +1,8 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
import { IDragEvent } from '@theia/core/shared/@phosphor/dragdrop';
import { DockPanel, Widget } from '@theia/core/shared/@phosphor/widgets';
@@ -7,6 +11,8 @@ import { Disposable } from '@theia/core/lib/common/disposable';
import { BaseWidget } from '@theia/core/lib/browser/widgets/widget';
import { SketchbookTreeWidget } from './sketchbook-tree-widget';
import { nls } from '@theia/core/lib/common';
import { CloudSketchbookCompositeWidget } from '../cloud-sketchbook/cloud-sketchbook-composite-widget';
import { URI } from '../../contributions/contribution';
@injectable()
export class SketchbookWidget extends BaseWidget {
@@ -45,6 +51,57 @@ export class SketchbookWidget extends BaseWidget {
return this.localSketchbookTreeWidget;
}
activeTreeWidgetId(): string | undefined {
const selectedTreeWidgets = toArray(
this.sketchbookTreesContainer.selectedWidgets()
).map(({ id }) => id);
if (selectedTreeWidgets.length > 1) {
console.warn(
`Found multiple selected tree widgets: ${JSON.stringify(
selectedTreeWidgets
)}. Expected only one.`
);
}
return selectedTreeWidgets.shift();
}
async revealSketchNode(treeWidgetId: string, nodeUri: string): Promise<void> {
const widget = toArray(this.sketchbookTreesContainer.widgets())
.filter(({ id }) => id === treeWidgetId)
.shift();
if (!widget) {
console.warn(`Could not find tree widget with ID: ${widget}`);
return;
}
// TODO: remove this when the remote/local sketchbooks and their widgets are cleaned up.
const findTreeWidget = (
widget: Widget | undefined
): SketchbookTreeWidget | undefined => {
if (widget instanceof SketchbookTreeWidget) {
return widget;
}
if (widget instanceof CloudSketchbookCompositeWidget) {
return widget.getTreeWidget();
}
return undefined;
};
const treeWidget = findTreeWidget(
toArray(this.sketchbookTreesContainer.widgets())
.filter(({ id }) => id === treeWidgetId)
.shift()
);
if (!treeWidget) {
console.warn(`Could not find tree widget with ID: ${treeWidget}`);
return;
}
this.sketchbookTreesContainer.activateWidget(widget);
const treeNode = await treeWidget.model.revealFile(new URI(nodeUri));
if (!treeNode) {
console.warn(`Could not find tree node with URI: ${nodeUri}`);
}
}
protected override onActivateRequest(message: Message): void {
super.onActivateRequest(message);

View File

@@ -0,0 +1,42 @@
import { injectable } from '@theia/core/shared/inversify';
import { WorkspaceInput as TheiaWorkspaceInput } from '@theia/workspace/lib/browser';
import { Contribution } from '../../contributions/contribution';
export interface Task {
command: string;
/**
* This must be JSON serializable.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args?: any[];
}
@injectable()
export class StartupTask extends Contribution {
override onReady(): void {
const params = new URLSearchParams(window.location.search);
const encoded = params.get(StartupTask.QUERY_STRING);
if (!encoded) return;
const commands = JSON.parse(decodeURIComponent(encoded));
if (Array.isArray(commands)) {
commands.forEach(({ command, args }) => {
this.commandService.executeCommand(command, ...args);
});
}
}
}
export namespace StartupTask {
export const QUERY_STRING = 'startupTasks';
export interface WorkspaceInput extends TheiaWorkspaceInput {
tasks: Task[];
}
export namespace WorkspaceInput {
export function is(
input: (TheiaWorkspaceInput & Partial<WorkspaceInput>) | undefined
): input is WorkspaceInput {
return !!input && !!input.tasks;
}
}
}