diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts index 5dc3896a..b6c8295e 100644 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ b/arduino-ide-extension/src/browser/arduino-commands.ts @@ -12,6 +12,19 @@ export namespace ArduinoCommands { label: 'Upload Sketch' } + export const SHOW_OPEN_CONTEXT_MENU: Command = { + id: 'arduino-show-open-context-menu', + label: 'Open Sketch' + } + + export const OPEN_FILE_NAVIGATOR: Command = { + id: 'arduino-open-file-navigator' + } + + export const OPEN_SKETCH: Command = { + id: 'arduino-open-file' + } + export const NEW_SKETCH: Command = { id: "arduino-new-sketch", label: 'New Sketch', diff --git a/arduino-ide-extension/src/browser/arduino-file-menu.ts b/arduino-ide-extension/src/browser/arduino-file-menu.ts index 703d6547..d087c030 100644 --- a/arduino-ide-extension/src/browser/arduino-file-menu.ts +++ b/arduino-ide-extension/src/browser/arduino-file-menu.ts @@ -1,15 +1,67 @@ -import { injectable } from "inversify"; -import { MenuContribution, MenuModelRegistry } from "@theia/core"; +import { injectable, inject } from "inversify"; +import { MenuContribution, MenuModelRegistry, MenuPath, CommandRegistry, Command } from "@theia/core"; import { CommonMenus } from "@theia/core/lib/browser"; import { ArduinoCommands } from "./arduino-commands"; +import URI from "@theia/core/lib/common/uri"; + +export namespace ArduinoOpenSketchContextMenu { + export const PATH: MenuPath = ['arduino-open-sketch-context-menu']; + export const OPEN_GROUP: MenuPath = [...PATH, '1_open']; + export const WS_SKETCHES_GROUP: MenuPath = [...PATH, '2_sketches']; + export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...PATH, '3_examples']; +} + +export interface SketchMenuEntry { + name: string, + uri: URI +} @injectable() export class ArduinoFileMenuContribution implements MenuContribution { + @inject(CommandRegistry) + protected readonly commands: CommandRegistry; + + + protected async getWorkspaceSketches(): Promise { + return [ + { + name: 'foo', + uri: new URI('this/is/a/test/uri/foo') + }, + { + name: 'bar', + uri: new URI('this/is/a/test/uri/bar') + } + ] + } + registerMenus(registry: MenuModelRegistry) { registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], { commandId: ArduinoCommands.NEW_SKETCH.id }) - } + registry.registerMenuAction(ArduinoOpenSketchContextMenu.OPEN_GROUP, { + commandId: ArduinoCommands.OPEN_FILE_NAVIGATOR.id, + label: 'Open...' + }); + + this.getWorkspaceSketches().then(sketches => { + sketches.forEach(sketch => { + + const command: Command = { + id: 'openSketch' + sketch.name + } + this.commands.registerCommand(command, { + execute: () => this.commands.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch) + }); + + registry.registerMenuAction(ArduinoOpenSketchContextMenu.WS_SKETCHES_GROUP, { + commandId: command.id, + label: sketch.name + }); + }) + }) + + } } \ No newline at end of file diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 9389324e..eeaef960 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -15,12 +15,16 @@ import { ToolOutputServiceClient } from '../common/protocol/tool-output-service' import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution'; import { BoardsNotificationService } from './boards-notification-service'; -import { WorkspaceRootUriAwareCommandHandler } from '@theia/workspace/lib/browser/workspace-commands'; +import { WorkspaceRootUriAwareCommandHandler, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands'; import { SelectionService } from '@theia/core'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { SketchFactory } from './sketch-factory'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; import { EditorManager } from '@theia/editor/lib/browser'; +import { open, ContextMenuRenderer, OpenerService, Widget } from '@theia/core/lib/browser'; +import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog'; +import { FileSystem } from '@theia/filesystem/lib/common'; +import { ArduinoOpenSketchContextMenu, SketchMenuEntry } from './arduino-file-menu'; @injectable() export class ArduinoFrontendContribution extends DefaultFrontendApplicationContribution implements TabBarToolbarContribution, CommandContribution { @@ -61,6 +65,18 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr @inject(EditorManager) protected readonly editorManager: EditorManager; + @inject(ContextMenuRenderer) + protected readonly contextMenuRenderer: ContextMenuRenderer; + + @inject(FileDialogService) + protected readonly fileDialogService: FileDialogService; + + @inject(FileSystem) + protected readonly fileSystem: FileSystem; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + @postConstruct() protected async init(): Promise { // This is a hack. Otherwise, the backend services won't bind. @@ -82,6 +98,13 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr group: 'arduino', text: '$(arrow-right)' }); + registry.registerItem({ + id: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id, + command: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id, + tooltip: 'Open', + group: 'arduino', + text: '$(arrow-up)' + }); registry.registerItem({ id: ConnectedBoards.TOOLBAR_ID, render: () => this.isArduinoToolbar(widget), + isEnabled: widget => this.isArduinoToolbar(widget), + execute: async (widget: Widget, event: React.MouseEvent) => { + const el = (event.target as HTMLElement).parentElement; + if(el) { + this.contextMenuRenderer.render(ArduinoOpenSketchContextMenu.PATH, { + x: el.getBoundingClientRect().left, + y: el.getBoundingClientRect().top + el.offsetHeight + }); + } + } + }); + registry.registerCommand(ArduinoCommands.OPEN_FILE_NAVIGATOR, { + isEnabled: () => true, + execute: () => this.doOpenFile() + }) + registry.registerCommand(ArduinoCommands.OPEN_SKETCH, { + isEnabled: () => true, + execute: (sketch: SketchMenuEntry) => { + console.log("OPEN SOME SKETCH", sketch); + } + }) registry.registerCommand(ArduinoCommands.NEW_SKETCH, new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, { execute: async uri => { try { @@ -157,6 +203,32 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr }) } + /** + * Opens a file after prompting the `Open File` dialog. Resolves to `undefined`, if + * - the workspace root is not set, + * - the file to open does not exist, or + * - it was not a file, but a directory. + * + * Otherwise, resolves to the URI of the file. + */ + protected async doOpenFile(): Promise { + const props: OpenFileDialogProps = { + title: WorkspaceCommands.OPEN_FILE.dialogLabel, + canSelectFolders: false, + canSelectFiles: true + }; + const [rootStat] = await this.workspaceService.roots; + const destinationFileUri = await this.fileDialogService.showOpenDialog(props, rootStat); + if (destinationFileUri) { + const destinationFile = await this.fileSystem.getFileStat(destinationFileUri.toString()); + if (destinationFile && !destinationFile.isDirectory) { + await open(this.openerService, destinationFileUri); + return destinationFileUri; + } + } + return undefined; + } + protected getCurrentWidget(): EditorWidget | undefined { let widget = this.editorManager.currentEditor; if (!widget) { diff --git a/arduino-ide-extension/src/browser/style/main.css b/arduino-ide-extension/src/browser/style/main.css index 4675c552..9d42c156 100644 --- a/arduino-ide-extension/src/browser/style/main.css +++ b/arduino-ide-extension/src/browser/style/main.css @@ -13,15 +13,22 @@ #arduino-verify.arduino-tool-icon { background: url(../icons/buttons.svg); background-size: 800%; - background-position-x: 141px; background-position-y: 21px; + background-position-x: 141px; } #arduino-upload.arduino-tool-icon { background: url(../icons/buttons.svg); background-size: 800%; - background-position-x: 117px; background-position-y: 21px; + background-position-x: 117px; +} + +#arduino-show-open-context-menu.arduino-tool-icon { + background: url(../icons/buttons.svg); + background-size: 800%; + background-position-y: 21px; + background-position-x: 69px; } #arduino-verify.arduino-tool-icon:hover { diff --git a/arduino-ide-extension/src/browser/toolbar/arduino-toolbar.tsx b/arduino-ide-extension/src/browser/toolbar/arduino-toolbar.tsx index ac2ac327..d0c2301e 100644 --- a/arduino-ide-extension/src/browser/toolbar/arduino-toolbar.tsx +++ b/arduino-ide-extension/src/browser/toolbar/arduino-toolbar.tsx @@ -26,14 +26,13 @@ export class ArduinoToolbarComponent extends React.Component
+ className={cls} >
this.setState({ tootip: item.tooltip || '' })} onMouseOut={() => this.setState({ tootip: '' })} @@ -84,4 +83,11 @@ export class ArduinoToolbar extends TabBarToolbar { /> } + protected executeCommand = (e: React.MouseEvent) => { + const item = this.items.get(e.currentTarget.id); + if (TabBarToolbarItem.is(item)) { + this.commands.executeCommand(item.command, this, e); + } + } + }