uicore-ts 1.1.310 → 1.1.315
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/UILayoutDebugger.d.ts +393 -0
- package/compiledScripts/UILayoutDebugger.js +2535 -0
- package/compiledScripts/UILayoutDebugger.js.map +7 -0
- package/compiledScripts/UITextView.d.ts +17 -0
- package/compiledScripts/UITextView.js +22 -1
- package/compiledScripts/UITextView.js.map +2 -2
- package/compiledScripts/UIView.d.ts +32 -0
- package/compiledScripts/UIView.js +33 -8
- package/compiledScripts/UIView.js.map +2 -2
- package/compiledScripts/UIViewController.js +8 -2
- package/compiledScripts/UIViewController.js.map +2 -2
- package/package.json +1 -1
- package/scripts/UILayoutDebugger.ts +3252 -0
- package/scripts/UITextView.ts +45 -1
- package/scripts/UIView.ts +75 -1
- package/scripts/UIViewController.ts +9 -2
package/scripts/UITextView.ts
CHANGED
|
@@ -12,6 +12,22 @@ export class UITextView extends UIView {
|
|
|
12
12
|
|
|
13
13
|
static defaultTextColor = UIColor.blackColor
|
|
14
14
|
static notificationTextColor = UIColor.redColor
|
|
15
|
+
static attentionRequiredColor = new UIColor("#f59e0b")
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Override this to customise the attention indicator HTML that is appended
|
|
19
|
+
* to a label's text when `attentionRequired` is `true`.
|
|
20
|
+
*
|
|
21
|
+
* The default renders an amber `●` dot:
|
|
22
|
+
* ```
|
|
23
|
+
* UITextView.renderAttentionIndicator = () =>
|
|
24
|
+
* `<span style="color: #f59e0b; margin-left: 4px;">●</span>`
|
|
25
|
+
* ```
|
|
26
|
+
* Call sites never need to change — only this one function needs to be
|
|
27
|
+
* replaced at app startup to restyle every attention indicator globally.
|
|
28
|
+
*/
|
|
29
|
+
static renderAttentionIndicator: () => string = () =>
|
|
30
|
+
"<span style=\"color: " + UITextView.attentionRequiredColor.stringValue + "; margin-left: 4px;\">●</span>"
|
|
15
31
|
|
|
16
32
|
// Global caches for all UILabels
|
|
17
33
|
static _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any
|
|
@@ -283,6 +299,10 @@ export class UITextView extends UIView {
|
|
|
283
299
|
return false
|
|
284
300
|
}
|
|
285
301
|
|
|
302
|
+
if (this._attentionRequired) {
|
|
303
|
+
return false
|
|
304
|
+
}
|
|
305
|
+
|
|
286
306
|
const hasComplexHTML = /<(?!\/?(b|i|em|strong|span)\b)[^>]+>/i.test(content)
|
|
287
307
|
|
|
288
308
|
if (hasComplexHTML) {
|
|
@@ -401,6 +421,24 @@ export class UITextView extends UIView {
|
|
|
401
421
|
|
|
402
422
|
//#endregion
|
|
403
423
|
|
|
424
|
+
//#region Getters & Setters - Attention Required
|
|
425
|
+
|
|
426
|
+
get attentionRequired() {
|
|
427
|
+
return this._attentionRequired
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
set attentionRequired(attentionRequired: boolean) {
|
|
431
|
+
if (this._attentionRequired == attentionRequired) {
|
|
432
|
+
return
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this._attentionRequired = attentionRequired
|
|
436
|
+
this.text = this.text
|
|
437
|
+
this.setNeedsLayoutUpToRootView()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
//#endregion
|
|
441
|
+
|
|
404
442
|
//#region Getters & Setters - Text Content
|
|
405
443
|
|
|
406
444
|
get text() {
|
|
@@ -415,11 +453,16 @@ export class UITextView extends UIView {
|
|
|
415
453
|
(" (" + this.notificationAmount + ")").bold() + "</span>"
|
|
416
454
|
}
|
|
417
455
|
|
|
456
|
+
var attentionDot = ""
|
|
457
|
+
if (this._attentionRequired) {
|
|
458
|
+
attentionDot = UITextView.renderAttentionIndicator()
|
|
459
|
+
}
|
|
460
|
+
|
|
418
461
|
const displayText = this.thousandsSeparator !== null
|
|
419
462
|
? UITextView.applyThousandsSeparatorToNumericalString(text, this.thousandsSeparator)
|
|
420
463
|
: text
|
|
421
464
|
|
|
422
|
-
const newInnerHTML = this.textPrefix + FIRST(displayText, "") + this.textSuffix + notificationText
|
|
465
|
+
const newInnerHTML = this.textPrefix + FIRST(displayText, "") + this.textSuffix + notificationText + attentionDot
|
|
423
466
|
|
|
424
467
|
if (this.textElementView.viewHTMLElement.innerHTML !== newInnerHTML) {
|
|
425
468
|
this.textElementView.viewHTMLElement.innerHTML = newInnerHTML
|
|
@@ -626,6 +669,7 @@ export class UITextView extends UIView {
|
|
|
626
669
|
textPrefix = ""
|
|
627
670
|
textSuffix = ""
|
|
628
671
|
_notificationAmount = 0
|
|
672
|
+
_attentionRequired = false
|
|
629
673
|
|
|
630
674
|
_thousandsSeparator: string | null = null
|
|
631
675
|
|
package/scripts/UIView.ts
CHANGED
|
@@ -6,6 +6,7 @@ import "./UICoreExtensions"
|
|
|
6
6
|
import type { UIDialogView } from "./UIDialogView"
|
|
7
7
|
import { UILocalizedTextObject } from "./UIInterfaces"
|
|
8
8
|
import { UILayoutCycleTracer } from "./UILayoutCycleTracer"
|
|
9
|
+
import { UILayoutDebugger } from "./UILayoutDebugger"
|
|
9
10
|
import {
|
|
10
11
|
FIRST,
|
|
11
12
|
FIRST_OR_NIL,
|
|
@@ -2033,6 +2034,7 @@ export class UIView extends UIObject {
|
|
|
2033
2034
|
}
|
|
2034
2035
|
|
|
2035
2036
|
window.UILayoutCycleTracer?.willBeginLayoutPass()
|
|
2037
|
+
window.UILayoutDebugger?.willBeginLayoutPass(UIView._viewsToLayout)
|
|
2036
2038
|
|
|
2037
2039
|
const maxIterations = 10
|
|
2038
2040
|
let iteration = 0
|
|
@@ -2041,6 +2043,7 @@ export class UIView extends UIObject {
|
|
|
2041
2043
|
while (UIView._viewsToLayout.length > 0 && iteration < maxIterations) {
|
|
2042
2044
|
|
|
2043
2045
|
window.UILayoutCycleTracer?.willBeginIteration(iteration)
|
|
2046
|
+
window.UILayoutDebugger?.willBeginIteration(iteration)
|
|
2044
2047
|
|
|
2045
2048
|
const viewsToProcess = UIView._viewsToLayout
|
|
2046
2049
|
UIView._viewsToLayout = []
|
|
@@ -2069,15 +2072,21 @@ export class UIView extends UIObject {
|
|
|
2069
2072
|
|
|
2070
2073
|
for (let i = 0; i < sortedViews.length; i++) {
|
|
2071
2074
|
const view = sortedViews[i]
|
|
2075
|
+
window.UILayoutDebugger?.willLayoutView(view)
|
|
2076
|
+
if (window.UILayoutDebugger?._shouldHitBreakpoint(view)) {
|
|
2077
|
+
const breakpointOnThisLine = "Add a breakpoint on this line to step through layout."
|
|
2078
|
+
}
|
|
2072
2079
|
view.layoutIfNeeded()
|
|
2073
2080
|
layoutCounts.set(view, (layoutCounts.get(view) || 0) + 1)
|
|
2074
2081
|
window.UILayoutCycleTracer?.didLayoutView(view)
|
|
2082
|
+
window.UILayoutDebugger?.didLayoutView(view)
|
|
2075
2083
|
}
|
|
2076
2084
|
|
|
2077
2085
|
iteration++
|
|
2078
2086
|
}
|
|
2079
2087
|
|
|
2080
2088
|
window.UILayoutCycleTracer?.didFinishLayoutPass(iteration)
|
|
2089
|
+
window.UILayoutDebugger?.didFinishLayoutPass(iteration)
|
|
2081
2090
|
|
|
2082
2091
|
// console.log(iteration + " iterations to finish layout")
|
|
2083
2092
|
|
|
@@ -2097,6 +2106,7 @@ export class UIView extends UIObject {
|
|
|
2097
2106
|
this.clearIntrinsicSizeCache()
|
|
2098
2107
|
|
|
2099
2108
|
window.UILayoutCycleTracer?.viewDidCallSetNeedsLayout(this)
|
|
2109
|
+
window.UILayoutDebugger?.viewDidCallSetNeedsLayout(this)
|
|
2100
2110
|
|
|
2101
2111
|
// // Auto-propagate if intrinsic height changed
|
|
2102
2112
|
// if (IS(this.superview) && this.superview.usesVirtualLayoutingForIntrinsicSizing) {
|
|
@@ -2133,7 +2143,7 @@ export class UIView extends UIObject {
|
|
|
2133
2143
|
|
|
2134
2144
|
try {
|
|
2135
2145
|
|
|
2136
|
-
if (this.bounds.width < 0) {
|
|
2146
|
+
if (this.bounds.width < 0 || (!this.isVirtualLayouting && !this.isMemberOfViewTree)) {
|
|
2137
2147
|
return
|
|
2138
2148
|
}
|
|
2139
2149
|
|
|
@@ -2167,12 +2177,14 @@ export class UIView extends UIObject {
|
|
|
2167
2177
|
|
|
2168
2178
|
this.applyClassesAndStyles()
|
|
2169
2179
|
|
|
2180
|
+
window.UILayoutDebugger?.willSetSubviewFrames(this)
|
|
2170
2181
|
for (let i = 0; i < this.subviews?.length; i++) {
|
|
2171
2182
|
|
|
2172
2183
|
const subview = this.subviews[i]
|
|
2173
2184
|
subview.calculateAndSetViewFrame()
|
|
2174
2185
|
|
|
2175
2186
|
}
|
|
2187
|
+
window.UILayoutDebugger?.didSetSubviewFrames(this)
|
|
2176
2188
|
|
|
2177
2189
|
// if (this._loadingView && this._loadingView.superview == this) {
|
|
2178
2190
|
// this._loadingView.setFrame(this.bounds)
|
|
@@ -4111,9 +4123,11 @@ export class UIView extends UIObject {
|
|
|
4111
4123
|
UIView._sharedIntrinsicSizeCaches.set(this.sharedIntrinsicSizeCacheIdentifier, bucket)
|
|
4112
4124
|
}
|
|
4113
4125
|
bucket[cacheKey] = size.copy()
|
|
4126
|
+
window.UILayoutDebugger?.didSetCachedIntrinsicSize(this, cacheKey, size)
|
|
4114
4127
|
return
|
|
4115
4128
|
}
|
|
4116
4129
|
this._intrinsicSizesCache[cacheKey] = size.copy()
|
|
4130
|
+
window.UILayoutDebugger?.didSetCachedIntrinsicSize(this, cacheKey, size)
|
|
4117
4131
|
}
|
|
4118
4132
|
|
|
4119
4133
|
// clearIntrinsicSizeCache(): void {
|
|
@@ -4363,6 +4377,66 @@ export class UIView extends UIObject {
|
|
|
4363
4377
|
}
|
|
4364
4378
|
|
|
4365
4379
|
|
|
4380
|
+
/**
|
|
4381
|
+
* ⚠️ NUCLEAR OPTION — DO NOT CALL IN NORMAL CODE ⚠️
|
|
4382
|
+
*
|
|
4383
|
+
* Performs a full-scorched-earth cache purge across the entire view subtree rooted
|
|
4384
|
+
* at `this`, then drives an immediate synchronous layout pass, as if the app had
|
|
4385
|
+
* just cold-started.
|
|
4386
|
+
*
|
|
4387
|
+
* What gets destroyed:
|
|
4388
|
+
* • Every per-instance intrinsic-size cache entry (`_intrinsicSizesCache`)
|
|
4389
|
+
* • Every shared intrinsic-size cache bucket (`UIView._sharedIntrinsicSizeCaches`)
|
|
4390
|
+
* • Every frame cache (`_frameCache`) and virtual-layout frame cache
|
|
4391
|
+
* (`_frameCacheForVirtualLayouting`) on every view in the subtree
|
|
4392
|
+
* • Every UITextMeasurement style cache (canvas glyph metrics,
|
|
4393
|
+
* computed-style snapshots, etc.)
|
|
4394
|
+
*
|
|
4395
|
+
* After the purge every view in the subtree is unconditionally marked dirty via
|
|
4396
|
+
* `setNeedsLayout()`, then `UIView.layoutViewsIfNeeded()` is called immediately
|
|
4397
|
+
* to flush the queue rather than waiting for the next rAF tick.
|
|
4398
|
+
*
|
|
4399
|
+
* Legitimate uses (exhaustive list):
|
|
4400
|
+
* - Recovery from a confirmed cache-corruption bug while a proper fix ships
|
|
4401
|
+
* - Test harness reset between isolated rendering assertions
|
|
4402
|
+
* - Post-hot-module-replacement refresh in development tooling
|
|
4403
|
+
*
|
|
4404
|
+
* Do NOT use this to fix a layout glitch you don't understand.
|
|
4405
|
+
* Fix the root cause instead. If you find yourself calling this in production
|
|
4406
|
+
* code for any reason other than the above, that is a bug — file it.
|
|
4407
|
+
*
|
|
4408
|
+
* Complexity: O(n) where n = total views in the subtree.
|
|
4409
|
+
* All shared caches are wiped globally, not just for the subtree.
|
|
4410
|
+
*/
|
|
4411
|
+
performForcedSubtreeLayout(): void {
|
|
4412
|
+
|
|
4413
|
+
// 1. Nuke all text measurement caches globally — these are shared across
|
|
4414
|
+
// the entire document and cannot be scoped to a subtree.
|
|
4415
|
+
UITextMeasurement.clearCaches()
|
|
4416
|
+
|
|
4417
|
+
// 2. Wipe all shared intrinsic-size cache buckets globally. These are
|
|
4418
|
+
// keyed by sharedIntrinsicSizeCacheIdentifier and may be shared across
|
|
4419
|
+
// views outside this subtree, but stale global state is worse than a
|
|
4420
|
+
// cold remeasure of an unrelated view.
|
|
4421
|
+
UIView.invalidateAllSharedIntrinsicSizeCaches()
|
|
4422
|
+
|
|
4423
|
+
// 3. Walk every view in the subtree and obliterate all per-instance caches,
|
|
4424
|
+
// then unconditionally schedule it for layout.
|
|
4425
|
+
this.forEachViewInSubtree(view => {
|
|
4426
|
+
view._intrinsicSizesCache = {}
|
|
4427
|
+
view._frameCache = undefined
|
|
4428
|
+
view._frameCacheForVirtualLayouting = undefined
|
|
4429
|
+
view.setNeedsLayout()
|
|
4430
|
+
})
|
|
4431
|
+
|
|
4432
|
+
// 4. Drive the layout queue right now, synchronously — don't wait for the
|
|
4433
|
+
// scheduled rAF tick. Callers of this method expect the tree to be
|
|
4434
|
+
// fully laid out by the time the call returns.
|
|
4435
|
+
UIView.layoutViewsIfNeeded()
|
|
4436
|
+
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
|
|
4366
4440
|
}
|
|
4367
4441
|
|
|
4368
4442
|
|
|
@@ -187,6 +187,9 @@ export class UIViewController extends UIObject {
|
|
|
187
187
|
if (IS(controller.view)) {
|
|
188
188
|
controller.view.removeFromSuperview()
|
|
189
189
|
}
|
|
190
|
+
else {
|
|
191
|
+
var asd = 1
|
|
192
|
+
}
|
|
190
193
|
controller.viewDidDisappear()
|
|
191
194
|
|
|
192
195
|
}
|
|
@@ -197,8 +200,12 @@ export class UIViewController extends UIObject {
|
|
|
197
200
|
controller = FIRST_OR_NIL(controller)
|
|
198
201
|
containerView = FIRST_OR_NIL(containerView)
|
|
199
202
|
controller.viewWillAppear()
|
|
200
|
-
this.
|
|
201
|
-
|
|
203
|
+
if (!this.hasChildViewController(controller)) {
|
|
204
|
+
controller.willMoveToParentViewController(this)
|
|
205
|
+
this.childViewControllers.push(controller)
|
|
206
|
+
containerView.addSubview(controller.view)
|
|
207
|
+
controller.didMoveToParentViewController(this)
|
|
208
|
+
}
|
|
202
209
|
|
|
203
210
|
controller.handleRouteRecursively(UIRoute.currentRoute)
|
|
204
211
|
|