Skip to content
Snippets Groups Projects
Unverified Commit d232b01c authored by David Sehnal's avatar David Sehnal Committed by GitHub
Browse files

Merge pull request #283 from MadCatX/improve-measurements-ux

Improve measurements user experience
parents 7f39cf0f ec952708
No related branches found
No related tags found
No related merge requests found
...@@ -17,10 +17,12 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'; ...@@ -17,10 +17,12 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { MeasurementRepresentationCommonTextParams, LociLabelTextParams } from '../../../mol-repr/shape/loci/common'; import { MeasurementRepresentationCommonTextParams, LociLabelTextParams } from '../../../mol-repr/shape/loci/common';
import { LineParams } from '../../../mol-repr/structure/representation/line'; import { LineParams } from '../../../mol-repr/structure/representation/line';
import { Expression } from '../../../mol-script/language/expression'; import { Expression } from '../../../mol-script/language/expression';
import { Color } from '../../../mol-util/color';
export { StructureMeasurementManager }; export { StructureMeasurementManager };
export const MeasurementGroupTag = 'measurement-group'; export const MeasurementGroupTag = 'measurement-group';
export const MeasurementOrderLabelTag = 'measurement-order-label';
export type StructureMeasurementCell = StateObjectCell<PluginStateObject.Shape.Representation3D, StateTransform<StateTransformer<PluginStateObject.Molecule.Structure.Selections, PluginStateObject.Shape.Representation3D, any>>> export type StructureMeasurementCell = StateObjectCell<PluginStateObject.Shape.Representation3D, StateTransform<StateTransformer<PluginStateObject.Molecule.Structure.Selections, PluginStateObject.Shape.Representation3D, any>>>
...@@ -281,6 +283,41 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu ...@@ -281,6 +283,41 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } }); await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
} }
async addOrderLabels(locis: StructureElement.Loci[]) {
const update = this.getGroup();
const current = this.plugin.state.data.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Selections).withTag(MeasurementOrderLabelTag));
for (const obj of current)
update.delete(obj);
let order = 1;
for (const loci of locis) {
const cell = this.plugin.helpers.substructureParent.get(loci.structure);
if (!cell) continue;
const dependsOn = [cell.transform.ref];
update
.apply(StateTransforms.Model.MultiStructureSelectionFromExpression, {
selections: [
{ key: 'a', ref: cell.transform.ref, expression: StructureElement.Loci.toExpression(loci) },
],
isTransitive: true,
label: 'Order'
}, { dependsOn, tags: MeasurementOrderLabelTag })
.apply(StateTransforms.Representation.StructureSelectionsLabel3D, {
textColor: Color.fromRgb(255, 255, 255),
borderColor: Color.fromRgb(0, 0, 0),
borderWidth: 0.5,
textSize: 0.33,
customText: `${order++}`
}, { tags: MeasurementOrderLabelTag });
}
const state = this.plugin.state.data;
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
}
private _empty: any[] = []; private _empty: any[] = [];
private getTransforms<T extends StateTransformer<A, B, any>, A extends PluginStateObject.Molecule.Structure.Selections, B extends StateObject>(transformer: T) { private getTransforms<T extends StateTransformer<A, B, any>, A extends PluginStateObject.Molecule.Structure.Selections, B extends StateObject>(transformer: T) {
const state = this.plugin.state.data; const state = this.plugin.state.data;
...@@ -291,8 +328,15 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu ...@@ -291,8 +328,15 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
} }
private sync() { private sync() {
const labels = [];
for (const cell of this.getTransforms(StateTransforms.Representation.StructureSelectionsLabel3D) as StructureMeasurementCell[]) {
const tags = (cell.obj as any)['tags'] as string[];
if (!tags || !tags.includes(MeasurementOrderLabelTag))
labels.push(cell);
}
const updated = this.updateState({ const updated = this.updateState({
labels: this.getTransforms(StateTransforms.Representation.StructureSelectionsLabel3D), labels,
distances: this.getTransforms(StateTransforms.Representation.StructureSelectionsDistance3D), distances: this.getTransforms(StateTransforms.Representation.StructureSelectionsDistance3D),
angles: this.getTransforms(StateTransforms.Representation.StructureSelectionsAngle3D), angles: this.getTransforms(StateTransforms.Representation.StructureSelectionsAngle3D),
dihedrals: this.getTransforms(StateTransforms.Representation.StructureSelectionsDihedral3D), dihedrals: this.getTransforms(StateTransforms.Representation.StructureSelectionsDihedral3D),
......
...@@ -62,7 +62,6 @@ export class MeasurementList extends PurePluginUIComponent { ...@@ -62,7 +62,6 @@ export class MeasurementList extends PurePluginUIComponent {
render() { render() {
const measurements = this.plugin.managers.structure.measurement.state; const measurements = this.plugin.managers.structure.measurement.state;
return <div style={{ marginTop: '6px' }}> return <div style={{ marginTop: '6px' }}>
{this.renderGroup(measurements.labels, 'Labels')} {this.renderGroup(measurements.labels, 'Labels')}
{this.renderGroup(measurements.distances, 'Distances')} {this.renderGroup(measurements.distances, 'Distances')}
...@@ -80,6 +79,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo ...@@ -80,6 +79,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
componentDidMount() { componentDidMount() {
this.subscribe(this.selection.events.additionsHistoryUpdated, () => { this.subscribe(this.selection.events.additionsHistoryUpdated, () => {
this.forceUpdate(); this.forceUpdate();
this.updateOrderLabels();
}); });
this.subscribe(this.plugin.behaviors.state.isBusy, v => { this.subscribe(this.plugin.behaviors.state.isBusy, v => {
...@@ -87,6 +87,33 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo ...@@ -87,6 +87,33 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
}); });
} }
componentWillUnmount() {
this.clearOrderLabels();
super.componentWillUnmount();
}
componentDidUpdate(prevProps: {}, prevState: { isBusy: boolean, action?: 'add' | 'options' }) {
if (this.state.action !== prevState.action)
this.updateOrderLabels();
}
clearOrderLabels() {
this.plugin.managers.structure.measurement.addOrderLabels([]);
}
updateOrderLabels() {
if (this.state.action !== 'add') {
this.clearOrderLabels();
return;
}
const locis = [];
const history = this.selection.additionsHistory;
for (let idx = 0; idx < history.length && idx < 4; idx++)
locis.push(history[idx].loci);
this.plugin.managers.structure.measurement.addOrderLabels(locis);
}
get selection() { get selection() {
return this.plugin.managers.structure.selection; return this.plugin.managers.structure.selection;
} }
...@@ -163,8 +190,8 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo ...@@ -163,8 +190,8 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
historyEntry(e: StructureSelectionHistoryEntry, idx: number) { historyEntry(e: StructureSelectionHistoryEntry, idx: number) {
const history = this.plugin.managers.structure.selection.additionsHistory; const history = this.plugin.managers.structure.selection.additionsHistory;
return <div className='msp-flex-row' key={e.id}> return <div className='msp-flex-row' key={e.id} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={() => this.plugin.managers.interactivity.lociHighlights.clearHighlights()}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={() => this.plugin.managers.interactivity.lociHighlights.clearHighlights()}> <Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }}>
{idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} /> {idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button> </Button>
{history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />} {history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment