@opentui/core 0.0.0-20250908-4906ddad → 0.0.0-20250915-f5db043a
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/3d.js +1 -1
- package/3d.js.map +1 -1
- package/Renderable.d.ts +60 -20
- package/buffer.d.ts +3 -1
- package/{index-d6kwx5pm.js → index-6esrcarp.js} +849 -263
- package/index-6esrcarp.js.map +38 -0
- package/index.js +666 -84
- package/index.js.map +14 -13
- package/lib/KeyHandler.d.ts +7 -3
- package/lib/objects-in-viewport.d.ts +10 -0
- package/lib/parse.keypress-kitty.d.ts +2 -0
- package/lib/parse.keypress.d.ts +11 -1
- package/lib/styled-text.d.ts +4 -0
- package/package.json +12 -7
- package/renderables/Input.d.ts +1 -1
- package/renderables/ScrollBar.d.ts +1 -1
- package/renderables/ScrollBox.d.ts +17 -2
- package/renderables/Select.d.ts +1 -1
- package/renderables/Slider.d.ts +24 -11
- package/renderables/TabSelect.d.ts +1 -1
- package/renderables/Text.d.ts +12 -1
- package/renderables/TextNode.d.ts +67 -0
- package/renderables/composition/constructs.d.ts +30 -9
- package/renderables/composition/vnode.d.ts +1 -1
- package/renderables/index.d.ts +1 -0
- package/renderer.d.ts +18 -2
- package/testing/mock-keys.d.ts +67 -0
- package/testing/mock-mouse.d.ts +38 -0
- package/testing/test-renderer.d.ts +18 -0
- package/testing.d.ts +3 -0
- package/testing.js +343 -0
- package/testing.js.map +12 -0
- package/text-buffer.d.ts +3 -2
- package/types.d.ts +8 -0
- package/utils.d.ts +2 -0
- package/zig.d.ts +6 -2
- package/index-d6kwx5pm.js.map +0 -36
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
ASCIIFontSelectionHelper,
|
|
4
|
+
BaseRenderable,
|
|
4
5
|
BorderCharArrays,
|
|
5
6
|
BorderChars,
|
|
6
7
|
CliRenderEvents,
|
|
@@ -65,6 +66,7 @@ import {
|
|
|
65
66
|
getBorderSides,
|
|
66
67
|
getCharacterPositions,
|
|
67
68
|
getKeyHandler,
|
|
69
|
+
getObjectsInViewport,
|
|
68
70
|
green,
|
|
69
71
|
h,
|
|
70
72
|
hastToStyledText,
|
|
@@ -80,6 +82,7 @@ import {
|
|
|
80
82
|
isPositionTypeType,
|
|
81
83
|
isRenderable,
|
|
82
84
|
isSizeType,
|
|
85
|
+
isStyledText,
|
|
83
86
|
isVNode,
|
|
84
87
|
isValidPercentage,
|
|
85
88
|
italic,
|
|
@@ -114,10 +117,11 @@ import {
|
|
|
114
117
|
stringToStyledText,
|
|
115
118
|
t,
|
|
116
119
|
underline,
|
|
120
|
+
visualizeRenderableTree,
|
|
117
121
|
white,
|
|
118
122
|
wrapWithDelegates,
|
|
119
123
|
yellow
|
|
120
|
-
} from "./index-
|
|
124
|
+
} from "./index-6esrcarp.js";
|
|
121
125
|
// src/post/filters.ts
|
|
122
126
|
function applyScanlines(buffer, strength = 0.8, step = 2) {
|
|
123
127
|
const width = buffer.width;
|
|
@@ -1434,7 +1438,7 @@ class FrameBufferRenderable extends Renderable {
|
|
|
1434
1438
|
this.requestRender();
|
|
1435
1439
|
}
|
|
1436
1440
|
renderSelf(buffer) {
|
|
1437
|
-
if (!this.visible)
|
|
1441
|
+
if (!this.visible || this.isDestroyed)
|
|
1438
1442
|
return;
|
|
1439
1443
|
buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
|
|
1440
1444
|
}
|
|
@@ -1443,6 +1447,232 @@ class FrameBufferRenderable extends Renderable {
|
|
|
1443
1447
|
super.destroySelf();
|
|
1444
1448
|
}
|
|
1445
1449
|
}
|
|
1450
|
+
// src/renderables/TextNode.ts
|
|
1451
|
+
var BrandedTextNodeRenderable = Symbol.for("@opentui/core/TextNodeRenderable");
|
|
1452
|
+
function isTextNodeRenderable(obj) {
|
|
1453
|
+
return !!obj?.[BrandedTextNodeRenderable];
|
|
1454
|
+
}
|
|
1455
|
+
function styledTextToTextNodes(styledText) {
|
|
1456
|
+
return styledText.chunks.map((chunk) => {
|
|
1457
|
+
const node = new TextNodeRenderable({
|
|
1458
|
+
fg: chunk.fg,
|
|
1459
|
+
bg: chunk.bg,
|
|
1460
|
+
attributes: chunk.attributes
|
|
1461
|
+
});
|
|
1462
|
+
node.add(chunk.text);
|
|
1463
|
+
return node;
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
class TextNodeRenderable extends BaseRenderable {
|
|
1468
|
+
[BrandedTextNodeRenderable] = true;
|
|
1469
|
+
_fg;
|
|
1470
|
+
_bg;
|
|
1471
|
+
_attributes;
|
|
1472
|
+
_children = [];
|
|
1473
|
+
parent = null;
|
|
1474
|
+
constructor(options) {
|
|
1475
|
+
super(options);
|
|
1476
|
+
this._fg = options.fg ? parseColor(options.fg) : undefined;
|
|
1477
|
+
this._bg = options.bg ? parseColor(options.bg) : undefined;
|
|
1478
|
+
this._attributes = options.attributes ?? 0;
|
|
1479
|
+
}
|
|
1480
|
+
get children() {
|
|
1481
|
+
return this._children;
|
|
1482
|
+
}
|
|
1483
|
+
set children(children) {
|
|
1484
|
+
this._children = children;
|
|
1485
|
+
this.requestRender();
|
|
1486
|
+
}
|
|
1487
|
+
requestRender() {
|
|
1488
|
+
this.markDirty();
|
|
1489
|
+
this.parent?.requestRender();
|
|
1490
|
+
}
|
|
1491
|
+
add(obj, index) {
|
|
1492
|
+
if (typeof obj === "string") {
|
|
1493
|
+
if (index !== undefined) {
|
|
1494
|
+
this._children.splice(index, 0, obj);
|
|
1495
|
+
this.requestRender();
|
|
1496
|
+
return index;
|
|
1497
|
+
}
|
|
1498
|
+
const insertIndex = this._children.length;
|
|
1499
|
+
this._children.push(obj);
|
|
1500
|
+
this.requestRender();
|
|
1501
|
+
return insertIndex;
|
|
1502
|
+
}
|
|
1503
|
+
if (isTextNodeRenderable(obj)) {
|
|
1504
|
+
if (index !== undefined) {
|
|
1505
|
+
this._children.splice(index, 0, obj);
|
|
1506
|
+
obj.parent = this;
|
|
1507
|
+
this.requestRender();
|
|
1508
|
+
return index;
|
|
1509
|
+
}
|
|
1510
|
+
const insertIndex = this._children.length;
|
|
1511
|
+
this._children.push(obj);
|
|
1512
|
+
obj.parent = this;
|
|
1513
|
+
this.requestRender();
|
|
1514
|
+
return insertIndex;
|
|
1515
|
+
}
|
|
1516
|
+
if (isStyledText(obj)) {
|
|
1517
|
+
const textNodes = styledTextToTextNodes(obj);
|
|
1518
|
+
if (index !== undefined) {
|
|
1519
|
+
this._children.splice(index, 0, ...textNodes);
|
|
1520
|
+
textNodes.forEach((node) => node.parent = this);
|
|
1521
|
+
this.requestRender();
|
|
1522
|
+
return index;
|
|
1523
|
+
}
|
|
1524
|
+
const insertIndex = this._children.length;
|
|
1525
|
+
this._children.push(...textNodes);
|
|
1526
|
+
textNodes.forEach((node) => node.parent = this);
|
|
1527
|
+
this.requestRender();
|
|
1528
|
+
return insertIndex;
|
|
1529
|
+
}
|
|
1530
|
+
throw new Error("TextNodeRenderable only accepts strings, TextNodeRenderable instances, or StyledText instances");
|
|
1531
|
+
}
|
|
1532
|
+
replace(obj, index) {
|
|
1533
|
+
this._children[index] = obj;
|
|
1534
|
+
if (typeof obj !== "string") {
|
|
1535
|
+
obj.parent = this;
|
|
1536
|
+
}
|
|
1537
|
+
this.requestRender();
|
|
1538
|
+
}
|
|
1539
|
+
insertBefore(child, anchorNode) {
|
|
1540
|
+
if (!anchorNode || !isTextNodeRenderable(anchorNode)) {
|
|
1541
|
+
throw new Error("Anchor must be a TextNodeRenderable");
|
|
1542
|
+
}
|
|
1543
|
+
const anchorIndex = this._children.indexOf(anchorNode);
|
|
1544
|
+
if (anchorIndex === -1) {
|
|
1545
|
+
throw new Error("Anchor node not found in children");
|
|
1546
|
+
}
|
|
1547
|
+
if (typeof child === "string") {
|
|
1548
|
+
this._children.splice(anchorIndex, 0, child);
|
|
1549
|
+
} else if (isTextNodeRenderable(child)) {
|
|
1550
|
+
this._children.splice(anchorIndex, 0, child);
|
|
1551
|
+
child.parent = this;
|
|
1552
|
+
} else if (child instanceof StyledText) {
|
|
1553
|
+
const textNodes = styledTextToTextNodes(child);
|
|
1554
|
+
this._children.splice(anchorIndex, 0, ...textNodes);
|
|
1555
|
+
textNodes.forEach((node) => node.parent = this);
|
|
1556
|
+
} else {
|
|
1557
|
+
throw new Error("Child must be a string, TextNodeRenderable, or StyledText instance");
|
|
1558
|
+
}
|
|
1559
|
+
this.requestRender();
|
|
1560
|
+
return this;
|
|
1561
|
+
}
|
|
1562
|
+
remove(child) {
|
|
1563
|
+
const childIndex = this._children.indexOf(child);
|
|
1564
|
+
if (childIndex === -1) {
|
|
1565
|
+
throw new Error("Child not found in children");
|
|
1566
|
+
}
|
|
1567
|
+
this._children.splice(childIndex, 1);
|
|
1568
|
+
if (typeof child !== "string") {
|
|
1569
|
+
child.parent = null;
|
|
1570
|
+
}
|
|
1571
|
+
this.requestRender();
|
|
1572
|
+
return this;
|
|
1573
|
+
}
|
|
1574
|
+
clear() {
|
|
1575
|
+
this._children = [];
|
|
1576
|
+
this.requestRender();
|
|
1577
|
+
}
|
|
1578
|
+
mergeStyles(parentStyle) {
|
|
1579
|
+
return {
|
|
1580
|
+
fg: this._fg ?? parentStyle.fg,
|
|
1581
|
+
bg: this._bg ?? parentStyle.bg,
|
|
1582
|
+
attributes: this._attributes | parentStyle.attributes
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
gatherWithInheritedStyle(parentStyle = { fg: undefined, bg: undefined, attributes: 0 }) {
|
|
1586
|
+
const currentStyle = this.mergeStyles(parentStyle);
|
|
1587
|
+
const chunks = [];
|
|
1588
|
+
for (const child of this._children) {
|
|
1589
|
+
if (typeof child === "string") {
|
|
1590
|
+
chunks.push({
|
|
1591
|
+
__isChunk: true,
|
|
1592
|
+
text: child,
|
|
1593
|
+
fg: currentStyle.fg,
|
|
1594
|
+
bg: currentStyle.bg,
|
|
1595
|
+
attributes: currentStyle.attributes
|
|
1596
|
+
});
|
|
1597
|
+
} else {
|
|
1598
|
+
const childChunks = child.gatherWithInheritedStyle(currentStyle);
|
|
1599
|
+
chunks.push(...childChunks);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
this.markClean();
|
|
1603
|
+
return chunks;
|
|
1604
|
+
}
|
|
1605
|
+
static fromString(text, options = {}) {
|
|
1606
|
+
const node = new TextNodeRenderable(options);
|
|
1607
|
+
node.add(text);
|
|
1608
|
+
return node;
|
|
1609
|
+
}
|
|
1610
|
+
static fromNodes(nodes, options = {}) {
|
|
1611
|
+
const node = new TextNodeRenderable(options);
|
|
1612
|
+
for (const childNode of nodes) {
|
|
1613
|
+
node.add(childNode);
|
|
1614
|
+
}
|
|
1615
|
+
return node;
|
|
1616
|
+
}
|
|
1617
|
+
toChunks(parentStyle = { fg: undefined, bg: undefined, attributes: 0 }) {
|
|
1618
|
+
return this.gatherWithInheritedStyle(parentStyle);
|
|
1619
|
+
}
|
|
1620
|
+
getChildren() {
|
|
1621
|
+
return this._children.filter((child) => typeof child !== "string");
|
|
1622
|
+
}
|
|
1623
|
+
getChildrenCount() {
|
|
1624
|
+
return this._children.length;
|
|
1625
|
+
}
|
|
1626
|
+
getRenderable(id) {
|
|
1627
|
+
return this._children.find((child) => typeof child !== "string" && child.id === id);
|
|
1628
|
+
}
|
|
1629
|
+
get fg() {
|
|
1630
|
+
return this._fg;
|
|
1631
|
+
}
|
|
1632
|
+
set fg(fg2) {
|
|
1633
|
+
if (!fg2) {
|
|
1634
|
+
this._fg = undefined;
|
|
1635
|
+
this.requestRender();
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
this._fg = parseColor(fg2);
|
|
1639
|
+
this.requestRender();
|
|
1640
|
+
}
|
|
1641
|
+
set bg(bg2) {
|
|
1642
|
+
if (!bg2) {
|
|
1643
|
+
this._bg = undefined;
|
|
1644
|
+
this.requestRender();
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
this._bg = parseColor(bg2);
|
|
1648
|
+
this.requestRender();
|
|
1649
|
+
}
|
|
1650
|
+
get bg() {
|
|
1651
|
+
return this._bg;
|
|
1652
|
+
}
|
|
1653
|
+
set attributes(attributes) {
|
|
1654
|
+
this._attributes = attributes;
|
|
1655
|
+
this.requestRender();
|
|
1656
|
+
}
|
|
1657
|
+
get attributes() {
|
|
1658
|
+
return this._attributes;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
class RootTextNodeRenderable extends TextNodeRenderable {
|
|
1663
|
+
ctx;
|
|
1664
|
+
textParent;
|
|
1665
|
+
constructor(ctx, options, textParent) {
|
|
1666
|
+
super(options);
|
|
1667
|
+
this.ctx = ctx;
|
|
1668
|
+
this.textParent = textParent;
|
|
1669
|
+
}
|
|
1670
|
+
requestRender() {
|
|
1671
|
+
this.markDirty();
|
|
1672
|
+
this.ctx.requestRender();
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1446
1676
|
// src/renderables/Text.ts
|
|
1447
1677
|
class TextRenderable extends Renderable {
|
|
1448
1678
|
selectable = true;
|
|
@@ -1455,6 +1685,7 @@ class TextRenderable extends Renderable {
|
|
|
1455
1685
|
lastLocalSelection = null;
|
|
1456
1686
|
textBuffer;
|
|
1457
1687
|
_lineInfo = { lineStarts: [], lineWidths: [] };
|
|
1688
|
+
rootTextNode;
|
|
1458
1689
|
_defaultOptions = {
|
|
1459
1690
|
content: "",
|
|
1460
1691
|
fg: RGBA.fromValues(1, 1, 1, 1),
|
|
@@ -1480,6 +1711,12 @@ class TextRenderable extends Renderable {
|
|
|
1480
1711
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1481
1712
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
1482
1713
|
this.setupMeasureFunc();
|
|
1714
|
+
this.rootTextNode = new RootTextNodeRenderable(ctx, {
|
|
1715
|
+
id: `${this.id}-root`,
|
|
1716
|
+
fg: this._defaultFg,
|
|
1717
|
+
bg: this._defaultBg,
|
|
1718
|
+
attributes: this._defaultAttributes
|
|
1719
|
+
}, this);
|
|
1483
1720
|
this.updateTextBuffer(styledText);
|
|
1484
1721
|
this._text.mount(this);
|
|
1485
1722
|
this.updateTextInfo();
|
|
@@ -1488,12 +1725,7 @@ class TextRenderable extends Renderable {
|
|
|
1488
1725
|
this.textBuffer.setStyledText(styledText);
|
|
1489
1726
|
this.clearChunks(styledText);
|
|
1490
1727
|
}
|
|
1491
|
-
clearChunks(styledText) {
|
|
1492
|
-
styledText.chunks.forEach((chunk) => {
|
|
1493
|
-
chunk.text = undefined;
|
|
1494
|
-
chunk.plainText = undefined;
|
|
1495
|
-
});
|
|
1496
|
-
}
|
|
1728
|
+
clearChunks(styledText) {}
|
|
1497
1729
|
get content() {
|
|
1498
1730
|
return this._text;
|
|
1499
1731
|
}
|
|
@@ -1506,6 +1738,9 @@ class TextRenderable extends Renderable {
|
|
|
1506
1738
|
get chunks() {
|
|
1507
1739
|
return this._text.chunks;
|
|
1508
1740
|
}
|
|
1741
|
+
get textNode() {
|
|
1742
|
+
return this.rootTextNode;
|
|
1743
|
+
}
|
|
1509
1744
|
set content(value) {
|
|
1510
1745
|
const styledText = typeof value === "string" ? stringToStyledText(value) : value;
|
|
1511
1746
|
if (this._text !== styledText) {
|
|
@@ -1520,9 +1755,11 @@ class TextRenderable extends Renderable {
|
|
|
1520
1755
|
}
|
|
1521
1756
|
set fg(value) {
|
|
1522
1757
|
const newColor = parseColor(value ?? this._defaultOptions.fg);
|
|
1758
|
+
this.rootTextNode.fg = newColor;
|
|
1523
1759
|
if (this._defaultFg !== newColor) {
|
|
1524
1760
|
this._defaultFg = newColor;
|
|
1525
1761
|
this.textBuffer.setDefaultFg(this._defaultFg);
|
|
1762
|
+
this.rootTextNode.fg = newColor;
|
|
1526
1763
|
this.requestRender();
|
|
1527
1764
|
}
|
|
1528
1765
|
}
|
|
@@ -1557,9 +1794,11 @@ class TextRenderable extends Renderable {
|
|
|
1557
1794
|
}
|
|
1558
1795
|
set bg(value) {
|
|
1559
1796
|
const newColor = parseColor(value ?? this._defaultOptions.bg);
|
|
1797
|
+
this.rootTextNode.bg = newColor;
|
|
1560
1798
|
if (this._defaultBg !== newColor) {
|
|
1561
1799
|
this._defaultBg = newColor;
|
|
1562
1800
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1801
|
+
this.rootTextNode.bg = newColor;
|
|
1563
1802
|
this.requestRender();
|
|
1564
1803
|
}
|
|
1565
1804
|
}
|
|
@@ -1570,6 +1809,7 @@ class TextRenderable extends Renderable {
|
|
|
1570
1809
|
if (this._defaultAttributes !== value) {
|
|
1571
1810
|
this._defaultAttributes = value;
|
|
1572
1811
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
1812
|
+
this.rootTextNode.attributes = value;
|
|
1573
1813
|
this.requestRender();
|
|
1574
1814
|
}
|
|
1575
1815
|
}
|
|
@@ -1625,7 +1865,7 @@ class TextRenderable extends Renderable {
|
|
|
1625
1865
|
this.layoutNode.yogaNode.setMeasureFunc(measureFunc);
|
|
1626
1866
|
}
|
|
1627
1867
|
insertChunk(chunk, index) {
|
|
1628
|
-
this.textBuffer.
|
|
1868
|
+
this.textBuffer.insertChunkGroup(index ?? this.textBuffer.chunkGroupCount, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
|
|
1629
1869
|
this.updateTextInfo();
|
|
1630
1870
|
this.clearChunks(this._text);
|
|
1631
1871
|
}
|
|
@@ -1641,10 +1881,46 @@ class TextRenderable extends Renderable {
|
|
|
1641
1881
|
const index = this._text.chunks.indexOf(oldChunk);
|
|
1642
1882
|
if (index === -1)
|
|
1643
1883
|
return;
|
|
1644
|
-
this.textBuffer.
|
|
1884
|
+
this.textBuffer.replaceChunkGroup(index, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
|
|
1645
1885
|
this.updateTextInfo();
|
|
1646
1886
|
this.clearChunks(this._text);
|
|
1647
1887
|
}
|
|
1888
|
+
updateTextFromNodes() {
|
|
1889
|
+
if (this.rootTextNode.isDirty) {
|
|
1890
|
+
const chunks = this.rootTextNode.gatherWithInheritedStyle({
|
|
1891
|
+
fg: this._defaultFg,
|
|
1892
|
+
bg: this._defaultBg,
|
|
1893
|
+
attributes: this._defaultAttributes
|
|
1894
|
+
});
|
|
1895
|
+
this.textBuffer.setStyledText(new StyledText(chunks));
|
|
1896
|
+
this.updateTextInfo();
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
add(obj, index) {
|
|
1900
|
+
return this.rootTextNode.add(obj, index);
|
|
1901
|
+
}
|
|
1902
|
+
remove(id) {
|
|
1903
|
+
const child = this.rootTextNode.getRenderable(id);
|
|
1904
|
+
if (child && isTextNodeRenderable(child)) {
|
|
1905
|
+
this.rootTextNode.remove(child);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
insertBefore(obj, anchor) {
|
|
1909
|
+
this.rootTextNode.insertBefore(obj, anchor);
|
|
1910
|
+
return this.rootTextNode.children.indexOf(obj);
|
|
1911
|
+
}
|
|
1912
|
+
getTextChildren() {
|
|
1913
|
+
return this.rootTextNode.getChildren();
|
|
1914
|
+
}
|
|
1915
|
+
clear() {
|
|
1916
|
+
this.rootTextNode.clear();
|
|
1917
|
+
const emptyStyledText = stringToStyledText("");
|
|
1918
|
+
this._text = emptyStyledText;
|
|
1919
|
+
emptyStyledText.mount(this);
|
|
1920
|
+
this.updateTextBuffer(emptyStyledText);
|
|
1921
|
+
this.updateTextInfo();
|
|
1922
|
+
this.requestRender();
|
|
1923
|
+
}
|
|
1648
1924
|
shouldStartSelection(x, y) {
|
|
1649
1925
|
if (!this.selectable)
|
|
1650
1926
|
return false;
|
|
@@ -1670,6 +1946,16 @@ class TextRenderable extends Renderable {
|
|
|
1670
1946
|
getSelection() {
|
|
1671
1947
|
return this.textBuffer.getSelection();
|
|
1672
1948
|
}
|
|
1949
|
+
onLifecyclePass = () => {
|
|
1950
|
+
this.updateTextFromNodes();
|
|
1951
|
+
};
|
|
1952
|
+
render(buffer, deltaTime) {
|
|
1953
|
+
if (!this.visible)
|
|
1954
|
+
return;
|
|
1955
|
+
this.markClean();
|
|
1956
|
+
this._ctx.addToHitGrid(this.x, this.y, this.width, this.height, this.num);
|
|
1957
|
+
this.renderSelf(buffer);
|
|
1958
|
+
}
|
|
1673
1959
|
renderSelf(buffer) {
|
|
1674
1960
|
if (this.textBuffer.ptr) {
|
|
1675
1961
|
const clipRect = {
|
|
@@ -1683,6 +1969,7 @@ class TextRenderable extends Renderable {
|
|
|
1683
1969
|
}
|
|
1684
1970
|
destroy() {
|
|
1685
1971
|
this.textBuffer.destroy();
|
|
1972
|
+
this.rootTextNode.children.length = 0;
|
|
1686
1973
|
super.destroy();
|
|
1687
1974
|
}
|
|
1688
1975
|
}
|
|
@@ -1795,6 +2082,8 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
|
|
|
1795
2082
|
this.renderFontToBuffer();
|
|
1796
2083
|
}
|
|
1797
2084
|
renderFontToBuffer() {
|
|
2085
|
+
if (this.isDestroyed)
|
|
2086
|
+
return;
|
|
1798
2087
|
this.frameBuffer.clear(this._bg);
|
|
1799
2088
|
renderFontToFrameBuffer(this.frameBuffer, {
|
|
1800
2089
|
text: this._text,
|
|
@@ -1842,7 +2131,7 @@ var InputRenderableEvents;
|
|
|
1842
2131
|
})(InputRenderableEvents ||= {});
|
|
1843
2132
|
|
|
1844
2133
|
class InputRenderable extends Renderable {
|
|
1845
|
-
|
|
2134
|
+
_focusable = true;
|
|
1846
2135
|
_value = "";
|
|
1847
2136
|
_cursorPosition = 0;
|
|
1848
2137
|
_placeholder;
|
|
@@ -2115,7 +2404,7 @@ var SelectRenderableEvents;
|
|
|
2115
2404
|
})(SelectRenderableEvents ||= {});
|
|
2116
2405
|
|
|
2117
2406
|
class SelectRenderable extends Renderable {
|
|
2118
|
-
|
|
2407
|
+
_focusable = true;
|
|
2119
2408
|
_options = [];
|
|
2120
2409
|
selectedIndex = 0;
|
|
2121
2410
|
scrollOffset = 0;
|
|
@@ -2451,7 +2740,7 @@ function calculateDynamicHeight(showUnderline, showDescription) {
|
|
|
2451
2740
|
}
|
|
2452
2741
|
|
|
2453
2742
|
class TabSelectRenderable extends Renderable {
|
|
2454
|
-
|
|
2743
|
+
_focusable = true;
|
|
2455
2744
|
_options = [];
|
|
2456
2745
|
selectedIndex = 0;
|
|
2457
2746
|
scrollOffset = 0;
|
|
@@ -2741,43 +3030,71 @@ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
|
2741
3030
|
|
|
2742
3031
|
class SliderRenderable extends Renderable {
|
|
2743
3032
|
orientation;
|
|
2744
|
-
|
|
2745
|
-
|
|
3033
|
+
_value;
|
|
3034
|
+
_min;
|
|
3035
|
+
_max;
|
|
3036
|
+
_viewPortSize;
|
|
2746
3037
|
_backgroundColor;
|
|
2747
3038
|
_foregroundColor;
|
|
2748
3039
|
_onChange;
|
|
2749
3040
|
constructor(ctx, options) {
|
|
2750
3041
|
super(ctx, options);
|
|
2751
3042
|
this.orientation = options.orientation;
|
|
2752
|
-
this.
|
|
2753
|
-
this.
|
|
3043
|
+
this._min = options.min ?? 0;
|
|
3044
|
+
this._max = options.max ?? 100;
|
|
3045
|
+
this._value = options.value ?? this._min;
|
|
3046
|
+
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
2754
3047
|
this._onChange = options.onChange;
|
|
2755
3048
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
2756
3049
|
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
2757
3050
|
this.setupMouseHandling();
|
|
2758
3051
|
}
|
|
2759
|
-
get
|
|
2760
|
-
return this.
|
|
3052
|
+
get value() {
|
|
3053
|
+
return this._value;
|
|
2761
3054
|
}
|
|
2762
|
-
set
|
|
2763
|
-
const clamped = Math.max(
|
|
2764
|
-
if (clamped !== this.
|
|
2765
|
-
this.
|
|
3055
|
+
set value(newValue) {
|
|
3056
|
+
const clamped = Math.max(this._min, Math.min(this._max, newValue));
|
|
3057
|
+
if (clamped !== this._value) {
|
|
3058
|
+
this._value = clamped;
|
|
3059
|
+
this._onChange?.(clamped);
|
|
3060
|
+
this.emit("change", { value: clamped });
|
|
2766
3061
|
this.requestRender();
|
|
2767
3062
|
}
|
|
2768
3063
|
}
|
|
2769
|
-
get
|
|
2770
|
-
return this.
|
|
3064
|
+
get min() {
|
|
3065
|
+
return this._min;
|
|
2771
3066
|
}
|
|
2772
|
-
set
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
this.
|
|
2776
|
-
|
|
2777
|
-
|
|
3067
|
+
set min(newMin) {
|
|
3068
|
+
if (newMin !== this._min) {
|
|
3069
|
+
this._min = newMin;
|
|
3070
|
+
if (this._value < newMin) {
|
|
3071
|
+
this.value = newMin;
|
|
3072
|
+
}
|
|
3073
|
+
this.requestRender();
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
get max() {
|
|
3077
|
+
return this._max;
|
|
3078
|
+
}
|
|
3079
|
+
set max(newMax) {
|
|
3080
|
+
if (newMax !== this._max) {
|
|
3081
|
+
this._max = newMax;
|
|
3082
|
+
if (this._value > newMax) {
|
|
3083
|
+
this.value = newMax;
|
|
3084
|
+
}
|
|
2778
3085
|
this.requestRender();
|
|
2779
3086
|
}
|
|
2780
3087
|
}
|
|
3088
|
+
set viewPortSize(size) {
|
|
3089
|
+
const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
|
|
3090
|
+
if (clampedSize !== this._viewPortSize) {
|
|
3091
|
+
this._viewPortSize = clampedSize;
|
|
3092
|
+
this.requestRender();
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
get viewPortSize() {
|
|
3096
|
+
return this._viewPortSize;
|
|
3097
|
+
}
|
|
2781
3098
|
get backgroundColor() {
|
|
2782
3099
|
return this._backgroundColor;
|
|
2783
3100
|
}
|
|
@@ -2792,69 +3109,183 @@ class SliderRenderable extends Renderable {
|
|
|
2792
3109
|
this._foregroundColor = parseColor(value);
|
|
2793
3110
|
this.requestRender();
|
|
2794
3111
|
}
|
|
3112
|
+
calculateDragOffsetVirtual(event) {
|
|
3113
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3114
|
+
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
3115
|
+
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
3116
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3117
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3118
|
+
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
3119
|
+
}
|
|
2795
3120
|
setupMouseHandling() {
|
|
2796
3121
|
let isDragging = false;
|
|
2797
|
-
let
|
|
3122
|
+
let dragOffsetVirtual = 0;
|
|
2798
3123
|
this.onMouseDown = (event) => {
|
|
2799
3124
|
event.stopPropagation();
|
|
2800
3125
|
event.preventDefault();
|
|
2801
|
-
|
|
2802
|
-
const
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3126
|
+
const thumb = this.getThumbRect();
|
|
3127
|
+
const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
|
|
3128
|
+
if (inThumb) {
|
|
3129
|
+
isDragging = true;
|
|
3130
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2806
3131
|
} else {
|
|
2807
|
-
|
|
3132
|
+
this.updateValueFromMouseDirect(event);
|
|
3133
|
+
isDragging = true;
|
|
3134
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2808
3135
|
}
|
|
2809
|
-
this.updatePositionFromMouse(event, relativeStartPos);
|
|
2810
3136
|
};
|
|
2811
3137
|
this.onMouseDrag = (event) => {
|
|
2812
3138
|
if (!isDragging)
|
|
2813
3139
|
return;
|
|
2814
3140
|
event.stopPropagation();
|
|
2815
|
-
this.
|
|
3141
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
2816
3142
|
};
|
|
2817
|
-
this.onMouseUp = () => {
|
|
3143
|
+
this.onMouseUp = (event) => {
|
|
3144
|
+
if (isDragging) {
|
|
3145
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
3146
|
+
}
|
|
2818
3147
|
isDragging = false;
|
|
2819
3148
|
};
|
|
2820
3149
|
}
|
|
2821
|
-
|
|
3150
|
+
updateValueFromMouseDirect(event) {
|
|
2822
3151
|
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2823
3152
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2824
3153
|
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
2825
|
-
const
|
|
2826
|
-
const
|
|
2827
|
-
const
|
|
2828
|
-
const
|
|
2829
|
-
this.
|
|
2830
|
-
|
|
2831
|
-
|
|
3154
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3155
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3156
|
+
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
3157
|
+
const range = this._max - this._min;
|
|
3158
|
+
const newValue = this._min + ratio * range;
|
|
3159
|
+
this.value = newValue;
|
|
3160
|
+
}
|
|
3161
|
+
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
3162
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2832
3163
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2833
|
-
const
|
|
2834
|
-
|
|
3164
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3165
|
+
const virtualTrackSize = trackSize * 2;
|
|
3166
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3167
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3168
|
+
const virtualMousePos = clampedMousePos * 2;
|
|
3169
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3170
|
+
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
3171
|
+
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
3172
|
+
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
3173
|
+
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
3174
|
+
const range = this._max - this._min;
|
|
3175
|
+
const newValue = this._min + ratio * range;
|
|
3176
|
+
this.value = newValue;
|
|
2835
3177
|
}
|
|
2836
3178
|
getThumbRect() {
|
|
2837
|
-
const
|
|
3179
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3180
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3181
|
+
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
3182
|
+
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
2838
3183
|
if (this.orientation === "vertical") {
|
|
2839
3184
|
return {
|
|
2840
3185
|
x: this.x,
|
|
2841
|
-
y: this.y +
|
|
3186
|
+
y: this.y + realThumbStart,
|
|
2842
3187
|
width: this.width,
|
|
2843
|
-
height:
|
|
3188
|
+
height: Math.max(1, realThumbSize)
|
|
2844
3189
|
};
|
|
2845
3190
|
} else {
|
|
2846
3191
|
return {
|
|
2847
|
-
x: this.x +
|
|
3192
|
+
x: this.x + realThumbStart,
|
|
2848
3193
|
y: this.y,
|
|
2849
|
-
width:
|
|
3194
|
+
width: Math.max(1, realThumbSize),
|
|
2850
3195
|
height: this.height
|
|
2851
3196
|
};
|
|
2852
3197
|
}
|
|
2853
3198
|
}
|
|
2854
3199
|
renderSelf(buffer) {
|
|
3200
|
+
if (this.orientation === "horizontal") {
|
|
3201
|
+
this.renderHorizontal(buffer);
|
|
3202
|
+
} else {
|
|
3203
|
+
this.renderVertical(buffer);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
renderHorizontal(buffer) {
|
|
3207
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3208
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3209
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
3210
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
3211
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3212
|
+
const realEndCell = Math.floor((virtualThumbEnd - 1) / 2);
|
|
3213
|
+
const startX = Math.max(0, realStartCell);
|
|
3214
|
+
const endX = Math.min(this.width - 1, realEndCell);
|
|
3215
|
+
for (let realX = startX;realX <= endX; realX++) {
|
|
3216
|
+
const virtualCellStart = realX * 2;
|
|
3217
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3218
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3219
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3220
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3221
|
+
let char = " ";
|
|
3222
|
+
if (coverage >= 2) {
|
|
3223
|
+
char = "\u2588";
|
|
3224
|
+
} else {
|
|
3225
|
+
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
3226
|
+
if (isLeftHalf) {
|
|
3227
|
+
char = "\u258C";
|
|
3228
|
+
} else {
|
|
3229
|
+
char = "\u2590";
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
for (let y = 0;y < this.height; y++) {
|
|
3233
|
+
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
renderVertical(buffer) {
|
|
3238
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3239
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3240
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
2855
3241
|
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
2856
|
-
const
|
|
2857
|
-
|
|
3242
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3243
|
+
const realEndCell = Math.floor((virtualThumbEnd - 1) / 2);
|
|
3244
|
+
const startY = Math.max(0, realStartCell);
|
|
3245
|
+
const endY = Math.min(this.height - 1, realEndCell);
|
|
3246
|
+
for (let realY = startY;realY <= endY; realY++) {
|
|
3247
|
+
const virtualCellStart = realY * 2;
|
|
3248
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3249
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3250
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3251
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3252
|
+
let char = " ";
|
|
3253
|
+
if (coverage >= 2) {
|
|
3254
|
+
char = "\u2588";
|
|
3255
|
+
} else if (coverage > 0) {
|
|
3256
|
+
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
3257
|
+
if (virtualPositionInCell === 0) {
|
|
3258
|
+
char = "\u2580";
|
|
3259
|
+
} else {
|
|
3260
|
+
char = "\u2584";
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
for (let x = 0;x < this.width; x++) {
|
|
3264
|
+
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
getVirtualThumbSize() {
|
|
3269
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3270
|
+
const range = this._max - this._min;
|
|
3271
|
+
if (range === 0)
|
|
3272
|
+
return virtualTrackSize;
|
|
3273
|
+
const viewportSize = Math.max(1, this._viewPortSize);
|
|
3274
|
+
const contentSize = range + viewportSize;
|
|
3275
|
+
if (contentSize <= viewportSize)
|
|
3276
|
+
return virtualTrackSize;
|
|
3277
|
+
const thumbRatio = viewportSize / contentSize;
|
|
3278
|
+
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
3279
|
+
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
3280
|
+
}
|
|
3281
|
+
getVirtualThumbStart() {
|
|
3282
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3283
|
+
const range = this._max - this._min;
|
|
3284
|
+
if (range === 0)
|
|
3285
|
+
return 0;
|
|
3286
|
+
const valueRatio = (this._value - this._min) / range;
|
|
3287
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3288
|
+
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
2858
3289
|
}
|
|
2859
3290
|
}
|
|
2860
3291
|
|
|
@@ -2864,7 +3295,7 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2864
3295
|
startArrow;
|
|
2865
3296
|
endArrow;
|
|
2866
3297
|
orientation;
|
|
2867
|
-
|
|
3298
|
+
_focusable = true;
|
|
2868
3299
|
_scrollSize = 0;
|
|
2869
3300
|
_scrollPosition = 0;
|
|
2870
3301
|
_viewportSize = 0;
|
|
@@ -2897,6 +3328,7 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2897
3328
|
return;
|
|
2898
3329
|
this._scrollSize = value;
|
|
2899
3330
|
this.recalculateVisibility();
|
|
3331
|
+
this.updateSliderFromScrollState();
|
|
2900
3332
|
this.scrollPosition = this.scrollPosition;
|
|
2901
3333
|
}
|
|
2902
3334
|
set scrollPosition(value) {
|
|
@@ -2904,15 +3336,15 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2904
3336
|
if (newPosition !== this._scrollPosition) {
|
|
2905
3337
|
this._scrollPosition = newPosition;
|
|
2906
3338
|
this.updateSliderFromScrollState();
|
|
2907
|
-
this._onChange?.(newPosition);
|
|
2908
|
-
this.emit("change", { position: newPosition });
|
|
2909
3339
|
}
|
|
2910
3340
|
}
|
|
2911
3341
|
set viewportSize(value) {
|
|
2912
3342
|
if (value === this.viewportSize)
|
|
2913
3343
|
return;
|
|
2914
3344
|
this._viewportSize = value;
|
|
3345
|
+
this.slider.viewPortSize = Math.max(1, this._viewportSize);
|
|
2915
3346
|
this.recalculateVisibility();
|
|
3347
|
+
this.updateSliderFromScrollState();
|
|
2916
3348
|
this.scrollPosition = this.scrollPosition;
|
|
2917
3349
|
}
|
|
2918
3350
|
get showArrows() {
|
|
@@ -2935,16 +3367,22 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2935
3367
|
this._onChange = options.onChange;
|
|
2936
3368
|
this.orientation = orientation;
|
|
2937
3369
|
this._showArrows = showArrows;
|
|
3370
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
3371
|
+
const defaultStepSize = Math.max(1, this._viewportSize);
|
|
3372
|
+
const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
|
|
2938
3373
|
this.slider = new SliderRenderable(ctx, {
|
|
2939
3374
|
orientation,
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
3375
|
+
min: 0,
|
|
3376
|
+
max: scrollRange,
|
|
3377
|
+
value: this._scrollPosition,
|
|
3378
|
+
viewPortSize: stepSize,
|
|
3379
|
+
onChange: (value) => {
|
|
3380
|
+
this._scrollPosition = Math.round(value);
|
|
2943
3381
|
this._onChange?.(this._scrollPosition);
|
|
2944
3382
|
this.emit("change", { position: this._scrollPosition });
|
|
2945
3383
|
},
|
|
2946
3384
|
...orientation === "vertical" ? {
|
|
2947
|
-
width: 2,
|
|
3385
|
+
width: Math.max(1, Math.min(2, this.width)),
|
|
2948
3386
|
height: "100%",
|
|
2949
3387
|
marginLeft: "auto"
|
|
2950
3388
|
} : {
|
|
@@ -3023,17 +3461,10 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3023
3461
|
this.requestRender();
|
|
3024
3462
|
}
|
|
3025
3463
|
updateSliderFromScrollState() {
|
|
3026
|
-
const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
|
|
3027
3464
|
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
} else {
|
|
3032
|
-
const sizeRatio = this._viewportSize / this._scrollSize;
|
|
3033
|
-
this.slider.thumbSize = Math.max(1, Math.round(sizeRatio * trackSize));
|
|
3034
|
-
const positionRatio = this._scrollPosition / scrollRange;
|
|
3035
|
-
this.slider.thumbPosition = Math.max(0, Math.min(1, positionRatio));
|
|
3036
|
-
}
|
|
3465
|
+
this.slider.min = 0;
|
|
3466
|
+
this.slider.max = scrollRange;
|
|
3467
|
+
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
3037
3468
|
}
|
|
3038
3469
|
scrollBy(delta, unit = "absolute") {
|
|
3039
3470
|
const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
|
|
@@ -3103,10 +3534,10 @@ class ArrowRenderable extends Renderable {
|
|
|
3103
3534
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
|
|
3104
3535
|
this._attributes = options.attributes ?? 0;
|
|
3105
3536
|
this._arrowChars = {
|
|
3106
|
-
up: "\
|
|
3107
|
-
down: "\
|
|
3108
|
-
left: "
|
|
3109
|
-
right: "
|
|
3537
|
+
up: "\u25B2",
|
|
3538
|
+
down: "\u25BC",
|
|
3539
|
+
left: "\u25C0",
|
|
3540
|
+
right: "\u25B6",
|
|
3110
3541
|
...options.arrowChars
|
|
3111
3542
|
};
|
|
3112
3543
|
if (!options.width) {
|
|
@@ -3184,7 +3615,7 @@ class ContentRenderable extends BoxRenderable {
|
|
|
3184
3615
|
this.viewport = viewport;
|
|
3185
3616
|
}
|
|
3186
3617
|
_getChildren() {
|
|
3187
|
-
return this.
|
|
3618
|
+
return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis);
|
|
3188
3619
|
}
|
|
3189
3620
|
}
|
|
3190
3621
|
|
|
@@ -3196,7 +3627,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3196
3627
|
content;
|
|
3197
3628
|
horizontalScrollBar;
|
|
3198
3629
|
verticalScrollBar;
|
|
3199
|
-
|
|
3630
|
+
_focusable = true;
|
|
3200
3631
|
selectionListener;
|
|
3201
3632
|
autoScrollMouseX = 0;
|
|
3202
3633
|
autoScrollMouseY = 0;
|
|
@@ -3209,17 +3640,42 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3209
3640
|
cachedAutoScrollSpeed = 3;
|
|
3210
3641
|
autoScrollAccumulatorX = 0;
|
|
3211
3642
|
autoScrollAccumulatorY = 0;
|
|
3643
|
+
_stickyScroll;
|
|
3644
|
+
_stickyScrollTop = false;
|
|
3645
|
+
_stickyScrollBottom = false;
|
|
3646
|
+
_stickyScrollLeft = false;
|
|
3647
|
+
_stickyScrollRight = false;
|
|
3648
|
+
_stickyStart;
|
|
3649
|
+
_hasManualScroll = false;
|
|
3650
|
+
get stickyScroll() {
|
|
3651
|
+
return this._stickyScroll;
|
|
3652
|
+
}
|
|
3653
|
+
set stickyScroll(value) {
|
|
3654
|
+
this._stickyScroll = value;
|
|
3655
|
+
this.updateStickyState();
|
|
3656
|
+
}
|
|
3657
|
+
get stickyStart() {
|
|
3658
|
+
return this._stickyStart;
|
|
3659
|
+
}
|
|
3660
|
+
set stickyStart(value) {
|
|
3661
|
+
this._stickyStart = value;
|
|
3662
|
+
this.updateStickyState();
|
|
3663
|
+
}
|
|
3212
3664
|
get scrollTop() {
|
|
3213
3665
|
return this.verticalScrollBar.scrollPosition;
|
|
3214
3666
|
}
|
|
3215
3667
|
set scrollTop(value) {
|
|
3216
3668
|
this.verticalScrollBar.scrollPosition = value;
|
|
3669
|
+
this._hasManualScroll = true;
|
|
3670
|
+
this.updateStickyState();
|
|
3217
3671
|
}
|
|
3218
3672
|
get scrollLeft() {
|
|
3219
3673
|
return this.horizontalScrollBar.scrollPosition;
|
|
3220
3674
|
}
|
|
3221
3675
|
set scrollLeft(value) {
|
|
3222
3676
|
this.horizontalScrollBar.scrollPosition = value;
|
|
3677
|
+
this._hasManualScroll = true;
|
|
3678
|
+
this.updateStickyState();
|
|
3223
3679
|
}
|
|
3224
3680
|
get scrollWidth() {
|
|
3225
3681
|
return this.horizontalScrollBar.scrollSize;
|
|
@@ -3227,6 +3683,56 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3227
3683
|
get scrollHeight() {
|
|
3228
3684
|
return this.verticalScrollBar.scrollSize;
|
|
3229
3685
|
}
|
|
3686
|
+
updateStickyState() {
|
|
3687
|
+
if (!this._stickyScroll)
|
|
3688
|
+
return;
|
|
3689
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3690
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3691
|
+
if (this.scrollTop <= 0) {
|
|
3692
|
+
this._stickyScrollTop = true;
|
|
3693
|
+
this._stickyScrollBottom = false;
|
|
3694
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
3695
|
+
this._stickyScrollTop = false;
|
|
3696
|
+
this._stickyScrollBottom = true;
|
|
3697
|
+
} else {
|
|
3698
|
+
this._stickyScrollTop = false;
|
|
3699
|
+
this._stickyScrollBottom = false;
|
|
3700
|
+
}
|
|
3701
|
+
if (this.scrollLeft <= 0) {
|
|
3702
|
+
this._stickyScrollLeft = true;
|
|
3703
|
+
this._stickyScrollRight = false;
|
|
3704
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
3705
|
+
this._stickyScrollLeft = false;
|
|
3706
|
+
this._stickyScrollRight = true;
|
|
3707
|
+
} else {
|
|
3708
|
+
this._stickyScrollLeft = false;
|
|
3709
|
+
this._stickyScrollRight = false;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
applyStickyStart(stickyStart) {
|
|
3713
|
+
switch (stickyStart) {
|
|
3714
|
+
case "top":
|
|
3715
|
+
this._stickyScrollTop = true;
|
|
3716
|
+
this._stickyScrollBottom = false;
|
|
3717
|
+
this.verticalScrollBar.scrollPosition = 0;
|
|
3718
|
+
break;
|
|
3719
|
+
case "bottom":
|
|
3720
|
+
this._stickyScrollTop = false;
|
|
3721
|
+
this._stickyScrollBottom = true;
|
|
3722
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3723
|
+
break;
|
|
3724
|
+
case "left":
|
|
3725
|
+
this._stickyScrollLeft = true;
|
|
3726
|
+
this._stickyScrollRight = false;
|
|
3727
|
+
this.horizontalScrollBar.scrollPosition = 0;
|
|
3728
|
+
break;
|
|
3729
|
+
case "right":
|
|
3730
|
+
this._stickyScrollLeft = false;
|
|
3731
|
+
this._stickyScrollRight = true;
|
|
3732
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3733
|
+
break;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3230
3736
|
constructor(ctx, {
|
|
3231
3737
|
wrapperOptions,
|
|
3232
3738
|
viewportOptions,
|
|
@@ -3235,6 +3741,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3235
3741
|
scrollbarOptions,
|
|
3236
3742
|
verticalScrollbarOptions,
|
|
3237
3743
|
horizontalScrollbarOptions,
|
|
3744
|
+
stickyScroll = false,
|
|
3745
|
+
stickyStart,
|
|
3238
3746
|
...options
|
|
3239
3747
|
}) {
|
|
3240
3748
|
super(ctx, {
|
|
@@ -3247,6 +3755,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3247
3755
|
...rootOptions
|
|
3248
3756
|
});
|
|
3249
3757
|
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
3758
|
+
this._stickyScroll = stickyScroll;
|
|
3759
|
+
this._stickyStart = stickyStart;
|
|
3250
3760
|
this.wrapper = new BoxRenderable(ctx, {
|
|
3251
3761
|
flexDirection: "column",
|
|
3252
3762
|
flexGrow: 1,
|
|
@@ -3295,6 +3805,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3295
3805
|
orientation: "vertical",
|
|
3296
3806
|
onChange: (position) => {
|
|
3297
3807
|
this.content.translateY = -position;
|
|
3808
|
+
this._hasManualScroll = true;
|
|
3809
|
+
this.updateStickyState();
|
|
3298
3810
|
}
|
|
3299
3811
|
});
|
|
3300
3812
|
super.add(this.verticalScrollBar);
|
|
@@ -3309,10 +3821,15 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3309
3821
|
orientation: "horizontal",
|
|
3310
3822
|
onChange: (position) => {
|
|
3311
3823
|
this.content.translateX = -position;
|
|
3824
|
+
this._hasManualScroll = true;
|
|
3825
|
+
this.updateStickyState();
|
|
3312
3826
|
}
|
|
3313
3827
|
});
|
|
3314
3828
|
this.wrapper.add(this.horizontalScrollBar);
|
|
3315
3829
|
this.recalculateBarProps();
|
|
3830
|
+
if (stickyStart && stickyScroll) {
|
|
3831
|
+
this.applyStickyStart(stickyStart);
|
|
3832
|
+
}
|
|
3316
3833
|
this.selectionListener = () => {
|
|
3317
3834
|
const selection = this._ctx.getSelection();
|
|
3318
3835
|
if (!selection || !selection.isSelecting) {
|
|
@@ -3331,6 +3848,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3331
3848
|
this.verticalScrollBar.scrollBy(delta.y, unit);
|
|
3332
3849
|
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
3333
3850
|
}
|
|
3851
|
+
this._hasManualScroll = true;
|
|
3334
3852
|
}
|
|
3335
3853
|
scrollTo(position) {
|
|
3336
3854
|
if (typeof position === "number") {
|
|
@@ -3362,6 +3880,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3362
3880
|
this.scrollLeft -= event.scroll?.delta ?? 0;
|
|
3363
3881
|
else if (dir === "right")
|
|
3364
3882
|
this.scrollLeft += event.scroll?.delta ?? 0;
|
|
3883
|
+
this._hasManualScroll = true;
|
|
3365
3884
|
}
|
|
3366
3885
|
if (event.type === "drag" && event.isSelecting) {
|
|
3367
3886
|
this.updateAutoScroll(event.x, event.y);
|
|
@@ -3370,10 +3889,14 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3370
3889
|
}
|
|
3371
3890
|
}
|
|
3372
3891
|
handleKeyPress(key) {
|
|
3373
|
-
if (this.verticalScrollBar.handleKeyPress(key))
|
|
3892
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
3893
|
+
this._hasManualScroll = true;
|
|
3374
3894
|
return true;
|
|
3375
|
-
|
|
3895
|
+
}
|
|
3896
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
3897
|
+
this._hasManualScroll = true;
|
|
3376
3898
|
return true;
|
|
3899
|
+
}
|
|
3377
3900
|
return false;
|
|
3378
3901
|
}
|
|
3379
3902
|
startAutoScroll(mouseX, mouseY) {
|
|
@@ -3487,6 +4010,27 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3487
4010
|
this.verticalScrollBar.viewportSize = this.viewport.height;
|
|
3488
4011
|
this.horizontalScrollBar.scrollSize = this.content.width;
|
|
3489
4012
|
this.horizontalScrollBar.viewportSize = this.viewport.width;
|
|
4013
|
+
if (this._stickyScroll) {
|
|
4014
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
4015
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
4016
|
+
if (this._stickyStart && !this._hasManualScroll) {
|
|
4017
|
+
this.applyStickyStart(this._stickyStart);
|
|
4018
|
+
} else {
|
|
4019
|
+
if (this._stickyScrollTop) {
|
|
4020
|
+
this.scrollTop = 0;
|
|
4021
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
4022
|
+
this.scrollTop = newMaxScrollTop;
|
|
4023
|
+
}
|
|
4024
|
+
if (this._stickyScrollLeft) {
|
|
4025
|
+
this.scrollLeft = 0;
|
|
4026
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
4027
|
+
this.scrollLeft = newMaxScrollLeft;
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
process.nextTick(() => {
|
|
4032
|
+
this.requestRender();
|
|
4033
|
+
});
|
|
3490
4034
|
}
|
|
3491
4035
|
set rootOptions(options) {
|
|
3492
4036
|
Object.assign(this, options);
|
|
@@ -3550,6 +4094,37 @@ function TabSelect(props, ...children) {
|
|
|
3550
4094
|
function FrameBuffer(props, ...children) {
|
|
3551
4095
|
return h(FrameBufferRenderable, props, ...children);
|
|
3552
4096
|
}
|
|
4097
|
+
function StyledText2(props, ...children) {
|
|
4098
|
+
const styledProps = props;
|
|
4099
|
+
const textNodeOptions = {
|
|
4100
|
+
...styledProps,
|
|
4101
|
+
attributes: styledProps?.attributes ?? 0
|
|
4102
|
+
};
|
|
4103
|
+
const textNode = new TextNodeRenderable(textNodeOptions);
|
|
4104
|
+
for (const child of children) {
|
|
4105
|
+
textNode.add(child);
|
|
4106
|
+
}
|
|
4107
|
+
return textNode;
|
|
4108
|
+
}
|
|
4109
|
+
var vstyles = {
|
|
4110
|
+
bold: (...children) => StyledText2({ attributes: TextAttributes.BOLD }, ...children),
|
|
4111
|
+
italic: (...children) => StyledText2({ attributes: TextAttributes.ITALIC }, ...children),
|
|
4112
|
+
underline: (...children) => StyledText2({ attributes: TextAttributes.UNDERLINE }, ...children),
|
|
4113
|
+
dim: (...children) => StyledText2({ attributes: TextAttributes.DIM }, ...children),
|
|
4114
|
+
blink: (...children) => StyledText2({ attributes: TextAttributes.BLINK }, ...children),
|
|
4115
|
+
inverse: (...children) => StyledText2({ attributes: TextAttributes.INVERSE }, ...children),
|
|
4116
|
+
hidden: (...children) => StyledText2({ attributes: TextAttributes.HIDDEN }, ...children),
|
|
4117
|
+
strikethrough: (...children) => StyledText2({ attributes: TextAttributes.STRIKETHROUGH }, ...children),
|
|
4118
|
+
boldItalic: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC }, ...children),
|
|
4119
|
+
boldUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE }, ...children),
|
|
4120
|
+
italicUnderline: (...children) => StyledText2({ attributes: TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4121
|
+
boldItalicUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4122
|
+
color: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4123
|
+
bgColor: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4124
|
+
fg: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4125
|
+
bg: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4126
|
+
styled: (attributes = 0, ...children) => StyledText2({ attributes }, ...children)
|
|
4127
|
+
};
|
|
3553
4128
|
// src/renderables/composition/VRenderable.ts
|
|
3554
4129
|
class VRenderable extends Renderable {
|
|
3555
4130
|
options;
|
|
@@ -3567,6 +4142,8 @@ export {
|
|
|
3567
4142
|
yellow,
|
|
3568
4143
|
wrapWithDelegates,
|
|
3569
4144
|
white,
|
|
4145
|
+
vstyles,
|
|
4146
|
+
visualizeRenderableTree,
|
|
3570
4147
|
underline,
|
|
3571
4148
|
t,
|
|
3572
4149
|
stringToStyledText,
|
|
@@ -3601,6 +4178,8 @@ export {
|
|
|
3601
4178
|
italic,
|
|
3602
4179
|
isValidPercentage,
|
|
3603
4180
|
isVNode,
|
|
4181
|
+
isTextNodeRenderable,
|
|
4182
|
+
isStyledText,
|
|
3604
4183
|
isSizeType,
|
|
3605
4184
|
isRenderable,
|
|
3606
4185
|
isPositionTypeType,
|
|
@@ -3667,6 +4246,7 @@ export {
|
|
|
3667
4246
|
TrackedNode,
|
|
3668
4247
|
Timeline,
|
|
3669
4248
|
TextRenderable,
|
|
4249
|
+
TextNodeRenderable,
|
|
3670
4250
|
TextBuffer,
|
|
3671
4251
|
TextAttributes,
|
|
3672
4252
|
Text,
|
|
@@ -3682,6 +4262,7 @@ export {
|
|
|
3682
4262
|
Select,
|
|
3683
4263
|
ScrollBoxRenderable,
|
|
3684
4264
|
ScrollBarRenderable,
|
|
4265
|
+
RootTextNodeRenderable,
|
|
3685
4266
|
RootRenderable,
|
|
3686
4267
|
RenderableEvents,
|
|
3687
4268
|
Renderable,
|
|
@@ -3711,11 +4292,12 @@ export {
|
|
|
3711
4292
|
BorderCharArrays,
|
|
3712
4293
|
BlurEffect,
|
|
3713
4294
|
BloomEffect,
|
|
4295
|
+
BaseRenderable,
|
|
3714
4296
|
ArrowRenderable,
|
|
3715
4297
|
ASCIIFontSelectionHelper,
|
|
3716
4298
|
ASCIIFontRenderable,
|
|
3717
4299
|
ASCIIFont
|
|
3718
4300
|
};
|
|
3719
4301
|
|
|
3720
|
-
//# debugId=
|
|
4302
|
+
//# debugId=E23A05456FB5EA0464756E2164756E21
|
|
3721
4303
|
//# sourceMappingURL=index.js.map
|