theme.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ColorTheme } from './color';
  7. import { SizeTheme } from './size';
  8. import { Structure } from '../mol-model/structure';
  9. import { VolumeData } from '../mol-model/volume';
  10. import { ParamDefinition as PD } from '../mol-util/param-definition';
  11. import { Shape } from '../mol-model/shape';
  12. import { Task } from '../mol-task';
  13. export interface ThemeRegistryContext {
  14. colorThemeRegistry: ColorTheme.Registry
  15. sizeThemeRegistry: SizeTheme.Registry
  16. }
  17. export interface ThemeDataContext {
  18. [k: string]: any
  19. structure?: Structure
  20. volume?: VolumeData
  21. shape?: Shape
  22. }
  23. export { Theme }
  24. interface Theme {
  25. color: ColorTheme<any>
  26. size: SizeTheme<any>
  27. // label: LabelTheme // TODO
  28. }
  29. namespace Theme {
  30. type Props = { [k: string]: any }
  31. export function create(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props, theme?: Theme) {
  32. theme = theme || createEmpty()
  33. const colorProps = props.colorTheme as PD.NamedParams
  34. const sizeProps = props.sizeTheme as PD.NamedParams
  35. theme.color = ctx.colorThemeRegistry.create(colorProps.name, data, colorProps.params)
  36. theme.size = ctx.sizeThemeRegistry.create(sizeProps.name, data, sizeProps.params)
  37. return theme
  38. }
  39. export function createEmpty(): Theme {
  40. return { color: ColorTheme.Empty, size: SizeTheme.Empty }
  41. }
  42. export function ensureDependencies(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props) {
  43. return Task.create(`Theme dependencies`, async runtime => {
  44. await ctx.colorThemeRegistry.get(props.colorTheme.name).ensureDependencies?.(data).runInContext(runtime)
  45. await ctx.sizeThemeRegistry.get(props.sizeTheme.name).ensureDependencies?.(data).runInContext(runtime)
  46. })
  47. }
  48. }
  49. //
  50. export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends PD.Params> {
  51. readonly label: string
  52. readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => T
  53. readonly getParams: (ctx: ThemeDataContext) => P
  54. readonly defaultValues: PD.Values<P>
  55. readonly isApplicable: (ctx: ThemeDataContext) => boolean
  56. readonly ensureDependencies?: (ctx: ThemeDataContext) => Task<void>
  57. }
  58. function getTypes(list: { name: string, provider: ThemeProvider<any, any> }[]) {
  59. return list.map(e => [e.name, e.provider.label] as [string, string]);
  60. }
  61. export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
  62. private _list: { name: string, provider: ThemeProvider<T, any> }[] = []
  63. private _map = new Map<string, ThemeProvider<T, any>>()
  64. private _name = new Map<ThemeProvider<T, any>, string>()
  65. get default() { return this._list[0] }
  66. get list() { return this._list }
  67. get types(): [string, string][] { return getTypes(this._list) }
  68. constructor(builtInThemes: { [k: string]: ThemeProvider<T, any> }, private emptyProvider: ThemeProvider<T, any>) {
  69. Object.keys(builtInThemes).forEach(name => this.add(name, builtInThemes[name]))
  70. }
  71. add<P extends PD.Params>(name: string, provider: ThemeProvider<T, P>) {
  72. this._list.push({ name, provider })
  73. this._map.set(name, provider)
  74. this._name.set(provider, name)
  75. }
  76. remove(name: string) {
  77. this._list.splice(this._list.findIndex(e => e.name === name), 1)
  78. const p = this._map.get(name);
  79. if (p) {
  80. this._map.delete(name);
  81. this._name.delete(p);
  82. }
  83. }
  84. get<P extends PD.Params>(name: string): ThemeProvider<T, P> {
  85. return this._map.get(name) || this.emptyProvider
  86. }
  87. getName(provider: ThemeProvider<T, any>): string {
  88. if (!this._name.has(provider)) throw new Error(`'${provider.label}' is not a registered represenatation provider.`);
  89. return this._name.get(provider)!;
  90. }
  91. create(name: string, ctx: ThemeDataContext, props = {}) {
  92. const provider = this.get(name)
  93. return provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props })
  94. }
  95. getApplicableList(ctx: ThemeDataContext) {
  96. return this._list.filter(e => e.provider.isApplicable(ctx));
  97. }
  98. getApplicableTypes(ctx: ThemeDataContext) {
  99. return getTypes(this.getApplicableList(ctx))
  100. }
  101. }