Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 080c396

Browse files
committed
Sketchbook explorer
1 parent 51d4dc6 commit 080c396

File tree

8 files changed

+211
-18
lines changed

8 files changed

+211
-18
lines changed

‎arduino-ide-extension/src/browser/data/arduino.color-theme.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@
8181
],
8282
"colors": {
8383
"list.highlightForeground": "#005c5f",
84-
"list.activeSelectionBackground": "#005c5f",
84+
"list.activeSelectionForeground": "#424242",
85+
"list.activeSelectionBackground": "#DAE3E3",
86+
"list.inactiveSelectionForeground": "#424242",
87+
"list.inactiveSelectionBackground": "#DAE3E3",
88+
"list.hoverBackground": "#ECF1F1",
8589
"progressBar.background": "#005c5f",
8690
"editor.background": "#ffffff",
8791
"editorCursor.foreground": "#434f54",
Lines changed: 10 additions & 0 deletions
Loading[フレーム]

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@
1818
.sketchbook-trees-container {
1919
height: 99%;
2020
}
21+
22+
.sketchbook-tree__opts {
23+
background: url('./sketchbook-opts-icon.svg') center center no-repeat;
24+
width: var(--theia-icon-size);
25+
height: var(--theia-icon-size);
26+
}
27+
28+
.theia-TreeNodeSegmentGrow {
29+
flex: 1;
30+
}

‎arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-commands.ts‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@ import { Command } from '@theia/core/lib/common/command';
22

33
export namespace SketchbookCommands {
44

5-
export const OPEN: Command = {
6-
id: 'arduino-sketchbook--open-sketch',
7-
label: 'Open Sketch',
8-
iconClass: 'fa fa-play-circle'
9-
};
10-
115
export const OPEN_NEW_WINDOW: Command = {
126
id: 'arduino-sketchbook--open-sketch-new-window',
137
label: 'Open Sketch in New Window',
14-
iconClass: 'fa fa-check'
8+
};
9+
10+
export const REVEAL_IN_FINDER: Command = {
11+
id: 'arduino-sketchbook--reveal-in-finder',
12+
label: 'Open Folder',
13+
};
14+
15+
export const OPEN_SKETCHBOOK_CONTEXT_MENU: Command = {
16+
id: 'arduino-sketchbook--open-sketch-context-menu',
17+
label: 'Contextual menu',
18+
iconClass: 'sketchbook-tree__opts'
1519
};
1620

1721
}

‎arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-tree-model.ts‎

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { inject, injectable } from 'inversify';
22
import URI from '@theia/core/lib/common/uri';
3-
import { FileTreeModel } from '@theia/filesystem/lib/browser';
3+
import { FileNode,FileTreeModel } from '@theia/filesystem/lib/browser';
44
import { FileService } from '@theia/filesystem/lib/browser/file-service';
55
import { ConfigService } from '../../../common/protocol';
66
import { SketchbookTree } from './sketchbook-tree';
77
import { ArduinoPreferences } from '../../arduino-preferences';
8+
import { SelectableTreeNode, TreeNode } from '@theia/core/lib/browser/tree';
9+
import { SketchbookCommands } from './sketchbook-commands';
10+
import { OpenerService, open } from '@theia/core/lib/browser';
11+
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
12+
import { CommandRegistry } from '@theia/core/lib/common/command';
813

914
@injectable()
1015
export class SketchbookTreeModel extends FileTreeModel {
@@ -15,14 +20,94 @@ export class SketchbookTreeModel extends FileTreeModel {
1520
@inject(ArduinoPreferences)
1621
protected readonly arduinoPreferences: ArduinoPreferences;
1722

23+
@inject(CommandRegistry)
24+
protected readonly commandRegistry: CommandRegistry;
25+
1826
@inject(ConfigService)
1927
protected readonly configService: ConfigService;
2028

29+
@inject(OpenerService)
30+
protected readonly openerService: OpenerService;
31+
32+
@inject(SketchesServiceClientImpl)
33+
protected readonly sketchServiceClient: SketchesServiceClientImpl;
34+
2135
async updateRoot(): Promise<void> {
2236
const config = await this.configService.getConfiguration();
2337
const fileStat = await this.fileService.resolve(new URI(config.sketchDirUri));
2438
const showAllFiles = this.arduinoPreferences['arduino.sketchbook.showAllFiles'];
2539
this.tree.root = SketchbookTree.RootNode.create(fileStat, showAllFiles);
2640
}
2741

42+
async openNode(raw?: TreeNode | undefined) {
43+
const node = raw || this.selectedNodes[0];
44+
45+
if (!node) { return }
46+
47+
// if it's a sketch dir, or a file from another sketch, open in new window
48+
if (!(await this.isFileInsideCurrentSketch(node))) {
49+
const sketchRoot = this.recursivelyFindSketchRoot(node);
50+
if (sketchRoot) {
51+
this.commandRegistry.executeCommand(SketchbookCommands.OPEN_NEW_WINDOW.id, { node: sketchRoot })
52+
}
53+
return;
54+
}
55+
56+
if (node) {
57+
this.doOpenNode(node);
58+
this.onOpenNodeEmitter.fire(node);
59+
}
60+
}
61+
62+
// selectNode gets called when the user single-clicks on an item
63+
// when this happens, we want to open the file if it belongs to the currently open sketch
64+
async selectNode(node: Readonly<SelectableTreeNode>): Promise<void> {
65+
66+
if (await (this.isFileInsideCurrentSketch(node))) {
67+
super.selectNode(node);
68+
this.doOpenNode(node);
69+
this.onOpenNodeEmitter.fire(node);
70+
}
71+
}
72+
73+
protected async doOpenNode(node: TreeNode): Promise<void> {
74+
75+
if (node.visible === false) {
76+
return;
77+
} else if (FileNode.is(node)) {
78+
open(this.openerService, node.uri);
79+
} else {
80+
super.doOpenNode(node);
81+
}
82+
}
83+
84+
private async isFileInsideCurrentSketch(node: TreeNode): Promise<boolean> {
85+
86+
// it's a directory, not a file
87+
if (!FileNode.is(node)) {
88+
return false;
89+
}
90+
91+
// check if the node is a file that belongs to another sketch
92+
const sketch = await this.sketchServiceClient.currentSketch();
93+
if (sketch && node.uri.toString().indexOf(sketch.uri) !== 0) {
94+
return false;
95+
}
96+
return true;
97+
}
98+
99+
private recursivelyFindSketchRoot(node: TreeNode): TreeNode | false {
100+
101+
if (node && SketchbookTree.SketchDirNode.is(node)) {
102+
return node;
103+
}
104+
105+
if (node && node.parent) {
106+
return this.recursivelyFindSketchRoot(node.parent);
107+
}
108+
109+
// can't find a root, return false
110+
return false;
111+
}
112+
28113
}

‎arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-tree-widget.tsx‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ContextMenuRenderer } from '@theia/core/lib/browser/context-menu-render
99
import { SketchbookTree } from './sketchbook-tree';
1010
import { SketchbookTreeModel } from './sketchbook-tree-model';
1111
import { ArduinoPreferences } from '../../arduino-preferences';
12+
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
1213

1314
@injectable()
1415
export class SketchbookTreeWidget extends FileTreeWidget {
@@ -19,6 +20,11 @@ export class SketchbookTreeWidget extends FileTreeWidget {
1920
@inject(ArduinoPreferences)
2021
protected readonly arduinoPreferences: ArduinoPreferences;
2122

23+
@inject(SketchesServiceClientImpl)
24+
protected readonly sketchServiceClient: SketchesServiceClientImpl;
25+
26+
private currentSketchUri = '';
27+
2228
constructor(
2329
@inject(TreeProps) readonly props: TreeProps,
2430
@inject(SketchbookTreeModel) readonly model: SketchbookTreeModel,
@@ -41,6 +47,9 @@ export class SketchbookTreeWidget extends FileTreeWidget {
4147
}
4248
}));
4349
this.updateModel();
50+
// cache the current open sketch uri
51+
const currentSketch = await this.sketchServiceClient.currentSketch();
52+
this.currentSketchUri = currentSketch && currentSketch.uri || '';
4453
}
4554

4655
async updateModel(): Promise<void> {
@@ -80,7 +89,7 @@ export class SketchbookTreeWidget extends FileTreeWidget {
8089
}
8190

8291
protected renderInlineCommands(node: TreeNode, props: NodeProps): React.ReactNode {
83-
if (SketchbookTree.SketchDirNode.is(node) && node.commands && node.id === this.hoveredNodeId) {
92+
if (SketchbookTree.SketchDirNode.is(node) && (node.commands && node.id === this.hoveredNodeId||this.currentSketchUri===node?.uri.toString())) {
8493
return Array.from(new Set(node.commands)).map(command => this.renderInlineCommand(command.id, node));
8594
}
8695
return undefined;
@@ -99,7 +108,7 @@ export class SketchbookTreeWidget extends FileTreeWidget {
99108
onClick={event => {
100109
event.preventDefault();
101110
event.stopPropagation();
102-
this.commandRegistry.executeCommand(commandId, Object.assign(args, { event: event.nativeEvent }))
111+
this.commandRegistry.executeCommand(commandId, Object.assign(args, { event: event.nativeEvent }));
103112
}}
104113
/>;
105114
}

‎arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-tree.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class SketchbookTree extends FileTree {
4141
if (DirNode.is(node)) {
4242
const sketch = await this.sketchesService.maybeLoadSketch(node.uri.toString());
4343
if (sketch) {
44-
Object.assign(node, { type: 'sketch', commands: [SketchbookCommands.OPEN,SketchbookCommands.OPEN_NEW_WINDOW] });
44+
Object.assign(node, { type: 'sketch', commands: [SketchbookCommands.OPEN_SKETCHBOOK_CONTEXT_MENU] });
4545
if (!showAllFiles) {
4646
delete (node as any).expanded;
4747
}

‎arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-widget-contribution.ts‎

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { shell } from 'electron';
12
import { inject, injectable } from 'inversify';
23
import { CommandRegistry } from '@theia/core/lib/common/command';
34
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
@@ -7,10 +8,19 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
78
import { MainMenuManager } from '../../../common/main-menu-manager';
89
import { ArduinoPreferences } from '../../arduino-preferences';
910
import { SketchbookWidget } from './sketchbook-widget';
10-
import { ArduinoMenus } from '../../menu/arduino-menus';
11+
import { ArduinoMenus,PlaceholderMenuNode } from '../../menu/arduino-menus';
1112
import { SketchbookTree } from './sketchbook-tree';
1213
import { SketchbookCommands } from './sketchbook-commands';
1314
import { WorkspaceService } from '../../theia/workspace/workspace-service';
15+
import { ContextMenuRenderer, RenderContextMenuOptions } from '@theia/core/lib/browser';
16+
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
17+
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
18+
19+
20+
export const SKETCHBOOK__CONTEXT = ['arduino-sketchbook--context'];
21+
22+
// `Open Folder`, `Open in New Window`
23+
export const SKETCHBOOK__CONTEXT__MAIN_GROUP = [...SKETCHBOOK__CONTEXT, '0_main'];
1424

1525
@injectable()
1626
export class SketchbookWidgetContribution extends AbstractViewContribution<SketchbookWidget> implements FrontendApplicationContribution {
@@ -27,6 +37,17 @@ export class SketchbookWidgetContribution extends AbstractViewContribution<Sketc
2737
@inject(WorkspaceService)
2838
protected readonly workspaceService: WorkspaceService;
2939

40+
@inject(MenuModelRegistry)
41+
protected readonly menuRegistry: MenuModelRegistry;
42+
43+
@inject(SketchesServiceClientImpl)
44+
protected readonly sketchServiceClient: SketchesServiceClientImpl;
45+
46+
@inject(ContextMenuRenderer)
47+
protected readonly contextMenuRenderer: ContextMenuRenderer;
48+
49+
protected readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
50+
3051
constructor() {
3152
super({
3253
widgetId: 'arduino-sketchbook-widget',
@@ -64,16 +85,59 @@ export class SketchbookWidgetContribution extends AbstractViewContribution<Sketc
6485
isEnabled: () => this.arduinoPreferences['arduino.sketchbook.showAllFiles'],
6586
isVisible: () => this.arduinoPreferences['arduino.sketchbook.showAllFiles']
6687
});
67-
registry.registerCommand(SketchbookCommands.OPEN, {
68-
execute: arg => this.workspaceService.open(arg.node.uri, { preserveWindow: true }),
69-
isEnabled: arg => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
70-
isVisible: arg => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node)
71-
});
7288
registry.registerCommand(SketchbookCommands.OPEN_NEW_WINDOW, {
7389
execute: arg => this.workspaceService.open(arg.node.uri),
7490
isEnabled: arg => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
7591
isVisible: arg => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node)
7692
});
93+
94+
registry.registerCommand(SketchbookCommands.REVEAL_IN_FINDER, {
95+
execute: (arg) => {
96+
shell.openPath(arg.node.id);
97+
},
98+
isEnabled: (arg) => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
99+
isVisible: (arg) => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
100+
});
101+
102+
103+
registry.registerCommand(SketchbookCommands.OPEN_SKETCHBOOK_CONTEXT_MENU, {
104+
isEnabled: (arg) => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
105+
isVisible: (arg) => !!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
106+
execute: async (arg) => {
107+
// cleanup previous context menu entries
108+
this.toDisposeBeforeNewContextMenu.dispose();
109+
const container = arg.event.target;
110+
if (!container) {
111+
return;
112+
}
113+
114+
// disable the "open sketch" command for the current sketch.
115+
// otherwise make the command clickable
116+
const currentSketch = await this.sketchServiceClient.currentSketch();
117+
if (currentSketch && currentSketch.uri === arg.node.uri.toString()) {
118+
const placeholder = new PlaceholderMenuNode(SKETCHBOOK__CONTEXT__MAIN_GROUP, SketchbookCommands.OPEN_NEW_WINDOW.label!);
119+
this.menuRegistry.registerMenuNode(SKETCHBOOK__CONTEXT__MAIN_GROUP, placeholder);
120+
this.toDisposeBeforeNewContextMenu.push(Disposable.create(() => this.menuRegistry.unregisterMenuNode(placeholder.id)));
121+
} else {
122+
this.menuRegistry.registerMenuAction(SKETCHBOOK__CONTEXT__MAIN_GROUP, {
123+
commandId: SketchbookCommands.OPEN_NEW_WINDOW.id,
124+
label: SketchbookCommands.OPEN_NEW_WINDOW.label,
125+
});
126+
this.toDisposeBeforeNewContextMenu.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction(SketchbookCommands.OPEN_NEW_WINDOW)));
127+
}
128+
129+
130+
const options: RenderContextMenuOptions = {
131+
menuPath: SKETCHBOOK__CONTEXT,
132+
anchor: {
133+
x: container.getBoundingClientRect().left,
134+
y: container.getBoundingClientRect().top + container.offsetHeight
135+
},
136+
args: arg
137+
}
138+
this.contextMenuRenderer.render(options);
139+
}
140+
});
77141
}
78142

79143
registerMenus(registry: MenuModelRegistry): void {
@@ -88,6 +152,13 @@ export class SketchbookWidgetContribution extends AbstractViewContribution<Sketc
88152
label: 'Hide sketch files',
89153
order: '2'
90154
});
155+
156+
registry.registerMenuAction(SKETCHBOOK__CONTEXT__MAIN_GROUP, {
157+
commandId: SketchbookCommands.REVEAL_IN_FINDER.id,
158+
label: SketchbookCommands.REVEAL_IN_FINDER.label,
159+
order: '0'
160+
});
161+
91162
}
92163

93164
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /