Browse Source

mol-plugin: behavior categories

David Sehnal 6 years ago
parent
commit
ae1920fa90

+ 33 - 2
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>) {

+ 1 - 0
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> {

+ 1 - 0
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 => {

+ 1 - 0
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(

+ 1 - 0
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);

+ 1 - 0
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> {

+ 3 - 0
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); }

+ 6 - 1
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));

+ 1 - 1
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);

+ 4 - 3
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);
     }
 }

+ 2 - 2
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 {

+ 2 - 2
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]]));
     }