Skip to content

Commit 17af8e2

Browse files
authored
Improve Scene Preview ergonomics (#665)
* Rename Scene Preview "pinning" to "locking" * Add "refresh scene preview" command as button * Expose scene preview commands to command palette * Add openCurrentScene and openMainScript commands
1 parent f55d36e commit 17af8e2

File tree

2 files changed

+127
-66
lines changed

2 files changed

+127
-66
lines changed

package.json

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -149,19 +149,47 @@
149149
{
150150
"category": "Godot Tools",
151151
"command": "godotTools.scenePreview.refresh",
152-
"title": "Refresh Scene Preview"
152+
"title": "Refresh Scene Preview",
153+
"icon": {
154+
"light": "resources/godot_icons/light/Reload.svg",
155+
"dark": "resources/godot_icons/dark/Reload.svg"
156+
}
153157
},
154158
{
155159
"category": "Godot Tools",
156-
"command": "godotTools.scenePreview.pin",
157-
"title": "Pin Scene Preview",
158-
"icon": "resources/pin_off.svg"
160+
"command": "godotTools.scenePreview.openCurrentScene",
161+
"title": "Open the Scene Preview's current scene",
162+
"icon": {
163+
"light": "resources/godot_icons/light/PackedScene.svg",
164+
"dark": "resources/godot_icons/dark/PackedScene.svg"
165+
}
159166
},
160167
{
161168
"category": "Godot Tools",
162-
"command": "godotTools.scenePreview.unpin",
163-
"title": "Unpin Scene Preview",
164-
"icon": "resources/pin_on.svg"
169+
"command": "godotTools.scenePreview.openMainScript",
170+
"title": "Open the main script of the Scene Preview's current scene",
171+
"icon": {
172+
"light": "resources/godot_icons/light/Script.svg",
173+
"dark": "resources/godot_icons/dark/Script.svg"
174+
}
175+
},
176+
{
177+
"category": "Godot Tools",
178+
"command": "godotTools.scenePreview.lock",
179+
"title": "Lock Scene Preview",
180+
"icon": {
181+
"light": "resources/godot_icons/light/Unlock.svg",
182+
"dark": "resources/godot_icons/dark/Unlock.svg"
183+
}
184+
},
185+
{
186+
"category": "Godot Tools",
187+
"command": "godotTools.scenePreview.unlock",
188+
"title": "Unlock Scene Preview",
189+
"icon": {
190+
"light": "resources/godot_icons/light/Lock.svg",
191+
"dark": "resources/godot_icons/dark/Lock.svg"
192+
}
165193
},
166194
{
167195
"category": "Godot Tools",
@@ -628,10 +656,6 @@
628656
"command": "godotTools.listGodotClasses",
629657
"when": "godotTools.context.connectedToLSP"
630658
},
631-
{
632-
"command": "godotTools.scenePreview.refresh",
633-
"when": "false"
634-
},
635659
{
636660
"command": "godotTools.scenePreview.goToDefinition",
637661
"when": "false"
@@ -640,14 +664,6 @@
640664
"command": "godotTools.scenePreview.openDocumentation",
641665
"when": "false"
642666
},
643-
{
644-
"command": "godotTools.scenePreview.pin",
645-
"when": "false"
646-
},
647-
{
648-
"command": "godotTools.scenePreview.unpin",
649-
"when": "false"
650-
},
651667
{
652668
"command": "godotTools.scenePreview.copyNodePath",
653669
"when": "false"
@@ -693,14 +709,29 @@
693709
"group": "navigation"
694710
},
695711
{
696-
"command": "godotTools.scenePreview.pin",
697-
"when": "view == scenePreview && !godotTools.context.scenePreview.pinned",
698-
"group": "navigation"
712+
"command": "godotTools.scenePreview.lock",
713+
"when": "view == scenePreview && !godotTools.context.scenePreview.locked",
714+
"group": "navigation@1"
699715
},
700716
{
701-
"command": "godotTools.scenePreview.unpin",
702-
"when": "view == scenePreview && godotTools.context.scenePreview.pinned",
703-
"group": "navigation"
717+
"command": "godotTools.scenePreview.unlock",
718+
"when": "view == scenePreview && godotTools.context.scenePreview.locked",
719+
"group": "navigation@1"
720+
},
721+
{
722+
"command": "godotTools.scenePreview.refresh",
723+
"when": "view == scenePreview",
724+
"group": "navigation@2"
725+
},
726+
{
727+
"command": "godotTools.scenePreview.openMainScript",
728+
"when": "view == scenePreview",
729+
"group": "navigation@3"
730+
},
731+
{
732+
"command": "godotTools.scenePreview.openCurrentScene",
733+
"when": "view == scenePreview",
734+
"group": "navigation@4"
704735
}
705736
],
706737
"view/item/context": [

src/scene_tools/preview.ts

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import * as vscode from "vscode";
22
import {
3-
TreeDataProvider,
4-
TreeDragAndDropController,
5-
ExtensionContext,
3+
type TreeDataProvider,
4+
type TreeDragAndDropController,
5+
type ExtensionContext,
66
EventEmitter,
7-
Event,
8-
TreeView,
9-
ProviderResult,
10-
TreeItem,
7+
type Event,
8+
type TreeView,
9+
type ProviderResult,
10+
type TreeItem,
1111
TreeItemCollapsibleState,
1212
window,
1313
languages,
14-
Uri,
15-
CancellationToken,
16-
FileDecoration,
17-
DocumentDropEditProvider,
14+
type Uri,
15+
type CancellationToken,
16+
type FileDecoration,
17+
type DocumentDropEditProvider,
1818
workspace,
1919
} from "vscode";
20-
import * as fs from "fs";
20+
import * as fs from "node:fs";
2121
import {
2222
get_configuration,
2323
find_file,
@@ -28,15 +28,17 @@ import {
2828
make_docs_uri,
2929
} from "../utils";
3030
import { SceneParser } from "./parser";
31-
import { SceneNode, Scene } from "./types";
31+
import type { SceneNode, Scene } from "./types";
3232

3333
const log = createLogger("scenes.preview");
3434

35-
export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode>, DocumentDropEditProvider {
35+
export class ScenePreviewProvider
36+
implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode>, DocumentDropEditProvider
37+
{
3638
public dropMimeTypes = [];
3739
public dragMimeTypes = [];
3840
private tree: TreeView<SceneNode>;
39-
private scenePreviewPinned = false;
41+
private scenePreviewLocked = false;
4042
private currentScene = "";
4143
public parser = new SceneParser();
4244
public scene: Scene;
@@ -52,20 +54,22 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
5254
constructor(private context: ExtensionContext) {
5355
this.tree = vscode.window.createTreeView("scenePreview", {
5456
treeDataProvider: this,
55-
dragAndDropController: this
57+
dragAndDropController: this,
5658
});
5759

5860
const selector = [
5961
{ language: "csharp", scheme: "file" },
6062
{ language: "gdscript", scheme: "file" },
6163
];
6264
context.subscriptions.push(
63-
register_command("scenePreview.pin", this.pin_preview.bind(this)),
64-
register_command("scenePreview.unpin", this.unpin_preview.bind(this)),
65+
register_command("scenePreview.lock", this.lock_preview.bind(this)),
66+
register_command("scenePreview.unlock", this.unlock_preview.bind(this)),
6567
register_command("scenePreview.copyNodePath", this.copy_node_path.bind(this)),
6668
register_command("scenePreview.copyResourcePath", this.copy_resource_path.bind(this)),
6769
register_command("scenePreview.openScene", this.open_scene.bind(this)),
6870
register_command("scenePreview.openScript", this.open_script.bind(this)),
71+
register_command("scenePreview.openCurrentScene", this.open_current_scene.bind(this)),
72+
register_command("scenePreview.openCurrentScript", this.open_main_script.bind(this)),
6973
register_command("scenePreview.goToDefinition", this.go_to_definition.bind(this)),
7074
register_command("scenePreview.openDocumentation", this.open_documentation.bind(this)),
7175
register_command("scenePreview.refresh", this.refresh.bind(this)),
@@ -82,12 +86,21 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
8286
this.refresh();
8387
}
8488

85-
public handleDrag(source: readonly SceneNode[], data: vscode.DataTransfer, token: vscode.CancellationToken): void | Thenable<void> {
89+
public handleDrag(
90+
source: readonly SceneNode[],
91+
data: vscode.DataTransfer,
92+
token: vscode.CancellationToken,
93+
): void | Thenable<void> {
8694
data.set("godot/path", new vscode.DataTransferItem(source[0].relativePath));
8795
data.set("godot/class", new vscode.DataTransferItem(source[0].className));
8896
}
8997

90-
public provideDocumentDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): vscode.ProviderResult<vscode.DocumentDropEdit> {
98+
public provideDocumentDropEdits(
99+
document: vscode.TextDocument,
100+
position: vscode.Position,
101+
dataTransfer: vscode.DataTransfer,
102+
token: vscode.CancellationToken,
103+
): vscode.ProviderResult<vscode.DocumentDropEdit> {
91104
const path = dataTransfer.get("godot/path").value;
92105
const className = dataTransfer.get("godot/class").value;
93106

@@ -106,7 +119,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
106119
return;
107120
}
108121
setTimeout(async () => {
109-
if (uri.fsPath == this.currentScene) {
122+
if (uri.fsPath === this.currentScene) {
110123
this.refresh();
111124
} else {
112125
const document = await vscode.workspace.openTextDocument(uri);
@@ -116,7 +129,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
116129
}
117130

118131
public async refresh() {
119-
if (this.scenePreviewPinned) {
132+
if (this.scenePreviewLocked) {
120133
return;
121134
}
122135

@@ -128,22 +141,22 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
128141
if (!fileName.endsWith(".tscn")) {
129142
const searchName = fileName.replace(".gd", ".tscn").replace(".cs", ".tscn");
130143

131-
if (mode == "anyFolder") {
144+
if (mode === "anyFolder") {
132145
const relatedScene = await find_file(searchName);
133146
if (!relatedScene) {
134147
return;
135148
}
136149
fileName = relatedScene.fsPath;
137150
}
138151

139-
if (mode == "sameFolder") {
152+
if (mode === "sameFolder") {
140153
if (fs.existsSync(searchName)) {
141154
fileName = searchName;
142155
} else {
143156
return;
144157
}
145158
}
146-
if (mode == "off") {
159+
if (mode === "off") {
147160
return;
148161
}
149162
}
@@ -162,20 +175,20 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
162175
}
163176
}
164177

165-
private pin_preview() {
166-
this.scenePreviewPinned = true;
167-
set_context("scenePreview.pinned", true);
178+
private lock_preview() {
179+
this.scenePreviewLocked = true;
180+
set_context("scenePreview.locked", true);
168181
}
169182

170-
private unpin_preview() {
171-
this.scenePreviewPinned = false;
172-
set_context("scenePreview.pinned", false);
183+
private unlock_preview() {
184+
this.scenePreviewLocked = false;
185+
set_context("scenePreview.locked", false);
173186
this.refresh();
174187
}
175188

176189
private copy_node_path(item: SceneNode) {
177190
if (item.unique) {
178-
vscode.env.clipboard.writeText("%" + item.label);
191+
vscode.env.clipboard.writeText(`%${item.label}`);
179192
return;
180193
}
181194
vscode.env.clipboard.writeText(item.relativePath);
@@ -201,6 +214,26 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
201214
}
202215
}
203216

217+
private async open_current_scene() {
218+
if (this.currentScene) {
219+
const document = await vscode.workspace.openTextDocument(this.currentScene);
220+
vscode.window.showTextDocument(document);
221+
}
222+
}
223+
224+
private async open_main_script() {
225+
if (this.currentScene) {
226+
const root = this.scene.root;
227+
if (root?.hasScript) {
228+
const path = this.scene.externalResources[root.scriptId].path;
229+
const uri = await convert_resource_path_to_uri(path);
230+
if (uri) {
231+
vscode.window.showTextDocument(uri, { preview: true });
232+
}
233+
}
234+
}
235+
}
236+
204237
private async go_to_definition(item: SceneNode) {
205238
const document = await vscode.workspace.openTextDocument(this.currentScene);
206239
const start = document.positionAt(item.position);
@@ -216,7 +249,6 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
216249
private tree_selection_changed(event: vscode.TreeViewSelectionChangeEvent<SceneNode>) {
217250
// const item = event.selection[0];
218251
// log(item.body);
219-
220252
// const editor = vscode.window.activeTextEditor;
221253
// const range = editor.document.getText()
222254
// editor.revealRange(range)
@@ -226,12 +258,10 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
226258
if (!element) {
227259
if (!this.scene?.root) {
228260
return Promise.resolve([]);
229-
} else {
230-
return Promise.resolve([this.scene?.root]);
231261
}
232-
} else {
233-
return Promise.resolve(element.children);
262+
return Promise.resolve([this.scene?.root]);
234263
}
264+
return Promise.resolve(element.children);
235265
}
236266

237267
public getTreeItem(element: SceneNode): TreeItem | Thenable<TreeItem> {
@@ -254,13 +284,13 @@ class UniqueDecorationProvider implements vscode.FileDecorationProvider {
254284
return this.changeDecorationsEvent.event;
255285
}
256286

257-
constructor(private previewer: ScenePreviewProvider) { }
287+
constructor(private previewer: ScenePreviewProvider) {}
258288

259289
provideFileDecoration(uri: Uri, token: CancellationToken): FileDecoration | undefined {
260290
if (uri.scheme !== "godot") return undefined;
261291

262292
const node = this.previewer.scene?.nodes.get(uri.path);
263-
if (node && node.unique) {
293+
if (node?.unique) {
264294
return {
265295
badge: "%",
266296
};
@@ -274,13 +304,13 @@ class ScriptDecorationProvider implements vscode.FileDecorationProvider {
274304
return this.changeDecorationsEvent.event;
275305
}
276306

277-
constructor(private previewer: ScenePreviewProvider) { }
307+
constructor(private previewer: ScenePreviewProvider) {}
278308

279309
provideFileDecoration(uri: Uri, token: CancellationToken): FileDecoration | undefined {
280310
if (uri.scheme !== "godot") return undefined;
281311

282312
const node = this.previewer.scene?.nodes.get(uri.path);
283-
if (node && node.hasScript) {
313+
if (node?.hasScript) {
284314
return {
285315
badge: "S",
286316
};

0 commit comments

Comments
 (0)