Browse Source

mol-state: UpdateOptions, small bug fixes

David Sehnal 6 years ago
parent
commit
505720271e

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

@@ -49,7 +49,7 @@ export function SetCurrentObject(ctx: PluginContext) {
 }
 
 export function Update(ctx: PluginContext) {
-    PluginCommands.State.Update.subscribe(ctx, ({ state, tree, doNotLogTiming }) => ctx.runTask(state.updateTree(tree, doNotLogTiming)));
+    PluginCommands.State.Update.subscribe(ctx, ({ state, tree, options }) => ctx.runTask(state.updateTree(tree, options)));
 }
 
 export function ApplyAction(ctx: PluginContext) {

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

@@ -17,7 +17,7 @@ export const PluginCommands = {
     State: {
         SetCurrentObject: PluginCommand<{ state: State, ref: StateTransform.Ref }>(),
         ApplyAction: PluginCommand<{ state: State, action: StateAction.Instance, ref?: StateTransform.Ref }>(),
-        Update: PluginCommand<{ state: State, tree: State.Tree | State.Builder, doNotLogTiming?: boolean }>(),
+        Update: PluginCommand<{ state: State, tree: State.Tree | State.Builder, options?: Partial<State.UpdateOptions> }>(),
 
         RemoveObject: PluginCommand<{ state: State, ref: StateTransform.Ref, removeParentGhosts?: boolean }>(),
 

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

@@ -180,7 +180,7 @@ export class PluginContext {
             tree.to(PluginBehavior.getCategoryId(b.transformer)).apply(b.transformer, b.defaultParams, { ref: b.transformer.id });
         }
 
-        await this.runTask(this.state.behaviorState.updateTree(tree, true));
+        await this.runTask(this.state.behaviorState.updateTree(tree, { doNotUpdateCurrent: true, doNotLogTiming: true }));
     }
 
     private initDataActions() {

+ 6 - 1
src/mol-plugin/state/animation/built-in.ts

@@ -33,6 +33,11 @@ export const AnimateModelIndex = PluginStateAnimation.create({
         const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model)
             .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory));
 
+        if (models.length === 0) {
+            // nothing more to do here
+            return { kind: 'finished' };
+        }
+
         const update = state.build();
 
         const params = ctx.params;
@@ -70,7 +75,7 @@ export const AnimateModelIndex = PluginStateAnimation.create({
                 });
         }
 
-        await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, doNotLogTiming: true });
+        await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } });
 
         if (params.mode.name === 'once' && isEnd) return { kind: 'finished' };
         if (params.mode.name === 'palindrome') return { kind: 'next', state: { palindromeDirections } };

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

@@ -84,8 +84,8 @@ class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State, d
     }
 
     render() {
-        const cell = this.props.state.cells.get(this.props.nodeRef)!;
-        if (cell.obj === StateObject.Null) return null;
+        const cell = this.props.state.cells.get(this.props.nodeRef);
+        if (!cell || cell.obj === StateObject.Null) return null;
 
         const cellState = this.cellState;
         const showLabel = cell.status !== 'ok' || !cell.transform.props || !cell.transform.props.isGhost;
@@ -202,7 +202,8 @@ class StateTreeNodeLabel extends PluginUIComponent<
 
     render() {
         const n = this.props.state.transforms.get(this.props.nodeRef)!;
-        const cell = this.props.state.cells.get(this.props.nodeRef)!;
+        const cell = this.props.state.cells.get(this.props.nodeRef);
+        if (!cell) return null;
 
         const isCurrent = this.is(this.props.state.behaviors.currentObject.value);
 

+ 24 - 14
src/mol-state/state.ts

@@ -126,13 +126,13 @@ class State {
      * @param tree Tree instance or a tree builder instance
      * @param doNotReportTiming Indicates whether to log timing of the individual transforms
      */
-    updateTree<T extends StateObject>(tree: StateTree | StateBuilder | StateBuilder.To<T>, doNotLogTiming?: boolean): Task<T>
-    updateTree(tree: StateTree | StateBuilder, doNotLogTiming?: boolean): Task<void>
-    updateTree(tree: StateTree | StateBuilder, doNotLogTiming: boolean = false): Task<any> {
+    updateTree<T extends StateObject>(tree: StateBuilder.To<T>, options?: Partial<State.UpdateOptions>): Task<T>
+    updateTree(tree: StateTree | StateBuilder, options?: Partial<State.UpdateOptions>): Task<void>
+    updateTree(tree: StateTree | StateBuilder, options?: Partial<State.UpdateOptions>): Task<any> {
         return Task.create('Update Tree', async taskCtx => {
             let updated = false;
             try {
-                const ctx = this.updateTreeAndCreateCtx(tree, taskCtx, doNotLogTiming);
+                const ctx = this.updateTreeAndCreateCtx(tree, taskCtx, options);
                 updated = await update(ctx);
                 if (StateBuilder.isTo(tree)) {
                     const cell = this.select(tree.ref)[0];
@@ -144,7 +144,7 @@ class State {
         });
     }
 
-    private updateTreeAndCreateCtx(tree: StateTree | StateBuilder, taskCtx: RuntimeContext, doNotLogTiming: boolean) {
+    private updateTreeAndCreateCtx(tree: StateTree | StateBuilder, taskCtx: RuntimeContext, options: Partial<State.UpdateOptions> | undefined) {
         const _tree = (StateBuilder.is(tree) ? tree.getTree() : tree).asTransient();
         const oldTree = this._tree;
         this._tree = _tree;
@@ -162,7 +162,7 @@ class State {
 
             results: [],
 
-            silent: doNotLogTiming,
+            options: { ...StateUpdateDefaultOptions, ...options },
 
             changed: false,
             hadError: false,
@@ -210,11 +210,21 @@ namespace State {
         readonly tree: StateTree.Serialized
     }
 
+    export interface UpdateOptions {
+        doNotLogTiming: boolean,
+        doNotUpdateCurrent: boolean
+    }
+
     export function create(rootObject: StateObject, params?: { globalContext?: unknown, rootProps?: StateTransform.Props }) {
         return new State(rootObject, params);
     }
 }
 
+const StateUpdateDefaultOptions: State.UpdateOptions = {
+    doNotLogTiming: false,
+    doNotUpdateCurrent: false
+};
+
 type Ref = StateTransform.Ref
 
 interface UpdateContext {
@@ -231,7 +241,7 @@ interface UpdateContext {
     results: UpdateNodeResult[],
 
     // suppress timing messages
-    silent: boolean,
+    options: State.UpdateOptions,
 
     changed: boolean,
     hadError: boolean,
@@ -324,13 +334,13 @@ async function update(ctx: UpdateContext) {
         }
     }
 
-    if (newCurrent) ctx.parent.setCurrent(newCurrent);
-    else {
+    if (newCurrent) {
+        if (!ctx.options.doNotUpdateCurrent) ctx.parent.setCurrent(newCurrent);
+    } else {
         // check if old current or its parent hasn't become null
         const current = ctx.parent.current;
         const currentCell = ctx.cells.get(current);
-        if (currentCell && (
-            currentCell.obj === StateObject.Null
+        if (currentCell && (currentCell.obj === StateObject.Null
             || (currentCell.status === 'error' && currentCell.errorText === ParentNullErrorText))) {
             newCurrent = findNewCurrent(ctx.oldTree, current, [], ctx.cells);
             ctx.parent.setCurrent(newCurrent);
@@ -515,13 +525,13 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) {
         ctx.results.push(update);
         if (update.action === 'created') {
             isNull = update.obj === StateObject.Null;
-            if (!isNull && !ctx.silent) ctx.parent.events.log.next(LogEntry.info(`Created ${update.obj.label} in ${formatTimespan(time)}.`));
+            if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(LogEntry.info(`Created ${update.obj.label} in ${formatTimespan(time)}.`));
         } else if (update.action === 'updated') {
             isNull = update.obj === StateObject.Null;
-            if (!isNull && !ctx.silent) ctx.parent.events.log.next(LogEntry.info(`Updated ${update.obj.label} in ${formatTimespan(time)}.`));
+            if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(LogEntry.info(`Updated ${update.obj.label} in ${formatTimespan(time)}.`));
         } else if (update.action === 'replaced') {
             isNull = update.obj === StateObject.Null;
-            if (!isNull && !ctx.silent) ctx.parent.events.log.next(LogEntry.info(`Updated ${update.obj.label} in ${formatTimespan(time)}.`));
+            if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(LogEntry.info(`Updated ${update.obj.label} in ${formatTimespan(time)}.`));
         }
     } catch (e) {
         ctx.changed = true;