Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit de32bdd

Browse files
author
Alberto Iannaccone
authoredAug 18, 2022
Fix dialogs UI scalability (#1311)
* make dialogs scroll when scaling up the UI * add unit of measure to settings step input * wrap settings dialog items when scaling up the UI * fix dialogs width when scaling up the UI * rework board config UI to make it scale up better * refactor ide updater dialog: move buttons outside the dialog content * refactor ide updater dialog: clean-up code and rename events * fix board config dialog title case and and remove double ellipsis
1 parent 79ea0fa commit de32bdd

20 files changed

+375
-298
lines changed
 

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
453453
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
454454
bind(BoardsConfigDialog).toSelf().inSingletonScope();
455455
bind(BoardsConfigDialogProps).toConstantValue({
456-
title: nls.localize('arduino/common/selectBoard', 'Select Board'),
456+
title: nls.localize(
457+
'arduino/board/boardConfigDialogTitle',
458+
'Select Other Board and Port'
459+
),
457460
});
458461

459462
// Core service

‎arduino-ide-extension/src/browser/boards/boards-config-dialog.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
1+
import {
2+
injectable,
3+
inject,
4+
postConstruct,
5+
} from '@theia/core/shared/inversify';
26
import { Message } from '@theia/core/shared/@phosphor/messaging';
37
import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
48
import { AbstractDialog } from '../theia/dialogs/dialogs';
@@ -28,7 +32,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
2832
@inject(BoardsConfigDialogProps)
2933
protected override readonly props: BoardsConfigDialogProps
3034
) {
31-
super(props);
35+
super({ ...props, maxWidth: 500 });
3236

3337
this.contentNode.classList.add('select-board-dialog');
3438
this.contentNode.appendChild(this.createDescription());
@@ -65,14 +69,6 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
6569
const head = document.createElement('div');
6670
head.classList.add('head');
6771

68-
const title = document.createElement('div');
69-
title.textContent = nls.localize(
70-
'arduino/board/configDialogTitle',
71-
'Select Other Board & Port'
72-
);
73-
title.classList.add('title');
74-
head.appendChild(title);
75-
7672
const text = document.createElement('div');
7773
text.classList.add('text');
7874
head.appendChild(text);

‎arduino-ide-extension/src/browser/boards/boards-config.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,14 @@ export class BoardsConfig extends React.Component<
258258

259259
override render(): React.ReactNode {
260260
return (
261-
<div className="body">
261+
<>
262262
{this.renderContainer('boards', this.renderBoards.bind(this))}
263263
{this.renderContainer(
264264
'ports',
265265
this.renderPorts.bind(this),
266266
this.renderPortsFooter.bind(this)
267267
)}
268-
</div>
268+
</>
269269
);
270270
}
271271

‎arduino-ide-extension/src/browser/dialogs/certificate-uploader/certificate-uploader-dialog.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import * as React from '@theia/core/shared/react';
2-
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
2+
import {
3+
inject,
4+
injectable,
5+
postConstruct,
6+
} from '@theia/core/shared/inversify';
37
import { DialogProps } from '@theia/core/lib/browser/dialogs';
48
import { AbstractDialog } from '../../theia/dialogs/dialogs';
59
import { Widget } from '@theia/core/shared/@phosphor/widgets';
@@ -153,6 +157,7 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
153157
'Upload SSL Root Certificates'
154158
),
155159
});
160+
this.node.id = 'certificate-uploader-dialog-container';
156161
this.contentNode.classList.add('certificate-uploader-dialog');
157162
this.acceptButton = undefined;
158163
}

‎arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
101101
protected override readonly props: UploadFirmwareDialogProps
102102
) {
103103
super({ title: UploadFirmware.Commands.OPEN.label || '' });
104+
this.node.id = 'firmware-uploader-dialog-container';
104105
this.contentNode.classList.add('firmware-uploader-dialog');
105106
this.acceptButton = undefined;
106107
}

‎arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx

Lines changed: 23 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { WindowService } from '@theia/core/lib/browser/window/window-service';
21
import { nls } from '@theia/core/lib/common';
32
import { shell } from 'electron';
43
import * as React from '@theia/core/shared/react';
@@ -7,36 +6,32 @@ import ReactMarkdown from 'react-markdown';
76
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
87
import ProgressBar from '../../components/ProgressBar';
98

10-
export type IDEUpdaterComponentProps = {
11-
updateInfo: UpdateInfo;
12-
windowService: WindowService;
9+
export interface UpdateProgress {
10+
progressInfo?: ProgressInfo | undefined;
1311
downloadFinished?: boolean;
1412
downloadStarted?: boolean;
15-
progress?: ProgressInfo;
1613
error?: Error;
17-
onDownload: () => void;
18-
onClose: () => void;
19-
onSkipVersion: () => void;
20-
onCloseAndInstall: () => void;
21-
};
14+
}
15+
16+
export interface IDEUpdaterComponentProps {
17+
updateInfo: UpdateInfo;
18+
updateProgress: UpdateProgress;
19+
}
2220

2321
export const IDEUpdaterComponent = ({
24-
updateInfo: { version, releaseNotes },
25-
downloadStarted = false,
26-
downloadFinished = false,
27-
windowService,
28-
progress,
29-
error,
30-
onDownload,
31-
onClose,
32-
onSkipVersion,
33-
onCloseAndInstall,
22+
updateInfo,
23+
updateProgress: {
24+
downloadStarted = false,
25+
downloadFinished = false,
26+
progressInfo,
27+
error,
28+
},
3429
}: IDEUpdaterComponentProps): React.ReactElement => {
35-
const changelogDivRef = React.useRef() as React.MutableRefObject<
36-
HTMLDivElement
37-
>;
30+
const { version, releaseNotes } = updateInfo;
31+
const changelogDivRef =
32+
React.useRef() as React.MutableRefObject<HTMLDivElement>;
3833
React.useEffect(() => {
39-
if (!!releaseNotes) {
34+
if (!!releaseNotes && changelogDivRef.current) {
4035
let changelog: string;
4136
if (typeof releaseNotes === 'string') changelog = releaseNotes;
4237
else
@@ -58,12 +53,7 @@ export const IDEUpdaterComponent = ({
5853
changelogDivRef.current
5954
);
6055
}
61-
}, [releaseNotes]);
62-
const closeButton = (
63-
<button onClick={onClose} type="button" className="theia-button secondary">
64-
{nls.localize('arduino/ide-updater/notNowButton', 'Not now')}
65-
</button>
66-
);
56+
}, [updateInfo]);
6757

6858
const DownloadCompleted: () => React.ReactElement = () => (
6959
<div className="ide-updater-dialog--downloaded">
@@ -80,19 +70,6 @@ export const IDEUpdaterComponent = ({
8070
'Close the software and install the update on your machine.'
8171
)}
8272
</div>
83-
<div className="buttons-container">
84-
{closeButton}
85-
<button
86-
onClick={onCloseAndInstall}
87-
type="button"
88-
className="theia-button close-and-install"
89-
>
90-
{nls.localize(
91-
'arduino/ide-updater/closeAndInstallButton',
92-
'Close and Install'
93-
)}
94-
</button>
95-
</div>
9673
</div>
9774
);
9875

@@ -104,7 +81,7 @@ export const IDEUpdaterComponent = ({
10481
'Downloading the latest version of the Arduino IDE.'
10582
)}
10683
</div>
107-
<ProgressBar percent={progress?.percent} showPercentage />
84+
<ProgressBar percent={progressInfo?.percent} showPercentage />
10885
</div>
10986
);
11087

@@ -130,46 +107,14 @@ export const IDEUpdaterComponent = ({
130107
)}
131108
</div>
132109
{releaseNotes && (
133-
<div className="dialogRow">
134-
<div className="changelog-container" ref={changelogDivRef} />
110+
<div className="dialogRow changelog-container">
111+
<div className="changelog" ref={changelogDivRef} />
135112
</div>
136113
)}
137-
<div className="buttons-container">
138-
<button
139-
onClick={onSkipVersion}
140-
type="button"
141-
className="theia-button secondary skip-version"
142-
>
143-
{nls.localize(
144-
'arduino/ide-updater/skipVersionButton',
145-
'Skip Version'
146-
)}
147-
</button>
148-
<div className="push"></div>
149-
{closeButton}
150-
<button
151-
onClick={onDownload}
152-
type="button"
153-
className="theia-button primary"
154-
>
155-
{nls.localize('arduino/ide-updater/downloadButton', 'Download')}
156-
</button>
157-
</div>
158114
</div>
159115
</div>
160116
);
161117

162-
const onGoToDownloadClick = (
163-
event: React.SyntheticEvent<HTMLAnchorElement, Event>
164-
) => {
165-
const { target } = event.nativeEvent;
166-
if (target instanceof HTMLAnchorElement) {
167-
event.nativeEvent.preventDefault();
168-
windowService.openNewWindow(target.href, { external: true });
169-
onClose();
170-
}
171-
};
172-
173118
const GoToDownloadPage: () => React.ReactElement = () => (
174119
<div className="ide-updater-dialog--go-to-download-page">
175120
<div>
@@ -178,19 +123,6 @@ export const IDEUpdaterComponent = ({
178123
"An update for the Arduino IDE is available, but we're not able to download and install it automatically. Please go to the download page and download the latest version from there."
179124
)}
180125
</div>
181-
<div className="buttons-container">
182-
{closeButton}
183-
<a
184-
className="theia-button primary"
185-
href="https://www.arduino.cc/en/software#experimental-software"
186-
onClick={onGoToDownloadClick}
187-
>
188-
{nls.localize(
189-
'arduino/ide-updater/goToDownloadButton',
190-
'Go To Download'
191-
)}
192-
</a>
193-
</div>
194126
</div>
195127
);
196128

Lines changed: 167 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,57 @@
11
import * as React from '@theia/core/shared/react';
2-
import { inject, injectable } from '@theia/core/shared/inversify';
2+
import {
3+
inject,
4+
injectable,
5+
postConstruct,
6+
} from '@theia/core/shared/inversify';
37
import { DialogProps } from '@theia/core/lib/browser/dialogs';
48
import { AbstractDialog } from '../../theia/dialogs/dialogs';
59
import { Widget } from '@theia/core/shared/@phosphor/widgets';
610
import { Message } from '@theia/core/shared/@phosphor/messaging';
711
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
812
import { nls } from '@theia/core';
9-
import { IDEUpdaterComponent } from './ide-updater-component';
10-
13+
import { IDEUpdaterComponent, UpdateProgress } from './ide-updater-component';
1114
import {
1215
IDEUpdater,
1316
IDEUpdaterClient,
14-
ProgressInfo,
1517
SKIP_IDE_VERSION,
1618
UpdateInfo,
1719
} from '../../../common/protocol/ide-updater';
1820
import { LocalStorageService } from '@theia/core/lib/browser';
1921
import { WindowService } from '@theia/core/lib/browser/window/window-service';
2022

23+
const DOWNLOAD_PAGE_URL =
24+
'https://www.arduino.cc/en/software#experimental-software';
25+
2126
@injectable()
2227
export class IDEUpdaterDialogWidget extends ReactWidget {
23-
protected isOpen = new Object();
24-
updateInfo: UpdateInfo;
25-
progressInfo: ProgressInfo | undefined;
26-
error: Error | undefined;
27-
downloadFinished: boolean;
28-
downloadStarted: boolean;
29-
onClose: () => void;
30-
31-
@inject(IDEUpdater)
32-
protected readonly updater: IDEUpdater;
33-
34-
@inject(IDEUpdaterClient)
35-
protected readonly updaterClient: IDEUpdaterClient;
36-
37-
@inject(LocalStorageService)
38-
protected readonly localStorageService: LocalStorageService;
39-
40-
@inject(WindowService)
41-
protected windowService: WindowService;
42-
43-
init(updateInfo: UpdateInfo, onClose: () => void): void {
44-
this.updateInfo = updateInfo;
45-
this.progressInfo = undefined;
46-
this.error = undefined;
47-
this.downloadStarted = false;
48-
this.downloadFinished = false;
49-
this.onClose = onClose;
50-
51-
this.updaterClient.onError((e) => {
52-
this.error = e;
53-
this.update();
54-
});
55-
this.updaterClient.onDownloadProgressChanged((e) => {
56-
this.progressInfo = e;
57-
this.update();
58-
});
59-
this.updaterClient.onDownloadFinished((e) => {
60-
this.downloadFinished = true;
61-
this.update();
62-
});
63-
}
28+
private _updateInfo: UpdateInfo;
29+
private _updateProgress: UpdateProgress = {};
6430

65-
async onSkipVersion(): Promise<void> {
66-
this.localStorageService.setData<string>(
67-
SKIP_IDE_VERSION,
68-
this.updateInfo.version
69-
);
70-
this.close();
71-
}
72-
73-
override close(): void {
74-
super.close();
75-
this.onClose();
31+
setUpdateInfo(updateInfo: UpdateInfo): void {
32+
this._updateInfo = updateInfo;
33+
this.update();
7634
}
7735

78-
onDispose(): void {
79-
if (this.downloadStarted && !this.downloadFinished)
80-
this.updater.stopDownload();
36+
mergeUpdateProgress(updateProgress: UpdateProgress): void {
37+
this._updateProgress = { ...this._updateProgress, ...updateProgress };
38+
this.update();
8139
}
8240

83-
async onDownload(): Promise<void> {
84-
this.progressInfo = undefined;
85-
this.downloadStarted = true;
86-
this.error = undefined;
87-
this.updater.downloadUpdate();
88-
this.update();
41+
get updateInfo(): UpdateInfo {
42+
return this._updateInfo;
8943
}
9044

91-
onCloseAndInstall(): void {
92-
this.updater.quitAndInstall();
45+
get updateProgress(): UpdateProgress {
46+
return this._updateProgress;
9347
}
9448

9549
protected render(): React.ReactNode {
96-
return !!this.updateInfo ? (
97-
<form>
98-
<IDEUpdaterComponent
99-
updateInfo={this.updateInfo}
100-
windowService={this.windowService}
101-
downloadStarted={this.downloadStarted}
102-
downloadFinished={this.downloadFinished}
103-
progress={this.progressInfo}
104-
error={this.error}
105-
onClose={this.close.bind(this)}
106-
onSkipVersion={this.onSkipVersion.bind(this)}
107-
onDownload={this.onDownload.bind(this)}
108-
onCloseAndInstall={this.onCloseAndInstall.bind(this)}
109-
/>
110-
</form>
50+
return !!this._updateInfo ? (
51+
<IDEUpdaterComponent
52+
updateInfo={this._updateInfo}
53+
updateProgress={this._updateProgress}
54+
/>
11155
) : null;
11256
}
11357
}
@@ -118,7 +62,19 @@ export class IDEUpdaterDialogProps extends DialogProps {}
11862
@injectable()
11963
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
12064
@inject(IDEUpdaterDialogWidget)
121-
protected readonly widget: IDEUpdaterDialogWidget;
65+
private readonly widget: IDEUpdaterDialogWidget;
66+
67+
@inject(IDEUpdater)
68+
private readonly updater: IDEUpdater;
69+
70+
@inject(IDEUpdaterClient)
71+
private readonly updaterClient: IDEUpdaterClient;
72+
73+
@inject(LocalStorageService)
74+
private readonly localStorageService: LocalStorageService;
75+
76+
@inject(WindowService)
77+
private readonly windowService: WindowService;
12278

12379
constructor(
12480
@inject(IDEUpdaterDialogProps)
@@ -130,10 +86,26 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
13086
'Software Update'
13187
),
13288
});
89+
this.node.id = 'ide-updater-dialog-container';
13390
this.contentNode.classList.add('ide-updater-dialog');
13491
this.acceptButton = undefined;
13592
}
13693

94+
@postConstruct()
95+
protected init(): void {
96+
this.updaterClient.onUpdaterDidFail((error) => {
97+
this.appendErrorButtons();
98+
this.widget.mergeUpdateProgress({ error });
99+
});
100+
this.updaterClient.onDownloadProgressDidChange((progressInfo) => {
101+
this.widget.mergeUpdateProgress({ progressInfo });
102+
});
103+
this.updaterClient.onDownloadDidFinish(() => {
104+
this.appendInstallButtons();
105+
this.widget.mergeUpdateProgress({ downloadFinished: true });
106+
});
107+
}
108+
137109
get value(): UpdateInfo {
138110
return this.widget.updateInfo;
139111
}
@@ -143,31 +115,136 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
143115
Widget.detach(this.widget);
144116
}
145117
Widget.attach(this.widget, this.contentNode);
118+
this.appendInitialButtons();
146119
super.onAfterAttach(msg);
147-
this.update();
120+
}
121+
122+
private clearButtons(): void {
123+
while (this.controlPanel.firstChild) {
124+
this.controlPanel.removeChild(this.controlPanel.firstChild);
125+
}
126+
this.closeButton = undefined;
127+
}
128+
129+
private appendNotNowButton(): void {
130+
this.appendCloseButton(
131+
nls.localize('arduino/ide-updater/notNowButton', 'Not now')
132+
);
133+
if (this.closeButton) {
134+
this.addCloseAction(this.closeButton, 'click');
135+
}
136+
}
137+
138+
private appendInitialButtons(): void {
139+
this.clearButtons();
140+
141+
const skipVersionButton = this.createButton(
142+
nls.localize('arduino/ide-updater/skipVersionButton', 'Skip Version')
143+
);
144+
skipVersionButton.classList.add('secondary');
145+
skipVersionButton.classList.add('skip-version-button');
146+
this.addAction(skipVersionButton, this.skipVersion.bind(this), 'click');
147+
this.controlPanel.appendChild(skipVersionButton);
148+
149+
this.appendNotNowButton();
150+
151+
const downloadButton = this.createButton(
152+
nls.localize('arduino/ide-updater/downloadButton', 'Download')
153+
);
154+
this.addAction(downloadButton, this.startDownload.bind(this), 'click');
155+
this.controlPanel.appendChild(downloadButton);
156+
downloadButton.focus();
157+
}
158+
159+
private appendInstallButtons(): void {
160+
this.clearButtons();
161+
this.appendNotNowButton();
162+
163+
const closeAndInstallButton = this.createButton(
164+
nls.localize(
165+
'arduino/ide-updater/closeAndInstallButton',
166+
'Close and Install'
167+
)
168+
);
169+
this.addAction(
170+
closeAndInstallButton,
171+
this.closeAndInstall.bind(this),
172+
'click'
173+
);
174+
this.controlPanel.appendChild(closeAndInstallButton);
175+
closeAndInstallButton.focus();
176+
}
177+
178+
private appendErrorButtons(): void {
179+
this.clearButtons();
180+
this.appendNotNowButton();
181+
182+
const goToDownloadPageButton = this.createButton(
183+
nls.localize('arduino/ide-updater/goToDownloadButton', 'Go To Download')
184+
);
185+
this.addAction(
186+
goToDownloadPageButton,
187+
this.openDownloadPage.bind(this),
188+
'click'
189+
);
190+
this.controlPanel.appendChild(goToDownloadPageButton);
191+
goToDownloadPageButton.focus();
192+
}
193+
194+
private openDownloadPage(): void {
195+
this.windowService.openNewWindow(DOWNLOAD_PAGE_URL, { external: true });
196+
this.close();
197+
}
198+
199+
private skipVersion(): void {
200+
this.localStorageService.setData<string>(
201+
SKIP_IDE_VERSION,
202+
this.widget.updateInfo.version
203+
);
204+
this.close();
205+
}
206+
207+
private startDownload(): void {
208+
this.widget.mergeUpdateProgress({
209+
downloadStarted: true,
210+
});
211+
this.clearButtons();
212+
this.updater.downloadUpdate();
213+
}
214+
215+
private closeAndInstall() {
216+
this.updater.quitAndInstall();
217+
this.close();
148218
}
149219

150220
override async open(
151221
data: UpdateInfo | undefined = undefined
152222
): Promise<UpdateInfo | undefined> {
153223
if (data && data.version) {
154-
this.widget.init(data, this.close.bind(this));
224+
this.widget.mergeUpdateProgress({
225+
progressInfo: undefined,
226+
downloadStarted: false,
227+
downloadFinished: false,
228+
error: undefined,
229+
});
230+
this.widget.setUpdateInfo(data);
155231
return super.open();
156232
}
157233
}
158234

159-
protected override onUpdateRequest(msg: Message): void {
160-
super.onUpdateRequest(msg);
161-
this.widget.update();
162-
}
163-
164235
protected override onActivateRequest(msg: Message): void {
165236
super.onActivateRequest(msg);
166237
this.widget.activate();
167238
}
168239

169240
override close(): void {
170241
this.widget.dispose();
242+
if (
243+
this.widget.updateProgress?.downloadStarted &&
244+
!this.widget.updateProgress?.downloadFinished
245+
) {
246+
this.updater.stopDownload();
247+
}
171248
super.close();
172249
}
173250
}

‎arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,17 @@ export class SettingsComponent extends React.Component<
188188
/>
189189
{nls.localize('arduino/preferences/automatic', 'Automatic')}
190190
</label>
191-
<SettingsStepInput
192-
value={scalePercentage}
193-
setSettingsStateValue={this.setInterfaceScale}
194-
step={scaleStep}
195-
maxValue={maxScale}
196-
minValue={minScale}
197-
classNames={{ input: 'theia-input small with-margin' }}
198-
/>
199-
%
191+
<div>
192+
<SettingsStepInput
193+
value={scalePercentage}
194+
setSettingsStateValue={this.setInterfaceScale}
195+
step={scaleStep}
196+
maxValue={maxScale}
197+
minValue={minScale}
198+
unitOfMeasure="%"
199+
classNames={{ input: 'theia-input small with-margin' }}
200+
/>
201+
</div>
200202
</div>
201203
<div className="flex-line">
202204
<select

‎arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,22 @@ interface SettingsStepInputProps {
77
step: number;
88
maxValue: number;
99
minValue: number;
10+
unitOfMeasure?: string;
1011
classNames?: { [key: string]: string };
1112
}
1213

1314
const SettingsStepInput: React.FC<SettingsStepInputProps> = (
1415
props: SettingsStepInputProps
1516
) => {
16-
const { value, setSettingsStateValue, step, maxValue, minValue, classNames } =
17-
props;
17+
const {
18+
value,
19+
setSettingsStateValue,
20+
step,
21+
maxValue,
22+
minValue,
23+
unitOfMeasure,
24+
classNames,
25+
} = props;
1826

1927
const clamp = (value: number, min: number, max: number): number => {
2028
return Math.min(Math.max(value, min), max);
@@ -86,6 +94,7 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
8694
&#9662;
8795
</button>
8896
</div>
97+
{unitOfMeasure && `${unitOfMeasure}`}
8998
</div>
9099
);
91100
};

‎arduino-ide-extension/src/browser/ide-updater/ide-updater-client-impl.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,43 @@ import { IDEUpdaterClient } from '../../common/protocol/ide-updater';
55

66
@injectable()
77
export class IDEUpdaterClientImpl implements IDEUpdaterClient {
8-
protected readonly onErrorEmitter = new Emitter<Error>();
9-
protected readonly onCheckingForUpdateEmitter = new Emitter<void>();
10-
protected readonly onUpdateAvailableEmitter = new Emitter<UpdateInfo>();
11-
protected readonly onUpdateNotAvailableEmitter = new Emitter<UpdateInfo>();
12-
protected readonly onDownloadProgressEmitter = new Emitter<ProgressInfo>();
13-
protected readonly onDownloadFinishedEmitter = new Emitter<UpdateInfo>();
8+
protected readonly onUpdaterDidFailEmitter = new Emitter<Error>();
9+
protected readonly onUpdaterDidCheckForUpdateEmitter = new Emitter<void>();
10+
protected readonly onUpdaterDidFindUpdateAvailableEmitter =
11+
new Emitter<UpdateInfo>();
12+
protected readonly onUpdaterDidNotFindUpdateAvailableEmitter =
13+
new Emitter<UpdateInfo>();
14+
protected readonly onDownloadProgressDidChangeEmitter =
15+
new Emitter<ProgressInfo>();
16+
protected readonly onDownloadDidFinishEmitter = new Emitter<UpdateInfo>();
1417

15-
readonly onError = this.onErrorEmitter.event;
16-
readonly onCheckingForUpdate = this.onCheckingForUpdateEmitter.event;
17-
readonly onUpdateAvailable = this.onUpdateAvailableEmitter.event;
18-
readonly onUpdateNotAvailable = this.onUpdateNotAvailableEmitter.event;
19-
readonly onDownloadProgressChanged = this.onDownloadProgressEmitter.event;
20-
readonly onDownloadFinished = this.onDownloadFinishedEmitter.event;
18+
readonly onUpdaterDidFail = this.onUpdaterDidFailEmitter.event;
19+
readonly onUpdaterDidCheckForUpdate =
20+
this.onUpdaterDidCheckForUpdateEmitter.event;
21+
readonly onUpdaterDidFindUpdateAvailable =
22+
this.onUpdaterDidFindUpdateAvailableEmitter.event;
23+
readonly onUpdaterDidNotFindUpdateAvailable =
24+
this.onUpdaterDidNotFindUpdateAvailableEmitter.event;
25+
readonly onDownloadProgressDidChange =
26+
this.onDownloadProgressDidChangeEmitter.event;
27+
readonly onDownloadDidFinish = this.onDownloadDidFinishEmitter.event;
2128

22-
notifyError(message: Error): void {
23-
this.onErrorEmitter.fire(message);
29+
notifyUpdaterFailed(message: Error): void {
30+
this.onUpdaterDidFailEmitter.fire(message);
2431
}
25-
notifyCheckingForUpdate(message: void): void {
26-
this.onCheckingForUpdateEmitter.fire(message);
32+
notifyCheckedForUpdate(message: void): void {
33+
this.onUpdaterDidCheckForUpdateEmitter.fire(message);
2734
}
28-
notifyUpdateAvailable(message: UpdateInfo): void {
29-
this.onUpdateAvailableEmitter.fire(message);
35+
notifyUpdateAvailableFound(message: UpdateInfo): void {
36+
this.onUpdaterDidFindUpdateAvailableEmitter.fire(message);
3037
}
31-
notifyUpdateNotAvailable(message: UpdateInfo): void {
32-
this.onUpdateNotAvailableEmitter.fire(message);
38+
notifyUpdateAvailableNotFound(message: UpdateInfo): void {
39+
this.onUpdaterDidNotFindUpdateAvailableEmitter.fire(message);
3340
}
3441
notifyDownloadProgressChanged(message: ProgressInfo): void {
35-
this.onDownloadProgressEmitter.fire(message);
42+
this.onDownloadProgressDidChangeEmitter.fire(message);
3643
}
3744
notifyDownloadFinished(message: UpdateInfo): void {
38-
this.onDownloadFinishedEmitter.fire(message);
45+
this.onDownloadDidFinishEmitter.fire(message);
3946
}
4047
}

‎arduino-ide-extension/src/browser/style/boards-config-dialog.css

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ div#select-board-dialog {
22
margin: 5px;
33
}
44

5-
div#select-board-dialog .selectBoardContainer .body {
5+
div#select-board-dialog .selectBoardContainer {
66
display: flex;
7+
gap: 10px;
78
overflow: hidden;
9+
max-height: 100%;
810
}
911

1012
.select-board-dialog .head {
@@ -19,12 +21,13 @@ div.dialogContent.select-board-dialog > div.head .title {
1921
margin-bottom: 10px;
2022
}
2123

22-
div#select-board-dialog .selectBoardContainer .body .list .item.selected {
24+
25+
div#select-board-dialog .selectBoardContainer .list .item.selected {
2326
background: var(--theia-secondaryButton-hoverBackground);
2427
}
2528

26-
div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
27-
color: var(--theia-list-activeSelectionIconForeground);
29+
div#select-board-dialog .selectBoardContainer .list .item.selected i {
30+
color: var(--theia-arduino-branding-primary);
2831
}
2932

3033
#select-board-dialog .selectBoardContainer .search,
@@ -34,7 +37,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
3437
background: var(--theia-editor-background);
3538
}
3639

37-
#select-board-dialog .selectBoardContainer .body .search input {
40+
#select-board-dialog .selectBoardContainer .search input {
3841
border: none;
3942
width: 100%;
4043
height: auto;
@@ -46,58 +49,63 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
4649
color: var(--theia-input-foreground);
4750
}
4851

49-
#select-board-dialog .selectBoardContainer .body .search input:focus {
52+
#select-board-dialog .selectBoardContainer .search input:focus {
5053
box-shadow: none;
5154
}
5255

53-
#select-board-dialog .selectBoardContainer .body .container {
56+
#select-board-dialog .selectBoardContainer .container {
5457
flex: 1;
55-
padding: 0px 10px 0px 0px;
56-
min-width: 240px;
57-
max-width: 240px;
58+
overflow: hidden;
59+
max-height: 100%;
60+
}
61+
62+
#select-board-dialog .selectBoardContainer .container .content {
63+
display: flex;
64+
flex-direction: column;
65+
max-height: 100%;
5866
}
5967

60-
#select-board-dialog .selectBoardContainer .body .left.container .content {
68+
#select-board-dialog .selectBoardContainer .left.container .content {
6169
margin: 0 5px 0 0;
6270
}
6371

64-
#select-board-dialog .selectBoardContainer .body .right.container .content {
72+
#select-board-dialog .selectBoardContainer .right.container .content {
6573
margin: 0 0 0 5px;
6674
}
6775

68-
#select-board-dialog .selectBoardContainer .body .container .content .title {
76+
#select-board-dialog .selectBoardContainer .container .content .title {
6977
color: var(--theia-editorWidget-foreground);
7078
padding: 0px 0px 10px 0px;
7179
text-transform: uppercase;
7280
}
7381

74-
#select-board-dialog .selectBoardContainer .body .container .content .footer {
82+
#select-board-dialog .selectBoardContainer .container .content .footer {
7583
padding: 10px 5px 10px 0px;
7684
}
7785

78-
#select-board-dialog .selectBoardContainer .body .container .content .loading {
86+
#select-board-dialog .selectBoardContainer .container .content .loading {
7987
font-size: var(--theia-ui-font-size1);
8088
color: var(--theia-editorWidget-foreground);
8189
padding: 10px 5px 10px 10px;
8290
text-transform: uppercase;
83-
/* The max, min-height comes from `.body .list` 200px + 47px top padding - 2 * 10px top padding */
91+
/* The max, min-height comes from `.list` 200px + 47px top padding - 2 * 10px top padding */
8492
max-height: 227px;
8593
min-height: 227px;
8694
}
8795

88-
#select-board-dialog .selectBoardContainer .body .list .item {
96+
#select-board-dialog .selectBoardContainer .list .item {
8997
padding: 10px 5px 10px 10px;
9098
display: flex;
91-
justify-content: end;
9299
white-space: nowrap;
93100
overflow-x: hidden;
101+
flex: 1 0;
94102
}
95103

96-
#select-board-dialog .selectBoardContainer .body .list .item .selected-icon {
104+
#select-board-dialog .selectBoardContainer .list .item .selected-icon {
97105
margin-left: auto;
98106
}
99107

100-
#select-board-dialog .selectBoardContainer .body .list .item .details {
108+
#select-board-dialog .selectBoardContainer .list .item .details {
101109
font-size: var(--theia-ui-font-size1);
102110
opacity: var(--theia-mod-disabled-opacity);
103111
width: 155px; /* used heuristics for the calculation */
@@ -106,43 +114,36 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
106114
text-overflow: ellipsis;
107115
}
108116

109-
#select-board-dialog .selectBoardContainer .body .list .item.missing {
117+
#select-board-dialog .selectBoardContainer .list .item.missing {
110118
opacity: var(--theia-mod-disabled-opacity);
111119
}
112120

113-
#select-board-dialog .selectBoardContainer .body .list .item:hover {
121+
#select-board-dialog .selectBoardContainer .list .item:hover {
114122
background: var(--theia-secondaryButton-hoverBackground);
115123
}
116124

117-
#select-board-dialog .selectBoardContainer .body .list .label {
118-
max-width: 215px;
119-
width: 215px;
125+
#select-board-dialog .selectBoardContainer .list .label {
120126
white-space: pre;
121127
overflow: hidden;
122128
text-overflow: ellipsis;
123129
}
124130

125-
#select-board-dialog .selectBoardContainer .body .list {
131+
#select-board-dialog .selectBoardContainer .list {
126132
max-height: 200px;
127-
min-height: 200px;
128133
overflow-y: auto;
129134
}
130135

131-
#select-board-dialog .selectBoardContainer .body .ports.list {
136+
#select-board-dialog .selectBoardContainer .ports.list {
132137
margin: 47px 0px 0px 0px; /* 47 is 37 as input height for the `Boards`, plus 10 margin bottom. */
133138
}
134139

135-
#select-board-dialog .selectBoardContainer .body .search {
140+
#select-board-dialog .selectBoardContainer .search {
136141
margin-bottom: 10px;
137142
display: flex;
138143
align-items: center;
139144
padding-right: 5px;
140145
}
141146

142-
.p-Widget.dialogOverlay .dialogContent.select-board-dialog {
143-
width: 500px;
144-
}
145-
146147
.arduino-boards-toolbar-item-container {
147148
align-items: center;
148149
background: var(--theia-arduino-toolbar-dropdown-background);
@@ -264,10 +265,20 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
264265

265266
/* High Contrast Theme rules */
266267
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
267-
.hc-black.hc-theia.theia-hc #select-board-dialog .selectBoardContainer .body .list .item:hover {
268+
.hc-black.hc-theia.theia-hc #select-board-dialog .selectBoardContainer .list .item:hover {
268269
outline: 1px dashed var(--theia-focusBorder);
269270
}
270271

271-
.hc-black.hc-theia.theia-hc div#select-board-dialog .selectBoardContainer .body .list .item.selected {
272+
.hc-black.hc-theia.theia-hc div#select-board-dialog .selectBoardContainer .list .item.selected {
272273
outline: 1px solid var(--theia-focusBorder);
273274
}
275+
276+
@media only screen and (max-height: 400px) {
277+
div.dialogContent.select-board-dialog > div.head {
278+
display: none;
279+
}
280+
281+
#select-board-dialog .selectBoardContainer .container .content .title {
282+
display: none;
283+
}
284+
}

‎arduino-ide-extension/src/browser/style/certificate-uploader-dialog.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.certificate-uploader-dialog {
1+
#certificate-uploader-dialog-container > .dialogBlock {
22
width: 600px;
33
}
44

‎arduino-ide-extension/src/browser/style/dialogs.css

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
total = padding + margin = 96px
1010
*/
1111
max-width: calc(100% - 96px) !important;
12+
min-width: unset;
13+
max-height: 560px;
1214
padding: 0 28px;
1315
}
1416

1517
.p-Widget.dialogOverlay .dialogBlock .dialogTitle {
16-
padding: 36px 0 28px;
18+
padding: 20px 0;
1719
font-weight: 500;
1820
background-color: transparent;
1921
font-size: var(--theia-ui-font-size2);
@@ -28,6 +30,7 @@
2830

2931
.p-Widget.dialogOverlay .dialogBlock .dialogContent {
3032
padding: 0;
33+
overflow: auto;
3134
}
3235

3336
.p-Widget.dialogOverlay .dialogBlock .dialogContent > input {
@@ -75,3 +78,10 @@
7578
.fa.disabled {
7679
opacity: .4;
7780
}
81+
82+
83+
@media only screen and (max-height: 560px) {
84+
.p-Widget.dialogOverlay .dialogBlock {
85+
max-height: 400px;
86+
}
87+
}

‎arduino-ide-extension/src/browser/style/firmware-uploader-dialog.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.firmware-uploader-dialog {
1+
#firmware-uploader-dialog-container > .dialogBlock {
22
width: 600px;
33
}
44

@@ -11,7 +11,6 @@
1111
}
1212

1313
.firmware-uploader-dialog .dialogRow > button{
14-
width: 33%;
1514
margin-right: 3px;
1615
}
1716

‎arduino-ide-extension/src/browser/style/ide-updater-dialog.css

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.ide-updater-dialog {
1+
#ide-updater-dialog-container > .dialogBlock {
22
width: 546px;
33
}
44

@@ -10,6 +10,10 @@
1010
display: flex;
1111
}
1212

13+
.ide-updater-dialog--downloading {
14+
flex: 1;
15+
}
16+
1317
.ide-updater-dialog--logo-container {
1418
margin-right: 28px;
1519
}
@@ -23,37 +27,49 @@
2327
.dialogContent.ide-updater-dialog
2428
.ide-updater-dialog--content
2529
.ide-updater-dialog--new-version-text.dialogSection {
30+
display: flex;
31+
flex: 1;
32+
flex-direction: column;
2633
margin-top: 0;
34+
min-width: 0;
2735
}
2836

29-
.ide-updater-dialog .changelog-container {
37+
.ide-updater-dialog .changelog {
3038
color: var(--theia-editor-foreground);
3139
background-color: var(--theia-editor-background);
32-
border: 1px solid var(--theia-editorWidget-border);
33-
border-radius: 2px;
3440
font-size: 12px;
35-
height: 180px;
3641
overflow: auto;
3742
padding: 0 12px;
3843
cursor: text;
3944
}
4045

41-
.ide-updater-dialog .changelog-container a {
46+
.dialogContent.ide-updater-dialog
47+
.ide-updater-dialog--content
48+
.ide-updater-dialog--new-version-text
49+
.dialogRow.changelog-container {
50+
align-items: flex-start;
51+
border: 1px solid var(--theia-editorWidget-border);
52+
border-radius: 2px;
53+
overflow: auto;
54+
max-height: 180px;
55+
}
56+
57+
.ide-updater-dialog .changelog a {
4258
color: var(--theia-textLink-foreground);
4359
}
4460

45-
.ide-updater-dialog .changelog-container a:hover {
61+
.ide-updater-dialog .changelog a:hover {
4662
text-decoration: underline;
4763
cursor: pointer;
4864
}
4965

50-
.ide-updater-dialog .changelog-container code {
66+
.ide-updater-dialog .changelog code {
5167
background: var(--theia-textBlockQuote-background);
5268
border-radius: 2px;
5369
padding: 0 2px;
5470
}
5571

56-
.ide-updater-dialog .changelog-container a code {
72+
.ide-updater-dialog .changelog a code {
5773
color: var(--theia-textLink-foreground);
5874
}
5975

@@ -77,3 +93,14 @@
7793
.ide-updater-dialog .buttons-container .push {
7894
margin-right: auto;
7995
}
96+
97+
.ide-updater-dialog--content {
98+
max-height: 100%;
99+
overflow: hidden;
100+
display: flex;
101+
}
102+
103+
#ide-updater-dialog-container .skip-version-button {
104+
margin-left: 79px;
105+
margin-right: auto;
106+
}

‎arduino-ide-extension/src/browser/style/settings-dialog.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
display: flex;
2222
align-items: center;
2323
white-space: nowrap;
24+
flex-wrap: wrap;
2425
}
2526

2627
.arduino-settings-dialog .with-margin {

‎arduino-ide-extension/src/browser/style/settings-step-input.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
display: none;
1313
flex-direction: column;
1414
position: absolute;
15-
right: 0px;
15+
right: 14px;
1616
top: 50%;
1717
transform: translate(0px, -50%);
1818
height: calc(100% - 4px);

‎arduino-ide-extension/src/common/protocol/ide-updater.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ export interface IDEUpdater extends JsonRpcServer<IDEUpdaterClient> {
5656

5757
export const IDEUpdaterClient = Symbol('IDEUpdaterClient');
5858
export interface IDEUpdaterClient {
59-
onError: Event<Error>;
60-
onCheckingForUpdate: Event<void>;
61-
onUpdateAvailable: Event<UpdateInfo>;
62-
onUpdateNotAvailable: Event<UpdateInfo>;
63-
onDownloadProgressChanged: Event<ProgressInfo>;
64-
onDownloadFinished: Event<UpdateInfo>;
65-
notifyError(message: Error): void;
66-
notifyCheckingForUpdate(message: void): void;
67-
notifyUpdateAvailable(message: UpdateInfo): void;
68-
notifyUpdateNotAvailable(message: UpdateInfo): void;
59+
onUpdaterDidFail: Event<Error>;
60+
onUpdaterDidCheckForUpdate: Event<void>;
61+
onUpdaterDidFindUpdateAvailable: Event<UpdateInfo>;
62+
onUpdaterDidNotFindUpdateAvailable: Event<UpdateInfo>;
63+
onDownloadProgressDidChange: Event<ProgressInfo>;
64+
onDownloadDidFinish: Event<UpdateInfo>;
65+
notifyUpdaterFailed(message: Error): void;
66+
notifyCheckedForUpdate(message: void): void;
67+
notifyUpdateAvailableFound(message: UpdateInfo): void;
68+
notifyUpdateAvailableNotFound(message: UpdateInfo): void;
6969
notifyDownloadProgressChanged(message: ProgressInfo): void;
7070
notifyDownloadFinished(message: UpdateInfo): void;
7171
}

‎arduino-ide-extension/src/electron-main/ide-updater/ide-updater-impl.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ export class IDEUpdaterImpl implements IDEUpdater {
1919

2020
constructor() {
2121
this.updater.on('checking-for-update', (e) => {
22-
this.clients.forEach((c) => c.notifyCheckingForUpdate(e));
22+
this.clients.forEach((c) => c.notifyCheckedForUpdate(e));
2323
});
2424
this.updater.on('update-available', (e) => {
25-
this.clients.forEach((c) => c.notifyUpdateAvailable(e));
25+
this.clients.forEach((c) => c.notifyUpdateAvailableFound(e));
2626
});
2727
this.updater.on('update-not-available', (e) => {
28-
this.clients.forEach((c) => c.notifyUpdateNotAvailable(e));
28+
this.clients.forEach((c) => c.notifyUpdateAvailableNotFound(e));
2929
});
3030
this.updater.on('download-progress', (e) => {
3131
this.clients.forEach((c) => c.notifyDownloadProgressChanged(e));
@@ -34,7 +34,7 @@ export class IDEUpdaterImpl implements IDEUpdater {
3434
this.clients.forEach((c) => c.notifyDownloadFinished(e));
3535
});
3636
this.updater.on('error', (e) => {
37-
this.clients.forEach((c) => c.notifyError(e));
37+
this.clients.forEach((c) => c.notifyUpdaterFailed(e));
3838
});
3939
}
4040

@@ -58,10 +58,8 @@ export class IDEUpdaterImpl implements IDEUpdater {
5858
this.isAlreadyChecked = true;
5959
}
6060

61-
const {
62-
updateInfo,
63-
cancellationToken,
64-
} = await this.updater.checkForUpdates();
61+
const { updateInfo, cancellationToken } =
62+
await this.updater.checkForUpdates();
6563

6664
this.cancellationToken = cancellationToken;
6765
if (this.updater.currentVersion.compare(updateInfo.version) === -1) {
@@ -104,7 +102,7 @@ export class IDEUpdaterImpl implements IDEUpdater {
104102
await this.updater.downloadUpdate(this.cancellationToken);
105103
} catch (e) {
106104
if (e.message === 'cancelled') return;
107-
this.clients.forEach((c) => c.notifyError(e));
105+
this.clients.forEach((c) => c.notifyUpdaterFailed(e));
108106
}
109107
}
110108

‎i18n/en.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
},
77
"board": {
88
"board": "Board{0}",
9+
"boardConfigDialogTitle": "Select Other Board and Port",
910
"boardInfo": "Board Info",
1011
"configDialog1": "Select both a Board and a Port if you want to upload a sketch.",
1112
"configDialog2": "If you only select a Board you will be able to compile, but not to upload your sketch.",
12-
"configDialogTitle": "Select Other Board & Port",
1313
"couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?",
1414
"disconnected": "Disconnected",
1515
"getBoardInfo": "Get Board Info",
@@ -107,7 +107,6 @@
107107
"offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.",
108108
"oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
109109
"processing": "Processing",
110-
"selectBoard": "Select Board",
111110
"selectedOn": "on {0}",
112111
"serialMonitor": "Serial Monitor",
113112
"unknown": "Unknown"

0 commit comments

Comments
 (0)
Please sign in to comment.