@solidtv/renderer 1.5.0 → 1.5.2
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/dist/src/common/EventEmitter.d.ts +8 -0
- package/dist/src/common/EventEmitter.js +20 -0
- package/dist/src/common/EventEmitter.js.map +1 -1
- package/dist/src/core/Autosizer.js +3 -1
- package/dist/src/core/Autosizer.js.map +1 -1
- package/dist/src/core/CoreNode.d.ts +39 -0
- package/dist/src/core/CoreNode.js +115 -25
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.js +11 -4
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/Stage.d.ts +6 -0
- package/dist/src/core/Stage.js +9 -0
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.d.ts +39 -2
- package/dist/src/core/TextureMemoryManager.js +53 -1
- package/dist/src/core/TextureMemoryManager.js.map +1 -1
- package/dist/src/core/renderers/canvas/CanvasRenderer.js +5 -1
- package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +5 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +28 -0
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +70 -10
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +23 -0
- package/dist/src/main-api/Renderer.js +2 -0
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/EventEmitter.ts +21 -0
- package/src/core/Autosizer.ts +3 -1
- package/src/core/CoreNode.test.ts +425 -0
- package/src/core/CoreNode.ts +146 -26
- package/src/core/CoreTextNode.test.ts +62 -0
- package/src/core/CoreTextNode.ts +13 -4
- package/src/core/Stage.ts +9 -0
- package/src/core/TextureMemoryManager.test.ts +272 -5
- package/src/core/TextureMemoryManager.ts +59 -2
- package/src/core/renderers/canvas/CanvasRenderer.ts +7 -1
- package/src/core/renderers/webgl/WebGlRenderer.ts +6 -1
- package/src/core/renderers/webgl/WebGlShaderProgram.ts +78 -15
- package/src/core/renderers/webgl/WebGlShaderProgram.uniformDedup.test.ts +233 -0
- package/src/main-api/Renderer.ts +26 -0
|
@@ -7,6 +7,8 @@ import { type TextureOptions } from './CoreTextureManager.js';
|
|
|
7
7
|
import { createBound } from './lib/utils.js';
|
|
8
8
|
import { ImageTexture } from './textures/ImageTexture.js';
|
|
9
9
|
import { Matrix3d } from './lib/Matrix3d.js';
|
|
10
|
+
import { EventEmitter } from '../common/EventEmitter.js';
|
|
11
|
+
import { premultiplyColorABGR } from '../utils.js';
|
|
10
12
|
|
|
11
13
|
describe('set color()', () => {
|
|
12
14
|
const defaultProps = (overrides?: Partial<CoreNodeProps>): CoreNodeProps => ({
|
|
@@ -23,6 +25,7 @@ describe('set color()', () => {
|
|
|
23
25
|
colorTl: 0,
|
|
24
26
|
colorTop: 0,
|
|
25
27
|
colorTr: 0,
|
|
28
|
+
placeholderColor: 0,
|
|
26
29
|
h: 0,
|
|
27
30
|
mount: 0,
|
|
28
31
|
mountX: 0,
|
|
@@ -1180,4 +1183,426 @@ describe('set color()', () => {
|
|
|
1180
1183
|
expect(node.clippingRect.h).toBe(30);
|
|
1181
1184
|
});
|
|
1182
1185
|
});
|
|
1186
|
+
|
|
1187
|
+
describe('RecalcUniforms scoping', () => {
|
|
1188
|
+
const makeAttachedNode = () => {
|
|
1189
|
+
// Fresh stage per node: earlier tests can mutate the shared stage
|
|
1190
|
+
// mock's bound objects through by-reference strictBound assignment
|
|
1191
|
+
// in createRenderBounds.
|
|
1192
|
+
const localStage = mock<Stage>({
|
|
1193
|
+
strictBound: createBound(0, 0, 200, 200),
|
|
1194
|
+
preloadBound: createBound(0, 0, 200, 200),
|
|
1195
|
+
defaultTexture: {
|
|
1196
|
+
state: 'loaded',
|
|
1197
|
+
},
|
|
1198
|
+
renderer: mock<CoreRenderer>() as CoreRenderer,
|
|
1199
|
+
});
|
|
1200
|
+
const parent = new CoreNode(localStage, defaultProps());
|
|
1201
|
+
parent.globalTransform = Matrix3d.identity();
|
|
1202
|
+
parent.worldAlpha = 1;
|
|
1203
|
+
|
|
1204
|
+
const node = new CoreNode(
|
|
1205
|
+
localStage,
|
|
1206
|
+
defaultProps({ parent, w: 100, h: 100 }),
|
|
1207
|
+
);
|
|
1208
|
+
node.alpha = 1;
|
|
1209
|
+
return node;
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
it('should not set RecalcUniforms on pure translation', () => {
|
|
1213
|
+
const node = makeAttachedNode();
|
|
1214
|
+
node.update(0, clippingRect);
|
|
1215
|
+
|
|
1216
|
+
node.x = 50;
|
|
1217
|
+
node.y = 25;
|
|
1218
|
+
|
|
1219
|
+
expect(node.updateType & UpdateType.Local).toBe(UpdateType.Local);
|
|
1220
|
+
expect(node.updateType & UpdateType.RecalcUniforms).toBe(0);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
it('should set RecalcUniforms when w changes', () => {
|
|
1224
|
+
const node = makeAttachedNode();
|
|
1225
|
+
node.update(0, clippingRect);
|
|
1226
|
+
|
|
1227
|
+
node.w = 150;
|
|
1228
|
+
|
|
1229
|
+
expect(node.updateType & UpdateType.RecalcUniforms).toBe(
|
|
1230
|
+
UpdateType.RecalcUniforms,
|
|
1231
|
+
);
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
it('should set RecalcUniforms when h changes', () => {
|
|
1235
|
+
const node = makeAttachedNode();
|
|
1236
|
+
node.update(0, clippingRect);
|
|
1237
|
+
|
|
1238
|
+
node.h = 75;
|
|
1239
|
+
|
|
1240
|
+
expect(node.updateType & UpdateType.RecalcUniforms).toBe(
|
|
1241
|
+
UpdateType.RecalcUniforms,
|
|
1242
|
+
);
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
it('should run the shader updater on resize but not on translation', () => {
|
|
1246
|
+
const node = makeAttachedNode();
|
|
1247
|
+
const shader = {
|
|
1248
|
+
shaderKey: 'test',
|
|
1249
|
+
update: vi.fn(),
|
|
1250
|
+
attachNode: vi.fn(),
|
|
1251
|
+
time: undefined,
|
|
1252
|
+
};
|
|
1253
|
+
// Assignment raises RecalcUniforms | IsRenderable via the setter
|
|
1254
|
+
node.shader = shader as never;
|
|
1255
|
+
node.update(0, clippingRect);
|
|
1256
|
+
expect(shader.update).toHaveBeenCalledTimes(1);
|
|
1257
|
+
|
|
1258
|
+
// Pure translation: no uniform recompute
|
|
1259
|
+
node.x = 50;
|
|
1260
|
+
node.update(0, clippingRect);
|
|
1261
|
+
expect(shader.update).toHaveBeenCalledTimes(1);
|
|
1262
|
+
|
|
1263
|
+
// Resize: uniforms depend on dimensions, must recompute
|
|
1264
|
+
node.w = 150;
|
|
1265
|
+
node.update(0, clippingRect);
|
|
1266
|
+
expect(shader.update).toHaveBeenCalledTimes(2);
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
describe('placeholderColor', () => {
|
|
1271
|
+
// A texture fake on a real EventEmitter so CoreNode's loadTextureTask
|
|
1272
|
+
// subscribes for real and we can drive the loaded/freed/failed handler
|
|
1273
|
+
// chain by emitting, like the engine does.
|
|
1274
|
+
function emittingTexture(state: string): ImageTexture & {
|
|
1275
|
+
emit: (event: string, data?: unknown) => void;
|
|
1276
|
+
} {
|
|
1277
|
+
return Object.assign(new EventEmitter(), {
|
|
1278
|
+
state,
|
|
1279
|
+
preventCleanup: false,
|
|
1280
|
+
retryCount: 0,
|
|
1281
|
+
maxRetryCount: 1,
|
|
1282
|
+
dimensions: { w: 100, h: 100 },
|
|
1283
|
+
setRenderableOwner: vi.fn(),
|
|
1284
|
+
}) as unknown as ImageTexture & {
|
|
1285
|
+
emit: (event: string, data?: unknown) => void;
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
function visibleNode(): CoreNode {
|
|
1290
|
+
const parent = new CoreNode(stage, defaultProps());
|
|
1291
|
+
parent.globalTransform = Matrix3d.identity();
|
|
1292
|
+
parent.worldAlpha = 1;
|
|
1293
|
+
|
|
1294
|
+
const node = new CoreNode(stage, defaultProps({ parent }));
|
|
1295
|
+
node.alpha = 1;
|
|
1296
|
+
node.x = 0;
|
|
1297
|
+
node.y = 0;
|
|
1298
|
+
node.w = 100;
|
|
1299
|
+
node.h = 100;
|
|
1300
|
+
return node;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Flush the queueMicrotask(loadTextureTask) so listeners attach.
|
|
1304
|
+
const flushMicrotasks = () => Promise.resolve();
|
|
1305
|
+
|
|
1306
|
+
it('renders the placeholder while the texture is loading', () => {
|
|
1307
|
+
const node = visibleNode();
|
|
1308
|
+
node.placeholderColor = 0x336699ff;
|
|
1309
|
+
node.texture = emittingTexture('initial');
|
|
1310
|
+
|
|
1311
|
+
node.update(0, clippingRect);
|
|
1312
|
+
|
|
1313
|
+
expect(node.placeholderActive).toBe(true);
|
|
1314
|
+
expect(node.isRenderable).toBe(true);
|
|
1315
|
+
expect(node.renderTexture).toBe(stage.defaultTexture);
|
|
1316
|
+
|
|
1317
|
+
const expected = premultiplyColorABGR(0x336699ff, 1);
|
|
1318
|
+
expect(node.premultipliedColorTl).toBe(expected);
|
|
1319
|
+
expect(node.premultipliedColorTr).toBe(expected);
|
|
1320
|
+
expect(node.premultipliedColorBl).toBe(expected);
|
|
1321
|
+
expect(node.premultipliedColorBr).toBe(expected);
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
it('is inactive without a placeholderColor (loading renders nothing)', () => {
|
|
1325
|
+
const node = visibleNode();
|
|
1326
|
+
node.texture = emittingTexture('initial');
|
|
1327
|
+
|
|
1328
|
+
node.update(0, clippingRect);
|
|
1329
|
+
|
|
1330
|
+
expect(node.placeholderActive).toBe(false);
|
|
1331
|
+
expect(node.isRenderable).toBe(false);
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
it('is inactive without a texture (color-only nodes are unaffected)', () => {
|
|
1335
|
+
const node = visibleNode();
|
|
1336
|
+
node.placeholderColor = 0x336699ff;
|
|
1337
|
+
|
|
1338
|
+
node.update(0, clippingRect);
|
|
1339
|
+
|
|
1340
|
+
expect(node.placeholderActive).toBe(false);
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
it('switches to the texture and regular colors once loaded', async () => {
|
|
1344
|
+
const node = visibleNode();
|
|
1345
|
+
node.color = 0xffffffff;
|
|
1346
|
+
node.placeholderColor = 0x336699ff;
|
|
1347
|
+
const texture = emittingTexture('initial');
|
|
1348
|
+
node.texture = texture;
|
|
1349
|
+
node.update(0, clippingRect);
|
|
1350
|
+
expect(node.renderTexture).toBe(stage.defaultTexture);
|
|
1351
|
+
|
|
1352
|
+
await flushMicrotasks();
|
|
1353
|
+
(texture as { state: string }).state = 'loaded';
|
|
1354
|
+
texture.emit('loaded', { w: 100, h: 100 });
|
|
1355
|
+
node.isQuadDirty = false;
|
|
1356
|
+
node.update(1, clippingRect);
|
|
1357
|
+
|
|
1358
|
+
expect(node.placeholderActive).toBe(false);
|
|
1359
|
+
expect(node.isRenderable).toBe(true);
|
|
1360
|
+
expect(node.renderTexture).toBe(texture);
|
|
1361
|
+
expect(node.premultipliedColorTl).toBe(
|
|
1362
|
+
premultiplyColorABGR(0xffffffff, 1),
|
|
1363
|
+
);
|
|
1364
|
+
// The color switch must reach the GPU quad buffer
|
|
1365
|
+
expect(node.isQuadDirty).toBe(true);
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
it('shows the placeholder again while a freed texture reloads', async () => {
|
|
1369
|
+
const node = visibleNode();
|
|
1370
|
+
node.placeholderColor = 0x336699ff;
|
|
1371
|
+
const texture = emittingTexture('initial');
|
|
1372
|
+
node.texture = texture;
|
|
1373
|
+
node.update(0, clippingRect);
|
|
1374
|
+
|
|
1375
|
+
await flushMicrotasks();
|
|
1376
|
+
(texture as { state: string }).state = 'loaded';
|
|
1377
|
+
texture.emit('loaded', { w: 100, h: 100 });
|
|
1378
|
+
node.update(1, clippingRect);
|
|
1379
|
+
expect(node.placeholderActive).toBe(false);
|
|
1380
|
+
|
|
1381
|
+
(texture as { state: string }).state = 'freed';
|
|
1382
|
+
texture.emit('freed');
|
|
1383
|
+
node.update(2, clippingRect);
|
|
1384
|
+
|
|
1385
|
+
expect(node.placeholderActive).toBe(true);
|
|
1386
|
+
expect(node.isRenderable).toBe(true);
|
|
1387
|
+
expect(node.renderTexture).toBe(stage.defaultTexture);
|
|
1388
|
+
expect(node.premultipliedColorTl).toBe(
|
|
1389
|
+
premultiplyColorABGR(0x336699ff, 1),
|
|
1390
|
+
);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
it('keeps the placeholder when the texture permanently fails', async () => {
|
|
1394
|
+
const node = visibleNode();
|
|
1395
|
+
node.placeholderColor = 0x336699ff;
|
|
1396
|
+
const texture = emittingTexture('initial');
|
|
1397
|
+
node.texture = texture;
|
|
1398
|
+
node.update(0, clippingRect);
|
|
1399
|
+
|
|
1400
|
+
await flushMicrotasks();
|
|
1401
|
+
(texture as { state: string }).state = 'failed';
|
|
1402
|
+
(texture as { retryCount: number }).retryCount = 2; // > maxRetryCount (1)
|
|
1403
|
+
texture.emit('failed', new Error('404'));
|
|
1404
|
+
node.update(1, clippingRect);
|
|
1405
|
+
|
|
1406
|
+
expect(node.placeholderActive).toBe(true);
|
|
1407
|
+
expect(node.isRenderable).toBe(true);
|
|
1408
|
+
expect(node.renderTexture).toBe(stage.defaultTexture);
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
it('a permanently failed texture without a placeholder stays non-renderable', async () => {
|
|
1412
|
+
const node = visibleNode();
|
|
1413
|
+
const texture = emittingTexture('initial');
|
|
1414
|
+
node.texture = texture;
|
|
1415
|
+
node.update(0, clippingRect);
|
|
1416
|
+
|
|
1417
|
+
await flushMicrotasks();
|
|
1418
|
+
(texture as { state: string }).state = 'failed';
|
|
1419
|
+
(texture as { retryCount: number }).retryCount = 2;
|
|
1420
|
+
texture.emit('failed', new Error('404'));
|
|
1421
|
+
node.update(1, clippingRect);
|
|
1422
|
+
|
|
1423
|
+
expect(node.isRenderable).toBe(false);
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
it('deactivates when placeholderColor is cleared while loading', () => {
|
|
1427
|
+
const node = visibleNode();
|
|
1428
|
+
node.placeholderColor = 0x336699ff;
|
|
1429
|
+
node.texture = emittingTexture('initial');
|
|
1430
|
+
node.update(0, clippingRect);
|
|
1431
|
+
expect(node.isRenderable).toBe(true);
|
|
1432
|
+
|
|
1433
|
+
node.placeholderColor = 0;
|
|
1434
|
+
node.update(1, clippingRect);
|
|
1435
|
+
|
|
1436
|
+
expect(node.placeholderActive).toBe(false);
|
|
1437
|
+
expect(node.isRenderable).toBe(false);
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
it('updates the shown color when placeholderColor changes while active', () => {
|
|
1441
|
+
const node = visibleNode();
|
|
1442
|
+
node.placeholderColor = 0x336699ff;
|
|
1443
|
+
node.texture = emittingTexture('initial');
|
|
1444
|
+
node.update(0, clippingRect);
|
|
1445
|
+
|
|
1446
|
+
node.placeholderColor = 0x993311ff;
|
|
1447
|
+
node.update(1, clippingRect);
|
|
1448
|
+
|
|
1449
|
+
expect(node.premultipliedColorTl).toBe(
|
|
1450
|
+
premultiplyColorABGR(0x993311ff, 1),
|
|
1451
|
+
);
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
it('does not activate when the assigned texture is already loaded', () => {
|
|
1455
|
+
const node = visibleNode();
|
|
1456
|
+
node.placeholderColor = 0x336699ff;
|
|
1457
|
+
const texture = emittingTexture('loaded');
|
|
1458
|
+
node.texture = texture;
|
|
1459
|
+
|
|
1460
|
+
node.update(0, clippingRect);
|
|
1461
|
+
|
|
1462
|
+
expect(node.placeholderActive).toBe(false);
|
|
1463
|
+
expect(node.renderTexture).toBe(texture);
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
describe('renderOnlyInViewport', () => {
|
|
1468
|
+
// Viewport is 0..200; the preload (bounds-margin) ring extends to 400.
|
|
1469
|
+
// A node at x=250 is InBounds (margin ring); at x=50 it is InViewport;
|
|
1470
|
+
// at x=500 it is OutOfBounds.
|
|
1471
|
+
function boundsStage(renderOnlyInViewport: boolean): Stage {
|
|
1472
|
+
return mock<Stage>({
|
|
1473
|
+
strictBound: createBound(0, 0, 200, 200),
|
|
1474
|
+
preloadBound: createBound(0, 0, 400, 200),
|
|
1475
|
+
defaultTexture: {
|
|
1476
|
+
state: 'loaded',
|
|
1477
|
+
},
|
|
1478
|
+
renderer: mock<CoreRenderer>() as CoreRenderer,
|
|
1479
|
+
renderOnlyInViewport,
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function loadedTextureNode(stage: Stage, x: number): CoreNode {
|
|
1484
|
+
const parent = new CoreNode(stage, defaultProps());
|
|
1485
|
+
parent.globalTransform = Matrix3d.identity();
|
|
1486
|
+
parent.worldAlpha = 1;
|
|
1487
|
+
|
|
1488
|
+
const node = new CoreNode(stage, defaultProps({ parent }));
|
|
1489
|
+
node.alpha = 1;
|
|
1490
|
+
node.x = x;
|
|
1491
|
+
node.y = 0;
|
|
1492
|
+
node.w = 100;
|
|
1493
|
+
node.h = 100;
|
|
1494
|
+
node.texture = mock<ImageTexture>({
|
|
1495
|
+
state: 'loaded',
|
|
1496
|
+
setRenderableOwner: vi.fn(),
|
|
1497
|
+
});
|
|
1498
|
+
node.textureLoaded = true;
|
|
1499
|
+
return node;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
it('off: a margin-ring node is renderable (legacy behavior)', () => {
|
|
1503
|
+
const node = loadedTextureNode(boundsStage(false), 250);
|
|
1504
|
+
|
|
1505
|
+
node.update(0, clippingRect);
|
|
1506
|
+
|
|
1507
|
+
expect(node.isRenderable).toBe(true);
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
it('on: a margin-ring node is not renderable but still owns its texture', () => {
|
|
1511
|
+
const node = loadedTextureNode(boundsStage(true), 250);
|
|
1512
|
+
|
|
1513
|
+
node.update(0, clippingRect);
|
|
1514
|
+
|
|
1515
|
+
expect(node.isRenderable).toBe(false);
|
|
1516
|
+
// Ownership is the load trigger and cleanup protection — it must stay.
|
|
1517
|
+
expect(node.texture!.setRenderableOwner).toHaveBeenCalledWith(
|
|
1518
|
+
expect.anything(),
|
|
1519
|
+
true,
|
|
1520
|
+
);
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
it('on: a viewport node is renderable', () => {
|
|
1524
|
+
const node = loadedTextureNode(boundsStage(true), 50);
|
|
1525
|
+
|
|
1526
|
+
node.update(0, clippingRect);
|
|
1527
|
+
|
|
1528
|
+
expect(node.isRenderable).toBe(true);
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
it('on: a node becomes renderable when it crosses into the viewport', () => {
|
|
1532
|
+
const node = loadedTextureNode(boundsStage(true), 250);
|
|
1533
|
+
node.update(0, clippingRect);
|
|
1534
|
+
expect(node.isRenderable).toBe(false);
|
|
1535
|
+
|
|
1536
|
+
// Scroll it in.
|
|
1537
|
+
node.x = 50;
|
|
1538
|
+
node.update(1, clippingRect);
|
|
1539
|
+
expect(node.isRenderable).toBe(true);
|
|
1540
|
+
|
|
1541
|
+
// And back out into the ring.
|
|
1542
|
+
node.x = 250;
|
|
1543
|
+
node.update(2, clippingRect);
|
|
1544
|
+
expect(node.isRenderable).toBe(false);
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
it('on: an out-of-bounds node releases texture ownership', () => {
|
|
1548
|
+
const node = loadedTextureNode(boundsStage(true), 500);
|
|
1549
|
+
|
|
1550
|
+
node.update(0, clippingRect);
|
|
1551
|
+
|
|
1552
|
+
expect(node.isRenderable).toBe(false);
|
|
1553
|
+
expect(node.texture!.setRenderableOwner).not.toHaveBeenCalledWith(
|
|
1554
|
+
expect.anything(),
|
|
1555
|
+
true,
|
|
1556
|
+
);
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
it('on: a margin-ring placeholder is gated the same way', () => {
|
|
1560
|
+
const stage = boundsStage(true);
|
|
1561
|
+
const parent = new CoreNode(stage, defaultProps());
|
|
1562
|
+
parent.globalTransform = Matrix3d.identity();
|
|
1563
|
+
parent.worldAlpha = 1;
|
|
1564
|
+
|
|
1565
|
+
const node = new CoreNode(stage, defaultProps({ parent }));
|
|
1566
|
+
node.alpha = 1;
|
|
1567
|
+
node.x = 250;
|
|
1568
|
+
node.y = 0;
|
|
1569
|
+
node.w = 100;
|
|
1570
|
+
node.h = 100;
|
|
1571
|
+
node.placeholderColor = 0x336699ff;
|
|
1572
|
+
node.texture = mock<ImageTexture>({
|
|
1573
|
+
state: 'initial',
|
|
1574
|
+
setRenderableOwner: vi.fn(),
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
node.update(0, clippingRect);
|
|
1578
|
+
expect(node.placeholderActive).toBe(true);
|
|
1579
|
+
expect(node.isRenderable).toBe(false);
|
|
1580
|
+
|
|
1581
|
+
node.x = 50;
|
|
1582
|
+
node.update(1, clippingRect);
|
|
1583
|
+
expect(node.isRenderable).toBe(true);
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
it('on: color-only nodes in the margin ring are gated too', () => {
|
|
1587
|
+
const stage = boundsStage(true);
|
|
1588
|
+
const parent = new CoreNode(stage, defaultProps());
|
|
1589
|
+
parent.globalTransform = Matrix3d.identity();
|
|
1590
|
+
parent.worldAlpha = 1;
|
|
1591
|
+
|
|
1592
|
+
const node = new CoreNode(stage, defaultProps({ parent }));
|
|
1593
|
+
node.alpha = 1;
|
|
1594
|
+
node.x = 250;
|
|
1595
|
+
node.y = 0;
|
|
1596
|
+
node.w = 100;
|
|
1597
|
+
node.h = 100;
|
|
1598
|
+
node.color = 0xff0000ff;
|
|
1599
|
+
|
|
1600
|
+
node.update(0, clippingRect);
|
|
1601
|
+
expect(node.isRenderable).toBe(false);
|
|
1602
|
+
|
|
1603
|
+
node.x = 50;
|
|
1604
|
+
node.update(1, clippingRect);
|
|
1605
|
+
expect(node.isRenderable).toBe(true);
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1183
1608
|
});
|