From ae1920fa9054679e143c8941bd4e53de11676710 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Fri, 22 Feb 2019 16:52:21 +0100 Subject: [PATCH] mol-plugin: behavior categories --- src/mol-plugin/behavior/behavior.ts | 35 +++++++++++++++++-- src/mol-plugin/behavior/dynamic/animation.ts | 1 + src/mol-plugin/behavior/dynamic/camera.ts | 1 + .../pdbe/structure-quality-report.ts | 1 + .../custom-props/rcsb/assembly-symmetry.ts | 1 + src/mol-plugin/behavior/dynamic/labels.ts | 1 + .../behavior/dynamic/representation.ts | 3 ++ src/mol-plugin/context.ts | 7 +++- src/mol-plugin/state.ts | 2 +- src/mol-state/state.ts | 7 ++-- src/mol-state/transform.ts | 4 +-- src/mol-state/tree/immutable.ts | 4 +-- 12 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/mol-plugin/behavior/behavior.ts b/src/mol-plugin/behavior/behavior.ts index f687f69d5..ed03b478a 100644 --- a/src/mol-plugin/behavior/behavior.ts +++ b/src/mol-plugin/behavior/behavior.ts @@ -25,12 +25,22 @@ interface PluginBehavior<P = unknown> { namespace PluginBehavior { export class Root extends PluginStateObject.Create({ name: 'Root', typeClass: 'Root' }) { } + export class Category extends PluginStateObject.Create({ name: 'Category', typeClass: 'Object' }) { } export class Behavior extends PluginStateObject.CreateBehavior<PluginBehavior>({ name: 'Behavior' }) { } export interface Ctor<P = undefined> { new(ctx: PluginContext, params: P): PluginBehavior<P> } + export const Categories = { + 'common': 'Common', + 'representation': 'Representation', + 'interaction': 'Interaction', + 'custom-props': 'Custom Properties', + 'misc': 'Miscellaneous' + }; + export interface CreateParams<P> { name: string, + category: keyof typeof Categories, ctor: Ctor<P>, canAutoUpdate?: StateTransformer.Definition<Root, Behavior, P>['canAutoUpdate'], label?: (params: P) => { label: string, description?: string }, @@ -42,9 +52,28 @@ namespace PluginBehavior { params?(a: Root, globalCtx: PluginContext): { [K in keyof P]: ParamDefinition.Any } } + export type CreateCategory = typeof CreateCategory + export const CreateCategory = PluginStateTransform.BuiltIn({ + name: 'create-behavior-category', + display: { name: 'Create Cateogry' }, + from: Root, + to: Category, + params: { + label: ParamDefinition.Text('', { isHidden: true }), + } + })({ + apply({ params }) { + return new Category({}, { label: params.label }); + } + }); + + const categoryMap = new Map<string, string>(); + export function getCategoryId(t: StateTransformer) { + return categoryMap.get(t.id)!; + } + export function create<P>(params: CreateParams<P>) { - // TODO: cache groups etc - return PluginStateTransform.CreateBuiltIn<Root, Behavior, P>({ + const t = PluginStateTransform.CreateBuiltIn<Category, Behavior, P>({ name: params.name, display: params.display, from: [Root], @@ -63,6 +92,8 @@ namespace PluginBehavior { }, canAutoUpdate: params.canAutoUpdate }); + categoryMap.set(t.id, params.category); + return t; } export function simpleCommandHandler<T>(cmd: PluginCommand<T>, action: (data: T, ctx: PluginContext) => void | Promise<void>) { diff --git a/src/mol-plugin/behavior/dynamic/animation.ts b/src/mol-plugin/behavior/dynamic/animation.ts index cdff329ad..a2c863ed3 100644 --- a/src/mol-plugin/behavior/dynamic/animation.ts +++ b/src/mol-plugin/behavior/dynamic/animation.ts @@ -29,6 +29,7 @@ type StructureAnimationProps = PD.Values<typeof StructureAnimationParams> */ export const StructureAnimation = PluginBehavior.create<StructureAnimationProps>({ name: 'structure-animation', + category: 'representation', display: { name: 'Structure Animation', group: 'Animation' }, canAutoUpdate: () => true, ctor: class extends PluginBehavior.Handler<StructureAnimationProps> { diff --git a/src/mol-plugin/behavior/dynamic/camera.ts b/src/mol-plugin/behavior/dynamic/camera.ts index 4431eec95..b2caee3fe 100644 --- a/src/mol-plugin/behavior/dynamic/camera.ts +++ b/src/mol-plugin/behavior/dynamic/camera.ts @@ -10,6 +10,7 @@ import { PluginBehavior } from '../behavior'; export const FocusLociOnSelect = PluginBehavior.create<{ minRadius: number, extraRadius: number }>({ name: 'focus-loci-on-select', + category: 'interaction', ctor: class extends PluginBehavior.Handler<{ minRadius: number, extraRadius: number }> { register(): void { this.subscribeObservable(this.ctx.behaviors.canvas.selectLoci, current => { diff --git a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts index 6c0998201..dc882b0ef 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts @@ -16,6 +16,7 @@ import { ThemeDataContext } from 'mol-theme/theme'; export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean }>({ name: 'pdbe-structure-quality-report-prop', + category: 'custom-props', display: { name: 'PDBe Structure Quality Report', group: 'Custom Props' }, ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> { private attach = StructureQualityReport.createAttachTask( diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts index 3079bbcab..fc131da26 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts @@ -16,6 +16,7 @@ import { Table } from 'mol-data/db'; export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({ name: 'rcsb-assembly-symmetry-prop', + category: 'custom-props', display: { name: 'RCSB Assembly Symmetry', group: 'Custom Props' }, ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> { private attach = AssemblySymmetry.createAttachTask(this.ctx.fetch); diff --git a/src/mol-plugin/behavior/dynamic/labels.ts b/src/mol-plugin/behavior/dynamic/labels.ts index 42966e07c..f3f17ea82 100644 --- a/src/mol-plugin/behavior/dynamic/labels.ts +++ b/src/mol-plugin/behavior/dynamic/labels.ts @@ -73,6 +73,7 @@ function getLabelsText(data: LabelsData, props: PD.Values<Text.Params>, text?: T export const SceneLabels = PluginBehavior.create<SceneLabelsProps>({ name: 'scene-labels', + category: 'representation', display: { name: 'Scene Labels', group: 'Labels' }, canAutoUpdate: () => true, ctor: class extends PluginBehavior.Handler<SceneLabelsProps> { diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index b5bcf3251..e5566019b 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -17,6 +17,7 @@ import { PluginBehavior } from '../behavior'; export const HighlightLoci = PluginBehavior.create({ name: 'representation-highlight-loci', + category: 'interaction', ctor: class extends PluginBehavior.Handler { register(): void { let prevLoci: Loci = EmptyLoci, prevRepr: any = void 0; @@ -37,6 +38,7 @@ export const HighlightLoci = PluginBehavior.create({ export const SelectLoci = PluginBehavior.create({ name: 'representation-select-loci', + category: 'interaction', ctor: class extends PluginBehavior.Handler { register(): void { let prevLoci: Loci = EmptyLoci, prevRepr: any = void 0; @@ -59,6 +61,7 @@ export const SelectLoci = PluginBehavior.create({ export const DefaultLociLabelProvider = PluginBehavior.create({ name: 'default-loci-label-provider', + category: 'interaction', ctor: class implements PluginBehavior<undefined> { private f = labelFirst; register(): void { this.ctx.lociLabels.addProvider(this.f); } diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index e7ebde9fa..9be0646bf 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -31,6 +31,7 @@ import { PluginLayout } from './layout'; import { List } from 'immutable'; import { StateTransformParameters } from './ui/state/common'; import { DataFormatRegistry } from './state/actions/basic'; +import { PluginBehavior } from './behavior/behavior'; export class PluginContext { private disposed = false; @@ -166,8 +167,12 @@ export class PluginContext { private async initBehaviors() { const tree = this.state.behaviorState.build(); + for (const cat of Object.keys(PluginBehavior.Categories)) { + tree.toRoot().apply(PluginBehavior.CreateCategory, { label: (PluginBehavior.Categories as any)[cat] }, { ref: cat, props: { isLocked: true } }); + } + for (const b of this.spec.behaviors) { - tree.toRoot().apply(b.transformer, b.defaultParams, { ref: b.transformer.id }); + tree.to(PluginBehavior.getCategoryId(b.transformer)).apply(b.transformer, b.defaultParams, { ref: b.transformer.id }); } await this.runTask(this.state.behaviorState.updateTree(tree, true)); diff --git a/src/mol-plugin/state.ts b/src/mol-plugin/state.ts index 86d08ec73..9c5e4c13b 100644 --- a/src/mol-plugin/state.ts +++ b/src/mol-plugin/state.ts @@ -77,7 +77,7 @@ class PluginState { constructor(private plugin: import('./context').PluginContext) { this.dataState = State.create(new SO.Root({ }), { globalContext: plugin }); - this.behaviorState = State.create(new PluginBehavior.Root({ }), { globalContext: plugin }); + this.behaviorState = State.create(new PluginBehavior.Root({ }), { globalContext: plugin, rootProps: { isLocked: true } }); this.dataState.behaviors.currentObject.subscribe(o => { if (this.behavior.kind.value === 'data') this.behavior.currentObject.next(o); diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index 9ac0289e5..81b56a729 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -22,7 +22,7 @@ import { ParamDefinition } from 'mol-util/param-definition'; export { State } class State { - private _tree: TransientTree = StateTree.createEmpty().asTransient(); + private _tree: TransientTree; protected errorFree = true; private transformCache = new Map<StateTransform.Ref, unknown>(); @@ -174,7 +174,8 @@ class State { return ctx; } - constructor(rootObject: StateObject, params?: { globalContext?: unknown }) { + constructor(rootObject: StateObject, params?: { globalContext?: unknown, rootProps?: StateTransform.Props }) { + this._tree = StateTree.createEmpty(StateTransform.createRoot(params && params.rootProps)).asTransient(); const tree = this._tree; const root = tree.root; @@ -209,7 +210,7 @@ namespace State { readonly tree: StateTree.Serialized } - export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps?: unknown }) { + export function create(rootObject: StateObject, params?: { globalContext?: unknown, rootProps?: StateTransform.Props }) { return new State(rootObject, params); } } diff --git a/src/mol-state/transform.ts b/src/mol-state/transform.ts index 22794b34c..d4acdb81a 100644 --- a/src/mol-state/transform.ts +++ b/src/mol-state/transform.ts @@ -52,8 +52,8 @@ namespace Transform { return { ...t, params, version: UUID.create22() }; } - export function createRoot(): Transform { - return create(RootRef, StateTransformer.ROOT, {}, { ref: RootRef }); + export function createRoot(props?: Props): Transform { + return create(RootRef, StateTransformer.ROOT, {}, { ref: RootRef, props }); } export interface Serialized { diff --git a/src/mol-state/tree/immutable.ts b/src/mol-state/tree/immutable.ts index 8cb9ed73a..ab3d8f603 100644 --- a/src/mol-state/tree/immutable.ts +++ b/src/mol-state/tree/immutable.ts @@ -59,8 +59,8 @@ namespace StateTree { /** * Create an instance of an immutable tree. */ - export function createEmpty(): StateTree { - const root = StateTransform.createRoot(); + export function createEmpty(customRoot?: StateTransform): StateTree { + const root = customRoot || StateTransform.createRoot(); return create(ImmutableMap([[root.ref, root]]), ImmutableMap([[root.ref, OrderedSet()]]), ImmutableMap([[root.ref, StateObjectCell.DefaultState]])); } -- GitLab