diff --git a/arduino-ide-extension/src/browser/arduino-file-menu.ts b/arduino-ide-extension/src/browser/arduino-file-menu.ts index 30598267..2a0d1663 100644 --- a/arduino-ide-extension/src/browser/arduino-file-menu.ts +++ b/arduino-ide-extension/src/browser/arduino-file-menu.ts @@ -4,8 +4,7 @@ import { CommonMenus } from "@theia/core/lib/browser"; import { ArduinoCommands } from "./arduino-commands"; import { SketchesService, Sketch } from "../common/protocol/sketches-service"; import { AWorkspaceService } from "./arduino-workspace-service"; -import { BoardsService, Board, AttachedSerialBoard, AttachedNetworkBoard } from "../common/protocol/boards-service"; -import { BoardFrontendService } from "./boards/board-frontend-service"; +import { BoardsService } from "../common/protocol/boards-service"; export namespace ArduinoToolbarContextMenu { export const OPEN_SKETCH_PATH: MenuPath = ['arduino-open-sketch-context-menu']; @@ -30,51 +29,16 @@ export class ArduinoToolbarMenuContribution implements MenuContribution { @inject(BoardsService) protected readonly boardsService: BoardsService; - @inject(BoardFrontendService) - protected readonly boardFrontendService: BoardFrontendService; - constructor( @inject(AWorkspaceService) protected readonly workspaceService: AWorkspaceService, @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry) { workspaceService.onWorkspaceChanged(() => { if (this.workspaceService.workspace) { this.registerSketchesInMenu(menuRegistry); - this.registerConnectedBoardsInMenu(menuRegistry); } }) } - protected async registerConnectedBoardsInMenu(registry: MenuModelRegistry) { - const boards = await this.boardFrontendService.getAttachedBoards(); - const selectedBoard = await this.boardsService.getSelectBoard(); - const selectedPort = selectedBoard ? this.getPort(selectedBoard) : ''; - boards.forEach(board => { - const port = this.getPort(board); - const command: Command = { - id: 'selectBoard' + port - } - this.commands.registerCommand(command, { - execute: () => this.commands.executeCommand(ArduinoCommands.SELECT_BOARD.id, board) - }); - registry.registerMenuAction(ArduinoToolbarContextMenu.CONNECTED_GROUP, { - commandId: command.id, - label: board.name + ' at ' + port + (selectedPort === port ? '*' : '') - }); - }); - } - - - - protected getPort(board: Board): string { - if (AttachedSerialBoard.is(board)) { - return board.port; - } - if (AttachedNetworkBoard.is(board)) { - return 'netport: ' + board.port; - } - return ''; - } - protected async registerSketchesInMenu(registry: MenuModelRegistry) { const sketches = await this.getWorkspaceSketches(); sketches.forEach(sketch => { diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 0dd0f38e..97849d20 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -3,9 +3,9 @@ import { injectable, inject, postConstruct } from 'inversify'; import URI from '@theia/core/lib/common/uri'; import { EditorWidget } from '@theia/editor/lib/browser/editor-widget'; import { MessageService } from '@theia/core/lib/common/message-service'; -import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common/command'; import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { BoardsService, Board } from '../common/protocol/boards-service'; +import { BoardsService, Board, AttachedSerialBoard } from '../common/protocol/boards-service'; import { ArduinoCommands } from './arduino-commands'; import { ConnectedBoards } from './components/connected-boards'; import { CoreService } from '../common/protocol/core-service'; @@ -15,7 +15,7 @@ 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, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands'; -import { SelectionService } from '@theia/core'; +import { SelectionService, MenuModelRegistry } from '@theia/core'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { SketchFactory } from './sketch-factory'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; @@ -28,8 +28,7 @@ import { Sketch, SketchesService } from '../common/protocol/sketches-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution' import { BoardsToolBarItem } from './boards/boards-toolbar-item'; -import { SelectBoardsDialog } from './boards/select-board-dialog'; -import { BoardFrontendService } from './boards/board-frontend-service'; +import { SelectBoardDialog } from './boards/select-board-dialog'; @injectable() export class ArduinoFrontendContribution implements TabBarToolbarContribution, CommandContribution { @@ -40,9 +39,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C @inject(BoardsService) protected readonly boardService: BoardsService; - @inject(BoardFrontendService) - protected readonly boardFrontendService: BoardFrontendService; - @inject(CoreService) protected readonly coreService: CoreService; @@ -91,15 +87,58 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C @inject(SketchesService) protected readonly sketches: SketchesService; - @inject(SelectBoardsDialog) - protected readonly selectBoardsDialog: SelectBoardsDialog; + @inject(SelectBoardDialog) + protected readonly selectBoardsDialog: SelectBoardDialog; + + @inject(MenuModelRegistry) + protected readonly menuRegistry: MenuModelRegistry; + + @inject(CommandRegistry) + protected readonly commands: CommandRegistry; protected boardsToolbarItem: BoardsToolBarItem | null; + protected attachedBoards: Board[]; + protected selectedBoard: Board; @postConstruct() protected async init(): Promise { // This is a hack. Otherwise, the backend services won't bind. await this.workspaceServiceExt.roots(); + const { boards } = await this.boardService.getAttachedBoards(); + this.attachedBoards = boards; + this.registerConnectedBoardsInMenu(this.menuRegistry); + } + + protected async registerConnectedBoardsInMenu(registry: MenuModelRegistry) { + this.attachedBoards.forEach(board => { + const port = this.getPort(board); + const command: Command = { + id: 'selectBoard' + port + } + this.commands.registerCommand(command, { + execute: () => this.commands.executeCommand(ArduinoCommands.SELECT_BOARD.id, board), + isToggled: () => this.isSelectedBoard(board) + }); + registry.registerMenuAction(ArduinoToolbarContextMenu.CONNECTED_GROUP, { + commandId: command.id, + label: board.name + ' at ' + port + }); + }); + } + + protected isSelectedBoard(board: Board): boolean { + return AttachedSerialBoard.is(board) && + this.selectedBoard && + AttachedSerialBoard.is(this.selectedBoard) && + board.port === this.selectedBoard.port && + board.fqbn === this.selectedBoard.fqbn; + } + + protected getPort(board: Board): string { + if (AttachedSerialBoard.is(board)) { + return board.port; + } + return ''; } registerToolbarItems(registry: TabBarToolbarRegistry): void { @@ -133,7 +172,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C ref={ref => this.boardsToolbarItem = ref} contextMenuRenderer={this.contextMenuRenderer} boardsNotificationService={this.boardsNotificationService} - boardFrontendService={this.boardFrontendService} boardService={this.boardService} />, isVisible: widget => this.isArduinoToolbar(widget) }) @@ -241,26 +279,18 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C execute: async () => { const boardAndPort = await this.selectBoardsDialog.open(); if (boardAndPort && boardAndPort.board) { - const selectedBoard = { - fqbn: boardAndPort.board.fqbn, - name: boardAndPort.board.name, - port: boardAndPort.port - } - this.selectBoard(selectedBoard); + this.selectBoard(boardAndPort.board); } } }) } protected async selectBoard(board: Board) { - const boards = await this.boardFrontendService.getAttachedBoards(); - if (boards.length) { - board = boards.find(b => b.name === board.name && b.fqbn === board.fqbn) || board; - } await this.boardService.selectBoard(board) if (this.boardsToolbarItem) { this.boardsToolbarItem.setSelectedBoard(board); } + this.selectedBoard = board; } protected async openSketchFilesInNewWindow(uri: string) { diff --git a/arduino-ide-extension/src/browser/arduino-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-frontend-module.ts index dc7317d8..89483e52 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-frontend-module.ts @@ -48,9 +48,8 @@ import { CustomApplicationShell } from './customization/custom-application-shell import { CustomFrontendApplication } from './customization/custom-frontend-application'; import { EditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory'; import { CustomEditorWidgetFactory } from './customization/custom-editor-widget-factory'; -import { SelectBoardsDialog, SelectBoardsDialogProps } from './boards/select-board-dialog'; +import { SelectBoardDialog, SelectBoardDialogProps } from './boards/select-board-dialog'; import { SelectBoardDialogWidget } from './boards/select-board-dialog-widget'; -import { BoardFrontendService } from './boards/board-frontend-service'; export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => { // Commands and toolbar items @@ -87,7 +86,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un // Boards service bind(BoardsService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath)).inSingletonScope(); - bind(BoardFrontendService).toSelf().inSingletonScope(); // Boards list widget bind(BoardsListWidget).toSelf(); @@ -100,8 +98,8 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un // Board select dialog bind(SelectBoardDialogWidget).toSelf().inSingletonScope(); - bind(SelectBoardsDialog).toSelf().inSingletonScope(); - bind(SelectBoardsDialogProps).toConstantValue({ + bind(SelectBoardDialog).toSelf().inSingletonScope(); + bind(SelectBoardDialogProps).toConstantValue({ title: 'Select Board' }) diff --git a/arduino-ide-extension/src/browser/boards/board-frontend-service.ts b/arduino-ide-extension/src/browser/boards/board-frontend-service.ts deleted file mode 100644 index edacf39c..00000000 --- a/arduino-ide-extension/src/browser/boards/board-frontend-service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { injectable, inject } from "inversify"; -import { BoardsService, Board } from "../../common/protocol/boards-service"; - -@injectable() -export class BoardFrontendService { - @inject(BoardsService) protected readonly boardService: BoardsService; - - protected attachedBoards: Board[]; - - async getAttachedBoards(): Promise { - if (this.attachedBoards) { - return this.attachedBoards; - } - await this.refreshAttachedBoards(); - return this.attachedBoards; - } - - async refreshAttachedBoards(): Promise { - const { boards } = await this.boardService.getAttachedBoards(); - this.attachedBoards = boards; - } -} \ No newline at end of file diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx index 01c8e9eb..765daf5f 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -3,14 +3,12 @@ import { BoardsService, Board } from '../../common/protocol/boards-service'; import { ContextMenuRenderer } from '@theia/core/lib/browser'; import { ArduinoToolbarContextMenu } from '../arduino-file-menu'; import { BoardsNotificationService } from '../boards-notification-service'; -import { BoardFrontendService } from './board-frontend-service'; export namespace BoardsToolBarItem { export interface Props { readonly contextMenuRenderer: ContextMenuRenderer; readonly boardsNotificationService: BoardsNotificationService; readonly boardService: BoardsService; - readonly boardFrontendService: BoardFrontendService; } export interface State { @@ -37,16 +35,16 @@ export class BoardsToolBarItem extends React.Component attachedBoard.name === board.name) }); } this.setState({ selectedBoard: board }); @@ -56,10 +54,13 @@ export class BoardsToolBarItem extends React.Component) { const el = (event.target as HTMLElement).parentElement; if (el) { - this.props.contextMenuRenderer.render(ArduinoToolbarContextMenu.SELECT_BOARDS_PATH, { - x: el.getBoundingClientRect().left, - y: el.getBoundingClientRect().top + el.offsetHeight - }); + this.props.contextMenuRenderer.render({ + menuPath: ArduinoToolbarContextMenu.SELECT_BOARDS_PATH, + anchor: { + x: el.getBoundingClientRect().left, + y: el.getBoundingClientRect().top + el.offsetHeight + } + }) } } diff --git a/arduino-ide-extension/src/browser/boards/select-board-dialog-widget.tsx b/arduino-ide-extension/src/browser/boards/select-board-dialog-widget.tsx index eead64e6..d7657303 100644 --- a/arduino-ide-extension/src/browser/boards/select-board-dialog-widget.tsx +++ b/arduino-ide-extension/src/browser/boards/select-board-dialog-widget.tsx @@ -4,7 +4,6 @@ import { injectable, inject } from 'inversify'; import { BoardsService, Board, BoardPackage, AttachedSerialBoard } from '../../common/protocol/boards-service'; import { BoardsNotificationService } from '../boards-notification-service'; import { Emitter, Event } from '@theia/core'; -import { BoardFrontendService } from './board-frontend-service'; export interface BoardAndPortSelection { board?: Board; @@ -94,7 +93,6 @@ export class BoardAndPortSelectionList extends React.Component void; } @@ -160,11 +158,14 @@ export class BoardAndPortSelectionComponent extends React.Component PORTS - { this.portListComponent = ref }} - type='ports' - onSelect={this.doSelect} - list={this.state.ports.map(port => ({ port }))} /> + { + this.state.ports.length ? + { this.portListComponent = ref }} + type='ports' + onSelect={this.doSelect} + list={this.state.ports.map(port => ({ port }))} /> : 'loading ports...' + } @@ -209,7 +210,7 @@ export class BoardAndPortSelectionComponent extends React.Component { if (AttachedSerialBoard.is(board)) { ports.push(board.port); @@ -221,16 +222,14 @@ export class BoardAndPortSelectionComponent extends React.Component(); protected boardAndPortSelectionComponent: BoardAndPortSelectionComponent | null; + protected attachedBoards: Promise<{ boards: Board[] }>; boardAndPort: BoardAndPortSelection = {}; @@ -252,6 +251,10 @@ export class SelectBoardDialogWidget extends ReactWidget { this.boardAndPort = {}; } + setAttachedBoards(attachedBoards: Promise<{ boards: Board[] }>): void { + this.attachedBoards = attachedBoards; + } + protected fireChanged(boardAndPort: BoardAndPortSelection): void { this.onChangedEmitter.fire(boardAndPort); } @@ -260,8 +263,9 @@ export class SelectBoardDialogWidget extends ReactWidget { let content: React.ReactNode; const boardsServiceDelegate = this.boardsService; + const attachedBoards = this.attachedBoards; const boardsService: BoardsService = { - getAttachedBoards: () => boardsServiceDelegate.getAttachedBoards(), + getAttachedBoards: () => attachedBoards, selectBoard: (board: Board) => boardsServiceDelegate.selectBoard(board), getSelectBoard: () => boardsServiceDelegate.getSelectBoard(), search: (options: { query?: string }) => boardsServiceDelegate.search(options), @@ -285,7 +289,6 @@ export class SelectBoardDialogWidget extends ReactWidget { this.boardAndPortSelectionComponent = ref} - boardFrontendService={this.boardFrontendService} boardsService={boardsService} onSelect={this.onSelect} /> diff --git a/arduino-ide-extension/src/browser/boards/select-board-dialog.ts b/arduino-ide-extension/src/browser/boards/select-board-dialog.ts index 2541b4ad..d7c5c81d 100644 --- a/arduino-ide-extension/src/browser/boards/select-board-dialog.ts +++ b/arduino-ide-extension/src/browser/boards/select-board-dialog.ts @@ -3,20 +3,23 @@ import { injectable, inject } from 'inversify'; import { SelectBoardDialogWidget, BoardAndPortSelection } from './select-board-dialog-widget'; import { Message } from '@phosphor/messaging'; import { Disposable } from '@theia/core'; +import { Board, BoardsService, AttachedSerialBoard } from '../../common/protocol/boards-service'; @injectable() -export class SelectBoardsDialogProps extends DialogProps { +export class SelectBoardDialogProps extends DialogProps { } @injectable() -export class SelectBoardsDialog extends AbstractDialog { +export class SelectBoardDialog extends AbstractDialog { protected readonly dialogPanel: Panel; + protected attachedBoards: Board[]; constructor( - @inject(SelectBoardsDialogProps) protected readonly props: SelectBoardsDialogProps, - @inject(SelectBoardDialogWidget) protected readonly widget: SelectBoardDialogWidget + @inject(SelectBoardDialogProps) protected readonly props: SelectBoardDialogProps, + @inject(SelectBoardDialogWidget) protected readonly widget: SelectBoardDialogWidget, + @inject(BoardsService) protected readonly boardService: BoardsService ) { super({ title: props.title }); @@ -24,13 +27,21 @@ export class SelectBoardsDialog extends AbstractDialog { this.dialogPanel.addWidget(this.widget); this.toDispose.push(this.widget.onChanged(() => this.update())); - this.toDispose.push(this.dialogPanel); + this.attachedBoards = []; + this.init(); + this.appendCloseButton('CANCEL'); this.appendAcceptButton('OK'); } + protected init() { + const boards = this.boardService.getAttachedBoards(); + boards.then(b => this.attachedBoards = b.boards); + this.widget.setAttachedBoards(boards); + } + protected onAfterAttach(msg: Message): void { Widget.attach(this.dialogPanel, this.contentNode); @@ -68,7 +79,24 @@ export class SelectBoardsDialog extends AbstractDialog { } get value(): BoardAndPortSelection { - return this.widget.boardAndPort; + const boardAndPortSelection = this.widget.boardAndPort; + if (this.attachedBoards.length) { + boardAndPortSelection.board = this.attachedBoards.find(b => { + const isAttachedBoard = !!boardAndPortSelection.board && + b.name === boardAndPortSelection.board.name && + b.fqbn === boardAndPortSelection.board.fqbn; + if (boardAndPortSelection.port) { + return isAttachedBoard && + AttachedSerialBoard.is(b) && + b.port === boardAndPortSelection.port; + } else { + return isAttachedBoard; + } + + }) + || boardAndPortSelection.board; + } + return boardAndPortSelection; } close(): void {