Toggle the selected board in dropdown

Signed-off-by: jbicker <jan.bicker@typefox.io>
This commit is contained in:
jbicker 2019-07-10 17:48:20 +02:00
parent 89fb2fddbd
commit 23446284b7
7 changed files with 115 additions and 113 deletions

View File

@ -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 => {

View File

@ -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<void> {
// 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) {

View File

@ -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'
})

View File

@ -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<Board[]> {
if (this.attachedBoards) {
return this.attachedBoards;
}
await this.refreshAttachedBoards();
return this.attachedBoards;
}
async refreshAttachedBoards(): Promise<void> {
const { boards } = await this.boardService.getAttachedBoards();
this.attachedBoards = boards;
}
}

View File

@ -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,7 +35,7 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
}
protected async setAttachedBoards() {
const boards = await this.props.boardFrontendService.getAttachedBoards();
const { boards } = await this.props.boardService.getAttachedBoards();
this.attachedBoards = boards;
if (this.attachedBoards.length) {
await this.props.boardService.selectBoard(this.attachedBoards[0]);
@ -46,7 +44,7 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
}
setSelectedBoard(board: Board) {
if (this.attachedBoards.length) {
if (this.attachedBoards && this.attachedBoards.length) {
this.setState({ selectedIsAttached: !!this.attachedBoards.find(attachedBoard => attachedBoard.name === board.name) });
}
this.setState({ selectedBoard: board });
@ -56,10 +54,13 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
protected showSelectBoardsMenu(event: React.MouseEvent<HTMLElement>) {
const el = (event.target as HTMLElement).parentElement;
if (el) {
this.props.contextMenuRenderer.render(ArduinoToolbarContextMenu.SELECT_BOARDS_PATH, {
this.props.contextMenuRenderer.render({
menuPath: ArduinoToolbarContextMenu.SELECT_BOARDS_PATH,
anchor: {
x: el.getBoundingClientRect().left,
y: el.getBoundingClientRect().top + el.offsetHeight
});
}
})
}
}

View File

@ -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<BoardAndPortSelec
export namespace BoardAndPortSelectionComponent {
export interface Props {
boardsService: BoardsService;
boardFrontendService: BoardFrontendService;
onSelect: (selection: BoardAndPortSelection) => void;
}
@ -160,11 +158,14 @@ export class BoardAndPortSelectionComponent extends React.Component<BoardAndPort
<div className='title'>
PORTS
</div>
{
this.state.ports.length ?
<BoardAndPortSelectionList
ref={ref => { this.portListComponent = ref }}
type='ports'
onSelect={this.doSelect}
list={this.state.ports.map<BoardAndPortSelection>(port => ({ port }))} />
list={this.state.ports.map<BoardAndPortSelection>(port => ({ port }))} /> : 'loading ports...'
}
</div>
</div>
</div>
@ -209,7 +210,7 @@ export class BoardAndPortSelectionComponent extends React.Component<BoardAndPort
protected async setPorts() {
const ports: string[] = [];
const boards = await this.props.boardFrontendService.getAttachedBoards();
const { boards } = await this.props.boardsService.getAttachedBoards();
boards.forEach(board => {
if (AttachedSerialBoard.is(board)) {
ports.push(board.port);
@ -221,16 +222,14 @@ export class BoardAndPortSelectionComponent extends React.Component<BoardAndPort
@injectable()
export class SelectBoardDialogWidget extends ReactWidget {
@inject(BoardsService)
protected readonly boardsService: BoardsService;
@inject(BoardFrontendService)
protected readonly boardFrontendService: BoardFrontendService;
@inject(BoardsNotificationService)
protected readonly boardsNotificationService: BoardsNotificationService;
protected readonly onChangedEmitter = new Emitter<BoardAndPortSelection>();
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 {
</div>
<BoardAndPortSelectionComponent
ref={ref => this.boardAndPortSelectionComponent = ref}
boardFrontendService={this.boardFrontendService}
boardsService={boardsService}
onSelect={this.onSelect} />
</div>

View File

@ -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<BoardAndPortSelection> {
export class SelectBoardDialog extends AbstractDialog<BoardAndPortSelection> {
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<BoardAndPortSelection> {
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<BoardAndPortSelection> {
}
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 {