Skip to content

Commit

Permalink
feat(IText): expose getCursorRenderingData() function. (#10204)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored Oct 12, 2024
1 parent 5f96c54 commit b00491e
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- feat(IText): expose getCursorRenderingData() function. [#10204](https://github.com/fabricjs/fabric.js/pull/10204)
- fix(Canvas): allowTouchScrolling interactions [#10078](https://github.com/fabricjs/fabric.js/pull/10078)
- update(IText): Add method enterEditingImpl/exitEditingImpl that executes the logic of enterEditing/exitEditing without events [#10187](https://github.com/fabricjs/fabric.js/issues/10187)
- fix(FabricObject): Fix clipPath blurryness with scale [#9774](https://github.com/fabricjs/fabric.js/pull/9774)
Expand Down
2 changes: 2 additions & 0 deletions fabric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export {
export type {
ITextProps,
SerializedITextProps,
CursorRenderingData,
CursorBoundaries,
} from './src/shapes/IText/IText';
export { IText } from './src/shapes/IText/IText';
export type {
Expand Down
76 changes: 52 additions & 24 deletions src/shapes/IText/IText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@ import {
import { CENTER, FILL, LEFT, RIGHT } from '../../constants';
import type { ObjectToCanvasElementOptions } from '../Object/Object';

type CursorBoundaries = {
export type CursorBoundaries = {
left: number;
top: number;
leftOffset: number;
topOffset: number;
};

export type CursorRenderingData = {
color: string;
opacity: number;
left: number;
top: number;
width: number;
height: number;
};

// Declare IText protected properties to workaround TS
const protectedDefaultValues = {
_selectionDirection: null,
Expand Down Expand Up @@ -380,7 +389,7 @@ export class IText<
return;
}
const boundaries = this._getCursorBoundaries();
if (this.selectionStart === this.selectionEnd) {
if (this.selectionStart === this.selectionEnd && !this.inCompositionMode) {
this.renderCursor(ctx, boundaries);
} else {
this.renderSelection(ctx, boundaries);
Expand Down Expand Up @@ -482,8 +491,11 @@ export class IText<
* If contextTop is not available, do nothing.
*/
renderCursorAt(selectionStart: number) {
const boundaries = this._getCursorBoundaries(selectionStart, true);
this._renderCursor(this.canvas!.contextTop, boundaries, selectionStart);
this._renderCursor(
this.canvas!.contextTop,
this._getCursorBoundaries(selectionStart, true),
selectionStart,
);
}

/**
Expand All @@ -495,11 +507,16 @@ export class IText<
this._renderCursor(ctx, boundaries, this.selectionStart);
}

_renderCursor(
ctx: CanvasRenderingContext2D,
boundaries: CursorBoundaries,
selectionStart: number,
) {
/**
* Return the data needed to render the cursor for given selection start
* The left,top are relative to the object, while width and height are prescaled
* to look think with canvas zoom and object scaling,
* so they depend on canvas and object scaling
*/
getCursorRenderingData(
selectionStart: number = this.selectionStart,
boundaries: CursorBoundaries = this._getCursorBoundaries(selectionStart),
): CursorRenderingData {
const cursorLocation = this.get2DCursorLocation(selectionStart),
lineIndex = cursorLocation.lineIndex,
charIndex =
Expand All @@ -514,21 +531,32 @@ export class IText<
this.lineHeight -
charHeight * (1 - this._fontSizeFraction);

if (this.inCompositionMode) {
// TODO: investigate why there isn't a return inside the if,
// and why can't happen at the top of the function
this.renderSelection(ctx, boundaries);
}
ctx.fillStyle =
this.cursorColor ||
(this.getValueOfPropertyAt(lineIndex, charIndex, FILL) as string);
ctx.globalAlpha = this._currentCursorOpacity;
ctx.fillRect(
boundaries.left + boundaries.leftOffset - cursorWidth / 2,
topOffset + boundaries.top + dy,
cursorWidth,
charHeight,
);
return {
color:
this.cursorColor ||
(this.getValueOfPropertyAt(lineIndex, charIndex, 'fill') as string),
opacity: this._currentCursorOpacity,
left: boundaries.left + boundaries.leftOffset - cursorWidth / 2,
top: topOffset + boundaries.top + dy,
width: cursorWidth,
height: charHeight,
};
}

/**
* Render the cursor at the given selectionStart.
*
*/
_renderCursor(
ctx: CanvasRenderingContext2D,
boundaries: CursorBoundaries,
selectionStart: number,
) {
const { color, opacity, left, top, width, height } =
this.getCursorRenderingData(selectionStart, boundaries);
ctx.fillStyle = color;
ctx.globalAlpha = opacity;
ctx.fillRect(left, top, width, height);
}

/**
Expand Down

0 comments on commit b00491e

Please sign in to comment.