diff --git a/src/apps/basic-wrapper/controls.tsx b/src/apps/basic-wrapper/controls.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e3b144fd9deae3808d82ce56c3554b4d530c0b11 --- /dev/null +++ b/src/apps/basic-wrapper/controls.tsx @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { PluginUIComponent } from 'mol-plugin/ui/base'; +import * as React from 'react'; +import { TransformUpdaterControl } from 'mol-plugin/ui/state/update-transform'; + +export class BasicWrapperControls extends PluginUIComponent { + + render() { + return <div style={{ overflowY: 'auto', display: 'block', height: '100%' }}> + <TransformUpdaterControl nodeRef='asm' /> + <TransformUpdaterControl nodeRef='seq-visual' header={{ name: 'Sequence Visual' }} /> + <TransformUpdaterControl nodeRef='het-visual' header={{ name: 'HET Visual' }} /> + <TransformUpdaterControl nodeRef='water-visual' header={{ name: 'Water Visual' }} initiallyCollapsed={true} /> + <TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} /> + </div>; + } +} \ No newline at end of file diff --git a/src/apps/basic-wrapper/index.ts b/src/apps/basic-wrapper/index.ts index 9ce98ddac0cc0b27808a39f163dc91f8df875cca..17da1682ad2b4869eefc36faeefd04d24e047879 100644 --- a/src/apps/basic-wrapper/index.ts +++ b/src/apps/basic-wrapper/index.ts @@ -15,6 +15,7 @@ import { PluginStateObject as PSO } from 'mol-plugin/state/objects'; import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in'; import { StateBuilder } from 'mol-state'; import { StripedResidues } from './coloring'; +import { BasicWrapperControls } from './controls'; require('mol-plugin/skin/light.scss') type SupportedFormats = 'cif' | 'pdb' @@ -30,6 +31,10 @@ class BasicWrapper { initial: { isExpanded: false, showControls: false + }, + controls: { + left: 'none', + right: BasicWrapperControls } } }); @@ -57,16 +62,16 @@ class BasicWrapper { private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) { visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }) .apply(StateTransforms.Representation.StructureRepresentation3D, - StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon')); + StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' }); visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }) .apply(StateTransforms.Representation.StructureRepresentation3D, - StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick')); + StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' }); visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }) .apply(StateTransforms.Representation.StructureRepresentation3D, - StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 })); + StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 }), { ref: 'water-visual' }); visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' }) .apply(StateTransforms.Representation.StructureRepresentation3D, - StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill')); + StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill'), { ref: 'ihm-visual' }); return visualRoot; } diff --git a/src/mol-plugin/skin/base/components/viewport.scss b/src/mol-plugin/skin/base/components/viewport.scss index 228e721a5d122265db9f71a2abea5b487b666c19..a03f6020ff54bff550ab2169f1e7d5c4d20877ed 100644 --- a/src/mol-plugin/skin/base/components/viewport.scss +++ b/src/mol-plugin/skin/base/components/viewport.scss @@ -63,6 +63,8 @@ } .msp-viewport-controls-scene-options { + overflow-y: auto; + max-height: 400px; width: 290px; background: $control-background; diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx index e6c0bc198bfdb2d7abf330d1aaab0b07a223a13d..600786fc9c5ccfdf007f4487b9437017e98f0bc6 100644 --- a/src/mol-plugin/ui/state/update-transform.tsx +++ b/src/mol-plugin/ui/state/update-transform.tsx @@ -4,19 +4,22 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { State, StateTransform } from 'mol-state'; +import { State, StateTransform, StateTransformer } from 'mol-state'; import { memoizeLatest } from 'mol-util/memoize'; import { StateTransformParameters, TransformContolBase } from './common'; import { Observable } from 'rxjs'; +import * as React from 'react'; +import { PluginUIComponent } from '../base'; -export { UpdateTransformContol }; +export { UpdateTransformContol, TransformUpdaterControl }; namespace UpdateTransformContol { export interface Props { transform: StateTransform, state: State, toggleCollapsed?: Observable<any>, - initiallyCollapsed?: boolean + initiallyCollapsed?: boolean, + customHeader?: StateTransformer.Definition['display'] } export interface ComponentState extends TransformContolBase.ComponentState { @@ -28,7 +31,7 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr applyAction() { return this.plugin.updateTransform(this.props.state, this.props.transform.ref, this.state.params); } getInfo() { return this._getInfo(this.props.transform); } getTransformerId() { return this.props.transform.transformer.id; } - getHeader() { return this.props.transform.transformer.definition.display; } + getHeader() { return this.props.customHeader || this.props.transform.transformer.definition.display; } canApply() { return !this.state.error && !this.state.busy && !this.state.isInitial; } applyText() { return this.canApply() ? 'Update' : 'Nothing to Update'; } isUpdate() { return true; } @@ -74,4 +77,25 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr }; return newState; } +} + +class TransformUpdaterControl extends PluginUIComponent<{ nodeRef: string, initiallyCollapsed?: boolean, header?: StateTransformer.Definition['display'] }> { + componentDidMount() { + this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => { + if (this.props.nodeRef !== ref || this.plugin.state.dataState !== state) return; + this.forceUpdate(); + }); + } + + render() { + const state = this.plugin.state.dataState; + const ref = this.props.nodeRef; + const cell = state.cells.get(ref)!; + + if (!cell || (cell.status !== 'ok' && cell.status !== 'error')) return null; + + const transform = cell.transform; + + return <UpdateTransformContol state={state} transform={transform} initiallyCollapsed={this.props.initiallyCollapsed} customHeader={this.props.header} />; + } } \ No newline at end of file diff --git a/src/mol-plugin/ui/viewport.tsx b/src/mol-plugin/ui/viewport.tsx index 5d202a78be80008496a776e22e47345f5dfcb837..3c9d3b4c84a920220e501127d963af84a81ef888 100644 --- a/src/mol-plugin/ui/viewport.tsx +++ b/src/mol-plugin/ui/viewport.tsx @@ -69,7 +69,7 @@ export class ViewportControls extends PluginUIComponent<{}, { isSettingsExpanded {this.icon('reset-scene', this.resetCamera, 'Reset Camera')}<br/> {this.icon('tools', this.toggleControls, 'Toggle Controls', this.plugin.layout.state.showControls)}<br/> {this.icon('expand-layout', this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}<br /> - {this.icon('settings', this.toggleSettingsExpanded, 'Settings', this.state.isSettingsExpanded)} + {this.icon('settings', this.toggleSettingsExpanded, 'Settings', this.state.isSettingsExpanded)}<br/> </div> {this.state.isSettingsExpanded && <div className='msp-viewport-controls-scene-options'> <ControlGroup header='Layout' initialExpanded={true}>