diff --git a/src/apps/viewer/extensions/jolecule.ts b/src/apps/viewer/extensions/jolecule.ts index 2a5744a8e7da6b384d97375384bb7ee0ab3332a6..839ad61ff5262b63a51a89a0f811ee8aca1e91f3 100644 --- a/src/apps/viewer/extensions/jolecule.ts +++ b/src/apps/viewer/extensions/jolecule.ts @@ -21,7 +21,7 @@ import { Camera } from 'mol-canvas3d/camera'; import { StructureRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation'; export const CreateJoleculeState = StateAction.build({ - display: { name: 'Jolecule State' }, + display: { name: 'Jolecule State Import' }, params: { id: ParamDefinition.Text('1mbo') }, from: PluginStateObject.Root })(async ({ ref, state, params }, plugin: PluginContext) => { diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index 3562b60caf45d28c6c6668892192f2d46e6a999e..3fce9342c9409b2ce783bb4559a0f3b8de0aa507 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -25,8 +25,6 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Action(StateActions.Structure.CreateComplexRepresentation), PluginSpec.Action(StateActions.Structure.EnableModelCustomProps), - PluginSpec.Action(StateActions.Structure.TestBlob), - // Volume streaming PluginSpec.Action(InitVolumeStreaming), PluginSpec.Action(BoxifyVolumeStreaming), diff --git a/src/mol-plugin/skin/base/components/temp.scss b/src/mol-plugin/skin/base/components/temp.scss index 13493870b4a06372528c64dfb17cded646da28c1..ce734a9e0c0fc3ed08864c6961091d76f91b6ded 100644 --- a/src/mol-plugin/skin/base/components/temp.scss +++ b/src/mol-plugin/skin/base/components/temp.scss @@ -163,6 +163,7 @@ > span { color: $font-color; + margin-left: $control-spacing; margin-right: $control-spacing; font-size: 85%; display: inline-block; diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index a2318fc63172903a21bd8136ea57cf9a7a478dbf..52284f8b22454d16307755884320737d38de563b 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -303,25 +303,4 @@ export const StructureFromSelection = StateAction.build({ const query = StructureElement.Loci.toScriptExpression(sel); const root = state.build().to(ref).apply(StructureSelection, { query, label: params.label }); return state.updateTree(root); -}); - - -export const TestBlob = StateAction.build({ - display: { name: 'Test Blob' }, - from: PluginStateObject.Root -})(({ ref, state }, ctx: PluginContext) => { - - const ids = '5B6V,5B6W,5H2H,5H2I,5H2J,5B6X,5H2K,5H2L,5H2M,5B6Y,5H2N,5H2O,5H2P,5B6Z'.split(',').map(u => u.toLowerCase()); - - const root = state.build().to(ref) - .apply(StateTransforms.Data.DownloadBlob, { - sources: ids.map(id => ({ id, url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${id}`, isBinary: true })), - maxConcurrency: 4 - }).apply(StateTransforms.Data.ParseBlob, { - formats: ids.map(id => ({ id, format: 'cif' as 'cif' })) - }) - .apply(StateTransforms.Model.TrajectoryFromBlob) - .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); - createStructureTree(ctx, root, false); - return state.updateTree(root); }); \ No newline at end of file diff --git a/src/mol-plugin/state/animation/built-in.ts b/src/mol-plugin/state/animation/built-in.ts index 6d7ed7033917e07df09b972a28328ed451e7e8c4..4144341112d4451cd550835c4109f5fb38b99efc 100644 --- a/src/mol-plugin/state/animation/built-in.ts +++ b/src/mol-plugin/state/animation/built-in.ts @@ -13,12 +13,12 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; export const AnimateModelIndex = PluginStateAnimation.create({ name: 'built-in.animate-model-index', - display: { name: 'Animate Model Index' }, + display: { name: 'Animate Trajectory' }, params: () => ({ mode: PD.MappedStatic('palindrome', { palindrome: PD.Group({ }), loop: PD.Group({ }), - once: PD.Group({ direction: PD.Select('palindrome', [['forward', 'Forward'], ['backward', 'Backward']]) }, { isFlat: true }) + once: PD.Group({ direction: PD.Select('forward', [['forward', 'Forward'], ['backward', 'Backward']]) }, { isFlat: true }) }, { options: [['palindrome', 'Palindrome'], ['loop', 'Loop'], ['once', 'Once']] }), maxFPS: PD.Numeric(15, { min: 1, max: 30, step: 1 }) }), @@ -42,7 +42,7 @@ export const AnimateModelIndex = PluginStateAnimation.create({ const params = ctx.params; const palindromeDirections = animState.palindromeDirections || { }; - let isEnd = false; + let isEnd = false, allSingles = true; for (const m of models) { const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]); @@ -51,6 +51,11 @@ export const AnimateModelIndex = PluginStateAnimation.create({ update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory, old => { const len = traj.data.length; + if (len !== 1) { + allSingles = false; + } else { + return old; + } let dir: -1 | 1 = 1; if (params.mode.name === 'once') { dir = params.mode.params.direction === 'backward' ? -1 : 1; @@ -77,7 +82,7 @@ export const AnimateModelIndex = PluginStateAnimation.create({ await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } }); - if (params.mode.name === 'once' && isEnd) return { kind: 'finished' }; + if (allSingles || (params.mode.name === 'once' && isEnd)) return { kind: 'finished' }; if (params.mode.name === 'palindrome') return { kind: 'next', state: { palindromeDirections } }; return { kind: 'next', state: {} }; } diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index 701b5a3d1a8e198fa8e4e9399cb3c36e18e0fa11..0bc8ee24e781724b4a81d366db3e19bb9a7d2109 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -89,16 +89,16 @@ export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: bo // } render() { - if (!this.state.show) return null; - const isAnimating = this.plugin.behaviors.state.isAnimating.value; + if (!this.state.show || (isAnimating && !this.state.label)) return null; + return <div className='msp-traj-controls'> {/* <IconButton icon={isAnimating ? 'stop' : 'play'} title={isAnimating ? 'Stop' : 'Play'} onClick={isAnimating ? this.stopAnimation : this.playAnimation} /> */} - <IconButton icon='model-first' title='First Model' onClick={this.reset} disabled={isAnimating} /> - <IconButton icon='model-prev' title='Previous Model' onClick={this.prev} disabled={isAnimating} /> - <IconButton icon='model-next' title='Next Model' onClick={this.next} disabled={isAnimating} /> - { !!this.state.label && <span>{this.state.label}</span> } + {!isAnimating && <IconButton icon='model-first' title='First Model' onClick={this.reset} disabled={isAnimating} />} + {!isAnimating && <IconButton icon='model-prev' title='Previous Model' onClick={this.prev} disabled={isAnimating} />} + {!isAnimating && <IconButton icon='model-next' title='Next Model' onClick={this.next} disabled={isAnimating} />} + {!!this.state.label && <span>{this.state.label}</span> } </div>; } } @@ -170,9 +170,18 @@ export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty: state = { isEmpty: true, isExpanded: false, isUpdating: false, isAnimating: false, isPlaying: false }; componentDidMount() { - this.subscribe(this.plugin.state.snapshots.events.changed, () => this.setState({ isPlaying: this.plugin.state.snapshots.state.isPlaying })); - this.subscribe(this.plugin.behaviors.state.isUpdating, isUpdating => this.setState({ isUpdating, isEmpty: this.plugin.state.dataState.tree.transforms.size < 2 })); - this.subscribe(this.plugin.behaviors.state.isAnimating, isAnimating => this.setState({ isAnimating })); + 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.dataState.tree.transforms.size < 2 }); + else this.setState({ isUpdating: false, isEmpty: this.plugin.state.dataState.tree.transforms.size < 2 }); + }); + this.subscribe(this.plugin.behaviors.state.isAnimating, isAnimating => { + if (isAnimating) this.setState({ isAnimating: true, isExpanded: false }); + else this.setState({ isAnimating: false }); + }); } toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded }); stop = () => this.plugin.state.animation.stop(); diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index a2fd3295f695074dffe4f292115b0547c604d6d3..4951a7bbb571169115de743792125b56475cea5c 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -194,7 +194,7 @@ export class CurrentObject extends PluginUIComponent { return <> {(cell.status === 'ok' || cell.status === 'error') && <UpdateTransformContol state={current.state} transform={transform} /> } - {cell.status === 'ok' && <StateObjectActions state={current.state} nodeRef={ref} />} + {cell.status === 'ok' && <StateObjectActions state={current.state} nodeRef={ref} initiallyColapsed />} </>; } } \ No newline at end of file diff --git a/src/mol-plugin/ui/state/actions.tsx b/src/mol-plugin/ui/state/actions.tsx index 0d2b0a2adb71c6460faf421f5232e680e3fbb5ec..49d102ea8c1d9176c97a9fb5cbdaf4909098e683 100644 --- a/src/mol-plugin/ui/state/actions.tsx +++ b/src/mol-plugin/ui/state/actions.tsx @@ -9,7 +9,7 @@ import { PluginUIComponent } from '../base'; import { ApplyActionContol } from './apply-action'; import { State } from 'mol-state'; -export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string, hideHeader?: boolean }> { +export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string, hideHeader?: boolean, initiallyColapsed?: boolean }> { get current() { return this.plugin.state.behavior.currentObject.value; } @@ -37,7 +37,7 @@ export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRe return <> {!this.props.hideHeader && <div className='msp-section-header'>{`Actions (${display})`}</div> } - {actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={state} action={act} nodeRef={ref} />)} + {actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={state} action={act} nodeRef={ref} initiallyCollapsed={this.props.initiallyColapsed} />)} </>; } } \ No newline at end of file diff --git a/src/mol-plugin/ui/state/apply-action.tsx b/src/mol-plugin/ui/state/apply-action.tsx index 702a5cfc7025694fd89cde0304ab9c1b186cc1fe..e4747abcb041de9eb2d1a3ef29cf9ee3e060b68d 100644 --- a/src/mol-plugin/ui/state/apply-action.tsx +++ b/src/mol-plugin/ui/state/apply-action.tsx @@ -18,7 +18,8 @@ namespace ApplyActionContol { plugin: PluginContext, nodeRef: StateTransform.Ref, state: State, - action: StateAction + action: StateAction, + initiallyCollapsed?: boolean } export interface ComponentState { @@ -49,7 +50,7 @@ class ApplyActionContol extends TransformContolBase<ApplyActionContol.Props, App private _getInfo = memoizeLatest((t: StateTransform.Ref, v: string) => StateTransformParameters.infoFromAction(this.plugin, this.props.state, this.props.action, this.props.nodeRef)); - state = { ref: this.props.nodeRef, version: this.props.state.transforms.get(this.props.nodeRef).version, error: void 0, isInitial: true, params: this.getInfo().initialValues, busy: false }; + state = { ref: this.props.nodeRef, version: this.props.state.transforms.get(this.props.nodeRef).version, error: void 0, isInitial: true, params: this.getInfo().initialValues, busy: false, isCollapsed: this.props.initiallyCollapsed }; static getDerivedStateFromProps(props: ApplyActionContol.Props, state: ApplyActionContol.ComponentState) { if (props.nodeRef === state.ref) return null;