Skip to content

Commit 4f27725

Browse files
Alberto Iannacconegithub-actions[bot]
Alberto Iannaccone
andauthored
New Board Selector UI: show port protocol (#1193)
* add new icons * implement new Board Selector design * make board selector item focusable * fix i18n * 💄 * re-add debug log on board config changed * Updated themes * use new color variables * update arduino-icons.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 73835ec commit 4f27725

16 files changed

+328
-202
lines changed

Diff for: arduino-ide-extension/arduino-icons.json

+1-1
Large diffs are not rendered by default.

Diff for: arduino-ide-extension/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"atob": "^2.1.2",
6262
"auth0-js": "^9.14.0",
6363
"btoa": "^1.2.1",
64+
"classnames": "^2.3.1",
6465
"dateformat": "^3.0.3",
6566
"deep-equal": "^2.0.5",
6667
"deepmerge": "2.0.1",

Diff for: arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+68
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,74 @@ export class ArduinoFrontendContribution
618618
hc: 'editor.background',
619619
},
620620
description: 'Background color of the Output view.',
621+
},
622+
{
623+
id: 'arduino.toolbar.dropdown.border',
624+
defaults: {
625+
dark: 'dropdown.border',
626+
light: 'dropdown.border',
627+
hc: 'dropdown.border',
628+
},
629+
description: 'Border color of the Board Selector.',
630+
},
631+
632+
{
633+
id: 'arduino.toolbar.dropdown.borderActive',
634+
defaults: {
635+
dark: 'focusBorder',
636+
light: 'focusBorder',
637+
hc: 'focusBorder',
638+
},
639+
description: "Border color of the Board Selector when it's active",
640+
},
641+
642+
{
643+
id: 'arduino.toolbar.dropdown.background',
644+
defaults: {
645+
dark: 'tab.unfocusedActiveBackground',
646+
light: 'tab.unfocusedActiveBackground',
647+
hc: 'tab.unfocusedActiveBackground',
648+
},
649+
description: 'Background color of the Board Selector.',
650+
},
651+
652+
{
653+
id: 'arduino.toolbar.dropdown.label',
654+
defaults: {
655+
dark: 'foreground',
656+
light: 'foreground',
657+
hc: 'foreground',
658+
},
659+
description: 'Font color of the Board Selector.',
660+
},
661+
{
662+
id: 'arduino.toolbar.dropdown.iconSelected',
663+
defaults: {
664+
dark: 'statusBar.background',
665+
light: 'statusBar.background',
666+
hc: 'statusBar.background',
667+
},
668+
description:
669+
'Color of the selected protocol icon in the Board Selector.',
670+
},
671+
{
672+
id: 'arduino.toolbar.dropdown.option.backgroundHover',
673+
defaults: {
674+
dark: 'editor.background',
675+
light: 'editor.background',
676+
hc: 'editor.background',
677+
},
678+
description: 'Background color on hover of the Board Selector options.',
679+
},
680+
{
681+
id: 'arduino.toolbar.dropdown.option.backgroundSelected',
682+
defaults: {
683+
dark: 'editor.background',
684+
light: 'editor.background',
685+
hc: 'editor.background',
686+
},
687+
description:
688+
'Background color of the selected board in the Board Selector.',
621689
}
622690
);
623691
}

Diff for: arduino-ide-extension/src/browser/boards/boards-service-provider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
270270
}
271271

272272
protected setBoardsConfig(config: BoardsConfig.Config): void {
273-
this.logger.info('Board config changed: ', JSON.stringify(config));
273+
this.logger.debug('Board config changed: ', JSON.stringify(config));
274274
this._boardsConfig = config;
275275
this.latestBoardsConfig = this._boardsConfig;
276276
if (this.canUploadTo(this._boardsConfig)) {

Diff for: arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx

+112-46
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import * as ReactDOM from '@theia/core/shared/react-dom';
33
import { CommandRegistry } from '@theia/core/lib/common/command';
44
import { DisposableCollection } from '@theia/core/lib/common/disposable';
55
import { Port } from '../../common/protocol';
6-
import { BoardsConfig } from './boards-config';
76
import { ArduinoCommands } from '../arduino-commands';
87
import {
98
BoardsServiceProvider,
109
AvailableBoard,
1110
} from './boards-service-provider';
1211
import { nls } from '@theia/core/lib/common';
12+
import classNames from 'classnames';
1313

1414
export interface BoardsDropDownListCoords {
1515
readonly top: number;
@@ -28,10 +28,12 @@ export namespace BoardsDropDown {
2828

2929
export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
3030
protected dropdownElement: HTMLElement;
31+
private listRef: React.RefObject<HTMLDivElement>;
3132

3233
constructor(props: BoardsDropDown.Props) {
3334
super(props);
3435

36+
this.listRef = React.createRef();
3537
let list = document.getElementById('boards-dropdown-container');
3638
if (!list) {
3739
list = document.createElement('div');
@@ -41,6 +43,12 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
4143
}
4244
}
4345

46+
override componentDidUpdate(prevProps: BoardsDropDown.Props): void {
47+
if (prevProps.coords === 'hidden' && this.listRef.current) {
48+
this.listRef.current.focus();
49+
}
50+
}
51+
4452
override render(): React.ReactNode {
4553
return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
4654
}
@@ -61,21 +69,22 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
6169
position: 'absolute',
6270
...coords,
6371
}}
72+
ref={this.listRef}
73+
tabIndex={0}
6474
>
65-
{items
66-
.map(({ name, port, selected, onClick }) => ({
67-
label: nls.localize(
68-
'arduino/board/boardListItem',
69-
'{0} at {1}',
70-
name,
71-
Port.toString(port)
72-
),
73-
selected,
74-
onClick,
75-
}))
76-
.map(this.renderItem)}
75+
<div className="arduino-boards-dropdown-list--items-container">
76+
{items
77+
.map(({ name, port, selected, onClick }) => ({
78+
boardLabel: name,
79+
port,
80+
selected,
81+
onClick,
82+
}))
83+
.map(this.renderItem)}
84+
</div>
7785
<div
7886
key={footerLabel}
87+
tabIndex={0}
7988
className="arduino-boards-dropdown-item arduino-board-dropdown-footer"
8089
onClick={() => this.props.openBoardsConfig()}
8190
>
@@ -86,22 +95,49 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
8695
}
8796

8897
protected renderItem({
89-
label,
98+
boardLabel,
99+
port,
90100
selected,
91101
onClick,
92102
}: {
93-
label: string;
103+
boardLabel: string;
104+
port: Port;
94105
selected?: boolean;
95106
onClick: () => void;
96107
}): React.ReactNode {
108+
const protocolIcon = iconNameFromProtocol(port.protocol);
109+
const onKeyUp = (e: React.KeyboardEvent) => {
110+
if (e.key === 'Enter') {
111+
onClick();
112+
}
113+
};
114+
97115
return (
98116
<div
99-
key={label}
100-
className={`arduino-boards-dropdown-item ${selected ? 'selected' : ''}`}
117+
key={`board-item--${boardLabel}-${port.address}`}
118+
className={classNames('arduino-boards-dropdown-item', {
119+
'arduino-boards-dropdown-item--selected': selected,
120+
})}
101121
onClick={onClick}
122+
onKeyUp={onKeyUp}
123+
tabIndex={0}
102124
>
103-
<div>{label}</div>
104-
{selected ? <span className="fa fa-check" /> : ''}
125+
<div
126+
className={classNames(
127+
'arduino-boards-dropdown-item--protocol',
128+
'fa',
129+
protocolIcon
130+
)}
131+
/>
132+
<div className="arduino-boards-dropdown-item--label">
133+
<div className="arduino-boards-dropdown-item--board-label">
134+
{boardLabel}
135+
</div>
136+
<div className="arduino-boards-dropdown-item--port-label">
137+
{port.address}
138+
</div>
139+
</div>
140+
{selected ? <div className="fa fa-check" /> : ''}
105141
</div>
106142
);
107143
}
@@ -163,36 +199,43 @@ export class BoardsToolBarItem extends React.Component<
163199

164200
override render(): React.ReactNode {
165201
const { coords, availableBoards } = this.state;
166-
const boardsConfig = this.props.boardsServiceClient.boardsConfig;
167-
const title = BoardsConfig.Config.toString(boardsConfig, {
168-
default: nls.localize(
169-
'arduino/common/noBoardSelected',
170-
'No board selected'
171-
),
172-
});
173-
const decorator = (() => {
174-
const selectedBoard = availableBoards.find(({ selected }) => selected);
175-
if (!selectedBoard || !selectedBoard.port) {
176-
return 'fa fa-times notAttached';
177-
}
178-
if (selectedBoard.state === AvailableBoard.State.guessed) {
179-
return 'fa fa-exclamation-triangle guessed';
180-
}
181-
return '';
182-
})();
202+
const selectedBoard = availableBoards.find(({ selected }) => selected);
203+
204+
const boardLabel =
205+
selectedBoard?.name ||
206+
nls.localize('arduino/board/selectBoard', 'Select Board');
207+
const selectedPortLabel = portLabel(selectedBoard?.port?.address);
208+
209+
const isConnected = Boolean(
210+
selectedBoard && AvailableBoard.hasPort(selectedBoard)
211+
);
212+
const protocolIcon = isConnected
213+
? iconNameFromProtocol(selectedBoard?.port?.protocol || '')
214+
: null;
215+
const procolIconClassNames = classNames(
216+
'arduino-boards-toolbar-item--protocol',
217+
'fa',
218+
protocolIcon
219+
);
183220

184221
return (
185222
<React.Fragment>
186-
<div className="arduino-boards-toolbar-item-container">
187-
<div className="arduino-boards-toolbar-item" title={title}>
188-
<div className="inner-container" onClick={this.show}>
189-
<span className={decorator} />
190-
<div className="label noWrapInfo">
191-
<div className="noWrapInfo noselect">{title}</div>
192-
</div>
193-
<span className="fa fa-caret-down caret" />
194-
</div>
223+
<div
224+
className="arduino-boards-toolbar-item-container"
225+
title={selectedPortLabel}
226+
onClick={this.show}
227+
>
228+
{protocolIcon && <div className={procolIconClassNames} />}
229+
<div
230+
className={classNames(
231+
'arduino-boards-toolbar-item--label',
232+
'noWrapInfo noselect',
233+
{ 'arduino-boards-toolbar-item--label-connected': isConnected }
234+
)}
235+
>
236+
{boardLabel}
195237
</div>
238+
<div className="fa fa-caret-down caret" />
196239
</div>
197240
<BoardsDropDown
198241
coords={coords}
@@ -212,6 +255,7 @@ export class BoardsToolBarItem extends React.Component<
212255
selectedPort: board.port,
213256
};
214257
}
258+
this.setState({ coords: 'hidden' });
215259
},
216260
}))}
217261
openBoardsConfig={this.openDialog}
@@ -222,7 +266,6 @@ export class BoardsToolBarItem extends React.Component<
222266

223267
protected openDialog = () => {
224268
this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id);
225-
this.setState({ coords: 'hidden' });
226269
};
227270
}
228271
export namespace BoardsToolBarItem {
@@ -236,3 +279,26 @@ export namespace BoardsToolBarItem {
236279
coords: BoardsDropDownListCoords | 'hidden';
237280
}
238281
}
282+
283+
function iconNameFromProtocol(protocol: string): string {
284+
switch (protocol) {
285+
case 'serial':
286+
return 'fa-arduino-technology-usb';
287+
case 'network':
288+
return 'fa-arduino-technology-connection';
289+
/*
290+
Bluetooth ports are not listed yet from the CLI;
291+
Not sure about the naming ('bluetooth'); make sure it's correct before uncommenting the following lines
292+
*/
293+
// case 'bluetooth':
294+
// return 'fa-arduino-technology-bluetooth';
295+
default:
296+
return 'fa-arduino-technology-3dimensionscube';
297+
}
298+
}
299+
300+
function portLabel(portName?: string): string {
301+
return portName
302+
? nls.localize('arduino/board/portLabel', 'Port: {0}', portName)
303+
: nls.localize('arduino/board/disconnected', 'Disconnected');
304+
}

Diff for: arduino-ide-extension/src/browser/data/dark.color-theme.json

+7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@
4747
"arduino.output.background": "#000000",
4848
"arduino.toolbar.button.hoverBackground": "#dae3e3",
4949
"arduino.toolbar.button.background": "#0ca1a6",
50+
"arduino.toolbar.dropdown.border": "#7fcbcd",
51+
"arduino.toolbar.dropdown.borderActive": "#0ca1a6",
52+
"arduino.toolbar.dropdown.background": "#2c353a",
53+
"arduino.toolbar.dropdown.label": "#dae3e3",
54+
"arduino.toolbar.dropdown.iconSelected": "#3fae98",
55+
"arduino.toolbar.dropdown.option.backgroundHover": "#374146",
56+
"arduino.toolbar.dropdown.option.backgroundSelected": "#4e5b61",
5057
"arduino.toolbar.toggleBackground": "#f1c40f",
5158
"sideBar.background": "#101618",
5259
"sideBar.foreground": "#dae3e3",

Diff for: arduino-ide-extension/src/browser/data/default.color-theme.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"titleBar.activeForeground": "#f7f9f9",
3030
"terminal.background": "#000000",
3131
"terminal.foreground": "#e0e0e0",
32-
"dropdown.border": "#f7f9f9",
32+
"dropdown.border": "#dae3e3",
3333
"dropdown.background": "#ffffff",
3434
"dropdown.foreground": "#4e5b61",
3535
"activityBar.background": "#ecf1f1",
@@ -47,6 +47,13 @@
4747
"arduino.output.background": "#000000",
4848
"arduino.toolbar.button.hoverBackground": "#f7f9f9",
4949
"arduino.toolbar.button.background": "#7fcbcd",
50+
"arduino.toolbar.dropdown.border": "#dae3e3",
51+
"arduino.toolbar.dropdown.borderActive": "#7fcbcd",
52+
"arduino.toolbar.dropdown.background": "#ffffff",
53+
"arduino.toolbar.dropdown.label": "#4e5b61",
54+
"arduino.toolbar.dropdown.iconSelected": "#1da086",
55+
"arduino.toolbar.dropdown.option.backgroundHover": "#ecf1f1",
56+
"arduino.toolbar.dropdown.option.backgroundSelected": "#dae3e3",
5057
"arduino.toolbar.toggleBackground": "#f1c40f",
5158
"sideBar.background": "#f7f9f9",
5259
"sideBar.foreground": "#4e5b61",

0 commit comments

Comments
 (0)