Browse Source

mol-plugin: loci label support

David Sehnal 6 years ago
parent
commit
f2da4303d3

+ 15 - 2
src/mol-plugin/behavior/dynamic/representation.ts

@@ -7,6 +7,8 @@
 import { PluginBehavior } from '../behavior';
 import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
+import { labelFirst } from 'mol-theme/label';
+import { PluginContext } from 'mol-plugin/context';
 
 export const HighlightLoci = PluginBehavior.create({
     name: 'representation-highlight-loci',
@@ -25,7 +27,7 @@ export const HighlightLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Highlight Loci on Canvas', group: 'Data' }
+    display: { name: 'Highlight Loci on Canvas', group: 'Representation' }
 });
 
 export const SelectLoci = PluginBehavior.create({
@@ -38,5 +40,16 @@ export const SelectLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Select Loci on Canvas', group: 'Data' }
+    display: { name: 'Select Loci on Canvas', group: 'Representation' }
+});
+
+export const DefaultLociLabelProvider = PluginBehavior.create({
+    name: 'default-loci-label-provider',
+    ctor: class implements PluginBehavior<undefined> {
+        private f = labelFirst;
+        register(): void { this.ctx.lociLabels.addProvider(this.f); }
+        unregister() { this.ctx.lociLabels.removeProvider(this.f); }
+        constructor(protected ctx: PluginContext) { }
+    },
+    display: { name: 'Provide Default Loci Label', group: 'Representation' }
 });

+ 9 - 1
src/mol-plugin/context.ts

@@ -22,6 +22,7 @@ import { PluginSpec } from './spec';
 import { PluginState } from './state';
 import { TaskManager } from './util/task-manager';
 import { Color } from 'mol-util/color';
+import { LociLabelEntry, LociLabelManager } from './util/loci-label-manager';
 
 export class PluginContext {
     private disposed = false;
@@ -47,9 +48,14 @@ export class PluginContext {
             snapshots: this.state.snapshots.events,
         },
         log: this.ev<LogEntry>(),
-        task: this.tasks.events
+        task: this.tasks.events,
+        labels: {
+            highlight: this.ev<{ entries: ReadonlyArray<LociLabelEntry> }>()
+        }
     };
 
+    readonly lociLabels: LociLabelManager;
+
     readonly structureReprensentation = {
         registry: new StructureRepresentationRegistry(),
         themeCtx: { colorThemeRegistry: new ColorTheme.Registry(), sizeThemeRegistry: new SizeTheme.Registry() } as ThemeRegistryContext
@@ -145,6 +151,8 @@ export class PluginContext {
 
         this.initBehaviors();
         this.initDataActions();
+
+        this.lociLabels = new LociLabelManager(this);
     }
 
     // settings = ;

+ 13 - 12
src/mol-plugin/index.ts

@@ -13,6 +13,7 @@ import { PluginSpec } from './spec';
 import { CreateStructureFromPDBe } from './state/actions/basic';
 import { StateTransforms } from './state/transforms';
 import { PluginBehaviors } from './behavior';
+import { LogEntry } from 'mol-util/log-entry';
 
 function getParam(name: string, regex: string): string {
     let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
@@ -31,7 +32,8 @@ const DefaultSpec: PluginSpec = {
     ],
     behaviors: [
         PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci),
-        PluginSpec.Behavior(PluginBehaviors.Representation.SelectLoci)
+        PluginSpec.Behavior(PluginBehaviors.Representation.SelectLoci),
+        PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider)
     ]
 }
 
@@ -39,19 +41,18 @@ export function createPlugin(target: HTMLElement): PluginContext {
     const ctx = new PluginContext(DefaultSpec);
     ReactDOM.render(React.createElement(Plugin, { plugin: ctx }), target);
 
-    try {
-        trySetSnapshot(ctx);
-    } catch (e) {
-        console.warn('Failed to load snapshot', e);
-    }
+    trySetSnapshot(ctx);
 
     return ctx;
 }
 
-function trySetSnapshot(ctx: PluginContext) {
-    const snapshotUrl = getParam('snapshot-url', `[^&]+`);
-    if (!snapshotUrl) return;
-    // const data = JSON.parse(atob(snapshot));
-    // setTimeout(() => ctx.state.setSnapshot(data), 250);
-    PluginCommands.State.Snapshots.Fetch.dispatch(ctx, { url: snapshotUrl })
+async function trySetSnapshot(ctx: PluginContext) {
+    try {
+        const snapshotUrl = getParam('snapshot-url', `[^&]+`);
+        if (!snapshotUrl) return;
+        await PluginCommands.State.Snapshots.Fetch.dispatch(ctx, { url: snapshotUrl })
+    } catch (e) {
+        ctx.log(LogEntry.error('Failed to load snapshot.'));
+        console.warn('Failed to load snapshot', e);
+    }
 }

+ 10 - 0
src/mol-plugin/skin/base/components/temp.scss

@@ -104,4 +104,14 @@
 
 .msp-tree-children {
     margin-left: $control-spacing;
+}
+
+.msp-log-list {
+    list-style: none;
+
+    > li {
+        background: $msp-form-control-background;
+        margin-bottom: 1px;
+        padding: 3px 6px;
+    }
 }

+ 15 - 0
src/mol-plugin/ui/controls.tsx

@@ -8,6 +8,7 @@ import * as React from 'react';
 import { PluginCommands } from 'mol-plugin/command';
 import { UpdateTrajectory } from 'mol-plugin/state/actions/basic';
 import { PluginComponent } from './base';
+import { LociLabelEntry } from 'mol-plugin/util/loci-label-manager';
 
 export class Controls extends PluginComponent<{ }, { }> {
     render() {
@@ -34,4 +35,18 @@ export class TrajectoryControls extends PluginComponent {
             })}>►</button><br />
         </div>
     }
+}
+
+export class LociLabelControl extends PluginComponent<{}, { entries: ReadonlyArray<LociLabelEntry> }> {
+    state = { entries: [] }
+
+    componentDidMount() {
+        this.subscribe(this.plugin.events.labels.highlight, e => this.setState({ entries: e.entries }));
+    }
+
+    render() {
+        return <div>
+            {this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)}
+        </div>
+    }
 }

+ 8 - 5
src/mol-plugin/ui/plugin.tsx

@@ -8,7 +8,7 @@ import * as React from 'react';
 import { PluginContext } from '../context';
 import { StateTree } from './state-tree';
 import { Viewport, ViewportControls } from './viewport';
-import { Controls, TrajectoryControls } from './controls';
+import { Controls, TrajectoryControls, LociLabelControl } from './controls';
 import { PluginComponent, PluginReactContext } from './base';
 import { CameraSnapshots } from './camera';
 import { StateSnapshots } from './state';
@@ -62,6 +62,9 @@ export class ViewportWrapper extends PluginComponent {
             <div style={{ position: 'absolute', left: '10px', bottom: '10px' }}>
                 <BackgroundTaskProgress />
             </div>
+            <div style={{ position: 'absolute', right: '10px', bottom: '10px' }}>
+                <LociLabelControl />
+            </div>
         </>;
     }
 }
@@ -107,10 +110,10 @@ export class Log extends PluginComponent<{}, { entries: List<LogEntry> }> {
     }
 
     render() {
-        return <div ref={this.wrapper} style={{ position: 'absolute', top: '0', right: '0', bottom: '0', left: '0', overflowY: 'scroll' }}>
-            <ul style={{ listStyle: 'none' }}>
-                {this.state.entries.map((e, i) => <li key={i} style={{ borderBottom: '1px solid #999', padding: '3px' }}>
-                    [{e!.type}] [{formatTime(e!.timestamp)}] {e!.message}
+        return <div ref={this.wrapper} style={{ position: 'absolute', top: '0', right: '0', bottom: '0', left: '0', overflowY: 'auto' }}>
+            <ul style={{ listStyle: 'none' }} className='msp-log-list'>
+                {this.state.entries.map((e, i) => <li key={i}>
+                    <b>[{formatTime(e!.timestamp)} | {e!.type}]</b> {e!.message}
                 </li>)}
             </ul>
         </div>;

+ 40 - 0
src/mol-plugin/util/loci-label-manager.ts

@@ -0,0 +1,40 @@
+/**
+ * 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';
+import { Loci } from 'mol-model/loci';
+import { Representation } from 'mol-repr/representation';
+
+export type LociLabelEntry = JSX.Element | string
+export type LociLabelProvider = (info: Loci, repr?: Representation<any>) => LociLabelEntry | undefined
+
+export class LociLabelManager {
+    providers: LociLabelProvider[] = [];
+
+    addProvider(provider: LociLabelProvider) {
+        this.providers.push(provider);
+    }
+
+    removeProvider(provider: LociLabelProvider) {
+        this.providers = this.providers.filter(p => p !== provider);
+        // Event.Interactivity.Highlight.dispatch(this.ctx, []);
+    }
+
+    private empty: any[] = [];
+    private getInfo(loci: Loci, repr?: Representation<any>) {
+        if (!loci || loci.kind === 'empty-loci') return this.empty;
+        const info: LociLabelEntry[] = [];
+        for (let p of this.providers) {
+            const e = p(loci, repr);
+            if (e) info.push(e);
+        }
+        return info;
+    }
+
+    constructor(public ctx: PluginContext) {
+        ctx.behaviors.canvas.highlightLoci.subscribe(ev => ctx.events.labels.highlight.next({ entries: this.getInfo(ev.loci, ev.repr) }));
+    }
+}