Skip to content
Snippets Groups Projects
Commit 8eaac7e3 authored by David Sehnal's avatar David Sehnal
Browse files

mol-state: selection & removed separate context

parent b3b43778
No related branches found
No related tags found
No related merge requests found
......@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateTree, StateSelection, Transformer, Transform } from 'mol-state';
import { StateTree, Transformer, Transform } from 'mol-state';
import { Canvas3D } from 'mol-canvas3d/canvas3d';
import { StateTransforms } from './state/transforms';
import { PluginStateObject as PSO } from './state/base';
......@@ -28,15 +28,15 @@ export class PluginContext {
readonly events = {
state: {
data: this.state.data.context.events,
behavior: this.state.behavior.context.events
data: this.state.data.events,
behavior: this.state.behavior.events
}
};
readonly behaviors = {
state: {
data: this.state.data.context.behaviors,
behavior: this.state.behavior.context.behaviors
data: this.state.data.behaviors,
behavior: this.state.behavior.behaviors
},
canvas: {
highlightLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }),
......@@ -151,7 +151,7 @@ export class PluginContext {
}
_test_centerView() {
const sel = StateSelection.select(StateSelection.root().subtree().ofType(SO.Molecule.Structure.type), this.state.data);
const sel = this.state.data.select(q => q.root.subtree().ofType(SO.Molecule.Structure.type));
if (!sel.length) return;
const center = (sel[0].obj! as SO.Molecule.Structure).data.boundary.sphere.center;
......@@ -160,7 +160,7 @@ export class PluginContext {
}
_test_nextModel() {
const models = StateSelection.select('models', this.state.data)[0].obj as SO.Molecule.Models;
const models = this.state.data.select('models')[0].obj as SO.Molecule.Models;
const idx = (this.state.data.tree.nodes.get('structure')!.params as Transformer.Params<typeof StateTransforms.Model.CreateStructureFromModel>).modelIndex;
const newTree = StateTree.updateParams(this.state.data.tree, 'structure', { modelIndex: (idx + 1) % models.data.length });
return this.state.updateData(newTree);
......
......@@ -6,7 +6,7 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { Transform, Transformer, StateSelection } from 'mol-state';
import { Transform, Transformer } from 'mol-state';
import { ParametersComponent } from 'mol-app/component/parameters';
export class Controls extends React.Component<{ plugin: PluginContext }, { id: string }> {
......@@ -99,7 +99,9 @@ export class _test_UpdateTransform extends React.Component<{ plugin: PluginConte
const def = this.getTransform().transformer.definition;
if (!def.params || !def.params.controls) return void 0;
const src = StateSelection.ancestorOfType(this.props.nodeRef, def.from).select(this.props.plugin.state.data)[0];
const src = this.props.plugin.state.data.select(q => q.byRef(this.props.nodeRef).ancestorOfType(def.from))[0];
// StateSelection.ancestorOfType(this.props.nodeRef, def.from).select(this.props.plugin.state.data)[0];
console.log(src, def.from);
......
......@@ -13,7 +13,7 @@ import { PluginCommands } from 'mol-plugin/command';
export class StateTree extends React.Component<{ plugin: PluginContext, state: State }, { }> {
componentDidMount() {
// TODO: move to constructor?
this.props.state.context.events.updated.subscribe(() => this.forceUpdate());
this.props.state.events.updated.subscribe(() => this.forceUpdate());
}
render() {
// const n = this.props.plugin.state.data.tree.nodes.get(this.props.plugin.state.data.tree.rootRef)!;
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateObject } from './object';
import { Transform } from './transform';
import { RxEventHelper } from 'mol-util/rx-event-helper';
export { StateContext }
class StateContext {
private ev = RxEventHelper.create();
readonly events = {
object: {
stateChanged: this.ev<{ ref: Transform.Ref }>(),
propsChanged: this.ev<{ ref: Transform.Ref, newProps: unknown }>(),
updated: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
replaced: this.ev<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>(),
created: this.ev<{ ref: Transform.Ref, obj: StateObject }>(),
removed: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
currentChanged: this.ev<{ ref: Transform.Ref }>()
},
warn: this.ev<string>(),
updated: this.ev<void>()
};
readonly behaviors = {
currentObject: this.ev.behavior<{ ref: Transform.Ref }>(void 0 as any)
};
readonly globalContext: unknown;
dispose() {
this.ev.dispose();
}
constructor(params: { globalContext: unknown }) {
this.globalContext = params.globalContext;
this.behaviors.currentObject.next({ ref: Transform.RootRef });
}
}
\ No newline at end of file
......@@ -8,6 +8,4 @@ export * from './object'
export * from './state'
export * from './transformer'
export * from './tree'
export * from './context'
export * from './transform'
export * from './selection'
\ No newline at end of file
export * from './transform'
\ No newline at end of file
......@@ -8,10 +8,10 @@ import { StateObject, StateObjectCell } from './object';
import { StateTree } from './tree';
import { Transform } from './transform';
import { Transformer } from './transformer';
import { StateContext } from './context';
import { UUID } from 'mol-util';
import { RuntimeContext, Task } from 'mol-task';
import { StateSelection } from './selection';
import { StateSelection } from './state/selection';
import { RxEventHelper } from 'mol-util/rx-event-helper';
export { State }
......@@ -20,11 +20,35 @@ class State {
private _current: Transform.Ref = this._tree.root.ref;
private transformCache = new Map<Transform.Ref, unknown>();
private ev = RxEventHelper.create();
readonly globalContext: unknown = void 0;
readonly events = {
object: {
stateChanged: this.ev<{ ref: Transform.Ref }>(),
propsChanged: this.ev<{ ref: Transform.Ref, newProps: unknown }>(),
updated: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
replaced: this.ev<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>(),
created: this.ev<{ ref: Transform.Ref, obj: StateObject }>(),
removed: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
currentChanged: this.ev<{ ref: Transform.Ref }>()
},
warn: this.ev<string>(),
updated: this.ev<void>()
};
readonly behaviors = {
currentObject: this.ev.behavior<{ ref: Transform.Ref }>({ ref: Transform.RootRef })
};
get tree() { return this._tree; }
get current() { return this._current; }
readonly cells: State.Cells = new Map();
readonly context: StateContext;
// readonly context: StateContext;
getSnapshot(): State.Snapshot {
return { tree: StateTree.toJSON(this._tree) };
......@@ -37,7 +61,7 @@ class State {
setCurrent(ref: Transform.Ref) {
this._current = ref;
this.context.behaviors.currentObject.next({ ref });
this.behaviors.currentObject.next({ ref });
}
updateCellState(ref: Transform.Ref, state?: Partial<StateObjectCell.State>) {
......@@ -45,15 +69,21 @@ class State {
}
dispose() {
this.context.dispose();
this.ev.dispose();
}
select(selector: StateSelection.Selector) {
return StateSelection.select(selector, this);
/**
* Select Cells by ref or a query generated on the fly.
* @example state.select('test')
* @example state.select(q => q.byRef('test').subtree())
*/
select(selector: Transform.Ref | ((q: typeof StateSelection.Generators) => StateSelection.Selector)) {
if (typeof selector === 'string') return StateSelection.select(selector, this);
return StateSelection.select(selector(StateSelection.Generators), this)
}
get selector() {
return StateSelection;
query(q: StateSelection.Query) {
return q(this);
}
update(tree: StateTree): Task<void> {
......@@ -64,7 +94,7 @@ class State {
this._tree = tree;
const ctx: UpdateContext = {
stateCtx: this.context,
parent: this,
taskCtx,
oldTree,
tree,
......@@ -74,7 +104,7 @@ class State {
// TODO: have "cancelled" error? Or would this be handled automatically?
await update(ctx);
} finally {
this.context.events.updated.next();
this.events.updated.next();
}
});
}
......@@ -91,9 +121,7 @@ class State {
state: { ...StateObjectCell.DefaultState }
});
this.context = new StateContext({
globalContext: params && params.globalContext
});
this.globalContext = params && params.globalContext;
}
}
......@@ -112,7 +140,8 @@ namespace State {
type Ref = Transform.Ref
interface UpdateContext {
stateCtx: StateContext,
parent: State,
taskCtx: RuntimeContext,
oldTree: StateTree,
tree: StateTree,
......@@ -127,7 +156,7 @@ async function update(ctx: UpdateContext) {
const obj = ctx.cells.has(d) ? ctx.cells.get(d)!.obj : void 0;
ctx.cells.delete(d);
ctx.transformCache.delete(d);
ctx.stateCtx.events.object.removed.next({ ref: d, obj });
ctx.parent.events.object.removed.next({ ref: d, obj });
// TODO: handle current object change
}
......@@ -169,7 +198,7 @@ function setObjectStatus(ctx: UpdateContext, ref: Ref, status: StateObjectCell.S
const changed = obj.status !== status;
obj.status = status;
obj.errorText = errorText;
if (changed) ctx.stateCtx.events.object.stateChanged.next({ ref });
if (changed) ctx.parent.events.object.stateChanged.next({ ref });
}
function _initObjectStatusVisitor(t: Transform, _: any, ctx: UpdateContext) {
......@@ -208,7 +237,7 @@ function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
setObjectStatus(ctx, ref, 'error', errorText);
const wrap = ctx.cells.get(ref)!;
if (wrap.obj) {
ctx.stateCtx.events.object.removed.next({ ref });
ctx.parent.events.object.removed.next({ ref });
ctx.transformCache.delete(ref);
wrap.obj = void 0;
}
......@@ -240,11 +269,11 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) {
const update = await updateNode(ctx, root);
setObjectStatus(ctx, root, 'ok');
if (update.action === 'created') {
ctx.stateCtx.events.object.created.next({ ref: root, obj: update.obj! });
ctx.parent.events.object.created.next({ ref: root, obj: update.obj! });
} else if (update.action === 'updated') {
ctx.stateCtx.events.object.updated.next({ ref: root, obj: update.obj });
ctx.parent.events.object.updated.next({ ref: root, obj: update.obj });
} else if (update.action === 'replaced') {
ctx.stateCtx.events.object.replaced.next({ ref: root, oldObj: update.oldObj, newObj: update.newObj });
ctx.parent.events.object.replaced.next({ ref: root, oldObj: update.oldObj, newObj: update.newObj });
}
} catch (e) {
doError(ctx, root, '' + e);
......@@ -305,7 +334,7 @@ function runTask<T>(t: T | Task<T>, ctx: RuntimeContext) {
function createObject(ctx: UpdateContext, ref: Ref, transformer: Transformer, a: StateObject, params: any) {
const cache = {};
ctx.transformCache.set(ref, cache);
return runTask(transformer.definition.apply({ a, params, cache }, ctx.stateCtx.globalContext), ctx.taskCtx);
return runTask(transformer.definition.apply({ a, params, cache }, ctx.parent.globalContext), ctx.taskCtx);
}
async function updateObject(ctx: UpdateContext, ref: Ref, transformer: Transformer, a: StateObject, b: StateObject, oldParams: any, newParams: any) {
......@@ -317,5 +346,5 @@ async function updateObject(ctx: UpdateContext, ref: Ref, transformer: Transform
cache = {};
ctx.transformCache.set(ref, cache);
}
return runTask(transformer.definition.update({ a, oldParams, b, newParams, cache }, ctx.stateCtx.globalContext), ctx.taskCtx);
return runTask(transformer.definition.update({ a, oldParams, b, newParams, cache }, ctx.parent.globalContext), ctx.taskCtx);
}
\ No newline at end of file
......@@ -4,10 +4,10 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateObject, StateObjectCell } from './object';
import { State } from './state';
import { StateTree } from './tree';
import { Transform } from './transform';
import { StateObject, StateObjectCell } from '../object';
import { State } from '../state';
import { StateTree } from '../tree';
import { Transform } from '../transform';
namespace StateSelection {
export type Selector = Query | Builder | string | StateObjectCell;
......@@ -19,12 +19,12 @@ namespace StateSelection {
}
export function compile(s: Selector): Query {
const selector = s ? s : root();
const selector = s ? s : Generators.root;
let query: Query;
if (isBuilder(selector)) query = (selector as any).compile();
else if (isObj(selector)) query = (byValue(selector) as any).compile();
else if (isObj(selector)) query = (Generators.byValue(selector) as any).compile();
else if (isQuery(selector)) query = selector;
else query = (byRef(selector as string) as any).compile();
else query = (Generators.byRef(selector as string) as any).compile();
return query;
}
......@@ -58,8 +58,8 @@ namespace StateSelection {
}
const BuilderPrototype: any = {
select(state: State) {
return select(this, state);
select(state?: State) {
return select(this, state || this.state);
}
};
......@@ -71,23 +71,24 @@ namespace StateSelection {
return Object.create(BuilderPrototype, { compile: { writable: false, configurable: false, value: compile } });
}
export function root() { return build(() => (state: State) => [state.cells.get(state.tree.root.ref)!]) }
export namespace Generators {
export const root = build(() => (state: State) => [state.cells.get(state.tree.root.ref)!]);
export function byRef(...refs: Transform.Ref[]) {
return build(() => (state: State) => {
const ret: StateObjectCell[] = [];
for (const ref of refs) {
const n = state.cells.get(ref);
if (!n) continue;
ret.push(n);
}
return ret;
});
}
export function byRef(...refs: Transform.Ref[]) {
return build(() => (state: State) => {
const ret: StateObjectCell[] = [];
for (const ref of refs) {
const n = state.cells.get(ref);
if (!n) continue;
ret.push(n);
}
return ret;
});
export function byValue(...objects: StateObjectCell[]) { return build(() => (state: State) => objects); }
}
export function byValue(...objects: StateObjectCell[]) { return build(() => (state: State) => objects); }
registerModifier('flatMap', flatMap);
export function flatMap(b: Selector, f: (obj: StateObjectCell, state: State) => CellSeq) {
const q = compile(b);
......
import { State, StateObject, StateTree, Transformer, StateSelection } from 'mol-state';
import { State, StateObject, StateTree, Transformer } from 'mol-state';
import { Task } from 'mol-task';
import * as util from 'util';
......@@ -65,11 +65,11 @@ export async function runTask<A>(t: A | Task<A>): Promise<A> {
}
function hookEvents(state: State) {
state.context.events.object.created.subscribe(e => console.log('created:', e.ref));
state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref));
state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.cells.get(e.ref)!.status));
state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref));
state.events.object.created.subscribe(e => console.log('created:', e.ref));
state.events.object.removed.subscribe(e => console.log('removed:', e.ref));
state.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
state.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.cells.get(e.ref)!.status));
state.events.object.updated.subscribe(e => console.log('updated:', e.ref));
}
export async function testState() {
......@@ -107,8 +107,7 @@ export async function testState() {
console.log('----------------');
const q = StateSelection.byRef('square').parent();
const sel = StateSelection.select(q, state);
const sel = state.select('square');
console.log(sel);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment