From 91e95167f7121dbdcfe1d723572af9056e0e19fc Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 12 Mar 2023 00:29:28 +0100 Subject: [PATCH 1/8] no animation default --- .codesandbox/templates/next/pages/index.tsx | 22 ++++----------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/.codesandbox/templates/next/pages/index.tsx b/.codesandbox/templates/next/pages/index.tsx index ddee244372a..9425571ad52 100644 --- a/.codesandbox/templates/next/pages/index.tsx +++ b/.codesandbox/templates/next/pages/index.tsx @@ -1,7 +1,7 @@ import * as fabric from 'fabric'; -import { NextPage } from 'next'; -import { useCallback } from 'react'; -import { Canvas } from '../components/Canvas'; +import {NextPage} from 'next'; +import {useCallback} from 'react'; +import {Canvas} from '../components/Canvas'; const IndexPage: NextPage = () => { const onLoad = useCallback(async (canvas: fabric.Canvas) => { @@ -9,26 +9,12 @@ const IndexPage: NextPage = () => { width: window.innerWidth, height: 500, }); - const text = new fabric.Text('fabric.js sandbox', { + const text = new fabric.Textbox('fabric.js sandbox', { originX: 'center', top: 20, }); canvas.add(text); canvas.centerObjectH(text); - function animate(toState: 0 | 1) { - text.animate( - { scaleX: Math.max(toState, 0.1) * 2 }, - { - onChange: () => canvas.renderAll(), - onComplete: () => animate(Number(!toState) as 0 | 1), - duration: 1000, - easing: toState - ? fabric.util.ease.easeInOutQuad - : fabric.util.ease.easeInOutSine, - } - ); - } - animate(1); }, []); return ( From 24b8beaea55a8b2b64319204a05ae30a91e6ca7a Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 12 Mar 2023 02:20:51 +0100 Subject: [PATCH 2/8] prettier --- .codesandbox/templates/next/pages/index.tsx | 6 +++--- src/shapes/IText/ITextKeyBehavior.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.codesandbox/templates/next/pages/index.tsx b/.codesandbox/templates/next/pages/index.tsx index 9425571ad52..68a874332bd 100644 --- a/.codesandbox/templates/next/pages/index.tsx +++ b/.codesandbox/templates/next/pages/index.tsx @@ -1,7 +1,7 @@ import * as fabric from 'fabric'; -import {NextPage} from 'next'; -import {useCallback} from 'react'; -import {Canvas} from '../components/Canvas'; +import { NextPage } from 'next'; +import { useCallback } from 'react'; +import { Canvas } from '../components/Canvas'; const IndexPage: NextPage = () => { const onLoad = useCallback(async (canvas: fabric.Canvas) => { diff --git a/src/shapes/IText/ITextKeyBehavior.ts b/src/shapes/IText/ITextKeyBehavior.ts index 6cd18de8179..f83e27f4ce5 100644 --- a/src/shapes/IText/ITextKeyBehavior.ts +++ b/src/shapes/IText/ITextKeyBehavior.ts @@ -168,6 +168,7 @@ export abstract class ITextKeyBehavior< * @param {Event} e Event object */ onInput(e: Event) { + this._forceClearCache = true; const fromPaste = this.fromPaste; this.fromPaste = false; e && e.stopPropagation(); From d6da2482f4c1cacbd50b776b9b8124e78c88b0f0 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 12 Mar 2023 02:22:53 +0100 Subject: [PATCH 3/8] correct changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df36c8d7a2f..8682eef9382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [next] +- fix(IText, Textbox): fix broken text input [#8775](https://github.com/fabricjs/fabric.js/pull/8775) - ci(): `.codesandbox` [#8135](https://github.com/fabricjs/fabric.js/pull/8135) - ci(): disallow circular deps [#8759](https://github.com/fabricjs/fabric.js/pull/8759) - fix(): env WebGL import cycle [#8758](https://github.com/fabricjs/fabric.js/pull/8758) From 0e65825a58505444d9717a43c46b125051b1e587 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 13 Mar 2023 00:04:14 +0100 Subject: [PATCH 4/8] add tests --- src/shapes/IText/ITextBehavior.ts | 31 ++++++++++---------- src/shapes/IText/ITextKeyBehavior.ts | 43 +++++++++++++--------------- test/unit/itext_key_behaviour.js | 25 ++++++++++++---- 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/shapes/IText/ITextBehavior.ts b/src/shapes/IText/ITextBehavior.ts index 13dd03a4262..728e02702da 100644 --- a/src/shapes/IText/ITextBehavior.ts +++ b/src/shapes/IText/ITextBehavior.ts @@ -1,16 +1,16 @@ -import { getDocument } from '../../env'; +import {getDocument} from '../../env'; import { ObjectEvents, TPointerEvent, TPointerEventInfo, } from '../../EventTypeDefs'; -import { Point } from '../../Point'; -import type { FabricObject } from '../Object/Object'; -import { Text } from '../Text/Text'; -import { animate } from '../../util/animation/animate'; -import { TOnAnimationChangeCallback } from '../../util/animation/types'; -import type { ValueAnimation } from '../../util/animation/ValueAnimation'; -import { TextStyleDeclaration } from '../Text/StyledText'; +import {Point} from '../../Point'; +import type {FabricObject} from '../Object/Object'; +import {Text} from '../Text/Text'; +import {animate} from '../../util/animation/animate'; +import {TOnAnimationChangeCallback} from '../../util/animation/types'; +import type {ValueAnimation} from '../../util/animation/ValueAnimation'; +import {TextStyleDeclaration} from '../Text/StyledText'; /** * extend this regex to support non english languages @@ -511,15 +511,14 @@ export abstract class ITextBehavior< return; } this.cursorOffsetCache = {}; - this.text = this.hiddenTextarea.value; - if (this._shouldClearDimensionCache()) { - this.initDimensions(); - this.setCoords(); - } + const textarea = this.hiddenTextarea; + this.text = textarea.value; + this.initDimensions(); + this.setCoords(); const newSelection = this.fromStringToGraphemeSelection( - this.hiddenTextarea.selectionStart, - this.hiddenTextarea.selectionEnd, - this.hiddenTextarea.value + textarea.selectionStart, + textarea.selectionEnd, + textarea.value ); this.selectionEnd = this.selectionStart = newSelection.selectionEnd; if (!this.inCompositionMode) { diff --git a/src/shapes/IText/ITextKeyBehavior.ts b/src/shapes/IText/ITextKeyBehavior.ts index f83e27f4ce5..b96a7727f73 100644 --- a/src/shapes/IText/ITextKeyBehavior.ts +++ b/src/shapes/IText/ITextKeyBehavior.ts @@ -1,11 +1,11 @@ //@ts-nocheck -import { config } from '../../config'; -import { getDocument, getEnv } from '../../env'; -import { TPointerEvent } from '../../EventTypeDefs'; -import { capValue } from '../../util/misc/capValue'; -import { ITextBehavior, ITextEvents } from './ITextBehavior'; -import type { TKeyMapIText } from './constants'; +import {config} from '../../config'; +import {getDocument,getEnv} from '../../env'; +import {TPointerEvent} from '../../EventTypeDefs'; +import {capValue} from '../../util/misc/capValue'; +import {ITextBehavior,ITextEvents} from './ITextBehavior'; +import type {TKeyMapIText} from './constants'; export abstract class ITextKeyBehavior< EventSpec extends ITextEvents = ITextEvents @@ -168,13 +168,25 @@ export abstract class ITextKeyBehavior< * @param {Event} e Event object */ onInput(e: Event) { - this._forceClearCache = true; const fromPaste = this.fromPaste; this.fromPaste = false; e && e.stopPropagation(); if (!this.isEditing) { return; } + const updateAndFire = () => { + this.updateFromTextArea(); + this.fire('changed'); + if (this.canvas) { + this.canvas.fire('text:changed', { target: this }); + this.canvas.requestRenderAll(); + } + } + if (this.hiddenTextarea.value === '') { + this.styles = {}; + updateAndFire(); + return; + } // decisions about style changes. const nextText = this._splitTextIntoLines( this.hiddenTextarea.value @@ -189,16 +201,6 @@ export abstract class ITextKeyBehavior< charDiff = nextCharCount - charCount, removeFrom, removeTo; - if (this.hiddenTextarea.value === '') { - this.styles = {}; - this.updateFromTextArea(); - this.fire('changed'); - if (this.canvas) { - this.canvas.fire('text:changed', { target: this }); - this.canvas.requestRenderAll(); - } - return; - } const textareaSelection = this.fromStringToGraphemeSelection( this.hiddenTextarea.selectionStart, @@ -266,12 +268,7 @@ export abstract class ITextKeyBehavior< } this.insertNewStyleBlock(insertedText, selectionStart, copiedStyle); } - this.updateFromTextArea(); - this.fire('changed'); - if (this.canvas) { - this.canvas.fire('text:changed', { target: this }); - this.canvas.requestRenderAll(); - } + updateAndFire(); } /** diff --git a/test/unit/itext_key_behaviour.js b/test/unit/itext_key_behaviour.js index c597a01f8b6..74fd02b49de 100644 --- a/test/unit/itext_key_behaviour.js +++ b/test/unit/itext_key_behaviour.js @@ -25,27 +25,42 @@ QUnit.module('selection changes', function (hooks) { let iText, selection = 0, _assertCursorAnimation, _setSelection; - hooks.before(() => { + hooks.beforeEach(() => { iText = new fabric.IText('test need some word\nsecond line'); iText.ctx = ctx; iText.on('selection:changed', () => selection++); _assertCursorAnimation = assertCursorAnimation.bind(null, QUnit.assert, iText); _setSelection = setSelection.bind(null, QUnit.assert, iText); + selection = 0; }); hooks.after(() => { // needed or test hangs iText.dispose(); }); - hooks.beforeEach(() => { - selection = 0; - }); - QUnit.test('enterEditing', async function (assert) { iText.enterEditing(); await _assertCursorAnimation(true); assert.equal(selection, 1, 'will fire on enter edit since the cursor is changing for the first time'); }); + QUnit.test('enterEditing and onInput', function (assert) { + iText.enterEditing(); + assert.equal(iText.text.includes('__UNIQUE_TEXT_'), false, 'does not contain __UNIQUE_TEXT_'); + const event = new (fabric.getWindow().InputEvent)("input", { inputType: "insertText", data: '__UNIQUE_TEXT_', composed: true }); + // manually crafted events have `isTrusted` as false so they won't interact with the html element + iText.hiddenTextarea.value = `__UNIQUE_TEXT_${iText.hiddenTextarea.value}`; + iText.hiddenTextarea.dispatchEvent(event); + assert.equal(iText.text.includes('__UNIQUE_TEXT_'), true, 'does contain __UNIQUE_TEXT_'); + }); + + QUnit.test('updateFromTextArea calls setDimensions', function (assert) { + iText.enterEditing(); + assert.equal(iText.width < 400, true, 'iText is less than 400px') + iText.hiddenTextarea.value = `more more more more text ${iText.hiddenTextarea.value}`; + iText.updateFromTextArea(); + assert.equal(iText.width > 700, true, 'iText is now more than 700px') + }); + QUnit.test('selectAll', async function (assert) { const done = assert.async(); iText.selectAll(); From 0c6f8d59d60a9a2a5c72152f837122c891292180 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 13 Mar 2023 00:08:28 +0100 Subject: [PATCH 5/8] vscode wont every work for me --- .vscode/settings.json | 4 +++- src/shapes/IText/ITextBehavior.ts | 16 ++++++++-------- src/shapes/IText/ITextKeyBehavior.ts | 14 +++++++------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d21dddc7466..a5633b2a249 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,5 +14,7 @@ "editor.codeActionsOnSave": { "source.organizeImports": false, "source.removeUnusedImports": true - } + }, + "prettier.configPath": ".prettierrc", + "prettier.bracketSpacing": true } diff --git a/src/shapes/IText/ITextBehavior.ts b/src/shapes/IText/ITextBehavior.ts index 728e02702da..78005f0cc9b 100644 --- a/src/shapes/IText/ITextBehavior.ts +++ b/src/shapes/IText/ITextBehavior.ts @@ -1,16 +1,16 @@ -import {getDocument} from '../../env'; +import { getDocument } from '../../env'; import { ObjectEvents, TPointerEvent, TPointerEventInfo, } from '../../EventTypeDefs'; -import {Point} from '../../Point'; -import type {FabricObject} from '../Object/Object'; -import {Text} from '../Text/Text'; -import {animate} from '../../util/animation/animate'; -import {TOnAnimationChangeCallback} from '../../util/animation/types'; -import type {ValueAnimation} from '../../util/animation/ValueAnimation'; -import {TextStyleDeclaration} from '../Text/StyledText'; +import { Point } from '../../Point'; +import type { FabricObject } from '../Object/Object'; +import { Text } from '../Text/Text'; +import { animate } from '../../util/animation/animate'; +import { TOnAnimationChangeCallback } from '../../util/animation/types'; +import type { ValueAnimation } from '../../util/animation/ValueAnimation'; +import { TextStyleDeclaration } from '../Text/StyledText'; /** * extend this regex to support non english languages diff --git a/src/shapes/IText/ITextKeyBehavior.ts b/src/shapes/IText/ITextKeyBehavior.ts index b96a7727f73..973aae571a8 100644 --- a/src/shapes/IText/ITextKeyBehavior.ts +++ b/src/shapes/IText/ITextKeyBehavior.ts @@ -1,11 +1,11 @@ //@ts-nocheck -import {config} from '../../config'; -import {getDocument,getEnv} from '../../env'; -import {TPointerEvent} from '../../EventTypeDefs'; -import {capValue} from '../../util/misc/capValue'; -import {ITextBehavior,ITextEvents} from './ITextBehavior'; -import type {TKeyMapIText} from './constants'; +import { config } from '../../config'; +import { getDocument, getEnv } from '../../env'; +import { TPointerEvent } from '../../EventTypeDefs'; +import { capValue } from '../../util/misc/capValue'; +import { ITextBehavior, ITextEvents } from './ITextBehavior'; +import type { TKeyMapIText } from './constants'; export abstract class ITextKeyBehavior< EventSpec extends ITextEvents = ITextEvents @@ -181,7 +181,7 @@ export abstract class ITextKeyBehavior< this.canvas.fire('text:changed', { target: this }); this.canvas.requestRenderAll(); } - } + }; if (this.hiddenTextarea.value === '') { this.styles = {}; updateAndFire(); From ffeee29d724b52cb127ff8ba806171f4b36f4c7b Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 13 Mar 2023 00:09:05 +0100 Subject: [PATCH 6/8] didntmean to change --- .vscode/settings.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a5633b2a249..d21dddc7466 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,5 @@ "editor.codeActionsOnSave": { "source.organizeImports": false, "source.removeUnusedImports": true - }, - "prettier.configPath": ".prettierrc", - "prettier.bracketSpacing": true + } } From 3394213dc6af5fdfa587ab278fa56a48a2277619 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 13 Mar 2023 00:16:21 +0100 Subject: [PATCH 7/8] also set as dirty --- src/shapes/IText/ITextBehavior.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/shapes/IText/ITextBehavior.ts b/src/shapes/IText/ITextBehavior.ts index 78005f0cc9b..038ce07480d 100644 --- a/src/shapes/IText/ITextBehavior.ts +++ b/src/shapes/IText/ITextBehavior.ts @@ -1,16 +1,16 @@ -import { getDocument } from '../../env'; +import {getDocument} from '../../env'; import { ObjectEvents, TPointerEvent, TPointerEventInfo, } from '../../EventTypeDefs'; -import { Point } from '../../Point'; -import type { FabricObject } from '../Object/Object'; -import { Text } from '../Text/Text'; -import { animate } from '../../util/animation/animate'; -import { TOnAnimationChangeCallback } from '../../util/animation/types'; -import type { ValueAnimation } from '../../util/animation/ValueAnimation'; -import { TextStyleDeclaration } from '../Text/StyledText'; +import {Point} from '../../Point'; +import type {FabricObject} from '../Object/Object'; +import {Text} from '../Text/Text'; +import {animate} from '../../util/animation/animate'; +import {TOnAnimationChangeCallback} from '../../util/animation/types'; +import type {ValueAnimation} from '../../util/animation/ValueAnimation'; +import {TextStyleDeclaration} from '../Text/StyledText'; /** * extend this regex to support non english languages @@ -513,6 +513,7 @@ export abstract class ITextBehavior< this.cursorOffsetCache = {}; const textarea = this.hiddenTextarea; this.text = textarea.value; + this.set('dirty', true); this.initDimensions(); this.setCoords(); const newSelection = this.fromStringToGraphemeSelection( From aeae93a88d86f79f4054518928ab14dfc0cd0813 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 13 Mar 2023 00:25:07 +0100 Subject: [PATCH 8/8] prettier --- src/shapes/IText/ITextBehavior.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shapes/IText/ITextBehavior.ts b/src/shapes/IText/ITextBehavior.ts index 038ce07480d..d3f75855810 100644 --- a/src/shapes/IText/ITextBehavior.ts +++ b/src/shapes/IText/ITextBehavior.ts @@ -1,16 +1,16 @@ -import {getDocument} from '../../env'; +import { getDocument } from '../../env'; import { ObjectEvents, TPointerEvent, TPointerEventInfo, } from '../../EventTypeDefs'; -import {Point} from '../../Point'; -import type {FabricObject} from '../Object/Object'; -import {Text} from '../Text/Text'; -import {animate} from '../../util/animation/animate'; -import {TOnAnimationChangeCallback} from '../../util/animation/types'; -import type {ValueAnimation} from '../../util/animation/ValueAnimation'; -import {TextStyleDeclaration} from '../Text/StyledText'; +import { Point } from '../../Point'; +import type { FabricObject } from '../Object/Object'; +import { Text } from '../Text/Text'; +import { animate } from '../../util/animation/animate'; +import { TOnAnimationChangeCallback } from '../../util/animation/types'; +import type { ValueAnimation } from '../../util/animation/ValueAnimation'; +import { TextStyleDeclaration } from '../Text/StyledText'; /** * extend this regex to support non english languages