Browse Source

Merge branch 'master' of https://github.com/molstar/molstar

Alexander Rose 5 years ago
parent
commit
f823a887b7

+ 7 - 4
src/examples/proteopedia-wrapper/index.html

@@ -176,16 +176,19 @@
 
             var snapshot;
             addControl('Set Snapshot', () => {
-                snapshot = PluginWrapper.snapshot.get();
-                // could use JSON.stringify(snapshot) and upload the data
+                // const options = { data: true, behavior: false, animation: false, interactivity: false, canvas3d: false, camera: false, cameraTransition: false };
+                snapshot = PluginWrapper.plugin.state.getSnapshot(/** options */);
                 // console.log(JSON.stringify(snapshot, null, 2));
             });
             addControl('Restore Snapshot', () => {
                 if (!snapshot) return;
                 PluginWrapper.snapshot.set(snapshot);
             });
-            addControl('Download Snapshot', () => {
-                snapshot = PluginWrapper.snapshot.download();
+            addControl('Download State', () => {
+                snapshot = PluginWrapper.snapshot.download('molj');
+            });
+            addControl('Download Session', () => {
+                snapshot = PluginWrapper.snapshot.download('molx');
             });
 
             ////////////////////////////////////////////////////////

+ 6 - 7
src/examples/proteopedia-wrapper/index.ts

@@ -33,7 +33,7 @@ require('../../mol-plugin-ui/skin/light.scss');
 
 class MolStarProteopediaWrapper {
     static VERSION_MAJOR = 5;
-    static VERSION_MINOR = 3;
+    static VERSION_MINOR = 4;
 
     private _ev = RxEventHelper.create();
 
@@ -393,16 +393,15 @@ class MolStarProteopediaWrapper {
     }
 
     snapshot = {
-        get: () => {
-            return this.plugin.state.getSnapshot();
+        get: (params?: PluginState.SnapshotParams) => {
+            return this.plugin.state.getSnapshot(params);
         },
         set: (snapshot: PluginState.Snapshot) => {
             return this.plugin.state.setSnapshot(snapshot);
         },
-        download: () => {
-            const json = JSON.stringify(this.plugin.state.getSnapshot(), null, 2);
-            const blob = new Blob([json], {type : 'application/json;charset=utf-8'});
-            download(blob, `mol-star_state_${(name || getFormattedTime())}.json`);
+        download: async (type: 'molj' | 'molx' = 'molj', params?: PluginState.SnapshotParams) => {
+            const data = await this.plugin.managers.snapshot.serialize({ type, params });
+            download(data, `mol-star_state_${(name || getFormattedTime())}.${type}`);
         },
         fetch: async (url: string, type: 'molj' | 'molx' = 'molj') => {
             try {

+ 3 - 3
src/mol-plugin-state/manager/snapshots.ts

@@ -189,10 +189,10 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
         };
     }
 
-    async serialize(type: 'json' | 'zip' = 'json') {
-        const json = JSON.stringify(this.getStateSnapshot(), null, 2);
+    async serialize(options?: { type: 'json' | 'molj' | 'zip' | 'molx', params?: PluginState.SnapshotParams }) {
+        const json = JSON.stringify(this.getStateSnapshot({ params: options?.params }), null, 2);
 
-        if (type === 'json') {
+        if (!options?.type || options.type === 'json' || options.type === 'molj') {
             return new Blob([json], {type : 'application/json;charset=utf-8'});
         } else {
             const state = new Uint8Array(utf8ByteCount(json));

+ 2 - 2
src/mol-plugin-ui/skin/base/components/misc.scss

@@ -427,7 +427,7 @@
     position: relative;
     // display: inline-block;
     margin: $control-spacing auto 0 auto;
-    width: 332px;
+    width: 400px;
     line-height: $row-height;
 
     &-actions {
@@ -444,7 +444,7 @@
     select.msp-form-control {
         padding: 0 5px;
         text-align: center;
-        background: none;
+        background: $msp-form-control-background;
         flex: 0 0 80px;
         text-overflow: ellipsis;
     }

+ 2 - 2
src/mol-plugin-ui/state/snapshots.tsx

@@ -75,10 +75,10 @@ export class StateExportImportControls extends PluginUIComponent<{ onAction?: ()
     render() {
         return <div className='msp-flex-row'>
             <Button icon={GetApp} onClick={this.downloadToFileJson} title='Save the state description. Input data are loaded using the provided sources. Does not work if local files are used as input.'>
-                Base
+                State
             </Button>
             <Button icon={GetApp} onClick={this.downloadToFileZip} title='Save the state including the input data.'>
-                Full
+                Session
             </Button>
             <div className='msp-btn msp-btn-block msp-btn-action msp-loader-msp-btn-file'>
                 <Icon svg={OpenInBrowser} inline /> Open <input onChange={this.open} type='file' multiple={false} accept='.molx,.molj' />

+ 39 - 10
src/mol-plugin-ui/structure/selection.tsx

@@ -8,11 +8,13 @@
 import Close from '@material-ui/icons/Close';
 import CancelOutlined from '@material-ui/icons/CancelOutlined';
 import Brush from '@material-ui/icons/Brush';
+import Restore from '@material-ui/icons/Restore';
+import Remove from '@material-ui/icons/Remove';
 import * as React from 'react';
 import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
 import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
 import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
-import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
+import { StructureRef, StructureComponentRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
 import { StructureSelectionModifier } from '../../mol-plugin-state/manager/structure/selection';
 import { memoizeLatest } from '../../mol-util/memoize';
 import { ParamDefinition } from '../../mol-util/param-definition';
@@ -31,15 +33,16 @@ const StructureSelectionParams = {
 interface StructureSelectionActionsControlsState {
     isEmpty: boolean,
     isBusy: boolean,
+    canUndo: boolean,
 
     action?: StructureSelectionModifier | 'color' | 'add-repr'
 }
 
 const ActionHeader = new Map<StructureSelectionModifier, string>([
-    ['add', 'Add/Union'],
-    ['remove', 'Remove/Subtract'],
-    ['intersect', 'Intersect'],
-    ['set', 'Set']
+    ['add', 'Add/Union Selection'],
+    ['remove', 'Remove/Subtract Selection'],
+    ['intersect', 'Intersect Selection'],
+    ['set', 'Set Selection']
 ] as const);
 
 export class StructureSelectionActionsControls extends PluginUIComponent<{}, StructureSelectionActionsControlsState> {
@@ -48,6 +51,7 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
 
         isEmpty: true,
         isBusy: false,
+        canUndo: false,
     }
 
     componentDidMount() {
@@ -65,6 +69,10 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
         this.subscribe(this.plugin.managers.interactivity.events.propsUpdated, () => {
             this.forceUpdate();
         });
+
+        this.subscribe(this.plugin.state.data.events.historyUpdated, ({ state }) => {
+            this.setState({ canUndo: state.canUndo });
+        });
     }
 
     get isDisabled() {
@@ -119,25 +127,46 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
 
     turnOff = () => this.plugin.selectionMode = false;
 
+    undo = () => {
+        const task = this.plugin.state.data.undo();
+        if (task) this.plugin.runTask(task);
+    }
+
+    subtract = () => {
+        const sel = this.plugin.managers.structure.hierarchy.getStructuresWithSelection();
+        const components: StructureComponentRef[] = [];
+        for (const s of sel) components.push(...s.components);
+        if (components.length === 0) return;
+        this.plugin.managers.structure.component.modifyByCurrentSelection(components, 'subtract');
+    }
+
     render() {
         const granularity = this.plugin.managers.interactivity.props.granularity;
+        const undoTitle = this.state.canUndo
+            ? `Undo ${this.plugin.state.data.latestUndoLabel}`
+            : 'Some mistakes of the past can be undone.';
+
         return <>
-            <div className='msp-flex-row'>
+            <div className='msp-flex-row' style={{ background: 'none' }}>
+                <PureSelectControl title={`Picking Level`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
                 <ToggleButton icon={Union} title={ActionHeader.get('add')} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
                 <ToggleButton icon={Subtract} title={ActionHeader.get('remove')} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
                 <ToggleButton icon={Intersect} title={ActionHeader.get('intersect')} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
                 <ToggleButton icon={SetSvg} title={ActionHeader.get('set')} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
-                <ToggleButton icon={Brush} title='Color' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} />
+
+                <ToggleButton icon={Brush} title='Color' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }}  />
                 <ToggleButton icon={CubeSvg} title='Create Representation' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
-                <PureSelectControl title={`Picking Level`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
-                <IconButton svg={CancelOutlined} title='Turn selection mode off' onClick={this.turnOff} />
+                <IconButton svg={Remove} title='Subtract from Representations' onClick={this.subtract} disabled={this.isDisabled} />
+                <IconButton svg={Restore} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
+
+                <IconButton svg={CancelOutlined} title='Turn selection mode off' onClick={this.turnOff} style={{ marginLeft: '10px' }} />
             </div>
             {(this.state.action && this.state.action !== 'color' && this.state.action !== 'add-repr') && <div className='msp-selection-viewport-controls-actions'>
                 <ActionMenu header={ActionHeader.get(this.state.action as StructureSelectionModifier)} items={this.queries} onSelect={this.selectQuery} noOffset />
             </div>}
             {this.state.action === 'color' && <div className='msp-selection-viewport-controls-actions'>
                 <ControlGroup header='Color' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
-                    <ApplyColorControls />
+                    <ApplyColorControls onApply={this.toggleColor} />
                 </ControlGroup>
             </div>}
             {this.state.action === 'add-repr' && <div className='msp-selection-viewport-controls-actions'>

+ 1 - 1
src/mol-plugin-ui/viewport.tsx

@@ -114,7 +114,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                 </div>}
             </div>
             {this.state.isScreenshotExpanded && <div className='msp-viewport-controls-panel'>
-                <ControlGroup header='Screenshot / State Snapshot' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleScreenshotExpanded}
+                <ControlGroup header='Screenshot / State' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleScreenshotExpanded}
                     topRightIcon={Close} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
                     <DownloadScreenshotControls close={this.toggleScreenshotExpanded} />
                 </ControlGroup>

+ 1 - 1
src/mol-plugin-ui/viewport/screenshot.tsx

@@ -147,7 +147,7 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
                 <Button icon={Launch} onClick={this.openTab} disabled={this.state.isDisabled}>Open in new Tab</Button>
             </div>
             <ParameterControls params={this.plugin.helpers.viewportScreenshot!.params} values={this.plugin.helpers.viewportScreenshot!.values} onChange={this.setProps} isDisabled={this.state.isDisabled} />
-            <ExpandGroup header='State Snapshot'>
+            <ExpandGroup header='State'>
                 <StateExportImportControls onAction={this.props.close} />
                 <ExpandGroup header='Save Options' initiallyExpanded={false} noOffset>
                     <LocalStateSnapshotParams />

+ 2 - 2
src/mol-plugin/behavior/static/state.ts

@@ -178,9 +178,9 @@ export function Snapshots(ctx: PluginContext) {
         await ctx.managers.snapshot.setStateSnapshot(json.data);
     });
 
-    PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, async ({ name, type }) => {
+    PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, async ({ name, type, params }) => {
         const filename = `mol-star_state_${(name || getFormattedTime())}.${type === 'json' ? 'molj' : 'molx'}`;
-        const data = await ctx.managers.snapshot.serialize(type);
+        const data = await ctx.managers.snapshot.serialize({ type, params });
         download(data, `${filename}`);
     });
 

+ 1 - 1
src/mol-plugin/commands.ts

@@ -37,7 +37,7 @@ export const PluginCommands = {
             Upload: PluginCommand<{ name?: string, description?: string, playOnLoad?: boolean, serverUrl: string, params?: PluginState.SnapshotParams }>(),
             Fetch: PluginCommand<{ url: string }>(),
 
-            DownloadToFile: PluginCommand<{ name?: string, type: 'json' | 'zip' }>(),
+            DownloadToFile: PluginCommand<{ name?: string, type: 'json' | 'molj' | 'zip' | 'molx', params?: PluginState.SnapshotParams }>(),
             OpenFile: PluginCommand<{ file: File }>(),
         }
     },

+ 3 - 3
src/mol-plugin/state.ts

@@ -57,8 +57,8 @@ class PluginState extends PluginComponent {
             startAnimation: p.startAnimation ? !!p.startAnimation : void 0,
             camera: p.camera ? {
                 current: this.plugin.canvas3d!.camera.getSnapshot(),
-                transitionStyle: p.cameraTranstion!.name,
-                transitionDurationInMs: p?.cameraTranstion?.name === 'animate' ? p.cameraTranstion.params.durationInMs : void 0
+                transitionStyle: p.cameraTransition!.name,
+                transitionDurationInMs: p?.cameraTransition?.name === 'animate' ? p.cameraTransition.params.durationInMs : void 0
             } : void 0,
             canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
             interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
@@ -136,7 +136,7 @@ namespace PluginState {
         canvas3d: PD.Boolean(true),
         interactivity: PD.Boolean(true),
         camera: PD.Boolean(true),
-        cameraTranstion: PD.MappedStatic('animate', {
+        cameraTransition: PD.MappedStatic('animate', {
             animate: PD.Group({
                 durationInMs: PD.Numeric(250, { min: 100, max: 5000, step: 500 }, { label: 'Duration in ms' }),
             }),