Bladeren bron

mol-plugin: wip UI

David Sehnal 6 jaren geleden
bovenliggende
commit
fc3d19245a

+ 16 - 26
src/mol-plugin/ui/plugin.tsx

@@ -4,23 +4,21 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import { List } from 'immutable';
+import { PluginState } from 'mol-plugin/state';
+import { formatTime } from 'mol-util';
+import { LogEntry } from 'mol-util/log-entry';
 import * as React from 'react';
 import { PluginContext } from '../context';
-import { StateTree } from './state/tree';
-import { Viewport, ViewportControls } from './viewport';
-import { Controls, TrajectoryControls, LociLabelControl } from './controls';
-import { PluginUIComponent, PluginReactContext } from './base';
+import { PluginReactContext, PluginUIComponent } from './base';
 import { CameraSnapshots } from './camera';
+import { Controls, LociLabelControl, TrajectoryControls } from './controls';
 import { StateSnapshots } from './state';
-import { List } from 'immutable';
-import { LogEntry } from 'mol-util/log-entry';
-import { formatTime } from 'mol-util';
-import { BackgroundTaskProgress } from './task';
-import { ApplyActionContol } from './state/apply-action';
-import { PluginState } from 'mol-plugin/state';
-import { UpdateTransformContol } from './state/update-transform';
-import { StateObjectCell } from 'mol-state';
+import { StateObjectActions } from './state/actions';
 import { AnimationControls } from './state/animation';
+import { StateTree } from './state/tree';
+import { BackgroundTaskProgress } from './task';
+import { Viewport, ViewportControls } from './viewport';
 
 export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
 
@@ -105,8 +103,8 @@ export class State extends PluginUIComponent {
         const kind = this.plugin.state.behavior.kind.value;
         return <div className='msp-scrollable-container'>
             <div className='msp-btn-row-group msp-data-beh'>
-                <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal'}}>Data</button>
-                <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal'}}>Behavior</button>
+                <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal' }}>Data</button>
+                <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal' }}>Behavior</button>
             </div>
             <StateTree state={kind === 'data' ? this.plugin.state.dataState : this.plugin.state.behaviorState} />
         </div>
@@ -172,21 +170,13 @@ export class CurrentObject extends PluginUIComponent {
         const current = this.current;
         const ref = current.ref;
         const cell = current.state.cells.get(ref)!;
-        const parent: StateObjectCell | undefined = (cell.sourceRef && current.state.cells.get(cell.sourceRef)!) || void 0;
-
         const transform = cell.transform;
         const def = transform.transformer.definition;
+        const display = cell.obj ? cell.obj.label : (def.display && def.display.name) || def.name;
 
-        const actions =  current.state.actions.fromCell(cell, this.plugin);
-        return <>
-            <div className='msp-current-header'>
-                {cell.obj ? cell.obj.label : (def.display && def.display.name) || def.name}
-            </div>
-            {(parent && parent.status === 'ok') && <UpdateTransformContol state={current.state} transform={transform} />}
-            {cell.status === 'ok' && <>
-                <div className='msp-section-header'>Actions</div>
-                {actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={current.state} action={act} nodeRef={ref} />)}
-                </>}
+        return cell.status === 'ok' && <>
+            <div className='msp-section-header'>{`Actions (${display})`}</div>
+            <StateObjectActions state={current.state} nodeRef={ref} />
         </>;
     }
 }

+ 35 - 0
src/mol-plugin/ui/state/actions.tsx

@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2018 - 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import * as React from 'react';
+import { PluginUIComponent } from '../base';
+import { ApplyActionContol } from './apply-action';
+import { State } from 'mol-state';
+
+export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string }> {
+    get current() {
+        return this.plugin.state.behavior.currentObject.value;
+    }
+
+    componentDidMount() {
+        this.subscribe(this.plugin.state.behavior.currentObject, o => {
+            this.forceUpdate();
+        });
+
+        this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
+            const current = this.current;
+            if (current.ref !== ref || current.state !== state) return;
+            this.forceUpdate();
+        });
+    }
+
+    render() {
+        const { state, nodeRef: ref } = this.props;
+        const cell = state.cells.get(ref)!;
+        const actions = state.actions.fromCell(cell, this.plugin);
+        return actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={state} action={act} nodeRef={ref} />);
+    }
+}

+ 3 - 3
src/mol-plugin/ui/state/common.tsx

@@ -166,7 +166,7 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState
 
     render() {
         const info = this.getInfo();
-        if (info.isEmpty && this.isUpdate()) return null;
+        const isEmpty = info.isEmpty && this.isUpdate();
 
         const display = this.getHeader();
 
@@ -178,9 +178,9 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState
         return <div className='msp-transform-wrapper'>
             <div className='msp-transform-header'>
                 <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>{display.name}</button>
-                {!this.state.isCollapsed && <button className='msp-btn msp-btn-link msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} style={{ float: 'right'}} title='Set default params'>↻</button>}
+                {!isEmpty && !this.state.isCollapsed && <button className='msp-btn msp-btn-link msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} style={{ float: 'right'}} title='Set default params'>↻</button>}
             </div>
-            {!this.state.isCollapsed && <>
+            {!isEmpty && !this.state.isCollapsed && <>
                 <ParamEditor info={info} events={this.events} params={this.state.params} isDisabled={this.state.busy} />
 
                 <div className='msp-transform-apply-wrap'>

+ 53 - 6
src/mol-plugin/ui/state/tree.tsx

@@ -1,19 +1,44 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018 - 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
 import * as React from 'react';
 import { PluginStateObject } from 'mol-plugin/state/objects';
-import { State, StateObject } from 'mol-state'
+import { State, StateObject, StateObjectCell, StateTransform } from 'mol-state'
 import { PluginCommands } from 'mol-plugin/command';
 import { PluginUIComponent } from '../base';
+import { UpdateTransformContol } from './update-transform';
+import { StateObjectActions } from './actions';
+
+export class StateTree extends PluginUIComponent<{ state: State }, { showActions: boolean }> {
+    state = { showActions: true };
+
+    componentDidMount() {
+        this.subscribe(this.plugin.events.state.cell.created, e => {
+            if (e.cell.transform.parent === StateTransform.RootRef) this.forceUpdate();
+        });
+
+        this.subscribe(this.plugin.events.state.cell.removed, e => {
+            if (e.parent === StateTransform.RootRef) this.forceUpdate();
+        });
+    }
+
+    static getDerivedStateFromProps(props: { state: State }, state: { showActions: boolean }) {
+        const n = props.state.tree.root.ref;
+        const children = props.state.tree.children.get(n);
+        const showActions = children.size === 0;
+        if (state.showActions === showActions) return null;
+        return { showActions };
+    }
 
-export class StateTree extends PluginUIComponent<{ state: State }> {
     render() {
-        const n = this.props.state.tree.root.ref;
-        return <StateTreeNode state={this.props.state} nodeRef={n} depth={0} />;
+        const ref = this.props.state.tree.root.ref;
+        if (this.state.showActions) {
+            return <StateObjectActions state={this.props.state} nodeRef={ref} />
+        }
+        return <StateTreeNode state={this.props.state} nodeRef={ref} depth={0} />;
     }
 }
 
@@ -195,7 +220,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
             <span className='msp-icon msp-icon-visual-visibility' />
         </button>;
 
-        return <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight}
+        const row = <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight}
             style={{ marginLeft: this.state.isCurrent ? '0px' : `${this.props.depth * 10}px`, borderRadius: this.state.isCurrent ? '0' : void 0 }}>
             {isCurrent ? <b>{label}</b> : label}
             {children.size > 0 &&  <button onClick={this.toggleExpanded} className='msp-btn msp-btn-link msp-tree-toggle-exp-button'>
@@ -205,5 +230,27 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
                 <span className='msp-icon msp-icon-remove' />
             </button>}{visibility}
         </div>;
+
+        if (this.state.isCurrent) {
+            return <>
+                {row}
+                <StateTreeNodeTransform {...this.props}/>
+            </>
+        }
+
+        return row;
+    }
+}
+
+class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { isExpanded: boolean }> {
+    render() {
+        const ref = this.props.nodeRef;
+        const cell = this.props.state.cells.get(ref)!;
+        const parent: StateObjectCell | undefined = (cell.sourceRef && this.props.state.cells.get(cell.sourceRef)!) || void 0;
+
+        if (!parent || parent.status !== 'ok') return null;
+
+        const transform = cell.transform;
+        return <UpdateTransformContol state={this.props.state} transform={transform} />;
     }
 }