ソースを参照

ParamDefinition.Select grouping & used in theme definitions

David Sehnal 5 年 前
コミット
2da3df6e4d
42 ファイル変更129 行追加27 行削除
  1. 1 1
      src/apps/state-docs/pd-to-md.ts
  2. 1 0
      src/examples/proteopedia-wrapper/coloring.ts
  3. 1 0
      src/mol-model-props/common/custom-element-property.ts
  4. 1 0
      src/mol-model-props/computed/themes/accessible-surface-area.ts
  5. 1 0
      src/mol-model-props/computed/themes/interaction-type.ts
  6. 1 0
      src/mol-model-props/integrative/cross-link-restraint/color.ts
  7. 1 0
      src/mol-model-props/pdbe/themes/structure-quality-report.ts
  8. 1 0
      src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts
  9. 1 0
      src/mol-model-props/rcsb/themes/density-fit.ts
  10. 1 0
      src/mol-model-props/rcsb/themes/geometry-quality.ts
  11. 1 0
      src/mol-model-props/rcsb/themes/random-coil-index.ts
  12. 59 2
      src/mol-plugin-ui/controls/action-menu.tsx
  13. 6 14
      src/mol-plugin-ui/controls/parameters.tsx
  14. 8 1
      src/mol-theme/color.ts
  15. 2 1
      src/mol-theme/color/carbohydrate-symbol.ts
  16. 1 0
      src/mol-theme/color/chain-id.ts
  17. 1 0
      src/mol-theme/color/element-index.ts
  18. 1 0
      src/mol-theme/color/element-symbol.ts
  19. 1 0
      src/mol-theme/color/entity-source.ts
  20. 1 0
      src/mol-theme/color/hydrophobicity.ts
  21. 1 0
      src/mol-theme/color/illustrative.ts
  22. 1 0
      src/mol-theme/color/model-index.ts
  23. 1 0
      src/mol-theme/color/molecule-type.ts
  24. 1 0
      src/mol-theme/color/occupancy.ts
  25. 1 0
      src/mol-theme/color/operator-hkl.ts
  26. 1 0
      src/mol-theme/color/operator-name.ts
  27. 1 0
      src/mol-theme/color/polymer-id.ts
  28. 1 0
      src/mol-theme/color/polymer-index.ts
  29. 1 0
      src/mol-theme/color/residue-name.ts
  30. 1 0
      src/mol-theme/color/secondary-structure.ts
  31. 1 0
      src/mol-theme/color/sequence-id.ts
  32. 1 0
      src/mol-theme/color/shape-group.ts
  33. 1 0
      src/mol-theme/color/uncertainty.ts
  34. 1 0
      src/mol-theme/color/uniform.ts
  35. 1 0
      src/mol-theme/color/unit-index.ts
  36. 1 1
      src/mol-theme/size.ts
  37. 1 0
      src/mol-theme/size/physical.ts
  38. 1 0
      src/mol-theme/size/shape-group.ts
  39. 1 0
      src/mol-theme/size/uncertainty.ts
  40. 1 0
      src/mol-theme/size/uniform.ts
  41. 14 3
      src/mol-theme/theme.ts
  42. 4 4
      src/mol-util/param-definition.ts

+ 1 - 1
src/apps/state-docs/pd-to-md.ts

@@ -39,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string {
     }
 }
 
-function oToS(options: readonly (readonly [string, string])[]) {
+function oToS(options: readonly (readonly [string, string] | [string, string, string])[]) {
     return options.map(o => `'${o[0]}'`).join(', ');
 }
 

+ 1 - 0
src/examples/proteopedia-wrapper/coloring.ts

@@ -96,6 +96,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
 
     const ProteopediaCustomColorThemeProvider: ColorTheme.Provider<ProteopediaCustomColorThemeParams> = {
         label: 'Proteopedia Custom',
+        category: 'Custom',
         factory: ProteopediaCustomColorTheme,
         getParams: getChainIdColorThemeParams,
         defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),

+ 1 - 0
src/mol-model-props/common/custom-element-property.ts

@@ -99,6 +99,7 @@ namespace CustomElementProperty {
 
         return {
             label: modelProperty.label,
+            category: 'Custom',
             factory: Coloring,
             getParams: () => ({}),
             defaultValues: {},

+ 1 - 0
src/mol-model-props/computed/themes/accessible-surface-area.ts

@@ -68,6 +68,7 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
 
 export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams> = {
     label: 'Accessible Surface Area',
+    category: ColorTheme.Category.Computed,
     factory: AccessibleSurfaceAreaColorTheme,
     getParams: getAccessibleSurfaceAreaColorThemeParams,
     defaultValues: PD.getDefaultValues(AccessibleSurfaceAreaColorThemeParams),

+ 1 - 0
src/mol-model-props/computed/themes/interaction-type.ts

@@ -108,6 +108,7 @@ export function InteractionTypeColorTheme(ctx: ThemeDataContext, props: PD.Value
 
 export const InteractionTypeColorThemeProvider: ColorTheme.Provider<InteractionTypeColorThemeParams> = {
     label: 'Interaction Type',
+    category: ColorTheme.Category.Computed,
     factory: InteractionTypeColorTheme,
     getParams: getInteractionTypeColorThemeParams,
     defaultValues: PD.getDefaultValues(InteractionTypeColorThemeParams),

+ 1 - 0
src/mol-model-props/integrative/cross-link-restraint/color.ts

@@ -63,6 +63,7 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<Cros
 
 export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThemeParams> = {
     label: 'Cross Link',
+    category: ColorTheme.Category.Advanced,
     factory: CrossLinkColorTheme,
     getParams: getCrossLinkColorThemeParams,
     defaultValues: PD.getDefaultValues(CrossLinkColorThemeParams),

+ 1 - 0
src/mol-model-props/pdbe/themes/structure-quality-report.ts

@@ -79,6 +79,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
 
 export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params> =  {
     label: 'PDBe Structure Quality Report',
+    category: 'PDBe',
     factory: StructureQualityReportColorTheme,
     getParams: ctx => {
         const issueTypes = StructureQualityReport.getIssueTypes(ctx.structure);

+ 1 - 0
src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts

@@ -93,6 +93,7 @@ export function AssemblySymmetryClusterColorTheme(ctx: ThemeDataContext, props:
 
 export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<AssemblySymmetryClusterColorThemeParams> = {
     label: 'RCSB Assembly Symmetry Cluster',
+    category: 'RCSB',
     factory: AssemblySymmetryClusterColorTheme,
     getParams: getAssemblySymmetryClusterColorThemeParams,
     defaultValues: PD.getDefaultValues(AssemblySymmetryClusterColorThemeParams),

+ 1 - 0
src/mol-model-props/rcsb/themes/density-fit.ts

@@ -62,6 +62,7 @@ export function DensityFitColorTheme(ctx: ThemeDataContext, props: {}): ColorThe
 
 export const DensityFitColorThemeProvider: ColorTheme.Provider<{}> = {
     label: 'RCSB Density Fit',
+    category: 'RCSB',
     factory: DensityFitColorTheme,
     getParams: () => ({}),
     defaultValues: PD.getDefaultValues({}),

+ 1 - 0
src/mol-model-props/rcsb/themes/geometry-quality.ts

@@ -102,6 +102,7 @@ export function GeometryQualityColorTheme(ctx: ThemeDataContext, props: PD.Value
 
 export const GeometryQualityColorThemeProvider: ColorTheme.Provider<GeometricQualityColorThemeParams> = {
     label: 'RCSB Geometry Quality',
+    category: 'RCSB',
     factory: GeometryQualityColorTheme,
     getParams: getGeometricQualityColorThemeParams,
     defaultValues: PD.getDefaultValues(getGeometricQualityColorThemeParams({})),

+ 1 - 0
src/mol-model-props/rcsb/themes/random-coil-index.ts

@@ -53,6 +53,7 @@ export function RandomCoilIndexColorTheme(ctx: ThemeDataContext, props: {}): Col
 
 export const RandomCoilIndexColorThemeProvider: ColorTheme.Provider<{}> = {
     label: 'RCSB Random Coil Index',
+    category: 'RCSB',
     factory: RandomCoilIndexColorTheme,
     getParams: () => ({}),
     defaultValues: PD.getDefaultValues({}),

+ 59 - 2
src/mol-plugin-ui/controls/action-menu.tsx

@@ -7,6 +7,7 @@
 import * as React from 'react'
 import { Icon } from './common';
 import { Subscription, BehaviorSubject, Observable } from 'rxjs';
+import { ParamDefinition } from '../../mol-util/param-definition';
 
 export class ActionMenu {
     private _command: BehaviorSubject<ActionMenu.Command>;
@@ -139,16 +140,29 @@ export namespace ActionMenu {
         }
     }
 
-    class Section extends React.PureComponent<{ menu: ActionMenu, header?: string, items: Spec, onSelect: OnSelect, current: Item | undefined  }, { isExpanded: boolean }> {
-        state = { isExpanded: false }
+    type SectionProps = { menu: ActionMenu, header?: string, items: Spec, onSelect: OnSelect, current: Item | undefined }
+    type SectionState = { items: Spec, current: Item | undefined, isExpanded: boolean }
+
+    class Section extends React.PureComponent<SectionProps, SectionState> {
+        state = {
+            items: this.props.items,
+            current: this.props.current,
+            isExpanded: !!this.props.current && !!findCurrent(this.props.items, this.props.current.value)
+        }
 
         toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => {
             this.setState({ isExpanded: !this.state.isExpanded });
             e.currentTarget.blur();
         }
 
+        static getDerivedStateFromProps(props: SectionProps, state: SectionState) {
+            if (props.items === state.items && props.current === state.current) return null;
+            return { items: props.items, current: props.current, isExpanded: props.current && !!findCurrent(props.items, props.current.value) }
+        }
+
         render() {
             const { header, items, onSelect, current, menu } = this.props;
+
             if (typeof items === 'string') return null;
             if (isItem(items)) return <Action menu={menu} item={items} onSelect={onSelect} current={current} />
             return <div>
@@ -196,4 +210,47 @@ export namespace ActionMenu {
         if (value) return { name, icon: iconOrValue, value };
         return { name, value: iconOrValue };
     }
+
+    function createSpecFromSelectParamSimple(param: ParamDefinition.Select<any>) {
+        const spec: Item[] = [];
+        for (const [v, l] of param.options) {
+            spec.push(ActionMenu.Item(l, v));
+        }
+        return spec as Spec;
+    }
+
+    function createSpecFromSelectParamCategories(param: ParamDefinition.Select<any>) {
+        const cats = new Map<string, (Item | string)[]>();
+        const spec: (Item | (Item | string)[] | string)[] = [];
+        for (const [v, l, c] of param.options) {
+            if (!!c) {
+                let cat = cats.get(c);
+                if (!cat) {
+                    cat = [c];
+                    cats.set(c, cat);
+                    spec.push(cat);
+                }
+                cat.push(ActionMenu.Item(l, v));
+            } else {
+                spec.push(ActionMenu.Item(l, v));
+            }
+        }
+        return spec as Spec;
+    }
+
+    export function createSpecFromSelectParam(param: ParamDefinition.Select<any>) {
+        for (const o of param.options) {
+            if (!!o[2]) return createSpecFromSelectParamCategories(param);
+        }
+        return createSpecFromSelectParamSimple(param);
+    }
+
+    export function findCurrent(spec: Spec, value: any): Item | undefined {
+        if (typeof spec === 'string') return;
+        if (isItem(spec)) return spec.value === value ? spec : void 0;
+        for (const s of spec) {
+            const found = findCurrent(s, value);
+            if (found) return found;
+        }
+    }
 }

+ 6 - 14
src/mol-plugin-ui/controls/parameters.tsx

@@ -8,7 +8,7 @@
 import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
 import { Color } from '../../mol-util/color';
 import { ColorListName, getColorListFromName } from '../../mol-util/color/lists';
-import { memoize1 } from '../../mol-util/memoize';
+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';
@@ -328,22 +328,14 @@ export class SelectControl extends SimpleParam<PD.Select<string | number>> {
         this.update(value);
     }
 
+    items = memoizeLatest((param: PD.Select<any>) => ActionMenu.createSpecFromSelectParam(param));
+
     renderControl() {
-        const isInvalid = this.props.value !== void 0 && !this.props.param.options.some(e => e[0] === this.props.value);
-        const items: ActionMenu.Item[] = [];
-        let current: ActionMenu.Item | undefined = void 0;
-        if (isInvalid) {
-            current = ActionMenu.Item(`[Invalid] ${this.props.value}`, this.props.value);
-            items.push(current);
-        }
-        for (const [value, label] of this.props.param.options) {
-            const item = ActionMenu.Item(label, value);
-            items.push(item);
-            if (value === this.props.value) current = item;
-        }
+        const items = this.items(this.props.param);
+        const current = ActionMenu.findCurrent(items, this.props.value);
 
         return <ActionMenu.Toggle menu={this.menu} disabled={this.props.isDisabled} 
-            onSelect={this.onSelect} items={items as ActionMenu.Spec} label={current?.name}
+            onSelect={this.onSelect} items={items as ActionMenu.Spec} label={current?.name || `[Invalid] ${this.props.value}`}
             current={current} />;
     }
 

+ 8 - 1
src/mol-theme/color.ts

@@ -46,6 +46,13 @@ interface ColorTheme<P extends PD.Params> {
     readonly legend?: Readonly<ScaleLegend | TableLegend>
 }
 namespace ColorTheme {
+    export const enum Category {
+        Basic = 'Basic',
+        Advanced = 'Advanced',
+        Computed = 'Computed',
+        Misc = 'Miscellaneous'
+    }
+
     export type Props = { [k: string]: any }
     export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
     export const EmptyFactory = () => Empty
@@ -62,7 +69,7 @@ namespace ColorTheme {
     }
 
     export interface Provider<P extends PD.Params> extends ThemeProvider<ColorTheme<P>, P> { }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
+    export const EmptyProvider: Provider<{}> = { label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
     export type Registry = ThemeRegistry<ColorTheme<any>>
     export function createRegistry() {

+ 2 - 1
src/mol-theme/color/carbohydrate-symbol.ts

@@ -61,7 +61,8 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: PD.Va
 }
 
 export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<CarbohydrateSymbolColorThemeParams> = {
-    label: 'Carbohydrate Symbol',
+    label: 'Carbohydrate Symbol',    
+    category: ColorTheme.Category.Advanced,
     factory: CarbohydrateSymbolColorTheme,
     getParams: getCarbohydrateSymbolColorThemeParams,
     defaultValues: PD.getDefaultValues(CarbohydrateSymbolColorThemeParams),

+ 1 - 0
src/mol-theme/color/chain-id.ts

@@ -119,6 +119,7 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: PD.Values<ChainI
 
 export const ChainIdColorThemeProvider: ColorTheme.Provider<ChainIdColorThemeParams> = {
     label: 'Chain Id',
+    category: ColorTheme.Category.Basic,
     factory: ChainIdColorTheme,
     getParams: getChainIdColorThemeParams,
     defaultValues: PD.getDefaultValues(ChainIdColorThemeParams),

+ 1 - 0
src/mol-theme/color/element-index.ts

@@ -73,6 +73,7 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<E
 
 export const ElementIndexColorThemeProvider: ColorTheme.Provider<ElementIndexColorThemeParams> = {
     label: 'Element Index',
+    category: ColorTheme.Category.Basic,
     factory: ElementIndexColorTheme,
     getParams: getElementIndexColorThemeParams,
     defaultValues: PD.getDefaultValues(ElementIndexColorThemeParams),

+ 1 - 0
src/mol-theme/color/element-symbol.ts

@@ -69,6 +69,7 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<
 
 export const ElementSymbolColorThemeProvider: ColorTheme.Provider<ElementSymbolColorThemeParams> = {
     label: 'Element Symbol',
+    category: ColorTheme.Category.Basic,
     factory: ElementSymbolColorTheme,
     getParams: getElementSymbolColorThemeParams,
     defaultValues: PD.getDefaultValues(ElementSymbolColorThemeParams),

+ 1 - 0
src/mol-theme/color/entity-source.ts

@@ -175,6 +175,7 @@ export function EntitySourceColorTheme(ctx: ThemeDataContext, props: PD.Values<E
 
 export const EntitySourceColorThemeProvider: ColorTheme.Provider<EntitySourceColorThemeParams> = {
     label: 'Entity Source',
+    category: ColorTheme.Category.Advanced,
     factory: EntitySourceColorTheme,
     getParams: getEntitySourceColorThemeParams,
     defaultValues: PD.getDefaultValues(EntitySourceColorThemeParams),

+ 1 - 0
src/mol-theme/color/hydrophobicity.ts

@@ -94,6 +94,7 @@ export function HydrophobicityColorTheme(ctx: ThemeDataContext, props: PD.Values
 
 export const HydrophobicityColorThemeProvider: ColorTheme.Provider<HydrophobicityColorThemeParams> = {
     label: 'Hydrophobicity',
+    category: ColorTheme.Category.Advanced,
     factory: HydrophobicityColorTheme,
     getParams: getHydrophobicityColorThemeParams,
     defaultValues: PD.getDefaultValues(HydrophobicityColorThemeParams),

+ 1 - 0
src/mol-theme/color/illustrative.ts

@@ -76,6 +76,7 @@ export function IllustrativeColorTheme(ctx: ThemeDataContext, props: PD.Values<I
 
 export const IllustrativeColorThemeProvider: ColorTheme.Provider<IllustrativeColorThemeParams> = {
     label: 'Illustrative',
+    category: ColorTheme.Category.Misc,
     factory: IllustrativeColorTheme,
     getParams: getIllustrativeColorThemeParams,
     defaultValues: PD.getDefaultValues(IllustrativeColorThemeParams),

+ 1 - 0
src/mol-theme/color/model-index.ts

@@ -61,6 +61,7 @@ export function ModelIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<Mod
 
 export const ModelIndexColorThemeProvider: ColorTheme.Provider<ModelIndexColorThemeParams> = {
     label: 'Model Index',
+    category: ColorTheme.Category.Basic,
     factory: ModelIndexColorTheme,
     getParams: getModelIndexColorThemeParams,
     defaultValues: PD.getDefaultValues(ModelIndexColorThemeParams),

+ 1 - 0
src/mol-theme/color/molecule-type.ts

@@ -78,6 +78,7 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<M
 
 export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<MoleculeTypeColorThemeParams> = {
     label: 'Molecule Type',
+    category: ColorTheme.Category.Advanced,
     factory: MoleculeTypeColorTheme,
     getParams: getMoleculeTypeColorThemeParams,
     defaultValues: PD.getDefaultValues(MoleculeTypeColorThemeParams),

+ 1 - 0
src/mol-theme/color/occupancy.ts

@@ -60,6 +60,7 @@ export function OccupancyColorTheme(ctx: ThemeDataContext, props: PD.Values<Occu
 
 export const OccupancyColorThemeProvider: ColorTheme.Provider<OccupancyColorThemeParams> = {
     label: 'Occupancy',
+    category: ColorTheme.Category.Advanced,
     factory: OccupancyColorTheme,
     getParams: getOccupancyColorThemeParams,
     defaultValues: PD.getDefaultValues(OccupancyColorThemeParams),

+ 1 - 0
src/mol-theme/color/operator-hkl.ts

@@ -119,6 +119,7 @@ export function OperatorHklColorTheme(ctx: ThemeDataContext, props: PD.Values<Op
 
 export const OperatorHklColorThemeProvider: ColorTheme.Provider<OperatorHklColorThemeParams> = {
     label: 'Operator HKL',
+    category: ColorTheme.Category.Advanced,
     factory: OperatorHklColorTheme,
     getParams: getOperatorHklColorThemeParams,
     defaultValues: PD.getDefaultValues(OperatorHklColorThemeParams),

+ 1 - 0
src/mol-theme/color/operator-name.ts

@@ -85,6 +85,7 @@ export function OperatorNameColorTheme(ctx: ThemeDataContext, props: PD.Values<O
 
 export const OperatorNameColorThemeProvider: ColorTheme.Provider<OperatorNameColorThemeParams> = {
     label: 'Operator Name',
+    category: ColorTheme.Category.Advanced,
     factory: OperatorNameColorTheme,
     getParams: getOperatorNameColorThemeParams,
     defaultValues: PD.getDefaultValues(OperatorNameColorThemeParams),

+ 1 - 0
src/mol-theme/color/polymer-id.ts

@@ -128,6 +128,7 @@ export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Poly
 
 export const PolymerIdColorThemeProvider: ColorTheme.Provider<PolymerIdColorThemeParams> = {
     label: 'Polymer Id',
+    category: ColorTheme.Category.Basic,
     factory: PolymerIdColorTheme,
     getParams: getPolymerIdColorThemeParams,
     defaultValues: PD.getDefaultValues(PolymerIdColorThemeParams),

+ 1 - 0
src/mol-theme/color/polymer-index.ts

@@ -88,6 +88,7 @@ export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<P
 
 export const PolymerIndexColorThemeProvider: ColorTheme.Provider<PolymerIndexColorThemeParams> = {
     label: 'Polymer Index',
+    category: ColorTheme.Category.Advanced,
     factory: PolymerIndexColorTheme,
     getParams: getPolymerIndexColorThemeParams,
     defaultValues: PD.getDefaultValues(PolymerIndexColorThemeParams),

+ 1 - 0
src/mol-theme/color/residue-name.ts

@@ -130,6 +130,7 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<Re
 
 export const ResidueNameColorThemeProvider: ColorTheme.Provider<ResidueNameColorThemeParams> = {
     label: 'Residue Name',
+    category: ColorTheme.Category.Basic,
     factory: ResidueNameColorTheme,
     getParams: getResidueNameColorThemeParams,
     defaultValues: PD.getDefaultValues(ResidueNameColorThemeParams),

+ 1 - 0
src/mol-theme/color/secondary-structure.ts

@@ -113,6 +113,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
 
 export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<SecondaryStructureColorThemeParams> = {
     label: 'Secondary Structure',
+    category: ColorTheme.Category.Basic,
     factory: SecondaryStructureColorTheme,
     getParams: getSecondaryStructureColorThemeParams,
     defaultValues: PD.getDefaultValues(SecondaryStructureColorThemeParams),

+ 1 - 0
src/mol-theme/color/sequence-id.ts

@@ -101,6 +101,7 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Seq
 
 export const SequenceIdColorThemeProvider: ColorTheme.Provider<SequenceIdColorThemeParams> = {
     label: 'Sequence Id',
+    category: ColorTheme.Category.Basic,
     factory: SequenceIdColorTheme,
     getParams: getSequenceIdColorThemeParams,
     defaultValues: PD.getDefaultValues(SequenceIdColorThemeParams),

+ 1 - 0
src/mol-theme/color/shape-group.ts

@@ -37,6 +37,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<Sha
 
 export const ShapeGroupColorThemeProvider: ColorTheme.Provider<ShapeGroupColorThemeParams> = {
     label: 'Shape Group',
+    category: ColorTheme.Category.Advanced,
     factory: ShapeGroupColorTheme,
     getParams: getShapeGroupColorThemeParams,
     defaultValues: PD.getDefaultValues(ShapeGroupColorThemeParams),

+ 1 - 0
src/mol-theme/color/uncertainty.ts

@@ -64,6 +64,7 @@ export function UncertaintyColorTheme(ctx: ThemeDataContext, props: PD.Values<Un
 
 export const UncertaintyColorThemeProvider: ColorTheme.Provider<UncertaintyColorThemeParams> = {
     label: 'Uncertainty/Disorder',
+    category: ColorTheme.Category.Advanced,
     factory: UncertaintyColorTheme,
     getParams: getUncertaintyColorThemeParams,
     defaultValues: PD.getDefaultValues(UncertaintyColorThemeParams),

+ 1 - 0
src/mol-theme/color/uniform.ts

@@ -37,6 +37,7 @@ export function UniformColorTheme(ctx: ThemeDataContext, props: PD.Values<Unifor
 
 export const UniformColorThemeProvider: ColorTheme.Provider<UniformColorThemeParams> = {
     label: 'Uniform',
+    category: ColorTheme.Category.Basic,
     factory: UniformColorTheme,
     getParams: getUniformColorThemeParams,
     defaultValues: PD.getDefaultValues(UniformColorThemeParams),

+ 1 - 0
src/mol-theme/color/unit-index.ts

@@ -73,6 +73,7 @@ export function UnitIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<Unit
 
 export const UnitIndexColorThemeProvider: ColorTheme.Provider<UnitIndexColorThemeParams> = {
     label: 'Unit Index',
+    category: ColorTheme.Category.Advanced,
     factory: UnitIndexColorTheme,
     getParams: getUnitIndexColorThemeParams,
     defaultValues: PD.getDefaultValues(UnitIndexColorThemeParams),

+ 1 - 1
src/mol-theme/size.ts

@@ -32,7 +32,7 @@ namespace SizeTheme {
     }
 
     export interface Provider<P extends PD.Params> extends ThemeProvider<SizeTheme<P>, P> { }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
+    export const EmptyProvider: Provider<{}> = { label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
     export type Registry = ThemeRegistry<SizeTheme<any>>
     export function createRegistry() {

+ 1 - 0
src/mol-theme/size/physical.ts

@@ -58,6 +58,7 @@ export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PD.Values<Physic
 
 export const PhysicalSizeThemeProvider: SizeTheme.Provider<PhysicalSizeThemeParams> = {
     label: 'Physical',
+    category: '',
     factory: PhysicalSizeTheme,
     getParams: getPhysicalSizeThemeParams,
     defaultValues: PD.getDefaultValues(PhysicalSizeThemeParams),

+ 1 - 0
src/mol-theme/size/shape-group.ts

@@ -36,6 +36,7 @@ export function ShapeGroupSizeTheme(ctx: ThemeDataContext, props: PD.Values<Shap
 
 export const ShapeGroupSizeThemeProvider: SizeTheme.Provider<ShapeGroupSizeThemeParams> = {
     label: 'Shape Group',
+    category: '',    
     factory: ShapeGroupSizeTheme,
     getParams: getShapeGroupSizeThemeParams,
     defaultValues: PD.getDefaultValues(ShapeGroupSizeThemeParams),

+ 1 - 0
src/mol-theme/size/uncertainty.ts

@@ -54,6 +54,7 @@ export function UncertaintySizeTheme(ctx: ThemeDataContext, props: PD.Values<Unc
 
 export const UncertaintySizeThemeProvider: SizeTheme.Provider<UncertaintySizeThemeParams> = {
     label: 'Uncertainty/Disorder',
+    category: '',
     factory: UncertaintySizeTheme,
     getParams: getUncertaintySizeThemeParams,
     defaultValues: PD.getDefaultValues(UncertaintySizeThemeParams),

+ 1 - 0
src/mol-theme/size/uniform.ts

@@ -32,6 +32,7 @@ export function UniformSizeTheme(ctx: ThemeDataContext, props: PD.Values<Uniform
 
 export const UniformSizeThemeProvider: SizeTheme.Provider<UniformSizeThemeParams> = {
     label: 'Uniform',
+    category: '',
     factory: UniformSizeTheme,
     getParams: getUniformSizeThemeParams,
     defaultValues: PD.getDefaultValues(UniformSizeThemeParams),

+ 14 - 3
src/mol-theme/theme.ts

@@ -60,7 +60,8 @@ namespace Theme {
 //
 
 export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends PD.Params> {
-    readonly label: string
+    readonly label: string    
+    readonly category: string
     readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => T
     readonly getParams: (ctx: ThemeDataContext) => P
     readonly defaultValues: PD.Values<P>
@@ -69,7 +70,7 @@ export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends
 }
 
 function getTypes(list: { name: string, provider: ThemeProvider<any, any> }[]) {
-    return list.map(e => [e.name, e.provider.label] as [string, string]);
+    return list.map(e => [e.name, e.provider.label, e.provider.category] as [string, string, string]);
 }
 
 export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
@@ -79,16 +80,26 @@ export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
 
     get default() { return this._list[0] }
     get list() { return this._list }
-    get types(): [string, string][] { return getTypes(this._list) }
+    get types(): [string, string, string][] { return getTypes(this._list) }
 
     constructor(builtInThemes: { [k: string]: ThemeProvider<T, any> }, private emptyProvider: ThemeProvider<T, any>) {
         Object.keys(builtInThemes).forEach(name => this.add(name, builtInThemes[name]))
     }
 
+    private sort() {
+        this._list.sort((a, b) => {
+            if (a.provider.category === b.provider.category) {
+                return a.provider.label < b.provider.label ? -1 : a.provider.label > b.provider.label ? 1 : 0;
+            }
+            return a.provider.category < b.provider.label ? -1 : 1;
+        });
+    }
+
     add<P extends PD.Params>(name: string, provider: ThemeProvider<T, P>) {
         this._list.push({ name, provider })
         this._map.set(name, provider)
         this._name.set(provider, name)
+        this.sort();
     }
 
     remove(name: string) {

+ 4 - 4
src/mol-util/param-definition.ts

@@ -65,9 +65,9 @@ export namespace ParamDefinition {
     export interface Select<T extends string | number> extends Base<T> {
         type: 'select'
         /** array of (value, label) tuples */
-        options: readonly (readonly [T, string])[]
+        options: readonly (readonly [T, string] | [T, string, string])[]
     }
-    export function Select<T extends string | number>(defaultValue: T, options: readonly (readonly [T, string])[], info?: Info): Select<T> {
+    export function Select<T extends string | number>(defaultValue: T, options: readonly (readonly [T, string] | [T, string, string])[], info?: Info): Select<T> {
         return setInfo<Select<T>>({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options }, info)
     }
 
@@ -201,7 +201,7 @@ export namespace ParamDefinition {
         select: Select<string>,
         map(name: string): Any
     }
-    export function Mapped<T>(defaultKey: string, names: [string, string][], map: (name: string) => Any, info?: Info): Mapped<NamedParams<T>> {
+    export function Mapped<T>(defaultKey: string, names: ([string, string] | [string, string, string])[], map: (name: string) => Any, info?: Info): Mapped<NamedParams<T>> {
         const name = checkDefaultKey(defaultKey, names);
         return setInfo<Mapped<NamedParams<T>>>({
             type: 'mapped',
@@ -406,7 +406,7 @@ export namespace ParamDefinition {
         return ret;
     }
 
-    function checkDefaultKey<T>(k: T, options: readonly (readonly [T, string])[]) {
+    function checkDefaultKey<T>(k: T, options: readonly (readonly [T, string] | [T, string, string])[]) {
         for (const o of options) {
             if (o[0] === k) return k;
         }