diff --git a/src/mol-plugin/behavior/static/camera.ts b/src/mol-plugin/behavior/static/camera.ts index 48a47e214091910efa7ebf44416b7ffee241502f..8aa1074d5a0cb96f0eed422ab4bf46552831b3da 100644 --- a/src/mol-plugin/behavior/static/camera.ts +++ b/src/mol-plugin/behavior/static/camera.ts @@ -17,7 +17,7 @@ export function registerDefault(ctx: PluginContext) { export function Reset(ctx: PluginContext) { PluginCommands.Camera.Reset.subscribe(ctx, () => { - const sel = ctx.state.data.select(q => q.root.subtree().ofType(SO.Molecule.Structure)); + const sel = ctx.state.dataState.select(q => q.root.subtree().ofType(SO.Molecule.Structure)); if (!sel.length) return; const center = (sel[0].obj! as SO.Molecule.Structure).data.boundary.sphere.center; diff --git a/src/mol-plugin/behavior/static/representation.ts b/src/mol-plugin/behavior/static/representation.ts index 9cf4423633247c50c74ba8aa52644e5ed5c9b369..714c16554ff87b9982df238d24dd4017c037aff7 100644 --- a/src/mol-plugin/behavior/static/representation.ts +++ b/src/mol-plugin/behavior/static/representation.ts @@ -12,14 +12,15 @@ export function registerDefault(ctx: PluginContext) { } export function SyncRepresentationToCanvas(ctx: PluginContext) { - ctx.events.state.data.object.created.subscribe(e => { + const events = ctx.state.dataState.events; + events.object.created.subscribe(e => { if (!SO.isRepresentation3D(e.obj)) return; ctx.canvas3d.add(e.obj.data); ctx.canvas3d.requestDraw(true); // TODO: update visiblity }); - ctx.events.state.data.object.updated.subscribe(e => { + events.object.updated.subscribe(e => { if (e.oldObj && SO.isRepresentation3D(e.oldObj)) { ctx.canvas3d.remove(e.oldObj.data); ctx.canvas3d.requestDraw(true); @@ -32,7 +33,7 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) { ctx.canvas3d.add(e.obj.data); ctx.canvas3d.requestDraw(true); }); - ctx.events.state.data.object.removed.subscribe(e => { + events.object.removed.subscribe(e => { const oo = e.obj; if (!SO.isRepresentation3D(oo)) return; ctx.canvas3d.remove(oo.data); @@ -42,7 +43,7 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) { } export function UpdateRepresentationVisibility(ctx: PluginContext) { - ctx.events.state.data.object.cellState.subscribe(e => { + ctx.state.dataState.events.object.cellState.subscribe(e => { const cell = e.state.cells.get(e.ref)!; if (!SO.isRepresentation3D(cell.obj)) return; diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index b112f78f83c62ee36d731b941ed80e8b6a2fccf4..0051e50d906c88d6e77ea5a2c9c01c7fff0c0035 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -30,8 +30,16 @@ export class PluginContext { readonly events = { state: { - data: this.state.data.events, - behavior: this.state.behavior.events, + object: { + cellState: merge(this.state.dataState.events.object.cellState, this.state.behaviorState.events.object.cellState), + cellCreated: merge(this.state.dataState.events.object.cellCreated, this.state.behaviorState.events.object.cellCreated), + + created: merge(this.state.dataState.events.object.created, this.state.behaviorState.events.object.created), + removed: merge(this.state.dataState.events.object.removed, this.state.behaviorState.events.object.removed), + updated: merge(this.state.dataState.events.object.updated, this.state.behaviorState.events.object.updated) + }, + // data: this.state.dataState.events, + // behavior: this.state.behaviorState.events, cameraSnapshots: this.state.cameraSnapshots.events, snapshots: this.state.snapshots.events, }, @@ -40,10 +48,10 @@ export class PluginContext { }; readonly behaviors = { - state: { - data: this.state.data.behaviors, - behavior: this.state.behavior.behaviors - }, + // state: { + // data: this.state.dataState.behaviors, + // behavior: this.state.behaviorState.behaviors + // }, canvas: { highlightLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }), selectLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }), @@ -98,20 +106,20 @@ export class PluginContext { BuiltInPluginBehaviors.Representation.registerDefault(this); BuiltInPluginBehaviors.Camera.registerDefault(this); - merge(this.state.data.events.log, this.state.behavior.events.log).subscribe(e => this.events.log.next(e)); + merge(this.state.dataState.events.log, this.state.behaviorState.events.log).subscribe(e => this.events.log.next(e)); } async _test_initBehaviors() { - const tree = this.state.behavior.tree.build() + const tree = this.state.behaviorState.tree.build() .toRoot().apply(PluginBehaviors.Representation.HighlightLoci, { ref: PluginBehaviors.Representation.HighlightLoci.id }) .toRoot().apply(PluginBehaviors.Representation.SelectLoci, { ref: PluginBehaviors.Representation.SelectLoci.id }) .getTree(); - await this.runTask(this.state.behavior.update(tree)); + await this.runTask(this.state.behaviorState.update(tree)); } _test_initDataActions() { - this.state.data.actions + this.state.dataState.actions .add(CreateStructureFromPDBe) .add(StateTransforms.Data.Download) .add(StateTransforms.Data.ParseCif) @@ -132,18 +140,17 @@ export class PluginContext { } private initEvents() { - merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => { + this.events.state.object.created.subscribe(o => { if (!SO.isBehavior(o.obj)) return; - console.log('registering behavior', o.obj.label); o.obj.data.register(); }); - merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => { + this.events.state.object.removed.subscribe(o => { if (!SO.isBehavior(o.obj)) return; o.obj.data.unregister(); }); - merge(this.events.state.data.object.updated, this.events.state.behavior.object.updated).subscribe(o => { + this.events.state.object.updated.subscribe(o => { if (o.action === 'recreate') { if (o.oldObj && SO.isBehavior(o.oldObj)) o.oldObj.data.unregister(); if (o.obj && SO.isBehavior(o.obj)) o.obj.data.register(); diff --git a/src/mol-plugin/state.ts b/src/mol-plugin/state.ts index 15dbf204e714d1908ec5be732d5db8769a7a92a5..ae001017ff1d0a41a2cd57403506b46a81b0d667 100644 --- a/src/mol-plugin/state.ts +++ b/src/mol-plugin/state.ts @@ -10,20 +10,37 @@ import { Camera } from 'mol-canvas3d/camera'; import { PluginBehavior } from './behavior'; import { CameraSnapshotManager } from './state/camera'; import { PluginStateSnapshotManager } from './state/snapshots'; - +import { RxEventHelper } from 'mol-util/rx-event-helper'; export { PluginState } class PluginState { - readonly data: State; - readonly behavior: State; + private ev = RxEventHelper.create(); + + readonly dataState: State; + readonly behaviorState: State; readonly cameraSnapshots = new CameraSnapshotManager(); readonly snapshots = new PluginStateSnapshotManager(); + readonly behavior = { + kind: this.ev.behavior<PluginState.Kind>('data'), + currentObject: this.ev.behavior<State.ObjectEvent>({} as any) + } + + setKind(kind: PluginState.Kind) { + const current = this.behavior.kind.value; + if (kind !== current) { + this.behavior.kind.next(kind); + this.behavior.currentObject.next(kind === 'data' + ? this.dataState.behaviors.currentObject.value + : this.behaviorState.behaviors.currentObject.value) + } + } + getSnapshot(): PluginState.Snapshot { return { - data: this.data.getSnapshot(), - behaviour: this.behavior.getSnapshot(), + data: this.dataState.getSnapshot(), + behaviours: this.behaviorState.getSnapshot(), cameraSnapshots: this.cameraSnapshots.getStateSnapshot(), canvas3d: { camera: this.plugin.canvas3d.camera.getSnapshot() @@ -32,29 +49,41 @@ class PluginState { } async setSnapshot(snapshot: PluginState.Snapshot) { - await this.plugin.runTask(this.behavior.setSnapshot(snapshot.behaviour)); - await this.plugin.runTask(this.data.setSnapshot(snapshot.data)); + await this.plugin.runTask(this.behaviorState.setSnapshot(snapshot.behaviours)); + await this.plugin.runTask(this.dataState.setSnapshot(snapshot.data)); this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots); this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera); this.plugin.canvas3d.requestDraw(true); } dispose() { - this.data.dispose(); - this.behavior.dispose(); + this.ev.dispose(); + this.dataState.dispose(); + this.behaviorState.dispose(); this.cameraSnapshots.dispose(); } constructor(private plugin: import('./context').PluginContext) { - this.data = State.create(new SO.Root({ }), { globalContext: plugin }); - this.behavior = State.create(new PluginBehavior.Root({ }), { globalContext: plugin }); + this.dataState = State.create(new SO.Root({ }), { globalContext: plugin }); + this.behaviorState = State.create(new PluginBehavior.Root({ }), { globalContext: plugin }); + + this.dataState.behaviors.currentObject.subscribe(o => { + if (this.behavior.kind.value === 'data') this.behavior.currentObject.next(o); + }); + this.behaviorState.behaviors.currentObject.subscribe(o => { + if (this.behavior.kind.value === 'behavior') this.behavior.currentObject.next(o); + }); + + this.behavior.currentObject.next(this.dataState.behaviors.currentObject.value); } } namespace PluginState { + export type Kind = 'data' | 'behavior' + export interface Snapshot { data: State.Snapshot, - behaviour: State.Snapshot, + behaviours: State.Snapshot, cameraSnapshots: CameraSnapshotManager.StateSnapshot, canvas3d: { camera: Camera.Snapshot diff --git a/src/mol-plugin/ui/action.tsx b/src/mol-plugin/ui/action.tsx index b6f6eafed6b9293723331daa29e019cea1abd451..65113bf64ba9844a0e6e9d8fd4d4c4c638921adc 100644 --- a/src/mol-plugin/ui/action.tsx +++ b/src/mol-plugin/ui/action.tsx @@ -118,17 +118,23 @@ class ActionContol extends PluginComponent<ActionContol.Props, { params: any, in state = this.defaultState() + nothingToUpdate() { + return <div>Nothing to update</div>; + } + render() { - console.log('render', this.props.nodeRef, this.action.id); const cell = this.cell; - if (cell.status !== 'ok' || (this.isUpdate && cell.transform.ref === Transform.RootRef)) return null; + if (cell.status !== 'ok' || (this.isUpdate && cell.transform.ref === Transform.RootRef)) return this.nothingToUpdate(); + + const paramDefs = this.getParamDefinitions(); + if (this.isUpdate && Object.keys(paramDefs).length === 0) return this.nothingToUpdate(); const action = this.action; return <div> <div style={{ borderBottom: '1px solid #999', marginBottom: '5px' }}><h3>{(action.definition.display && action.definition.display.name) || action.id}</h3></div> - <ParameterControls params={this.getParamDefinitions()} values={this.state.params} changes={this.changes} onEnter={this.onEnter} isEnabled={!this.state.busy} /> + <ParameterControls params={paramDefs} values={this.state.params} changes={this.changes} onEnter={this.onEnter} isEnabled={!this.state.busy} /> <div style={{ textAlign: 'right' }}> <span style={{ color: 'red' }}>{this.state.error}</span> diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index 095db8a58dd69434c28041e3a0bc30db4b5772bd..610b37c92f2b55f2c71a4045e494f47c30b41e83 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -22,15 +22,15 @@ export class TrajectoryControls extends PluginComponent { return <div> <b>Trajectory: </b> <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, { - state: this.plugin.state.data, + state: this.plugin.state.dataState, action: UpdateTrajectory.create({ action: 'advance', by: -1 }) })}><<</button> <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, { - state: this.plugin.state.data, + state: this.plugin.state.dataState, action: UpdateTrajectory.create({ action: 'reset' }) })}>Reset</button> <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, { - state: this.plugin.state.data, + state: this.plugin.state.dataState, action: UpdateTrajectory.create({ action: 'advance', by: +1 }) })}>>></button><br /> </div> diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index 6fe4d445b723fa0e48fd73b0f49a9dc46b540aba..901d23d0d7b8f1ade3d575156c8b2e1962b53364 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -10,7 +10,6 @@ import { StateTree } from './state-tree'; import { Viewport, ViewportControls } from './viewport'; import { Controls, TrajectoryControls } from './controls'; import { PluginComponent, PluginReactContext } from './base'; -import { merge } from 'rxjs'; import { CameraSnapshots } from './camera'; import { StateSnapshots } from './state'; import { List } from 'immutable'; @@ -18,15 +17,14 @@ import { LogEntry } from 'mol-util/log-entry'; import { formatTime } from 'mol-util'; import { BackgroundTaskProgress } from './task'; import { ActionContol } from './action'; +import { PluginState } from 'mol-plugin/state'; export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { render() { return <PluginReactContext.Provider value={this.props.plugin}> <div style={{ position: 'absolute', width: '100%', height: '100%', fontFamily: 'monospace' }}> <div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll', padding: '10px' }}> - <StateTree state={this.props.plugin.state.data} /> - <h3>Behaviors</h3> - <StateTree state={this.props.plugin.state.behavior} /> + <State /> </div> <div style={{ position: 'absolute', left: '350px', right: '300px', top: '0', bottom: '100px' }}> <Viewport /> @@ -55,6 +53,26 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { } } +export class State extends PluginComponent { + componentDidMount() { + this.subscribe(this.plugin.state.behavior.kind, () => this.forceUpdate()); + } + + set(kind: PluginState.Kind) { + // TODO: do command for this? + this.plugin.state.setKind(kind); + } + + render() { + const kind = this.plugin.state.behavior.kind.value; + return <> + <button onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal'}}>Data</button> + <button onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal'}}>Behavior</button> + <StateTree state={kind === 'data' ? this.plugin.state.dataState : this.plugin.state.behaviorState} /> + </> + } +} + export class Log extends PluginComponent<{}, { entries: List<LogEntry> }> { private wrapper = React.createRef<HTMLDivElement>(); @@ -85,33 +103,33 @@ export class Log extends PluginComponent<{}, { entries: List<LogEntry> }> { } export class CurrentObject extends PluginComponent { + get current() { + return this.plugin.state.behavior.currentObject.value; + } + componentDidMount() { - // let current: State.ObjectEvent | undefined = void 0; - this.subscribe(merge(this.plugin.behaviors.state.data.currentObject, this.plugin.behaviors.state.behavior.currentObject), o => { - // current = o; - this.forceUpdate() + this.subscribe(this.plugin.state.behavior.currentObject, o => { + this.forceUpdate(); }); - this.subscribe(this.plugin.events.state.data.object.updated, ({ ref, state }) => { - console.log('curr event', +new Date); - const current = this.plugin.behaviors.state.data.currentObject.value; + this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => { + const current = this.current; if (current.ref !== ref || current.state !== state) return; - console.log('curr event pass', +new Date); this.forceUpdate(); }); } render() { - console.log('curr', +new Date); - - const current = this.plugin.behaviors.state.data.currentObject.value; + const current = this.current; const ref = current.ref; // const n = this.props.plugin.state.data.tree.nodes.get(ref)!; - const obj = this.plugin.state.data.cells.get(ref)!; + const obj = current.state.cells.get(ref)!; const type = obj && obj.obj ? obj.obj.type : void 0; + console.log(obj); + const actions = type ? current.state.actions.fromType(type) : [] diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx index 7912c0a43649c3a71ce36beab43d03484329eabb..13cde81cc77c2fff83f9b8b6c34d3f077df49e73 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state-tree.tsx @@ -9,7 +9,6 @@ import { PluginStateObject } from 'mol-plugin/state/objects'; import { State } from 'mol-state' import { PluginCommands } from 'mol-plugin/command'; import { PluginComponent } from './base'; -import { merge } from 'rxjs'; export class StateTree extends PluginComponent<{ state: State }, { }> { componentDidMount() { @@ -28,7 +27,7 @@ export class StateTree extends PluginComponent<{ state: State }, { }> { export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: State }, { }> { componentDidMount() { - this.subscribe(merge(this.plugin.events.state.data.object.cellState, this.plugin.events.state.behavior.object.cellState), o => { + this.subscribe(this.plugin.events.state.object.cellState, o => { if (o.ref === this.props.nodeRef && o.state === this.props.state) this.forceUpdate(); }); }