uicore-ts 1.1.88 → 1.1.102

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.
@@ -1,51 +1,49 @@
1
1
  import { UIColor } from "./UIColor"
2
2
  import { UILocalizedTextObject } from "./UIInterfaces"
3
- import { FIRST, IS, IS_LIKE_NULL, nil, NO, UIObject, YES } from "./UIObject"
3
+ import { FIRST, IS_LIKE_NULL, nil, NO, UIObject, ValueOf, YES } from "./UIObject"
4
4
  import { UIRectangle } from "./UIRectangle"
5
- import type { ValueOf } from "./UIObject"
6
5
  import { TextMeasurementStyle, UITextMeasurement } from "./UITextMeasurement"
7
6
  import { UIView, UIViewBroadcastEvent } from "./UIView"
8
7
 
9
8
 
10
9
  export class UITextView extends UIView {
11
10
 
12
-
13
- _textColor: UIColor = UITextView.defaultTextColor
14
- _textAlignment?: ValueOf<typeof UITextView.textAlignment>
15
-
16
- _isSingleLine = YES
17
-
18
- textPrefix = ""
19
- textSuffix = ""
20
-
21
- _notificationAmount = 0
22
-
23
- _minFontSize?: number
24
- _maxFontSize?: number
25
-
26
- _automaticFontSizeSelection = NO
27
-
28
- changesOften = NO
11
+ //#region Static Properties
29
12
 
30
13
  static defaultTextColor = UIColor.blackColor
31
14
  static notificationTextColor = UIColor.redColor
32
15
 
16
+ // Global caches for all UILabels
33
17
  static _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
34
18
  static _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
35
19
 
36
- _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
37
- _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
38
-
39
-
40
20
  static _ptToPx: number
41
21
  static _pxToPt: number
42
- _text?: string
43
22
 
44
- private _useFastMeasurement: boolean | undefined;
45
- private _cachedMeasurementStyles: TextMeasurementStyle | undefined;
23
+ static type = {
24
+ "paragraph": "p",
25
+ "header1": "h1",
26
+ "header2": "h2",
27
+ "header3": "h3",
28
+ "header4": "h4",
29
+ "header5": "h5",
30
+ "header6": "h6",
31
+ "textArea": "textarea",
32
+ "textField": "input",
33
+ "span": "span",
34
+ "label": "label"
35
+ } as const
46
36
 
47
- override usesVirtualLayoutingForIntrinsicSizing = NO
37
+ static textAlignment = {
38
+ "left": "left",
39
+ "center": "center",
40
+ "right": "right",
41
+ "justify": "justify"
42
+ } as const
48
43
 
44
+ //#endregion
45
+
46
+ //#region Constructor
49
47
 
50
48
  constructor(
51
49
  elementID?: string,
@@ -65,64 +63,128 @@ export class UITextView extends UIView {
65
63
 
66
64
  this.userInteractionEnabled = YES
67
65
 
68
-
69
66
  if (textViewType == UITextView.type.textArea) {
70
-
71
67
  this.pausesPointerEvents = YES
72
-
73
68
  this.addTargetForControlEvent(
74
69
  UIView.controlEvent.PointerUpInside,
75
70
  (sender, event) => sender.focus()
76
71
  )
77
-
78
-
79
72
  }
80
-
81
-
82
73
  }
83
74
 
75
+ //#endregion
84
76
 
85
- static _determinePXAndPTRatios() {
77
+ //#region Lifecycle Methods
78
+
79
+ override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {
80
+ super.didReceiveBroadcastEvent(event)
81
+ }
82
+
83
+ override willMoveToSuperview(superview: UIView) {
84
+ super.willMoveToSuperview(superview)
85
+ }
86
+
87
+ override layoutSubviews() {
88
+ super.layoutSubviews()
86
89
 
87
- if (UITextView._ptToPx) {
88
- return
90
+ if (this._automaticFontSizeSelection) {
91
+ this.fontSize = UITextView.automaticallyCalculatedFontSize(
92
+ new UIRectangle(0, 0, 1 *
93
+ this.viewHTMLElement.offsetHeight, 1 *
94
+ this.viewHTMLElement.offsetWidth),
95
+ this.intrinsicContentSize(),
96
+ this.fontSize,
97
+ this._minFontSize,
98
+ this._maxFontSize
99
+ )
89
100
  }
90
-
91
- const o = document.createElement("div")
92
- o.style.width = "1000pt"
93
- document.body.appendChild(o)
94
- UITextView._ptToPx = o.clientWidth / 1000
95
- document.body.removeChild(o)
96
- UITextView._pxToPt = 1 / UITextView._ptToPx
97
-
98
101
  }
99
102
 
103
+ //#endregion
100
104
 
101
- static type = {
105
+ //#region Measurement & Sizing - Private Methods
106
+
107
+ private _invalidateMeasurementStyles(): void {
108
+ this._cachedMeasurementStyles = undefined
109
+ UITextMeasurement.invalidateElement(this.viewHTMLElement)
110
+ this._intrinsicSizesCache = {}
111
+ }
112
+
113
+ private _getMeasurementStyles(): TextMeasurementStyle {
114
+ if (this._cachedMeasurementStyles) {
115
+ return this._cachedMeasurementStyles
116
+ }
102
117
 
103
- "paragraph": "p",
104
- "header1": "h1",
105
- "header2": "h2",
106
- "header3": "h3",
107
- "header4": "h4",
108
- "header5": "h5",
109
- "header6": "h6",
110
- "textArea": "textarea",
111
- "textField": "input",
112
- "span": "span",
113
- "label": "label"
118
+ const computed = window.getComputedStyle(this.viewHTMLElement)
119
+ const fontSize = parseFloat(computed.fontSize)
114
120
 
115
- } as const
121
+ this._cachedMeasurementStyles = {
122
+ font: [
123
+ computed.fontStyle,
124
+ computed.fontVariant,
125
+ computed.fontWeight,
126
+ computed.fontSize,
127
+ computed.fontFamily
128
+ ].join(" "),
129
+ fontSize: fontSize,
130
+ lineHeight: this._parseLineHeight(computed.lineHeight, fontSize),
131
+ whiteSpace: computed.whiteSpace,
132
+ paddingLeft: parseFloat(computed.paddingLeft) || 0,
133
+ paddingRight: parseFloat(computed.paddingRight) || 0,
134
+ paddingTop: parseFloat(computed.paddingTop) || 0,
135
+ paddingBottom: parseFloat(computed.paddingBottom) || 0
136
+ }
137
+
138
+ return this._cachedMeasurementStyles
139
+ }
116
140
 
141
+ private _parseLineHeight(lineHeight: string, fontSize: number): number {
142
+ if (lineHeight === "normal") {
143
+ return fontSize * 1.2
144
+ }
145
+ if (lineHeight.endsWith("px")) {
146
+ return parseFloat(lineHeight)
147
+ }
148
+ const numericLineHeight = parseFloat(lineHeight)
149
+ if (!isNaN(numericLineHeight)) {
150
+ return fontSize * numericLineHeight
151
+ }
152
+ return fontSize * 1.2
153
+ }
117
154
 
118
- static textAlignment = {
155
+ private _shouldUseFastMeasurement(): boolean {
156
+ const content = this.text || this.innerHTML
119
157
 
120
- "left": "left",
121
- "center": "center",
122
- "right": "right",
123
- "justify": "justify"
158
+ if (this._innerHTMLKey || this._localizedTextObject) {
159
+ return false
160
+ }
124
161
 
125
- } as const
162
+ if (this.notificationAmount > 0) {
163
+ return false
164
+ }
165
+
166
+ const hasComplexHTML = /<(?!\/?(b|i|em|strong|span|br)\b)[^>]+>/i.test(content)
167
+
168
+ return !hasComplexHTML
169
+ }
170
+
171
+ //#endregion
172
+
173
+ //#region Measurement & Sizing - Public Methods
174
+
175
+ setUseFastMeasurement(useFast: boolean): void {
176
+ this._useFastMeasurement = useFast
177
+ this._intrinsicSizesCache = {}
178
+ }
179
+
180
+ invalidateMeasurementStrategy(): void {
181
+ this._useFastMeasurement = undefined
182
+ this._invalidateMeasurementStyles()
183
+ }
184
+
185
+ //#endregion
186
+
187
+ //#region Getters & Setters - Text Alignment
126
188
 
127
189
  get textAlignment() {
128
190
  // @ts-ignore
@@ -134,81 +196,70 @@ export class UITextView extends UIView {
134
196
  this.style.textAlign = textAlignment
135
197
  }
136
198
 
199
+ //#endregion
200
+
201
+ //#region Getters & Setters - Text Color
137
202
 
138
203
  get textColor() {
139
204
  return this._textColor
140
205
  }
141
206
 
142
207
  set textColor(color: UIColor) {
143
-
144
208
  this._textColor = color || UITextView.defaultTextColor
145
209
  this.style.color = this._textColor.stringValue
146
-
147
210
  }
148
211
 
212
+ //#endregion
213
+
214
+ //#region Getters & Setters - Single Line
149
215
 
150
216
  get isSingleLine() {
151
-
152
217
  return this._isSingleLine
153
-
154
218
  }
155
219
 
156
220
  set isSingleLine(isSingleLine: boolean) {
157
-
158
221
  this._isSingleLine = isSingleLine
159
222
 
160
223
  this._intrinsicHeightCache = new UIObject() as any
161
224
  this._intrinsicWidthCache = new UIObject() as any
162
225
 
163
226
  if (isSingleLine) {
164
-
165
227
  this.style.whiteSpace = "pre"
166
-
167
228
  return
168
-
169
229
  }
170
230
 
171
231
  this.style.whiteSpace = "pre-wrap"
172
-
173
232
  this.invalidateMeasurementStrategy()
174
-
175
233
  }
176
234
 
235
+ //#endregion
236
+
237
+ //#region Getters & Setters - Notification Amount
177
238
 
178
239
  get notificationAmount() {
179
-
180
240
  return this._notificationAmount
181
-
182
241
  }
183
242
 
184
243
  set notificationAmount(notificationAmount: number) {
185
-
186
244
  if (this._notificationAmount == notificationAmount) {
187
-
188
245
  return
189
-
190
246
  }
191
247
 
192
248
  this._notificationAmount = notificationAmount
193
-
194
249
  this.text = this.text
195
-
196
250
  this.setNeedsLayoutUpToRootView()
197
-
198
251
  this.notificationAmountDidChange(notificationAmount)
199
-
200
252
  }
201
253
 
202
254
  notificationAmountDidChange(notificationAmount: number) {
203
-
204
-
205
255
  }
206
256
 
257
+ //#endregion
258
+
259
+ //#region Getters & Setters - Text Content
207
260
 
208
261
  get text() {
209
-
210
262
  return (this._text || this.viewHTMLElement.innerHTML)
211
-
212
263
  }
213
264
 
214
265
  set text(text) {
@@ -227,76 +278,125 @@ export class UITextView extends UIView {
227
278
  this._intrinsicHeightCache = new UIObject() as any
228
279
  this._intrinsicWidthCache = new UIObject() as any
229
280
  }
230
- // Invalidate measurement strategy when text changes significantly
231
- this._useFastMeasurement = undefined;
232
- this._intrinsicSizesCache = {};
281
+
282
+ this._useFastMeasurement = undefined
283
+ this._intrinsicSizesCache = {}
233
284
  this.invalidateMeasurementStrategy()
234
285
  this._invalidateMeasurementStyles()
286
+ this.clearIntrinsicSizeCache()
235
287
 
236
288
  this.setNeedsLayout()
237
-
238
289
  }
239
290
 
240
291
  override set innerHTML(innerHTML: string) {
241
-
242
292
  this.text = innerHTML
243
293
  this.invalidateMeasurementStrategy()
244
-
245
294
  }
246
295
 
247
296
  override get innerHTML() {
248
-
249
297
  return this.viewHTMLElement.innerHTML
250
-
251
298
  }
252
299
 
253
-
254
300
  setText(key: string, defaultString: string, parameters?: { [x: string]: string | UILocalizedTextObject }) {
255
-
256
301
  this.setInnerHTML(key, defaultString, parameters)
257
302
  this.invalidateMeasurementStrategy()
258
-
259
303
  }
260
304
 
305
+ //#endregion
306
+
307
+ //#region Getters & Setters - Font Size
261
308
 
262
309
  get fontSize() {
263
-
264
310
  const style = this.style.fontSize || window.getComputedStyle(this.viewHTMLElement, null).fontSize
265
-
266
311
  const result = (parseFloat(style) * UITextView._pxToPt)
267
-
268
312
  return result
269
-
270
313
  }
271
314
 
272
315
  set fontSize(fontSize: number) {
273
-
274
316
  if (fontSize != this.fontSize) {
275
-
276
317
  this.style.fontSize = "" + fontSize + "pt"
277
318
 
278
319
  this._intrinsicHeightCache = new UIObject() as any
279
- this._intrinsicWidthCache = new UIObject() as any // MEETOD LUUA!!!!
320
+ this._intrinsicWidthCache = new UIObject() as any
280
321
 
281
- this._invalidateMeasurementStyles() // Invalidate when font changes
282
-
322
+ this._invalidateFontCache()
323
+ this._invalidateMeasurementStyles()
324
+ this.clearIntrinsicSizeCache()
283
325
  }
284
-
285
326
  }
286
327
 
287
-
288
-
289
328
  useAutomaticFontSize(minFontSize: number = nil, maxFontSize: number = nil) {
290
-
291
329
  this._automaticFontSizeSelection = YES
292
-
293
330
  this._minFontSize = minFontSize
294
331
  this._maxFontSize = maxFontSize
295
-
296
332
  this.setNeedsLayout()
333
+ }
334
+
335
+ //#endregion
336
+
337
+ //#region Font Caching - Private Methods
338
+
339
+ /**
340
+ * Get a stable cache key for the font without triggering reflow.
341
+ * Only computes font on first access or when font properties change.
342
+ */
343
+ private _getFontCacheKey(): string {
344
+ // Check if font-related properties have changed
345
+ const currentTriggers = {
346
+ fontSize: this.style.fontSize || "",
347
+ fontFamily: this.style.fontFamily || "",
348
+ fontWeight: this.style.fontWeight || "",
349
+ fontStyle: this.style.fontStyle || "",
350
+ styleClasses: this.styleClasses.join(",")
351
+ }
297
352
 
353
+ const hasChanged =
354
+ currentTriggers.fontSize !== this._fontInvalidationTriggers.fontSize ||
355
+ currentTriggers.fontFamily !== this._fontInvalidationTriggers.fontFamily ||
356
+ currentTriggers.fontWeight !== this._fontInvalidationTriggers.fontWeight ||
357
+ currentTriggers.fontStyle !== this._fontInvalidationTriggers.fontStyle ||
358
+ currentTriggers.styleClasses !== this._fontInvalidationTriggers.styleClasses
359
+
360
+ if (!this._cachedFontKey || hasChanged) {
361
+ // Only access computedStyle when we know something changed
362
+ const computed = this.computedStyle
363
+ this._cachedFontKey = [
364
+ computed.fontStyle,
365
+ computed.fontVariant,
366
+ computed.fontWeight,
367
+ computed.fontSize,
368
+ computed.fontFamily
369
+ ].join("_").replace(/[.\s]/g, "_")
370
+
371
+ this._fontInvalidationTriggers = currentTriggers
372
+ }
373
+
374
+ return this._cachedFontKey
298
375
  }
299
376
 
377
+ /**
378
+ * Invalidate font cache when font properties change
379
+ */
380
+ private _invalidateFontCache(): void {
381
+ this._cachedFontKey = undefined
382
+ }
383
+
384
+ //#endregion
385
+
386
+ //#region Static Methods
387
+
388
+ static _determinePXAndPTRatios() {
389
+ if (UITextView._ptToPx) {
390
+ return
391
+ }
392
+
393
+ const o = document.createElement("div")
394
+ o.style.width = "1000pt"
395
+ document.body.appendChild(o)
396
+ UITextView._ptToPx = o.clientWidth / 1000
397
+ document.body.removeChild(o)
398
+ UITextView._pxToPt = 1 / UITextView._ptToPx
399
+ }
300
400
 
301
401
  static automaticallyCalculatedFontSize(
302
402
  bounds: UIRectangle,
@@ -305,7 +405,6 @@ export class UITextView extends UIView {
305
405
  minFontSize?: number,
306
406
  maxFontSize?: number
307
407
  ) {
308
-
309
408
  minFontSize = FIRST(minFontSize, 1)
310
409
  maxFontSize = FIRST(maxFontSize, 100000000000)
311
410
 
@@ -328,305 +427,121 @@ export class UITextView extends UIView {
328
427
  }
329
428
 
330
429
  return maxFittingFontSize
331
-
332
430
  }
333
431
 
432
+ //#endregion
334
433
 
335
- override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {
336
-
337
- super.didReceiveBroadcastEvent(event)
338
-
339
- }
434
+ //#region Instance Properties - Text Content
340
435
 
436
+ _text?: string
437
+ textPrefix = ""
438
+ textSuffix = ""
439
+ _notificationAmount = 0
341
440
 
342
- override willMoveToSuperview(superview: UIView) {
343
-
344
- super.willMoveToSuperview(superview)
345
-
346
- }
441
+ //#endregion
347
442
 
443
+ //#region Instance Properties - Styling
348
444
 
349
- override layoutSubviews() {
350
-
351
- super.layoutSubviews()
352
-
353
-
354
- if (this._automaticFontSizeSelection) {
355
-
356
- this.fontSize = UITextView.automaticallyCalculatedFontSize(
357
- new UIRectangle(0, 0, 1 *
358
- this.viewHTMLElement.offsetHeight, 1 *
359
- this.viewHTMLElement.offsetWidth),
360
- this.intrinsicContentSize(),
361
- this.fontSize,
362
- this._minFontSize,
363
- this._maxFontSize
364
- )
365
-
366
-
367
- }
368
-
369
-
445
+ _textColor: UIColor = UITextView.defaultTextColor
446
+ _textAlignment?: ValueOf<typeof UITextView.textAlignment>
447
+ _isSingleLine = YES
448
+
449
+ //#endregion
450
+
451
+ //#region Instance Properties - Font & Sizing
452
+
453
+ _minFontSize?: number
454
+ _maxFontSize?: number
455
+ _automaticFontSizeSelection = NO
456
+
457
+ // Cache for the computed font string
458
+ private _cachedFontKey?: string
459
+ private _fontInvalidationTriggers = {
460
+ fontSize: this.style.fontSize || "",
461
+ fontFamily: this.style.fontFamily || "",
462
+ fontWeight: this.style.fontWeight || "",
463
+ fontStyle: this.style.fontStyle || "",
464
+ styleClasses: this.styleClasses.join(",")
370
465
  }
371
466
 
467
+ //#endregion
468
+
469
+ //#region Instance Properties - Caching & Performance
470
+
471
+ changesOften = NO
472
+
473
+ // Local cache for this instance if the label changes often
474
+ _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
475
+ _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
476
+
477
+ private _useFastMeasurement: boolean | undefined
478
+ private _cachedMeasurementStyles: TextMeasurementStyle | undefined
479
+
480
+ override usesVirtualLayoutingForIntrinsicSizing = NO
481
+
482
+ //#endregion
483
+
484
+
372
485
 
373
486
  override intrinsicContentHeight(constrainingWidth = 0) {
374
487
 
375
- const keyPath = (this.viewHTMLElement.innerHTML + "_csf_" + this.computedStyle.font).replace(new RegExp(
376
- "\\.",
377
- "g"
378
- ), "_") + "." +
488
+ const keyPath = (this.viewHTMLElement.innerHTML + "_csf_" + this._getFontCacheKey()) + "." +
379
489
  ("" + constrainingWidth).replace(new RegExp("\\.", "g"), "_")
380
490
 
381
491
  let cacheObject = UITextView._intrinsicHeightCache
382
492
 
383
493
  if (this.changesOften) {
384
-
385
- // @ts-ignore
386
494
  cacheObject = this._intrinsicHeightCache
387
-
388
-
389
495
  }
390
496
 
391
-
392
497
  var result = cacheObject.valueForKeyPath(keyPath)
393
498
 
394
-
395
499
  if (IS_LIKE_NULL(result)) {
396
-
397
500
  result = super.intrinsicContentHeight(constrainingWidth)
398
-
399
501
  cacheObject.setValueForKeyPath(keyPath, result)
400
-
401
-
402
502
  }
403
503
 
504
+ if (isNaN(result) || (!result && !this.text)) {
505
+ result = super.intrinsicContentHeight(constrainingWidth)
506
+ cacheObject.setValueForKeyPath(keyPath, result)
507
+ }
404
508
 
405
509
  return result
406
-
407
510
  }
408
511
 
409
512
  override intrinsicContentWidth(constrainingHeight = 0) {
410
513
 
411
- const keyPath = (this.viewHTMLElement.innerHTML + "_csf_" + this.computedStyle.font).replace(new RegExp(
412
- "\\.",
413
- "g"
414
- ), "_") + "." +
514
+ const keyPath = (this.viewHTMLElement.innerHTML + "_csf_" + this._getFontCacheKey()) + "." +
415
515
  ("" + constrainingHeight).replace(new RegExp("\\.", "g"), "_")
416
516
 
417
517
  let cacheObject = UITextView._intrinsicWidthCache
418
518
 
419
519
  if (this.changesOften) {
420
-
421
- // @ts-ignore
422
520
  cacheObject = this._intrinsicWidthCache
423
-
424
-
425
521
  }
426
522
 
427
-
428
523
  var result = cacheObject.valueForKeyPath(keyPath)
429
524
 
430
-
431
525
  if (IS_LIKE_NULL(result)) {
432
-
433
526
  result = super.intrinsicContentWidth(constrainingHeight)
434
-
435
527
  cacheObject.setValueForKeyPath(keyPath, result)
436
-
437
-
438
528
  }
439
529
 
440
-
441
530
  return result
442
-
443
- }
444
-
445
-
446
- // Call this when styles change (fontSize, padding, etc.)
447
- private _invalidateMeasurementStyles(): void {
448
- this._cachedMeasurementStyles = undefined;
449
- UITextMeasurement.invalidateElement(this.viewHTMLElement);
450
- this._intrinsicSizesCache = {};
451
- }
452
-
453
- // Extract styles ONCE and cache them (avoids getComputedStyle)
454
- private _getMeasurementStyles(): TextMeasurementStyle {
455
- if (this._cachedMeasurementStyles) {
456
- return this._cachedMeasurementStyles;
457
- }
458
-
459
- // Only call getComputedStyle once and cache the result
460
- const computed = window.getComputedStyle(this.viewHTMLElement);
461
- const fontSize = parseFloat(computed.fontSize);
462
-
463
- this._cachedMeasurementStyles = {
464
- font: [
465
- computed.fontStyle,
466
- computed.fontVariant,
467
- computed.fontWeight,
468
- computed.fontSize,
469
- computed.fontFamily
470
- ].join(' '),
471
- fontSize: fontSize,
472
- lineHeight: this._parseLineHeight(computed.lineHeight, fontSize),
473
- whiteSpace: computed.whiteSpace,
474
- paddingLeft: parseFloat(computed.paddingLeft) || 0,
475
- paddingRight: parseFloat(computed.paddingRight) || 0,
476
- paddingTop: parseFloat(computed.paddingTop) || 0,
477
- paddingBottom: parseFloat(computed.paddingBottom) || 0
478
- };
479
-
480
- return this._cachedMeasurementStyles;
481
- }
482
-
483
- private _parseLineHeight(lineHeight: string, fontSize: number): number {
484
- if (lineHeight === 'normal') {
485
- return fontSize * 1.2;
486
- }
487
- if (lineHeight.endsWith('px')) {
488
- return parseFloat(lineHeight);
489
- }
490
- const numericLineHeight = parseFloat(lineHeight);
491
- if (!isNaN(numericLineHeight)) {
492
- return fontSize * numericLineHeight;
493
- }
494
- return fontSize * 1.2;
495
- }
496
-
497
- // Override the intrinsic size method
498
- override intrinsicContentSizeWithConstraints(
499
- constrainingHeight: number = 0,
500
- constrainingWidth: number = 0
501
- ): UIRectangle {
502
- const cacheKey = "h_" + constrainingHeight + "__w_" + constrainingWidth;
503
- const cachedResult = this._intrinsicSizesCache[cacheKey];
504
- if (cachedResult) {
505
- return cachedResult;
506
- }
507
-
508
- // Determine measurement strategy
509
- const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement();
510
-
511
- let result: UIRectangle;
512
-
513
- if (shouldUseFastPath) {
514
- // Fast path: canvas-based measurement with pre-extracted styles
515
- const styles = this._getMeasurementStyles();
516
- const size = UITextMeasurement.calculateTextSize(
517
- this.viewHTMLElement,
518
- this.text || this.innerHTML,
519
- constrainingWidth || undefined,
520
- constrainingHeight || undefined,
521
- styles // Pass pre-computed styles to avoid getComputedStyle!
522
- );
523
- result = new UIRectangle(0, 0, size.height, size.width);
524
- } else {
525
- // Fallback: original DOM-based measurement for complex content
526
- result = super.intrinsicContentSizeWithConstraints(constrainingHeight, constrainingWidth);
527
- }
528
-
529
- this._intrinsicSizesCache[cacheKey] = result.copy();
530
- return result;
531
- }
532
-
533
- // Helper to determine if we can use fast measurement
534
- private _shouldUseFastMeasurement(): boolean {
535
- const content = this.text || this.innerHTML;
536
-
537
- // If using dynamic innerHTML with parameters, use DOM measurement
538
- if (this._innerHTMLKey || this._localizedTextObject) {
539
- return false;
540
- }
541
-
542
- // Check for notification badges
543
- if (this.notificationAmount > 0) {
544
- return false; // Has span with colored text
545
- }
546
-
547
- // Check content complexity
548
- const hasComplexHTML = /<(?!\/?(b|i|em|strong|span|br)\b)[^>]+>/i.test(content);
549
-
550
- return !hasComplexHTML;
551
531
  }
552
532
 
553
- // Optional: Allow manual override for specific instances
554
- setUseFastMeasurement(useFast: boolean): void {
555
- this._useFastMeasurement = useFast;
556
- this._intrinsicSizesCache = {};
557
- }
558
533
 
559
- // Optional: Force re-evaluation of measurement strategy
560
- invalidateMeasurementStrategy(): void {
561
- this._useFastMeasurement = undefined;
562
- this._invalidateMeasurementStyles();
534
+ // Override addStyleClass to invalidate font cache
535
+ override addStyleClass(styleClass: string) {
536
+ super.addStyleClass(styleClass)
537
+ this._invalidateFontCache()
563
538
  }
564
539
 
565
-
566
-
567
-
568
-
569
- override intrinsicContentSize() {
570
-
571
- // This works but is slow
572
- const result = this.intrinsicContentSizeWithConstraints(nil, nil)
573
-
574
- return result
575
-
540
+ // Override removeStyleClass to invalidate font cache
541
+ override removeStyleClass(styleClass: string) {
542
+ super.removeStyleClass(styleClass)
543
+ this._invalidateFontCache()
576
544
  }
577
545
 
578
546
 
579
547
  }
580
-
581
-
582
- UITextView._determinePXAndPTRatios()
583
-
584
-
585
- // /**
586
- // * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
587
- // *
588
- // * @param {String} text The text to be rendered.
589
- // * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
590
- // *
591
- // * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
592
- // */
593
- // function getTextMetrics(text, font) {
594
- // // re-use canvas object for better performance
595
- // var canvas = getTextMetrics.canvas || (getTextMetrics.canvas = document.createElement("canvas"));
596
- // var context = canvas.getContext("2d");
597
- // context.font = font;
598
- // var metrics = context.measureText(text);
599
- // return metrics;
600
- // }
601
-
602
-
603
-
604
-
605
-
606
-
607
-
608
-
609
-
610
-
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
-
619
-
620
-
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
-