diff --git a/src/mol-plugin/skin/base/components/controls-base.scss b/src/mol-plugin/skin/base/components/controls-base.scss index 4d9573ce7ae95897dde3de9c86cca1126d24b2fd..4bd3f6c21b1314a4d4daefe728338d306b86e74c 100644 --- a/src/mol-plugin/skin/base/components/controls-base.scss +++ b/src/mol-plugin/skin/base/components/controls-base.scss @@ -90,7 +90,7 @@ -webkit-appearance: none; -moz-appearance: none; appearance: none; - box-shadow: none; // !important; + box-shadow: none !important; &:hover { color: $hover-font-color; diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 168481fc2bda05f8aae21e347a971137f7207082..c0e2dc23b589b9cacb345975cca2e5861786e3bd 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -44,6 +44,10 @@ const StructureRepresentation3D = PluginStateTransform.Create<SO.Molecule.Struct name => PD.Group<any>(ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(name).getParams({ structure: a.data })) ), }), + canAutoUpdate({ oldParams, newParams }) { + // TODO: allow for small molecules + return oldParams.type.name === newParams.type.name; + }, apply({ a, params }, plugin: PluginContext) { return Task.create('Structure Representation', async ctx => { const provider = plugin.structureRepresentation.registry.get(params.type.name) diff --git a/src/mol-plugin/ui/state/apply-action.tsx b/src/mol-plugin/ui/state/apply-action.tsx index cfc0157dcadfb73a836f32027e19fb799daef8f8..c78e3cf1128a1c91fe80147df15a7f7bae4ad77e 100644 --- a/src/mol-plugin/ui/state/apply-action.tsx +++ b/src/mol-plugin/ui/state/apply-action.tsx @@ -44,6 +44,7 @@ class ApplyActionContol extends TransformContolBase<ApplyActionContol.Props, App getHeader() { return this.props.action.definition.display; } getHeaderFallback() { return this.props.action.id; } canApply() { return !this.state.error && !this.state.busy; } + canAutoApply() { return false; } applyText() { return 'Apply'; } isUpdate() { return false; } diff --git a/src/mol-plugin/ui/state/common.tsx b/src/mol-plugin/ui/state/common.tsx index faa468f19ca706faa2bdb271f01297487fd9200e..5934dcdd0f57542cde5a82964dc11b3f1e3a442b 100644 --- a/src/mol-plugin/ui/state/common.tsx +++ b/src/mol-plugin/ui/state/common.tsx @@ -101,6 +101,7 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState abstract getHeader(): Transformer.Definition['display']; abstract getHeaderFallback(): string; abstract canApply(): boolean; + abstract canAutoApply(newParams: any): boolean; abstract applyText(): string; abstract isUpdate(): boolean; abstract state: S; @@ -112,12 +113,25 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState this.apply(); } + private autoApplyHandle: number | undefined = void 0; + events: StateTransformParameters.Props['events'] = { onEnter: this.onEnter, - onChange: (params, isInitial, errors) => this.setState({ params, isInitial, error: errors && errors[0] }) + onChange: (params, isInitial, errors) => { + this.setState({ params, isInitial, error: errors && errors[0] }, () => { + if (!isInitial && !this.state.error && this.canAutoApply(params)) { + if (this.autoApplyHandle) clearTimeout(this.autoApplyHandle); + this.autoApplyHandle = setTimeout(this.apply, 50) as any as number; + } + }); + } } apply = async () => { + if (this.autoApplyHandle !== void 0) { + clearTimeout(this.autoApplyHandle); + this.autoApplyHandle = void 0; + } this.setState({ busy: true }); try { await this.applyAction(); diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx index 40b61774dc76df8eb9d860ad68ea560f330bd213..d31f378fc934dacf5e584b0d22eaf845e83ea68f 100644 --- a/src/mol-plugin/ui/state/update-transform.tsx +++ b/src/mol-plugin/ui/state/update-transform.tsx @@ -34,6 +34,18 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr applyText() { return this.canApply() ? 'Update' : 'Nothing to Update'; } isUpdate() { return true; } + canAutoApply(newParams: any) { + const autoUpdate = this.props.transform.transformer.definition.canAutoUpdate + if (!autoUpdate) return false; + + const { state } = this.props; + const cell = state.cells.get(this.props.transform.ref); + if (!cell || !cell.sourceRef || cell.status !== 'ok') return false; + const parentCell = state.cells.get(cell.sourceRef)!; + + return autoUpdate({ a: cell.obj!, b: parentCell.obj!, oldParams: this.props.transform.params, newParams }, this.plugin); + } + private _getInfo = memoizeOne((t: Transform) => StateTransformParameters.infoFromTransform(this.plugin, this.props.state, this.props.transform)); state: UpdateTransformContol.ComponentState = { transform: this.props.transform, error: void 0, isInitial: true, params: this.getInfo().initialValues, busy: false }; diff --git a/src/mol-state/transformer.ts b/src/mol-state/transformer.ts index 950e1b3764613e5ab795bd5ccea307352945b2aa..a8b95195ce76266fbe45f2c180d2820ed612ef57 100644 --- a/src/mol-state/transformer.ts +++ b/src/mol-state/transformer.ts @@ -44,6 +44,13 @@ export namespace Transformer { cache: unknown } + export interface AutoUpdateParams<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> { + a: A, + b: B, + oldParams: P, + newParams: P + } + export enum UpdateResult { Unchanged, Updated, Recreate } /** Specify default control descriptors for the parameters */ @@ -68,6 +75,9 @@ export namespace Transformer { */ update?(params: UpdateParams<A, B, P>, globalCtx: unknown): Task<UpdateResult> | UpdateResult, + /** Determine if the transformer can be applied automatically on UI change. Default is false. */ + canAutoUpdate?(params: AutoUpdateParams<A, B, P>, globalCtx: unknown): boolean, + params?(a: A, globalCtx: unknown): { [K in keyof P]: PD.Any }, /** Test if the transform can be applied to a given node */