Skip to content

Commit 14e3476

Browse files
author
Akos Kitta
committed
feat: new UX for the boards/library manager widgets
Closes #19 Closes #781 Closes #1591 Closes #1607 Closes #1697 Closes #1707 Closes #1924 Closes #1941 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 7721350 commit 14e3476

29 files changed

+1372
-480
lines changed

arduino-ide-extension/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
"@types/p-queue": "^2.3.1",
5959
"@types/ps-tree": "^1.1.0",
6060
"@types/react-tabs": "^2.3.2",
61-
"@types/react-virtualized": "^9.21.21",
6261
"@types/temp": "^0.8.34",
6362
"@types/which": "^1.3.1",
6463
"@vscode/debugprotocol": "^1.51.0",
@@ -95,7 +94,6 @@
9594
"react-perfect-scrollbar": "^1.5.8",
9695
"react-select": "^5.6.0",
9796
"react-tabs": "^3.1.2",
98-
"react-virtualized": "^9.22.3",
9997
"react-window": "^1.8.6",
10098
"semver": "^7.3.2",
10199
"string-natural-compare": "^2.0.3",

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browse
7979
import { ProblemManager } from './theia/markers/problem-manager';
8080
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
8181
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
82-
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
82+
import {
83+
ArduinoComponentContextMenuRenderer,
84+
ListItemRenderer,
85+
} from './widgets/component-list/list-item-renderer';
8386
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
8487

8588
import {
@@ -1021,4 +1024,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
10211024

10221025
bind(SidebarBottomMenuWidget).toSelf();
10231026
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
1027+
1028+
bind(ArduinoComponentContextMenuRenderer).toSelf().inSingletonScope();
10241029
});

arduino-ide-extension/src/browser/boards/boards-auto-installer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
174174
// CLI returns the packages already sorted with the deprecated ones at the end of the list
175175
// in order to ensure the new ones are preferred
176176
const candidates = packagesForBoard.filter(
177-
({ installable, installedVersion }) => installable && !installedVersion
177+
({ installedVersion }) => !installedVersion
178178
);
179179

180180
return candidates[0];

arduino-ide-extension/src/browser/contributions/examples.ts

Lines changed: 91 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as PQueue from 'p-queue';
22
import { inject, injectable } from '@theia/core/shared/inversify';
3-
import { CommandHandler } from '@theia/core/lib/common/command';
3+
import { CommandHandler, CommandService } from '@theia/core/lib/common/command';
44
import {
55
MenuPath,
66
CompositeMenuNode,
@@ -11,7 +11,11 @@ import {
1111
DisposableCollection,
1212
} from '@theia/core/lib/common/disposable';
1313
import { OpenSketch } from './open-sketch';
14-
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
14+
import {
15+
ArduinoMenus,
16+
examplesLabel,
17+
PlaceholderMenuNode,
18+
} from '../menu/arduino-menus';
1519
import { BoardsServiceProvider } from '../boards/boards-service-provider';
1620
import { ExamplesService } from '../../common/protocol/examples-service';
1721
import {
@@ -25,11 +29,73 @@ import {
2529
SketchRef,
2630
SketchContainer,
2731
SketchesError,
28-
Sketch,
2932
CoreService,
33+
SketchesService,
34+
Sketch,
3035
} from '../../common/protocol';
31-
import { nls } from '@theia/core/lib/common';
36+
import { nls } from '@theia/core/lib/common/nls';
3237
import { unregisterSubmenu } from '../menu/arduino-menus';
38+
import { MaybePromise } from '@theia/core/lib/common/types';
39+
import { ApplicationError } from '@theia/core/lib/common/application-error';
40+
41+
/**
42+
* Creates a cloned copy of the example sketch and opens it in a new window.
43+
*/
44+
export async function openClonedExample(
45+
uri: string,
46+
services: {
47+
sketchesService: SketchesService;
48+
commandService: CommandService;
49+
},
50+
onError: {
51+
onDidFailClone?: (
52+
err: ApplicationError<
53+
number,
54+
{
55+
uri: string;
56+
}
57+
>,
58+
uri: string
59+
) => MaybePromise<unknown>;
60+
onDidFailOpen?: (
61+
err: ApplicationError<
62+
number,
63+
{
64+
uri: string;
65+
}
66+
>,
67+
sketch: Sketch
68+
) => MaybePromise<unknown>;
69+
} = {}
70+
): Promise<void> {
71+
const { sketchesService, commandService } = services;
72+
const { onDidFailClone, onDidFailOpen } = onError;
73+
try {
74+
const sketch = await sketchesService.cloneExample(uri);
75+
try {
76+
await commandService.executeCommand(
77+
OpenSketch.Commands.OPEN_SKETCH.id,
78+
sketch
79+
);
80+
} catch (openError) {
81+
if (SketchesError.NotFound.is(openError)) {
82+
if (onDidFailOpen) {
83+
await onDidFailOpen(openError, sketch);
84+
return;
85+
}
86+
}
87+
throw openError;
88+
}
89+
} catch (cloneError) {
90+
if (SketchesError.NotFound.is(cloneError)) {
91+
if (onDidFailClone) {
92+
await onDidFailClone(cloneError, uri);
93+
return;
94+
}
95+
}
96+
throw cloneError;
97+
}
98+
}
3399

34100
@injectable()
35101
export abstract class Examples extends SketchContribution {
@@ -94,7 +160,7 @@ export abstract class Examples extends SketchContribution {
94160
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
95161
registry.registerSubmenu(
96162
ArduinoMenus.FILE__EXAMPLES_SUBMENU,
97-
nls.localize('arduino/examples/menu', 'Examples'),
163+
examplesLabel,
98164
{
99165
order: '4',
100166
}
@@ -174,47 +240,33 @@ export abstract class Examples extends SketchContribution {
174240
}
175241

176242
protected createHandler(uri: string): CommandHandler {
243+
const forceRefresh = () =>
244+
this.update({
245+
board: this.boardsServiceClient.boardsConfig.selectedBoard,
246+
forceRefresh: true,
247+
});
177248
return {
178249
execute: async () => {
179-
const sketch = await this.clone(uri);
180-
if (sketch) {
181-
try {
182-
return this.commandService.executeCommand(
183-
OpenSketch.Commands.OPEN_SKETCH.id,
184-
sketch
185-
);
186-
} catch (err) {
187-
if (SketchesError.NotFound.is(err)) {
250+
await openClonedExample(
251+
uri,
252+
{
253+
sketchesService: this.sketchesService,
254+
commandService: this.commandRegistry,
255+
},
256+
{
257+
onDidFailClone: () => {
188258
// Do not toast the error message. It's handled by the `Open Sketch` command.
189-
this.update({
190-
board: this.boardsServiceClient.boardsConfig.selectedBoard,
191-
forceRefresh: true,
192-
});
193-
} else {
194-
throw err;
195-
}
259+
forceRefresh();
260+
},
261+
onDidFailOpen: (err) => {
262+
this.messageService.error(err.message);
263+
forceRefresh();
264+
},
196265
}
197-
}
266+
);
198267
},
199268
};
200269
}
201-
202-
private async clone(uri: string): Promise<Sketch | undefined> {
203-
try {
204-
const sketch = await this.sketchesService.cloneExample(uri);
205-
return sketch;
206-
} catch (err) {
207-
if (SketchesError.NotFound.is(err)) {
208-
this.messageService.error(err.message);
209-
this.update({
210-
board: this.boardsServiceClient.boardsConfig.selectedBoard,
211-
forceRefresh: true,
212-
});
213-
} else {
214-
throw err;
215-
}
216-
}
217-
}
218270
}
219271

220272
@injectable()

arduino-ide-extension/src/browser/library/library-list-widget.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
LibrarySearch,
1313
LibraryService,
1414
} from '../../common/protocol/library-service';
15-
import { ListWidget } from '../widgets/component-list/list-widget';
15+
import {
16+
ListWidget,
17+
UserAbortError,
18+
} from '../widgets/component-list/list-widget';
1619
import { Installable } from '../../common/protocol';
1720
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
1821
import { nls } from '@theia/core/lib/common';
@@ -141,6 +144,8 @@ export class LibraryListWidget extends ListWidget<
141144
// All
142145
installDependencies = true;
143146
}
147+
} else {
148+
throw new UserAbortError();
144149
}
145150
} else {
146151
// The lib does not have any dependencies.
@@ -235,6 +240,21 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
235240
this.response = 0;
236241
super.handleEnter(event);
237242
}
243+
244+
protected override onAfterAttach(message: Message): void {
245+
super.onAfterAttach(message);
246+
let buttonToFocus: HTMLButtonElement | undefined = undefined;
247+
for (const child of Array.from(this.controlPanel.children)) {
248+
if (child instanceof HTMLButtonElement) {
249+
if (child.classList.contains('main')) {
250+
buttonToFocus = child;
251+
break;
252+
}
253+
buttonToFocus = child;
254+
}
255+
}
256+
buttonToFocus?.focus();
257+
}
238258
}
239259
export namespace MessageBoxDialog {
240260
export interface Options extends DialogProps {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { isOSX } from '@theia/core/lib/common/os';
21
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
32
import {
43
MAIN_MENU_BAR,
@@ -7,6 +6,8 @@ import {
76
MenuPath,
87
SubMenuOptions,
98
} from '@theia/core/lib/common/menu';
9+
import { nls } from '@theia/core/lib/common/nls';
10+
import { isOSX } from '@theia/core/lib/common/os';
1011

1112
export namespace ArduinoMenus {
1213
// Main menu
@@ -173,6 +174,17 @@ export namespace ArduinoMenus {
173174
'3_sign_out',
174175
];
175176

177+
// Context menu from the library and boards manager widget
178+
export const ARDUINO_COMPONENT__CONTEXT = ['arduino-component--context'];
179+
export const ARDUINO_COMPONENT__CONTEXT__INFO_GROUP = [
180+
...ARDUINO_COMPONENT__CONTEXT,
181+
'0_info',
182+
];
183+
export const ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP = [
184+
...ARDUINO_COMPONENT__CONTEXT,
185+
'1_action',
186+
];
187+
176188
// -- ROOT SSL CERTIFICATES
177189
export const ROOT_CERTIFICATES__CONTEXT = [
178190
'arduino-root-certificates--context',
@@ -230,3 +242,5 @@ export class PlaceholderMenuNode implements MenuNode {
230242
return [...this.menuPath, 'placeholder'].join('-');
231243
}
232244
}
245+
246+
export const examplesLabel = nls.localize('arduino/examples/menu', 'Examples');

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
165165
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
166166
display: flex;
167167
gap: 10px;
168-
height: 28px;
168+
height: var(--arduino-button-height);
169169
margin: 0 4px;
170170
overflow: hidden;
171171
padding: 0 10px;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
min-width: 424px;
1414
max-height: 560px;
15-
padding: 0 28px;
15+
padding: 0 var(--arduino-button-height);
1616
}
1717

1818
.p-Widget.dialogOverlay .dialogBlock .dialogTitle {
@@ -35,15 +35,15 @@
3535
}
3636

3737
.p-Widget.dialogOverlay .dialogBlock .dialogContent > input {
38-
margin-bottom: 28px;
38+
margin-bottom: var(--arduino-button-height);
3939
}
4040

4141
.p-Widget.dialogOverlay .dialogBlock .dialogContent > div {
4242
padding: 0 0 12px;
4343
}
4444

4545
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection {
46-
margin-top: 28px;
46+
margin-top: var(--arduino-button-height);
4747
}
4848
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection:first-child {
4949
margin-top: 0;

arduino-ide-extension/src/browser/style/fonts.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
@font-face {
22
font-family: 'Open Sans';
3+
src: url('fonts/OpenSans-Regular-webfont.woff') format('woff');
4+
}
5+
6+
@font-face {
7+
font-family: 'Open Sans Bold';
38
src: url('fonts/OpenSans-Bold-webfont.woff') format('woff');
49
}
510

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
}
1616

1717
.ide-updater-dialog--logo-container {
18-
margin-right: 28px;
18+
margin-right: var(--arduino-button-height);
1919
}
2020

2121
.ide-updater-dialog--logo {
@@ -76,7 +76,7 @@
7676
.ide-updater-dialog .buttons-container {
7777
display: flex;
7878
justify-content: flex-end;
79-
margin-top: 28px;
79+
margin-top: var(--arduino-button-height);
8080
}
8181

8282
.ide-updater-dialog .buttons-container a.theia-button {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy