Browse Source

adapt MeasurementControls to define strucmotif query

JonStargaryen 4 years ago
parent
commit
b16b30236b
4 changed files with 132 additions and 0 deletions
  1. 1 0
      src/viewer/index.ts
  2. 1 0
      src/viewer/types.ts
  3. 2 0
      src/viewer/ui/controls.tsx
  4. 128 0
      src/viewer/ui/strucmotif.tsx

+ 1 - 0
src/viewer/index.ts

@@ -134,6 +134,7 @@ export class Viewer {
             modelLoader: new ModelLoader(this.plugin),
             collapsed: new BehaviorSubject<CollapsedState>({
                 selection: true,
+                strucmotifSubmit: true,
                 measurements: true,
                 superposition: true,
                 component: false,

+ 1 - 0
src/viewer/types.ts

@@ -34,6 +34,7 @@ export interface ParseParams extends SharedParams {
 
 export type CollapsedState = {
     selection: boolean
+    strucmotifSubmit: boolean
     measurements: boolean
     superposition: boolean
     component: boolean

+ 2 - 0
src/viewer/ui/controls.tsx

@@ -15,6 +15,7 @@ import { StructureSuperpositionControls } from 'molstar/lib/mol-plugin-ui/struct
 import { StructureComponentControls } from 'molstar/lib/mol-plugin-ui/structure/components';
 import { VolumeStreamingControls } from 'molstar/lib/mol-plugin-ui/structure/volume';
 import { SessionControls } from './session';
+import {StrucmotifSubmitControls} from './strucmotif';
 
 export class StructureTools extends PluginUIComponent {
     get customState() {
@@ -29,6 +30,7 @@ export class StructureTools extends PluginUIComponent {
         const collapsed = this.customState.collapsed.value;
         return <>
             <StructureSourceControls />
+            <StrucmotifSubmitControls initiallyCollapsed={collapsed.strucmotifSubmit} />
             <StructureMeasurementsControls initiallyCollapsed={collapsed.measurements} />
             <StructureSuperpositionControls initiallyCollapsed={collapsed.superposition} />
             <StructureComponentControls initiallyCollapsed={collapsed.component} />

+ 128 - 0
src/viewer/ui/strucmotif.tsx

@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ * @author Alexander Rose <alex.rose@weirdbyte.de>
+ */
+
+import * as React from 'react';
+import {CollapsableControls, PurePluginUIComponent} from 'molstar/lib/mol-plugin-ui/base';
+import {Button, IconButton} from 'molstar/lib/mol-plugin-ui/controls/common';
+import {
+    ArrowDownwardSvg,
+    ArrowUpwardSvg,
+    DeleteOutlinedSvg,
+    HelpOutlineSvg,
+    Icon
+} from 'molstar/lib/mol-plugin-ui/controls/icons';
+import {ActionMenu} from 'molstar/lib/mol-plugin-ui/controls/action-menu';
+import {StructureSelectionHistoryEntry} from 'molstar/lib/mol-plugin-state/manager/structure/selection';
+import {StructureElement} from 'molstar/lib/mol-model/structure/structure';
+import {ToggleSelectionModeButton} from 'molstar/lib/mol-plugin-ui/structure/selection';
+
+export class StrucmotifSubmitControls extends CollapsableControls {
+    protected defaultState() {
+        return {
+            header: 'Structural Motif Search',
+            isCollapsed: false,
+            brand: { accent:  'gray' as const, svg: SearchIconSvg }
+        };
+    }
+
+    renderControls() {
+        return <>
+            <SubmitControls />
+        </>;
+    }
+}
+
+const _SearchIcon = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>;
+export function SearchIconSvg() { return _SearchIcon; }
+
+export class SubmitControls extends PurePluginUIComponent<{}, { isBusy: boolean }> {
+    state = { isBusy: false }
+
+    componentDidMount() {
+        this.subscribe(this.selection.events.additionsHistoryUpdated, () => {
+            this.forceUpdate();
+        });
+
+        this.subscribe(this.plugin.behaviors.state.isBusy, v => {
+            this.setState({ isBusy: v });
+        });
+    }
+
+    get selection() {
+        return this.plugin.managers.structure.selection;
+    }
+
+    submitSearch = () => {
+        const loci = this.plugin.managers.structure.selection.additionsHistory;
+        // this.plugin.managers.structure.measurement.addAngle(loci[0].loci, loci[1].loci, loci[2].loci);
+        console.log(loci[0].loci.elements);
+        window.open('https://rcsb.org', '_blank');
+    }
+
+
+    get actions(): ActionMenu.Items {
+        const history = this.selection.additionsHistory;
+        const ret: ActionMenu.Item[] = [
+            { kind: 'item', label: `Submit Search ${history.length < 3 ? ' (3 selections required)' : ''}`, value: this.submitSearch, disabled: history.length < 3 },
+        ];
+        return ret;
+    }
+
+    selectAction: ActionMenu.OnSelect = item => {
+        if (!item) return;
+        (item?.value as any)();
+    }
+
+    highlight(loci: StructureElement.Loci) {
+        this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci }, false);
+    }
+
+    moveHistory(e: StructureSelectionHistoryEntry, direction: 'up' | 'down') {
+        this.plugin.managers.structure.selection.modifyHistory(e, direction, 4);
+    }
+
+    focusLoci(loci: StructureElement.Loci) {
+        this.plugin.managers.camera.focusLoci(loci);
+    }
+
+    historyEntry(e: StructureSelectionHistoryEntry, idx: number) {
+        const history = this.plugin.managers.structure.selection.additionsHistory;
+        return <div className='msp-flex-row' key={e.id}>
+            <Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
+                {idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
+            </Button>
+            {history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
+            {history.length > 1 && <IconButton svg={ArrowDownwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'down')} flex='20px' title={'Move down'} />}
+            <IconButton svg={DeleteOutlinedSvg} small={true} className='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} flex title={'Remove'} />
+        </div>;
+    }
+
+    add() {
+        const history = this.plugin.managers.structure.selection.additionsHistory;
+
+        const entries: JSX.Element[] = [];
+        for (let i = 0, _i = Math.min(history.length, 10); i < _i; i++) {
+            entries.push(this.historyEntry(history[i], i + 1));
+        }
+
+        return <>
+            <ActionMenu items={this.actions} onSelect={this.selectAction} />
+            {entries.length > 0 && <div className='msp-control-offset'>
+                {entries}
+            </div>}
+            {entries.length === 0 && <div className='msp-control-offset msp-help-text'>
+                <div className='msp-help-description'><Icon svg={HelpOutlineSvg} inline />Add one or more selections (toggle <ToggleSelectionModeButton inline /> mode)</div>
+            </div>}
+        </>;
+    }
+
+    render() {
+        return <>
+            {this.add()}
+        </>;
+    }
+}