Browse Source

mol-plugin: do not disable UI for fast updates

David Sehnal 5 years ago
parent
commit
c2028d20a8

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

@@ -116,7 +116,7 @@ export class StateSnapshotViewportControls extends PluginUIComponent<{}, { isBus
     componentDidMount() {
         // TODO: this needs to be diabled when the state is updating!
         this.subscribe(this.plugin.state.snapshots.events.changed, () => this.forceUpdate());
-        this.subscribe(this.plugin.behaviors.state.isUpdating, isBusy => this.setState({ isBusy }));
+        this.subscribe(this.plugin.behaviors.state.isBusy, isBusy => this.setState({ isBusy }));
         this.subscribe(this.plugin.behaviors.state.isAnimating, isBusy => this.setState({ isBusy }))
 
         window.addEventListener('keyup', this.keyUp, false);
@@ -202,17 +202,17 @@ export class StateSnapshotViewportControls extends PluginUIComponent<{}, { isBus
     }
 }
 
-export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty: boolean, isExpanded: boolean, isUpdating: boolean, isAnimating: boolean, isPlaying: boolean }> {
-    state = { isEmpty: true, isExpanded: false, isUpdating: false, isAnimating: false, isPlaying: false };
+export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty: boolean, isExpanded: boolean, isBusy: boolean, isAnimating: boolean, isPlaying: boolean }> {
+    state = { isEmpty: true, isExpanded: false, isBusy: false, isAnimating: false, isPlaying: false };
 
     componentDidMount() {
         this.subscribe(this.plugin.state.snapshots.events.changed, () => {
             if (this.plugin.state.snapshots.state.isPlaying) this.setState({ isPlaying: true, isExpanded: false });
             else this.setState({ isPlaying: false });
         });
-        this.subscribe(this.plugin.behaviors.state.isUpdating, isUpdating => {
-            if (isUpdating) this.setState({ isUpdating: true, isExpanded: false, isEmpty: this.plugin.state.data.tree.transforms.size < 2 });
-            else this.setState({ isUpdating: false, isEmpty: this.plugin.state.data.tree.transforms.size < 2 });
+        this.subscribe(this.plugin.behaviors.state.isBusy, isBusy => {
+            if (isBusy) this.setState({ isBusy: true, isExpanded: false, isEmpty: this.plugin.state.data.tree.transforms.size < 2 });
+            else this.setState({ isBusy: false, isEmpty: this.plugin.state.data.tree.transforms.size < 2 });
         });
         this.subscribe(this.plugin.behaviors.state.isAnimating, isAnimating => {
             if (isAnimating) this.setState({ isAnimating: true, isExpanded: false });
@@ -237,9 +237,9 @@ export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty:
                 <div className='msp-semi-transparent-background' />
                 <IconButton icon={isAnimating || isPlaying ? 'stop' : 'tape'} title={isAnimating ? 'Stop' : 'Select Animation'}
                     onClick={isAnimating || isPlaying ? this.stop : this.toggleExpanded}
-                    disabled={isAnimating|| isPlaying ? false : this.state.isUpdating || this.state.isPlaying || this.state.isEmpty} />
+                    disabled={isAnimating|| isPlaying ? false : this.state.isBusy || this.state.isPlaying || this.state.isEmpty} />
             </div>
-            {(this.state.isExpanded && !this.state.isUpdating) && <div className='msp-animation-viewport-controls-select'>
+            {(this.state.isExpanded && !this.state.isBusy) && <div className='msp-animation-viewport-controls-select'>
                 <AnimationControls onStart={this.toggleExpanded} />
             </div>}
         </div>;

+ 2 - 0
src/mol-plugin-ui/controls/parameters.tsx

@@ -884,6 +884,8 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
 export class MappedControl extends React.PureComponent<ParamProps<PD.Mapped<any>>, { isExpanded: boolean }> {
     state = { isExpanded: false }
 
+    // TODO: this could lead to a rare bug where the component is reused with different mapped control.
+    // I think there are currently no cases where this could happen in the UI, but still need to watch out..
     private valuesCache: { [name: string]: PD.Values<any> } = {}
     private setValues(name: string, values: PD.Values<any>) {
         this.valuesCache[name] = values

+ 2 - 1
src/mol-plugin/config.ts

@@ -20,7 +20,8 @@ export const PluginConfig = {
     item,
     State: {
         DefaultServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state'),
-        CurrentServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state')
+        CurrentServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state'),
+        IsBusyTimeoutMs: item('plugin-state.is-busy-timeout', 750)
     },
     VolumeStreaming: {
         DefaultServer: item('volume-streaming.server', 'https://ds.litemol.org'),

+ 9 - 7
src/mol-plugin/context.ts

@@ -8,6 +8,7 @@
 import { setAutoFreeze } from 'immer';
 import { List } from 'immutable';
 import { merge } from 'rxjs';
+import { debounceTime } from 'rxjs/operators';
 import { Canvas3D } from '../mol-canvas3d/canvas3d';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
 import { Model, Structure } from '../mol-model/structure';
@@ -15,14 +16,19 @@ import { DataFormatRegistry } from '../mol-plugin-state/actions/data-format';
 import { DataBuilder } from '../mol-plugin-state/builder/data';
 import { StructureBuilder } from '../mol-plugin-state/builder/structure';
 import { TrajectoryFormatRegistry } from '../mol-plugin-state/formats/trajectory';
+import { StructureSelectionQueryRegistry } from '../mol-plugin-state/helpers/structure-selection-query';
 import { CameraManager } from '../mol-plugin-state/manager/camera';
 import { InteractivityManager } from '../mol-plugin-state/manager/interactivity';
 import { LociLabelEntry, LociLabelManager } from '../mol-plugin-state/manager/loci-label';
 import { StructureComponentManager } from '../mol-plugin-state/manager/structure/component';
+import { StructureFocusManager } from '../mol-plugin-state/manager/structure/focus';
 import { StructureHierarchyManager } from '../mol-plugin-state/manager/structure/hierarchy';
+import { HierarchyRef } from '../mol-plugin-state/manager/structure/hierarchy-state';
 import { StructureMeasurementManager } from '../mol-plugin-state/manager/structure/measurement';
 import { StructureSelectionManager } from '../mol-plugin-state/manager/structure/selection';
+import { PluginUIComponent } from '../mol-plugin-ui/base';
 import { StateTransformParameters } from '../mol-plugin-ui/state/common';
+import { Representation } from '../mol-repr/representation';
 import { StructureRepresentationRegistry } from '../mol-repr/structure/registry';
 import { VolumeRepresentationRegistry } from '../mol-repr/volume/registry';
 import { State, StateBuilder, StateTree } from '../mol-state';
@@ -40,7 +46,7 @@ import { BuiltInPluginBehaviors } from './behavior';
 import { PluginBehavior } from './behavior/behavior';
 import { PluginCommandManager } from './command';
 import { PluginCommands } from './commands';
-import { PluginConfigManager } from './config';
+import { PluginConfigManager, PluginConfig } from './config';
 import { LeftPanelTabName, PluginLayout } from './layout';
 import { PluginSpec } from './spec';
 import { PluginState } from './state';
@@ -49,11 +55,6 @@ import { TaskManager } from './util/task-manager';
 import { PluginToastManager } from './util/toast';
 import { ViewportScreenshotHelper } from './util/viewport-screenshot';
 import { PLUGIN_VERSION, PLUGIN_VERSION_DATE } from './version';
-import { Representation } from '../mol-repr/representation';
-import { HierarchyRef } from '../mol-plugin-state/manager/structure/hierarchy-state';
-import { PluginUIComponent } from '../mol-plugin-ui/base';
-import { StructureFocusManager } from '../mol-plugin-state/manager/structure/focus';
-import { StructureSelectionQueryRegistry } from '../mol-plugin-state/helpers/structure-selection-query';
 
 export class PluginContext {
     private disposed = false;
@@ -241,7 +242,8 @@ export class PluginContext {
             this.behaviors.state.isUpdating.next(u);
         });
 
-        merge(this.behaviors.state.isUpdating, this.behaviors.state.isAnimating).subscribe(() => {
+        const timeout = this.config.get(PluginConfig.State.IsBusyTimeoutMs) || 500;
+        merge(debounceTime(timeout)(this.behaviors.state.isUpdating), debounceTime(timeout)(this.behaviors.state.isAnimating)).subscribe(() => {
             const isUpdating = this.behaviors.state.isUpdating.value;
             const isAnimating = this.behaviors.state.isAnimating.value;
             const isBusy = this.behaviors.state.isBusy;