diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index f841c936382c7a440fa7f935db19cf6de87b1af0..13f4d4480d7ee680983295cdb2b06d562c25d4ec 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -29,6 +29,7 @@ import { VolumeRepresentationRegistry } from 'mol-repr/volume/registry'; import { PLUGIN_VERSION, PLUGIN_VERSION_DATE } from './version'; import { PluginLayout } from './layout'; import { List } from 'immutable'; +import { StateTransformParameters } from './ui/state/common'; export class PluginContext { private disposed = false; @@ -87,6 +88,7 @@ export class PluginContext { } readonly customModelProperties = new CustomPropertyRegistry(); + readonly customParamEditors = new Map<string, StateTransformParameters.Class>(); initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) { try { @@ -136,6 +138,16 @@ export class PluginContext { this.disposed = true; } + applyTransform(state: State, a: Transform.Ref, transformer: Transformer, params: any) { + const tree = state.tree.build().to(a).apply(transformer, params); + return PluginCommands.State.Update.dispatch(this, { state, tree }); + } + + updateTransform(state: State, a: Transform.Ref, params: any) { + const tree = state.build().to(a).update(params); + return PluginCommands.State.Update.dispatch(this, { state, tree }); + } + private initBuiltInBehavior() { BuiltInPluginBehaviors.State.registerDefault(this); BuiltInPluginBehaviors.Representation.registerDefault(this); @@ -145,7 +157,7 @@ export class PluginContext { merge(this.state.dataState.events.log, this.state.behaviorState.events.log).subscribe(e => this.events.log.next(e)); } - async initBehaviors() { + private async initBehaviors() { const tree = this.state.behaviorState.tree.build(); for (const b of this.spec.behaviors) { @@ -155,20 +167,18 @@ export class PluginContext { await this.runTask(this.state.behaviorState.updateTree(tree, true)); } - initDataActions() { + private initDataActions() { for (const a of this.spec.actions) { this.state.dataState.actions.add(a.action); } } - applyTransform(state: State, a: Transform.Ref, transformer: Transformer, params: any) { - const tree = state.tree.build().to(a).apply(transformer, params); - return PluginCommands.State.Update.dispatch(this, { state, tree }); - } + private initCustomParamEditors() { + if (!this.spec.customParamEditors) return; - updateTransform(state: State, a: Transform.Ref, params: any) { - const tree = state.build().to(a).update(params); - return PluginCommands.State.Update.dispatch(this, { state, tree }); + for (const [t, e] of this.spec.customParamEditors) { + this.customParamEditors.set(t.id, e); + } } constructor(public spec: PluginSpec) { @@ -178,6 +188,7 @@ export class PluginContext { this.initBehaviors(); this.initDataActions(); + this.initCustomParamEditors(); this.lociLabels = new LociLabelManager(this); diff --git a/src/mol-plugin/spec.ts b/src/mol-plugin/spec.ts index 86b95500864782a8d6d3767fe9643b8194616ece..7475b6ebd0446cd623ad7de994031eb321530218 100644 --- a/src/mol-plugin/spec.ts +++ b/src/mol-plugin/spec.ts @@ -14,6 +14,7 @@ export { PluginSpec } interface PluginSpec { actions: PluginSpec.Action[], behaviors: PluginSpec.Behavior[], + customParamEditors?: [StateAction | Transformer, StateTransformParameters.Class][] initialLayout?: PluginLayoutStateProps } diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 0df6a421c424eb64c2426ba8a3479f5f0afef48c..dcc0b6b9af09cfa667475e1059de255d3225edd6 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -140,17 +140,19 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ apply({ a, params }, plugin: PluginContext) { return Task.create('Build Assembly', async ctx => { const model = a.data; - const id = params.id; - const asm = ModelSymmetry.findAssembly(model, id || ''); + let id = params.id; + let asm = ModelSymmetry.findAssembly(model, id || ''); if (!!id && id !== 'deposited' && !asm) throw new Error(`Assembly '${id}' not found`); const base = Structure.ofModel(model); - if (!asm) { + if ((id && !asm) || model.symmetry.assemblies.length === 0) { if (!!id && id !== 'deposited') plugin.log.warn(`Model '${a.label}' has no assembly, returning deposited structure.`); const label = { label: a.data.label, description: structureDesc(base) }; return new SO.Molecule.Structure(base, label); } + asm = model.symmetry.assemblies[0]; + id = asm.id; const s = await StructureSymmetry.buildAssembly(base, id!).runInContext(ctx); const props = { label: `Assembly ${id}`, description: structureDesc(s) }; return new SO.Molecule.Structure(s, props); diff --git a/src/mol-plugin/ui/state/apply-action.tsx b/src/mol-plugin/ui/state/apply-action.tsx index 17482d70072e34909f0e0545b9c6e8986366a6e0..c82faeee8d7ce946b1dff0ddb087fb05382520f8 100644 --- a/src/mol-plugin/ui/state/apply-action.tsx +++ b/src/mol-plugin/ui/state/apply-action.tsx @@ -41,6 +41,7 @@ class ApplyActionContol extends TransformContolBase<ApplyActionContol.Props, App }); } getInfo() { return this._getInfo(this.props.nodeRef, this.props.state.transforms.get(this.props.nodeRef).version); } + getTransformerId() { return this.props.state.transforms.get(this.props.nodeRef).transformer.id; } getHeader() { return this.props.action.definition.display; } canApply() { return !this.state.error && !this.state.busy; } canAutoApply() { return false; } diff --git a/src/mol-plugin/ui/state/common.tsx b/src/mol-plugin/ui/state/common.tsx index a56615a85ba7e4579d5eb6626b204189726539f5..daf5f2568aeca146e4cbf638d614a67ddc2906cf 100644 --- a/src/mol-plugin/ui/state/common.tsx +++ b/src/mol-plugin/ui/state/common.tsx @@ -102,6 +102,7 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState abstract getInfo(): StateTransformParameters.Props['info']; abstract getHeader(): Transformer.Definition['display']; abstract canApply(): boolean; + abstract getTransformerId(): string; abstract canAutoApply(newParams: any): boolean; abstract applyText(): string; abstract isUpdate(): boolean; @@ -170,13 +171,18 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState const display = this.getHeader(); + const tId = this.getTransformerId(); + const ParamEditor: StateTransformParameters.Class = this.plugin.customParamEditors.has(tId) + ? this.plugin.customParamEditors.get(tId)! + : StateTransformParameters; + return <div className='msp-transform-wrapper'> <div className='msp-transform-header'> <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>{display.name}</button> {!this.state.isCollapsed && <button className='msp-btn msp-btn-link msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} style={{ float: 'right'}} title='Set default params'>↻</button>} </div> {!this.state.isCollapsed && <> - <StateTransformParameters info={info} events={this.events} params={this.state.params} isDisabled={this.state.busy} /> + <ParamEditor info={info} events={this.events} params={this.state.params} isDisabled={this.state.busy} /> <div className='msp-transform-apply-wrap'> <button className='msp-btn msp-btn-block msp-transform-refresh msp-form-control' title='Refresh params' onClick={this.refresh} disabled={this.state.busy || this.state.isInitial}> diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx index c7e89a1f43d4ba812913cecfc5572aa82787d822..ceff41e16f33a7debcd7edb87bb8693c79a58ff6 100644 --- a/src/mol-plugin/ui/state/update-transform.tsx +++ b/src/mol-plugin/ui/state/update-transform.tsx @@ -28,6 +28,7 @@ namespace UpdateTransformContol { class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Props, UpdateTransformContol.ComponentState> { 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; } canApply() { return !this.state.error && !this.state.busy && !this.state.isInitial; } applyText() { return this.canApply() ? 'Update' : 'Nothing to Update'; }