Skip to content

Commit

Permalink
feat(tools): custom keybinds and layer independent tool keybinds
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisj committed Mar 5, 2024
1 parent e6be32c commit ba79676
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 8 deletions.
104 changes: 101 additions & 3 deletions src/ui/default_viewer_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,124 @@
* limitations under the License.
*/

import type { UserLayer, UserLayerConstructor } from "#src/layer/index.js";
import { layerTypes } from "#src/layer/index.js";
import { StatusMessage } from "#src/status.js";
import {
bindDefaultCopyHandler,
bindDefaultPasteHandler,
} from "#src/ui/default_clipboard_handling.js";
import { setDefaultInputEventBindings } from "#src/ui/default_input_event_bindings.js";
import { makeDefaultViewer } from "#src/ui/default_viewer.js";
import type { MinimalViewerOptions } from "#src/ui/minimal_viewer.js";
import { bindTitle } from "#src/ui/title.js";
import type { Tool } from "#src/ui/tool.js";
import { restoreTool } from "#src/ui/tool.js";
import { UrlHashBinding } from "#src/ui/url_hash_binding.js";
import {
verifyObject,
verifyObjectProperty,
verifyString,
} from "#src/util/json.js";

declare let NEUROGLANCER_DEFAULT_STATE_FRAGMENT: string | undefined;

type CustomToolBinding = {
layer: string;
tool: unknown;
provider?: string;
};

type CustomBindings = {
[key: string]: CustomToolBinding | string | boolean;
};

declare const CUSTOM_BINDINGS: CustomBindings | undefined;
export const hasCustomBindings =
typeof CUSTOM_BINDINGS !== "undefined" &&
Object.keys(CUSTOM_BINDINGS).length > 0;

/**
* Sets up the default neuroglancer viewer.
*/
export function setupDefaultViewer(options?: Partial<MinimalViewerOptions>) {
const viewer = ((<any>window).viewer = makeDefaultViewer(options));
export function setupDefaultViewer() {
const viewer = ((<any>window).viewer = makeDefaultViewer());
setDefaultInputEventBindings(viewer.inputEventBindings);

const bindNonLayerSpecificTool = (
obj: unknown,
toolKey: string,
desiredLayerType: UserLayerConstructor,
desiredProvider?: string,
) => {
let previousTool: Tool<object> | undefined;
let previousLayer: UserLayer | undefined;
if (typeof obj === "string") {
obj = { type: obj };
}
verifyObject(obj);
const type = verifyObjectProperty(obj, "type", verifyString);
viewer.bindAction(`tool-${type}`, () => {
const acceptableLayers = viewer.layerManager.managedLayers.filter(
(managedLayer) => {
const correctLayerType =
managedLayer.layer instanceof desiredLayerType;
if (desiredProvider && correctLayerType) {
for (const dataSource of managedLayer.layer?.dataSources || []) {
const protocol = viewer.dataSourceProvider.getProvider(
dataSource.spec.url,
)[2];
if (protocol === desiredProvider) {
return true;
}
}
return false;
} else {
return correctLayerType;
}
},
);
if (acceptableLayers.length > 0) {
const firstLayer = acceptableLayers[0].layer;
if (firstLayer) {
if (firstLayer !== previousLayer) {
previousTool = restoreTool(firstLayer, obj);
previousLayer = firstLayer;
}
if (previousTool) {
viewer.activateTool(toolKey, previousTool);
}
}
}
});
};

if (hasCustomBindings) {
for (const [key, val] of Object.entries(CUSTOM_BINDINGS!)) {
if (typeof val === "string") {
viewer.inputEventBindings.global.set(key, val);
} else if (typeof val === "boolean") {
if (!val) {
viewer.inputEventBindings.global.delete(key);
viewer.inputEventBindings.global.parents.map((parent) =>
parent.delete(key),
);
}
} else {
viewer.inputEventBindings.global.set(key, `tool-${val.tool}`);
const layerConstructor = layerTypes.get(val.layer);
if (layerConstructor) {
const toolKey = key.charAt(key.length - 1).toUpperCase();
bindNonLayerSpecificTool(
val.tool,
toolKey,
layerConstructor,
val.provider,
);
}
}
}
}

const hashBinding = viewer.registerDisposer(
new UrlHashBinding(
viewer.state,
Expand Down
6 changes: 3 additions & 3 deletions src/ui/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,15 +318,15 @@ export class GlobalToolBinder extends RefCounted {
this.changed.dispatch();
}

activate(key: string): Borrowed<Tool> | undefined {
const tool = this.get(key);
activate(key: string, tool?: Tool<object>): Borrowed<Tool> | undefined {
tool = tool || this.get(key);
if (tool === undefined) {
this.deactivate_();
return;
}
this.debounceDeactivate.cancel();
const activeTool = this.activeTool_;
if (tool === activeTool?.tool) {
if (tool.toJSON() === activeTool?.tool.toJSON()) {
if (tool.toggle) {
this.deactivate_();
}
Expand Down
5 changes: 3 additions & 2 deletions src/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import { SelectionDetailsPanel } from "#src/ui/selection_details.js";
import { SidePanelManager } from "#src/ui/side_panel.js";
import { StateEditorDialog } from "#src/ui/state_editor.js";
import { StatisticsDisplayState, StatisticsPanel } from "#src/ui/statistics.js";
import type { Tool } from "#src/ui/tool.js";
import { GlobalToolBinder, LocalToolBinder } from "#src/ui/tool.js";
import {
ViewerSettingsPanel,
Expand Down Expand Up @@ -1124,8 +1125,8 @@ export class Viewer extends RefCounted implements ViewerState {
new LocalToolBinder(this, this.globalToolBinder),
);

activateTool(uppercase: string) {
this.globalToolBinder.activate(uppercase);
activateTool(key: string, tool?: Tool<object>) {
this.globalToolBinder.activate(key, tool);
}

editJsonState() {
Expand Down

0 comments on commit ba79676

Please sign in to comment.