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.
- package/compiledScripts/UICore.js +3 -0
- package/compiledScripts/UICore.js.map +2 -2
- package/compiledScripts/UIRectangle.d.ts +4 -0
- package/compiledScripts/UIRectangle.js +19 -4
- package/compiledScripts/UIRectangle.js.map +2 -2
- package/compiledScripts/UITableView.d.ts +34 -0
- package/compiledScripts/UITableView.js +214 -25
- package/compiledScripts/UITableView.js.map +3 -3
- package/compiledScripts/UITextView.d.ts +49 -39
- package/compiledScripts/UITextView.js +137 -126
- package/compiledScripts/UITextView.js.map +2 -2
- package/compiledScripts/UIView.d.ts +40 -2
- package/compiledScripts/UIView.js +242 -89
- package/compiledScripts/UIView.js.map +2 -2
- package/package.json +1 -1
- package/scripts/UICore.ts +5 -0
- package/scripts/UIRectangle.ts +35 -5
- package/scripts/UITableView.ts +330 -93
- package/scripts/UITextView.ts +273 -358
- package/scripts/UIView.ts +361 -90
package/scripts/UITextView.ts
CHANGED
|
@@ -1,51 +1,49 @@
|
|
|
1
1
|
import { UIColor } from "./UIColor"
|
|
2
2
|
import { UILocalizedTextObject } from "./UIInterfaces"
|
|
3
|
-
import { FIRST,
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
155
|
+
private _shouldUseFastMeasurement(): boolean {
|
|
156
|
+
const content = this.text || this.innerHTML
|
|
119
157
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"justify": "justify"
|
|
158
|
+
if (this._innerHTMLKey || this._localizedTextObject) {
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
124
161
|
|
|
125
|
-
|
|
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
|
-
|
|
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
|
|
320
|
+
this._intrinsicWidthCache = new UIObject() as any
|
|
280
321
|
|
|
281
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
super.willMoveToSuperview(superview)
|
|
345
|
-
|
|
346
|
-
}
|
|
441
|
+
//#endregion
|
|
347
442
|
|
|
443
|
+
//#region Instance Properties - Styling
|
|
348
444
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
this.
|
|
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
|
-
|