drawnui-react 0.1.0-preview.2 → 0.1.0-preview.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +17 -4
  2. package/SKIPPED.md +33 -27
  3. package/dist/controls/AnimatedFramesRenderer.d.ts +7 -0
  4. package/dist/controls/AnimatedFramesRenderer.js +16 -1
  5. package/dist/controls/DrawnGame.d.ts +40 -0
  6. package/dist/controls/DrawnGame.js +64 -0
  7. package/dist/controls/GridStructure.d.ts +16 -2
  8. package/dist/controls/GridStructure.js +40 -4
  9. package/dist/controls/SkiaBackdrop.d.ts +8 -2
  10. package/dist/controls/SkiaBackdrop.js +48 -8
  11. package/dist/controls/SkiaButton.d.ts +19 -2
  12. package/dist/controls/SkiaButton.js +41 -11
  13. package/dist/controls/SkiaCarousel.d.ts +9 -1
  14. package/dist/controls/SkiaCarousel.js +16 -3
  15. package/dist/controls/SkiaCheckbox.d.ts +0 -1
  16. package/dist/controls/SkiaCheckbox.js +0 -6
  17. package/dist/controls/SkiaDrawer.d.ts +2 -0
  18. package/dist/controls/SkiaDrawer.js +2 -0
  19. package/dist/controls/SkiaEditor.d.ts +12 -1
  20. package/dist/controls/SkiaEditor.js +46 -19
  21. package/dist/controls/SkiaGif.d.ts +3 -0
  22. package/dist/controls/SkiaGif.js +12 -2
  23. package/dist/controls/SkiaImage.js +15 -10
  24. package/dist/controls/SkiaLabel.d.ts +54 -3
  25. package/dist/controls/SkiaLabel.js +235 -52
  26. package/dist/controls/SkiaLabelFps.d.ts +16 -0
  27. package/dist/controls/SkiaLabelFps.js +41 -0
  28. package/dist/controls/SkiaLayout.d.ts +64 -7
  29. package/dist/controls/SkiaLayout.js +316 -19
  30. package/dist/controls/SkiaLottie.js +4 -2
  31. package/dist/controls/SkiaProgress.d.ts +2 -1
  32. package/dist/controls/SkiaProgress.js +6 -3
  33. package/dist/controls/SkiaRadioButton.js +4 -3
  34. package/dist/controls/SkiaScroll.d.ts +35 -5
  35. package/dist/controls/SkiaScroll.js +233 -23
  36. package/dist/controls/SkiaScrollBar.d.ts +27 -0
  37. package/dist/controls/SkiaScrollBar.js +51 -2
  38. package/dist/controls/SkiaShaderCarousel.d.ts +5 -0
  39. package/dist/controls/SkiaShaderCarousel.js +5 -0
  40. package/dist/controls/SkiaShape.d.ts +13 -2
  41. package/dist/controls/SkiaShape.js +50 -10
  42. package/dist/controls/SkiaSlider.d.ts +20 -2
  43. package/dist/controls/SkiaSlider.js +59 -5
  44. package/dist/controls/SkiaSprite.d.ts +1 -0
  45. package/dist/controls/SkiaSprite.js +4 -0
  46. package/dist/controls/SkiaSvg.js +1 -1
  47. package/dist/controls/SkiaSwitch.d.ts +0 -1
  48. package/dist/controls/SkiaSwitch.js +0 -6
  49. package/dist/controls/SkiaToggle.d.ts +25 -5
  50. package/dist/controls/SkiaToggle.js +58 -5
  51. package/dist/controls/SnappingLayout.d.ts +12 -1
  52. package/dist/controls/SnappingLayout.js +19 -1
  53. package/dist/core/Accessibility.d.ts +80 -0
  54. package/dist/core/Accessibility.js +365 -8
  55. package/dist/core/Animators.d.ts +37 -1
  56. package/dist/core/Animators.js +102 -9
  57. package/dist/core/Canvas.d.ts +108 -7
  58. package/dist/core/Canvas.js +363 -35
  59. package/dist/core/Easing.d.ts +12 -1
  60. package/dist/core/Easing.js +25 -1
  61. package/dist/core/Gestures.d.ts +48 -2
  62. package/dist/core/Gestures.js +95 -3
  63. package/dist/core/Hover.d.ts +47 -0
  64. package/dist/core/Hover.js +134 -0
  65. package/dist/core/KeyboardManager.d.ts +13 -0
  66. package/dist/core/KeyboardManager.js +18 -0
  67. package/dist/core/ScrollAnimators.js +9 -0
  68. package/dist/core/SkiaControl.d.ts +216 -7
  69. package/dist/core/SkiaControl.js +516 -59
  70. package/dist/core/SkiaEffect.d.ts +17 -0
  71. package/dist/core/SkiaEffect.js +43 -8
  72. package/dist/core/SkiaImageManager.js +1 -1
  73. package/dist/core/Styles.d.ts +51 -0
  74. package/dist/core/Styles.js +92 -0
  75. package/dist/core/Super.d.ts +17 -1
  76. package/dist/core/Super.js +35 -4
  77. package/dist/core/ViewsAdapter.d.ts +7 -2
  78. package/dist/core/ViewsAdapter.js +10 -3
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.js +4 -0
  81. package/dist/react/SkiaShell.js +33 -14
  82. package/dist/react/index.d.ts +15 -4
  83. package/dist/react/index.js +102 -15
  84. package/dist/react/reconciler.js +34 -12
  85. package/dist/vite/index.d.ts +9 -0
  86. package/dist/vite/index.js +36 -3
  87. package/package.json +33 -3
@@ -1,6 +1,5 @@
1
1
  import { SKPoint } from "../core/Gestures";
2
2
  import { Super } from "../core/Super";
3
- import { SkiaValueAnimator } from "../core/Animators";
4
3
  import { ResolveControlStyle } from "../core/ControlStyle";
5
4
  import { KeyboardManager } from "../core/KeyboardManager";
6
5
  import { TextInputProxy } from "../core/TextInputProxy";
@@ -67,13 +66,19 @@ export class SkiaEditor extends SkiaShape {
67
66
  placeholder = new SkiaLabel();
68
67
  scrollX = 0;
69
68
  scrollY = 0;
70
- blink = new SkiaValueAnimator(this);
69
+ /** The caret blinks on a timer, asleep between blinks: no frame runs while it waits (DrawnUi.Rust editor_rules). */
70
+ blinkTimer = 0;
71
71
  caretVisible = true;
72
72
  subscribed = false;
73
73
  stubSelectionStop = -1;
74
74
  selectionMovingEdge = -1;
75
75
  onKeyDown = (key, e) => this.OnKeyDown(key, e);
76
- onKeyChar = (ch, e) => { e.preventDefault(); this.StubTypeText(ch); };
76
+ onKeyChar = (ch, e) => {
77
+ if (KeyboardManager.IsOwnedByElement(e) || KeyboardManager.IsInputMethodKey(e))
78
+ return; // a page control or the IME owns it
79
+ e.preventDefault();
80
+ this.StubTypeText(ch);
81
+ };
77
82
  selectionAnchor = -1;
78
83
  dragSelecting = false;
79
84
  styleApplied = {};
@@ -93,15 +98,6 @@ export class SkiaEditor extends SkiaShape {
93
98
  this.placeholder.IsVisible = false;
94
99
  this.AddSubView(this.placeholder);
95
100
  this.AddSubView(this.Label);
96
- this.blink.mMinValue = 0;
97
- this.blink.mMaxValue = 1;
98
- this.blink.Speed = 1000;
99
- this.blink.Repeat = -1;
100
- this.blink.OnUpdated = (v) => { const visible = v < 0.5; if (visible !== this.caretVisible) {
101
- this.caretVisible = visible;
102
- this.InvalidateCache();
103
- this.RepaintComposition();
104
- } };
105
101
  this.ApplyControlStyleVisuals();
106
102
  this.UpdateLabel();
107
103
  }
@@ -192,9 +188,22 @@ export class SkiaEditor extends SkiaShape {
192
188
  this.selectionLength = l;
193
189
  this.OnCursorMoved();
194
190
  }
191
+ /** Shows the caret for a full half second, then blinks every 500 ms. */
192
+ StartBlink() {
193
+ this.StopBlink();
194
+ this.caretVisible = true;
195
+ this.blinkTimer = window.setInterval(() => { this.caretVisible = !this.caretVisible; this.InvalidateCache(); this.RepaintComposition(); }, 500);
196
+ }
197
+ StopBlink() { if (this.blinkTimer) {
198
+ clearInterval(this.blinkTimer);
199
+ this.blinkTimer = 0;
200
+ } }
195
201
  OnCursorMoved() {
196
202
  this.ScrollToCaret();
197
- this.caretVisible = true;
203
+ if (this.isFocused)
204
+ this.StartBlink();
205
+ else
206
+ this.caretVisible = true;
198
207
  TextInputProxy.Sync(this);
199
208
  this.CursorMoved?.(this);
200
209
  this.InvalidateCache();
@@ -210,8 +219,7 @@ export class SkiaEditor extends SkiaShape {
210
219
  SkiaEditor.Focused.IsFocused = false;
211
220
  SkiaEditor.Focused = this;
212
221
  this.SetFocusNative(true);
213
- this.caretVisible = true;
214
- this.blink.Start();
222
+ this.StartBlink();
215
223
  }
216
224
  else {
217
225
  if (SkiaEditor.Focused === this)
@@ -220,7 +228,7 @@ export class SkiaEditor extends SkiaShape {
220
228
  this.dragSelecting = false;
221
229
  this.stubSelectionStop = -1;
222
230
  this.selectionMovingEdge = -1;
223
- this.blink.Stop();
231
+ this.StopBlink();
224
232
  this.SetFocusNative(false);
225
233
  }
226
234
  this.FocusChanged?.(this, v);
@@ -305,6 +313,14 @@ export class SkiaEditor extends SkiaShape {
305
313
  MeasureAbsolute(w, h, scale) {
306
314
  const px = this.Padding.HorizontalThickness * scale, py = this.Padding.VerticalThickness * scale;
307
315
  const innerW = isFinite(w) ? Math.max(0, w - px) : Infinity;
316
+ // Single-line: the label is Start-aligned and measures to its content, so Center / End alignment had nothing to
317
+ // align in. Give it the field as a MINIMUM: short text centers in the field, longer text still grows past it and
318
+ // scrolls (C# 2e334ddd).
319
+ const minWidth = !this.IsMultiline && this.horizontalTextAlignment !== "Start" && isFinite(innerW) && innerW > 0 ? innerW / scale : -1;
320
+ if (this.Label.MinimumWidthRequest !== minWidth) {
321
+ this.Label.MinimumWidthRequest = minWidth;
322
+ this.Label.NeedMeasure = true;
323
+ }
308
324
  const labelSize = this.Label.Measure(this.IsMultiline ? innerW : Infinity, Infinity, scale);
309
325
  if (this.placeholder.IsVisible)
310
326
  this.placeholder.Measure(innerW, Infinity, scale);
@@ -500,7 +516,15 @@ export class SkiaEditor extends SkiaShape {
500
516
  }
501
517
  return this;
502
518
  }
503
- OnAccessibilityActivated() { this.IsFocused = true; }
519
+ /** A text field is a Tab stop by default, like a native text box, with no Tapped handler needed. */
520
+ DefaultAccessibilityCanInteract() { return true; }
521
+ /** Keyboard navigation reached the field (Tab) or left it: take the caret and the input sink like a native text box, or release them. */
522
+ OnAccessibilityFocused(focused) { this.IsFocused = focused; }
523
+ /** Enter / Space on the field while it has no caret start editing; while editing they are text. */
524
+ OnAccessibilityActivated() { if (!this.IsFocused)
525
+ this.OnAccessibilityFocused(true); }
526
+ /** While editing, arrows, Home / End and PageUp / PageDown belong to the text: they never move a surrounding group. */
527
+ OnAccessibilityKey(_key) { return this.IsFocused; }
504
528
  DefaultAccessibilityLabel() { return this.text || this.placeholderText || undefined; }
505
529
  // ---- editing API (C# names) ----
506
530
  Submit() {
@@ -580,6 +604,10 @@ export class SkiaEditor extends SkiaShape {
580
604
  OnKeyDown(key, e) {
581
605
  const shift = KeyboardManager.IsShiftPressed, ctrl = KeyboardManager.IsControlPressed, alt = KeyboardManager.IsAltPressed;
582
606
  const handled = () => e.preventDefault();
607
+ // keys of another element (a page field or button, an overlay node) and of the input method are not the editor's:
608
+ // a composition keeps its arrows, Escape and Backspace (C# 845b26e9, GitHub #231)
609
+ if (KeyboardManager.IsInputMethodKey(e) || (KeyboardManager.IsOwnedByElement(e) && !TextInputProxy.IsProxyTarget(e.target)))
610
+ return;
583
611
  // with the hidden textarea focused these keys become input / clipboard events there and are applied from those
584
612
  if (TextInputProxy.IsActive(this) && TextInputProxy.IsProxyTarget(e.target) && (key === "Backspace" || key === "Delete" || key === "Enter" || key === "NumpadEnter" || (ctrl && (key === "KeyC" || key === "KeyX" || key === "KeyV"))))
585
613
  return;
@@ -794,8 +822,7 @@ export class SkiaEditor extends SkiaShape {
794
822
  return out;
795
823
  }
796
824
  OnDisposing() {
797
- this.blink.Stop();
798
- this.blink.Dispose();
825
+ this.StopBlink();
799
826
  if (SkiaEditor.Focused === this)
800
827
  SkiaEditor.Focused = undefined;
801
828
  this.SetFocusNative(false);
@@ -14,6 +14,8 @@ export declare class GifAnimation {
14
14
  private positionsMs;
15
15
  /** Negative = the last frame. */
16
16
  SeekFrame(frame: number): void;
17
+ /** Start (ms) of the frame after the one at `msTime`; DurationMs after the last one. */
18
+ NextFrameStartMs(msTime: number): number;
17
19
  GetFrameNumber(msTime: number): number;
18
20
  /** Decodes every frame of an encoded GIF (CanvasKit AnimatedImage). */
19
21
  LoadFromBytes(bytes: ArrayBuffer): void;
@@ -37,6 +39,7 @@ export declare class SkiaGif extends AnimatedFramesRenderer {
37
39
  set Source(v: string);
38
40
  Start(delayMs?: number): void;
39
41
  protected OnAnimatorUpdated(value: number): void;
42
+ protected NextChangeMs(value: number): number | undefined;
40
43
  /** Seeks by time in ms (the animator range is 0..DurationMs). */
41
44
  protected OnAnimatorSeeking(time: number): void;
42
45
  protected OnAnimatorInitializing(): void;
@@ -21,6 +21,13 @@ export class GifAnimation {
21
21
  if (frame >= 0 && frame <= this.TotalFrames - 1)
22
22
  this.Frame = this.Frames[frame];
23
23
  }
24
+ /** Start (ms) of the frame after the one at `msTime`; DurationMs after the last one. */
25
+ NextFrameStartMs(msTime) {
26
+ for (let i = 0; i < this.positionsMs.length; i++)
27
+ if (msTime < this.positionsMs[i])
28
+ return this.positionsMs[i];
29
+ return this.DurationMs;
30
+ }
24
31
  GetFrameNumber(msTime) {
25
32
  if (this.positionsMs.length === 0 || this.DurationMs <= 0)
26
33
  return 0;
@@ -87,7 +94,9 @@ export class SkiaGif extends AnimatedFramesRenderer {
87
94
  loadGeneration = 0;
88
95
  constructor() {
89
96
  super();
90
- this.UseCache = "ImageDoubleBuffered";
97
+ // C# ImageDoubleBuffered pays off where a thread bakes the bitmap; on the web it is one offscreen pass per frame
98
+ // change, so the web engines draw a GIF frame with no cache (drawnui-cross rule 3)
99
+ this.UseCache = "None";
91
100
  }
92
101
  get Source() { return this.source; }
93
102
  set Source(v) { if (this.source !== v) {
@@ -97,6 +106,7 @@ export class SkiaGif extends AnimatedFramesRenderer {
97
106
  Start(delayMs = 0) { if ((this.Animation?.TotalFrames ?? 0) > 0)
98
107
  super.Start(delayMs); }
99
108
  OnAnimatorUpdated(value) { this.Seek(value); }
109
+ NextChangeMs(value) { return this.Animation && this.Animation.DurationMs > 0 ? this.Animation.NextFrameStartMs(value) : undefined; }
100
110
  /** Seeks by time in ms (the animator range is 0..DurationMs). */
101
111
  OnAnimatorSeeking(time) {
102
112
  const a = this.Animation;
@@ -158,7 +168,7 @@ export class SkiaGif extends AnimatedFramesRenderer {
158
168
  async LoadSource(source) {
159
169
  if (!source)
160
170
  return undefined;
161
- const r = await fetch(source);
171
+ const r = await fetch(Super.ResolveAssetUrl(source));
162
172
  if (!r.ok)
163
173
  throw new Error(`${r.status} ${source}`);
164
174
  const animation = new GifAnimation();
@@ -164,20 +164,25 @@ export class SkiaImage extends SkiaControl {
164
164
  MeasureAbsolute(widthConstraint, heightConstraint, scale) {
165
165
  const img = this.LoadedSource;
166
166
  let w = widthConstraint, h = heightConstraint;
167
- if (img) {
168
- const aspect = img.width() / img.height();
169
- if (!isFinite(w) && isFinite(h))
170
- w = h * aspect;
171
- else if (!isFinite(h) && isFinite(w))
172
- h = w / aspect;
167
+ // nothing loaded: the box it is offered (C# OnMeasuring), an unbounded side 0
168
+ if (!img) {
169
+ this.AspectScale = { X: 0, Y: 0 };
170
+ return ScaledSize.FromPixels(isFinite(w) ? w : 0, isFinite(h) ? h : 0, scale);
173
171
  }
174
- if (!isFinite(w) || !isFinite(h)) {
172
+ const iw = img.width(), ih = img.height();
173
+ if (!isFinite(w) && isFinite(h))
174
+ w = h * iw / ih;
175
+ else if (!isFinite(h) && isFinite(w))
176
+ h = w * ih / iw;
177
+ if (!isFinite(w) || !isFinite(h) || iw <= 0 || ih <= 0) {
175
178
  this.AspectScale = { X: 0, Y: 0 };
176
179
  return ScaledSize.FromPixels(0, 0, scale);
177
180
  }
178
- if (img)
179
- this.AspectScale = SkiaImage.RescaleAspect(img.width(), img.height(), SKRect.Create(0, 0, w, h), this.Aspect);
180
- return ScaledSize.FromPixels(w, h, scale);
181
+ // C# OnMeasuring / DrawnUi.Rust: an auto-sized side is the bitmap's pixels scaled by the aspect for this box, never
182
+ // more than the box (a Fill or requested side still takes its box in Measure)
183
+ const s = SkiaImage.RescaleAspect(iw, ih, SKRect.Create(0, 0, w, h), this.Aspect);
184
+ this.AspectScale = s;
185
+ return ScaledSize.FromPixels(Math.min(iw * s.X, w), Math.min(ih * s.Y, h), scale);
181
186
  }
182
187
  Paint(ctx) {
183
188
  const img = this.LoadedSource;
@@ -43,6 +43,8 @@ export declare class SkiaLabel extends SkiaControl {
43
43
  private verticalTextAlignment;
44
44
  private lineSpacing;
45
45
  private lineHeight;
46
+ private paragraphSpacing;
47
+ private characterSpacing;
46
48
  private textTransform;
47
49
  private padding;
48
50
  /** Styled fragments; when not empty they replace `Text` (same precedence as C#). */
@@ -53,6 +55,8 @@ export declare class SkiaLabel extends SkiaControl {
53
55
  protected DefaultAccessibilityLabel(): string | undefined;
54
56
  constructor();
55
57
  private Set;
58
+ /** A property that changes only the look (a color): no layout change, see UpdateDraw. */
59
+ private SetLook;
56
60
  get Text(): string;
57
61
  set Text(v: string);
58
62
  get FontSize(): number;
@@ -84,6 +88,18 @@ export declare class SkiaLabel extends SkiaControl {
84
88
  /** Multiplier applied to the natural line height (ascent + descent). */
85
89
  get LineHeight(): number;
86
90
  set LineHeight(v: number);
91
+ /**
92
+ * Space above every paragraph after the first, in lines (C# ParagraphSpacing; 0 here as DrawnUi.Rust, C# 0.25). A
93
+ * paragraph starts after "\n"; U+2028 breaks the line inside a paragraph.
94
+ */
95
+ get ParagraphSpacing(): number;
96
+ set ParagraphSpacing(v: number);
97
+ /**
98
+ * C# CharacterSpacing: 1 = the font's own spacing; every glyph is followed by (CharacterSpacing - 1) points more
99
+ * (less below 1), the line measures and wraps at the width it draws (C# 7cf1007c, DrawnUi.Rust label).
100
+ */
101
+ get CharacterSpacing(): number;
102
+ set CharacterSpacing(v: number);
87
103
  get TextTransform(): TextTransform;
88
104
  set TextTransform(v: TextTransform);
89
105
  get Padding(): Thickness;
@@ -97,17 +113,46 @@ export declare class SkiaLabel extends SkiaControl {
97
113
  private mainFonts?;
98
114
  private readonly fontsCache;
99
115
  private readonly runCache;
116
+ /** CharacterSpacing in pixels at the measured scale: added after every glyph of a run (its Width includes them). */
117
+ private glyphSpacing;
100
118
  private ResolveFonts;
101
119
  /** The label's own style, and the base every span inherits from. */
102
120
  private ResolveMainFonts;
103
121
  private ResolveSpanFonts;
104
- private static Advance;
122
+ private Advance;
123
+ private static readonly Ignorable;
124
+ /**
125
+ * Default-ignorable code points (zero width space, joiners, direction marks, word joiners, variation selectors such
126
+ * as U+FE0F after an emoji, tags) that none of the fonts has draw nothing and take no room, never the missing-glyph
127
+ * box (DrawnUi.Rust label_ignorables; text is not shaped here, C# hides them by shaping).
128
+ */
129
+ private static DropIgnorables;
105
130
  /**
106
131
  * Splits text into runs by glyph availability: the main font, or the first fallback that has a glyph where the
107
132
  * main font has glyph 0. Spaces always stay on the main font (fallback faces often carry very wide spaces).
108
133
  */
109
134
  private Segment;
110
135
  private Width;
136
+ /** A line never starts with these (closing punctuation, small kana, the long vowel mark): JIS X 4051 kinsoku (C#). */
137
+ private static readonly NoBreakBefore;
138
+ /** A line never ends with these (opening brackets and quotes). */
139
+ private static readonly NoBreakAfter;
140
+ /** Chinese / Japanese: ideographs, kana, CJK punctuation, full-width forms (C# IsCjkAt). Korean breaks at spaces only. */
141
+ private static readonly Cjk;
142
+ private static CanBreakBetween;
143
+ /**
144
+ * C# CanBreakInsideWord: whether a line may break between text[index - 1] and text[index] (UTF-16 indexes) inside a
145
+ * space-free run: next to a Chinese or Japanese character, never before closing punctuation or small kana, never
146
+ * after an opening bracket, never inside a surrogate pair.
147
+ */
148
+ static CanBreakInsideWord(text: string, index: number): boolean;
149
+ /**
150
+ * C# FitAtBreak: the last place inside the word where a Chinese / Japanese line may break and `prefix` plus the word
151
+ * up to there still fits; code points from the word's start, 0 = none.
152
+ */
153
+ private FitCjk;
154
+ /** The word cut after `count` code points: the head goes on a line, the rest follows. */
155
+ private SplitWord;
111
156
  private Transform;
112
157
  /**
113
158
  * Paragraphs (split on "\n") of wrap tokens, built from Spans when present, else from Text.
@@ -116,11 +161,17 @@ export declare class SkiaLabel extends SkiaControl {
116
161
  private Tokenize;
117
162
  private NewLine;
118
163
  private Append;
119
- /** Word-wraps tokens into lines that fit maxWidth (Infinity = no wrap), applies MaxLines with a tail ellipsis. */
164
+ /**
165
+ * Word-wraps tokens into lines that fit maxWidth (Infinity = no wrap); keeps the lines that fit maxHeight (at least one)
166
+ * and MaxLines, the last kept one ending with a tail ellipsis (C# / DrawnUi.Rust: a label in a box too short for its
167
+ * text cuts it, not only MaxLines).
168
+ */
120
169
  private LayoutLines;
121
170
  private LineHeightPx;
171
+ /** ParagraphSpacing above a line that starts a paragraph (after the first): that many of its lines, with their spacing. */
172
+ private ParagraphSpace;
122
173
  private BlockHeight;
123
- protected MeasureAbsolute(widthConstraint: number, _heightConstraint: number, scale: number): ScaledSize;
174
+ protected MeasureAbsolute(widthConstraint: number, heightConstraint: number, scale: number): ScaledSize;
124
175
  /** C# GradientByLines: FillGradient spans each line's bounds (default) instead of the whole text block. */
125
176
  GradientByLines: boolean;
126
177
  /** C# KeepSpacesOnLineBreaks / NeedsGlyphPositions: accepted; glyph boxes are always available through GetGlyphBoxes. */