ソースを参照

mol-plugin: wip

David Sehnal 6 年 前
コミット
e958ae17d3

+ 1 - 1
src/mol-plugin/behavior.ts

@@ -5,7 +5,7 @@
  */
 
 export * from './behavior/behavior'
-import * as Data from './behavior/built-in/data'
+import * as Data from './behavior/built-in/state'
 import * as Representation from './behavior/built-in/representation'
 
 export const PluginBehaviors = {

+ 6 - 6
src/mol-plugin/behavior/built-in/data.ts → src/mol-plugin/behavior/built-in/state.ts

@@ -5,25 +5,25 @@
  */
 
 import { PluginBehavior } from '../behavior';
-import { PluginCommands } from 'mol-plugin/command';
+import { PluginCommands } from '../../command';
 
 export const SetCurrentObject = PluginBehavior.create({
     name: 'set-current-data-object-behavior',
-    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.SetCurrentObject, ({ ref }, ctx) => ctx.state.data.setCurrent(ref)),
+    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.SetCurrentObject, ({ state, ref }, ctx) => state.setCurrent(ref)),
     display: { name: 'Set Current Handler', group: 'Data' }
 });
 
 export const Update = PluginBehavior.create({
     name: 'update-data-behavior',
-    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.Update, ({ tree }, ctx) => ctx.state.updateData(tree)),
+    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.Update, ({ state, tree }, ctx) => ctx.runTask(state.update(tree))),
     display: { name: 'Update Data Handler', group: 'Data' }
 });
 
 export const RemoveObject = PluginBehavior.create({
     name: 'remove-object-data-behavior',
-    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.RemoveObject, ({ ref }, ctx) => {
-        const tree = ctx.state.data.tree.build().delete(ref).getTree();
-        ctx.state.updateData(tree);
+    ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.RemoveObject, ({ state, ref }, ctx) => {
+        const tree = state.tree.build().delete(ref).getTree();
+        return ctx.runTask(state.update(tree));
     }),
     display: { name: 'Remove Object Handler', group: 'Data' }
 });

+ 2 - 2
src/mol-plugin/command.ts

@@ -4,9 +4,9 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as Data from './command/data';
+import * as State from './command/state';
 
 export * from './command/command';
 export const PluginCommands = {
-    Data
+    State
 }

+ 0 - 13
src/mol-plugin/command/data.ts

@@ -1,13 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { PluginCommand } from './command';
-import { Transform, StateTree } from 'mol-state';
-
-export const SetCurrentObject = PluginCommand<{ ref: Transform.Ref }>('ms-data', 'set-current-object');
-export const Update = PluginCommand<{ tree: StateTree }>('ms-data', 'update');
-export const UpdateObject = PluginCommand<{ ref: Transform.Ref, params: any }>('ms-data', 'update-object');
-export const RemoveObject = PluginCommand<{ ref: Transform.Ref }>('ms-data', 'remove-object');

+ 15 - 0
src/mol-plugin/command/state.ts

@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginCommand } from './command';
+import { Transform, StateTree, State } from 'mol-state';
+
+export const SetCurrentObject = PluginCommand<{ state: State, ref: Transform.Ref }>('ms-data', 'set-current-object');
+export const Update = PluginCommand<{ state: State, tree: StateTree }>('ms-data', 'update');
+
+// export const UpdateObject = PluginCommand<{ ref: Transform.Ref, params: any }>('ms-data', 'update-object');
+
+export const RemoveObject = PluginCommand<{ state: State, ref: Transform.Ref }>('ms-data', 'remove-object');

+ 10 - 10
src/mol-plugin/context.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { StateTree, Transformer, Transform } from 'mol-state';
+import { StateTree, Transformer, Transform, State } from 'mol-state';
 import { Canvas3D } from 'mol-canvas3d/canvas3d';
 import { StateTransforms } from './state/transforms';
 import { PluginStateObject as PSO } from './state/base';
@@ -92,17 +92,17 @@ export class PluginContext {
             .and().toRoot().apply(PluginBehaviors.Representation.SelectLoci, { ref: PluginBehaviors.Representation.SelectLoci.id })
             .getTree();
 
-        await this.state.updateBehaviour(tree);
+        await this.runTask(this.state.behavior.update(tree));
     }
 
-    _test_applyTransform(a: Transform.Ref, transformer: Transformer, params: any) {
-        const tree = this.state.data.tree.build().to(a).apply(transformer, params).getTree();
-        PluginCommands.Data.Update.dispatch(this, { tree });
+    applyTransform(state: State, a: Transform.Ref, transformer: Transformer, params: any) {
+        const tree = state.tree.build().to(a).apply(transformer, params).getTree();
+        return PluginCommands.State.Update.dispatch(this, { state, tree });
     }
 
-    _test_updateTransform(a: Transform.Ref, params: any) {
-        const tree = StateTree.updateParams(this.state.data.tree, a, params);
-        PluginCommands.Data.Update.dispatch(this, { tree });
+    updateTransform(state: State, a: Transform.Ref, params: any) {
+        const tree = StateTree.updateParams(state.tree, a, params);
+        return PluginCommands.State.Update.dispatch(this, { state, tree });
     }
 
     _test_createState(url: string) {
@@ -129,7 +129,7 @@ export class PluginContext {
             .apply(StateTransforms.Visuals.CreateStructureRepresentation)
             .getTree();
 
-        this.state.updateData(newTree);
+        this.runTask(this.state.data.update(newTree));
     }
 
     private initEvents() {
@@ -163,7 +163,7 @@ export class PluginContext {
         const models = this.state.data.select('models')[0].obj as SO.Molecule.Models;
         const idx = (this.state.data.tree.nodes.get('structure')!.params as Transformer.Params<typeof StateTransforms.Model.CreateStructureFromModel>).modelIndex;
         const newTree = StateTree.updateParams(this.state.data.tree, 'structure', { modelIndex: (idx + 1) % models.data.length });
-        return this.state.updateData(newTree);
+        return this.runTask(this.state.data.update(newTree));
         // this.viewer.requestDraw(true);
     }
 

+ 1 - 9
src/mol-plugin/state.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { State, StateTree } from 'mol-state';
+import { State } from 'mol-state';
 import { PluginStateObjects as SO } from './state/objects';
 import { Camera } from 'mol-canvas3d/camera';
 import { PluginBehavior } from './behavior';
@@ -32,14 +32,6 @@ class PluginState {
         this.plugin.canvas3d.requestDraw(true);
     }
 
-    updateData(tree: StateTree) {
-        return this.plugin.runTask(this.data.update(tree));
-    }
-
-    updateBehaviour(tree: StateTree) {
-        return this.plugin.runTask(this.behavior.update(tree));
-    }
-
     dispose() {
         this.data.dispose();
     }

+ 8 - 8
src/mol-plugin/ui/controls.tsx

@@ -6,7 +6,7 @@
 
 import * as React from 'react';
 import { PluginContext } from '../context';
-import { Transform, Transformer } from 'mol-state';
+import { Transform, Transformer, State } from 'mol-state';
 import { ParametersComponent } from 'mol-app/component/parameters';
 
 export class Controls extends React.Component<{ plugin: PluginContext }, { id: string }> {
@@ -42,9 +42,9 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s
     }
 }
 
-export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, transformer: Transformer }, { params: any }> {
+export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, state: State, transformer: Transformer }, { params: any }> {
     private getObj() {
-        const obj = this.props.plugin.state.data.cells.get(this.props.nodeRef)!;
+        const obj = this.props.state.cells.get(this.props.nodeRef)!;
         return obj;
     }
 
@@ -66,7 +66,7 @@ export class _test_CreateTransform extends React.Component<{ plugin: PluginConte
 
     private create() {
         console.log(this.props.transformer.definition.name, this.state.params);
-        this.props.plugin._test_applyTransform(this.props.nodeRef, this.props.transformer, this.state.params);
+        this.props.plugin.applyTransform(this.props.state, this.props.nodeRef, this.props.transformer, this.state.params);
     }
 
     state = { params: this.getDefaultParams() }
@@ -90,16 +90,16 @@ export class _test_CreateTransform extends React.Component<{ plugin: PluginConte
     }
 }
 
-export class _test_UpdateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref }, { params: any }> {
+export class _test_UpdateTransform extends React.Component<{ plugin: PluginContext, state: State, nodeRef: Transform.Ref }, { params: any }> {
     private getTransform() {
-        return this.props.plugin.state.data.tree.nodes.get(this.props.nodeRef)!;
+        return this.props.state.tree.nodes.get(this.props.nodeRef)!;
     }
 
     private getParamDef() {
         const def = this.getTransform().transformer.definition;
         if (!def.params || !def.params.controls) return void 0;
 
-        const src = this.props.plugin.state.data.select(q => q.byRef(this.props.nodeRef).ancestorOfType(def.from))[0];
+        const src = this.props.state.select(q => q.byRef(this.props.nodeRef).ancestorOfType(def.from))[0];
 
         // StateSelection.ancestorOfType(this.props.nodeRef, def.from).select(this.props.plugin.state.data)[0];
 
@@ -111,7 +111,7 @@ export class _test_UpdateTransform extends React.Component<{ plugin: PluginConte
 
     private update() {
         console.log(this.props.nodeRef, this.state.params);
-        this.props.plugin._test_updateTransform(this.props.nodeRef, this.state.params);
+        this.props.plugin.updateTransform(this.props.state, this.props.nodeRef, this.state.params);
     }
 
     // componentDidMount() {

+ 6 - 5
src/mol-plugin/ui/plugin.tsx

@@ -21,7 +21,7 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, { }> {
                 <StateTree plugin={this.props.plugin} state={this.props.plugin.state.data} />
                 <hr />
                 <_test_CurrentObject plugin={this.props.plugin} />
-                <h3>Behavior</h3>
+                <h3>Behaviors</h3>
                 <StateTree plugin={this.props.plugin} state={this.props.plugin.state.behavior} />
             </div>
             <div style={{ position: 'absolute', left: '350px', right: '250px', height: '100%' }}>
@@ -40,7 +40,8 @@ export class _test_CurrentObject extends React.Component<{ plugin: PluginContext
         this.props.plugin.behaviors.state.data.currentObject.subscribe(() => this.forceUpdate());
     }
     render() {
-        const ref = this.props.plugin.behaviors.state.data.currentObject.value.ref;
+        const current = this.props.plugin.behaviors.state.data.currentObject.value;
+        const ref = current.ref;
         // const n = this.props.plugin.state.data.tree.nodes.get(ref)!;
         const obj = this.props.plugin.state.data.cells.get(ref)!;
 
@@ -50,14 +51,14 @@ export class _test_CurrentObject extends React.Component<{ plugin: PluginContext
             ? Transformer.fromType(type)
             : []
         return <div>
-            Current Ref: {this.props.plugin.behaviors.state.data.currentObject.value.ref}
+            Current Ref: {ref}
             <hr />
             <h3>Update</h3>
-            <_test_UpdateTransform key={`${ref} update`} plugin={this.props.plugin} nodeRef={ref} />
+            <_test_UpdateTransform key={`${ref} update`} plugin={this.props.plugin} state={current.state} nodeRef={ref} />
             <hr />
             <h3>Create</h3>
             {
-                transforms.map((t, i) => <_test_CreateTransform key={`${t.id} ${ref} ${i}`} plugin={this.props.plugin} transformer={t} nodeRef={ref} />)
+                transforms.map((t, i) => <_test_CreateTransform key={`${t.id} ${ref} ${i}`} plugin={this.props.plugin} state={current.state} transformer={t} nodeRef={ref} />)
             }
         </div>;
     }

+ 3 - 3
src/mol-plugin/ui/state-tree.tsx

@@ -32,7 +32,7 @@ export class StateTreeNode extends React.Component<{ plugin: PluginContext, node
 
         const remove = <>[<a href='#' onClick={e => {
             e.preventDefault();
-            PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
+            PluginCommands.State.RemoveObject.dispatch(this.props.plugin, { state: this.props.state, ref: this.props.nodeRef });
         }}>X</a>]</>
 
         let label: any;
@@ -40,7 +40,7 @@ export class StateTreeNode extends React.Component<{ plugin: PluginContext, node
             const name = (n.transformer.definition.display && n.transformer.definition.display.name) || n.transformer.definition.name;
             label = <><b>{cell.status}</b> <a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.Data.SetCurrentObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
+                PluginCommands.State.SetCurrentObject.dispatch(this.props.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{name}</a>: <i>{cell.errorText}</i></>;
         } else {
             const obj = cell.obj as PluginStateObject.Any;
@@ -48,7 +48,7 @@ export class StateTreeNode extends React.Component<{ plugin: PluginContext, node
             const type = obj.type;
             label = <>[<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.Data.SetCurrentObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
+                PluginCommands.State.SetCurrentObject.dispatch(this.props.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{props.label}</a> {props.description ? <small>{props.description}</small> : void 0}</>;
         }
 

+ 37 - 37
src/mol-state/state.ts

@@ -25,22 +25,19 @@ class State {
     readonly globalContext: unknown = void 0;
     readonly events = {
         object: {
-            stateChanged: this.ev<{ ref: Transform.Ref }>(),
-            propsChanged: this.ev<{ ref: Transform.Ref, newProps: unknown }>(),
+            cellState: this.ev<State.ObjectEvent & { cell: StateObjectCell }>(),
 
-            updated: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
-            replaced: this.ev<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>(),
-            created: this.ev<{ ref: Transform.Ref, obj: StateObject }>(),
-            removed: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
-
-            currentChanged: this.ev<{ ref: Transform.Ref }>()
+            updated: this.ev<State.ObjectEvent & { obj?: StateObject }>(),
+            replaced: this.ev<State.ObjectEvent & { oldObj?: StateObject, newObj?: StateObject }>(),
+            created: this.ev<State.ObjectEvent & { obj: StateObject }>(),
+            removed: this.ev<State.ObjectEvent & { obj?: StateObject }>()
         },
         warn: this.ev<string>(),
         updated: this.ev<void>()
     };
 
     readonly behaviors = {
-        currentObject: this.ev.behavior<{ ref: Transform.Ref }>({ ref: Transform.RootRef })
+        currentObject: this.ev.behavior<State.ObjectEvent>({ state: this, ref: Transform.RootRef })
     };
 
     get tree() { return this._tree; }
@@ -48,8 +45,6 @@ class State {
 
     readonly cells: State.Cells = new Map();
 
-    // readonly context: StateContext;
-
     getSnapshot(): State.Snapshot {
         return { tree: StateTree.toJSON(this._tree) };
     }
@@ -61,7 +56,7 @@ class State {
 
     setCurrent(ref: Transform.Ref) {
         this._current = ref;
-        this.behaviors.currentObject.next({ ref });
+        this.behaviors.currentObject.next({ state: this, ref });
     }
 
     updateCellState(ref: Transform.Ref, state?: Partial<StateObjectCell.State>) {
@@ -87,7 +82,7 @@ class State {
     }
 
     update(tree: StateTree): Task<void> {
-        // TODO: support props
+        // TODO: support cell state
         return Task.create('Update Tree', async taskCtx => {
             try {
                 const oldTree = this._tree;
@@ -98,7 +93,7 @@ class State {
                     taskCtx,
                     oldTree,
                     tree,
-                    cells: this.cells,
+                    cells: this.cells as Map<Transform.Ref, StateObjectCell>,
                     transformCache: this.transformCache
                 };
                 // TODO: have "cancelled" error? Or would this be handled automatically?
@@ -113,7 +108,7 @@ class State {
         const tree = this._tree;
         const root = tree.root;
 
-        this.cells.set(root.ref, {
+        (this.cells as Map<Transform.Ref, StateObjectCell>).set(root.ref, {
             ref: root.ref,
             obj: rootObject,
             status: 'ok',
@@ -126,7 +121,12 @@ class State {
 }
 
 namespace State {
-    export type Cells = Map<Transform.Ref, StateObjectCell>
+    export type Cells = ReadonlyMap<Transform.Ref, StateObjectCell>
+
+    export interface ObjectEvent {
+        state: State,
+        ref: Ref
+    }
 
     export interface Snapshot {
         readonly tree: StateTree.Serialized
@@ -145,7 +145,7 @@ interface UpdateContext {
     taskCtx: RuntimeContext,
     oldTree: StateTree,
     tree: StateTree,
-    cells: State.Cells,
+    cells: Map<Transform.Ref, StateObjectCell>,
     transformCache: Map<Ref, unknown>
 }
 
@@ -156,19 +156,19 @@ async function update(ctx: UpdateContext) {
         const obj = ctx.cells.has(d) ? ctx.cells.get(d)!.obj : void 0;
         ctx.cells.delete(d);
         ctx.transformCache.delete(d);
-        ctx.parent.events.object.removed.next({ ref: d, obj });
+        ctx.parent.events.object.removed.next({ state: ctx.parent, ref: d, obj });
         // TODO: handle current object change
     }
 
     initCells(ctx, roots);
-    initObjectStatus(ctx, roots);
+    initCellStatus(ctx, roots);
 
     for (const root of roots) {
         await updateSubtree(ctx, root);
     }
 }
 
-function findUpdateRoots(cells: State.Cells, tree: StateTree) {
+function findUpdateRoots(cells: Map<Transform.Ref, StateObjectCell>, tree: StateTree) {
     const findState = { roots: [] as Ref[], cells };
     StateTree.doPreOrder(tree, tree.root, findState, _findUpdateRoots);
     return findState.roots;
@@ -193,22 +193,22 @@ function findDeletes(ctx: UpdateContext): Ref[] {
     return deleteCtx.deletes;
 }
 
-function setObjectStatus(ctx: UpdateContext, ref: Ref, status: StateObjectCell.Status, errorText?: string) {
-    const obj = ctx.cells.get(ref)!;
-    const changed = obj.status !== status;
-    obj.status = status;
-    obj.errorText = errorText;
-    if (changed) ctx.parent.events.object.stateChanged.next({ ref });
+function setCellStatus(ctx: UpdateContext, ref: Ref, status: StateObjectCell.Status, errorText?: string) {
+    const cell = ctx.cells.get(ref)!;
+    const changed = cell.status !== status;
+    cell.status = status;
+    cell.errorText = errorText;
+    if (changed) ctx.parent.events.object.cellState.next({ state: ctx.parent, ref, cell });
 }
 
-function _initObjectStatusVisitor(t: Transform, _: any, ctx: UpdateContext) {
-    setObjectStatus(ctx, t.ref, 'pending');
+function _initCellStatusVisitor(t: Transform, _: any, ctx: UpdateContext) {
+    setCellStatus(ctx, t.ref, 'pending');
 }
 
 /** Return "resolve set" */
-function initObjectStatus(ctx: UpdateContext, roots: Ref[]) {
+function initCellStatus(ctx: UpdateContext, roots: Ref[]) {
     for (const root of roots) {
-        StateTree.doPreOrder(ctx.tree, ctx.tree.nodes.get(root), ctx, _initObjectStatusVisitor);
+        StateTree.doPreOrder(ctx.tree, ctx.tree.nodes.get(root), ctx, _initCellStatusVisitor);
     }
 }
 
@@ -234,10 +234,10 @@ function initCells(ctx: UpdateContext, roots: Ref[]) {
 }
 
 function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
-    setObjectStatus(ctx, ref, 'error', errorText);
+    setCellStatus(ctx, ref, 'error', errorText);
     const wrap = ctx.cells.get(ref)!;
     if (wrap.obj) {
-        ctx.parent.events.object.removed.next({ ref });
+        ctx.parent.events.object.removed.next({ state: ctx.parent, ref });
         ctx.transformCache.delete(ref);
         wrap.obj = void 0;
     }
@@ -263,17 +263,17 @@ function findAncestor(tree: StateTree, cells: State.Cells, root: Ref, types: { t
 }
 
 async function updateSubtree(ctx: UpdateContext, root: Ref) {
-    setObjectStatus(ctx, root, 'processing');
+    setCellStatus(ctx, root, 'processing');
 
     try {
         const update = await updateNode(ctx, root);
-        setObjectStatus(ctx, root, 'ok');
+        setCellStatus(ctx, root, 'ok');
         if (update.action === 'created') {
-            ctx.parent.events.object.created.next({ ref: root, obj: update.obj! });
+            ctx.parent.events.object.created.next({ state: ctx.parent, ref: root, obj: update.obj! });
         } else if (update.action === 'updated') {
-            ctx.parent.events.object.updated.next({ ref: root, obj: update.obj });
+            ctx.parent.events.object.updated.next({ state: ctx.parent, ref: root, obj: update.obj });
         } else if (update.action === 'replaced') {
-            ctx.parent.events.object.replaced.next({ ref: root, oldObj: update.oldObj, newObj: update.newObj });
+            ctx.parent.events.object.replaced.next({ state: ctx.parent, ref: root, oldObj: update.oldObj, newObj: update.newObj });
         }
     } catch (e) {
         doError(ctx, root, '' + e);

+ 1 - 1
src/mol-state/transform.ts

@@ -18,7 +18,7 @@ export interface Transform<A extends StateObject = StateObject, B extends StateO
 }
 
 export namespace Transform {
-    export type Ref = string /* & { '@type': 'transform-ref' } */
+    export type Ref = string
 
     export const RootRef = '-=root=-' as Ref;
 

+ 1 - 1
src/perf-tests/state.ts

@@ -68,7 +68,7 @@ function hookEvents(state: State) {
     state.events.object.created.subscribe(e => console.log('created:', e.ref));
     state.events.object.removed.subscribe(e => console.log('removed:', e.ref));
     state.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
-    state.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.cells.get(e.ref)!.status));
+    state.events.object.cellState.subscribe(e => console.log('stateChanged:', e.ref, e.cell.status));
     state.events.object.updated.subscribe(e => console.log('updated:', e.ref));
 }