Skip to content

Commit

Permalink
#4888 - Enable editing sequences by click between symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
rrodionov91 committed Jul 23, 2024
1 parent 21691ea commit 0674d32
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 25 deletions.
7 changes: 4 additions & 3 deletions packages/ketcher-core/src/application/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,10 @@ export class CoreEditor {
this.events.selectHistory.add((name) => this.onSelectHistory(name));

renderersEvents.forEach((eventName) => {
this.events[eventName].add((event) =>
this.useToolIfNeeded(eventName, event),
);
this.events[eventName].add((event) => {
this.useModeIfNeeded(eventName, event);
this.useToolIfNeeded(eventName, event);
});
});
this.events.editSequence.add(
(sequenceItemRenderer: BaseSequenceItemRenderer) =>
Expand Down
8 changes: 8 additions & 0 deletions packages/ketcher-core/src/application/editor/editorEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export function resetEditorEvents() {
changeSequenceTypeEnterMode: new Subscription(),
toggleSequenceEditMode: new Subscription(),
toggleSequenceEditInRNABuilderMode: new Subscription(),
clickOnSequenceItem: new Subscription(),
mousedownBetweenSequenceItems: new Subscription(),
mouseDownOnSequenceItem: new Subscription(),
doubleClickOnSequenceItem: new Subscription(),
};
}
resetEditorEvents();
Expand Down Expand Up @@ -72,6 +76,10 @@ export const renderersEvents: ToolEventHandlerName[] = [
'changeSequenceTypeEnterMode',
'toggleSequenceEditMode',
'toggleSequenceEditInRNABuilderMode',
'clickOnSequenceItem',
'mousedownBetweenSequenceItems',
'mouseDownOnSequenceItem',
'doubleClickOnSequenceItem',
];

export const hotkeysConfiguration = {
Expand Down
58 changes: 54 additions & 4 deletions packages/ketcher-core/src/application/editor/modes/SequenceMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class SequenceMode extends BaseMode {
private _isEditInRNABuilderMode = false;
private selectionStarted = false;
private selectionStartCaretPosition = -1;
private mousemoveCounter = 0;

constructor(previousMode?: LayoutMode) {
super('sequence-layout-mode', previousMode);
Expand Down Expand Up @@ -101,16 +102,18 @@ export class SequenceMode extends BaseMode {
return modelChanges;
}

public turnOnEditMode(sequenceItemRenderer?: BaseSequenceItemRenderer) {
public turnOnEditMode(
sequenceItemRenderer?: BaseSequenceItemRenderer,
needToRemoveSelection = true,
) {
const editor = CoreEditor.provideEditorInstance();

this.isEditMode = true;
this.initialize(false);
this.initialize(false, needToRemoveSelection);
if (sequenceItemRenderer) {
SequenceRenderer.setCaretPositionByMonomer(
sequenceItemRenderer.node.monomer,
);
SequenceRenderer.moveCaretForward();
}
editor.events.toggleSequenceEditMode.dispatch(true);
}
Expand Down Expand Up @@ -277,13 +280,48 @@ export class SequenceMode extends BaseMode {
}
}

public doubleClickOnSequenceItem(event: MouseEvent) {
if (this.isEditInRNABuilderMode) {
return;
}

const eventData = event.target?.__data__ as BaseSequenceItemRenderer;

this.turnOnEditMode(eventData, false);
}

public mousedownBetweenSequenceItems(event: MouseEvent) {
if (this.isEditInRNABuilderMode) {
return;
}

event.stopPropagation();

const eventData = event.target?.__data__ as BaseSequenceItemRenderer;

this.turnOnEditMode(eventData);
SequenceRenderer.moveCaretForward();
}

public mousedown(event: MouseEvent) {
const eventData = event.target?.__data__;
const isEventOnSequenceItem = eventData instanceof BaseSequenceItemRenderer;

if (this.isEditMode && isEventOnSequenceItem && !event.shiftKey) {
const sequenceItemBoundingBox = eventData.rootBoundingClientRect;
const isRightSideOfSequenceItemClicked = sequenceItemBoundingBox
? event.clientX >
sequenceItemBoundingBox.x + sequenceItemBoundingBox.width / 2
: false;

SequenceRenderer.setCaretPositionBySequenceItemRenderer(
eventData as BaseSequenceItemRenderer,
);

if (isRightSideOfSequenceItemClicked) {
SequenceRenderer.moveCaretForward();
}

this.unselectAllEntities();
this.selectionStarted = true;
this.selectionStartCaretPosition = SequenceRenderer.caretPosition;
Expand All @@ -293,7 +331,14 @@ export class SequenceMode extends BaseMode {
public mousemove(event: MouseEvent) {
const eventData = event.target?.__data__;
const isEventOnSequenceItem = eventData instanceof BaseSequenceItemRenderer;
if (this.isEditMode && isEventOnSequenceItem && this.selectionStarted) {
// this.mousemoveCounter > 1 used here to prevent selection of single monomer
// when user just clicked on it during the mousemove event
if (
this.isEditMode &&
isEventOnSequenceItem &&
this.selectionStarted &&
this.mousemoveCounter > 1
) {
const editor = CoreEditor.provideEditorInstance();
SequenceRenderer.setCaretPositionBySequenceItemRenderer(
eventData as BaseSequenceItemRenderer,
Expand Down Expand Up @@ -321,6 +366,10 @@ export class SequenceMode extends BaseMode {
modelChanges.addOperation(moveCaretOperation);
editor.renderersContainer.update(modelChanges);
}

if (this.selectionStarted) {
this.mousemoveCounter++;
}
}

mouseup() {
Expand All @@ -331,6 +380,7 @@ export class SequenceMode extends BaseMode {
if (this.isEditMode) {
SequenceRenderer.resetLastUserDefinedCaretPosition();
}
this.mousemoveCounter = 0;
}

private bondNodesThroughNewPhosphate(
Expand Down
8 changes: 8 additions & 0 deletions packages/ketcher-core/src/application/editor/tools/Tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ interface ToolEventHandler {
toggleSequenceEditMode?(event: Event): void;

toggleSequenceEditInRNABuilderMode?(event: Event): void;

clickOnSequenceItem?(event: Event): void;

mousedownBetweenSequenceItems?(event: Event): void;

mouseDownOnSequenceItem?(event: Event): void;

doubleClickOnSequenceItem?(event: Event): void;
}

export interface IRnaPreset {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export abstract class BaseRenderer implements IBaseRenderer {
return rootNode.getBBox();
}

public get rootBoundingClientRect() {
const rootNode = this.rootElement?.node();
if (!rootNode) return;

return rootNode.getBoundingClientRect();
}

public get width() {
return this.rootBBox?.width || 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
public textElement?: D3SvgElementSelection<SVGTextElement, void>;
public counterElement?: D3SvgElementSelection<SVGTextElement, void>;
private selectionRectangle?: D3SvgElementSelection<SVGRectElement, void>;
private selectionBorder?: D3SvgElementSelection<SVGUseElement, void>;
public spacerElement?: D3SvgElementSelection<SVGGElement, void>;
public backgroundElement?: D3SvgElementSelection<SVGRectElement, void>;

Expand Down Expand Up @@ -99,12 +98,9 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
.attr('y', -16)
.attr('x', -2)
.attr('rx', 2)
.attr('cursor', 'text');
.attr('cursor', this.isSequenceEditModeTurnedOn ? 'text' : 'default');

backgroundElement?.attr(
'fill',
this.isSequenceEditModeTurnedOn ? '#FF7A001A' : 'transparent',
);
backgroundElement?.attr('fill', 'transparent');

return backgroundElement;
}
Expand All @@ -118,7 +114,10 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
?.append('rect')
.attr('width', 4)
.attr('height', 20)
.attr('cursor', 'text')
.attr(
'cursor',
this.isSequenceEditInRnaBuilderModeTurnedOn ? 'default' : 'text',
)
.attr('fill', 'transparent');

return spacerGroupElement;
Expand All @@ -143,6 +142,7 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
.attr('font-family', 'Courier New')
.attr('font-size', '12px')
.attr('font-weight', '700')
.attr('style', 'user-select: none')
.attr('fill', '#7C7C7F');
}

Expand All @@ -161,9 +161,9 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
public appendCaretElement() {
this.spacerElement
?.append('line')
.attr('x1', -16)
.attr('x1', -17)
.attr('y1', -1)
.attr('x2', -16)
.attr('x2', -17)
.attr('y2', 21)
.attr('stroke', '#333')
.attr('class', 'blinking');
Expand Down Expand Up @@ -209,7 +209,9 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
.attr(
'fill',
this.isSequenceEditInRnaBuilderModeTurnedOn ? '24545A' : '#333333',
);
)
.attr('style', 'user-select: none;')
.attr('cursor', this.isSequenceEditModeTurnedOn ? 'text' : 'default');

this.appendEvents();
if (this.needDisplayCounter) {
Expand Down Expand Up @@ -247,12 +249,6 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
this.selectionRectangle ||
this.rootElement?.insert('rect', ':first-child');

this.selectionBorder = this.rootElement
?.append('use')
.attr('href', this.monomerIndexInChain)
.attr('stroke', '#57FF8F')
.attr('pointer-events', 'none');

if (this.isSequenceEditInRnaBuilderModeTurnedOn) {
this.selectionRectangle
?.attr('fill', '#99D6DC')
Expand All @@ -271,13 +267,12 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
.attr('height', 20)
.attr('class', 'dynamic-element');
}
this.backgroundElement?.attr('fill', 'none');
}

public removeSelection() {
this.selectionRectangle?.remove();
this.selectionBorder?.remove();
this.selectionRectangle = undefined;
this.selectionBorder = undefined;
}

private raiseElement() {
Expand All @@ -304,13 +299,46 @@ export abstract class BaseSequenceItemRenderer extends BaseSequenceRenderer {
assert(this.textElement);

this.textElement.on('mouseover', (event) => {
if (!this.isSequenceEditModeTurnedOn) {
this.backgroundElement?.attr(
'fill',
this.node.monomer.selected ? 'none' : '#E1E8E9',
);
}
this.editorEvents.mouseOverSequenceItem.dispatch(event);
});
this.textElement.on('mousemove', (event) => {
this.editorEvents.mouseOnMoveSequenceItem.dispatch(event);
});
this.textElement.on('mouseleave', (event) => {
this.backgroundElement?.attr('fill', 'none');
this.editorEvents.mouseLeaveSequenceItem.dispatch(event);
});
this.spacerElement?.on('mousedown', (event) => {
this.editorEvents.mousedownBetweenSequenceItems.dispatch(event);
});
this.backgroundElement?.on('click', (event) => {
this.editorEvents.clickOnSequenceItem.dispatch(event);
});
this.backgroundElement?.on('mousedown', (event) => {
this.editorEvents.mouseDownOnSequenceItem.dispatch(event);
});
this.backgroundElement?.on('dblclick', (event) => {
this.editorEvents.doubleClickOnSequenceItem.dispatch(event);
});
this.textElement.on('dblclick', (event) => {
this.editorEvents.doubleClickOnSequenceItem.dispatch(event);
});
this.backgroundElement?.on('mouseover', () => {
if (!this.isSequenceEditModeTurnedOn) {
this.backgroundElement?.attr(
'fill',
this.node.monomer.selected ? 'none' : '#E1E8E9',
);
}
});
this.backgroundElement?.on('mouseleave', () => {
this.backgroundElement?.attr('fill', 'none');
});
}
}

0 comments on commit 0674d32

Please sign in to comment.