Преглед на файлове

mol-plugin: action select control

David Sehnal преди 5 години
родител
ревизия
f69670c368

+ 20 - 0
src/mol-plugin/skin/base/components/misc.scss

@@ -100,4 +100,24 @@
             outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;  
         }
     }
+}
+
+.msp-action-select {
+    position: relative;
+
+    select {
+        padding-left: $control-spacing + $row-height;
+    }
+
+    option:first-child {
+        color: color-lower-contrast($font-color, 15%);
+    }
+
+    > .msp-icon {
+        display: block;
+        top: 0;
+        left: $control-spacing;
+        position: absolute;
+        line-height: $row-height;
+    }
 }

+ 4 - 0
src/mol-plugin/skin/base/icons.scss

@@ -223,4 +223,8 @@
 
 .msp-icon-flow-cascade:before {
 	content: "\e8d8";
+}
+
+.msp-icon-flow-tree:before {
+	content: "\e8da";
 }

+ 6 - 2
src/mol-plugin/ui/plugin.tsx

@@ -14,7 +14,7 @@ import { PluginContext } from '../context';
 import { PluginReactContext, PluginUIComponent } from './base';
 import { LociLabels, TrajectoryViewportControls, StateSnapshotViewportControls, AnimationViewportControls, StructureToolsWrapper } from './controls';
 import { StateSnapshots } from './state';
-import { StateObjectActions } from './state/actions';
+import { StateObjectActionSelect } from './state/actions';
 import { StateTree } from './state/tree';
 import { BackgroundTaskProgress } from './task';
 import { Viewport, ViewportControls } from './viewport';
@@ -254,7 +254,11 @@ export class CurrentObject extends PluginUIComponent {
                 </div>
                 <UpdateTransformControl state={current.state} transform={transform} customHeader='none' />
             </> }
-            {cell.status === 'ok' && <StateObjectActions state={current.state} nodeRef={ref} initiallyCollapsed />}
+            {cell.status === 'ok' &&
+                <StateObjectActionSelect state={current.state} nodeRef={ref} plugin={this.plugin} />
+            }
+
+            {/* <StateObjectActions state={current.state} nodeRef={ref} initiallyCollapsed />} */}
         </>;
     }
 }

+ 78 - 4
src/mol-plugin/ui/state/actions.tsx

@@ -7,8 +7,9 @@
 import * as React from 'react';
 import { PluginUIComponent } from '../base';
 import { ApplyActionControl } from './apply-action';
-import { State } from '../../../mol-state';
+import { State, StateAction } from '../../../mol-state';
 import { Icon } from '../controls/common';
+import { PluginContext } from '../../context';
 
 export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string, hideHeader?: boolean, initiallyCollapsed?: boolean }> {
     get current() {
@@ -16,9 +17,9 @@ export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRe
     }
 
     componentDidMount() {
-        this.subscribe(this.plugin.state.behavior.currentObject, o => {
-            this.forceUpdate();
-        });
+        // this.subscribe(this.plugin.state.behavior.currentObject, o => {
+        //     this.forceUpdate();
+        // });
 
         this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
             const current = this.current;
@@ -41,4 +42,77 @@ export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRe
             {actions.map((act, i) => <ApplyActionControl plugin={this.plugin} key={`${act.id}`} state={state} action={act} nodeRef={ref} initiallyCollapsed={this.props.initiallyCollapsed} />)}
         </div>;
     }
+}
+
+interface StateObjectActionSelectProps {
+    plugin: PluginContext,
+    state: State,
+    nodeRef: string
+}
+
+interface StateObjectActionSelectState {
+    state: State,
+    nodeRef: string,
+    version: string,
+    actions: readonly StateAction[],
+    currentActionIndex: number
+}
+
+function createStateObjectActionSelectState(props: StateObjectActionSelectProps): StateObjectActionSelectState {
+    const cell = props.state.cells.get(props.nodeRef)!;
+    const actions = props.state.actions.fromCell(cell, props.plugin);
+    (actions as StateAction[]).sort((a, b) => a.definition.display.name < b.definition.display.name ? -1 : a.definition.display.name === b.definition.display.name ? 0 : 1);
+    return {
+        state: props.state,
+        nodeRef: props.nodeRef,
+        version: cell.transform.version,
+        actions,
+        currentActionIndex: -1
+    }
+}
+
+export class StateObjectActionSelect extends PluginUIComponent<StateObjectActionSelectProps, StateObjectActionSelectState> {
+    state = createStateObjectActionSelectState(this.props);
+
+    get current() {
+        return this.plugin.state.behavior.currentObject.value;
+    }
+
+    static getDerivedStateFromProps(props: StateObjectActionSelectProps, state: StateObjectActionSelectState) {
+        if (state.state !== props.state || state.nodeRef !== props.nodeRef) return createStateObjectActionSelectState(props);
+        const cell = props.state.cells.get(props.nodeRef)!;
+        if (cell.transform.version !== state.version) return createStateObjectActionSelectState(props);
+        return null;
+    }
+
+    componentDidMount() {
+        this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
+            const current = this.current;
+            if (current.ref !== ref || current.state !== state) return;
+            this.setState(createStateObjectActionSelectState(this.props));
+        });
+    }
+
+    onChange =  (e: React.ChangeEvent<HTMLSelectElement>) => {
+        this.setState({ currentActionIndex: parseInt(e.target.value, 10) });
+    }
+
+    render() {
+        const actions = this.state.actions;
+        if (actions.length === 0) return null;
+
+        const current = this.state.currentActionIndex >= 0 && actions[this.state.currentActionIndex];
+        const title = current ? current.definition.display.description : 'Select Action';
+
+        return <>
+            <div className='msp-contol-row msp-action-select'>
+                <select className='msp-form-control' title={title} value={this.state.currentActionIndex} onChange={this.onChange} style={{ fontWeight: 'bold' }}>
+                    <option key={-1} value={-1} style={{ color: '#999' }}>[ Select Action ]</option>
+                    {actions.map((a, i) => <option key={i} value={i}>{a.definition.display.name}</option>)}
+                </select>
+                <Icon name='flow-tree' />
+            </div>
+            {current && <ApplyActionControl key={current.id} plugin={this.plugin} state={this.props.state} action={current} nodeRef={this.props.nodeRef} hideHeader />}
+        </>;
+    }
 }

+ 2 - 1
src/mol-plugin/ui/state/apply-action.tsx

@@ -19,6 +19,7 @@ namespace ApplyActionControl {
         nodeRef: StateTransform.Ref,
         state: State,
         action: StateAction,
+        hideHeader?: boolean,
         initiallyCollapsed?: boolean
     }
 
@@ -42,7 +43,7 @@ class ApplyActionControl extends TransformControlBase<ApplyActionControl.Props,
     }
     getInfo() { return this._getInfo(this.props.nodeRef, this.props.state.transforms.get(this.props.nodeRef).version); }
     getTransformerId() { return this.props.state.transforms.get(this.props.nodeRef).transformer.id; }
-    getHeader() { return this.props.action.definition.display; }
+    getHeader() { return this.props.hideHeader ? 'none' : this.props.action.definition.display; }
     canApply() { return !this.state.error && !this.state.busy; }
     canAutoApply() { return false; }
     applyText() { return 'Apply'; }