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

mol-plugin: better trajectory controls

parent 2b58d68c
No related branches found
No related tags found
No related merge requests found
...@@ -149,4 +149,18 @@ ...@@ -149,4 +149,18 @@
margin-bottom: 1px; margin-bottom: 1px;
padding: 3px 6px; padding: 3px 6px;
} }
}
.msp-traj-controls {
position: absolute;
left: $control-spacing;
top: $control-spacing;
line-height: $row-height;
> span {
color: $font-color;
padding-top: 1px;
font-size: 85%;
display: inline-block;
}
} }
\ No newline at end of file
...@@ -75,9 +75,9 @@ ...@@ -75,9 +75,9 @@
background: $default-background; //$highlight-info-background; background: $default-background; //$highlight-info-background;
position: absolute; position: absolute;
top: $control-spacing; right: $control-spacing;
left: $control-spacing; bottom: $control-spacing;
text-align: left; text-align: right;
min-height: $row-height; min-height: $row-height;
max-width: 95%; max-width: 95%;
......
...@@ -132,4 +132,16 @@ ...@@ -132,4 +132,16 @@
.msp-icon-help-circle:before { .msp-icon-help-circle:before {
content: "\e81d"; content: "\e81d";
}
.msp-icon-model-prev:before {
content: "\e884";
}
.msp-icon-model-next:before {
content: "\e885";
}
.msp-icon-model-first:before {
content: "\e89c";
} }
\ No newline at end of file
...@@ -9,31 +9,72 @@ import { PluginCommands } from 'mol-plugin/command'; ...@@ -9,31 +9,72 @@ import { PluginCommands } from 'mol-plugin/command';
import { UpdateTrajectory } from 'mol-plugin/state/actions/structure'; import { UpdateTrajectory } from 'mol-plugin/state/actions/structure';
import { PluginUIComponent } from './base'; import { PluginUIComponent } from './base';
import { LociLabelEntry } from 'mol-plugin/util/loci-label-manager'; import { LociLabelEntry } from 'mol-plugin/util/loci-label-manager';
import { IconButton } from './controls/common';
import { PluginStateObject } from 'mol-plugin/state/objects';
import { StateTransforms } from 'mol-plugin/state/transforms';
import { StateTransformer } from 'mol-state';
import { ModelFromTrajectory } from 'mol-plugin/state/transforms/model';
export class Controls extends PluginUIComponent<{ }, { }> { export class TrajectoryControls extends PluginUIComponent<{}, { show: boolean, label: string }> {
render() { state = { show: false, label: '' }
return <>
private update = () => {
const state = this.plugin.state.dataState;
const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model)
.filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory));
if (models.length === 0) {
this.setState({ show: false })
}
let label = '', count = 0, parents = new Set<string>();
for (const m of models) {
if (!m.sourceRef) continue;
const parent = state.cells.get(m.sourceRef)!.obj as PluginStateObject.Molecule.Trajectory;
</>; if (!parent) continue;
if (parent.data.length > 1) {
if (parents.has(m.sourceRef)) {
// do not show the controls if there are 2 models of the same trajectory present
this.setState({ show: false });
}
parents.add(m.sourceRef);
count++;
if (!label) {
const idx = (m.transform.params! as StateTransformer.Params<ModelFromTrajectory>).modelIndex;
label = `Model ${idx + 1} / ${parent.data.length}`;
}
}
}
if (count > 1) label = '';
this.setState({ show: count > 0, label });
}
componentDidMount() {
this.subscribe(this.plugin.state.dataState.events.changed, this.update);
} }
}
export class TrajectoryControls extends PluginUIComponent {
render() { render() {
return <div> if (!this.state.show) return null;
<button className='msp-btn msp-btn-link' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.dataState, return <div className='msp-traj-controls'>
action: UpdateTrajectory.create({ action: 'advance', by: -1 }) <IconButton icon='model-first' title='First Model' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
})} title='Previou Model'></button>
<button className='msp-btn msp-btn-link' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.dataState, state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'reset' }) action: UpdateTrajectory.create({ action: 'reset' })
})} title='First Model'></button> })} />
<button className='msp-btn msp-btn-link' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, { <IconButton icon='model-prev' title='Previous Model' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'advance', by: -1 })
})} />
<IconButton icon='model-next' title='Next Model' onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.dataState, state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'advance', by: +1 }) action: UpdateTrajectory.create({ action: 'advance', by: 1 })
})} title='Next Model'></button><br /> })} />
</div> { !!this.state.label && <span>{this.state.label}</span> }
</div>;
} }
} }
...@@ -45,8 +86,10 @@ export class LociLabelControl extends PluginUIComponent<{}, { entries: ReadonlyA ...@@ -45,8 +86,10 @@ export class LociLabelControl extends PluginUIComponent<{}, { entries: ReadonlyA
} }
render() { render() {
return <div style={{ textAlign: 'right' }}> if (this.state.entries.length === 0) return null;
return <div className='msp-highlight-info'>
{this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)} {this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)}
</div> </div>;
} }
} }
\ No newline at end of file
...@@ -81,6 +81,14 @@ export class NumericInput extends React.PureComponent<{ ...@@ -81,6 +81,14 @@ export class NumericInput extends React.PureComponent<{
} }
} }
export function IconButton(props: { icon: string, onClick: (e: React.MouseEvent<HTMLButtonElement>) => void, title?: string, toggleState?: boolean }) {
let className = `msp-btn msp-btn-link msp-btn-icon`;
if (typeof props.toggleState !== 'undefined') className += ` msp-btn-link-toggle-${props.toggleState ? 'on' : 'off'}`
return <button className={className} onClick={props.onClick} title={props.title}>
<span className={`msp-icon msp-icon-${props.icon}`}/>
</button>;
}
// export const ToggleButton = (props: { // export const ToggleButton = (props: {
// onChange: (v: boolean) => void, // onChange: (v: boolean) => void,
......
...@@ -12,7 +12,7 @@ import * as React from 'react'; ...@@ -12,7 +12,7 @@ import * as React from 'react';
import { PluginContext } from '../context'; import { PluginContext } from '../context';
import { PluginReactContext, PluginUIComponent } from './base'; import { PluginReactContext, PluginUIComponent } from './base';
import { CameraSnapshots } from './camera'; import { CameraSnapshots } from './camera';
import { Controls, LociLabelControl, TrajectoryControls } from './controls'; import { LociLabelControl, TrajectoryControls } from './controls';
import { StateSnapshots } from './state'; import { StateSnapshots } from './state';
import { StateObjectActions } from './state/actions'; import { StateObjectActions } from './state/actions';
import { AnimationControls } from './state/animation'; import { AnimationControls } from './state/animation';
...@@ -60,7 +60,6 @@ class Layout extends PluginUIComponent { ...@@ -60,7 +60,6 @@ class Layout extends PluginUIComponent {
{layout.showControls && this.region('left', <State />)} {layout.showControls && this.region('left', <State />)}
{layout.showControls && this.region('right', <div className='msp-scrollable-container msp-right-controls'> {layout.showControls && this.region('right', <div className='msp-scrollable-container msp-right-controls'>
<CurrentObject /> <CurrentObject />
<Controls />
<AnimationControls /> <AnimationControls />
<CameraSnapshots /> <CameraSnapshots />
<StateSnapshots /> <StateSnapshots />
...@@ -72,20 +71,16 @@ class Layout extends PluginUIComponent { ...@@ -72,20 +71,16 @@ class Layout extends PluginUIComponent {
} }
} }
export class ViewportWrapper extends PluginUIComponent { export class ViewportWrapper extends PluginUIComponent {
render() { render() {
return <> return <>
<Viewport /> <Viewport />
<div style={{ position: 'absolute', left: '10px', top: '10px', color: 'white' }}> <TrajectoryControls />
<TrajectoryControls />
</div>
<ViewportControls /> <ViewportControls />
<div style={{ position: 'absolute', left: '10px', bottom: '10px' }}> <div style={{ position: 'absolute', left: '10px', bottom: '10px' }}>
<BackgroundTaskProgress /> <BackgroundTaskProgress />
</div> </div>
<div style={{ position: 'absolute', right: '10px', bottom: '10px' }}> <LociLabelControl />
<LociLabelControl />
</div>
</>; </>;
} }
} }
......
...@@ -212,7 +212,7 @@ namespace StateSelection { ...@@ -212,7 +212,7 @@ namespace StateSelection {
registerModifier('parent', parent); registerModifier('parent', parent);
export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.transforms.get(n.transform.ref)!.parent))); } export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.transforms.get(n.transform.ref)!.parent))); }
export function findAncestorOfType(tree: StateTree, cells: State.Cells, root: StateTransform.Ref, types: StateObject.Ctor[]): StateObjectCell | undefined { export function findAncestorOfType<T extends StateObject.Ctor>(tree: StateTree, cells: State.Cells, root: StateTransform.Ref, types: T[]): StateObjectCell<StateObject.From<T>> | undefined {
let current = tree.transforms.get(root)!, len = types.length; let current = tree.transforms.get(root)!, len = types.length;
while (true) { while (true) {
current = tree.transforms.get(current.parent)!; current = tree.transforms.get(current.parent)!;
...@@ -220,7 +220,7 @@ namespace StateSelection { ...@@ -220,7 +220,7 @@ namespace StateSelection {
if (!cell.obj) return void 0; if (!cell.obj) return void 0;
const obj = cell.obj; const obj = cell.obj;
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
if (obj.type === types[i].type) return cells.get(current.ref); if (obj.type === types[i].type) return cell as StateObjectCell<StateObject.From<T>>;
} }
if (current.ref === StateTransform.RootRef) { if (current.ref === StateTransform.RootRef) {
return void 0; return void 0;
......
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