Skip to content

Conversation

@ShaMan123
Copy link
Contributor

@ShaMan123 ShaMan123 commented Nov 26, 2022

Motivation

#7888
#7891
Wants to limit text input

#5911
Wants to avoid the textbox going out of canvas when inputing text

#5114
Wants to wrap text when reach a certain width becase of long words

It seems that maxWidth should indeed break the words.
so while starting to implement it is fine, i don't think it closes the related issues.

Originally posted by @asturur in #7981 (comment)

closes #7888
closes #7891
closes #5911
related #1184
closes #2376
closes #5114
closes #1162

Description

port #7981
This PR supports minWidth maxWidth, breaking lines to fit the size. Splitting words (word-break) is also possible when using with splitByGrapheme (use minWidth as well or set it to true after initialization).

Changes

I have refactored logic not to care if maxWidth < minWidth. Values are not changed by fabric and everything works!
maxWidth will be overriden by minWidth and by _actualMaxWidth (min values + largest word) in case of conflict.

Gist

In Action

splitByGrapheme + maxWidth

Fabric.React.App.Sandbox.-.Google.Chrome.2022-06-10.20-48-19.mp4

maxWidth

Fabric.React.App.Sandbox.-.Google.Chrome.2022-06-10.20-48-57.mp4

onInput + maxWidth

Fabric.React.App.Sandbox.-.Google.Chrome.2022-06-10.21-04-54.mp4

@ShaMan123 ShaMan123 changed the title port #7981 feat(Textbox): min/max width Nov 26, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Nov 26, 2022

Build Stats

file / KB (diff) bundled minified
fabric 933.159 (+1.109) 301.630 (+0.658)

@github-actions
Copy link
Contributor

github-actions bot commented Nov 26, 2022

Coverage after merging textbox-max-width-ts into master will be

83.38%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
index.js35%12.50%0%54.55%430, 432, 432, 432, 432, 432, 434–435, 437, 437, 437–438
src
   cache.ts96.97%90%100%100%56
   config.ts77.27%66.67%66.67%84.62%130, 138–140, 151–153
   constants.ts100%100%100%100%
   env.ts72.73%53.33%100%79.17%22, 22–23, 23, 23, 25, 25, 27, 29, 31–32, 62
   intersection.class.ts100%100%100%100%
   pattern.class.ts92.19%85.71%100%96.30%118, 124, 135, 144, 96
   point.class.ts100%100%100%100%
   shadow.class.ts98.48%96%100%100%156
src/brushes
   base_brush.class.ts100%100%100%100%
   circle_brush.class.ts0%0%0%0%100, 102–104, 113, 113, 113, 115, 117, 119–121, 123–126, 134, 141, 143, 23, 28–29, 37–41, 45–49, 56–59, 67–71, 73, 81, 81, 81, 81, 81–82, 84, 84, 84–87, 89, 97–98
   pattern_brush.class.ts97.06%87.50%100%100%21
   pencil_brush.class.ts91.86%85.42%100%93.58%122–123, 152, 152–154, 276, 280, 285–286, 68–69, 84–85
   spray_brush.class.ts0%0%0%0%100–101, 103–104, 112, 112, 112, 112, 112–113, 115–116, 123–124, 126, 128–132, 141, 145–146, 146, 154, 154, 154–157, 159–162, 166–167, 169, 171–174, 177, 184–185, 187, 189–190, 192, 199–200, 202–203, 206, 206, 213, 213, 217, 22–23, 25–27, 27, 27–29, 33, 42, 49, 56, 63, 70, 77, 89–91, 99
src/canvas
   canvas.class.ts92.81%88.60%94.12%95.70%1154, 1154–1155, 1158, 1178, 1178, 1240, 1276–1277, 1296–1297, 1305–1306, 1327, 1335, 1448–1449, 1451–1452, 1472–1473, 1636–1637, 1641, 510, 577–578, 583, 593, 722–723, 725–726, 726, 726, 772–773, 834–835, 888–890, 920, 925–926, 955–956
   canvas_events.ts78.19%75.30%82.81%79.48%1003–1004, 1004, 1004–1006, 1008–1009, 1009, 1009, 1011, 1019, 1019, 1019–1021, 1021, 1021, 1027–1028, 1036–1037, 1037, 1037–1038, 1043, 1045, 1075–1077, 1080–1081, 1085–1086, 1184, 1204–1206, 1209–1210, 1282, 1402, 145, 1496–1497, 1503, 1507–1508, 1524, 1546, 1593, 1598, 1637, 170, 318–319, 319, 319–320, 320, 323–327, 332, 334, 346–348, 351, 368, 368, 368–369, 369, 369–370, 378, 383–384, 384, 384–385, 387, 396, 402–403, 403, 403, 439, 443, 443, 443, 443, 443–444, 446, 521, 523, 523, 523–525, 545, 547, 547, 547–548, 548, 548, 551, 551, 551–552, 555, 564–565, 567–568, 570, 570–571, 573–574, 586–587, 587, 587–588, 590–594, 600, 607, 647, 647, 647, 649, 651–655, 661, 667, 667, 667–668, 670, 673, 678, 691, 718, 774–775, 775, 775–776, 778, 781–782, 782, 782–783, 785–786, 789, 789–791, 794–795, 805, 887, 901, 908, 929, 961, 982–983, 989
   static_canvas.class.ts94.56%88.97%97.92%97.11%1125–1126, 1126, 1126–1127, 1247, 1257, 1311–1312, 1315, 1350–1351, 1429, 1438, 1443, 1492–1493, 1721, 1721–1722, 1772, 1775, 1778, 1778, 1778, 1781, 1784, 1784, 1784, 309, 345, 358, 411–412, 414–415, 424, 428–429, 432–433, 885
src/color
   color.class.ts92.16%86.49%100%94.29%330–331, 335–336, 339–340, 58, 88–89, 89, 91, 91, 91–92, 94–95
   color_map.ts100%100%100%100%
   constants.ts100%100%100%100%
   util.ts100%100%100%100%
src/controls
   changeWidth.ts100%100%100%100%
   control.class.ts93.90%88.89%90.91%97.73%235, 319, 319, 354
   controls.render.ts81.63%78%100%84.78%106, 111, 121, 121, 45, 50, 61, 61, 65–72, 81–82
   default_controls.ts86.67%66.67%100%100%122, 129
   drag.ts100%100%100%100%
   rotate.ts20%12.50%50%22.22%45, 51, 51, 51–52, 55–57, 59, 59, 59, 59, 59–61, 61, 61–63, 65, 65, 65–67, 67, 67–68, 73, 73, 73–74, 76, 78, 80–81
   scale.ts93.41%92.68%100%93.59%129–130, 132–134, 148–149, 181–183, 42
   scaleSkew.ts78.79%64.29%100%85.71%27, 29, 29, 29, 31, 33, 35
   skew.ts91.03%79.31%100%97.67%130–131, 162–163, 170, 176, 178
   util.ts100%100%100%100%
   wrapWithFireEvent.ts100%100%100%100%
   wrapWithFixedAnchor.ts100%100%100%100%
src/filters
   2d_backend.class.ts92%83.33%100%93.75%35–36
   FilterBackend.ts88.89%88.89%100%85.71%15–16
   WebGLProbe.ts40.54%40%60%36.36%28–30, 30, 30–31, 33–35, 43, 46–48, 48, 48–51, 53, 58
   base_filter.class.ts20.83%20.83%33.33%18.18%102–104, 104, 104–105, 112–115, 115, 115–116, 122, 122, 122–125, 143, 173–178, 182–183, 183, 183–186, 186, 186,

Copy link
Contributor Author

@ShaMan123 ShaMan123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated from master

this.clearContextTop();
this._clearCache();
// clear dynamicMinWidth as it will be different after we re-wrap line
this.dynamicMinWidth = 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be set to the default value 2?

@ShaMan123 ShaMan123 added the text label Dec 14, 2022
Copy link
Contributor Author

@ShaMan123 ShaMan123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated from master

@Matergi
Copy link

Matergi commented Feb 4, 2024

A solution could be to create a new Fabric class that extends the Fabric.Textbox class. Here's an example:

const LimitedTextbox = fabric.util.createClass(fabric.Textbox, {
  onInput: function (e: any) {
    const oldText = this.text;

    /**
     * Sometimes, when you insert a line break, it doesn't appear to Fabric as an 'insertLineBreak' but simply sets the date to null.
     * Therefore, it's important to check that it's not deleting any characters, and if the date is null, it means that a new line is being inserted
     */
    if (e.inputType === 'insertLineBreak' || (e.inputType !== 'deleteContentBackward' && !e.data)) {
      this._textBeforeEdit = oldText;
      this.hiddenTextarea.value = oldText;
      this.text = oldText;
      return;
    } else if (e.inputType !== 'deleteContentBackward') {
      if (this.width > this.maxWidth) {
        this._textBeforeEdit = oldText;
        this.hiddenTextarea.value = oldText;
        this.text = oldText;

        // If you want to enable scaling to keep writing, there are issues when you delete a portion of text and not just a single character
        //
        // this.fontSizeList = [...(this.fontSizeList || []), this.fontSize];
        // this.fontSize *= this.maxWidth / (this.width + 1);
        // this.width = this.maxWidth;
        return;
      }

      if (this._textLines.length > this.maxLines) {
        this._textBeforeEdit = oldText;
        this.hiddenTextarea.value = oldText;
        this.text = oldText;
        return;
      }
    } else {
      // If you want to enable scaling to keep writing, there are issues when you delete a portion of text and not just a single character
      //
      // const lastFontSize = this.fontSizeList?.pop();
      // if (lastFontSize) {
      //   this.fontSize = lastFontSize;
      // }

      /**
       * It's needed because otherwise, if I write many characters, he automatically adjusts the width even if the visible text doesn't change.
       * So, every time I delete something, I reset the width.
       * I do it during deletion; otherwise, if I do it in the maxWidth check, I never block it, and it would keep writing
       */
      this.width = this.maxWidth;
    }

    this.callSuper('onInput', e);
  },
});

...

textbox = new LimitedTextbox('custom text', {
  maxLines: 1,
  maxWidth: 100,
});

@ShaMan123
Copy link
Contributor Author

I will probably abandon this PR for a better design.
we exposed methods that can be overridden to achieve text layout.
In the future I hope we provide a robust API that has layout management

@ShaMan123
Copy link
Contributor Author

Stale, can be achieved by overriding _wrapText, getGraphemeDataForRender

@ShaMan123 ShaMan123 closed this May 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

3 participants