Skip to content
Snippets Groups Projects
Commit e301eca9 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, sequence view, select options

parent 8a4ef015
No related branches found
No related tags found
No related merge requests found
...@@ -8,42 +8,42 @@ ...@@ -8,42 +8,42 @@
select, button, input[type=text] { select, button, input[type=text] {
@extend .msp-form-control; @extend .msp-form-control;
} }
button { button {
@extend .msp-btn; @extend .msp-btn;
@extend .msp-btn-block; @extend .msp-btn-block;
} }
} }
.msp-control-row { .msp-control-row {
position: relative; position: relative;
height: $row-height; height: $row-height;
background: $default-background; background: $default-background;
margin-top: 1px; margin-top: 1px;
> span { > span {
line-height: $row-height; line-height: $row-height;
display: block; display: block;
width: $control-label-width + $control-spacing; width: $control-label-width + $control-spacing;
text-align: right; text-align: right;
padding: 0 $control-spacing; padding: 0 $control-spacing;
color: color-lower-contrast($font-color, 15%); color: color-lower-contrast($font-color, 15%);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@include non-selectable; @include non-selectable;
} }
select, button, input[type=text] { select, button, input[type=text] {
@extend .msp-form-control; @extend .msp-form-control;
} }
button { button {
@extend .msp-btn; @extend .msp-btn;
@extend .msp-btn-block; @extend .msp-btn-block;
} }
> div:nth-child(2) { > div:nth-child(2) {
background: $msp-form-control-background; background: $msp-form-control-background;
position: absolute; position: absolute;
...@@ -58,21 +58,21 @@ ...@@ -58,21 +58,21 @@
position: relative; position: relative;
} }
.msp-toggle-button { .msp-toggle-button {
.msp-icon { .msp-icon {
display: inline-block; display: inline-block;
margin-right: 6px; margin-right: 6px;
} }
> div > button:hover { > div > button:hover {
border-color: color-increase-contrast($msp-form-control-background, 5%) !important; border-color: color-increase-contrast($msp-form-control-background, 5%) !important;
border: none; border: none;
outline-offset: -1px !important; outline-offset: -1px !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important; outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
} }
} }
.msp-slider { .msp-slider {
> div:first-child { > div:first-child {
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -91,14 +91,14 @@ ...@@ -91,14 +91,14 @@
top: 0; top: 0;
bottom: 0; bottom: 0;
} }
input[type=text] { input[type=text] {
padding-right: 6px; padding-right: 6px;
padding-left: 4px; padding-left: 4px;
font-size: 80%; font-size: 80%;
text-align: right; text-align: right;
} }
// input[type=range] { // input[type=range] {
// width: 100%; // width: 100%;
// } // }
...@@ -135,14 +135,14 @@ ...@@ -135,14 +135,14 @@
bottom: 0; bottom: 0;
font-size: 80%; font-size: 80%;
} }
input[type=text] { input[type=text] {
padding-right: 4px; padding-right: 4px;
padding-left: 4px; padding-left: 4px;
font-size: 80%; font-size: 80%;
text-align: center; text-align: center;
} }
// input[type=range] { // input[type=range] {
// width: 100%; // width: 100%;
// } // }
...@@ -154,24 +154,24 @@ ...@@ -154,24 +154,24 @@
margin: 0; margin: 0;
text-align: center; text-align: center;
padding-right: $control-spacing; padding-right: $control-spacing;
padding-left: $control-spacing; padding-left: $control-spacing;
&:hover { &:hover {
border-color: color-increase-contrast($msp-form-control-background, 5%) !important; border-color: color-increase-contrast($msp-form-control-background, 5%) !important;
border: none; border: none;
outline-offset: -1px !important; outline-offset: -1px !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important; outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
} }
} }
.msp-color-picker { .msp-color-picker {
position: absolute; position: absolute;
z-index: 100000; z-index: 100000;
background: $default-background; background: $default-background;
border-top: 1px solid $default-background; border-top: 1px solid $default-background;
padding-bottom: $control-spacing / 2; padding-bottom: $control-spacing / 2;
width: 100%; width: 100%;
// input[type=text] { // input[type=text] {
// background: $msp-form-control-background !important; // background: $msp-form-control-background !important;
// } // }
...@@ -217,7 +217,7 @@ ...@@ -217,7 +217,7 @@
height: 2 * $row-height / 3 !important; height: 2 * $row-height / 3 !important;
line-height: 2 * $row-height / 3 !important; line-height: 2 * $row-height / 3 !important;
font-size: 70% !important; font-size: 70% !important;
background: $default-background !important; background: $default-background !important;
color: color-lower-contrast($font-color, 15%) !important; color: color-lower-contrast($font-color, 15%) !important;
} }
} }
...@@ -231,13 +231,13 @@ ...@@ -231,13 +231,13 @@
.msp-control-subgroup { .msp-control-subgroup {
margin-top: 1px; margin-top: 1px;
.msp-control-row { .msp-control-row {
margin-left: $control-spacing !important; margin-left: $control-spacing !important;
> span { > span {
width: $control-label-width !important; width: $control-label-width !important;
} }
> div:nth-child(2) { > div:nth-child(2) {
left: $control-label-width !important; left: $control-label-width !important;
} }
...@@ -254,7 +254,7 @@ ...@@ -254,7 +254,7 @@
width: $control-label-width + $control-spacing; width: $control-label-width + $control-spacing;
text-align: left; text-align: left;
background: transparent; background: transparent;
.msp-icon { .msp-icon {
line-height: $row-height - 3; line-height: $row-height - 3;
width: $row-height - 1; width: $row-height - 1;
......
...@@ -4,19 +4,26 @@ ...@@ -4,19 +4,26 @@
top: 0; top: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
overflow-y: scroll;
overflow-x: hidden;
font-size: 90%; font-size: 90%;
background: $sequence-background; background: $sequence-background;
} }
.msp-sequence-entity { .msp-sequence-select {
float: left;
width: $sequence-select-width;
}
.msp-sequence-wrapper {
word-break: break-word; word-break: break-word;
padding: $info-vertical-padding $control-spacing $info-vertical-padding $control-spacing; padding: $info-vertical-padding $control-spacing $info-vertical-padding $control-spacing;
user-select: none; user-select: none;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
font-size: 90%;
} }
.msp-sequence-entity { .msp-sequence-wrapper {
span { span {
cursor: pointer; cursor: pointer;
} }
......
...@@ -78,4 +78,5 @@ $entity-color-partialy-visible: color-lower-contrast($font-color, 33%); ...@@ -78,4 +78,5 @@ $entity-color-partialy-visible: color-lower-contrast($font-color, 33%);
$entity-tag-color: color-lower-contrast($font-color, 20%); $entity-tag-color: color-lower-contrast($font-color, 20%);
// sequence // sequence
$sequence-background: $default-background; $sequence-background: $default-background;
\ No newline at end of file $sequence-select-width: 300px;
\ No newline at end of file
...@@ -18,92 +18,183 @@ import { MarkerAction } from '../../mol-util/marker-action'; ...@@ -18,92 +18,183 @@ import { MarkerAction } from '../../mol-util/marker-action';
import { ParameterControls } from './controls/parameters'; import { ParameterControls } from './controls/parameters';
import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { ParamDefinition as PD } from '../../mol-util/param-definition';
function getSequenceWrapperForStructure(index: number, structure: Structure, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined { function opKey(ids: string[]) {
let j = 0 return ids.sort().join(',')
}
function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined {
const { structure, entity, chain, operator } = state
const l = StructureElement.create()
for (let i = 0, il = structure.units.length; i < il; ++i) { for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i] const unit = structure.units[i]
if (unit.polymerElements.length === 0) continue if (unit.polymerElements.length === 0) continue
if (j === index) {
const sw = new PolymerSequenceWrapper({ structure, unit }) StructureElement.set(l, unit, unit.elements[0])
sw.markResidue(structureSelection.get(structure), MarkerAction.Select) if (SP.entity.id(l) !== entity) continue
return sw if (SP.chain.label_asym_id(l) !== chain) continue
} if (opKey(SP.unit.pdbx_struct_oper_list_ids(l)) !== operator) continue
j += 1
// console.log('new PolymerSequenceWrapper', structureSelection.get(structure))
const sw = new PolymerSequenceWrapper({ structure, unit })
sw.markResidue(structureSelection.get(structure), MarkerAction.Select)
return sw
} }
} }
function getPolymerOptionsForStructure(structure: Structure) { function getEntityOptions(structure: Structure) {
const options: [number, string][] = [] const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return
StructureElement.set(l, unit, unit.elements[0])
const id = SP.entity.id(l)
if (seen.has(id)) return
const label = `${id}: ${SP.entity.pdbx_description(l).join(', ')}`
options.push([ id, label ])
seen.add(id)
})
if (options.length === 0) options.push(['', 'No entities'])
return options
}
function getChainOptions(structure: Structure, entityId: string) {
const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
let i = 0
structure.units.forEach(unit => { structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return if (unit.polymerElements.length === 0) return
const l = StructureElement.create(unit, unit.elements[0]) StructureElement.set(l, unit, unit.elements[0])
const entityDescription = SP.entity.pdbx_description(l) if (SP.entity.id(l) !== entityId) return
const label_asym_id = SP.chain.label_asym_id(l)
const label = `${label_asym_id}: ${entityDescription}` const id = SP.chain.label_asym_id(l)
if (seen.has(id)) return
options.push([ i, label ]) const label = `${id}: ${SP.chain.auth_asym_id(l)}`
i += 1 options.push([ id, label ])
seen.add(id)
}) })
if (options.length === 0) options.push(['', 'No chains'])
return options return options
} }
export class SequenceView extends PluginUIComponent<{ }, { polymer: number }> { function getOperatorOptions(structure: Structure, entityId: string, label_asym_id: string) {
const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return
StructureElement.set(l, unit, unit.elements[0])
if (SP.entity.id(l) !== entityId) return
if (SP.chain.label_asym_id(l) !== label_asym_id) return
const id = opKey(SP.unit.pdbx_struct_oper_list_ids(l))
if (seen.has(id)) return
const label = `${SP.unit.pdbx_struct_oper_list_ids(l).join(', ')}`
options.push([ id, label ])
seen.add(id)
})
if (options.length === 0) options.push(['', 'No operators'])
return options
}
type SequenceViewState = { structure: Structure, entity: string, chain: string, operator: string }
export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
private spine: StateTreeSpine.Impl private spine: StateTreeSpine.Impl
state = { polymer: 0 } state = { structure: Structure.Empty, entity: '', chain: '', operator: '' }
componentDidMount() { constructor(props: {}, context?: any) {
super(props, context);
this.spine = new StateTreeSpine.Impl(this.plugin.state.dataState.cells); this.spine = new StateTreeSpine.Impl(this.plugin.state.dataState.cells);
}
componentDidMount() {
this.subscribe(this.plugin.state.behavior.currentObject, o => { this.subscribe(this.plugin.state.behavior.currentObject, o => {
const current = this.plugin.state.dataState.cells.get(o.ref)!; const current = this.plugin.state.dataState.cells.get(o.ref)!;
this.spine.current = current this.spine.current = current
this.forceUpdate(); this.setState(this.getInitialState())
}); });
this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => { this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
const current = this.spine.current; const current = this.spine.current;
if (!current || current.sourceRef !== ref || current.state !== state) return; if (!current || current.sourceRef !== ref || current.state !== state) return;
this.forceUpdate(); this.setState(this.getInitialState())
}); });
} }
private getStructure() { private getStructure() {
const so = this.spine && this.spine.getRootOfType(SO.Molecule.Structure) const so = this.spine.getRootOfType(SO.Molecule.Structure)
return so && so.data return (so && so.data) || Structure.Empty
}
private getSequenceWrapper() {
return getSequenceWrapper(this.state, this.plugin.helpers.structureSelection)
}
private getInitialState(): SequenceViewState {
const structure = this.getStructure()
const entity = getEntityOptions(structure)[0][0]
const chain = getChainOptions(structure, entity)[0][0]
const operator = getOperatorOptions(structure, entity, chain)[0][0]
return { structure, entity, chain, operator }
} }
private getParams(structure: Structure) { private get params() {
const { structure, entity, chain } = this.state
const entityOptions = getEntityOptions(structure)
const chainOptions = getChainOptions(structure, entity)
const operatorOptions = getOperatorOptions(structure, entity, chain)
return { return {
polymer: PD.Select(0, getPolymerOptionsForStructure(structure)) entity: PD.Select(entityOptions[0][0], entityOptions),
chain: PD.Select(chainOptions[0][0], chainOptions),
operator: PD.Select(operatorOptions[0][0], operatorOptions)
} }
} }
private setParamProps = (p: { param: PD.Base<any>, name: string, value: any }) => { private setParamProps = (p: { param: PD.Base<any>, name: string, value: any }) => {
if (p.name === 'polymer') { const state = { ...this.state }
this.setState({ polymer: p.value }) switch (p.name) {
case 'entity':
state.entity = p.value
state.chain = getChainOptions(state.structure, state.entity)[0][0]
state.operator = getOperatorOptions(state.structure, state.entity, state.chain)[0][0]
break
case 'chain':
state.chain = p.value
state.operator = getOperatorOptions(state.structure, state.entity, state.chain)[0][0]
break
case 'operator':
state.operator = p.value
break
} }
this.setState(state)
} }
render() { render() {
const structure = this.getStructure(); if (this.state.structure === Structure.Empty) return <div className='msp-sequence'>
if (!structure) return <div className='msp-sequence'> <div className='msp-sequence-wrapper'>No structure available</div>
<div className='msp-sequence-entity'>No structure available</div>
</div>; </div>;
const { structureSelection } = this.plugin.helpers const sequenceWrapper = this.getSequenceWrapper()
const params = this.getParams(structure)
const sequenceWrapper = getSequenceWrapperForStructure(this.state.polymer, structure, structureSelection)
return <div className='msp-sequence'> return <div className='msp-sequence'>
<ParameterControls params={params} values={this.state} onChange={this.setParamProps} /> <div className='msp-sequence-select'>
<ParameterControls params={this.params} values={this.state} onChange={this.setParamProps} />
</div>
{sequenceWrapper !== undefined {sequenceWrapper !== undefined
? <Sequence sequenceWrapper={sequenceWrapper} /> ? <Sequence sequenceWrapper={sequenceWrapper} />
: <div className='msp-sequence-entity'>No sequence available</div>} : <div className='msp-sequence-wrapper'>No sequence available</div>}
</div>; </div>;
} }
} }
\ No newline at end of file
...@@ -19,11 +19,11 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -19,11 +19,11 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
eachResidue(loci: Loci, apply: (interval: Interval) => boolean) { eachResidue(loci: Loci, apply: (interval: Interval) => boolean) {
let changed = false let changed = false
const { structure } = this.data const { structure, unit } = this.data
if (!StructureElement.isLoci(loci)) return false if (!StructureElement.isLoci(loci)) return false
if (!Structure.areParentsEquivalent(loci.structure, structure)) return false if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
const { location, entityId, label_asym_id } = this const { location, label_asym_id } = this
for (const e of loci.elements) { for (const e of loci.elements) {
let rIprev = -1 let rIprev = -1
location.unit = e.unit location.unit = e.unit
...@@ -35,7 +35,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -35,7 +35,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
const rI = residueIndex[location.element] const rI = residueIndex[location.element]
// avoid checking for the same residue multiple times // avoid checking for the same residue multiple times
if (rI !== rIprev) { if (rI !== rIprev) {
if (SP.entity.id(location) !== entityId) return if (SP.unit.id(location) !== unit.id) return
if (SP.chain.label_asym_id(location) !== label_asym_id) return if (SP.chain.label_asym_id(location) !== label_asym_id) return
if (apply(getSeqIdInterval(location))) changed = true if (apply(getSeqIdInterval(location))) changed = true
...@@ -47,7 +47,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -47,7 +47,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
} }
getLoci(seqId: number) { getLoci(seqId: number) {
const query = createResidueQuery(this.entityId, seqId, this.label_asym_id); const query = createResidueQuery(this.data.unit.id, seqId);
return StructureSelection.toLoci2(StructureQuery.run(query, this.data.structure)); return StructureSelection.toLoci2(StructureQuery.run(query, this.data.structure));
} }
...@@ -67,13 +67,10 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -67,13 +67,10 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
} }
} }
function createResidueQuery(entityId: string, label_seq_id: number, label_asym_id: string) { function createResidueQuery(unitId: number, label_seq_id: number) {
return Queries.generators.atoms({ return Queries.generators.atoms({
entityTest: ctx => { unitTest: ctx => {
return SP.entity.id(ctx.element) === entityId return SP.unit.id(ctx.element) === unitId
},
chainTest: ctx => {
return SP.chain.label_asym_id(ctx.element) === label_asym_id
}, },
residueTest: ctx => { residueTest: ctx => {
if (ctx.element.unit.kind === Unit.Kind.Atomic) { if (ctx.element.unit.kind === Unit.Kind.Atomic) {
......
...@@ -96,7 +96,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P, Sequ ...@@ -96,7 +96,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P, Sequ
} }
return <div return <div
className='msp-sequence-entity' className='msp-sequence-wrapper'
onContextMenu={this.contextMenu} onContextMenu={this.contextMenu}
onMouseDown={this.mouseDown} onMouseDown={this.mouseDown}
> >
......
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