Skip to content

Commit

Permalink
The selection and cursor rendering of IText objects are now clipped t…
Browse files Browse the repository at this point in the history
…o the object's clip path and its ancestors' clip paths.

This change is WIP in attempt to address [the issue](fabricjs#10274) where the selection and cursor were not being clipped correctly when the object was clipped.

A `applyClipPathToCtx` method has been added to apply the clip paths to the rendering context. A `findClipPathAncestors` method has been added to find the clip path ancestors of the object.

The `renderCursorOrSelection` method has been updated to use the `applyClipPathToCtx` and `findClipPathAncestors` methods to clip the selection and cursor rendering.

Currently only partially works — the selection is  positioned and clipped but the rect is black.
  • Loading branch information
RalphFurley committed Jan 21, 2025
1 parent 3b2742e commit 95bcfab
Showing 1 changed file with 49 additions and 2 deletions.
51 changes: 49 additions & 2 deletions src/shapes/IText/IText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,23 +381,70 @@ export class IText<
* it does on the contextTop. If contextTop is not available, do nothing.
*/
renderCursorOrSelection() {
if (!this.isEditing) {
if (!this.isEditing || !this.canvas) {
return;
}
const ctx = this.clearContextTop(true);
if (!ctx) {
return;
}
const boundaries = this._getCursorBoundaries();
const clipPaths = this.findClipPathAncestors();

if (clipPaths.length > 0) {
this._applyClipPathToContext(ctx, clipPaths);
}
if (this.selectionStart === this.selectionEnd && !this.inCompositionMode) {
this.renderCursor(ctx, boundaries);
} else {
this.renderSelection(ctx, boundaries);
}
this.canvas!.contextTopDirty = true;

if (clipPaths.length > 0) {
ctx.restore();
}

this.canvas.contextTopDirty = true;
ctx.restore();
}

/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {FabricObject[]} clipPathAncestors clipPaths
*/
private _applyClipPathToContext(
ctx: CanvasRenderingContext2D,
clipPathAncestors: FabricObject[],
) {
ctx.save();
for (const clipPath of clipPathAncestors) {
if (clipPath.absolutePositioned) {
const m = clipPath.calcTransformMatrix();
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
const { width, height } = this;
if (clipPath && !clipPath.isNotVisible()) {
(clipPath as any).drawObject(ctx, true);
ctx.clip();
}
}
}

/**
* finds if the current instance has parent groups that have clip paths applied.
*/
findClipPathAncestors() {
const clipPathAncestors: FabricObject[] = [];
let parent: FabricObject | undefined = this;
while (parent && (parent = parent.parent)) {
if (parent.clipPath && !(parent as any).clipPath.absolutePositioned) {
clipPathAncestors.push(parent.clipPath);
}
}
return clipPathAncestors;
}

/**
* Returns cursor boundaries (left, top, leftOffset, topOffset)
* left/top are left/top of entire text box
Expand Down

0 comments on commit 95bcfab

Please sign in to comment.