Parcourir la source

add PD.Group.pivot, simplified viewport settings

David Sehnal il y a 5 ans
Parent
commit
0b512487f5

+ 1 - 0
src/mol-plugin-ui/controls/color.tsx

@@ -29,6 +29,7 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
     onClickSwatch = (e: React.MouseEvent<HTMLButtonElement>) => {
         const value = Color(+(e.currentTarget.getAttribute('data-color') || '0'));
         if (value !== this.props.value) {
+            if (!this.props.param.isExpanded) this.setState({ isExpanded: false });
             this.update(value);
         }
     }

+ 44 - 13
src/mol-plugin-ui/controls/parameters.tsx

@@ -5,25 +5,25 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import * as React from 'react';
 import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
+import { PluginContext } from '../../mol-plugin/context';
 import { Color } from '../../mol-util/color';
 import { ColorListName, getColorListFromName } from '../../mol-util/color/lists';
-import { memoize1, memoizeLatest } from '../../mol-util/memoize';
-import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { camelCaseToWords } from '../../mol-util/string';
-import * as React from 'react';
-import LineGraphComponent from './line-graph/line-graph-component';
-import { Slider, Slider2 } from './slider';
-import { NumericInput, IconButton, ControlGroup, ToggleButton, ExpandGroup } from './common';
-import { _Props, _State, PluginUIComponent } from '../base';
-import { legendFor } from './legend';
 import { Legend as LegendData } from '../../mol-util/legend';
-import { CombinedColorControl, ColorValueOption, ColorOptions } from './color';
+import { memoize1, memoizeLatest } from '../../mol-util/memoize';
 import { getPrecision } from '../../mol-util/number';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ParamMapping } from '../../mol-util/param-mapping';
-import { PluginContext } from '../../mol-plugin/context';
+import { camelCaseToWords } from '../../mol-util/string';
+import { PluginUIComponent, _Props, _State } from '../base';
 import { ActionMenu } from './action-menu';
+import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
+import { ControlGroup, ExpandGroup, IconButton, NumericInput, ToggleButton } from './common';
 import { Icon } from './icons';
+import { legendFor } from './legend';
+import LineGraphComponent from './line-graph/line-graph-component';
+import { Slider, Slider2 } from './slider';
 
 export type ParameterControlsCategoryFilter = string | null | (string | null)[]
 
@@ -751,8 +751,8 @@ export class MultiSelectControl extends React.PureComponent<ParamProps<PD.MultiS
     }
 }
 
-export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean }> {
-    state = { isExpanded: !!this.props.param.isExpanded }
+export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean, showHelp: boolean }> {
+    state = { isExpanded: !!this.props.param.isExpanded, showHelp: false }
 
     change(value: any) {
         this.props.onChange({ name: this.props.name, param: this.props.param, value });
@@ -764,12 +764,43 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
 
     toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
 
+    pivoted() {
+        const key = this.props.param.pivot as string;
+        const params = this.props.param.params;
+        const pivot = params[key];
+        const Control = controlFor(pivot)!;
+
+        const ctrl = <Control name={key} param={pivot} value={this.props.value[key]} onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />;
+
+        if (!this.state.isExpanded) {
+            return <div className='msp-mapped-parameter-group'>
+                {ctrl}
+                <IconButton icon='dot-3' onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
+            </div>;
+        }
+
+        const filtered = Object.create(null);
+        for (const k of Object.keys(params)) {
+            if (k !== key) filtered[k] = params[k];
+        }
+
+        return <div className='msp-mapped-parameter-group'>
+            {ctrl}
+            <IconButton icon='dot-3' onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
+            <div className='msp-control-offset'>
+                <ParameterControls params={filtered} onEnter={this.props.onEnter} values={this.props.value} onChange={this.onChangeParam} isDisabled={this.props.isDisabled} />
+            </div>
+        </div>;        
+    }
+
     render() {
         const params = this.props.param.params;
 
         // Do not show if there are no params.
         if (Object.keys(params).length === 0) return null;
 
+        if (this.props.param.pivot) return this.pivoted();
+
         const label = this.props.param.label || camelCaseToWords(this.props.name);
 
         const controls = <ParameterControls params={params} onChange={this.onChangeParam} values={this.props.value} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />;

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

@@ -103,7 +103,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                 </ControlGroup>
             </div>}
             {this.state.isSettingsExpanded && <div className='msp-viewport-controls-panel'>
-                <ControlGroup header='Basic Settings' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded} topRightIcon='off'>
+                <ControlGroup header='Settings' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded} topRightIcon='off'>
                     <SimpleSettingsControl />
                 </ControlGroup>
             </div>}

+ 43 - 35
src/mol-plugin-ui/viewport/simple-settings.tsx

@@ -5,19 +5,18 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import { produce } from 'immer';
 import * as React from 'react';
 import { Canvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
 import { PluginCommands } from '../../mol-plugin/commands';
-import { ColorNames } from '../../mol-util/color/names';
-import { ParameterMappingControl } from '../controls/parameters';
-import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { PluginUIComponent } from '../base';
+import { PluginContext } from '../../mol-plugin/context';
+import { StateTransform } from '../../mol-state';
 import { Color } from '../../mol-util/color';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ParamMapping } from '../../mol-util/param-mapping';
-import { PluginContext } from '../../mol-plugin/context';
 import { Mutable } from '../../mol-util/type-helpers';
-import { produce } from 'immer';
-import { StateTransform } from '../../mol-state';
+import { PluginUIComponent } from '../base';
+import { ParameterMappingControl } from '../controls/parameters';
 
 export class SimpleSettingsControl extends PluginUIComponent {
     componentDidMount() {
@@ -31,18 +30,23 @@ export class SimpleSettingsControl extends PluginUIComponent {
 }
 
 const SimpleSettingsParams = {
-    layout: PD.MultiSelect<'sequence' | 'log' | 'left'>([], [['sequence', 'Sequence'], ['log', 'Log'], ['left', 'Left Panel']] as const),
-    spin: Canvas3DParams.trackball.params.spin,
+    spin: PD.Group({
+        spin: Canvas3DParams.trackball.params.spin,
+        speed: Canvas3DParams.trackball.params.spinSpeed
+    }, { pivot: 'spin' }),
     camera: Canvas3DParams.cameraMode,
-    background: PD.MappedStatic('opaque', {
-        'transparent': PD.EmptyGroup(),
-        'opaque': PD.Group({ color: PD.Color(Color(0xFCFBF9), { description: 'Custom background color' }) }, { isFlat: true })
-    }, { description: 'Background of the 3D canvas' }),
-    renderStyle: Canvas3DParams.renderer.params.style,
-    occlusion: Canvas3DParams.postprocessing.params.occlusion,
-    outline: Canvas3DParams.postprocessing.params.outline,
-    fog: PD.Boolean(false, { description: 'Show fog in the distance' }),
-    clipFar: PD.Boolean(true, { description: 'Clip scene in the distance' }),
+    background: PD.Group({ 
+        color: PD.Color(Color(0xFCFBF9), { label: "Background", description: 'Custom background color' }),
+        transparent: PD.Boolean(false)
+    }, { pivot: 'color' }),
+    renderer: PD.Group({
+        renderStyle: Canvas3DParams.renderer.params.style,
+        occlusion: Canvas3DParams.postprocessing.params.occlusion,
+        outline: Canvas3DParams.postprocessing.params.outline,
+        fog: PD.Boolean(false, { description: 'Show fog in the distance' }),
+        clipFar: PD.Boolean(true, { description: 'Clip scene in the distance' }),
+    }, { pivot: 'renderStyle' }),
+    layout: PD.MultiSelect<'sequence' | 'log' | 'left'>([], [['sequence', 'Sequence'], ['log', 'Log'], ['left', 'Left Panel']] as const),
 };
 
 type SimpleSettingsParams = typeof SimpleSettingsParams
@@ -62,29 +66,33 @@ const SimpleSettingsMapping = ParamMapping({
 
         return {
             layout: props.layout,
-            spin: !!canvas.trackball.spin,
+            spin: { spin: !!canvas.trackball.spin, speed: canvas.trackball.spinSpeed },
             camera: canvas.cameraMode,
-            background:  (renderer.backgroundColor === ColorNames.white && canvas.transparentBackground)
-                ? { name: 'transparent', params: { } }
-                : { name: 'opaque', params: { color: renderer.backgroundColor } },
-            renderStyle: renderer.style,
-            occlusion: canvas.postprocessing.occlusion,
-            outline: canvas.postprocessing.outline,
-            fog: ctx.canvas3d ? canvas.cameraFog > 1 : false,
-            clipFar: canvas.cameraClipFar
+            background: {
+                color: renderer.backgroundColor,
+                transparent: canvas.transparentBackground
+            },
+            renderer: {
+                renderStyle: renderer.style,
+                occlusion: canvas.postprocessing.occlusion,
+                outline: canvas.postprocessing.outline,
+                fog: ctx.canvas3d ? canvas.cameraFog > 1 : false,
+                clipFar: canvas.cameraClipFar
+            }
         };
     },
     update(s, props) {
         const canvas = props.canvas as Mutable<Canvas3DProps>;
-        canvas.trackball.spin = s.spin;
+        canvas.trackball.spin = s.spin.spin;
+        canvas.trackball.spinSpeed = s.spin.speed;
         canvas.cameraMode = s.camera;
-        canvas.transparentBackground = s.background.name === 'transparent';
-        canvas.renderer.backgroundColor = s.background.name === 'transparent' ? ColorNames.white : s.background.params.color;
-        canvas.renderer.style = s.renderStyle
-        canvas.postprocessing.occlusion = s.occlusion;
-        canvas.postprocessing.outline = s.outline;
-        canvas.cameraFog = s.fog ? 50 : 0;
-        canvas.cameraClipFar = s.clipFar;
+        canvas.transparentBackground = s.background.transparent;
+        canvas.renderer.backgroundColor = s.background.color;
+        canvas.renderer.style = s.renderer.renderStyle
+        canvas.postprocessing.occlusion = s.renderer.occlusion;
+        canvas.postprocessing.outline = s.renderer.outline;
+        canvas.cameraFog = s.renderer.fog ? 50 : 0;
+        canvas.cameraClipFar = s.renderer.clipFar;
 
         props.layout = s.layout;
     },

+ 11 - 9
src/mol-util/param-definition.ts

@@ -122,7 +122,7 @@ export namespace ParamDefinition {
     }
     export function Color(defaultValue: ColorData, info?: Info & { isExpanded?: boolean }): Color {
         const ret = setInfo<Color>({ type: 'color', defaultValue }, info);
-        if (info && info.isExpanded) ret.isExpanded = info.isExpanded;
+        if (info?.isExpanded) ret.isExpanded = info.isExpanded;
         return ret;
     }
 
@@ -139,7 +139,7 @@ export namespace ParamDefinition {
     }
     export function File(info?: Info & { accept?: string, multiple?: boolean }): FileParam {
         const ret = setInfo<FileParam>({ type: 'file', defaultValue: void 0 as any }, info);
-        if (info && info.accept) ret.accept = info.accept;
+        if (info?.accept) ret.accept = info.accept;
         return ret;
     }
 
@@ -149,7 +149,7 @@ export namespace ParamDefinition {
     }
     export function FileList(info?: Info & { accept?: string, multiple?: boolean }): FileListParam {
         const ret = setInfo<FileListParam>({ type: 'file-list', defaultValue: void 0 as any }, info);
-        if (info && info.accept) ret.accept = info.accept;
+        if (info?.accept) ret.accept = info.accept;
         return ret;
     }
 
@@ -197,12 +197,14 @@ export namespace ParamDefinition {
         type: 'group',
         params: Params,
         isExpanded?: boolean,
-        isFlat?: boolean
+        isFlat?: boolean,
+        pivot?: keyof T
     }
-    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any }): Group<Normalize<T>> {
+    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any, pivot?: keyof T }): Group<Normalize<T>> {
         const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: info?.customDefault || getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
-        if (info && info.isExpanded) ret.isExpanded = info.isExpanded;
-        if (info && info.isFlat) ret.isFlat = info.isFlat;
+        if (info?.isExpanded) ret.isExpanded = info.isExpanded;
+        if (info?.isFlat) ret.isFlat = info.isFlat;
+        if (info?.pivot) ret.pivot = info.pivot as any;
         return ret;
     }
     export function EmptyGroup(info?: Info) {
@@ -226,7 +228,7 @@ export namespace ParamDefinition {
         }, info);
     }
     export function MappedStatic<C extends Params>(defaultKey: keyof C, map: C, info?: Info & { options?: [keyof C, string][], cycle?: boolean }): Mapped<NamedParamUnion<C>> {
-        const options: [string, string][] = info && info.options
+        const options: [string, string][] = info?.options
             ? info.options as [string, string][]
             : Object.keys(map).map(k => [k, map[k].label || stringToWords(k)]) as [string, string][];
         const name = checkDefaultKey(defaultKey, options);
@@ -245,7 +247,7 @@ export namespace ParamDefinition {
         getLabel(t: T): string
     }
     export function ObjectList<T>(element: For<T>, getLabel: (e: T) => string, info?: Info & { defaultValue?: T[], ctor?: () => T }): ObjectList<Normalize<T>> {
-        return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info && info.defaultValue) || []  }, info);
+        return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info?.defaultValue) || []  }, info);
     }
     function _defaultObjectListCtor(this: ObjectList) { return getDefaultValues(this.element) as any; }