Skip to content

Commit bc4f166

Browse files
committed
feat(tools): custom keybinds and layer independent tool keybinds
1 parent 9f79ce1 commit bc4f166

File tree

3 files changed

+107
-8
lines changed

3 files changed

+107
-8
lines changed

src/ui/default_viewer_setup.ts

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,124 @@
1414
* limitations under the License.
1515
*/
1616

17+
import type { UserLayer, UserLayerConstructor } from "#src/layer/index.js";
18+
import { layerTypes } from "#src/layer/index.js";
1719
import { StatusMessage } from "#src/status.js";
1820
import {
1921
bindDefaultCopyHandler,
2022
bindDefaultPasteHandler,
2123
} from "#src/ui/default_clipboard_handling.js";
2224
import { setDefaultInputEventBindings } from "#src/ui/default_input_event_bindings.js";
2325
import { makeDefaultViewer } from "#src/ui/default_viewer.js";
24-
import type { MinimalViewerOptions } from "#src/ui/minimal_viewer.js";
2526
import { bindTitle } from "#src/ui/title.js";
27+
import type { Tool } from "#src/ui/tool.js";
28+
import { restoreTool } from "#src/ui/tool.js";
2629
import { UrlHashBinding } from "#src/ui/url_hash_binding.js";
30+
import {
31+
verifyObject,
32+
verifyObjectProperty,
33+
verifyString,
34+
} from "#src/util/json.js";
2735

2836
declare let NEUROGLANCER_DEFAULT_STATE_FRAGMENT: string | undefined;
2937

38+
type CustomToolBinding = {
39+
layer: string;
40+
tool: unknown;
41+
provider?: string;
42+
};
43+
44+
type CustomBindings = {
45+
[key: string]: CustomToolBinding | string | boolean;
46+
};
47+
48+
declare const CUSTOM_BINDINGS: CustomBindings | undefined;
49+
export const hasCustomBindings =
50+
typeof CUSTOM_BINDINGS !== "undefined" &&
51+
Object.keys(CUSTOM_BINDINGS).length > 0;
52+
3053
/**
3154
* Sets up the default neuroglancer viewer.
3255
*/
33-
export function setupDefaultViewer(options?: Partial<MinimalViewerOptions>) {
34-
const viewer = ((<any>window).viewer = makeDefaultViewer(options));
56+
export function setupDefaultViewer() {
57+
const viewer = ((<any>window).viewer = makeDefaultViewer());
3558
setDefaultInputEventBindings(viewer.inputEventBindings);
3659

60+
const bindNonLayerSpecificTool = (
61+
obj: unknown,
62+
toolKey: string,
63+
desiredLayerType: UserLayerConstructor,
64+
desiredProvider?: string,
65+
) => {
66+
let previousTool: Tool<object> | undefined;
67+
let previousLayer: UserLayer | undefined;
68+
if (typeof obj === "string") {
69+
obj = { type: obj };
70+
}
71+
verifyObject(obj);
72+
const type = verifyObjectProperty(obj, "type", verifyString);
73+
viewer.bindAction(`tool-${type}`, () => {
74+
const acceptableLayers = viewer.layerManager.managedLayers.filter(
75+
(managedLayer) => {
76+
const correctLayerType =
77+
managedLayer.layer instanceof desiredLayerType;
78+
if (desiredProvider && correctLayerType) {
79+
for (const dataSource of managedLayer.layer?.dataSources || []) {
80+
const protocol = viewer.dataSourceProvider.getProvider(
81+
dataSource.spec.url,
82+
)[2];
83+
if (protocol === desiredProvider) {
84+
return true;
85+
}
86+
}
87+
return false;
88+
} else {
89+
return correctLayerType;
90+
}
91+
},
92+
);
93+
if (acceptableLayers.length > 0) {
94+
const firstLayer = acceptableLayers[0].layer;
95+
if (firstLayer) {
96+
if (firstLayer !== previousLayer) {
97+
previousTool = restoreTool(firstLayer, obj);
98+
previousLayer = firstLayer;
99+
}
100+
if (previousTool) {
101+
viewer.activateTool(toolKey, previousTool);
102+
}
103+
}
104+
}
105+
});
106+
};
107+
108+
if (hasCustomBindings) {
109+
for (const [key, val] of Object.entries(CUSTOM_BINDINGS!)) {
110+
if (typeof val === "string") {
111+
viewer.inputEventBindings.global.set(key, val);
112+
} else if (typeof val === "boolean") {
113+
if (!val) {
114+
viewer.inputEventBindings.global.delete(key);
115+
viewer.inputEventBindings.global.parents.map((parent) =>
116+
parent.delete(key),
117+
);
118+
}
119+
} else {
120+
viewer.inputEventBindings.global.set(key, `tool-${val.tool}`);
121+
const layerConstructor = layerTypes.get(val.layer);
122+
if (layerConstructor) {
123+
const toolKey = key.charAt(key.length - 1).toUpperCase();
124+
bindNonLayerSpecificTool(
125+
val.tool,
126+
toolKey,
127+
layerConstructor,
128+
val.provider,
129+
);
130+
}
131+
}
132+
}
133+
}
134+
37135
const hashBinding = viewer.registerDisposer(
38136
new UrlHashBinding(
39137
viewer.state,

src/ui/tool.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,15 +318,15 @@ export class GlobalToolBinder extends RefCounted {
318318
this.changed.dispatch();
319319
}
320320

321-
activate(key: string): Borrowed<Tool> | undefined {
322-
const tool = this.get(key);
321+
activate(key: string, tool?: Tool<object>): Borrowed<Tool> | undefined {
322+
tool = tool || this.get(key);
323323
if (tool === undefined) {
324324
this.deactivate_();
325325
return;
326326
}
327327
this.debounceDeactivate.cancel();
328328
const activeTool = this.activeTool_;
329-
if (tool === activeTool?.tool) {
329+
if (tool.toJSON() === activeTool?.tool.toJSON()) {
330330
if (tool.toggle) {
331331
this.deactivate_();
332332
}

src/viewer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import { SelectionDetailsPanel } from "#src/ui/selection_details.js";
9393
import { SidePanelManager } from "#src/ui/side_panel.js";
9494
import { StateEditorDialog } from "#src/ui/state_editor.js";
9595
import { StatisticsDisplayState, StatisticsPanel } from "#src/ui/statistics.js";
96+
import type { Tool } from "#src/ui/tool.js";
9697
import { GlobalToolBinder, LocalToolBinder } from "#src/ui/tool.js";
9798
import {
9899
ViewerSettingsPanel,
@@ -1124,8 +1125,8 @@ export class Viewer extends RefCounted implements ViewerState {
11241125
new LocalToolBinder(this, this.globalToolBinder),
11251126
);
11261127

1127-
activateTool(uppercase: string) {
1128-
this.globalToolBinder.activate(uppercase);
1128+
activateTool(key: string, tool?: Tool<object>) {
1129+
this.globalToolBinder.activate(key, tool);
11291130
}
11301131

11311132
editJsonState() {

0 commit comments

Comments
 (0)