Skip to content

Commit 35086ca

Browse files
authored
Merge pull request #31 from bcmi-labs/open-boards-dialog
PROEDITOR-9: Open boards dialog
2 parents 75ef8ea + 2344628 commit 35086ca

16 files changed

+825
-145
lines changed

arduino-ide-extension/src/browser/arduino-commands.ts

+8
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,12 @@ export namespace ArduinoCommands {
4040
label: "Refresh attached boards"
4141
}
4242

43+
export const SELECT_BOARD: Command = {
44+
id: "arduino-select-board"
45+
}
46+
47+
export const OPEN_BOARDS_DIALOG: Command = {
48+
id: "arduino-open-boards-dialog"
49+
}
50+
4351
}

arduino-ide-extension/src/browser/arduino-file-menu.ts

+34-23
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,31 @@ import { CommonMenus } from "@theia/core/lib/browser";
44
import { ArduinoCommands } from "./arduino-commands";
55
import { SketchesService, Sketch } from "../common/protocol/sketches-service";
66
import { AWorkspaceService } from "./arduino-workspace-service";
7+
import { BoardsService } from "../common/protocol/boards-service";
78

8-
export namespace ArduinoOpenSketchContextMenu {
9-
export const PATH: MenuPath = ['arduino-open-sketch-context-menu'];
10-
export const OPEN_GROUP: MenuPath = [...PATH, '1_open'];
11-
export const WS_SKETCHES_GROUP: MenuPath = [...PATH, '2_sketches'];
12-
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...PATH, '3_examples'];
9+
export namespace ArduinoToolbarContextMenu {
10+
export const OPEN_SKETCH_PATH: MenuPath = ['arduino-open-sketch-context-menu'];
11+
export const OPEN_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '1_open'];
12+
export const WS_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '2_sketches'];
13+
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '3_examples'];
14+
15+
export const SELECT_BOARDS_PATH: MenuPath = ['arduino-select-boards-context-menu'];
16+
export const CONNECTED_GROUP: MenuPath = [...SELECT_BOARDS_PATH, '1_connected'];
17+
export const OPEN_BOARDS_DIALOG_GROUP: MenuPath = [...SELECT_BOARDS_PATH, '2_open_boards_dialog'];
1318
}
1419

1520
@injectable()
16-
export class ArduinoFileMenuContribution implements MenuContribution {
21+
export class ArduinoToolbarMenuContribution implements MenuContribution {
1722

1823
@inject(CommandRegistry)
1924
protected readonly commands: CommandRegistry;
2025

2126
@inject(SketchesService)
2227
protected readonly sketches: SketchesService;
2328

29+
@inject(BoardsService)
30+
protected readonly boardsService: BoardsService;
31+
2432
constructor(
2533
@inject(AWorkspaceService) protected readonly workspaceService: AWorkspaceService,
2634
@inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry) {
@@ -31,21 +39,19 @@ export class ArduinoFileMenuContribution implements MenuContribution {
3139
})
3240
}
3341

34-
protected registerSketchesInMenu(registry: MenuModelRegistry) {
35-
this.getWorkspaceSketches().then(sketches => {
36-
sketches.forEach(sketch => {
37-
const command: Command = {
38-
id: 'openSketch' + sketch.name
39-
}
40-
this.commands.registerCommand(command, {
41-
execute: () => this.commands.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
42-
});
43-
44-
registry.registerMenuAction(ArduinoOpenSketchContextMenu.WS_SKETCHES_GROUP, {
45-
commandId: command.id,
46-
label: sketch.name
47-
});
48-
})
42+
protected async registerSketchesInMenu(registry: MenuModelRegistry) {
43+
const sketches = await this.getWorkspaceSketches();
44+
sketches.forEach(sketch => {
45+
const command: Command = {
46+
id: 'openSketch' + sketch.name
47+
}
48+
this.commands.registerCommand(command, {
49+
execute: () => this.commands.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
50+
});
51+
registry.registerMenuAction(ArduinoToolbarContextMenu.WS_SKETCHES_GROUP, {
52+
commandId: command.id,
53+
label: sketch.name
54+
});
4955
})
5056
}
5157

@@ -57,11 +63,16 @@ export class ArduinoFileMenuContribution implements MenuContribution {
5763
registerMenus(registry: MenuModelRegistry) {
5864
registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], {
5965
commandId: ArduinoCommands.NEW_SKETCH.id
60-
})
66+
});
6167

62-
registry.registerMenuAction(ArduinoOpenSketchContextMenu.OPEN_GROUP, {
68+
registry.registerMenuAction(ArduinoToolbarContextMenu.OPEN_GROUP, {
6369
commandId: ArduinoCommands.OPEN_FILE_NAVIGATOR.id,
6470
label: 'Open...'
6571
});
72+
73+
registry.registerMenuAction(ArduinoToolbarContextMenu.OPEN_BOARDS_DIALOG_GROUP, {
74+
commandId: ArduinoCommands.OPEN_BOARDS_DIALOG.id,
75+
label: 'Select Other Board & Port'
76+
});
6677
}
6778
}

arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+97-28
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { injectable, inject, postConstruct } from 'inversify';
33
import URI from '@theia/core/lib/common/uri';
44
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
55
import { MessageService } from '@theia/core/lib/common/message-service';
6-
import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
6+
import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common/command';
77
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
8-
import { BoardsService } from '../common/protocol/boards-service';
8+
import { BoardsService, Board, AttachedSerialBoard } from '../common/protocol/boards-service';
99
import { ArduinoCommands } from './arduino-commands';
1010
import { ConnectedBoards } from './components/connected-boards';
1111
import { CoreService } from '../common/protocol/core-service';
@@ -15,18 +15,20 @@ import { QuickPickService } from '@theia/core/lib/common/quick-pick-service';
1515
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
1616
import { BoardsNotificationService } from './boards-notification-service';
1717
import { WorkspaceRootUriAwareCommandHandler, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
18-
import { SelectionService } from '@theia/core';
18+
import { SelectionService, MenuModelRegistry } from '@theia/core';
1919
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
2020
import { SketchFactory } from './sketch-factory';
2121
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
2222
import { EditorManager } from '@theia/editor/lib/browser';
2323
import { ContextMenuRenderer, OpenerService, Widget } from '@theia/core/lib/browser';
2424
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
2525
import { FileSystem } from '@theia/filesystem/lib/common';
26-
import { ArduinoOpenSketchContextMenu } from './arduino-file-menu';
26+
import { ArduinoToolbarContextMenu } from './arduino-file-menu';
2727
import { Sketch, SketchesService } from '../common/protocol/sketches-service';
2828
import { WindowService } from '@theia/core/lib/browser/window/window-service';
2929
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'
30+
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
31+
import { SelectBoardDialog } from './boards/select-board-dialog';
3032

3133
@injectable()
3234
export class ArduinoFrontendContribution implements TabBarToolbarContribution, CommandContribution {
@@ -85,10 +87,58 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
8587
@inject(SketchesService)
8688
protected readonly sketches: SketchesService;
8789

90+
@inject(SelectBoardDialog)
91+
protected readonly selectBoardsDialog: SelectBoardDialog;
92+
93+
@inject(MenuModelRegistry)
94+
protected readonly menuRegistry: MenuModelRegistry;
95+
96+
@inject(CommandRegistry)
97+
protected readonly commands: CommandRegistry;
98+
99+
protected boardsToolbarItem: BoardsToolBarItem | null;
100+
protected attachedBoards: Board[];
101+
protected selectedBoard: Board;
102+
88103
@postConstruct()
89104
protected async init(): Promise<void> {
90105
// This is a hack. Otherwise, the backend services won't bind.
91106
await this.workspaceServiceExt.roots();
107+
const { boards } = await this.boardService.getAttachedBoards();
108+
this.attachedBoards = boards;
109+
this.registerConnectedBoardsInMenu(this.menuRegistry);
110+
}
111+
112+
protected async registerConnectedBoardsInMenu(registry: MenuModelRegistry) {
113+
this.attachedBoards.forEach(board => {
114+
const port = this.getPort(board);
115+
const command: Command = {
116+
id: 'selectBoard' + port
117+
}
118+
this.commands.registerCommand(command, {
119+
execute: () => this.commands.executeCommand(ArduinoCommands.SELECT_BOARD.id, board),
120+
isToggled: () => this.isSelectedBoard(board)
121+
});
122+
registry.registerMenuAction(ArduinoToolbarContextMenu.CONNECTED_GROUP, {
123+
commandId: command.id,
124+
label: board.name + ' at ' + port
125+
});
126+
});
127+
}
128+
129+
protected isSelectedBoard(board: Board): boolean {
130+
return AttachedSerialBoard.is(board) &&
131+
this.selectedBoard &&
132+
AttachedSerialBoard.is(this.selectedBoard) &&
133+
board.port === this.selectedBoard.port &&
134+
board.fqbn === this.selectedBoard.fqbn;
135+
}
136+
137+
protected getPort(board: Board): string {
138+
if (AttachedSerialBoard.is(board)) {
139+
return board.port;
140+
}
141+
return '';
92142
}
93143

94144
registerToolbarItems(registry: TabBarToolbarRegistry): void {
@@ -118,15 +168,11 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
118168
});
119169
registry.registerItem({
120170
id: ConnectedBoards.TOOLBAR_ID,
121-
// render: () => <BoardsToolBarItem
122-
// onNoBoardsInstalled={this.onNoBoardsInstalled.bind(this)}
123-
// onUnknownBoard={this.onUnknownBoard.bind(this)} />,
124-
render: () => <ConnectedBoards
125-
boardsService={this.boardService}
171+
render: () => <BoardsToolBarItem
172+
ref={ref => this.boardsToolbarItem = ref}
173+
contextMenuRenderer={this.contextMenuRenderer}
126174
boardsNotificationService={this.boardsNotificationService}
127-
quickPickService={this.quickPickService}
128-
onNoBoardsInstalled={this.onNoBoardsInstalled.bind(this)}
129-
onUnknownBoard={this.onUnknownBoard.bind(this)} />,
175+
boardService={this.boardService} />,
130176
isVisible: widget => this.isArduinoToolbar(widget)
131177
})
132178
}
@@ -180,7 +226,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
180226
execute: async (widget: Widget, event: React.MouseEvent<HTMLElement>) => {
181227
const el = (event.target as HTMLElement).parentElement;
182228
if (el) {
183-
this.contextMenuRenderer.render(ArduinoOpenSketchContextMenu.PATH, {
229+
this.contextMenuRenderer.render(ArduinoToolbarContextMenu.OPEN_SKETCH_PATH, {
184230
x: el.getBoundingClientRect().left,
185231
y: el.getBoundingClientRect().top + el.offsetHeight
186232
});
@@ -221,9 +267,32 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
221267
registry.registerCommand(ArduinoCommands.REFRESH_BOARDS, {
222268
isEnabled: () => true,
223269
execute: () => this.boardsNotificationService.notifyBoardsInstalled()
270+
});
271+
registry.registerCommand(ArduinoCommands.SELECT_BOARD, {
272+
isEnabled: () => true,
273+
execute: async (board: Board) => {
274+
this.selectBoard(board);
275+
}
276+
})
277+
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
278+
isEnabled: () => true,
279+
execute: async () => {
280+
const boardAndPort = await this.selectBoardsDialog.open();
281+
if (boardAndPort && boardAndPort.board) {
282+
this.selectBoard(boardAndPort.board);
283+
}
284+
}
224285
})
225286
}
226287

288+
protected async selectBoard(board: Board) {
289+
await this.boardService.selectBoard(board)
290+
if (this.boardsToolbarItem) {
291+
this.boardsToolbarItem.setSelectedBoard(board);
292+
}
293+
this.selectedBoard = board;
294+
}
295+
227296
protected async openSketchFilesInNewWindow(uri: string) {
228297
const location = new URL(window.location.href);
229298
location.searchParams.set('sketch', uri);
@@ -278,24 +347,24 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
278347
return widget;
279348
}
280349

281-
private async onNoBoardsInstalled() {
282-
const action = await this.messageService.info("You have no boards installed. Use the boards mangager to install one.", "Open Boards Manager");
283-
if (!action) {
284-
return;
285-
}
350+
// private async onNoBoardsInstalled() {
351+
// const action = await this.messageService.info("You have no boards installed. Use the boards mangager to install one.", "Open Boards Manager");
352+
// if (!action) {
353+
// return;
354+
// }
286355

287-
this.boardsListWidgetFrontendContribution.openView({ reveal: true });
288-
}
356+
// this.boardsListWidgetFrontendContribution.openView({ reveal: true });
357+
// }
289358

290-
private async onUnknownBoard() {
291-
const action = await this.messageService.warn("There's a board connected for which you need to install software." +
292-
" If this were not a PoC we would offer you the right package now.", "Open Boards Manager");
293-
if (!action) {
294-
return;
295-
}
359+
// private async onUnknownBoard() {
360+
// const action = await this.messageService.warn("There's a board connected for which you need to install software." +
361+
// " If this were not a PoC we would offer you the right package now.", "Open Boards Manager");
362+
// if (!action) {
363+
// return;
364+
// }
296365

297-
this.boardsListWidgetFrontendContribution.openView({ reveal: true });
298-
}
366+
// this.boardsListWidgetFrontendContribution.openView({ reveal: true });
367+
// }
299368

300369
private isArduinoToolbar(maybeToolbarWidget: any): boolean {
301370
if (maybeToolbarWidget instanceof ArduinoToolbar) {

arduino-ide-extension/src/browser/arduino-frontend-module.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service
2727
import { AWorkspaceService } from './arduino-workspace-service';
2828
import { ThemeService } from '@theia/core/lib/browser/theming';
2929
import { ArduinoTheme } from './arduino-theme';
30-
import { ArduinoFileMenuContribution } from './arduino-file-menu';
30+
import { ArduinoToolbarMenuContribution } from './arduino-file-menu';
3131
import { MenuContribution } from '@theia/core';
3232
import { SketchFactory } from './sketch-factory';
3333
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
@@ -48,14 +48,16 @@ import { CustomApplicationShell } from './customization/custom-application-shell
4848
import { CustomFrontendApplication } from './customization/custom-frontend-application';
4949
import { EditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory';
5050
import { CustomEditorWidgetFactory } from './customization/custom-editor-widget-factory';
51+
import { SelectBoardDialog, SelectBoardDialogProps } from './boards/select-board-dialog';
52+
import { SelectBoardDialogWidget } from './boards/select-board-dialog-widget';
5153

5254
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
5355
// Commands and toolbar items
5456
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
5557
bind(CommandContribution).toService(ArduinoFrontendContribution);
5658
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
5759
bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution);
58-
bind(MenuContribution).to(ArduinoFileMenuContribution).inSingletonScope();
60+
bind(MenuContribution).to(ArduinoToolbarMenuContribution).inSingletonScope();
5961

6062
bind(ArduinoToolbarContribution).toSelf().inSingletonScope();
6163
bind(FrontendApplicationContribution).toService(ArduinoToolbarContribution);
@@ -94,6 +96,13 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
9496
}));
9597
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
9698

99+
// Board select dialog
100+
bind(SelectBoardDialogWidget).toSelf().inSingletonScope();
101+
bind(SelectBoardDialog).toSelf().inSingletonScope();
102+
bind(SelectBoardDialogProps).toConstantValue({
103+
title: 'Select Board'
104+
})
105+
97106
// Core service
98107
bind(CoreService)
99108
.toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, CoreServicePath))

0 commit comments

Comments
 (0)