diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts index 9553e5f24a94ef37e54aef18543c56c83a92fdbc..612800e4fe6c765762d1408fc60eed915539de3a 100644 --- a/src/mol-plugin/behavior/static/state.ts +++ b/src/mol-plugin/behavior/static/state.ts @@ -9,6 +9,8 @@ import { PluginContext } from '../../context'; import { StateTree, Transform, State } from 'mol-state'; import { PluginStateSnapshotManager } from 'mol-plugin/state/snapshots'; import { PluginStateObject as SO } from '../../state/objects'; +import { EmptyLoci, EveryLoci } from 'mol-model/loci'; +import { MarkerAction } from 'mol-geo/geometry/marker-data'; export function registerDefault(ctx: PluginContext) { SyncBehaviors(ctx); @@ -18,6 +20,8 @@ export function registerDefault(ctx: PluginContext) { RemoveObject(ctx); ToggleExpanded(ctx); ToggleVisibility(ctx); + Highlight(ctx); + ClearHighlight(ctx); Snapshots(ctx); } @@ -75,6 +79,35 @@ function setVisibilityVisitor(t: Transform, tree: StateTree, ctx: { state: State ctx.state.updateCellState(t.ref, { isHidden: ctx.value }); } +export function Highlight(ctx: PluginContext) { + PluginCommands.State.Highlight.subscribe(ctx, ({ state, ref }) => { + setHighlight(state, ref) + ctx.canvas3d.update() + ctx.canvas3d.requestDraw(true); + }); +} + +function setHighlight(state: State, root: Transform.Ref) { + StateTree.doPreOrder(state.tree, state.transforms.get(root), { state }, setHighlightVisitor); +} + +function setHighlightVisitor(t: Transform, tree: StateTree, ctx: { state: State }) { + const cell = ctx.state.select(t.ref)[0] + if (!cell) return + if (!SO.isRepresentation3D(cell.obj)) return; + cell.obj.data.mark(EveryLoci, MarkerAction.Highlight) + +} + +export function ClearHighlight(ctx: PluginContext) { + PluginCommands.State.ClearHighlight.subscribe(ctx, ({ state, ref }) => { + ctx.behaviors.canvas.highlightLoci.next({ loci: EmptyLoci }) + ctx.canvas3d.mark(EveryLoci, MarkerAction.RemoveHighlight); + ctx.canvas3d.update() + ctx.canvas3d.requestDraw(true); + }); +} + export function Snapshots(ctx: PluginContext) { PluginCommands.State.Snapshots.Clear.subscribe(ctx, () => { ctx.state.snapshots.clear(); diff --git a/src/mol-plugin/command.ts b/src/mol-plugin/command.ts index 34dee15c61b4b8c41202ea72f7efc25e78ff9a79..d948e992e3e67457386996e08471315328648d77 100644 --- a/src/mol-plugin/command.ts +++ b/src/mol-plugin/command.ts @@ -22,6 +22,8 @@ export const PluginCommands = { ToggleExpanded: PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }), ToggleVisibility: PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }), + Highlight: PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }), + ClearHighlight: PluginCommand<{ state: State, ref: Transform.Ref }>({ isImmediate: true }), Snapshots: { Add: PluginCommand<{ name?: string, description?: string }>({ isImmediate: true }), diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx index 5c56a979c7e226df685bbd70dcf26dcf46874e8d..01e85cc715b984d37961a48865ad6ffef2abd91f 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state-tree.tsx @@ -141,6 +141,18 @@ class StateTreeNodeLabel extends PluginComponent<{ nodeRef: string, state: State e.currentTarget.blur(); } + highlight = (e: React.MouseEvent<HTMLElement>) => { + e.preventDefault(); + PluginCommands.State.Highlight.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef }); + e.currentTarget.blur(); + } + + clearHighlight = (e: React.MouseEvent<HTMLElement>) => { + e.preventDefault(); + PluginCommands.State.ClearHighlight.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef }); + e.currentTarget.blur(); + } + render() { const n = this.props.state.transforms.get(this.props.nodeRef)!; const cell = this.props.state.cells.get(this.props.nodeRef)!; @@ -170,7 +182,7 @@ class StateTreeNodeLabel extends PluginComponent<{ nodeRef: string, state: State <span className='msp-icon msp-icon-visual-visibility' /> </button>; - return <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`}> + return <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight}> {isCurrent ? <b>{label}</b> : label} {children.size > 0 && <button onClick={this.toggleExpanded} className='msp-btn msp-btn-link msp-tree-toggle-exp-button'> <span className={`msp-icon msp-icon-${cellState.isCollapsed ? 'expand' : 'collapse'}`} />