diff --git a/src/mol-plugin/behavior.ts b/src/mol-plugin/behavior.ts index d1b36b1199fb920c534f18f1aae9241077a97bf5..5b14cd765a2a14bc14e95862447029b4ff303577 100644 --- a/src/mol-plugin/behavior.ts +++ b/src/mol-plugin/behavior.ts @@ -6,13 +6,18 @@ export * from './behavior/behavior' -import * as State from './behavior/built-in/state' -import * as Representation from './behavior/built-in/representation' +import * as StaticState from './behavior/static/state' +import * as StaticRepresentation from './behavior/static/representation' +import * as StaticCamera from './behavior/static/representation' + +import * as DynamicRepresentation from './behavior/dynamic/representation' export const BuiltInPluginBehaviors = { - State, + State: StaticState, + Representation: StaticRepresentation, + Camera: StaticCamera } export const PluginBehaviors = { - Representation + Representation: DynamicRepresentation } \ No newline at end of file diff --git a/src/mol-plugin/behavior/built-in/camera.ts b/src/mol-plugin/behavior/built-in/camera.ts deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/mol-plugin/behavior/built-in/representation.ts b/src/mol-plugin/behavior/built-in/representation.ts deleted file mode 100644 index 70cfeedf867b97d0904f41765c50604e96793f0f..0000000000000000000000000000000000000000 --- a/src/mol-plugin/behavior/built-in/representation.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { PluginBehavior } from '../behavior'; -import { PluginStateObject as SO } from '../../state/objects'; -import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci'; -import { MarkerAction } from 'mol-geo/geometry/marker-data'; - -export const AddRepresentationToCanvas = PluginBehavior.create({ - name: 'add-representation-to-canvas', - ctor: class extends PluginBehavior.Handler { - register(): void { - this.subscribeObservable(this.ctx.events.state.data.object.created, o => { - if (!SO.isRepresentation3D(o.obj)) return; - this.ctx.canvas3d.add(o.obj.data); - this.ctx.canvas3d.requestDraw(true); - }); - this.subscribeObservable(this.ctx.events.state.data.object.updated, o => { - if (o.oldObj && SO.isRepresentation3D(o.oldObj)) { - this.ctx.canvas3d.remove(o.oldObj.data); - this.ctx.canvas3d.requestDraw(true); - o.oldObj.data.destroy(); - } - - const oo = o.obj; - if (!SO.isRepresentation3D(oo)) return; - this.ctx.canvas3d.add(oo.data); - this.ctx.canvas3d.requestDraw(true); - }); - this.subscribeObservable(this.ctx.events.state.data.object.removed, o => { - const oo = o.obj; - if (!SO.isRepresentation3D(oo)) return; - this.ctx.canvas3d.remove(oo.data); - this.ctx.canvas3d.requestDraw(true); - oo.data.destroy(); - }); - // this.subscribeObservable(this.ctx.events.state.data.object.replaced, o => { - // if (o.oldObj && SO.isRepresentation3D(o.oldObj)) { - // this.ctx.canvas3d.remove(o.oldObj.data); - // this.ctx.canvas3d.requestDraw(true); - // o.oldObj.data.destroy(); - // } - // if (o.newObj && SO.isRepresentation3D(o.newObj)) { - // this.ctx.canvas3d.add(o.newObj.data); - // this.ctx.canvas3d.requestDraw(true); - // } - // }); - } - }, - display: { name: 'Add Representation To Canvas', group: 'Data' } -}); - -export const HighlightLoci = PluginBehavior.create({ - name: 'representation-highlight-loci', - ctor: class extends PluginBehavior.Handler { - register(): void { - let prevLoci: Loci = EmptyLoci, prevRepr: any = void 0; - this.subscribeObservable(this.ctx.behaviors.canvas.highlightLoci, current => { - if (!this.ctx.canvas3d) return; - - if (current.repr !== prevRepr || !areLociEqual(current.loci, prevLoci)) { - this.ctx.canvas3d.mark(prevLoci, MarkerAction.RemoveHighlight); - this.ctx.canvas3d.mark(current.loci, MarkerAction.Highlight); - prevLoci = current.loci; - prevRepr = current.repr; - } - }); - } - }, - display: { name: 'Highlight Loci on Canvas', group: 'Data' } -}); - -export const SelectLoci = PluginBehavior.create({ - name: 'representation-select-loci', - ctor: class extends PluginBehavior.Handler { - register(): void { - this.subscribeObservable(this.ctx.behaviors.canvas.selectLoci, ({ loci }) => { - if (!this.ctx.canvas3d) return; - this.ctx.canvas3d.mark(loci, MarkerAction.Toggle); - }); - } - }, - display: { name: 'Select Loci on Canvas', group: 'Data' } -}); \ No newline at end of file diff --git a/src/mol-plugin/behavior/built-in/state.ts b/src/mol-plugin/behavior/built-in/state.ts deleted file mode 100644 index f6c3d253b3f22d14ffec43458732b01871930a88..0000000000000000000000000000000000000000 --- a/src/mol-plugin/behavior/built-in/state.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { PluginCommands } from '../../command'; -import { PluginContext } from '../../context'; - -export function registerAll(ctx: PluginContext) { - SetCurrentObject(ctx); - Update(ctx); - ApplyAction(ctx); - RemoveObject(ctx); - ToggleExpanded(ctx); -} - -export function SetCurrentObject(ctx: PluginContext) { - PluginCommands.State.SetCurrentObject.subscribe(ctx, ({ state, ref }) => state.setCurrent(ref)); -} - -export function Update(ctx: PluginContext) { - PluginCommands.State.Update.subscribe(ctx, ({ state, tree }) => ctx.runTask(state.update(tree))); -} - -export function ApplyAction(ctx: PluginContext) { - PluginCommands.State.ApplyAction.subscribe(ctx, ({ state, action, ref }) => ctx.runTask(state.apply(action.action, action.params, ref))); -} - -export function RemoveObject(ctx: PluginContext) { - PluginCommands.State.RemoveObject.subscribe(ctx, ({ state, ref }) => { - const tree = state.tree.build().delete(ref).getTree(); - return ctx.runTask(state.update(tree)); - }); -} - -export function ToggleExpanded(ctx: PluginContext) { - PluginCommands.State.ToggleExpanded.subscribe(ctx, ({ state, ref }) => state.updateCellState(ref, ({ isCollapsed }) => ({ isCollapsed: !isCollapsed }))); -} - -// export const SetCurrentObject = PluginBehavior.create({ -// name: 'set-current-data-object-behavior', -// ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.SetCurrentObject, ({ state, ref }, ctx) => state.setCurrent(ref)), -// display: { name: 'Set Current Handler', group: 'Data' } -// }); - -// export const Update = PluginBehavior.create({ -// name: 'update-data-behavior', -// ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.Update, ({ state, tree }, ctx) => ctx.runTask(state.update(tree))), -// display: { name: 'Update Data Handler', group: 'Data' } -// }); - -// export const ApplyAction = PluginBehavior.create({ -// name: 'update-data-behavior', -// ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.Update, ({ state, tree }, ctx) => ctx.runTask(state.update(tree))), -// display: { name: 'Update Data Handler', group: 'Data' } -// }); - -// export const RemoveObject = PluginBehavior.create({ -// name: 'remove-object-data-behavior', -// ctor: PluginBehavior.simpleCommandHandler(PluginCommands.State.RemoveObject, ({ state, ref }, ctx) => { -// const tree = state.tree.build().delete(ref).getTree(); -// return ctx.runTask(state.update(tree)); -// }), -// display: { name: 'Remove Object Handler', group: 'Data' } -// }); \ No newline at end of file diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9d211670397911053a2d16bad52bcb63a717951 --- /dev/null +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { PluginBehavior } from '../behavior'; +import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci'; +import { MarkerAction } from 'mol-geo/geometry/marker-data'; + +export const HighlightLoci = PluginBehavior.create({ + name: 'representation-highlight-loci', + ctor: class extends PluginBehavior.Handler { + register(): void { + let prevLoci: Loci = EmptyLoci, prevRepr: any = void 0; + this.subscribeObservable(this.ctx.behaviors.canvas.highlightLoci, current => { + if (!this.ctx.canvas3d) return; + + if (current.repr !== prevRepr || !areLociEqual(current.loci, prevLoci)) { + this.ctx.canvas3d.mark(prevLoci, MarkerAction.RemoveHighlight); + this.ctx.canvas3d.mark(current.loci, MarkerAction.Highlight); + prevLoci = current.loci; + prevRepr = current.repr; + } + }); + } + }, + display: { name: 'Highlight Loci on Canvas', group: 'Data' } +}); + +export const SelectLoci = PluginBehavior.create({ + name: 'representation-select-loci', + ctor: class extends PluginBehavior.Handler { + register(): void { + this.subscribeObservable(this.ctx.behaviors.canvas.selectLoci, ({ loci }) => { + if (!this.ctx.canvas3d) return; + this.ctx.canvas3d.mark(loci, MarkerAction.Toggle); + }); + } + }, + display: { name: 'Select Loci on Canvas', group: 'Data' } +}); \ No newline at end of file diff --git a/src/mol-plugin/behavior/static/camera.ts b/src/mol-plugin/behavior/static/camera.ts new file mode 100644 index 0000000000000000000000000000000000000000..808b06a21bdb99d50f2a728163a7b453935afbe6 --- /dev/null +++ b/src/mol-plugin/behavior/static/camera.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { PluginContext } from 'mol-plugin/context'; + +export function registerDefault(ctx: PluginContext) { +} diff --git a/src/mol-plugin/behavior/static/representation.ts b/src/mol-plugin/behavior/static/representation.ts new file mode 100644 index 0000000000000000000000000000000000000000..9cf4423633247c50c74ba8aa52644e5ed5c9b369 --- /dev/null +++ b/src/mol-plugin/behavior/static/representation.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { PluginStateObject as SO } from '../../state/objects'; +import { PluginContext } from 'mol-plugin/context'; + +export function registerDefault(ctx: PluginContext) { + SyncRepresentationToCanvas(ctx); +} + +export function SyncRepresentationToCanvas(ctx: PluginContext) { + ctx.events.state.data.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 => { + if (e.oldObj && SO.isRepresentation3D(e.oldObj)) { + ctx.canvas3d.remove(e.oldObj.data); + ctx.canvas3d.requestDraw(true); + e.oldObj.data.destroy(); + } + + if (!SO.isRepresentation3D(e.obj)) return; + + // TODO: update visiblity + ctx.canvas3d.add(e.obj.data); + ctx.canvas3d.requestDraw(true); + }); + ctx.events.state.data.object.removed.subscribe(e => { + const oo = e.obj; + if (!SO.isRepresentation3D(oo)) return; + ctx.canvas3d.remove(oo.data); + ctx.canvas3d.requestDraw(true); + oo.data.destroy(); + }); +} + +export function UpdateRepresentationVisibility(ctx: PluginContext) { + ctx.events.state.data.object.cellState.subscribe(e => { + const cell = e.state.cells.get(e.ref)!; + if (!SO.isRepresentation3D(cell.obj)) return; + + // TODO: update visiblity + }) +} \ No newline at end of file diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts new file mode 100644 index 0000000000000000000000000000000000000000..06371c93095b95784f23b382ed701ebbb7927e23 --- /dev/null +++ b/src/mol-plugin/behavior/static/state.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { PluginCommands } from '../../command'; +import { PluginContext } from '../../context'; +import { StateTree, Transform, State } from 'mol-state'; + +export function registerDefault(ctx: PluginContext) { + SetCurrentObject(ctx); + Update(ctx); + ApplyAction(ctx); + RemoveObject(ctx); + ToggleExpanded(ctx); + ToggleVisibility(ctx); +} + +export function SetCurrentObject(ctx: PluginContext) { + PluginCommands.State.SetCurrentObject.subscribe(ctx, ({ state, ref }) => state.setCurrent(ref)); +} + +export function Update(ctx: PluginContext) { + PluginCommands.State.Update.subscribe(ctx, ({ state, tree }) => ctx.runTask(state.update(tree))); +} + +export function ApplyAction(ctx: PluginContext) { + PluginCommands.State.ApplyAction.subscribe(ctx, ({ state, action, ref }) => ctx.runTask(state.apply(action.action, action.params, ref))); +} + +export function RemoveObject(ctx: PluginContext) { + PluginCommands.State.RemoveObject.subscribe(ctx, ({ state, ref }) => { + const tree = state.tree.build().delete(ref).getTree(); + return ctx.runTask(state.update(tree)); + }); +} + +export function ToggleExpanded(ctx: PluginContext) { + PluginCommands.State.ToggleExpanded.subscribe(ctx, ({ state, ref }) => state.updateCellState(ref, ({ isCollapsed }) => ({ isCollapsed: !isCollapsed }))); +} + +export function ToggleVisibility(ctx: PluginContext) { + PluginCommands.State.ToggleVisibility.subscribe(ctx, ({ state, ref }) => setVisibility(state, ref, !state.tree.cellStates.get(ref).isHidden)); +} + +function setVisibility(state: State, root: Transform.Ref, value: boolean) { + StateTree.doPreOrder(state.tree, state.tree.transforms.get(root), { state, value }, setVisibilityVisitor); +} + +function setVisibilityVisitor(t: Transform, tree: StateTree, ctx: { state: State, value: boolean }) { + ctx.state.updateCellState(t.ref, { isHidden: ctx.value }); +} \ No newline at end of file diff --git a/src/mol-plugin/command/state.ts b/src/mol-plugin/command/state.ts index e28518f23fdab4ec5a4ed58987fedbbc9efd2d5e..c8e87bc7a2b0bd01136e959ecee9608cf17debbc 100644 --- a/src/mol-plugin/command/state.ts +++ b/src/mol-plugin/command/state.ts @@ -16,4 +16,6 @@ export const Update = PluginCommand<{ state: State, tree: State.Tree | State.Bui export const RemoveObject = PluginCommand<{ state: State, ref: Transform.Ref }>(); -export const ToggleExpanded = PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }); \ No newline at end of file +export const ToggleExpanded = PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }); + +export const ToggleVisibility = PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }); \ No newline at end of file diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 7e98cd71117d65a2a1b82987907608a2bd85245a..c5a4f2334389fcb601c53693c6b2906522f83a7a 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -82,12 +82,13 @@ export class PluginContext { } private initBuiltInBehavior() { - BuiltInPluginBehaviors.State.registerAll(this); + BuiltInPluginBehaviors.State.registerDefault(this); + BuiltInPluginBehaviors.Representation.registerDefault(this); + BuiltInPluginBehaviors.Camera.registerDefault(this); } async _test_initBehaviors() { const tree = this.state.behavior.tree.build() - .toRoot().apply(PluginBehaviors.Representation.AddRepresentationToCanvas, { ref: PluginBehaviors.Representation.AddRepresentationToCanvas.id }) .toRoot().apply(PluginBehaviors.Representation.HighlightLoci, { ref: PluginBehaviors.Representation.HighlightLoci.id }) .toRoot().apply(PluginBehaviors.Representation.SelectLoci, { ref: PluginBehaviors.Representation.SelectLoci.id }) .getTree(); diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx index 2438061838fcbeae7194b0e8fe11ddbd13b03d4d..d89911d2e682e5b4ca8785928404f1aa5300650a 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state-tree.tsx @@ -66,9 +66,16 @@ export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: Sta }}>{cellState.isCollapsed ? '+' : '-'}</a>] </>; + const visibility = <> + [<a href='#' onClick={e => { + e.preventDefault(); + PluginCommands.State.ToggleVisibility.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef }); + }}>{cellState.isHidden ? 'H' : 'V'}</a>] + </>; + const children = this.props.state.tree.children.get(this.props.nodeRef); return <div> - {remove}{children.size === 0 ? void 0 : expander} {label} + {remove}{visibility}{children.size === 0 ? void 0 : expander} {label} {cellState.isCollapsed || children.size === 0 ? void 0 : <div style={{ marginLeft: '7px', paddingLeft: '3px', borderLeft: '1px solid #999' }}>{children.map(c => <StateTreeNode state={this.props.state} nodeRef={c!} key={c} />)}</div>