@opentui/core 0.0.0-20250908-4906ddad → 0.0.0-20250912-12c969f4
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/Renderable.d.ts +60 -18
- package/buffer.d.ts +2 -1
- package/{index-d6kwx5pm.js → index-mh94hn7d.js} +328 -155
- package/index-mh94hn7d.js.map +37 -0
- package/index.js +647 -84
- package/index.js.map +14 -13
- package/lib/objects-in-viewport.d.ts +10 -0
- package/lib/styled-text.d.ts +4 -0
- package/package.json +7 -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 +64 -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 +10 -2
- package/testing/mock-keys.d.ts +67 -0
- package/testing/mock-mouse.d.ts +38 -0
- package/testing/test-renderer.d.ts +15 -0
- package/text-buffer.d.ts +3 -2
- package/types.d.ts +6 -0
- package/utils.d.ts +2 -0
- package/zig.d.ts +3 -1
- 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-mh94hn7d.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,213 @@ 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
|
+
set fg(fg2) {
|
|
1630
|
+
this._fg = parseColor(fg2);
|
|
1631
|
+
this.requestRender();
|
|
1632
|
+
}
|
|
1633
|
+
set bg(bg2) {
|
|
1634
|
+
this._bg = parseColor(bg2);
|
|
1635
|
+
this.requestRender();
|
|
1636
|
+
}
|
|
1637
|
+
set attributes(attributes) {
|
|
1638
|
+
this._attributes = attributes;
|
|
1639
|
+
this.requestRender();
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
class RootTextNodeRenderable extends TextNodeRenderable {
|
|
1644
|
+
ctx;
|
|
1645
|
+
textParent;
|
|
1646
|
+
constructor(ctx, options, textParent) {
|
|
1647
|
+
super(options);
|
|
1648
|
+
this.ctx = ctx;
|
|
1649
|
+
this.textParent = textParent;
|
|
1650
|
+
}
|
|
1651
|
+
requestRender() {
|
|
1652
|
+
this.markDirty();
|
|
1653
|
+
this.ctx.requestRender();
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1446
1657
|
// src/renderables/Text.ts
|
|
1447
1658
|
class TextRenderable extends Renderable {
|
|
1448
1659
|
selectable = true;
|
|
@@ -1455,6 +1666,7 @@ class TextRenderable extends Renderable {
|
|
|
1455
1666
|
lastLocalSelection = null;
|
|
1456
1667
|
textBuffer;
|
|
1457
1668
|
_lineInfo = { lineStarts: [], lineWidths: [] };
|
|
1669
|
+
rootTextNode;
|
|
1458
1670
|
_defaultOptions = {
|
|
1459
1671
|
content: "",
|
|
1460
1672
|
fg: RGBA.fromValues(1, 1, 1, 1),
|
|
@@ -1480,6 +1692,12 @@ class TextRenderable extends Renderable {
|
|
|
1480
1692
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1481
1693
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
1482
1694
|
this.setupMeasureFunc();
|
|
1695
|
+
this.rootTextNode = new RootTextNodeRenderable(ctx, {
|
|
1696
|
+
id: `${this.id}-root`,
|
|
1697
|
+
fg: this._defaultFg,
|
|
1698
|
+
bg: this._defaultBg,
|
|
1699
|
+
attributes: this._defaultAttributes
|
|
1700
|
+
}, this);
|
|
1483
1701
|
this.updateTextBuffer(styledText);
|
|
1484
1702
|
this._text.mount(this);
|
|
1485
1703
|
this.updateTextInfo();
|
|
@@ -1488,12 +1706,7 @@ class TextRenderable extends Renderable {
|
|
|
1488
1706
|
this.textBuffer.setStyledText(styledText);
|
|
1489
1707
|
this.clearChunks(styledText);
|
|
1490
1708
|
}
|
|
1491
|
-
clearChunks(styledText) {
|
|
1492
|
-
styledText.chunks.forEach((chunk) => {
|
|
1493
|
-
chunk.text = undefined;
|
|
1494
|
-
chunk.plainText = undefined;
|
|
1495
|
-
});
|
|
1496
|
-
}
|
|
1709
|
+
clearChunks(styledText) {}
|
|
1497
1710
|
get content() {
|
|
1498
1711
|
return this._text;
|
|
1499
1712
|
}
|
|
@@ -1506,6 +1719,9 @@ class TextRenderable extends Renderable {
|
|
|
1506
1719
|
get chunks() {
|
|
1507
1720
|
return this._text.chunks;
|
|
1508
1721
|
}
|
|
1722
|
+
get textNode() {
|
|
1723
|
+
return this.rootTextNode;
|
|
1724
|
+
}
|
|
1509
1725
|
set content(value) {
|
|
1510
1726
|
const styledText = typeof value === "string" ? stringToStyledText(value) : value;
|
|
1511
1727
|
if (this._text !== styledText) {
|
|
@@ -1520,9 +1736,11 @@ class TextRenderable extends Renderable {
|
|
|
1520
1736
|
}
|
|
1521
1737
|
set fg(value) {
|
|
1522
1738
|
const newColor = parseColor(value ?? this._defaultOptions.fg);
|
|
1739
|
+
this.rootTextNode.fg = newColor;
|
|
1523
1740
|
if (this._defaultFg !== newColor) {
|
|
1524
1741
|
this._defaultFg = newColor;
|
|
1525
1742
|
this.textBuffer.setDefaultFg(this._defaultFg);
|
|
1743
|
+
this.rootTextNode.fg = newColor;
|
|
1526
1744
|
this.requestRender();
|
|
1527
1745
|
}
|
|
1528
1746
|
}
|
|
@@ -1557,9 +1775,11 @@ class TextRenderable extends Renderable {
|
|
|
1557
1775
|
}
|
|
1558
1776
|
set bg(value) {
|
|
1559
1777
|
const newColor = parseColor(value ?? this._defaultOptions.bg);
|
|
1778
|
+
this.rootTextNode.bg = newColor;
|
|
1560
1779
|
if (this._defaultBg !== newColor) {
|
|
1561
1780
|
this._defaultBg = newColor;
|
|
1562
1781
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1782
|
+
this.rootTextNode.bg = newColor;
|
|
1563
1783
|
this.requestRender();
|
|
1564
1784
|
}
|
|
1565
1785
|
}
|
|
@@ -1570,6 +1790,7 @@ class TextRenderable extends Renderable {
|
|
|
1570
1790
|
if (this._defaultAttributes !== value) {
|
|
1571
1791
|
this._defaultAttributes = value;
|
|
1572
1792
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
1793
|
+
this.rootTextNode.attributes = value;
|
|
1573
1794
|
this.requestRender();
|
|
1574
1795
|
}
|
|
1575
1796
|
}
|
|
@@ -1625,7 +1846,7 @@ class TextRenderable extends Renderable {
|
|
|
1625
1846
|
this.layoutNode.yogaNode.setMeasureFunc(measureFunc);
|
|
1626
1847
|
}
|
|
1627
1848
|
insertChunk(chunk, index) {
|
|
1628
|
-
this.textBuffer.
|
|
1849
|
+
this.textBuffer.insertChunkGroup(index ?? this.textBuffer.chunkGroupCount, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
|
|
1629
1850
|
this.updateTextInfo();
|
|
1630
1851
|
this.clearChunks(this._text);
|
|
1631
1852
|
}
|
|
@@ -1641,10 +1862,46 @@ class TextRenderable extends Renderable {
|
|
|
1641
1862
|
const index = this._text.chunks.indexOf(oldChunk);
|
|
1642
1863
|
if (index === -1)
|
|
1643
1864
|
return;
|
|
1644
|
-
this.textBuffer.
|
|
1865
|
+
this.textBuffer.replaceChunkGroup(index, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
|
|
1645
1866
|
this.updateTextInfo();
|
|
1646
1867
|
this.clearChunks(this._text);
|
|
1647
1868
|
}
|
|
1869
|
+
updateTextFromNodes() {
|
|
1870
|
+
if (this.rootTextNode.isDirty) {
|
|
1871
|
+
const chunks = this.rootTextNode.gatherWithInheritedStyle({
|
|
1872
|
+
fg: this._defaultFg,
|
|
1873
|
+
bg: this._defaultBg,
|
|
1874
|
+
attributes: this._defaultAttributes
|
|
1875
|
+
});
|
|
1876
|
+
this.textBuffer.setStyledText(new StyledText(chunks));
|
|
1877
|
+
this.updateTextInfo();
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
add(obj, index) {
|
|
1881
|
+
return this.rootTextNode.add(obj, index);
|
|
1882
|
+
}
|
|
1883
|
+
remove(id) {
|
|
1884
|
+
const child = this.rootTextNode.getRenderable(id);
|
|
1885
|
+
if (child && isTextNodeRenderable(child)) {
|
|
1886
|
+
this.rootTextNode.remove(child);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
insertBefore(obj, anchor) {
|
|
1890
|
+
this.rootTextNode.insertBefore(obj, anchor);
|
|
1891
|
+
return this.rootTextNode.children.indexOf(obj);
|
|
1892
|
+
}
|
|
1893
|
+
getTextChildren() {
|
|
1894
|
+
return this.rootTextNode.getChildren();
|
|
1895
|
+
}
|
|
1896
|
+
clear() {
|
|
1897
|
+
this.rootTextNode.clear();
|
|
1898
|
+
const emptyStyledText = stringToStyledText("");
|
|
1899
|
+
this._text = emptyStyledText;
|
|
1900
|
+
emptyStyledText.mount(this);
|
|
1901
|
+
this.updateTextBuffer(emptyStyledText);
|
|
1902
|
+
this.updateTextInfo();
|
|
1903
|
+
this.requestRender();
|
|
1904
|
+
}
|
|
1648
1905
|
shouldStartSelection(x, y) {
|
|
1649
1906
|
if (!this.selectable)
|
|
1650
1907
|
return false;
|
|
@@ -1670,6 +1927,16 @@ class TextRenderable extends Renderable {
|
|
|
1670
1927
|
getSelection() {
|
|
1671
1928
|
return this.textBuffer.getSelection();
|
|
1672
1929
|
}
|
|
1930
|
+
onLifecyclePass = () => {
|
|
1931
|
+
this.updateTextFromNodes();
|
|
1932
|
+
};
|
|
1933
|
+
render(buffer, deltaTime) {
|
|
1934
|
+
if (!this.visible)
|
|
1935
|
+
return;
|
|
1936
|
+
this.markClean();
|
|
1937
|
+
this._ctx.addToHitGrid(this.x, this.y, this.width, this.height, this.num);
|
|
1938
|
+
this.renderSelf(buffer);
|
|
1939
|
+
}
|
|
1673
1940
|
renderSelf(buffer) {
|
|
1674
1941
|
if (this.textBuffer.ptr) {
|
|
1675
1942
|
const clipRect = {
|
|
@@ -1683,6 +1950,7 @@ class TextRenderable extends Renderable {
|
|
|
1683
1950
|
}
|
|
1684
1951
|
destroy() {
|
|
1685
1952
|
this.textBuffer.destroy();
|
|
1953
|
+
this.rootTextNode.children.length = 0;
|
|
1686
1954
|
super.destroy();
|
|
1687
1955
|
}
|
|
1688
1956
|
}
|
|
@@ -1795,6 +2063,8 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
|
|
|
1795
2063
|
this.renderFontToBuffer();
|
|
1796
2064
|
}
|
|
1797
2065
|
renderFontToBuffer() {
|
|
2066
|
+
if (this.isDestroyed)
|
|
2067
|
+
return;
|
|
1798
2068
|
this.frameBuffer.clear(this._bg);
|
|
1799
2069
|
renderFontToFrameBuffer(this.frameBuffer, {
|
|
1800
2070
|
text: this._text,
|
|
@@ -1842,7 +2112,7 @@ var InputRenderableEvents;
|
|
|
1842
2112
|
})(InputRenderableEvents ||= {});
|
|
1843
2113
|
|
|
1844
2114
|
class InputRenderable extends Renderable {
|
|
1845
|
-
|
|
2115
|
+
_focusable = true;
|
|
1846
2116
|
_value = "";
|
|
1847
2117
|
_cursorPosition = 0;
|
|
1848
2118
|
_placeholder;
|
|
@@ -2115,7 +2385,7 @@ var SelectRenderableEvents;
|
|
|
2115
2385
|
})(SelectRenderableEvents ||= {});
|
|
2116
2386
|
|
|
2117
2387
|
class SelectRenderable extends Renderable {
|
|
2118
|
-
|
|
2388
|
+
_focusable = true;
|
|
2119
2389
|
_options = [];
|
|
2120
2390
|
selectedIndex = 0;
|
|
2121
2391
|
scrollOffset = 0;
|
|
@@ -2451,7 +2721,7 @@ function calculateDynamicHeight(showUnderline, showDescription) {
|
|
|
2451
2721
|
}
|
|
2452
2722
|
|
|
2453
2723
|
class TabSelectRenderable extends Renderable {
|
|
2454
|
-
|
|
2724
|
+
_focusable = true;
|
|
2455
2725
|
_options = [];
|
|
2456
2726
|
selectedIndex = 0;
|
|
2457
2727
|
scrollOffset = 0;
|
|
@@ -2741,43 +3011,71 @@ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
|
2741
3011
|
|
|
2742
3012
|
class SliderRenderable extends Renderable {
|
|
2743
3013
|
orientation;
|
|
2744
|
-
|
|
2745
|
-
|
|
3014
|
+
_value;
|
|
3015
|
+
_min;
|
|
3016
|
+
_max;
|
|
3017
|
+
_viewPortSize;
|
|
2746
3018
|
_backgroundColor;
|
|
2747
3019
|
_foregroundColor;
|
|
2748
3020
|
_onChange;
|
|
2749
3021
|
constructor(ctx, options) {
|
|
2750
3022
|
super(ctx, options);
|
|
2751
3023
|
this.orientation = options.orientation;
|
|
2752
|
-
this.
|
|
2753
|
-
this.
|
|
3024
|
+
this._min = options.min ?? 0;
|
|
3025
|
+
this._max = options.max ?? 100;
|
|
3026
|
+
this._value = options.value ?? this._min;
|
|
3027
|
+
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
2754
3028
|
this._onChange = options.onChange;
|
|
2755
3029
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
2756
3030
|
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
2757
3031
|
this.setupMouseHandling();
|
|
2758
3032
|
}
|
|
2759
|
-
get
|
|
2760
|
-
return this.
|
|
3033
|
+
get value() {
|
|
3034
|
+
return this._value;
|
|
2761
3035
|
}
|
|
2762
|
-
set
|
|
2763
|
-
const clamped = Math.max(
|
|
2764
|
-
if (clamped !== this.
|
|
2765
|
-
this.
|
|
3036
|
+
set value(newValue) {
|
|
3037
|
+
const clamped = Math.max(this._min, Math.min(this._max, newValue));
|
|
3038
|
+
if (clamped !== this._value) {
|
|
3039
|
+
this._value = clamped;
|
|
3040
|
+
this._onChange?.(clamped);
|
|
3041
|
+
this.emit("change", { value: clamped });
|
|
2766
3042
|
this.requestRender();
|
|
2767
3043
|
}
|
|
2768
3044
|
}
|
|
2769
|
-
get
|
|
2770
|
-
return this.
|
|
3045
|
+
get min() {
|
|
3046
|
+
return this._min;
|
|
2771
3047
|
}
|
|
2772
|
-
set
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
this.
|
|
2776
|
-
|
|
2777
|
-
|
|
3048
|
+
set min(newMin) {
|
|
3049
|
+
if (newMin !== this._min) {
|
|
3050
|
+
this._min = newMin;
|
|
3051
|
+
if (this._value < newMin) {
|
|
3052
|
+
this.value = newMin;
|
|
3053
|
+
}
|
|
3054
|
+
this.requestRender();
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
get max() {
|
|
3058
|
+
return this._max;
|
|
3059
|
+
}
|
|
3060
|
+
set max(newMax) {
|
|
3061
|
+
if (newMax !== this._max) {
|
|
3062
|
+
this._max = newMax;
|
|
3063
|
+
if (this._value > newMax) {
|
|
3064
|
+
this.value = newMax;
|
|
3065
|
+
}
|
|
3066
|
+
this.requestRender();
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
set viewPortSize(size) {
|
|
3070
|
+
const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
|
|
3071
|
+
if (clampedSize !== this._viewPortSize) {
|
|
3072
|
+
this._viewPortSize = clampedSize;
|
|
2778
3073
|
this.requestRender();
|
|
2779
3074
|
}
|
|
2780
3075
|
}
|
|
3076
|
+
get viewPortSize() {
|
|
3077
|
+
return this._viewPortSize;
|
|
3078
|
+
}
|
|
2781
3079
|
get backgroundColor() {
|
|
2782
3080
|
return this._backgroundColor;
|
|
2783
3081
|
}
|
|
@@ -2792,69 +3090,183 @@ class SliderRenderable extends Renderable {
|
|
|
2792
3090
|
this._foregroundColor = parseColor(value);
|
|
2793
3091
|
this.requestRender();
|
|
2794
3092
|
}
|
|
3093
|
+
calculateDragOffsetVirtual(event) {
|
|
3094
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3095
|
+
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
3096
|
+
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
3097
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3098
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3099
|
+
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
3100
|
+
}
|
|
2795
3101
|
setupMouseHandling() {
|
|
2796
3102
|
let isDragging = false;
|
|
2797
|
-
let
|
|
3103
|
+
let dragOffsetVirtual = 0;
|
|
2798
3104
|
this.onMouseDown = (event) => {
|
|
2799
3105
|
event.stopPropagation();
|
|
2800
3106
|
event.preventDefault();
|
|
2801
|
-
|
|
2802
|
-
const
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3107
|
+
const thumb = this.getThumbRect();
|
|
3108
|
+
const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
|
|
3109
|
+
if (inThumb) {
|
|
3110
|
+
isDragging = true;
|
|
3111
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2806
3112
|
} else {
|
|
2807
|
-
|
|
3113
|
+
this.updateValueFromMouseDirect(event);
|
|
3114
|
+
isDragging = true;
|
|
3115
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2808
3116
|
}
|
|
2809
|
-
this.updatePositionFromMouse(event, relativeStartPos);
|
|
2810
3117
|
};
|
|
2811
3118
|
this.onMouseDrag = (event) => {
|
|
2812
3119
|
if (!isDragging)
|
|
2813
3120
|
return;
|
|
2814
3121
|
event.stopPropagation();
|
|
2815
|
-
this.
|
|
3122
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
2816
3123
|
};
|
|
2817
|
-
this.onMouseUp = () => {
|
|
3124
|
+
this.onMouseUp = (event) => {
|
|
3125
|
+
if (isDragging) {
|
|
3126
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
3127
|
+
}
|
|
2818
3128
|
isDragging = false;
|
|
2819
3129
|
};
|
|
2820
3130
|
}
|
|
2821
|
-
|
|
3131
|
+
updateValueFromMouseDirect(event) {
|
|
2822
3132
|
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2823
3133
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2824
3134
|
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
2825
|
-
const
|
|
2826
|
-
const
|
|
2827
|
-
const
|
|
2828
|
-
const
|
|
2829
|
-
this.
|
|
2830
|
-
|
|
2831
|
-
|
|
3135
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3136
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3137
|
+
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
3138
|
+
const range = this._max - this._min;
|
|
3139
|
+
const newValue = this._min + ratio * range;
|
|
3140
|
+
this.value = newValue;
|
|
3141
|
+
}
|
|
3142
|
+
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
3143
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2832
3144
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2833
|
-
const
|
|
2834
|
-
|
|
3145
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3146
|
+
const virtualTrackSize = trackSize * 2;
|
|
3147
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3148
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3149
|
+
const virtualMousePos = clampedMousePos * 2;
|
|
3150
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3151
|
+
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
3152
|
+
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
3153
|
+
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
3154
|
+
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
3155
|
+
const range = this._max - this._min;
|
|
3156
|
+
const newValue = this._min + ratio * range;
|
|
3157
|
+
this.value = newValue;
|
|
2835
3158
|
}
|
|
2836
3159
|
getThumbRect() {
|
|
2837
|
-
const
|
|
3160
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3161
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3162
|
+
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
3163
|
+
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
2838
3164
|
if (this.orientation === "vertical") {
|
|
2839
3165
|
return {
|
|
2840
3166
|
x: this.x,
|
|
2841
|
-
y: this.y +
|
|
3167
|
+
y: this.y + realThumbStart,
|
|
2842
3168
|
width: this.width,
|
|
2843
|
-
height:
|
|
3169
|
+
height: Math.max(1, realThumbSize)
|
|
2844
3170
|
};
|
|
2845
3171
|
} else {
|
|
2846
3172
|
return {
|
|
2847
|
-
x: this.x +
|
|
3173
|
+
x: this.x + realThumbStart,
|
|
2848
3174
|
y: this.y,
|
|
2849
|
-
width:
|
|
3175
|
+
width: Math.max(1, realThumbSize),
|
|
2850
3176
|
height: this.height
|
|
2851
3177
|
};
|
|
2852
3178
|
}
|
|
2853
3179
|
}
|
|
2854
3180
|
renderSelf(buffer) {
|
|
3181
|
+
if (this.orientation === "horizontal") {
|
|
3182
|
+
this.renderHorizontal(buffer);
|
|
3183
|
+
} else {
|
|
3184
|
+
this.renderVertical(buffer);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
renderHorizontal(buffer) {
|
|
3188
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3189
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3190
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
2855
3191
|
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
2856
|
-
const
|
|
2857
|
-
|
|
3192
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3193
|
+
const realEndCell = Math.floor((virtualThumbEnd - 1) / 2);
|
|
3194
|
+
const startX = Math.max(0, realStartCell);
|
|
3195
|
+
const endX = Math.min(this.width - 1, realEndCell);
|
|
3196
|
+
for (let realX = startX;realX <= endX; realX++) {
|
|
3197
|
+
const virtualCellStart = realX * 2;
|
|
3198
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3199
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3200
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3201
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3202
|
+
let char = " ";
|
|
3203
|
+
if (coverage >= 2) {
|
|
3204
|
+
char = "\u2588";
|
|
3205
|
+
} else {
|
|
3206
|
+
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
3207
|
+
if (isLeftHalf) {
|
|
3208
|
+
char = "\u258C";
|
|
3209
|
+
} else {
|
|
3210
|
+
char = "\u2590";
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
for (let y = 0;y < this.height; y++) {
|
|
3214
|
+
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
renderVertical(buffer) {
|
|
3219
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3220
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3221
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
3222
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
3223
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3224
|
+
const realEndCell = Math.floor((virtualThumbEnd - 1) / 2);
|
|
3225
|
+
const startY = Math.max(0, realStartCell);
|
|
3226
|
+
const endY = Math.min(this.height - 1, realEndCell);
|
|
3227
|
+
for (let realY = startY;realY <= endY; realY++) {
|
|
3228
|
+
const virtualCellStart = realY * 2;
|
|
3229
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3230
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3231
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3232
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3233
|
+
let char = " ";
|
|
3234
|
+
if (coverage >= 2) {
|
|
3235
|
+
char = "\u2588";
|
|
3236
|
+
} else if (coverage > 0) {
|
|
3237
|
+
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
3238
|
+
if (virtualPositionInCell === 0) {
|
|
3239
|
+
char = "\u2580";
|
|
3240
|
+
} else {
|
|
3241
|
+
char = "\u2584";
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
for (let x = 0;x < this.width; x++) {
|
|
3245
|
+
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
getVirtualThumbSize() {
|
|
3250
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3251
|
+
const range = this._max - this._min;
|
|
3252
|
+
if (range === 0)
|
|
3253
|
+
return virtualTrackSize;
|
|
3254
|
+
const viewportSize = Math.max(1, this._viewPortSize);
|
|
3255
|
+
const contentSize = range + viewportSize;
|
|
3256
|
+
if (contentSize <= viewportSize)
|
|
3257
|
+
return virtualTrackSize;
|
|
3258
|
+
const thumbRatio = viewportSize / contentSize;
|
|
3259
|
+
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
3260
|
+
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
3261
|
+
}
|
|
3262
|
+
getVirtualThumbStart() {
|
|
3263
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3264
|
+
const range = this._max - this._min;
|
|
3265
|
+
if (range === 0)
|
|
3266
|
+
return 0;
|
|
3267
|
+
const valueRatio = (this._value - this._min) / range;
|
|
3268
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3269
|
+
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
2858
3270
|
}
|
|
2859
3271
|
}
|
|
2860
3272
|
|
|
@@ -2864,7 +3276,7 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2864
3276
|
startArrow;
|
|
2865
3277
|
endArrow;
|
|
2866
3278
|
orientation;
|
|
2867
|
-
|
|
3279
|
+
_focusable = true;
|
|
2868
3280
|
_scrollSize = 0;
|
|
2869
3281
|
_scrollPosition = 0;
|
|
2870
3282
|
_viewportSize = 0;
|
|
@@ -2897,6 +3309,7 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2897
3309
|
return;
|
|
2898
3310
|
this._scrollSize = value;
|
|
2899
3311
|
this.recalculateVisibility();
|
|
3312
|
+
this.updateSliderFromScrollState();
|
|
2900
3313
|
this.scrollPosition = this.scrollPosition;
|
|
2901
3314
|
}
|
|
2902
3315
|
set scrollPosition(value) {
|
|
@@ -2904,15 +3317,15 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2904
3317
|
if (newPosition !== this._scrollPosition) {
|
|
2905
3318
|
this._scrollPosition = newPosition;
|
|
2906
3319
|
this.updateSliderFromScrollState();
|
|
2907
|
-
this._onChange?.(newPosition);
|
|
2908
|
-
this.emit("change", { position: newPosition });
|
|
2909
3320
|
}
|
|
2910
3321
|
}
|
|
2911
3322
|
set viewportSize(value) {
|
|
2912
3323
|
if (value === this.viewportSize)
|
|
2913
3324
|
return;
|
|
2914
3325
|
this._viewportSize = value;
|
|
3326
|
+
this.slider.viewPortSize = Math.max(1, this._viewportSize);
|
|
2915
3327
|
this.recalculateVisibility();
|
|
3328
|
+
this.updateSliderFromScrollState();
|
|
2916
3329
|
this.scrollPosition = this.scrollPosition;
|
|
2917
3330
|
}
|
|
2918
3331
|
get showArrows() {
|
|
@@ -2935,16 +3348,22 @@ class ScrollBarRenderable extends Renderable {
|
|
|
2935
3348
|
this._onChange = options.onChange;
|
|
2936
3349
|
this.orientation = orientation;
|
|
2937
3350
|
this._showArrows = showArrows;
|
|
3351
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
3352
|
+
const defaultStepSize = Math.max(1, this._viewportSize);
|
|
3353
|
+
const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
|
|
2938
3354
|
this.slider = new SliderRenderable(ctx, {
|
|
2939
3355
|
orientation,
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
3356
|
+
min: 0,
|
|
3357
|
+
max: scrollRange,
|
|
3358
|
+
value: this._scrollPosition,
|
|
3359
|
+
viewPortSize: stepSize,
|
|
3360
|
+
onChange: (value) => {
|
|
3361
|
+
this._scrollPosition = Math.round(value);
|
|
2943
3362
|
this._onChange?.(this._scrollPosition);
|
|
2944
3363
|
this.emit("change", { position: this._scrollPosition });
|
|
2945
3364
|
},
|
|
2946
3365
|
...orientation === "vertical" ? {
|
|
2947
|
-
width: 2,
|
|
3366
|
+
width: Math.max(1, Math.min(2, this.width)),
|
|
2948
3367
|
height: "100%",
|
|
2949
3368
|
marginLeft: "auto"
|
|
2950
3369
|
} : {
|
|
@@ -3023,17 +3442,10 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3023
3442
|
this.requestRender();
|
|
3024
3443
|
}
|
|
3025
3444
|
updateSliderFromScrollState() {
|
|
3026
|
-
const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
|
|
3027
3445
|
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
|
-
}
|
|
3446
|
+
this.slider.min = 0;
|
|
3447
|
+
this.slider.max = scrollRange;
|
|
3448
|
+
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
3037
3449
|
}
|
|
3038
3450
|
scrollBy(delta, unit = "absolute") {
|
|
3039
3451
|
const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
|
|
@@ -3103,10 +3515,10 @@ class ArrowRenderable extends Renderable {
|
|
|
3103
3515
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
|
|
3104
3516
|
this._attributes = options.attributes ?? 0;
|
|
3105
3517
|
this._arrowChars = {
|
|
3106
|
-
up: "\
|
|
3107
|
-
down: "\
|
|
3108
|
-
left: "
|
|
3109
|
-
right: "
|
|
3518
|
+
up: "\u25B2",
|
|
3519
|
+
down: "\u25BC",
|
|
3520
|
+
left: "\u25C0",
|
|
3521
|
+
right: "\u25B6",
|
|
3110
3522
|
...options.arrowChars
|
|
3111
3523
|
};
|
|
3112
3524
|
if (!options.width) {
|
|
@@ -3184,7 +3596,7 @@ class ContentRenderable extends BoxRenderable {
|
|
|
3184
3596
|
this.viewport = viewport;
|
|
3185
3597
|
}
|
|
3186
3598
|
_getChildren() {
|
|
3187
|
-
return this.
|
|
3599
|
+
return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis);
|
|
3188
3600
|
}
|
|
3189
3601
|
}
|
|
3190
3602
|
|
|
@@ -3196,7 +3608,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3196
3608
|
content;
|
|
3197
3609
|
horizontalScrollBar;
|
|
3198
3610
|
verticalScrollBar;
|
|
3199
|
-
|
|
3611
|
+
_focusable = true;
|
|
3200
3612
|
selectionListener;
|
|
3201
3613
|
autoScrollMouseX = 0;
|
|
3202
3614
|
autoScrollMouseY = 0;
|
|
@@ -3209,17 +3621,42 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3209
3621
|
cachedAutoScrollSpeed = 3;
|
|
3210
3622
|
autoScrollAccumulatorX = 0;
|
|
3211
3623
|
autoScrollAccumulatorY = 0;
|
|
3624
|
+
_stickyScroll;
|
|
3625
|
+
_stickyScrollTop = false;
|
|
3626
|
+
_stickyScrollBottom = false;
|
|
3627
|
+
_stickyScrollLeft = false;
|
|
3628
|
+
_stickyScrollRight = false;
|
|
3629
|
+
_stickyStart;
|
|
3630
|
+
_hasManualScroll = false;
|
|
3631
|
+
get stickyScroll() {
|
|
3632
|
+
return this._stickyScroll;
|
|
3633
|
+
}
|
|
3634
|
+
set stickyScroll(value) {
|
|
3635
|
+
this._stickyScroll = value;
|
|
3636
|
+
this.updateStickyState();
|
|
3637
|
+
}
|
|
3638
|
+
get stickyStart() {
|
|
3639
|
+
return this._stickyStart;
|
|
3640
|
+
}
|
|
3641
|
+
set stickyStart(value) {
|
|
3642
|
+
this._stickyStart = value;
|
|
3643
|
+
this.updateStickyState();
|
|
3644
|
+
}
|
|
3212
3645
|
get scrollTop() {
|
|
3213
3646
|
return this.verticalScrollBar.scrollPosition;
|
|
3214
3647
|
}
|
|
3215
3648
|
set scrollTop(value) {
|
|
3216
3649
|
this.verticalScrollBar.scrollPosition = value;
|
|
3650
|
+
this._hasManualScroll = true;
|
|
3651
|
+
this.updateStickyState();
|
|
3217
3652
|
}
|
|
3218
3653
|
get scrollLeft() {
|
|
3219
3654
|
return this.horizontalScrollBar.scrollPosition;
|
|
3220
3655
|
}
|
|
3221
3656
|
set scrollLeft(value) {
|
|
3222
3657
|
this.horizontalScrollBar.scrollPosition = value;
|
|
3658
|
+
this._hasManualScroll = true;
|
|
3659
|
+
this.updateStickyState();
|
|
3223
3660
|
}
|
|
3224
3661
|
get scrollWidth() {
|
|
3225
3662
|
return this.horizontalScrollBar.scrollSize;
|
|
@@ -3227,6 +3664,56 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3227
3664
|
get scrollHeight() {
|
|
3228
3665
|
return this.verticalScrollBar.scrollSize;
|
|
3229
3666
|
}
|
|
3667
|
+
updateStickyState() {
|
|
3668
|
+
if (!this._stickyScroll)
|
|
3669
|
+
return;
|
|
3670
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3671
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3672
|
+
if (this.scrollTop <= 0) {
|
|
3673
|
+
this._stickyScrollTop = true;
|
|
3674
|
+
this._stickyScrollBottom = false;
|
|
3675
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
3676
|
+
this._stickyScrollTop = false;
|
|
3677
|
+
this._stickyScrollBottom = true;
|
|
3678
|
+
} else {
|
|
3679
|
+
this._stickyScrollTop = false;
|
|
3680
|
+
this._stickyScrollBottom = false;
|
|
3681
|
+
}
|
|
3682
|
+
if (this.scrollLeft <= 0) {
|
|
3683
|
+
this._stickyScrollLeft = true;
|
|
3684
|
+
this._stickyScrollRight = false;
|
|
3685
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
3686
|
+
this._stickyScrollLeft = false;
|
|
3687
|
+
this._stickyScrollRight = true;
|
|
3688
|
+
} else {
|
|
3689
|
+
this._stickyScrollLeft = false;
|
|
3690
|
+
this._stickyScrollRight = false;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
applyStickyStart(stickyStart) {
|
|
3694
|
+
switch (stickyStart) {
|
|
3695
|
+
case "top":
|
|
3696
|
+
this._stickyScrollTop = true;
|
|
3697
|
+
this._stickyScrollBottom = false;
|
|
3698
|
+
this.verticalScrollBar.scrollPosition = 0;
|
|
3699
|
+
break;
|
|
3700
|
+
case "bottom":
|
|
3701
|
+
this._stickyScrollTop = false;
|
|
3702
|
+
this._stickyScrollBottom = true;
|
|
3703
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3704
|
+
break;
|
|
3705
|
+
case "left":
|
|
3706
|
+
this._stickyScrollLeft = true;
|
|
3707
|
+
this._stickyScrollRight = false;
|
|
3708
|
+
this.horizontalScrollBar.scrollPosition = 0;
|
|
3709
|
+
break;
|
|
3710
|
+
case "right":
|
|
3711
|
+
this._stickyScrollLeft = false;
|
|
3712
|
+
this._stickyScrollRight = true;
|
|
3713
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3714
|
+
break;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3230
3717
|
constructor(ctx, {
|
|
3231
3718
|
wrapperOptions,
|
|
3232
3719
|
viewportOptions,
|
|
@@ -3235,6 +3722,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3235
3722
|
scrollbarOptions,
|
|
3236
3723
|
verticalScrollbarOptions,
|
|
3237
3724
|
horizontalScrollbarOptions,
|
|
3725
|
+
stickyScroll = false,
|
|
3726
|
+
stickyStart,
|
|
3238
3727
|
...options
|
|
3239
3728
|
}) {
|
|
3240
3729
|
super(ctx, {
|
|
@@ -3247,6 +3736,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3247
3736
|
...rootOptions
|
|
3248
3737
|
});
|
|
3249
3738
|
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
3739
|
+
this._stickyScroll = stickyScroll;
|
|
3740
|
+
this._stickyStart = stickyStart;
|
|
3250
3741
|
this.wrapper = new BoxRenderable(ctx, {
|
|
3251
3742
|
flexDirection: "column",
|
|
3252
3743
|
flexGrow: 1,
|
|
@@ -3295,6 +3786,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3295
3786
|
orientation: "vertical",
|
|
3296
3787
|
onChange: (position) => {
|
|
3297
3788
|
this.content.translateY = -position;
|
|
3789
|
+
this._hasManualScroll = true;
|
|
3790
|
+
this.updateStickyState();
|
|
3298
3791
|
}
|
|
3299
3792
|
});
|
|
3300
3793
|
super.add(this.verticalScrollBar);
|
|
@@ -3309,10 +3802,15 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3309
3802
|
orientation: "horizontal",
|
|
3310
3803
|
onChange: (position) => {
|
|
3311
3804
|
this.content.translateX = -position;
|
|
3805
|
+
this._hasManualScroll = true;
|
|
3806
|
+
this.updateStickyState();
|
|
3312
3807
|
}
|
|
3313
3808
|
});
|
|
3314
3809
|
this.wrapper.add(this.horizontalScrollBar);
|
|
3315
3810
|
this.recalculateBarProps();
|
|
3811
|
+
if (stickyStart && stickyScroll) {
|
|
3812
|
+
this.applyStickyStart(stickyStart);
|
|
3813
|
+
}
|
|
3316
3814
|
this.selectionListener = () => {
|
|
3317
3815
|
const selection = this._ctx.getSelection();
|
|
3318
3816
|
if (!selection || !selection.isSelecting) {
|
|
@@ -3331,6 +3829,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3331
3829
|
this.verticalScrollBar.scrollBy(delta.y, unit);
|
|
3332
3830
|
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
3333
3831
|
}
|
|
3832
|
+
this._hasManualScroll = true;
|
|
3334
3833
|
}
|
|
3335
3834
|
scrollTo(position) {
|
|
3336
3835
|
if (typeof position === "number") {
|
|
@@ -3362,6 +3861,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3362
3861
|
this.scrollLeft -= event.scroll?.delta ?? 0;
|
|
3363
3862
|
else if (dir === "right")
|
|
3364
3863
|
this.scrollLeft += event.scroll?.delta ?? 0;
|
|
3864
|
+
this._hasManualScroll = true;
|
|
3365
3865
|
}
|
|
3366
3866
|
if (event.type === "drag" && event.isSelecting) {
|
|
3367
3867
|
this.updateAutoScroll(event.x, event.y);
|
|
@@ -3370,10 +3870,14 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3370
3870
|
}
|
|
3371
3871
|
}
|
|
3372
3872
|
handleKeyPress(key) {
|
|
3373
|
-
if (this.verticalScrollBar.handleKeyPress(key))
|
|
3873
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
3874
|
+
this._hasManualScroll = true;
|
|
3374
3875
|
return true;
|
|
3375
|
-
|
|
3876
|
+
}
|
|
3877
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
3878
|
+
this._hasManualScroll = true;
|
|
3376
3879
|
return true;
|
|
3880
|
+
}
|
|
3377
3881
|
return false;
|
|
3378
3882
|
}
|
|
3379
3883
|
startAutoScroll(mouseX, mouseY) {
|
|
@@ -3487,6 +3991,27 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3487
3991
|
this.verticalScrollBar.viewportSize = this.viewport.height;
|
|
3488
3992
|
this.horizontalScrollBar.scrollSize = this.content.width;
|
|
3489
3993
|
this.horizontalScrollBar.viewportSize = this.viewport.width;
|
|
3994
|
+
if (this._stickyScroll) {
|
|
3995
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3996
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3997
|
+
if (this._stickyStart && !this._hasManualScroll) {
|
|
3998
|
+
this.applyStickyStart(this._stickyStart);
|
|
3999
|
+
} else {
|
|
4000
|
+
if (this._stickyScrollTop) {
|
|
4001
|
+
this.scrollTop = 0;
|
|
4002
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
4003
|
+
this.scrollTop = newMaxScrollTop;
|
|
4004
|
+
}
|
|
4005
|
+
if (this._stickyScrollLeft) {
|
|
4006
|
+
this.scrollLeft = 0;
|
|
4007
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
4008
|
+
this.scrollLeft = newMaxScrollLeft;
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
process.nextTick(() => {
|
|
4013
|
+
this.requestRender();
|
|
4014
|
+
});
|
|
3490
4015
|
}
|
|
3491
4016
|
set rootOptions(options) {
|
|
3492
4017
|
Object.assign(this, options);
|
|
@@ -3550,6 +4075,37 @@ function TabSelect(props, ...children) {
|
|
|
3550
4075
|
function FrameBuffer(props, ...children) {
|
|
3551
4076
|
return h(FrameBufferRenderable, props, ...children);
|
|
3552
4077
|
}
|
|
4078
|
+
function StyledText2(props, ...children) {
|
|
4079
|
+
const styledProps = props;
|
|
4080
|
+
const textNodeOptions = {
|
|
4081
|
+
...styledProps,
|
|
4082
|
+
attributes: styledProps?.attributes ?? 0
|
|
4083
|
+
};
|
|
4084
|
+
const textNode = new TextNodeRenderable(textNodeOptions);
|
|
4085
|
+
for (const child of children) {
|
|
4086
|
+
textNode.add(child);
|
|
4087
|
+
}
|
|
4088
|
+
return textNode;
|
|
4089
|
+
}
|
|
4090
|
+
var vstyles = {
|
|
4091
|
+
bold: (...children) => StyledText2({ attributes: TextAttributes.BOLD }, ...children),
|
|
4092
|
+
italic: (...children) => StyledText2({ attributes: TextAttributes.ITALIC }, ...children),
|
|
4093
|
+
underline: (...children) => StyledText2({ attributes: TextAttributes.UNDERLINE }, ...children),
|
|
4094
|
+
dim: (...children) => StyledText2({ attributes: TextAttributes.DIM }, ...children),
|
|
4095
|
+
blink: (...children) => StyledText2({ attributes: TextAttributes.BLINK }, ...children),
|
|
4096
|
+
inverse: (...children) => StyledText2({ attributes: TextAttributes.INVERSE }, ...children),
|
|
4097
|
+
hidden: (...children) => StyledText2({ attributes: TextAttributes.HIDDEN }, ...children),
|
|
4098
|
+
strikethrough: (...children) => StyledText2({ attributes: TextAttributes.STRIKETHROUGH }, ...children),
|
|
4099
|
+
boldItalic: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC }, ...children),
|
|
4100
|
+
boldUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE }, ...children),
|
|
4101
|
+
italicUnderline: (...children) => StyledText2({ attributes: TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4102
|
+
boldItalicUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4103
|
+
color: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4104
|
+
bgColor: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4105
|
+
fg: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4106
|
+
bg: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4107
|
+
styled: (attributes = 0, ...children) => StyledText2({ attributes }, ...children)
|
|
4108
|
+
};
|
|
3553
4109
|
// src/renderables/composition/VRenderable.ts
|
|
3554
4110
|
class VRenderable extends Renderable {
|
|
3555
4111
|
options;
|
|
@@ -3567,6 +4123,8 @@ export {
|
|
|
3567
4123
|
yellow,
|
|
3568
4124
|
wrapWithDelegates,
|
|
3569
4125
|
white,
|
|
4126
|
+
vstyles,
|
|
4127
|
+
visualizeRenderableTree,
|
|
3570
4128
|
underline,
|
|
3571
4129
|
t,
|
|
3572
4130
|
stringToStyledText,
|
|
@@ -3601,6 +4159,8 @@ export {
|
|
|
3601
4159
|
italic,
|
|
3602
4160
|
isValidPercentage,
|
|
3603
4161
|
isVNode,
|
|
4162
|
+
isTextNodeRenderable,
|
|
4163
|
+
isStyledText,
|
|
3604
4164
|
isSizeType,
|
|
3605
4165
|
isRenderable,
|
|
3606
4166
|
isPositionTypeType,
|
|
@@ -3667,6 +4227,7 @@ export {
|
|
|
3667
4227
|
TrackedNode,
|
|
3668
4228
|
Timeline,
|
|
3669
4229
|
TextRenderable,
|
|
4230
|
+
TextNodeRenderable,
|
|
3670
4231
|
TextBuffer,
|
|
3671
4232
|
TextAttributes,
|
|
3672
4233
|
Text,
|
|
@@ -3682,6 +4243,7 @@ export {
|
|
|
3682
4243
|
Select,
|
|
3683
4244
|
ScrollBoxRenderable,
|
|
3684
4245
|
ScrollBarRenderable,
|
|
4246
|
+
RootTextNodeRenderable,
|
|
3685
4247
|
RootRenderable,
|
|
3686
4248
|
RenderableEvents,
|
|
3687
4249
|
Renderable,
|
|
@@ -3711,11 +4273,12 @@ export {
|
|
|
3711
4273
|
BorderCharArrays,
|
|
3712
4274
|
BlurEffect,
|
|
3713
4275
|
BloomEffect,
|
|
4276
|
+
BaseRenderable,
|
|
3714
4277
|
ArrowRenderable,
|
|
3715
4278
|
ASCIIFontSelectionHelper,
|
|
3716
4279
|
ASCIIFontRenderable,
|
|
3717
4280
|
ASCIIFont
|
|
3718
4281
|
};
|
|
3719
4282
|
|
|
3720
|
-
//# debugId=
|
|
4283
|
+
//# debugId=4D8A7644AF667D7864756E2164756E21
|
|
3721
4284
|
//# sourceMappingURL=index.js.map
|