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