@opentui/core 0.1.22 → 0.1.24
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 +22 -21
- package/buffer.d.ts +3 -1
- package/{index-443j38eh.js → index-0yx9rnxg.js} +981 -543
- package/index-0yx9rnxg.js.map +38 -0
- package/index.d.ts +1 -0
- package/index.js +447 -139
- package/index.js.map +10 -10
- package/lib/KeyHandler.d.ts +7 -3
- package/lib/index.d.ts +0 -1
- package/lib/parse.keypress-kitty.d.ts +2 -0
- package/lib/parse.keypress.d.ts +11 -1
- package/lib/renderable.validations.d.ts +12 -0
- package/package.json +14 -12
- package/renderables/ScrollBox.d.ts +18 -1
- package/renderables/Slider.d.ts +24 -11
- package/renderables/Text.d.ts +15 -3
- package/renderables/TextNode.d.ts +13 -4
- package/renderer.d.ts +16 -1
- package/testing/mock-mouse.d.ts +38 -0
- package/testing/test-renderer.d.ts +15 -3
- package/testing.d.ts +3 -0
- package/testing.js +343 -0
- package/testing.js.map +12 -0
- package/text-buffer.d.ts +8 -9
- package/types.d.ts +5 -0
- package/utils.d.ts +2 -0
- package/zig.d.ts +15 -10
- package/index-443j38eh.js.map +0 -37
- package/lib/TrackedNode.d.ts +0 -36
package/index.js
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
KeyHandler,
|
|
14
14
|
LayoutEvents,
|
|
15
15
|
LogLevel,
|
|
16
|
-
MeasureMode,
|
|
17
16
|
MouseButton,
|
|
18
17
|
MouseEvent,
|
|
19
18
|
MouseParser,
|
|
@@ -28,7 +27,6 @@ import {
|
|
|
28
27
|
TerminalConsole,
|
|
29
28
|
TextAttributes,
|
|
30
29
|
TextBuffer,
|
|
31
|
-
TrackedNode,
|
|
32
30
|
bg,
|
|
33
31
|
bgBlack,
|
|
34
32
|
bgBlue,
|
|
@@ -56,10 +54,10 @@ import {
|
|
|
56
54
|
coordinateToCharacterIndex,
|
|
57
55
|
createCliRenderer,
|
|
58
56
|
createTextAttributes,
|
|
59
|
-
createTrackedNode,
|
|
60
57
|
cyan,
|
|
61
58
|
delegate,
|
|
62
59
|
dim,
|
|
60
|
+
exports_src,
|
|
63
61
|
fg,
|
|
64
62
|
fonts,
|
|
65
63
|
getBorderFromSides,
|
|
@@ -73,15 +71,7 @@ import {
|
|
|
73
71
|
hexToRgb,
|
|
74
72
|
hsvToRgb,
|
|
75
73
|
instantiate,
|
|
76
|
-
isDimensionType,
|
|
77
|
-
isFlexBasisType,
|
|
78
|
-
isMarginType,
|
|
79
|
-
isOverflowType,
|
|
80
|
-
isPaddingType,
|
|
81
|
-
isPositionType,
|
|
82
|
-
isPositionTypeType,
|
|
83
74
|
isRenderable,
|
|
84
|
-
isSizeType,
|
|
85
75
|
isStyledText,
|
|
86
76
|
isVNode,
|
|
87
77
|
isValidPercentage,
|
|
@@ -117,10 +107,11 @@ import {
|
|
|
117
107
|
stringToStyledText,
|
|
118
108
|
t,
|
|
119
109
|
underline,
|
|
110
|
+
visualizeRenderableTree,
|
|
120
111
|
white,
|
|
121
112
|
wrapWithDelegates,
|
|
122
113
|
yellow
|
|
123
|
-
} from "./index-
|
|
114
|
+
} from "./index-0yx9rnxg.js";
|
|
124
115
|
// src/post/filters.ts
|
|
125
116
|
function applyScanlines(buffer, strength = 0.8, step = 2) {
|
|
126
117
|
const width = buffer.width;
|
|
@@ -1378,7 +1369,7 @@ class BoxRenderable extends Renderable {
|
|
|
1378
1369
|
};
|
|
1379
1370
|
}
|
|
1380
1371
|
applyYogaBorders() {
|
|
1381
|
-
const node = this.
|
|
1372
|
+
const node = this.yogaNode;
|
|
1382
1373
|
node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
|
|
1383
1374
|
node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
|
|
1384
1375
|
node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
|
|
@@ -1386,7 +1377,7 @@ class BoxRenderable extends Renderable {
|
|
|
1386
1377
|
this.requestRender();
|
|
1387
1378
|
}
|
|
1388
1379
|
applyYogaGap(options) {
|
|
1389
|
-
const node = this.
|
|
1380
|
+
const node = this.yogaNode;
|
|
1390
1381
|
if (isGapType(options.gap)) {
|
|
1391
1382
|
node.setGap(Gutter.All, options.gap);
|
|
1392
1383
|
}
|
|
@@ -1399,19 +1390,19 @@ class BoxRenderable extends Renderable {
|
|
|
1399
1390
|
}
|
|
1400
1391
|
set gap(gap) {
|
|
1401
1392
|
if (isGapType(gap)) {
|
|
1402
|
-
this.
|
|
1393
|
+
this.yogaNode.setGap(Gutter.All, gap);
|
|
1403
1394
|
this.requestRender();
|
|
1404
1395
|
}
|
|
1405
1396
|
}
|
|
1406
1397
|
set rowGap(rowGap) {
|
|
1407
1398
|
if (isGapType(rowGap)) {
|
|
1408
|
-
this.
|
|
1399
|
+
this.yogaNode.setGap(Gutter.Row, rowGap);
|
|
1409
1400
|
this.requestRender();
|
|
1410
1401
|
}
|
|
1411
1402
|
}
|
|
1412
1403
|
set columnGap(columnGap) {
|
|
1413
1404
|
if (isGapType(columnGap)) {
|
|
1414
|
-
this.
|
|
1405
|
+
this.yogaNode.setGap(Gutter.Column, columnGap);
|
|
1415
1406
|
this.requestRender();
|
|
1416
1407
|
}
|
|
1417
1408
|
}
|
|
@@ -1437,7 +1428,7 @@ class FrameBufferRenderable extends Renderable {
|
|
|
1437
1428
|
this.requestRender();
|
|
1438
1429
|
}
|
|
1439
1430
|
renderSelf(buffer) {
|
|
1440
|
-
if (!this.visible)
|
|
1431
|
+
if (!this.visible || this.isDestroyed)
|
|
1441
1432
|
return;
|
|
1442
1433
|
buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
|
|
1443
1434
|
}
|
|
@@ -1465,16 +1456,16 @@ function styledTextToTextNodes(styledText) {
|
|
|
1465
1456
|
|
|
1466
1457
|
class TextNodeRenderable extends BaseRenderable {
|
|
1467
1458
|
[BrandedTextNodeRenderable] = true;
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1459
|
+
_fg;
|
|
1460
|
+
_bg;
|
|
1461
|
+
_attributes;
|
|
1471
1462
|
_children = [];
|
|
1472
1463
|
parent = null;
|
|
1473
1464
|
constructor(options) {
|
|
1474
1465
|
super(options);
|
|
1475
|
-
this.
|
|
1476
|
-
this.
|
|
1477
|
-
this.
|
|
1466
|
+
this._fg = options.fg ? parseColor(options.fg) : undefined;
|
|
1467
|
+
this._bg = options.bg ? parseColor(options.bg) : undefined;
|
|
1468
|
+
this._attributes = options.attributes ?? 0;
|
|
1478
1469
|
}
|
|
1479
1470
|
get children() {
|
|
1480
1471
|
return this._children;
|
|
@@ -1528,6 +1519,13 @@ class TextNodeRenderable extends BaseRenderable {
|
|
|
1528
1519
|
}
|
|
1529
1520
|
throw new Error("TextNodeRenderable only accepts strings, TextNodeRenderable instances, or StyledText instances");
|
|
1530
1521
|
}
|
|
1522
|
+
replace(obj, index) {
|
|
1523
|
+
this._children[index] = obj;
|
|
1524
|
+
if (typeof obj !== "string") {
|
|
1525
|
+
obj.parent = this;
|
|
1526
|
+
}
|
|
1527
|
+
this.requestRender();
|
|
1528
|
+
}
|
|
1531
1529
|
insertBefore(child, anchorNode) {
|
|
1532
1530
|
if (!anchorNode || !isTextNodeRenderable(anchorNode)) {
|
|
1533
1531
|
throw new Error("Anchor must be a TextNodeRenderable");
|
|
@@ -1569,9 +1567,9 @@ class TextNodeRenderable extends BaseRenderable {
|
|
|
1569
1567
|
}
|
|
1570
1568
|
mergeStyles(parentStyle) {
|
|
1571
1569
|
return {
|
|
1572
|
-
fg: this.
|
|
1573
|
-
bg: this.
|
|
1574
|
-
attributes: this.
|
|
1570
|
+
fg: this._fg ?? parentStyle.fg,
|
|
1571
|
+
bg: this._bg ?? parentStyle.bg,
|
|
1572
|
+
attributes: this._attributes | parentStyle.attributes
|
|
1575
1573
|
};
|
|
1576
1574
|
}
|
|
1577
1575
|
gatherWithInheritedStyle(parentStyle = { fg: undefined, bg: undefined, attributes: 0 }) {
|
|
@@ -1618,13 +1616,46 @@ class TextNodeRenderable extends BaseRenderable {
|
|
|
1618
1616
|
getRenderable(id) {
|
|
1619
1617
|
return this._children.find((child) => typeof child !== "string" && child.id === id);
|
|
1620
1618
|
}
|
|
1619
|
+
get fg() {
|
|
1620
|
+
return this._fg;
|
|
1621
|
+
}
|
|
1622
|
+
set fg(fg2) {
|
|
1623
|
+
if (!fg2) {
|
|
1624
|
+
this._fg = undefined;
|
|
1625
|
+
this.requestRender();
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
this._fg = parseColor(fg2);
|
|
1629
|
+
this.requestRender();
|
|
1630
|
+
}
|
|
1631
|
+
set bg(bg2) {
|
|
1632
|
+
if (!bg2) {
|
|
1633
|
+
this._bg = undefined;
|
|
1634
|
+
this.requestRender();
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
this._bg = parseColor(bg2);
|
|
1638
|
+
this.requestRender();
|
|
1639
|
+
}
|
|
1640
|
+
get bg() {
|
|
1641
|
+
return this._bg;
|
|
1642
|
+
}
|
|
1643
|
+
set attributes(attributes) {
|
|
1644
|
+
this._attributes = attributes;
|
|
1645
|
+
this.requestRender();
|
|
1646
|
+
}
|
|
1647
|
+
get attributes() {
|
|
1648
|
+
return this._attributes;
|
|
1649
|
+
}
|
|
1621
1650
|
}
|
|
1622
1651
|
|
|
1623
1652
|
class RootTextNodeRenderable extends TextNodeRenderable {
|
|
1624
1653
|
ctx;
|
|
1625
|
-
|
|
1654
|
+
textParent;
|
|
1655
|
+
constructor(ctx, options, textParent) {
|
|
1626
1656
|
super(options);
|
|
1627
1657
|
this.ctx = ctx;
|
|
1658
|
+
this.textParent = textParent;
|
|
1628
1659
|
}
|
|
1629
1660
|
requestRender() {
|
|
1630
1661
|
this.markDirty();
|
|
@@ -1641,9 +1672,11 @@ class TextRenderable extends Renderable {
|
|
|
1641
1672
|
_defaultAttributes;
|
|
1642
1673
|
_selectionBg;
|
|
1643
1674
|
_selectionFg;
|
|
1675
|
+
_wrap = false;
|
|
1676
|
+
_wrapMode = "word";
|
|
1644
1677
|
lastLocalSelection = null;
|
|
1645
1678
|
textBuffer;
|
|
1646
|
-
_lineInfo = { lineStarts: [], lineWidths: [] };
|
|
1679
|
+
_lineInfo = { lineStarts: [], lineWidths: [], maxLineWidth: 0 };
|
|
1647
1680
|
rootTextNode;
|
|
1648
1681
|
_defaultOptions = {
|
|
1649
1682
|
content: "",
|
|
@@ -1652,7 +1685,9 @@ class TextRenderable extends Renderable {
|
|
|
1652
1685
|
selectionBg: undefined,
|
|
1653
1686
|
selectionFg: undefined,
|
|
1654
1687
|
selectable: true,
|
|
1655
|
-
attributes: 0
|
|
1688
|
+
attributes: 0,
|
|
1689
|
+
wrap: true,
|
|
1690
|
+
wrapMode: "word"
|
|
1656
1691
|
};
|
|
1657
1692
|
constructor(ctx, options) {
|
|
1658
1693
|
super(ctx, options);
|
|
@@ -1665,7 +1700,13 @@ class TextRenderable extends Renderable {
|
|
|
1665
1700
|
this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
|
|
1666
1701
|
this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
|
|
1667
1702
|
this.selectable = options.selectable ?? this._defaultOptions.selectable;
|
|
1668
|
-
this.
|
|
1703
|
+
this._wrap = options.wrap ?? this._defaultOptions.wrap;
|
|
1704
|
+
this._wrapMode = options.wrapMode ?? this._defaultOptions.wrapMode;
|
|
1705
|
+
this.textBuffer = TextBuffer.create(this._ctx.widthMethod);
|
|
1706
|
+
this.textBuffer.setWrapMode(this._wrapMode);
|
|
1707
|
+
if (this._wrap) {
|
|
1708
|
+
this.textBuffer.setWrapWidth(this.width > 0 ? this.width : 40);
|
|
1709
|
+
}
|
|
1669
1710
|
this.textBuffer.setDefaultFg(this._defaultFg);
|
|
1670
1711
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1671
1712
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
@@ -1675,7 +1716,7 @@ class TextRenderable extends Renderable {
|
|
|
1675
1716
|
fg: this._defaultFg,
|
|
1676
1717
|
bg: this._defaultBg,
|
|
1677
1718
|
attributes: this._defaultAttributes
|
|
1678
|
-
});
|
|
1719
|
+
}, this);
|
|
1679
1720
|
this.updateTextBuffer(styledText);
|
|
1680
1721
|
this._text.mount(this);
|
|
1681
1722
|
this.updateTextInfo();
|
|
@@ -1697,6 +1738,9 @@ class TextRenderable extends Renderable {
|
|
|
1697
1738
|
get chunks() {
|
|
1698
1739
|
return this._text.chunks;
|
|
1699
1740
|
}
|
|
1741
|
+
get textNode() {
|
|
1742
|
+
return this.rootTextNode;
|
|
1743
|
+
}
|
|
1700
1744
|
set content(value) {
|
|
1701
1745
|
const styledText = typeof value === "string" ? stringToStyledText(value) : value;
|
|
1702
1746
|
if (this._text !== styledText) {
|
|
@@ -1711,9 +1755,11 @@ class TextRenderable extends Renderable {
|
|
|
1711
1755
|
}
|
|
1712
1756
|
set fg(value) {
|
|
1713
1757
|
const newColor = parseColor(value ?? this._defaultOptions.fg);
|
|
1758
|
+
this.rootTextNode.fg = newColor;
|
|
1714
1759
|
if (this._defaultFg !== newColor) {
|
|
1715
1760
|
this._defaultFg = newColor;
|
|
1716
1761
|
this.textBuffer.setDefaultFg(this._defaultFg);
|
|
1762
|
+
this.rootTextNode.fg = newColor;
|
|
1717
1763
|
this.requestRender();
|
|
1718
1764
|
}
|
|
1719
1765
|
}
|
|
@@ -1748,9 +1794,11 @@ class TextRenderable extends Renderable {
|
|
|
1748
1794
|
}
|
|
1749
1795
|
set bg(value) {
|
|
1750
1796
|
const newColor = parseColor(value ?? this._defaultOptions.bg);
|
|
1797
|
+
this.rootTextNode.bg = newColor;
|
|
1751
1798
|
if (this._defaultBg !== newColor) {
|
|
1752
1799
|
this._defaultBg = newColor;
|
|
1753
1800
|
this.textBuffer.setDefaultBg(this._defaultBg);
|
|
1801
|
+
this.rootTextNode.bg = newColor;
|
|
1754
1802
|
this.requestRender();
|
|
1755
1803
|
}
|
|
1756
1804
|
}
|
|
@@ -1761,11 +1809,35 @@ class TextRenderable extends Renderable {
|
|
|
1761
1809
|
if (this._defaultAttributes !== value) {
|
|
1762
1810
|
this._defaultAttributes = value;
|
|
1763
1811
|
this.textBuffer.setDefaultAttributes(this._defaultAttributes);
|
|
1812
|
+
this.rootTextNode.attributes = value;
|
|
1813
|
+
this.requestRender();
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
get wrap() {
|
|
1817
|
+
return this._wrap;
|
|
1818
|
+
}
|
|
1819
|
+
set wrap(value) {
|
|
1820
|
+
if (this._wrap !== value) {
|
|
1821
|
+
this._wrap = value;
|
|
1822
|
+
this.textBuffer.setWrapWidth(this._wrap ? this.width : null);
|
|
1823
|
+
this.requestRender();
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
get wrapMode() {
|
|
1827
|
+
return this._wrapMode;
|
|
1828
|
+
}
|
|
1829
|
+
set wrapMode(value) {
|
|
1830
|
+
if (this._wrapMode !== value) {
|
|
1831
|
+
this._wrapMode = value;
|
|
1832
|
+
this.textBuffer.setWrapMode(this._wrapMode);
|
|
1764
1833
|
this.requestRender();
|
|
1765
1834
|
}
|
|
1766
1835
|
}
|
|
1767
1836
|
onResize(width, height) {
|
|
1768
|
-
if (this.
|
|
1837
|
+
if (this._wrap) {
|
|
1838
|
+
this.textBuffer.setWrapWidth(width);
|
|
1839
|
+
this.updateTextInfo();
|
|
1840
|
+
} else if (this.lastLocalSelection) {
|
|
1769
1841
|
const changed = this.updateLocalSelection(this.lastLocalSelection);
|
|
1770
1842
|
if (changed) {
|
|
1771
1843
|
this.requestRender();
|
|
@@ -1783,37 +1855,28 @@ class TextRenderable extends Renderable {
|
|
|
1783
1855
|
const lineInfo = this.textBuffer.lineInfo;
|
|
1784
1856
|
this._lineInfo.lineStarts = lineInfo.lineStarts;
|
|
1785
1857
|
this._lineInfo.lineWidths = lineInfo.lineWidths;
|
|
1858
|
+
this._lineInfo.maxLineWidth = lineInfo.maxLineWidth;
|
|
1786
1859
|
if (this.lastLocalSelection) {
|
|
1787
1860
|
const changed = this.updateLocalSelection(this.lastLocalSelection);
|
|
1788
1861
|
if (changed) {
|
|
1789
1862
|
this.requestRender();
|
|
1790
1863
|
}
|
|
1791
1864
|
}
|
|
1792
|
-
this.
|
|
1865
|
+
this.yogaNode.markDirty();
|
|
1793
1866
|
this.requestRender();
|
|
1794
1867
|
}
|
|
1795
1868
|
setupMeasureFunc() {
|
|
1796
1869
|
const measureFunc = (width, widthMode, height, heightMode) => {
|
|
1797
|
-
const maxLineWidth =
|
|
1798
|
-
const numLines = this._lineInfo.lineStarts.length
|
|
1870
|
+
const maxLineWidth = this._lineInfo.maxLineWidth;
|
|
1871
|
+
const numLines = this._lineInfo.lineStarts.length;
|
|
1799
1872
|
let measuredWidth = maxLineWidth;
|
|
1800
1873
|
let measuredHeight = numLines;
|
|
1801
|
-
if (widthMode === MeasureMode.Exactly) {
|
|
1802
|
-
measuredWidth = width;
|
|
1803
|
-
} else if (widthMode === MeasureMode.AtMost) {
|
|
1804
|
-
measuredWidth = Math.min(maxLineWidth, width);
|
|
1805
|
-
}
|
|
1806
|
-
if (heightMode === MeasureMode.Exactly) {
|
|
1807
|
-
measuredHeight = height;
|
|
1808
|
-
} else if (heightMode === MeasureMode.AtMost) {
|
|
1809
|
-
measuredHeight = Math.min(numLines, height);
|
|
1810
|
-
}
|
|
1811
1874
|
return {
|
|
1812
1875
|
width: Math.max(1, measuredWidth),
|
|
1813
1876
|
height: Math.max(1, measuredHeight)
|
|
1814
1877
|
};
|
|
1815
1878
|
};
|
|
1816
|
-
this.
|
|
1879
|
+
this.yogaNode.setMeasureFunc(measureFunc);
|
|
1817
1880
|
}
|
|
1818
1881
|
insertChunk(chunk, index) {
|
|
1819
1882
|
this.textBuffer.insertChunkGroup(index ?? this.textBuffer.chunkGroupCount, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
|
|
@@ -1860,6 +1923,9 @@ class TextRenderable extends Renderable {
|
|
|
1860
1923
|
this.rootTextNode.insertBefore(obj, anchor);
|
|
1861
1924
|
return this.rootTextNode.children.indexOf(obj);
|
|
1862
1925
|
}
|
|
1926
|
+
getTextChildren() {
|
|
1927
|
+
return this.rootTextNode.getChildren();
|
|
1928
|
+
}
|
|
1863
1929
|
clear() {
|
|
1864
1930
|
this.rootTextNode.clear();
|
|
1865
1931
|
const emptyStyledText = stringToStyledText("");
|
|
@@ -1894,9 +1960,9 @@ class TextRenderable extends Renderable {
|
|
|
1894
1960
|
getSelection() {
|
|
1895
1961
|
return this.textBuffer.getSelection();
|
|
1896
1962
|
}
|
|
1897
|
-
|
|
1963
|
+
onLifecyclePass = () => {
|
|
1898
1964
|
this.updateTextFromNodes();
|
|
1899
|
-
}
|
|
1965
|
+
};
|
|
1900
1966
|
render(buffer, deltaTime) {
|
|
1901
1967
|
if (!this.visible)
|
|
1902
1968
|
return;
|
|
@@ -2030,6 +2096,8 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
|
|
|
2030
2096
|
this.renderFontToBuffer();
|
|
2031
2097
|
}
|
|
2032
2098
|
renderFontToBuffer() {
|
|
2099
|
+
if (this.isDestroyed)
|
|
2100
|
+
return;
|
|
2033
2101
|
this.frameBuffer.clear(this._bg);
|
|
2034
2102
|
renderFontToFrameBuffer(this.frameBuffer, {
|
|
2035
2103
|
text: this._text,
|
|
@@ -2976,43 +3044,71 @@ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
|
2976
3044
|
|
|
2977
3045
|
class SliderRenderable extends Renderable {
|
|
2978
3046
|
orientation;
|
|
2979
|
-
|
|
2980
|
-
|
|
3047
|
+
_value;
|
|
3048
|
+
_min;
|
|
3049
|
+
_max;
|
|
3050
|
+
_viewPortSize;
|
|
2981
3051
|
_backgroundColor;
|
|
2982
3052
|
_foregroundColor;
|
|
2983
3053
|
_onChange;
|
|
2984
3054
|
constructor(ctx, options) {
|
|
2985
3055
|
super(ctx, options);
|
|
2986
3056
|
this.orientation = options.orientation;
|
|
2987
|
-
this.
|
|
2988
|
-
this.
|
|
3057
|
+
this._min = options.min ?? 0;
|
|
3058
|
+
this._max = options.max ?? 100;
|
|
3059
|
+
this._value = options.value ?? this._min;
|
|
3060
|
+
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
2989
3061
|
this._onChange = options.onChange;
|
|
2990
3062
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
2991
3063
|
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
2992
3064
|
this.setupMouseHandling();
|
|
2993
3065
|
}
|
|
2994
|
-
get
|
|
2995
|
-
return this.
|
|
3066
|
+
get value() {
|
|
3067
|
+
return this._value;
|
|
2996
3068
|
}
|
|
2997
|
-
set
|
|
2998
|
-
const clamped = Math.max(
|
|
2999
|
-
if (clamped !== this.
|
|
3000
|
-
this.
|
|
3069
|
+
set value(newValue) {
|
|
3070
|
+
const clamped = Math.max(this._min, Math.min(this._max, newValue));
|
|
3071
|
+
if (clamped !== this._value) {
|
|
3072
|
+
this._value = clamped;
|
|
3073
|
+
this._onChange?.(clamped);
|
|
3074
|
+
this.emit("change", { value: clamped });
|
|
3001
3075
|
this.requestRender();
|
|
3002
3076
|
}
|
|
3003
3077
|
}
|
|
3004
|
-
get
|
|
3005
|
-
return this.
|
|
3078
|
+
get min() {
|
|
3079
|
+
return this._min;
|
|
3006
3080
|
}
|
|
3007
|
-
set
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
this.
|
|
3011
|
-
|
|
3012
|
-
|
|
3081
|
+
set min(newMin) {
|
|
3082
|
+
if (newMin !== this._min) {
|
|
3083
|
+
this._min = newMin;
|
|
3084
|
+
if (this._value < newMin) {
|
|
3085
|
+
this.value = newMin;
|
|
3086
|
+
}
|
|
3087
|
+
this.requestRender();
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
get max() {
|
|
3091
|
+
return this._max;
|
|
3092
|
+
}
|
|
3093
|
+
set max(newMax) {
|
|
3094
|
+
if (newMax !== this._max) {
|
|
3095
|
+
this._max = newMax;
|
|
3096
|
+
if (this._value > newMax) {
|
|
3097
|
+
this.value = newMax;
|
|
3098
|
+
}
|
|
3099
|
+
this.requestRender();
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
set viewPortSize(size) {
|
|
3103
|
+
const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
|
|
3104
|
+
if (clampedSize !== this._viewPortSize) {
|
|
3105
|
+
this._viewPortSize = clampedSize;
|
|
3013
3106
|
this.requestRender();
|
|
3014
3107
|
}
|
|
3015
3108
|
}
|
|
3109
|
+
get viewPortSize() {
|
|
3110
|
+
return this._viewPortSize;
|
|
3111
|
+
}
|
|
3016
3112
|
get backgroundColor() {
|
|
3017
3113
|
return this._backgroundColor;
|
|
3018
3114
|
}
|
|
@@ -3027,69 +3123,183 @@ class SliderRenderable extends Renderable {
|
|
|
3027
3123
|
this._foregroundColor = parseColor(value);
|
|
3028
3124
|
this.requestRender();
|
|
3029
3125
|
}
|
|
3126
|
+
calculateDragOffsetVirtual(event) {
|
|
3127
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3128
|
+
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
3129
|
+
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
3130
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3131
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3132
|
+
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
3133
|
+
}
|
|
3030
3134
|
setupMouseHandling() {
|
|
3031
3135
|
let isDragging = false;
|
|
3032
|
-
let
|
|
3136
|
+
let dragOffsetVirtual = 0;
|
|
3033
3137
|
this.onMouseDown = (event) => {
|
|
3034
3138
|
event.stopPropagation();
|
|
3035
3139
|
event.preventDefault();
|
|
3036
|
-
|
|
3037
|
-
const
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3140
|
+
const thumb = this.getThumbRect();
|
|
3141
|
+
const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
|
|
3142
|
+
if (inThumb) {
|
|
3143
|
+
isDragging = true;
|
|
3144
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
3041
3145
|
} else {
|
|
3042
|
-
|
|
3146
|
+
this.updateValueFromMouseDirect(event);
|
|
3147
|
+
isDragging = true;
|
|
3148
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
3043
3149
|
}
|
|
3044
|
-
this.updatePositionFromMouse(event, relativeStartPos);
|
|
3045
3150
|
};
|
|
3046
3151
|
this.onMouseDrag = (event) => {
|
|
3047
3152
|
if (!isDragging)
|
|
3048
3153
|
return;
|
|
3049
3154
|
event.stopPropagation();
|
|
3050
|
-
this.
|
|
3155
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
3051
3156
|
};
|
|
3052
|
-
this.onMouseUp = () => {
|
|
3157
|
+
this.onMouseUp = (event) => {
|
|
3158
|
+
if (isDragging) {
|
|
3159
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
3160
|
+
}
|
|
3053
3161
|
isDragging = false;
|
|
3054
3162
|
};
|
|
3055
3163
|
}
|
|
3056
|
-
|
|
3164
|
+
updateValueFromMouseDirect(event) {
|
|
3057
3165
|
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3058
3166
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
3059
3167
|
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3060
|
-
const
|
|
3061
|
-
const
|
|
3062
|
-
const
|
|
3063
|
-
const
|
|
3064
|
-
this.
|
|
3065
|
-
|
|
3066
|
-
|
|
3168
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3169
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3170
|
+
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
3171
|
+
const range = this._max - this._min;
|
|
3172
|
+
const newValue = this._min + ratio * range;
|
|
3173
|
+
this.value = newValue;
|
|
3174
|
+
}
|
|
3175
|
+
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
3176
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3067
3177
|
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3178
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3179
|
+
const virtualTrackSize = trackSize * 2;
|
|
3180
|
+
const relativeMousePos = mousePos - trackStart;
|
|
3181
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3182
|
+
const virtualMousePos = clampedMousePos * 2;
|
|
3183
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3184
|
+
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
3185
|
+
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
3186
|
+
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
3187
|
+
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
3188
|
+
const range = this._max - this._min;
|
|
3189
|
+
const newValue = this._min + ratio * range;
|
|
3190
|
+
this.value = newValue;
|
|
3070
3191
|
}
|
|
3071
3192
|
getThumbRect() {
|
|
3072
|
-
const
|
|
3193
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3194
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3195
|
+
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
3196
|
+
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
3073
3197
|
if (this.orientation === "vertical") {
|
|
3074
3198
|
return {
|
|
3075
3199
|
x: this.x,
|
|
3076
|
-
y: this.y +
|
|
3200
|
+
y: this.y + realThumbStart,
|
|
3077
3201
|
width: this.width,
|
|
3078
|
-
height:
|
|
3202
|
+
height: Math.max(1, realThumbSize)
|
|
3079
3203
|
};
|
|
3080
3204
|
} else {
|
|
3081
3205
|
return {
|
|
3082
|
-
x: this.x +
|
|
3206
|
+
x: this.x + realThumbStart,
|
|
3083
3207
|
y: this.y,
|
|
3084
|
-
width:
|
|
3208
|
+
width: Math.max(1, realThumbSize),
|
|
3085
3209
|
height: this.height
|
|
3086
3210
|
};
|
|
3087
3211
|
}
|
|
3088
3212
|
}
|
|
3089
3213
|
renderSelf(buffer) {
|
|
3214
|
+
if (this.orientation === "horizontal") {
|
|
3215
|
+
this.renderHorizontal(buffer);
|
|
3216
|
+
} else {
|
|
3217
|
+
this.renderVertical(buffer);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
renderHorizontal(buffer) {
|
|
3221
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3222
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3223
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
3224
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
3225
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3226
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
3227
|
+
const startX = Math.max(0, realStartCell);
|
|
3228
|
+
const endX = Math.min(this.width - 1, realEndCell);
|
|
3229
|
+
for (let realX = startX;realX <= endX; realX++) {
|
|
3230
|
+
const virtualCellStart = realX * 2;
|
|
3231
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3232
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3233
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3234
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3235
|
+
let char = " ";
|
|
3236
|
+
if (coverage >= 2) {
|
|
3237
|
+
char = "\u2588";
|
|
3238
|
+
} else {
|
|
3239
|
+
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
3240
|
+
if (isLeftHalf) {
|
|
3241
|
+
char = "\u258C";
|
|
3242
|
+
} else {
|
|
3243
|
+
char = "\u2590";
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
for (let y = 0;y < this.height; y++) {
|
|
3247
|
+
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
renderVertical(buffer) {
|
|
3252
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3253
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3254
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
3090
3255
|
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3256
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3257
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
3258
|
+
const startY = Math.max(0, realStartCell);
|
|
3259
|
+
const endY = Math.min(this.height - 1, realEndCell);
|
|
3260
|
+
for (let realY = startY;realY <= endY; realY++) {
|
|
3261
|
+
const virtualCellStart = realY * 2;
|
|
3262
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
3263
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3264
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3265
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3266
|
+
let char = " ";
|
|
3267
|
+
if (coverage >= 2) {
|
|
3268
|
+
char = "\u2588";
|
|
3269
|
+
} else if (coverage > 0) {
|
|
3270
|
+
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
3271
|
+
if (virtualPositionInCell === 0) {
|
|
3272
|
+
char = "\u2580";
|
|
3273
|
+
} else {
|
|
3274
|
+
char = "\u2584";
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
for (let x = 0;x < this.width; x++) {
|
|
3278
|
+
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
getVirtualThumbSize() {
|
|
3283
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3284
|
+
const range = this._max - this._min;
|
|
3285
|
+
if (range === 0)
|
|
3286
|
+
return virtualTrackSize;
|
|
3287
|
+
const viewportSize = Math.max(1, this._viewPortSize);
|
|
3288
|
+
const contentSize = range + viewportSize;
|
|
3289
|
+
if (contentSize <= viewportSize)
|
|
3290
|
+
return virtualTrackSize;
|
|
3291
|
+
const thumbRatio = viewportSize / contentSize;
|
|
3292
|
+
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
3293
|
+
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
3294
|
+
}
|
|
3295
|
+
getVirtualThumbStart() {
|
|
3296
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3297
|
+
const range = this._max - this._min;
|
|
3298
|
+
if (range === 0)
|
|
3299
|
+
return 0;
|
|
3300
|
+
const valueRatio = (this._value - this._min) / range;
|
|
3301
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3302
|
+
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
3093
3303
|
}
|
|
3094
3304
|
}
|
|
3095
3305
|
|
|
@@ -3132,6 +3342,7 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3132
3342
|
return;
|
|
3133
3343
|
this._scrollSize = value;
|
|
3134
3344
|
this.recalculateVisibility();
|
|
3345
|
+
this.updateSliderFromScrollState();
|
|
3135
3346
|
this.scrollPosition = this.scrollPosition;
|
|
3136
3347
|
}
|
|
3137
3348
|
set scrollPosition(value) {
|
|
@@ -3139,15 +3350,15 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3139
3350
|
if (newPosition !== this._scrollPosition) {
|
|
3140
3351
|
this._scrollPosition = newPosition;
|
|
3141
3352
|
this.updateSliderFromScrollState();
|
|
3142
|
-
this._onChange?.(newPosition);
|
|
3143
|
-
this.emit("change", { position: newPosition });
|
|
3144
3353
|
}
|
|
3145
3354
|
}
|
|
3146
3355
|
set viewportSize(value) {
|
|
3147
3356
|
if (value === this.viewportSize)
|
|
3148
3357
|
return;
|
|
3149
3358
|
this._viewportSize = value;
|
|
3359
|
+
this.slider.viewPortSize = Math.max(1, this._viewportSize);
|
|
3150
3360
|
this.recalculateVisibility();
|
|
3361
|
+
this.updateSliderFromScrollState();
|
|
3151
3362
|
this.scrollPosition = this.scrollPosition;
|
|
3152
3363
|
}
|
|
3153
3364
|
get showArrows() {
|
|
@@ -3170,16 +3381,22 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3170
3381
|
this._onChange = options.onChange;
|
|
3171
3382
|
this.orientation = orientation;
|
|
3172
3383
|
this._showArrows = showArrows;
|
|
3384
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
3385
|
+
const defaultStepSize = Math.max(1, this._viewportSize);
|
|
3386
|
+
const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
|
|
3173
3387
|
this.slider = new SliderRenderable(ctx, {
|
|
3174
3388
|
orientation,
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3389
|
+
min: 0,
|
|
3390
|
+
max: scrollRange,
|
|
3391
|
+
value: this._scrollPosition,
|
|
3392
|
+
viewPortSize: stepSize,
|
|
3393
|
+
onChange: (value) => {
|
|
3394
|
+
this._scrollPosition = Math.round(value);
|
|
3178
3395
|
this._onChange?.(this._scrollPosition);
|
|
3179
3396
|
this.emit("change", { position: this._scrollPosition });
|
|
3180
3397
|
},
|
|
3181
3398
|
...orientation === "vertical" ? {
|
|
3182
|
-
width: 2,
|
|
3399
|
+
width: Math.max(1, Math.min(2, this.width)),
|
|
3183
3400
|
height: "100%",
|
|
3184
3401
|
marginLeft: "auto"
|
|
3185
3402
|
} : {
|
|
@@ -3258,17 +3475,10 @@ class ScrollBarRenderable extends Renderable {
|
|
|
3258
3475
|
this.requestRender();
|
|
3259
3476
|
}
|
|
3260
3477
|
updateSliderFromScrollState() {
|
|
3261
|
-
const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
|
|
3262
3478
|
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
} else {
|
|
3267
|
-
const sizeRatio = this._viewportSize / this._scrollSize;
|
|
3268
|
-
this.slider.thumbSize = Math.max(1, Math.round(sizeRatio * trackSize));
|
|
3269
|
-
const positionRatio = this._scrollPosition / scrollRange;
|
|
3270
|
-
this.slider.thumbPosition = Math.max(0, Math.min(1, positionRatio));
|
|
3271
|
-
}
|
|
3479
|
+
this.slider.min = 0;
|
|
3480
|
+
this.slider.max = scrollRange;
|
|
3481
|
+
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
3272
3482
|
}
|
|
3273
3483
|
scrollBy(delta, unit = "absolute") {
|
|
3274
3484
|
const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
|
|
@@ -3338,10 +3548,10 @@ class ArrowRenderable extends Renderable {
|
|
|
3338
3548
|
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
|
|
3339
3549
|
this._attributes = options.attributes ?? 0;
|
|
3340
3550
|
this._arrowChars = {
|
|
3341
|
-
up: "\
|
|
3342
|
-
down: "\
|
|
3343
|
-
left: "
|
|
3344
|
-
right: "
|
|
3551
|
+
up: "\u25B2",
|
|
3552
|
+
down: "\u25BC",
|
|
3553
|
+
left: "\u25C0",
|
|
3554
|
+
right: "\u25B6",
|
|
3345
3555
|
...options.arrowChars
|
|
3346
3556
|
};
|
|
3347
3557
|
if (!options.width) {
|
|
@@ -3444,17 +3654,42 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3444
3654
|
cachedAutoScrollSpeed = 3;
|
|
3445
3655
|
autoScrollAccumulatorX = 0;
|
|
3446
3656
|
autoScrollAccumulatorY = 0;
|
|
3657
|
+
_stickyScroll;
|
|
3658
|
+
_stickyScrollTop = false;
|
|
3659
|
+
_stickyScrollBottom = false;
|
|
3660
|
+
_stickyScrollLeft = false;
|
|
3661
|
+
_stickyScrollRight = false;
|
|
3662
|
+
_stickyStart;
|
|
3663
|
+
_hasManualScroll = false;
|
|
3664
|
+
get stickyScroll() {
|
|
3665
|
+
return this._stickyScroll;
|
|
3666
|
+
}
|
|
3667
|
+
set stickyScroll(value) {
|
|
3668
|
+
this._stickyScroll = value;
|
|
3669
|
+
this.updateStickyState();
|
|
3670
|
+
}
|
|
3671
|
+
get stickyStart() {
|
|
3672
|
+
return this._stickyStart;
|
|
3673
|
+
}
|
|
3674
|
+
set stickyStart(value) {
|
|
3675
|
+
this._stickyStart = value;
|
|
3676
|
+
this.updateStickyState();
|
|
3677
|
+
}
|
|
3447
3678
|
get scrollTop() {
|
|
3448
3679
|
return this.verticalScrollBar.scrollPosition;
|
|
3449
3680
|
}
|
|
3450
3681
|
set scrollTop(value) {
|
|
3451
3682
|
this.verticalScrollBar.scrollPosition = value;
|
|
3683
|
+
this._hasManualScroll = true;
|
|
3684
|
+
this.updateStickyState();
|
|
3452
3685
|
}
|
|
3453
3686
|
get scrollLeft() {
|
|
3454
3687
|
return this.horizontalScrollBar.scrollPosition;
|
|
3455
3688
|
}
|
|
3456
3689
|
set scrollLeft(value) {
|
|
3457
3690
|
this.horizontalScrollBar.scrollPosition = value;
|
|
3691
|
+
this._hasManualScroll = true;
|
|
3692
|
+
this.updateStickyState();
|
|
3458
3693
|
}
|
|
3459
3694
|
get scrollWidth() {
|
|
3460
3695
|
return this.horizontalScrollBar.scrollSize;
|
|
@@ -3462,6 +3697,56 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3462
3697
|
get scrollHeight() {
|
|
3463
3698
|
return this.verticalScrollBar.scrollSize;
|
|
3464
3699
|
}
|
|
3700
|
+
updateStickyState() {
|
|
3701
|
+
if (!this._stickyScroll)
|
|
3702
|
+
return;
|
|
3703
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3704
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3705
|
+
if (this.scrollTop <= 0) {
|
|
3706
|
+
this._stickyScrollTop = true;
|
|
3707
|
+
this._stickyScrollBottom = false;
|
|
3708
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
3709
|
+
this._stickyScrollTop = false;
|
|
3710
|
+
this._stickyScrollBottom = true;
|
|
3711
|
+
} else {
|
|
3712
|
+
this._stickyScrollTop = false;
|
|
3713
|
+
this._stickyScrollBottom = false;
|
|
3714
|
+
}
|
|
3715
|
+
if (this.scrollLeft <= 0) {
|
|
3716
|
+
this._stickyScrollLeft = true;
|
|
3717
|
+
this._stickyScrollRight = false;
|
|
3718
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
3719
|
+
this._stickyScrollLeft = false;
|
|
3720
|
+
this._stickyScrollRight = true;
|
|
3721
|
+
} else {
|
|
3722
|
+
this._stickyScrollLeft = false;
|
|
3723
|
+
this._stickyScrollRight = false;
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
applyStickyStart(stickyStart) {
|
|
3727
|
+
switch (stickyStart) {
|
|
3728
|
+
case "top":
|
|
3729
|
+
this._stickyScrollTop = true;
|
|
3730
|
+
this._stickyScrollBottom = false;
|
|
3731
|
+
this.verticalScrollBar.scrollPosition = 0;
|
|
3732
|
+
break;
|
|
3733
|
+
case "bottom":
|
|
3734
|
+
this._stickyScrollTop = false;
|
|
3735
|
+
this._stickyScrollBottom = true;
|
|
3736
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3737
|
+
break;
|
|
3738
|
+
case "left":
|
|
3739
|
+
this._stickyScrollLeft = true;
|
|
3740
|
+
this._stickyScrollRight = false;
|
|
3741
|
+
this.horizontalScrollBar.scrollPosition = 0;
|
|
3742
|
+
break;
|
|
3743
|
+
case "right":
|
|
3744
|
+
this._stickyScrollLeft = false;
|
|
3745
|
+
this._stickyScrollRight = true;
|
|
3746
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3747
|
+
break;
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3465
3750
|
constructor(ctx, {
|
|
3466
3751
|
wrapperOptions,
|
|
3467
3752
|
viewportOptions,
|
|
@@ -3470,25 +3755,26 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3470
3755
|
scrollbarOptions,
|
|
3471
3756
|
verticalScrollbarOptions,
|
|
3472
3757
|
horizontalScrollbarOptions,
|
|
3758
|
+
stickyScroll = false,
|
|
3759
|
+
stickyStart,
|
|
3760
|
+
scrollX = false,
|
|
3761
|
+
scrollY = true,
|
|
3473
3762
|
...options
|
|
3474
3763
|
}) {
|
|
3475
3764
|
super(ctx, {
|
|
3476
3765
|
flexShrink: 1,
|
|
3477
|
-
flexGrow: 1,
|
|
3478
3766
|
flexDirection: "row",
|
|
3479
|
-
flexWrap: "wrap",
|
|
3480
3767
|
alignItems: "stretch",
|
|
3481
3768
|
...options,
|
|
3482
3769
|
...rootOptions
|
|
3483
3770
|
});
|
|
3484
3771
|
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
3772
|
+
this._stickyScroll = stickyScroll;
|
|
3773
|
+
this._stickyStart = stickyStart;
|
|
3485
3774
|
this.wrapper = new BoxRenderable(ctx, {
|
|
3486
3775
|
flexDirection: "column",
|
|
3487
3776
|
flexGrow: 1,
|
|
3488
3777
|
flexShrink: 1,
|
|
3489
|
-
flexBasis: "auto",
|
|
3490
|
-
maxHeight: "100%",
|
|
3491
|
-
maxWidth: "100%",
|
|
3492
3778
|
...wrapperOptions,
|
|
3493
3779
|
id: `scroll-box-wrapper-${this.internalId}`
|
|
3494
3780
|
});
|
|
@@ -3497,10 +3783,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3497
3783
|
flexDirection: "column",
|
|
3498
3784
|
flexGrow: 1,
|
|
3499
3785
|
flexShrink: 1,
|
|
3500
|
-
|
|
3501
|
-
maxHeight: "100%",
|
|
3502
|
-
maxWidth: "100%",
|
|
3503
|
-
overflow: "scroll",
|
|
3786
|
+
overflow: "hidden",
|
|
3504
3787
|
onSizeChange: () => {
|
|
3505
3788
|
this.recalculateBarProps();
|
|
3506
3789
|
},
|
|
@@ -3510,8 +3793,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3510
3793
|
this.wrapper.add(this.viewport);
|
|
3511
3794
|
this.content = new ContentRenderable(ctx, this.viewport, {
|
|
3512
3795
|
alignSelf: "flex-start",
|
|
3513
|
-
minWidth: "100%",
|
|
3514
|
-
minHeight: "100%",
|
|
3796
|
+
...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
|
|
3797
|
+
...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
|
|
3515
3798
|
onSizeChange: () => {
|
|
3516
3799
|
this.recalculateBarProps();
|
|
3517
3800
|
},
|
|
@@ -3530,6 +3813,8 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3530
3813
|
orientation: "vertical",
|
|
3531
3814
|
onChange: (position) => {
|
|
3532
3815
|
this.content.translateY = -position;
|
|
3816
|
+
this._hasManualScroll = true;
|
|
3817
|
+
this.updateStickyState();
|
|
3533
3818
|
}
|
|
3534
3819
|
});
|
|
3535
3820
|
super.add(this.verticalScrollBar);
|
|
@@ -3544,10 +3829,15 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3544
3829
|
orientation: "horizontal",
|
|
3545
3830
|
onChange: (position) => {
|
|
3546
3831
|
this.content.translateX = -position;
|
|
3832
|
+
this._hasManualScroll = true;
|
|
3833
|
+
this.updateStickyState();
|
|
3547
3834
|
}
|
|
3548
3835
|
});
|
|
3549
3836
|
this.wrapper.add(this.horizontalScrollBar);
|
|
3550
3837
|
this.recalculateBarProps();
|
|
3838
|
+
if (stickyStart && stickyScroll) {
|
|
3839
|
+
this.applyStickyStart(stickyStart);
|
|
3840
|
+
}
|
|
3551
3841
|
this.selectionListener = () => {
|
|
3552
3842
|
const selection = this._ctx.getSelection();
|
|
3553
3843
|
if (!selection || !selection.isSelecting) {
|
|
@@ -3566,6 +3856,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3566
3856
|
this.verticalScrollBar.scrollBy(delta.y, unit);
|
|
3567
3857
|
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
3568
3858
|
}
|
|
3859
|
+
this._hasManualScroll = true;
|
|
3569
3860
|
}
|
|
3570
3861
|
scrollTo(position) {
|
|
3571
3862
|
if (typeof position === "number") {
|
|
@@ -3597,6 +3888,7 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3597
3888
|
this.scrollLeft -= event.scroll?.delta ?? 0;
|
|
3598
3889
|
else if (dir === "right")
|
|
3599
3890
|
this.scrollLeft += event.scroll?.delta ?? 0;
|
|
3891
|
+
this._hasManualScroll = true;
|
|
3600
3892
|
}
|
|
3601
3893
|
if (event.type === "drag" && event.isSelecting) {
|
|
3602
3894
|
this.updateAutoScroll(event.x, event.y);
|
|
@@ -3605,10 +3897,14 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3605
3897
|
}
|
|
3606
3898
|
}
|
|
3607
3899
|
handleKeyPress(key) {
|
|
3608
|
-
if (this.verticalScrollBar.handleKeyPress(key))
|
|
3900
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
3901
|
+
this._hasManualScroll = true;
|
|
3609
3902
|
return true;
|
|
3610
|
-
|
|
3903
|
+
}
|
|
3904
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
3905
|
+
this._hasManualScroll = true;
|
|
3611
3906
|
return true;
|
|
3907
|
+
}
|
|
3612
3908
|
return false;
|
|
3613
3909
|
}
|
|
3614
3910
|
startAutoScroll(mouseX, mouseY) {
|
|
@@ -3722,6 +4018,27 @@ class ScrollBoxRenderable extends BoxRenderable {
|
|
|
3722
4018
|
this.verticalScrollBar.viewportSize = this.viewport.height;
|
|
3723
4019
|
this.horizontalScrollBar.scrollSize = this.content.width;
|
|
3724
4020
|
this.horizontalScrollBar.viewportSize = this.viewport.width;
|
|
4021
|
+
if (this._stickyScroll) {
|
|
4022
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
4023
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
4024
|
+
if (this._stickyStart && !this._hasManualScroll) {
|
|
4025
|
+
this.applyStickyStart(this._stickyStart);
|
|
4026
|
+
} else {
|
|
4027
|
+
if (this._stickyScrollTop) {
|
|
4028
|
+
this.scrollTop = 0;
|
|
4029
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
4030
|
+
this.scrollTop = newMaxScrollTop;
|
|
4031
|
+
}
|
|
4032
|
+
if (this._stickyScrollLeft) {
|
|
4033
|
+
this.scrollLeft = 0;
|
|
4034
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
4035
|
+
this.scrollLeft = newMaxScrollLeft;
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
process.nextTick(() => {
|
|
4040
|
+
this.requestRender();
|
|
4041
|
+
});
|
|
3725
4042
|
}
|
|
3726
4043
|
set rootOptions(options) {
|
|
3727
4044
|
Object.assign(this, options);
|
|
@@ -3834,6 +4151,7 @@ export {
|
|
|
3834
4151
|
wrapWithDelegates,
|
|
3835
4152
|
white,
|
|
3836
4153
|
vstyles,
|
|
4154
|
+
visualizeRenderableTree,
|
|
3837
4155
|
underline,
|
|
3838
4156
|
t,
|
|
3839
4157
|
stringToStyledText,
|
|
@@ -3866,19 +4184,10 @@ export {
|
|
|
3866
4184
|
maybeMakeRenderable,
|
|
3867
4185
|
magenta,
|
|
3868
4186
|
italic,
|
|
3869
|
-
isValidPercentage,
|
|
3870
4187
|
isVNode,
|
|
3871
4188
|
isTextNodeRenderable,
|
|
3872
4189
|
isStyledText,
|
|
3873
|
-
isSizeType,
|
|
3874
4190
|
isRenderable,
|
|
3875
|
-
isPositionTypeType,
|
|
3876
|
-
isPositionType,
|
|
3877
|
-
isPaddingType,
|
|
3878
|
-
isOverflowType,
|
|
3879
|
-
isMarginType,
|
|
3880
|
-
isFlexBasisType,
|
|
3881
|
-
isDimensionType,
|
|
3882
4191
|
instantiate,
|
|
3883
4192
|
hsvToRgb,
|
|
3884
4193
|
hexToRgb,
|
|
@@ -3895,7 +4204,6 @@ export {
|
|
|
3895
4204
|
dim,
|
|
3896
4205
|
delegate,
|
|
3897
4206
|
cyan,
|
|
3898
|
-
createTrackedNode,
|
|
3899
4207
|
createTimeline,
|
|
3900
4208
|
createTextAttributes,
|
|
3901
4209
|
createCliRenderer,
|
|
@@ -3931,9 +4239,9 @@ export {
|
|
|
3931
4239
|
applyGrayscale,
|
|
3932
4240
|
applyChromaticAberration,
|
|
3933
4241
|
applyAsciiArt,
|
|
4242
|
+
exports_src as Yoga,
|
|
3934
4243
|
VignetteEffect,
|
|
3935
4244
|
VRenderable,
|
|
3936
|
-
TrackedNode,
|
|
3937
4245
|
Timeline,
|
|
3938
4246
|
TextRenderable,
|
|
3939
4247
|
TextNodeRenderable,
|
|
@@ -3989,5 +4297,5 @@ export {
|
|
|
3989
4297
|
ASCIIFont
|
|
3990
4298
|
};
|
|
3991
4299
|
|
|
3992
|
-
//# debugId=
|
|
4300
|
+
//# debugId=5FB73C20BB0792E864756E2164756E21
|
|
3993
4301
|
//# sourceMappingURL=index.js.map
|