@opentui/core 0.1.26 → 0.1.28
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 +140 -140
- package/3d.js.map +2 -2
- package/{index-pxa2sv92.js → index-phjxdb6g.js} +97 -43
- package/{index-pxa2sv92.js.map → index-phjxdb6g.js.map} +6 -6
- package/index.js +2060 -2052
- package/index.js.map +12 -12
- package/lib/KeyHandler.d.ts +3 -0
- package/package.json +8 -7
- package/renderables/ScrollBox.d.ts +2 -0
- package/renderables/TextNode.d.ts +2 -1
- package/renderables/index.d.ts +10 -9
- package/renderer.d.ts +14 -1
- package/testing.js +1 -1
package/index.js
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
RGBA,
|
|
27
27
|
Renderable,
|
|
28
28
|
RenderableEvents,
|
|
29
|
+
RendererControlState,
|
|
29
30
|
RootRenderable,
|
|
30
31
|
Selection,
|
|
31
32
|
StyledText,
|
|
@@ -132,7 +133,7 @@ import {
|
|
|
132
133
|
white,
|
|
133
134
|
wrapWithDelegates,
|
|
134
135
|
yellow
|
|
135
|
-
} from "./index-
|
|
136
|
+
} from "./index-phjxdb6g.js";
|
|
136
137
|
// src/post/filters.ts
|
|
137
138
|
function applyScanlines(buffer, strength = 0.8, step = 2) {
|
|
138
139
|
const width = buffer.width;
|
|
@@ -1222,6 +1223,187 @@ function createTimeline(options = {}) {
|
|
|
1222
1223
|
engine.register(timeline);
|
|
1223
1224
|
return timeline;
|
|
1224
1225
|
}
|
|
1226
|
+
// src/renderables/FrameBuffer.ts
|
|
1227
|
+
class FrameBufferRenderable extends Renderable {
|
|
1228
|
+
frameBuffer;
|
|
1229
|
+
respectAlpha;
|
|
1230
|
+
constructor(ctx, options) {
|
|
1231
|
+
super(ctx, options);
|
|
1232
|
+
this.respectAlpha = options.respectAlpha || false;
|
|
1233
|
+
this.frameBuffer = OptimizedBuffer.create(options.width, options.height, this._ctx.widthMethod, {
|
|
1234
|
+
respectAlpha: this.respectAlpha,
|
|
1235
|
+
id: options.id || `framebufferrenderable-${this.id}`
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
onResize(width, height) {
|
|
1239
|
+
if (width <= 0 || height <= 0) {
|
|
1240
|
+
throw new Error(`Invalid resize dimensions for FrameBufferRenderable ${this.id}: ${width}x${height}`);
|
|
1241
|
+
}
|
|
1242
|
+
this.frameBuffer.resize(width, height);
|
|
1243
|
+
super.onResize(width, height);
|
|
1244
|
+
this.requestRender();
|
|
1245
|
+
}
|
|
1246
|
+
renderSelf(buffer) {
|
|
1247
|
+
if (!this.visible || this.isDestroyed)
|
|
1248
|
+
return;
|
|
1249
|
+
buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
|
|
1250
|
+
}
|
|
1251
|
+
destroySelf() {
|
|
1252
|
+
this.frameBuffer?.destroy();
|
|
1253
|
+
super.destroySelf();
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// src/renderables/ASCIIFont.ts
|
|
1258
|
+
class ASCIIFontRenderable extends FrameBufferRenderable {
|
|
1259
|
+
selectable = true;
|
|
1260
|
+
_text;
|
|
1261
|
+
_font;
|
|
1262
|
+
_fg;
|
|
1263
|
+
_bg;
|
|
1264
|
+
_selectionBg;
|
|
1265
|
+
_selectionFg;
|
|
1266
|
+
lastLocalSelection = null;
|
|
1267
|
+
selectionHelper;
|
|
1268
|
+
constructor(ctx, options) {
|
|
1269
|
+
const font = options.font || "tiny";
|
|
1270
|
+
const text = options.text || "";
|
|
1271
|
+
const measurements = measureText({ text, font });
|
|
1272
|
+
super(ctx, {
|
|
1273
|
+
flexShrink: 0,
|
|
1274
|
+
...options,
|
|
1275
|
+
width: measurements.width || 1,
|
|
1276
|
+
height: measurements.height || 1,
|
|
1277
|
+
respectAlpha: true
|
|
1278
|
+
});
|
|
1279
|
+
this._text = text;
|
|
1280
|
+
this._font = font;
|
|
1281
|
+
this._fg = Array.isArray(options.fg) ? options.fg : [options.fg || RGBA.fromInts(255, 255, 255, 255)];
|
|
1282
|
+
this._bg = options.bg || RGBA.fromValues(0, 0, 0, 0);
|
|
1283
|
+
this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined;
|
|
1284
|
+
this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined;
|
|
1285
|
+
this.selectable = options.selectable ?? true;
|
|
1286
|
+
this.selectionHelper = new ASCIIFontSelectionHelper(() => this._text, () => this._font);
|
|
1287
|
+
this.renderFontToBuffer();
|
|
1288
|
+
}
|
|
1289
|
+
get text() {
|
|
1290
|
+
return this._text;
|
|
1291
|
+
}
|
|
1292
|
+
set text(value) {
|
|
1293
|
+
this._text = value;
|
|
1294
|
+
this.updateDimensions();
|
|
1295
|
+
if (this.lastLocalSelection) {
|
|
1296
|
+
this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
|
|
1297
|
+
}
|
|
1298
|
+
this.renderFontToBuffer();
|
|
1299
|
+
this.requestRender();
|
|
1300
|
+
}
|
|
1301
|
+
get font() {
|
|
1302
|
+
return this._font;
|
|
1303
|
+
}
|
|
1304
|
+
set font(value) {
|
|
1305
|
+
this._font = value;
|
|
1306
|
+
this.updateDimensions();
|
|
1307
|
+
if (this.lastLocalSelection) {
|
|
1308
|
+
this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
|
|
1309
|
+
}
|
|
1310
|
+
this.renderFontToBuffer();
|
|
1311
|
+
this.requestRender();
|
|
1312
|
+
}
|
|
1313
|
+
get fg() {
|
|
1314
|
+
return this._fg;
|
|
1315
|
+
}
|
|
1316
|
+
set fg(value) {
|
|
1317
|
+
if (Array.isArray(value)) {
|
|
1318
|
+
this._fg = value.map((color) => typeof color === "string" ? parseColor(color) : color);
|
|
1319
|
+
} else {
|
|
1320
|
+
this._fg = [typeof value === "string" ? parseColor(value) : value];
|
|
1321
|
+
}
|
|
1322
|
+
this.renderFontToBuffer();
|
|
1323
|
+
this.requestRender();
|
|
1324
|
+
}
|
|
1325
|
+
get bg() {
|
|
1326
|
+
return this._bg;
|
|
1327
|
+
}
|
|
1328
|
+
set bg(value) {
|
|
1329
|
+
this._bg = typeof value === "string" ? parseColor(value) : value;
|
|
1330
|
+
this.renderFontToBuffer();
|
|
1331
|
+
this.requestRender();
|
|
1332
|
+
}
|
|
1333
|
+
updateDimensions() {
|
|
1334
|
+
const measurements = measureText({ text: this._text, font: this._font });
|
|
1335
|
+
this.width = measurements.width;
|
|
1336
|
+
this.height = measurements.height;
|
|
1337
|
+
}
|
|
1338
|
+
shouldStartSelection(x, y) {
|
|
1339
|
+
const localX = x - this.x;
|
|
1340
|
+
const localY = y - this.y;
|
|
1341
|
+
return this.selectionHelper.shouldStartSelection(localX, localY, this.width, this.height);
|
|
1342
|
+
}
|
|
1343
|
+
onSelectionChanged(selection) {
|
|
1344
|
+
const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y);
|
|
1345
|
+
this.lastLocalSelection = localSelection;
|
|
1346
|
+
const changed = this.selectionHelper.onLocalSelectionChanged(localSelection, this.width, this.height);
|
|
1347
|
+
if (changed) {
|
|
1348
|
+
this.renderFontToBuffer();
|
|
1349
|
+
this.requestRender();
|
|
1350
|
+
}
|
|
1351
|
+
return changed;
|
|
1352
|
+
}
|
|
1353
|
+
getSelectedText() {
|
|
1354
|
+
const selection = this.selectionHelper.getSelection();
|
|
1355
|
+
if (!selection)
|
|
1356
|
+
return "";
|
|
1357
|
+
return this._text.slice(selection.start, selection.end);
|
|
1358
|
+
}
|
|
1359
|
+
hasSelection() {
|
|
1360
|
+
return this.selectionHelper.hasSelection();
|
|
1361
|
+
}
|
|
1362
|
+
onResize(width, height) {
|
|
1363
|
+
super.onResize(width, height);
|
|
1364
|
+
this.renderFontToBuffer();
|
|
1365
|
+
}
|
|
1366
|
+
renderFontToBuffer() {
|
|
1367
|
+
if (this.isDestroyed)
|
|
1368
|
+
return;
|
|
1369
|
+
this.frameBuffer.clear(this._bg);
|
|
1370
|
+
renderFontToFrameBuffer(this.frameBuffer, {
|
|
1371
|
+
text: this._text,
|
|
1372
|
+
x: 0,
|
|
1373
|
+
y: 0,
|
|
1374
|
+
fg: this._fg,
|
|
1375
|
+
bg: this._bg,
|
|
1376
|
+
font: this._font
|
|
1377
|
+
});
|
|
1378
|
+
const selection = this.selectionHelper.getSelection();
|
|
1379
|
+
if (selection && (this._selectionBg || this._selectionFg)) {
|
|
1380
|
+
this.renderSelectionHighlight(selection);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
renderSelectionHighlight(selection) {
|
|
1384
|
+
if (!this._selectionBg && !this._selectionFg)
|
|
1385
|
+
return;
|
|
1386
|
+
const selectedText = this._text.slice(selection.start, selection.end);
|
|
1387
|
+
if (!selectedText)
|
|
1388
|
+
return;
|
|
1389
|
+
const positions = getCharacterPositions(this._text, this._font);
|
|
1390
|
+
const startX = positions[selection.start] || 0;
|
|
1391
|
+
const endX = selection.end < positions.length ? positions[selection.end] : measureText({ text: this._text, font: this._font }).width;
|
|
1392
|
+
if (this._selectionBg) {
|
|
1393
|
+
this.frameBuffer.fillRect(startX, 0, endX - startX, this.height, this._selectionBg);
|
|
1394
|
+
}
|
|
1395
|
+
if (this._selectionFg || this._selectionBg) {
|
|
1396
|
+
renderFontToFrameBuffer(this.frameBuffer, {
|
|
1397
|
+
text: selectedText,
|
|
1398
|
+
x: startX,
|
|
1399
|
+
y: 0,
|
|
1400
|
+
fg: this._selectionFg ? [this._selectionFg] : this._fg,
|
|
1401
|
+
bg: this._selectionBg || this._bg,
|
|
1402
|
+
font: this._font
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1225
1407
|
// src/renderables/Box.ts
|
|
1226
1408
|
function isGapType(value) {
|
|
1227
1409
|
if (value === undefined) {
|
|
@@ -1776,36 +1958,6 @@ class CodeRenderable extends TextBufferRenderable {
|
|
|
1776
1958
|
return new StyledText(chunks);
|
|
1777
1959
|
}
|
|
1778
1960
|
}
|
|
1779
|
-
// src/renderables/FrameBuffer.ts
|
|
1780
|
-
class FrameBufferRenderable extends Renderable {
|
|
1781
|
-
frameBuffer;
|
|
1782
|
-
respectAlpha;
|
|
1783
|
-
constructor(ctx, options) {
|
|
1784
|
-
super(ctx, options);
|
|
1785
|
-
this.respectAlpha = options.respectAlpha || false;
|
|
1786
|
-
this.frameBuffer = OptimizedBuffer.create(options.width, options.height, this._ctx.widthMethod, {
|
|
1787
|
-
respectAlpha: this.respectAlpha,
|
|
1788
|
-
id: options.id || `framebufferrenderable-${this.id}`
|
|
1789
|
-
});
|
|
1790
|
-
}
|
|
1791
|
-
onResize(width, height) {
|
|
1792
|
-
if (width <= 0 || height <= 0) {
|
|
1793
|
-
throw new Error(`Invalid resize dimensions for FrameBufferRenderable ${this.id}: ${width}x${height}`);
|
|
1794
|
-
}
|
|
1795
|
-
this.frameBuffer.resize(width, height);
|
|
1796
|
-
super.onResize(width, height);
|
|
1797
|
-
this.requestRender();
|
|
1798
|
-
}
|
|
1799
|
-
renderSelf(buffer) {
|
|
1800
|
-
if (!this.visible || this.isDestroyed)
|
|
1801
|
-
return;
|
|
1802
|
-
buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
|
|
1803
|
-
}
|
|
1804
|
-
destroySelf() {
|
|
1805
|
-
this.frameBuffer?.destroy();
|
|
1806
|
-
super.destroySelf();
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
1961
|
// src/renderables/TextNode.ts
|
|
1810
1962
|
var BrandedTextNodeRenderable = Symbol.for("@opentui/core/TextNodeRenderable");
|
|
1811
1963
|
function isTextNodeRenderable(obj) {
|
|
@@ -1918,15 +2070,14 @@ class TextNodeRenderable extends BaseRenderable {
|
|
|
1918
2070
|
this.requestRender();
|
|
1919
2071
|
return this;
|
|
1920
2072
|
}
|
|
1921
|
-
remove(
|
|
1922
|
-
const childIndex = this.
|
|
2073
|
+
remove(id) {
|
|
2074
|
+
const childIndex = this.getRenderableIndex(id);
|
|
1923
2075
|
if (childIndex === -1) {
|
|
1924
2076
|
throw new Error("Child not found in children");
|
|
1925
2077
|
}
|
|
2078
|
+
const child = this._children[childIndex];
|
|
1926
2079
|
this._children.splice(childIndex, 1);
|
|
1927
|
-
|
|
1928
|
-
child.parent = null;
|
|
1929
|
-
}
|
|
2080
|
+
child.parent = null;
|
|
1930
2081
|
this.requestRender();
|
|
1931
2082
|
return this;
|
|
1932
2083
|
}
|
|
@@ -1985,6 +2136,9 @@ class TextNodeRenderable extends BaseRenderable {
|
|
|
1985
2136
|
getRenderable(id) {
|
|
1986
2137
|
return this._children.find((child) => typeof child !== "string" && child.id === id);
|
|
1987
2138
|
}
|
|
2139
|
+
getRenderableIndex(id) {
|
|
2140
|
+
return this._children.findIndex((child) => isTextNodeRenderable(child) && child.id === id);
|
|
2141
|
+
}
|
|
1988
2142
|
get fg() {
|
|
1989
2143
|
return this._fg;
|
|
1990
2144
|
}
|
|
@@ -2035,2296 +2189,2148 @@ class RootTextNodeRenderable extends TextNodeRenderable {
|
|
|
2035
2189
|
}
|
|
2036
2190
|
}
|
|
2037
2191
|
|
|
2038
|
-
// src/renderables/
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2192
|
+
// src/renderables/composition/constructs.ts
|
|
2193
|
+
function Generic(props, ...children) {
|
|
2194
|
+
return h(VRenderable, props || {}, ...children);
|
|
2195
|
+
}
|
|
2196
|
+
function Box(props, ...children) {
|
|
2197
|
+
return h(BoxRenderable, props || {}, ...children);
|
|
2198
|
+
}
|
|
2199
|
+
function Text(props, ...children) {
|
|
2200
|
+
return h(TextRenderable, props || {}, ...children);
|
|
2201
|
+
}
|
|
2202
|
+
function ASCIIFont(props, ...children) {
|
|
2203
|
+
return h(ASCIIFontRenderable, props || {}, ...children);
|
|
2204
|
+
}
|
|
2205
|
+
function Input(props, ...children) {
|
|
2206
|
+
return h(InputRenderable, props || {}, ...children);
|
|
2207
|
+
}
|
|
2208
|
+
function Select(props, ...children) {
|
|
2209
|
+
return h(SelectRenderable, props || {}, ...children);
|
|
2210
|
+
}
|
|
2211
|
+
function TabSelect(props, ...children) {
|
|
2212
|
+
return h(TabSelectRenderable, props || {}, ...children);
|
|
2213
|
+
}
|
|
2214
|
+
function FrameBuffer(props, ...children) {
|
|
2215
|
+
return h(FrameBufferRenderable, props, ...children);
|
|
2216
|
+
}
|
|
2217
|
+
function StyledText2(props, ...children) {
|
|
2218
|
+
const styledProps = props;
|
|
2219
|
+
const textNodeOptions = {
|
|
2220
|
+
...styledProps,
|
|
2221
|
+
attributes: styledProps?.attributes ?? 0
|
|
2045
2222
|
};
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
const styledText = typeof content === "string" ? stringToStyledText(content) : content;
|
|
2050
|
-
this._text = styledText;
|
|
2051
|
-
this._hasManualStyledText = options.content !== undefined && content !== "";
|
|
2052
|
-
this.rootTextNode = new RootTextNodeRenderable(ctx, {
|
|
2053
|
-
id: `${this.id}-root`,
|
|
2054
|
-
fg: this._defaultFg,
|
|
2055
|
-
bg: this._defaultBg,
|
|
2056
|
-
attributes: this._defaultAttributes
|
|
2057
|
-
}, this);
|
|
2058
|
-
this.updateTextBuffer(styledText);
|
|
2059
|
-
}
|
|
2060
|
-
updateTextBuffer(styledText) {
|
|
2061
|
-
this.textBuffer.setStyledText(styledText);
|
|
2062
|
-
this.clearChunks(styledText);
|
|
2063
|
-
}
|
|
2064
|
-
clearChunks(styledText) {}
|
|
2065
|
-
get content() {
|
|
2066
|
-
return this._text;
|
|
2067
|
-
}
|
|
2068
|
-
get chunks() {
|
|
2069
|
-
return this._text.chunks;
|
|
2223
|
+
const textNode = new TextNodeRenderable(textNodeOptions);
|
|
2224
|
+
for (const child of children) {
|
|
2225
|
+
textNode.add(child);
|
|
2070
2226
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2227
|
+
return textNode;
|
|
2228
|
+
}
|
|
2229
|
+
var vstyles = {
|
|
2230
|
+
bold: (...children) => StyledText2({ attributes: TextAttributes.BOLD }, ...children),
|
|
2231
|
+
italic: (...children) => StyledText2({ attributes: TextAttributes.ITALIC }, ...children),
|
|
2232
|
+
underline: (...children) => StyledText2({ attributes: TextAttributes.UNDERLINE }, ...children),
|
|
2233
|
+
dim: (...children) => StyledText2({ attributes: TextAttributes.DIM }, ...children),
|
|
2234
|
+
blink: (...children) => StyledText2({ attributes: TextAttributes.BLINK }, ...children),
|
|
2235
|
+
inverse: (...children) => StyledText2({ attributes: TextAttributes.INVERSE }, ...children),
|
|
2236
|
+
hidden: (...children) => StyledText2({ attributes: TextAttributes.HIDDEN }, ...children),
|
|
2237
|
+
strikethrough: (...children) => StyledText2({ attributes: TextAttributes.STRIKETHROUGH }, ...children),
|
|
2238
|
+
boldItalic: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC }, ...children),
|
|
2239
|
+
boldUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE }, ...children),
|
|
2240
|
+
italicUnderline: (...children) => StyledText2({ attributes: TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
2241
|
+
boldItalicUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
2242
|
+
color: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
2243
|
+
bgColor: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
2244
|
+
fg: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
2245
|
+
bg: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
2246
|
+
styled: (attributes = 0, ...children) => StyledText2({ attributes }, ...children)
|
|
2247
|
+
};
|
|
2248
|
+
// src/renderables/composition/VRenderable.ts
|
|
2249
|
+
class VRenderable extends Renderable {
|
|
2250
|
+
options;
|
|
2251
|
+
constructor(ctx, options) {
|
|
2252
|
+
super(ctx, options);
|
|
2253
|
+
this.options = options;
|
|
2073
2254
|
}
|
|
2074
|
-
|
|
2075
|
-
this.
|
|
2076
|
-
|
|
2077
|
-
if (this._text !== styledText) {
|
|
2078
|
-
this._text = styledText;
|
|
2079
|
-
this.updateTextBuffer(styledText);
|
|
2080
|
-
this.updateTextInfo();
|
|
2255
|
+
renderSelf(buffer, deltaTime) {
|
|
2256
|
+
if (this.options.render) {
|
|
2257
|
+
this.options.render.call(this.options, buffer, deltaTime, this);
|
|
2081
2258
|
}
|
|
2082
2259
|
}
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2260
|
+
}
|
|
2261
|
+
// src/renderables/Input.ts
|
|
2262
|
+
var InputRenderableEvents;
|
|
2263
|
+
((InputRenderableEvents2) => {
|
|
2264
|
+
InputRenderableEvents2["INPUT"] = "input";
|
|
2265
|
+
InputRenderableEvents2["CHANGE"] = "change";
|
|
2266
|
+
InputRenderableEvents2["ENTER"] = "enter";
|
|
2267
|
+
})(InputRenderableEvents ||= {});
|
|
2268
|
+
|
|
2269
|
+
class InputRenderable extends Renderable {
|
|
2270
|
+
_focusable = true;
|
|
2271
|
+
_value = "";
|
|
2272
|
+
_cursorPosition = 0;
|
|
2273
|
+
_placeholder;
|
|
2274
|
+
_backgroundColor;
|
|
2275
|
+
_textColor;
|
|
2276
|
+
_focusedBackgroundColor;
|
|
2277
|
+
_focusedTextColor;
|
|
2278
|
+
_placeholderColor;
|
|
2279
|
+
_cursorColor;
|
|
2280
|
+
_maxLength;
|
|
2281
|
+
_lastCommittedValue = "";
|
|
2282
|
+
_defaultOptions = {
|
|
2283
|
+
backgroundColor: "transparent",
|
|
2284
|
+
textColor: "#FFFFFF",
|
|
2285
|
+
focusedBackgroundColor: "#1a1a1a",
|
|
2286
|
+
focusedTextColor: "#FFFFFF",
|
|
2287
|
+
placeholder: "",
|
|
2288
|
+
placeholderColor: "#666666",
|
|
2289
|
+
cursorColor: "#FFFFFF",
|
|
2290
|
+
maxLength: 1000,
|
|
2291
|
+
value: ""
|
|
2292
|
+
};
|
|
2293
|
+
constructor(ctx, options) {
|
|
2294
|
+
super(ctx, { ...options, buffered: true });
|
|
2295
|
+
this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
|
|
2296
|
+
this._textColor = parseColor(options.textColor || this._defaultOptions.textColor);
|
|
2297
|
+
this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || options.backgroundColor || this._defaultOptions.focusedBackgroundColor);
|
|
2298
|
+
this._focusedTextColor = parseColor(options.focusedTextColor || options.textColor || this._defaultOptions.focusedTextColor);
|
|
2299
|
+
this._placeholder = options.placeholder || this._defaultOptions.placeholder;
|
|
2300
|
+
this._value = options.value || this._defaultOptions.value;
|
|
2301
|
+
this._lastCommittedValue = this._value;
|
|
2302
|
+
this._cursorPosition = this._value.length;
|
|
2303
|
+
this._maxLength = options.maxLength || this._defaultOptions.maxLength;
|
|
2304
|
+
this._placeholderColor = parseColor(options.placeholderColor || this._defaultOptions.placeholderColor);
|
|
2305
|
+
this._cursorColor = parseColor(options.cursorColor || this._defaultOptions.cursorColor);
|
|
2086
2306
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
if (index === -1)
|
|
2307
|
+
updateCursorPosition() {
|
|
2308
|
+
if (!this._focused)
|
|
2090
2309
|
return;
|
|
2091
|
-
|
|
2092
|
-
|
|
2310
|
+
const contentX = 0;
|
|
2311
|
+
const contentY = 0;
|
|
2312
|
+
const contentWidth = this.width;
|
|
2313
|
+
const maxVisibleChars = contentWidth - 1;
|
|
2314
|
+
let displayStartIndex = 0;
|
|
2315
|
+
if (this._cursorPosition >= maxVisibleChars) {
|
|
2316
|
+
displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
|
|
2317
|
+
}
|
|
2318
|
+
const cursorDisplayX = this._cursorPosition - displayStartIndex;
|
|
2319
|
+
if (cursorDisplayX >= 0 && cursorDisplayX < contentWidth) {
|
|
2320
|
+
const absoluteCursorX = this.x + contentX + cursorDisplayX + 1;
|
|
2321
|
+
const absoluteCursorY = this.y + contentY + 1;
|
|
2322
|
+
this._ctx.setCursorPosition(absoluteCursorX, absoluteCursorY, true);
|
|
2323
|
+
this._ctx.setCursorColor(this._cursorColor);
|
|
2324
|
+
}
|
|
2093
2325
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
this.clearChunks(this._text);
|
|
2326
|
+
focus() {
|
|
2327
|
+
super.focus();
|
|
2328
|
+
this._ctx.setCursorStyle("block", true);
|
|
2329
|
+
this._ctx.setCursorColor(this._cursorColor);
|
|
2330
|
+
this.updateCursorPosition();
|
|
2100
2331
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
});
|
|
2108
|
-
this.textBuffer.setStyledText(new StyledText(chunks));
|
|
2109
|
-
this.refreshLocalSelection();
|
|
2110
|
-
this.yogaNode.markDirty();
|
|
2332
|
+
blur() {
|
|
2333
|
+
super.blur();
|
|
2334
|
+
this._ctx.setCursorPosition(0, 0, false);
|
|
2335
|
+
if (this._value !== this._lastCommittedValue) {
|
|
2336
|
+
this._lastCommittedValue = this._value;
|
|
2337
|
+
this.emit("change" /* CHANGE */, this._value);
|
|
2111
2338
|
}
|
|
2112
2339
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
if (child && isTextNodeRenderable(child)) {
|
|
2119
|
-
this.rootTextNode.remove(child);
|
|
2340
|
+
renderSelf(buffer, deltaTime) {
|
|
2341
|
+
if (!this.visible || !this.frameBuffer)
|
|
2342
|
+
return;
|
|
2343
|
+
if (this.isDirty) {
|
|
2344
|
+
this.refreshFrameBuffer();
|
|
2120
2345
|
}
|
|
2121
2346
|
}
|
|
2122
|
-
|
|
2123
|
-
this.
|
|
2124
|
-
|
|
2347
|
+
refreshFrameBuffer() {
|
|
2348
|
+
if (!this.frameBuffer)
|
|
2349
|
+
return;
|
|
2350
|
+
const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
2351
|
+
this.frameBuffer.clear(bgColor);
|
|
2352
|
+
const contentX = 0;
|
|
2353
|
+
const contentY = 0;
|
|
2354
|
+
const contentWidth = this.width;
|
|
2355
|
+
const contentHeight = this.height;
|
|
2356
|
+
const displayText = this._value || this._placeholder;
|
|
2357
|
+
const isPlaceholder = !this._value && this._placeholder;
|
|
2358
|
+
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
2359
|
+
const textColor = isPlaceholder ? this._placeholderColor : baseTextColor;
|
|
2360
|
+
const maxVisibleChars = contentWidth - 1;
|
|
2361
|
+
let displayStartIndex = 0;
|
|
2362
|
+
if (this._cursorPosition >= maxVisibleChars) {
|
|
2363
|
+
displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
|
|
2364
|
+
}
|
|
2365
|
+
const visibleText = displayText.substring(displayStartIndex, displayStartIndex + maxVisibleChars);
|
|
2366
|
+
if (visibleText) {
|
|
2367
|
+
this.frameBuffer.drawText(visibleText, contentX, contentY, textColor);
|
|
2368
|
+
}
|
|
2369
|
+
if (this._focused) {
|
|
2370
|
+
this.updateCursorPosition();
|
|
2371
|
+
}
|
|
2125
2372
|
}
|
|
2126
|
-
|
|
2127
|
-
return this.
|
|
2373
|
+
get value() {
|
|
2374
|
+
return this._value;
|
|
2128
2375
|
}
|
|
2129
|
-
|
|
2130
|
-
this.
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2376
|
+
set value(value) {
|
|
2377
|
+
const newValue = value.substring(0, this._maxLength);
|
|
2378
|
+
if (this._value !== newValue) {
|
|
2379
|
+
this._value = newValue;
|
|
2380
|
+
this._cursorPosition = Math.min(this._cursorPosition, this._value.length);
|
|
2381
|
+
this.requestRender();
|
|
2382
|
+
this.updateCursorPosition();
|
|
2383
|
+
this.emit("input" /* INPUT */, this._value);
|
|
2384
|
+
}
|
|
2136
2385
|
}
|
|
2137
|
-
|
|
2138
|
-
this.
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2386
|
+
set placeholder(placeholder) {
|
|
2387
|
+
if (this._placeholder !== placeholder) {
|
|
2388
|
+
this._placeholder = placeholder;
|
|
2389
|
+
this.requestRender();
|
|
2390
|
+
}
|
|
2142
2391
|
}
|
|
2143
|
-
|
|
2144
|
-
this.
|
|
2392
|
+
get cursorPosition() {
|
|
2393
|
+
return this._cursorPosition;
|
|
2145
2394
|
}
|
|
2146
|
-
|
|
2147
|
-
this.
|
|
2395
|
+
set cursorPosition(position) {
|
|
2396
|
+
const newPosition = Math.max(0, Math.min(position, this._value.length));
|
|
2397
|
+
if (this._cursorPosition !== newPosition) {
|
|
2398
|
+
this._cursorPosition = newPosition;
|
|
2399
|
+
this.requestRender();
|
|
2400
|
+
this.updateCursorPosition();
|
|
2401
|
+
}
|
|
2148
2402
|
}
|
|
2149
|
-
|
|
2150
|
-
this.
|
|
2151
|
-
|
|
2403
|
+
insertText(text) {
|
|
2404
|
+
if (this._value.length + text.length > this._maxLength) {
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
const beforeCursor = this._value.substring(0, this._cursorPosition);
|
|
2408
|
+
const afterCursor = this._value.substring(this._cursorPosition);
|
|
2409
|
+
this._value = beforeCursor + text + afterCursor;
|
|
2410
|
+
this._cursorPosition += text.length;
|
|
2411
|
+
this.requestRender();
|
|
2412
|
+
this.updateCursorPosition();
|
|
2413
|
+
this.emit("input" /* INPUT */, this._value);
|
|
2152
2414
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
super(ctx, {
|
|
2170
|
-
flexShrink: 0,
|
|
2171
|
-
...options,
|
|
2172
|
-
width: measurements.width || 1,
|
|
2173
|
-
height: measurements.height || 1,
|
|
2174
|
-
respectAlpha: true
|
|
2175
|
-
});
|
|
2176
|
-
this._text = text;
|
|
2177
|
-
this._font = font;
|
|
2178
|
-
this._fg = Array.isArray(options.fg) ? options.fg : [options.fg || RGBA.fromInts(255, 255, 255, 255)];
|
|
2179
|
-
this._bg = options.bg || RGBA.fromValues(0, 0, 0, 0);
|
|
2180
|
-
this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined;
|
|
2181
|
-
this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined;
|
|
2182
|
-
this.selectable = options.selectable ?? true;
|
|
2183
|
-
this.selectionHelper = new ASCIIFontSelectionHelper(() => this._text, () => this._font);
|
|
2184
|
-
this.renderFontToBuffer();
|
|
2185
|
-
}
|
|
2186
|
-
get text() {
|
|
2187
|
-
return this._text;
|
|
2188
|
-
}
|
|
2189
|
-
set text(value) {
|
|
2190
|
-
this._text = value;
|
|
2191
|
-
this.updateDimensions();
|
|
2192
|
-
if (this.lastLocalSelection) {
|
|
2193
|
-
this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
|
|
2415
|
+
deleteCharacter(direction) {
|
|
2416
|
+
if (direction === "backward" && this._cursorPosition > 0) {
|
|
2417
|
+
const beforeCursor = this._value.substring(0, this._cursorPosition - 1);
|
|
2418
|
+
const afterCursor = this._value.substring(this._cursorPosition);
|
|
2419
|
+
this._value = beforeCursor + afterCursor;
|
|
2420
|
+
this._cursorPosition--;
|
|
2421
|
+
this.requestRender();
|
|
2422
|
+
this.updateCursorPosition();
|
|
2423
|
+
this.emit("input" /* INPUT */, this._value);
|
|
2424
|
+
} else if (direction === "forward" && this._cursorPosition < this._value.length) {
|
|
2425
|
+
const beforeCursor = this._value.substring(0, this._cursorPosition);
|
|
2426
|
+
const afterCursor = this._value.substring(this._cursorPosition + 1);
|
|
2427
|
+
this._value = beforeCursor + afterCursor;
|
|
2428
|
+
this.requestRender();
|
|
2429
|
+
this.updateCursorPosition();
|
|
2430
|
+
this.emit("input" /* INPUT */, this._value);
|
|
2194
2431
|
}
|
|
2195
|
-
this.renderFontToBuffer();
|
|
2196
|
-
this.requestRender();
|
|
2197
2432
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2433
|
+
handleKeyPress(key) {
|
|
2434
|
+
const keyName = typeof key === "string" ? key : key.name;
|
|
2435
|
+
const keySequence = typeof key === "string" ? key : key.sequence;
|
|
2436
|
+
switch (keyName) {
|
|
2437
|
+
case "left":
|
|
2438
|
+
this.cursorPosition = this._cursorPosition - 1;
|
|
2439
|
+
return true;
|
|
2440
|
+
case "right":
|
|
2441
|
+
this.cursorPosition = this._cursorPosition + 1;
|
|
2442
|
+
return true;
|
|
2443
|
+
case "home":
|
|
2444
|
+
this.cursorPosition = 0;
|
|
2445
|
+
return true;
|
|
2446
|
+
case "end":
|
|
2447
|
+
this.cursorPosition = this._value.length;
|
|
2448
|
+
return true;
|
|
2449
|
+
case "backspace":
|
|
2450
|
+
this.deleteCharacter("backward");
|
|
2451
|
+
return true;
|
|
2452
|
+
case "delete":
|
|
2453
|
+
this.deleteCharacter("forward");
|
|
2454
|
+
return true;
|
|
2455
|
+
case "return":
|
|
2456
|
+
case "enter":
|
|
2457
|
+
if (this._value !== this._lastCommittedValue) {
|
|
2458
|
+
this._lastCommittedValue = this._value;
|
|
2459
|
+
this.emit("change" /* CHANGE */, this._value);
|
|
2460
|
+
}
|
|
2461
|
+
this.emit("enter" /* ENTER */, this._value);
|
|
2462
|
+
return true;
|
|
2463
|
+
default:
|
|
2464
|
+
if (keySequence && keySequence.length === 1 && keySequence.charCodeAt(0) >= 32 && keySequence.charCodeAt(0) <= 126) {
|
|
2465
|
+
this.insertText(keySequence);
|
|
2466
|
+
return true;
|
|
2467
|
+
}
|
|
2468
|
+
break;
|
|
2206
2469
|
}
|
|
2207
|
-
|
|
2208
|
-
this.requestRender();
|
|
2209
|
-
}
|
|
2210
|
-
get fg() {
|
|
2211
|
-
return this._fg;
|
|
2470
|
+
return false;
|
|
2212
2471
|
}
|
|
2213
|
-
set
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
this.
|
|
2472
|
+
set maxLength(maxLength) {
|
|
2473
|
+
this._maxLength = maxLength;
|
|
2474
|
+
if (this._value.length > maxLength) {
|
|
2475
|
+
this._value = this._value.substring(0, maxLength);
|
|
2476
|
+
this.requestRender();
|
|
2218
2477
|
}
|
|
2219
|
-
this.renderFontToBuffer();
|
|
2220
|
-
this.requestRender();
|
|
2221
2478
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2479
|
+
set backgroundColor(value) {
|
|
2480
|
+
const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
|
|
2481
|
+
if (this._backgroundColor !== newColor) {
|
|
2482
|
+
this._backgroundColor = newColor;
|
|
2483
|
+
this.requestRender();
|
|
2484
|
+
}
|
|
2224
2485
|
}
|
|
2225
|
-
set
|
|
2226
|
-
|
|
2227
|
-
this.
|
|
2228
|
-
|
|
2486
|
+
set textColor(value) {
|
|
2487
|
+
const newColor = parseColor(value ?? this._defaultOptions.textColor);
|
|
2488
|
+
if (this._textColor !== newColor) {
|
|
2489
|
+
this._textColor = newColor;
|
|
2490
|
+
this.requestRender();
|
|
2491
|
+
}
|
|
2229
2492
|
}
|
|
2230
|
-
|
|
2231
|
-
const
|
|
2232
|
-
this.
|
|
2233
|
-
|
|
2493
|
+
set focusedBackgroundColor(value) {
|
|
2494
|
+
const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
|
|
2495
|
+
if (this._focusedBackgroundColor !== newColor) {
|
|
2496
|
+
this._focusedBackgroundColor = newColor;
|
|
2497
|
+
this.requestRender();
|
|
2498
|
+
}
|
|
2234
2499
|
}
|
|
2235
|
-
|
|
2236
|
-
const
|
|
2237
|
-
|
|
2238
|
-
|
|
2500
|
+
set focusedTextColor(value) {
|
|
2501
|
+
const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
|
|
2502
|
+
if (this._focusedTextColor !== newColor) {
|
|
2503
|
+
this._focusedTextColor = newColor;
|
|
2504
|
+
this.requestRender();
|
|
2505
|
+
}
|
|
2239
2506
|
}
|
|
2240
|
-
|
|
2241
|
-
const
|
|
2242
|
-
this.
|
|
2243
|
-
|
|
2244
|
-
if (changed) {
|
|
2245
|
-
this.renderFontToBuffer();
|
|
2507
|
+
set placeholderColor(value) {
|
|
2508
|
+
const newColor = parseColor(value ?? this._defaultOptions.placeholderColor);
|
|
2509
|
+
if (this._placeholderColor !== newColor) {
|
|
2510
|
+
this._placeholderColor = newColor;
|
|
2246
2511
|
this.requestRender();
|
|
2247
2512
|
}
|
|
2248
|
-
return changed;
|
|
2249
2513
|
}
|
|
2250
|
-
|
|
2251
|
-
const
|
|
2252
|
-
if (
|
|
2253
|
-
|
|
2254
|
-
|
|
2514
|
+
set cursorColor(value) {
|
|
2515
|
+
const newColor = parseColor(value ?? this._defaultOptions.cursorColor);
|
|
2516
|
+
if (this._cursorColor !== newColor) {
|
|
2517
|
+
this._cursorColor = newColor;
|
|
2518
|
+
this.requestRender();
|
|
2519
|
+
}
|
|
2255
2520
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2521
|
+
updateFromLayout() {
|
|
2522
|
+
super.updateFromLayout();
|
|
2523
|
+
this.updateCursorPosition();
|
|
2258
2524
|
}
|
|
2259
2525
|
onResize(width, height) {
|
|
2260
2526
|
super.onResize(width, height);
|
|
2261
|
-
this.
|
|
2262
|
-
}
|
|
2263
|
-
renderFontToBuffer() {
|
|
2264
|
-
if (this.isDestroyed)
|
|
2265
|
-
return;
|
|
2266
|
-
this.frameBuffer.clear(this._bg);
|
|
2267
|
-
renderFontToFrameBuffer(this.frameBuffer, {
|
|
2268
|
-
text: this._text,
|
|
2269
|
-
x: 0,
|
|
2270
|
-
y: 0,
|
|
2271
|
-
fg: this._fg,
|
|
2272
|
-
bg: this._bg,
|
|
2273
|
-
font: this._font
|
|
2274
|
-
});
|
|
2275
|
-
const selection = this.selectionHelper.getSelection();
|
|
2276
|
-
if (selection && (this._selectionBg || this._selectionFg)) {
|
|
2277
|
-
this.renderSelectionHighlight(selection);
|
|
2278
|
-
}
|
|
2527
|
+
this.updateCursorPosition();
|
|
2279
2528
|
}
|
|
2280
|
-
|
|
2281
|
-
if (
|
|
2282
|
-
|
|
2283
|
-
const selectedText = this._text.slice(selection.start, selection.end);
|
|
2284
|
-
if (!selectedText)
|
|
2285
|
-
return;
|
|
2286
|
-
const positions = getCharacterPositions(this._text, this._font);
|
|
2287
|
-
const startX = positions[selection.start] || 0;
|
|
2288
|
-
const endX = selection.end < positions.length ? positions[selection.end] : measureText({ text: this._text, font: this._font }).width;
|
|
2289
|
-
if (this._selectionBg) {
|
|
2290
|
-
this.frameBuffer.fillRect(startX, 0, endX - startX, this.height, this._selectionBg);
|
|
2291
|
-
}
|
|
2292
|
-
if (this._selectionFg || this._selectionBg) {
|
|
2293
|
-
renderFontToFrameBuffer(this.frameBuffer, {
|
|
2294
|
-
text: selectedText,
|
|
2295
|
-
x: startX,
|
|
2296
|
-
y: 0,
|
|
2297
|
-
fg: this._selectionFg ? [this._selectionFg] : this._fg,
|
|
2298
|
-
bg: this._selectionBg || this._bg,
|
|
2299
|
-
font: this._font
|
|
2300
|
-
});
|
|
2529
|
+
onRemove() {
|
|
2530
|
+
if (this._focused) {
|
|
2531
|
+
this._ctx.setCursorPosition(0, 0, false);
|
|
2301
2532
|
}
|
|
2302
2533
|
}
|
|
2303
2534
|
}
|
|
2304
|
-
// src/renderables/
|
|
2305
|
-
var
|
|
2306
|
-
(
|
|
2307
|
-
InputRenderableEvents2["INPUT"] = "input";
|
|
2308
|
-
InputRenderableEvents2["CHANGE"] = "change";
|
|
2309
|
-
InputRenderableEvents2["ENTER"] = "enter";
|
|
2310
|
-
})(InputRenderableEvents ||= {});
|
|
2535
|
+
// src/renderables/Slider.ts
|
|
2536
|
+
var defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
|
|
2537
|
+
var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
2311
2538
|
|
|
2312
|
-
class
|
|
2313
|
-
|
|
2314
|
-
_value
|
|
2315
|
-
|
|
2316
|
-
|
|
2539
|
+
class SliderRenderable extends Renderable {
|
|
2540
|
+
orientation;
|
|
2541
|
+
_value;
|
|
2542
|
+
_min;
|
|
2543
|
+
_max;
|
|
2544
|
+
_viewPortSize;
|
|
2317
2545
|
_backgroundColor;
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
_focusedTextColor;
|
|
2321
|
-
_placeholderColor;
|
|
2322
|
-
_cursorColor;
|
|
2323
|
-
_maxLength;
|
|
2324
|
-
_lastCommittedValue = "";
|
|
2325
|
-
_defaultOptions = {
|
|
2326
|
-
backgroundColor: "transparent",
|
|
2327
|
-
textColor: "#FFFFFF",
|
|
2328
|
-
focusedBackgroundColor: "#1a1a1a",
|
|
2329
|
-
focusedTextColor: "#FFFFFF",
|
|
2330
|
-
placeholder: "",
|
|
2331
|
-
placeholderColor: "#666666",
|
|
2332
|
-
cursorColor: "#FFFFFF",
|
|
2333
|
-
maxLength: 1000,
|
|
2334
|
-
value: ""
|
|
2335
|
-
};
|
|
2546
|
+
_foregroundColor;
|
|
2547
|
+
_onChange;
|
|
2336
2548
|
constructor(ctx, options) {
|
|
2337
|
-
super(ctx, { ...options
|
|
2338
|
-
this.
|
|
2339
|
-
this.
|
|
2340
|
-
this.
|
|
2341
|
-
this.
|
|
2342
|
-
this.
|
|
2343
|
-
this.
|
|
2344
|
-
this.
|
|
2345
|
-
this.
|
|
2346
|
-
this.
|
|
2347
|
-
this._placeholderColor = parseColor(options.placeholderColor || this._defaultOptions.placeholderColor);
|
|
2348
|
-
this._cursorColor = parseColor(options.cursorColor || this._defaultOptions.cursorColor);
|
|
2349
|
-
}
|
|
2350
|
-
updateCursorPosition() {
|
|
2351
|
-
if (!this._focused)
|
|
2352
|
-
return;
|
|
2353
|
-
const contentX = 0;
|
|
2354
|
-
const contentY = 0;
|
|
2355
|
-
const contentWidth = this.width;
|
|
2356
|
-
const maxVisibleChars = contentWidth - 1;
|
|
2357
|
-
let displayStartIndex = 0;
|
|
2358
|
-
if (this._cursorPosition >= maxVisibleChars) {
|
|
2359
|
-
displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
|
|
2360
|
-
}
|
|
2361
|
-
const cursorDisplayX = this._cursorPosition - displayStartIndex;
|
|
2362
|
-
if (cursorDisplayX >= 0 && cursorDisplayX < contentWidth) {
|
|
2363
|
-
const absoluteCursorX = this.x + contentX + cursorDisplayX + 1;
|
|
2364
|
-
const absoluteCursorY = this.y + contentY + 1;
|
|
2365
|
-
this._ctx.setCursorPosition(absoluteCursorX, absoluteCursorY, true);
|
|
2366
|
-
this._ctx.setCursorColor(this._cursorColor);
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
focus() {
|
|
2370
|
-
super.focus();
|
|
2371
|
-
this._ctx.setCursorStyle("block", true);
|
|
2372
|
-
this._ctx.setCursorColor(this._cursorColor);
|
|
2373
|
-
this.updateCursorPosition();
|
|
2374
|
-
}
|
|
2375
|
-
blur() {
|
|
2376
|
-
super.blur();
|
|
2377
|
-
this._ctx.setCursorPosition(0, 0, false);
|
|
2378
|
-
if (this._value !== this._lastCommittedValue) {
|
|
2379
|
-
this._lastCommittedValue = this._value;
|
|
2380
|
-
this.emit("change" /* CHANGE */, this._value);
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
renderSelf(buffer, deltaTime) {
|
|
2384
|
-
if (!this.visible || !this.frameBuffer)
|
|
2385
|
-
return;
|
|
2386
|
-
if (this.isDirty) {
|
|
2387
|
-
this.refreshFrameBuffer();
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2390
|
-
refreshFrameBuffer() {
|
|
2391
|
-
if (!this.frameBuffer)
|
|
2392
|
-
return;
|
|
2393
|
-
const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
2394
|
-
this.frameBuffer.clear(bgColor);
|
|
2395
|
-
const contentX = 0;
|
|
2396
|
-
const contentY = 0;
|
|
2397
|
-
const contentWidth = this.width;
|
|
2398
|
-
const contentHeight = this.height;
|
|
2399
|
-
const displayText = this._value || this._placeholder;
|
|
2400
|
-
const isPlaceholder = !this._value && this._placeholder;
|
|
2401
|
-
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
2402
|
-
const textColor = isPlaceholder ? this._placeholderColor : baseTextColor;
|
|
2403
|
-
const maxVisibleChars = contentWidth - 1;
|
|
2404
|
-
let displayStartIndex = 0;
|
|
2405
|
-
if (this._cursorPosition >= maxVisibleChars) {
|
|
2406
|
-
displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
|
|
2407
|
-
}
|
|
2408
|
-
const visibleText = displayText.substring(displayStartIndex, displayStartIndex + maxVisibleChars);
|
|
2409
|
-
if (visibleText) {
|
|
2410
|
-
this.frameBuffer.drawText(visibleText, contentX, contentY, textColor);
|
|
2411
|
-
}
|
|
2412
|
-
if (this._focused) {
|
|
2413
|
-
this.updateCursorPosition();
|
|
2414
|
-
}
|
|
2549
|
+
super(ctx, { flexShrink: 0, ...options });
|
|
2550
|
+
this.orientation = options.orientation;
|
|
2551
|
+
this._min = options.min ?? 0;
|
|
2552
|
+
this._max = options.max ?? 100;
|
|
2553
|
+
this._value = options.value ?? this._min;
|
|
2554
|
+
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
2555
|
+
this._onChange = options.onChange;
|
|
2556
|
+
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
2557
|
+
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
2558
|
+
this.setupMouseHandling();
|
|
2415
2559
|
}
|
|
2416
2560
|
get value() {
|
|
2417
2561
|
return this._value;
|
|
2418
2562
|
}
|
|
2419
|
-
set value(
|
|
2420
|
-
const
|
|
2421
|
-
if (this._value
|
|
2422
|
-
this._value =
|
|
2423
|
-
this.
|
|
2563
|
+
set value(newValue) {
|
|
2564
|
+
const clamped = Math.max(this._min, Math.min(this._max, newValue));
|
|
2565
|
+
if (clamped !== this._value) {
|
|
2566
|
+
this._value = clamped;
|
|
2567
|
+
this._onChange?.(clamped);
|
|
2568
|
+
this.emit("change", { value: clamped });
|
|
2424
2569
|
this.requestRender();
|
|
2425
|
-
this.updateCursorPosition();
|
|
2426
|
-
this.emit("input" /* INPUT */, this._value);
|
|
2427
2570
|
}
|
|
2428
2571
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2572
|
+
get min() {
|
|
2573
|
+
return this._min;
|
|
2574
|
+
}
|
|
2575
|
+
set min(newMin) {
|
|
2576
|
+
if (newMin !== this._min) {
|
|
2577
|
+
this._min = newMin;
|
|
2578
|
+
if (this._value < newMin) {
|
|
2579
|
+
this.value = newMin;
|
|
2580
|
+
}
|
|
2432
2581
|
this.requestRender();
|
|
2433
2582
|
}
|
|
2434
2583
|
}
|
|
2435
|
-
get
|
|
2436
|
-
return this.
|
|
2584
|
+
get max() {
|
|
2585
|
+
return this._max;
|
|
2437
2586
|
}
|
|
2438
|
-
set
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
this.
|
|
2587
|
+
set max(newMax) {
|
|
2588
|
+
if (newMax !== this._max) {
|
|
2589
|
+
this._max = newMax;
|
|
2590
|
+
if (this._value > newMax) {
|
|
2591
|
+
this.value = newMax;
|
|
2592
|
+
}
|
|
2442
2593
|
this.requestRender();
|
|
2443
|
-
this.updateCursorPosition();
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
insertText(text) {
|
|
2447
|
-
if (this._value.length + text.length > this._maxLength) {
|
|
2448
|
-
return;
|
|
2449
2594
|
}
|
|
2450
|
-
const beforeCursor = this._value.substring(0, this._cursorPosition);
|
|
2451
|
-
const afterCursor = this._value.substring(this._cursorPosition);
|
|
2452
|
-
this._value = beforeCursor + text + afterCursor;
|
|
2453
|
-
this._cursorPosition += text.length;
|
|
2454
|
-
this.requestRender();
|
|
2455
|
-
this.updateCursorPosition();
|
|
2456
|
-
this.emit("input" /* INPUT */, this._value);
|
|
2457
2595
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
this._value = beforeCursor + afterCursor;
|
|
2463
|
-
this._cursorPosition--;
|
|
2464
|
-
this.requestRender();
|
|
2465
|
-
this.updateCursorPosition();
|
|
2466
|
-
this.emit("input" /* INPUT */, this._value);
|
|
2467
|
-
} else if (direction === "forward" && this._cursorPosition < this._value.length) {
|
|
2468
|
-
const beforeCursor = this._value.substring(0, this._cursorPosition);
|
|
2469
|
-
const afterCursor = this._value.substring(this._cursorPosition + 1);
|
|
2470
|
-
this._value = beforeCursor + afterCursor;
|
|
2596
|
+
set viewPortSize(size) {
|
|
2597
|
+
const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
|
|
2598
|
+
if (clampedSize !== this._viewPortSize) {
|
|
2599
|
+
this._viewPortSize = clampedSize;
|
|
2471
2600
|
this.requestRender();
|
|
2472
|
-
this.updateCursorPosition();
|
|
2473
|
-
this.emit("input" /* INPUT */, this._value);
|
|
2474
2601
|
}
|
|
2475
2602
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
const keySequence = typeof key === "string" ? key : key.sequence;
|
|
2479
|
-
switch (keyName) {
|
|
2480
|
-
case "left":
|
|
2481
|
-
this.cursorPosition = this._cursorPosition - 1;
|
|
2482
|
-
return true;
|
|
2483
|
-
case "right":
|
|
2484
|
-
this.cursorPosition = this._cursorPosition + 1;
|
|
2485
|
-
return true;
|
|
2486
|
-
case "home":
|
|
2487
|
-
this.cursorPosition = 0;
|
|
2488
|
-
return true;
|
|
2489
|
-
case "end":
|
|
2490
|
-
this.cursorPosition = this._value.length;
|
|
2491
|
-
return true;
|
|
2492
|
-
case "backspace":
|
|
2493
|
-
this.deleteCharacter("backward");
|
|
2494
|
-
return true;
|
|
2495
|
-
case "delete":
|
|
2496
|
-
this.deleteCharacter("forward");
|
|
2497
|
-
return true;
|
|
2498
|
-
case "return":
|
|
2499
|
-
case "enter":
|
|
2500
|
-
if (this._value !== this._lastCommittedValue) {
|
|
2501
|
-
this._lastCommittedValue = this._value;
|
|
2502
|
-
this.emit("change" /* CHANGE */, this._value);
|
|
2503
|
-
}
|
|
2504
|
-
this.emit("enter" /* ENTER */, this._value);
|
|
2505
|
-
return true;
|
|
2506
|
-
default:
|
|
2507
|
-
if (keySequence && keySequence.length === 1 && keySequence.charCodeAt(0) >= 32 && keySequence.charCodeAt(0) <= 126) {
|
|
2508
|
-
this.insertText(keySequence);
|
|
2509
|
-
return true;
|
|
2510
|
-
}
|
|
2511
|
-
break;
|
|
2512
|
-
}
|
|
2513
|
-
return false;
|
|
2603
|
+
get viewPortSize() {
|
|
2604
|
+
return this._viewPortSize;
|
|
2514
2605
|
}
|
|
2515
|
-
|
|
2516
|
-
this.
|
|
2517
|
-
if (this._value.length > maxLength) {
|
|
2518
|
-
this._value = this._value.substring(0, maxLength);
|
|
2519
|
-
this.requestRender();
|
|
2520
|
-
}
|
|
2606
|
+
get backgroundColor() {
|
|
2607
|
+
return this._backgroundColor;
|
|
2521
2608
|
}
|
|
2522
2609
|
set backgroundColor(value) {
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
this._backgroundColor = newColor;
|
|
2526
|
-
this.requestRender();
|
|
2527
|
-
}
|
|
2610
|
+
this._backgroundColor = parseColor(value);
|
|
2611
|
+
this.requestRender();
|
|
2528
2612
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
if (this._textColor !== newColor) {
|
|
2532
|
-
this._textColor = newColor;
|
|
2533
|
-
this.requestRender();
|
|
2534
|
-
}
|
|
2613
|
+
get foregroundColor() {
|
|
2614
|
+
return this._foregroundColor;
|
|
2535
2615
|
}
|
|
2536
|
-
set
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
this._focusedBackgroundColor = newColor;
|
|
2540
|
-
this.requestRender();
|
|
2541
|
-
}
|
|
2616
|
+
set foregroundColor(value) {
|
|
2617
|
+
this._foregroundColor = parseColor(value);
|
|
2618
|
+
this.requestRender();
|
|
2542
2619
|
}
|
|
2543
|
-
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2620
|
+
calculateDragOffsetVirtual(event) {
|
|
2621
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2622
|
+
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
2623
|
+
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
2624
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
2625
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2626
|
+
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
2549
2627
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2628
|
+
setupMouseHandling() {
|
|
2629
|
+
let isDragging = false;
|
|
2630
|
+
let dragOffsetVirtual = 0;
|
|
2631
|
+
this.onMouseDown = (event) => {
|
|
2632
|
+
event.stopPropagation();
|
|
2633
|
+
event.preventDefault();
|
|
2634
|
+
const thumb = this.getThumbRect();
|
|
2635
|
+
const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
|
|
2636
|
+
if (inThumb) {
|
|
2637
|
+
isDragging = true;
|
|
2638
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2639
|
+
} else {
|
|
2640
|
+
this.updateValueFromMouseDirect(event);
|
|
2641
|
+
isDragging = true;
|
|
2642
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
this.onMouseDrag = (event) => {
|
|
2646
|
+
if (!isDragging)
|
|
2647
|
+
return;
|
|
2648
|
+
event.stopPropagation();
|
|
2649
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
2650
|
+
};
|
|
2651
|
+
this.onMouseUp = (event) => {
|
|
2652
|
+
if (isDragging) {
|
|
2653
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
2654
|
+
}
|
|
2655
|
+
isDragging = false;
|
|
2656
|
+
};
|
|
2563
2657
|
}
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
this.
|
|
2658
|
+
updateValueFromMouseDirect(event) {
|
|
2659
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2660
|
+
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2661
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
2662
|
+
const relativeMousePos = mousePos - trackStart;
|
|
2663
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
2664
|
+
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
2665
|
+
const range = this._max - this._min;
|
|
2666
|
+
const newValue = this._min + ratio * range;
|
|
2667
|
+
this.value = newValue;
|
|
2567
2668
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
this.
|
|
2669
|
+
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
2670
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
2671
|
+
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
2672
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
2673
|
+
const virtualTrackSize = trackSize * 2;
|
|
2674
|
+
const relativeMousePos = mousePos - trackStart;
|
|
2675
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
2676
|
+
const virtualMousePos = clampedMousePos * 2;
|
|
2677
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2678
|
+
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
2679
|
+
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
2680
|
+
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
2681
|
+
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
2682
|
+
const range = this._max - this._min;
|
|
2683
|
+
const newValue = this._min + ratio * range;
|
|
2684
|
+
this.value = newValue;
|
|
2571
2685
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2686
|
+
getThumbRect() {
|
|
2687
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2688
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
2689
|
+
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
2690
|
+
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
2691
|
+
if (this.orientation === "vertical") {
|
|
2692
|
+
return {
|
|
2693
|
+
x: this.x,
|
|
2694
|
+
y: this.y + realThumbStart,
|
|
2695
|
+
width: this.width,
|
|
2696
|
+
height: Math.max(1, realThumbSize)
|
|
2697
|
+
};
|
|
2698
|
+
} else {
|
|
2699
|
+
return {
|
|
2700
|
+
x: this.x + realThumbStart,
|
|
2701
|
+
y: this.y,
|
|
2702
|
+
width: Math.max(1, realThumbSize),
|
|
2703
|
+
height: this.height
|
|
2704
|
+
};
|
|
2575
2705
|
}
|
|
2576
2706
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
SelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
|
|
2583
|
-
})(SelectRenderableEvents ||= {});
|
|
2584
|
-
|
|
2585
|
-
class SelectRenderable extends Renderable {
|
|
2586
|
-
_focusable = true;
|
|
2587
|
-
_options = [];
|
|
2588
|
-
selectedIndex = 0;
|
|
2589
|
-
scrollOffset = 0;
|
|
2590
|
-
maxVisibleItems;
|
|
2591
|
-
_backgroundColor;
|
|
2592
|
-
_textColor;
|
|
2593
|
-
_focusedBackgroundColor;
|
|
2594
|
-
_focusedTextColor;
|
|
2595
|
-
_selectedBackgroundColor;
|
|
2596
|
-
_selectedTextColor;
|
|
2597
|
-
_descriptionColor;
|
|
2598
|
-
_selectedDescriptionColor;
|
|
2599
|
-
_showScrollIndicator;
|
|
2600
|
-
_wrapSelection;
|
|
2601
|
-
_showDescription;
|
|
2602
|
-
_font;
|
|
2603
|
-
_itemSpacing;
|
|
2604
|
-
linesPerItem;
|
|
2605
|
-
fontHeight;
|
|
2606
|
-
_fastScrollStep;
|
|
2607
|
-
_defaultOptions = {
|
|
2608
|
-
backgroundColor: "transparent",
|
|
2609
|
-
textColor: "#FFFFFF",
|
|
2610
|
-
focusedBackgroundColor: "#1a1a1a",
|
|
2611
|
-
focusedTextColor: "#FFFFFF",
|
|
2612
|
-
selectedBackgroundColor: "#334455",
|
|
2613
|
-
selectedTextColor: "#FFFF00",
|
|
2614
|
-
descriptionColor: "#888888",
|
|
2615
|
-
selectedDescriptionColor: "#CCCCCC",
|
|
2616
|
-
showScrollIndicator: false,
|
|
2617
|
-
wrapSelection: false,
|
|
2618
|
-
showDescription: true,
|
|
2619
|
-
itemSpacing: 0,
|
|
2620
|
-
fastScrollStep: 5
|
|
2621
|
-
};
|
|
2622
|
-
constructor(ctx, options) {
|
|
2623
|
-
super(ctx, { ...options, buffered: true });
|
|
2624
|
-
this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
|
|
2625
|
-
this._textColor = parseColor(options.textColor || this._defaultOptions.textColor);
|
|
2626
|
-
this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || this._defaultOptions.focusedBackgroundColor);
|
|
2627
|
-
this._focusedTextColor = parseColor(options.focusedTextColor || this._defaultOptions.focusedTextColor);
|
|
2628
|
-
this._options = options.options || [];
|
|
2629
|
-
this._showScrollIndicator = options.showScrollIndicator ?? this._defaultOptions.showScrollIndicator;
|
|
2630
|
-
this._wrapSelection = options.wrapSelection ?? this._defaultOptions.wrapSelection;
|
|
2631
|
-
this._showDescription = options.showDescription ?? this._defaultOptions.showDescription;
|
|
2632
|
-
this._font = options.font;
|
|
2633
|
-
this._itemSpacing = options.itemSpacing || this._defaultOptions.itemSpacing;
|
|
2634
|
-
this.fontHeight = this._font ? measureText({ text: "A", font: this._font }).height : 1;
|
|
2635
|
-
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
2636
|
-
this.linesPerItem += this._itemSpacing;
|
|
2637
|
-
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
2638
|
-
this._selectedBackgroundColor = parseColor(options.selectedBackgroundColor || this._defaultOptions.selectedBackgroundColor);
|
|
2639
|
-
this._selectedTextColor = parseColor(options.selectedTextColor || this._defaultOptions.selectedTextColor);
|
|
2640
|
-
this._descriptionColor = parseColor(options.descriptionColor || this._defaultOptions.descriptionColor);
|
|
2641
|
-
this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || this._defaultOptions.selectedDescriptionColor);
|
|
2642
|
-
this._fastScrollStep = options.fastScrollStep || this._defaultOptions.fastScrollStep;
|
|
2643
|
-
this.requestRender();
|
|
2644
|
-
}
|
|
2645
|
-
renderSelf(buffer, deltaTime) {
|
|
2646
|
-
if (!this.visible || !this.frameBuffer)
|
|
2647
|
-
return;
|
|
2648
|
-
if (this.isDirty) {
|
|
2649
|
-
this.refreshFrameBuffer();
|
|
2707
|
+
renderSelf(buffer) {
|
|
2708
|
+
if (this.orientation === "horizontal") {
|
|
2709
|
+
this.renderHorizontal(buffer);
|
|
2710
|
+
} else {
|
|
2711
|
+
this.renderVertical(buffer);
|
|
2650
2712
|
}
|
|
2651
2713
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
const
|
|
2656
|
-
this.
|
|
2657
|
-
const
|
|
2658
|
-
const
|
|
2659
|
-
const
|
|
2660
|
-
const
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
const
|
|
2664
|
-
const
|
|
2665
|
-
const
|
|
2666
|
-
const
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
const contentHeight2 = this.linesPerItem - this._itemSpacing;
|
|
2671
|
-
this.frameBuffer.fillRect(contentX, itemY, contentWidth, contentHeight2, this._selectedBackgroundColor);
|
|
2672
|
-
}
|
|
2673
|
-
const nameContent = `${isSelected ? "\u25B6 " : " "}${option.name}`;
|
|
2674
|
-
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
2675
|
-
const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
|
|
2676
|
-
let descX = contentX + 3;
|
|
2677
|
-
if (this._font) {
|
|
2678
|
-
const indicator = isSelected ? "\u25B6 " : " ";
|
|
2679
|
-
this.frameBuffer.drawText(indicator, contentX + 1, itemY, nameColor);
|
|
2680
|
-
const indicatorWidth = 2;
|
|
2681
|
-
renderFontToFrameBuffer(this.frameBuffer, {
|
|
2682
|
-
text: option.name,
|
|
2683
|
-
x: contentX + 1 + indicatorWidth,
|
|
2684
|
-
y: itemY,
|
|
2685
|
-
fg: nameColor,
|
|
2686
|
-
bg: isSelected ? this._selectedBackgroundColor : bgColor,
|
|
2687
|
-
font: this._font
|
|
2688
|
-
});
|
|
2689
|
-
descX = contentX + 1 + indicatorWidth;
|
|
2714
|
+
renderHorizontal(buffer) {
|
|
2715
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2716
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
2717
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
2718
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
2719
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
2720
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
2721
|
+
const startX = Math.max(0, realStartCell);
|
|
2722
|
+
const endX = Math.min(this.width - 1, realEndCell);
|
|
2723
|
+
for (let realX = startX;realX <= endX; realX++) {
|
|
2724
|
+
const virtualCellStart = realX * 2;
|
|
2725
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
2726
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
2727
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
2728
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
2729
|
+
let char = " ";
|
|
2730
|
+
if (coverage >= 2) {
|
|
2731
|
+
char = "\u2588";
|
|
2690
2732
|
} else {
|
|
2691
|
-
|
|
2733
|
+
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
2734
|
+
if (isLeftHalf) {
|
|
2735
|
+
char = "\u258C";
|
|
2736
|
+
} else {
|
|
2737
|
+
char = "\u2590";
|
|
2738
|
+
}
|
|
2692
2739
|
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
const descBg = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
2696
|
-
this.frameBuffer.drawText(option.description, descX, itemY + this.fontHeight, descColor);
|
|
2740
|
+
for (let y = 0;y < this.height; y++) {
|
|
2741
|
+
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
2697
2742
|
}
|
|
2698
2743
|
}
|
|
2699
|
-
if (this._showScrollIndicator && this._options.length > this.maxVisibleItems) {
|
|
2700
|
-
this.renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
|
|
2704
|
-
if (!this.frameBuffer)
|
|
2705
|
-
return;
|
|
2706
|
-
const scrollPercent = this.selectedIndex / Math.max(1, this._options.length - 1);
|
|
2707
|
-
const indicatorHeight = Math.max(1, contentHeight - 2);
|
|
2708
|
-
const indicatorY = contentY + 1 + Math.floor(scrollPercent * indicatorHeight);
|
|
2709
|
-
const indicatorX = contentX + contentWidth - 1;
|
|
2710
|
-
this.frameBuffer.drawText("\u2588", indicatorX, indicatorY, parseColor("#666666"));
|
|
2711
2744
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
this.
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
const newIndex = this.selectedIndex + steps;
|
|
2742
|
-
if (newIndex < this._options.length) {
|
|
2743
|
-
this.selectedIndex = newIndex;
|
|
2744
|
-
} else if (this._wrapSelection && this._options.length > 0) {
|
|
2745
|
-
this.selectedIndex = 0;
|
|
2746
|
-
} else {
|
|
2747
|
-
this.selectedIndex = this._options.length - 1;
|
|
2748
|
-
}
|
|
2749
|
-
this.updateScrollOffset();
|
|
2750
|
-
this.requestRender();
|
|
2751
|
-
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
2752
|
-
}
|
|
2753
|
-
selectCurrent() {
|
|
2754
|
-
const selected = this.getSelectedOption();
|
|
2755
|
-
if (selected) {
|
|
2756
|
-
this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
setSelectedIndex(index) {
|
|
2760
|
-
if (index >= 0 && index < this._options.length) {
|
|
2761
|
-
this.selectedIndex = index;
|
|
2762
|
-
this.updateScrollOffset();
|
|
2763
|
-
this.requestRender();
|
|
2764
|
-
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
updateScrollOffset() {
|
|
2768
|
-
if (!this._options)
|
|
2769
|
-
return;
|
|
2770
|
-
const halfVisible = Math.floor(this.maxVisibleItems / 2);
|
|
2771
|
-
const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleItems));
|
|
2772
|
-
if (newScrollOffset !== this.scrollOffset) {
|
|
2773
|
-
this.scrollOffset = newScrollOffset;
|
|
2774
|
-
this.requestRender();
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
onResize(width, height) {
|
|
2778
|
-
this.maxVisibleItems = Math.max(1, Math.floor(height / this.linesPerItem));
|
|
2779
|
-
this.updateScrollOffset();
|
|
2780
|
-
this.requestRender();
|
|
2781
|
-
}
|
|
2782
|
-
handleKeyPress(key) {
|
|
2783
|
-
const keyName = typeof key === "string" ? key : key.name;
|
|
2784
|
-
const isShift = typeof key !== "string" && key.shift;
|
|
2785
|
-
switch (keyName) {
|
|
2786
|
-
case "up":
|
|
2787
|
-
case "k":
|
|
2788
|
-
this.moveUp(isShift ? this._fastScrollStep : 1);
|
|
2789
|
-
return true;
|
|
2790
|
-
case "down":
|
|
2791
|
-
case "j":
|
|
2792
|
-
this.moveDown(isShift ? this._fastScrollStep : 1);
|
|
2793
|
-
return true;
|
|
2794
|
-
case "return":
|
|
2795
|
-
case "enter":
|
|
2796
|
-
this.selectCurrent();
|
|
2797
|
-
return true;
|
|
2745
|
+
renderVertical(buffer) {
|
|
2746
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2747
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
2748
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
2749
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
2750
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
2751
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
2752
|
+
const startY = Math.max(0, realStartCell);
|
|
2753
|
+
const endY = Math.min(this.height - 1, realEndCell);
|
|
2754
|
+
for (let realY = startY;realY <= endY; realY++) {
|
|
2755
|
+
const virtualCellStart = realY * 2;
|
|
2756
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
2757
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
2758
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
2759
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
2760
|
+
let char = " ";
|
|
2761
|
+
if (coverage >= 2) {
|
|
2762
|
+
char = "\u2588";
|
|
2763
|
+
} else if (coverage > 0) {
|
|
2764
|
+
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
2765
|
+
if (virtualPositionInCell === 0) {
|
|
2766
|
+
char = "\u2580";
|
|
2767
|
+
} else {
|
|
2768
|
+
char = "\u2584";
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
for (let x = 0;x < this.width; x++) {
|
|
2772
|
+
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
2773
|
+
}
|
|
2798
2774
|
}
|
|
2799
|
-
return false;
|
|
2800
2775
|
}
|
|
2801
|
-
|
|
2802
|
-
|
|
2776
|
+
getVirtualThumbSize() {
|
|
2777
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
2778
|
+
const range = this._max - this._min;
|
|
2779
|
+
if (range === 0)
|
|
2780
|
+
return virtualTrackSize;
|
|
2781
|
+
const viewportSize = Math.max(1, this._viewPortSize);
|
|
2782
|
+
const contentSize = range + viewportSize;
|
|
2783
|
+
if (contentSize <= viewportSize)
|
|
2784
|
+
return virtualTrackSize;
|
|
2785
|
+
const thumbRatio = viewportSize / contentSize;
|
|
2786
|
+
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
2787
|
+
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
2803
2788
|
}
|
|
2804
|
-
|
|
2805
|
-
this.
|
|
2806
|
-
this.
|
|
2789
|
+
getVirtualThumbStart() {
|
|
2790
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
2791
|
+
const range = this._max - this._min;
|
|
2792
|
+
if (range === 0)
|
|
2793
|
+
return 0;
|
|
2794
|
+
const valueRatio = (this._value - this._min) / range;
|
|
2795
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
2796
|
+
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
2807
2797
|
}
|
|
2808
|
-
|
|
2809
|
-
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
// src/renderables/ScrollBar.ts
|
|
2801
|
+
class ScrollBarRenderable extends Renderable {
|
|
2802
|
+
slider;
|
|
2803
|
+
startArrow;
|
|
2804
|
+
endArrow;
|
|
2805
|
+
orientation;
|
|
2806
|
+
_focusable = true;
|
|
2807
|
+
_scrollSize = 0;
|
|
2808
|
+
_scrollPosition = 0;
|
|
2809
|
+
_viewportSize = 0;
|
|
2810
|
+
_showArrows = false;
|
|
2811
|
+
_manualVisibility = false;
|
|
2812
|
+
_onChange;
|
|
2813
|
+
scrollStep = null;
|
|
2814
|
+
get visible() {
|
|
2815
|
+
return super.visible;
|
|
2810
2816
|
}
|
|
2811
|
-
set
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
2815
|
-
this.linesPerItem += this._itemSpacing;
|
|
2816
|
-
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
2817
|
-
this.updateScrollOffset();
|
|
2818
|
-
this.requestRender();
|
|
2819
|
-
}
|
|
2817
|
+
set visible(value) {
|
|
2818
|
+
this._manualVisibility = true;
|
|
2819
|
+
super.visible = value;
|
|
2820
2820
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
2821
|
+
resetVisibilityControl() {
|
|
2822
|
+
this._manualVisibility = false;
|
|
2823
|
+
this.recalculateVisibility();
|
|
2823
2824
|
}
|
|
2824
|
-
|
|
2825
|
-
this.
|
|
2825
|
+
get scrollSize() {
|
|
2826
|
+
return this._scrollSize;
|
|
2826
2827
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
if (this._backgroundColor !== newColor) {
|
|
2830
|
-
this._backgroundColor = newColor;
|
|
2831
|
-
this.requestRender();
|
|
2832
|
-
}
|
|
2828
|
+
get scrollPosition() {
|
|
2829
|
+
return this._scrollPosition;
|
|
2833
2830
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
if (this._textColor !== newColor) {
|
|
2837
|
-
this._textColor = newColor;
|
|
2838
|
-
this.requestRender();
|
|
2839
|
-
}
|
|
2831
|
+
get viewportSize() {
|
|
2832
|
+
return this._viewportSize;
|
|
2840
2833
|
}
|
|
2841
|
-
set
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2834
|
+
set scrollSize(value) {
|
|
2835
|
+
if (value === this.scrollSize)
|
|
2836
|
+
return;
|
|
2837
|
+
this._scrollSize = value;
|
|
2838
|
+
this.recalculateVisibility();
|
|
2839
|
+
this.updateSliderFromScrollState();
|
|
2840
|
+
this.scrollPosition = this.scrollPosition;
|
|
2847
2841
|
}
|
|
2848
|
-
set
|
|
2849
|
-
const
|
|
2850
|
-
if (
|
|
2851
|
-
this.
|
|
2852
|
-
this.
|
|
2842
|
+
set scrollPosition(value) {
|
|
2843
|
+
const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
|
|
2844
|
+
if (newPosition !== this._scrollPosition) {
|
|
2845
|
+
this._scrollPosition = newPosition;
|
|
2846
|
+
this.updateSliderFromScrollState();
|
|
2853
2847
|
}
|
|
2854
2848
|
}
|
|
2855
|
-
set
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2849
|
+
set viewportSize(value) {
|
|
2850
|
+
if (value === this.viewportSize)
|
|
2851
|
+
return;
|
|
2852
|
+
this._viewportSize = value;
|
|
2853
|
+
this.slider.viewPortSize = Math.max(1, this._viewportSize);
|
|
2854
|
+
this.recalculateVisibility();
|
|
2855
|
+
this.updateSliderFromScrollState();
|
|
2856
|
+
this.scrollPosition = this.scrollPosition;
|
|
2861
2857
|
}
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
if (this._selectedTextColor !== newColor) {
|
|
2865
|
-
this._selectedTextColor = newColor;
|
|
2866
|
-
this.requestRender();
|
|
2867
|
-
}
|
|
2858
|
+
get showArrows() {
|
|
2859
|
+
return this._showArrows;
|
|
2868
2860
|
}
|
|
2869
|
-
set
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2861
|
+
set showArrows(value) {
|
|
2862
|
+
if (value === this._showArrows)
|
|
2863
|
+
return;
|
|
2864
|
+
this._showArrows = value;
|
|
2865
|
+
this.startArrow.visible = value;
|
|
2866
|
+
this.endArrow.visible = value;
|
|
2875
2867
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2868
|
+
constructor(ctx, { trackOptions, arrowOptions, orientation, showArrows = false, ...options }) {
|
|
2869
|
+
super(ctx, {
|
|
2870
|
+
flexDirection: orientation === "vertical" ? "column" : "row",
|
|
2871
|
+
alignSelf: "stretch",
|
|
2872
|
+
alignItems: "stretch",
|
|
2873
|
+
...options
|
|
2874
|
+
});
|
|
2875
|
+
this._onChange = options.onChange;
|
|
2876
|
+
this.orientation = orientation;
|
|
2877
|
+
this._showArrows = showArrows;
|
|
2878
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
2879
|
+
const defaultStepSize = Math.max(1, this._viewportSize);
|
|
2880
|
+
const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
|
|
2881
|
+
this.slider = new SliderRenderable(ctx, {
|
|
2882
|
+
orientation,
|
|
2883
|
+
min: 0,
|
|
2884
|
+
max: scrollRange,
|
|
2885
|
+
value: this._scrollPosition,
|
|
2886
|
+
viewPortSize: stepSize,
|
|
2887
|
+
onChange: (value) => {
|
|
2888
|
+
this._scrollPosition = Math.round(value);
|
|
2889
|
+
this._onChange?.(this._scrollPosition);
|
|
2890
|
+
this.emit("change", { position: this._scrollPosition });
|
|
2891
|
+
},
|
|
2892
|
+
...orientation === "vertical" ? {
|
|
2893
|
+
width: Math.max(1, Math.min(2, this.width)),
|
|
2894
|
+
height: "100%",
|
|
2895
|
+
marginLeft: "auto"
|
|
2896
|
+
} : {
|
|
2897
|
+
width: "100%",
|
|
2898
|
+
height: 1,
|
|
2899
|
+
marginTop: "auto"
|
|
2900
|
+
},
|
|
2901
|
+
flexGrow: 1,
|
|
2902
|
+
flexShrink: 1,
|
|
2903
|
+
...trackOptions
|
|
2904
|
+
});
|
|
2905
|
+
this.updateSliderFromScrollState();
|
|
2906
|
+
const arrowOpts = arrowOptions ? {
|
|
2907
|
+
foregroundColor: arrowOptions.backgroundColor,
|
|
2908
|
+
backgroundColor: arrowOptions.backgroundColor,
|
|
2909
|
+
attributes: arrowOptions.attributes,
|
|
2910
|
+
...arrowOptions
|
|
2911
|
+
} : {};
|
|
2912
|
+
this.startArrow = new ArrowRenderable(ctx, {
|
|
2913
|
+
alignSelf: "center",
|
|
2914
|
+
visible: this.showArrows,
|
|
2915
|
+
direction: this.orientation === "vertical" ? "up" : "left",
|
|
2916
|
+
height: this.orientation === "vertical" ? 1 : 1,
|
|
2917
|
+
...arrowOpts
|
|
2918
|
+
});
|
|
2919
|
+
this.endArrow = new ArrowRenderable(ctx, {
|
|
2920
|
+
alignSelf: "center",
|
|
2921
|
+
visible: this.showArrows,
|
|
2922
|
+
direction: this.orientation === "vertical" ? "down" : "right",
|
|
2923
|
+
height: this.orientation === "vertical" ? 1 : 1,
|
|
2924
|
+
...arrowOpts
|
|
2925
|
+
});
|
|
2926
|
+
this.add(this.startArrow);
|
|
2927
|
+
this.add(this.slider);
|
|
2928
|
+
this.add(this.endArrow);
|
|
2929
|
+
let startArrowMouseTimeout = undefined;
|
|
2930
|
+
let endArrowMouseTimeout = undefined;
|
|
2931
|
+
this.startArrow.onMouseDown = (event) => {
|
|
2932
|
+
event.stopPropagation();
|
|
2933
|
+
event.preventDefault();
|
|
2934
|
+
this.scrollBy(-0.5, "viewport");
|
|
2935
|
+
startArrowMouseTimeout = setTimeout(() => {
|
|
2936
|
+
this.scrollBy(-0.5, "viewport");
|
|
2937
|
+
startArrowMouseTimeout = setInterval(() => {
|
|
2938
|
+
this.scrollBy(-0.2, "viewport");
|
|
2939
|
+
}, 200);
|
|
2940
|
+
}, 500);
|
|
2941
|
+
};
|
|
2942
|
+
this.startArrow.onMouseUp = (event) => {
|
|
2943
|
+
event.stopPropagation();
|
|
2944
|
+
clearInterval(startArrowMouseTimeout);
|
|
2945
|
+
};
|
|
2946
|
+
this.endArrow.onMouseDown = (event) => {
|
|
2947
|
+
event.stopPropagation();
|
|
2948
|
+
event.preventDefault();
|
|
2949
|
+
this.scrollBy(0.5, "viewport");
|
|
2950
|
+
endArrowMouseTimeout = setTimeout(() => {
|
|
2951
|
+
this.scrollBy(0.5, "viewport");
|
|
2952
|
+
endArrowMouseTimeout = setInterval(() => {
|
|
2953
|
+
this.scrollBy(0.2, "viewport");
|
|
2954
|
+
}, 200);
|
|
2955
|
+
}, 500);
|
|
2956
|
+
};
|
|
2957
|
+
this.endArrow.onMouseUp = (event) => {
|
|
2958
|
+
event.stopPropagation();
|
|
2959
|
+
clearInterval(endArrowMouseTimeout);
|
|
2960
|
+
};
|
|
2882
2961
|
}
|
|
2883
|
-
set
|
|
2884
|
-
this.
|
|
2885
|
-
|
|
2886
|
-
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
2887
|
-
this.linesPerItem += this._itemSpacing;
|
|
2888
|
-
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
2889
|
-
this.updateScrollOffset();
|
|
2962
|
+
set arrowOptions(options) {
|
|
2963
|
+
Object.assign(this.startArrow, options);
|
|
2964
|
+
Object.assign(this.endArrow, options);
|
|
2890
2965
|
this.requestRender();
|
|
2891
2966
|
}
|
|
2892
|
-
set
|
|
2893
|
-
this.
|
|
2894
|
-
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
2895
|
-
this.linesPerItem += this._itemSpacing;
|
|
2896
|
-
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
2897
|
-
this.updateScrollOffset();
|
|
2967
|
+
set trackOptions(options) {
|
|
2968
|
+
Object.assign(this.slider, options);
|
|
2898
2969
|
this.requestRender();
|
|
2899
2970
|
}
|
|
2900
|
-
|
|
2901
|
-
this.
|
|
2971
|
+
updateSliderFromScrollState() {
|
|
2972
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
2973
|
+
this.slider.min = 0;
|
|
2974
|
+
this.slider.max = scrollRange;
|
|
2975
|
+
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
2902
2976
|
}
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
TabSelectRenderableEvents2["SELECTION_CHANGED"] = "selectionChanged";
|
|
2908
|
-
TabSelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
|
|
2909
|
-
})(TabSelectRenderableEvents ||= {});
|
|
2910
|
-
function calculateDynamicHeight(showUnderline, showDescription) {
|
|
2911
|
-
let height = 1;
|
|
2912
|
-
if (showUnderline) {
|
|
2913
|
-
height += 1;
|
|
2977
|
+
scrollBy(delta, unit = "absolute") {
|
|
2978
|
+
const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
|
|
2979
|
+
const resolvedDelta = multiplier * delta;
|
|
2980
|
+
this.scrollPosition += resolvedDelta;
|
|
2914
2981
|
}
|
|
2915
|
-
|
|
2916
|
-
|
|
2982
|
+
recalculateVisibility() {
|
|
2983
|
+
if (!this._manualVisibility) {
|
|
2984
|
+
const sizeRatio = this.scrollSize <= this.viewportSize ? 1 : this.viewportSize / this.scrollSize;
|
|
2985
|
+
super.visible = sizeRatio < 1;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
handleKeyPress(key) {
|
|
2989
|
+
const keyName = typeof key === "string" ? key : key.name;
|
|
2990
|
+
switch (keyName) {
|
|
2991
|
+
case "left":
|
|
2992
|
+
case "h":
|
|
2993
|
+
if (this.orientation !== "horizontal")
|
|
2994
|
+
return false;
|
|
2995
|
+
this.scrollBy(-1 / 5, "viewport");
|
|
2996
|
+
return true;
|
|
2997
|
+
case "right":
|
|
2998
|
+
case "l":
|
|
2999
|
+
if (this.orientation !== "horizontal")
|
|
3000
|
+
return false;
|
|
3001
|
+
this.scrollBy(1 / 5, "viewport");
|
|
3002
|
+
return true;
|
|
3003
|
+
case "up":
|
|
3004
|
+
case "k":
|
|
3005
|
+
if (this.orientation !== "vertical")
|
|
3006
|
+
return false;
|
|
3007
|
+
this.scrollBy(-1 / 5, "viewport");
|
|
3008
|
+
return true;
|
|
3009
|
+
case "down":
|
|
3010
|
+
case "j":
|
|
3011
|
+
if (this.orientation !== "vertical")
|
|
3012
|
+
return false;
|
|
3013
|
+
this.scrollBy(1 / 5, "viewport");
|
|
3014
|
+
return true;
|
|
3015
|
+
case "pageup":
|
|
3016
|
+
this.scrollBy(-1 / 2, "viewport");
|
|
3017
|
+
return true;
|
|
3018
|
+
case "pagedown":
|
|
3019
|
+
this.scrollBy(1 / 2, "viewport");
|
|
3020
|
+
return true;
|
|
3021
|
+
case "home":
|
|
3022
|
+
this.scrollBy(-1, "content");
|
|
3023
|
+
return true;
|
|
3024
|
+
case "end":
|
|
3025
|
+
this.scrollBy(1, "content");
|
|
3026
|
+
return true;
|
|
3027
|
+
}
|
|
3028
|
+
return false;
|
|
2917
3029
|
}
|
|
2918
|
-
return height;
|
|
2919
3030
|
}
|
|
2920
3031
|
|
|
2921
|
-
class
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
selectedIndex = 0;
|
|
2925
|
-
scrollOffset = 0;
|
|
2926
|
-
_tabWidth;
|
|
2927
|
-
maxVisibleTabs;
|
|
3032
|
+
class ArrowRenderable extends Renderable {
|
|
3033
|
+
_direction;
|
|
3034
|
+
_foregroundColor;
|
|
2928
3035
|
_backgroundColor;
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
_focusedTextColor;
|
|
2932
|
-
_selectedBackgroundColor;
|
|
2933
|
-
_selectedTextColor;
|
|
2934
|
-
_selectedDescriptionColor;
|
|
2935
|
-
_showScrollArrows;
|
|
2936
|
-
_showDescription;
|
|
2937
|
-
_showUnderline;
|
|
2938
|
-
_wrapSelection;
|
|
3036
|
+
_attributes;
|
|
3037
|
+
_arrowChars;
|
|
2939
3038
|
constructor(ctx, options) {
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
this.
|
|
2943
|
-
this.
|
|
2944
|
-
this.
|
|
2945
|
-
this.
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || "#CCCCCC");
|
|
3039
|
+
super(ctx, options);
|
|
3040
|
+
this._direction = options.direction;
|
|
3041
|
+
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : RGBA.fromValues(1, 1, 1, 1);
|
|
3042
|
+
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
|
|
3043
|
+
this._attributes = options.attributes ?? 0;
|
|
3044
|
+
this._arrowChars = {
|
|
3045
|
+
up: "\u25B2",
|
|
3046
|
+
down: "\u25BC",
|
|
3047
|
+
left: "\u25C0",
|
|
3048
|
+
right: "\u25B6",
|
|
3049
|
+
...options.arrowChars
|
|
3050
|
+
};
|
|
3051
|
+
if (!options.width) {
|
|
3052
|
+
this.width = Bun.stringWidth(this.getArrowChar());
|
|
3053
|
+
}
|
|
2956
3054
|
}
|
|
2957
|
-
|
|
2958
|
-
return
|
|
3055
|
+
get direction() {
|
|
3056
|
+
return this._direction;
|
|
2959
3057
|
}
|
|
2960
|
-
|
|
2961
|
-
if (
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
this.refreshFrameBuffer();
|
|
3058
|
+
set direction(value) {
|
|
3059
|
+
if (this._direction !== value) {
|
|
3060
|
+
this._direction = value;
|
|
3061
|
+
this.requestRender();
|
|
2965
3062
|
}
|
|
2966
3063
|
}
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
return;
|
|
2970
|
-
const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
2971
|
-
this.frameBuffer.clear(bgColor);
|
|
2972
|
-
const contentX = 0;
|
|
2973
|
-
const contentY = 0;
|
|
2974
|
-
const contentWidth = this.width;
|
|
2975
|
-
const contentHeight = this.height;
|
|
2976
|
-
const visibleOptions = this._options.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleTabs);
|
|
2977
|
-
for (let i = 0;i < visibleOptions.length; i++) {
|
|
2978
|
-
const actualIndex = this.scrollOffset + i;
|
|
2979
|
-
const option = visibleOptions[i];
|
|
2980
|
-
const isSelected = actualIndex === this.selectedIndex;
|
|
2981
|
-
const tabX = contentX + i * this._tabWidth;
|
|
2982
|
-
if (tabX >= contentX + contentWidth)
|
|
2983
|
-
break;
|
|
2984
|
-
const actualTabWidth = Math.min(this._tabWidth, contentWidth - i * this._tabWidth);
|
|
2985
|
-
if (isSelected) {
|
|
2986
|
-
this.frameBuffer.fillRect(tabX, contentY, actualTabWidth, 1, this._selectedBackgroundColor);
|
|
2987
|
-
}
|
|
2988
|
-
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
2989
|
-
const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
|
|
2990
|
-
const nameContent = this.truncateText(option.name, actualTabWidth - 2);
|
|
2991
|
-
this.frameBuffer.drawText(nameContent, tabX + 1, contentY, nameColor);
|
|
2992
|
-
if (isSelected && this._showUnderline && contentHeight >= 2) {
|
|
2993
|
-
const underlineY = contentY + 1;
|
|
2994
|
-
const underlineBg = isSelected ? this._selectedBackgroundColor : bgColor;
|
|
2995
|
-
this.frameBuffer.drawText("\u25AC".repeat(actualTabWidth), tabX, underlineY, nameColor, underlineBg);
|
|
2996
|
-
}
|
|
2997
|
-
}
|
|
2998
|
-
if (this._showDescription && contentHeight >= (this._showUnderline ? 3 : 2)) {
|
|
2999
|
-
const selectedOption = this.getSelectedOption();
|
|
3000
|
-
if (selectedOption) {
|
|
3001
|
-
const descriptionY = contentY + (this._showUnderline ? 2 : 1);
|
|
3002
|
-
const descColor = this._selectedDescriptionColor;
|
|
3003
|
-
const descContent = this.truncateText(selectedOption.description, contentWidth - 2);
|
|
3004
|
-
this.frameBuffer.drawText(descContent, contentX + 1, descriptionY, descColor);
|
|
3005
|
-
}
|
|
3006
|
-
}
|
|
3007
|
-
if (this._showScrollArrows && this._options.length > this.maxVisibleTabs) {
|
|
3008
|
-
this.renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
|
|
3009
|
-
}
|
|
3010
|
-
}
|
|
3011
|
-
truncateText(text, maxWidth) {
|
|
3012
|
-
if (text.length <= maxWidth)
|
|
3013
|
-
return text;
|
|
3014
|
-
return text.substring(0, Math.max(0, maxWidth - 1)) + "\u2026";
|
|
3015
|
-
}
|
|
3016
|
-
renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
|
|
3017
|
-
if (!this.frameBuffer)
|
|
3018
|
-
return;
|
|
3019
|
-
const hasMoreLeft = this.scrollOffset > 0;
|
|
3020
|
-
const hasMoreRight = this.scrollOffset + this.maxVisibleTabs < this._options.length;
|
|
3021
|
-
if (hasMoreLeft) {
|
|
3022
|
-
this.frameBuffer.drawText("\u2039", contentX, contentY, parseColor("#AAAAAA"));
|
|
3023
|
-
}
|
|
3024
|
-
if (hasMoreRight) {
|
|
3025
|
-
this.frameBuffer.drawText("\u203A", contentX + contentWidth - 1, contentY, parseColor("#AAAAAA"));
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
setOptions(options) {
|
|
3029
|
-
this._options = options;
|
|
3030
|
-
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
|
|
3031
|
-
this.updateScrollOffset();
|
|
3032
|
-
this.requestRender();
|
|
3033
|
-
}
|
|
3034
|
-
getSelectedOption() {
|
|
3035
|
-
return this._options[this.selectedIndex] || null;
|
|
3036
|
-
}
|
|
3037
|
-
getSelectedIndex() {
|
|
3038
|
-
return this.selectedIndex;
|
|
3039
|
-
}
|
|
3040
|
-
moveLeft() {
|
|
3041
|
-
if (this.selectedIndex > 0) {
|
|
3042
|
-
this.selectedIndex--;
|
|
3043
|
-
} else if (this._wrapSelection && this._options.length > 0) {
|
|
3044
|
-
this.selectedIndex = this._options.length - 1;
|
|
3045
|
-
} else {
|
|
3046
|
-
return;
|
|
3047
|
-
}
|
|
3048
|
-
this.updateScrollOffset();
|
|
3049
|
-
this.requestRender();
|
|
3050
|
-
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3064
|
+
get foregroundColor() {
|
|
3065
|
+
return this._foregroundColor;
|
|
3051
3066
|
}
|
|
3052
|
-
|
|
3053
|
-
if (this.
|
|
3054
|
-
this.
|
|
3055
|
-
|
|
3056
|
-
this.selectedIndex = 0;
|
|
3057
|
-
} else {
|
|
3058
|
-
return;
|
|
3067
|
+
set foregroundColor(value) {
|
|
3068
|
+
if (this._foregroundColor !== value) {
|
|
3069
|
+
this._foregroundColor = parseColor(value);
|
|
3070
|
+
this.requestRender();
|
|
3059
3071
|
}
|
|
3060
|
-
this.updateScrollOffset();
|
|
3061
|
-
this.requestRender();
|
|
3062
|
-
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3063
3072
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
if (selected) {
|
|
3067
|
-
this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
|
|
3068
|
-
}
|
|
3073
|
+
get backgroundColor() {
|
|
3074
|
+
return this._backgroundColor;
|
|
3069
3075
|
}
|
|
3070
|
-
|
|
3071
|
-
if (
|
|
3072
|
-
this.
|
|
3073
|
-
this.updateScrollOffset();
|
|
3076
|
+
set backgroundColor(value) {
|
|
3077
|
+
if (this._backgroundColor !== value) {
|
|
3078
|
+
this._backgroundColor = parseColor(value);
|
|
3074
3079
|
this.requestRender();
|
|
3075
|
-
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3076
3080
|
}
|
|
3077
3081
|
}
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3082
|
+
get attributes() {
|
|
3083
|
+
return this._attributes;
|
|
3084
|
+
}
|
|
3085
|
+
set attributes(value) {
|
|
3086
|
+
if (this._attributes !== value) {
|
|
3087
|
+
this._attributes = value;
|
|
3083
3088
|
this.requestRender();
|
|
3084
3089
|
}
|
|
3085
3090
|
}
|
|
3086
|
-
|
|
3087
|
-
this.
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
setTabWidth(tabWidth) {
|
|
3092
|
-
if (this._tabWidth === tabWidth)
|
|
3093
|
-
return;
|
|
3094
|
-
this._tabWidth = tabWidth;
|
|
3095
|
-
this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
|
|
3096
|
-
this.updateScrollOffset();
|
|
3091
|
+
set arrowChars(value) {
|
|
3092
|
+
this._arrowChars = {
|
|
3093
|
+
...this._arrowChars,
|
|
3094
|
+
...value
|
|
3095
|
+
};
|
|
3097
3096
|
this.requestRender();
|
|
3098
3097
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3098
|
+
renderSelf(buffer) {
|
|
3099
|
+
const char = this.getArrowChar();
|
|
3100
|
+
buffer.drawText(char, this.x, this.y, this._foregroundColor, this._backgroundColor, this._attributes);
|
|
3101
3101
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3102
|
+
getArrowChar() {
|
|
3103
|
+
switch (this._direction) {
|
|
3104
|
+
case "up":
|
|
3105
|
+
return this._arrowChars.up;
|
|
3106
|
+
case "down":
|
|
3107
|
+
return this._arrowChars.down;
|
|
3105
3108
|
case "left":
|
|
3106
|
-
|
|
3107
|
-
this.moveLeft();
|
|
3108
|
-
return true;
|
|
3109
|
+
return this._arrowChars.left;
|
|
3109
3110
|
case "right":
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
return
|
|
3113
|
-
case "return":
|
|
3114
|
-
case "enter":
|
|
3115
|
-
this.selectCurrent();
|
|
3116
|
-
return true;
|
|
3111
|
+
return this._arrowChars.right;
|
|
3112
|
+
default:
|
|
3113
|
+
return "?";
|
|
3117
3114
|
}
|
|
3118
|
-
return false;
|
|
3119
|
-
}
|
|
3120
|
-
get options() {
|
|
3121
|
-
return this._options;
|
|
3122
3115
|
}
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3116
|
+
}
|
|
3117
|
+
// src/renderables/ScrollBox.ts
|
|
3118
|
+
class ContentRenderable extends BoxRenderable {
|
|
3119
|
+
viewport;
|
|
3120
|
+
constructor(ctx, viewport, options) {
|
|
3121
|
+
super(ctx, options);
|
|
3122
|
+
this.viewport = viewport;
|
|
3128
3123
|
}
|
|
3129
|
-
|
|
3130
|
-
this.
|
|
3131
|
-
this.requestRender();
|
|
3124
|
+
_getChildren() {
|
|
3125
|
+
return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis);
|
|
3132
3126
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
class ScrollBoxRenderable extends BoxRenderable {
|
|
3130
|
+
static idCounter = 0;
|
|
3131
|
+
internalId = 0;
|
|
3132
|
+
wrapper;
|
|
3133
|
+
viewport;
|
|
3134
|
+
content;
|
|
3135
|
+
horizontalScrollBar;
|
|
3136
|
+
verticalScrollBar;
|
|
3137
|
+
_focusable = true;
|
|
3138
|
+
selectionListener;
|
|
3139
|
+
autoScrollMouseX = 0;
|
|
3140
|
+
autoScrollMouseY = 0;
|
|
3141
|
+
autoScrollThresholdVertical = 3;
|
|
3142
|
+
autoScrollThresholdHorizontal = 3;
|
|
3143
|
+
autoScrollSpeedSlow = 6;
|
|
3144
|
+
autoScrollSpeedMedium = 36;
|
|
3145
|
+
autoScrollSpeedFast = 72;
|
|
3146
|
+
isAutoScrolling = false;
|
|
3147
|
+
cachedAutoScrollSpeed = 3;
|
|
3148
|
+
autoScrollAccumulatorX = 0;
|
|
3149
|
+
autoScrollAccumulatorY = 0;
|
|
3150
|
+
_stickyScroll;
|
|
3151
|
+
_stickyScrollTop = false;
|
|
3152
|
+
_stickyScrollBottom = false;
|
|
3153
|
+
_stickyScrollLeft = false;
|
|
3154
|
+
_stickyScrollRight = false;
|
|
3155
|
+
_stickyStart;
|
|
3156
|
+
_hasManualScroll = false;
|
|
3157
|
+
scrollAccel;
|
|
3158
|
+
get stickyScroll() {
|
|
3159
|
+
return this._stickyScroll;
|
|
3136
3160
|
}
|
|
3137
|
-
set
|
|
3138
|
-
this.
|
|
3139
|
-
this.
|
|
3161
|
+
set stickyScroll(value) {
|
|
3162
|
+
this._stickyScroll = value;
|
|
3163
|
+
this.updateStickyState();
|
|
3140
3164
|
}
|
|
3141
|
-
|
|
3142
|
-
this.
|
|
3143
|
-
this.requestRender();
|
|
3165
|
+
get stickyStart() {
|
|
3166
|
+
return this._stickyStart;
|
|
3144
3167
|
}
|
|
3145
|
-
set
|
|
3146
|
-
this.
|
|
3147
|
-
this.
|
|
3168
|
+
set stickyStart(value) {
|
|
3169
|
+
this._stickyStart = value;
|
|
3170
|
+
this.updateStickyState();
|
|
3148
3171
|
}
|
|
3149
|
-
|
|
3150
|
-
this.
|
|
3151
|
-
this.requestRender();
|
|
3172
|
+
get scrollTop() {
|
|
3173
|
+
return this.verticalScrollBar.scrollPosition;
|
|
3152
3174
|
}
|
|
3153
|
-
set
|
|
3154
|
-
this.
|
|
3155
|
-
this.
|
|
3175
|
+
set scrollTop(value) {
|
|
3176
|
+
this.verticalScrollBar.scrollPosition = value;
|
|
3177
|
+
this._hasManualScroll = true;
|
|
3178
|
+
this.updateStickyState();
|
|
3156
3179
|
}
|
|
3157
|
-
get
|
|
3158
|
-
return this.
|
|
3180
|
+
get scrollLeft() {
|
|
3181
|
+
return this.horizontalScrollBar.scrollPosition;
|
|
3159
3182
|
}
|
|
3160
|
-
set
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
this.height = newHeight;
|
|
3165
|
-
this.requestRender();
|
|
3166
|
-
}
|
|
3183
|
+
set scrollLeft(value) {
|
|
3184
|
+
this.horizontalScrollBar.scrollPosition = value;
|
|
3185
|
+
this._hasManualScroll = true;
|
|
3186
|
+
this.updateStickyState();
|
|
3167
3187
|
}
|
|
3168
|
-
get
|
|
3169
|
-
return this.
|
|
3188
|
+
get scrollWidth() {
|
|
3189
|
+
return this.horizontalScrollBar.scrollSize;
|
|
3170
3190
|
}
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
this._showUnderline = show;
|
|
3174
|
-
const newHeight = this.calculateDynamicHeight();
|
|
3175
|
-
this.height = newHeight;
|
|
3176
|
-
this.requestRender();
|
|
3177
|
-
}
|
|
3191
|
+
get scrollHeight() {
|
|
3192
|
+
return this.verticalScrollBar.scrollSize;
|
|
3178
3193
|
}
|
|
3179
|
-
|
|
3180
|
-
|
|
3194
|
+
updateStickyState() {
|
|
3195
|
+
if (!this._stickyScroll)
|
|
3196
|
+
return;
|
|
3197
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3198
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3199
|
+
if (this.scrollTop <= 0) {
|
|
3200
|
+
this._stickyScrollTop = true;
|
|
3201
|
+
this._stickyScrollBottom = false;
|
|
3202
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
3203
|
+
this._stickyScrollTop = false;
|
|
3204
|
+
this._stickyScrollBottom = true;
|
|
3205
|
+
} else {
|
|
3206
|
+
this._stickyScrollTop = false;
|
|
3207
|
+
this._stickyScrollBottom = false;
|
|
3208
|
+
}
|
|
3209
|
+
if (this.scrollLeft <= 0) {
|
|
3210
|
+
this._stickyScrollLeft = true;
|
|
3211
|
+
this._stickyScrollRight = false;
|
|
3212
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
3213
|
+
this._stickyScrollLeft = false;
|
|
3214
|
+
this._stickyScrollRight = true;
|
|
3215
|
+
} else {
|
|
3216
|
+
this._stickyScrollLeft = false;
|
|
3217
|
+
this._stickyScrollRight = false;
|
|
3218
|
+
}
|
|
3181
3219
|
}
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3220
|
+
applyStickyStart(stickyStart) {
|
|
3221
|
+
switch (stickyStart) {
|
|
3222
|
+
case "top":
|
|
3223
|
+
this._stickyScrollTop = true;
|
|
3224
|
+
this._stickyScrollBottom = false;
|
|
3225
|
+
this.verticalScrollBar.scrollPosition = 0;
|
|
3226
|
+
break;
|
|
3227
|
+
case "bottom":
|
|
3228
|
+
this._stickyScrollTop = false;
|
|
3229
|
+
this._stickyScrollBottom = true;
|
|
3230
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3231
|
+
break;
|
|
3232
|
+
case "left":
|
|
3233
|
+
this._stickyScrollLeft = true;
|
|
3234
|
+
this._stickyScrollRight = false;
|
|
3235
|
+
this.horizontalScrollBar.scrollPosition = 0;
|
|
3236
|
+
break;
|
|
3237
|
+
case "right":
|
|
3238
|
+
this._stickyScrollLeft = false;
|
|
3239
|
+
this._stickyScrollRight = true;
|
|
3240
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3241
|
+
break;
|
|
3186
3242
|
}
|
|
3187
3243
|
}
|
|
3188
|
-
|
|
3189
|
-
|
|
3244
|
+
constructor(ctx, {
|
|
3245
|
+
wrapperOptions,
|
|
3246
|
+
viewportOptions,
|
|
3247
|
+
contentOptions,
|
|
3248
|
+
rootOptions,
|
|
3249
|
+
scrollbarOptions,
|
|
3250
|
+
verticalScrollbarOptions,
|
|
3251
|
+
horizontalScrollbarOptions,
|
|
3252
|
+
stickyScroll = false,
|
|
3253
|
+
stickyStart,
|
|
3254
|
+
scrollX = false,
|
|
3255
|
+
scrollY = true,
|
|
3256
|
+
scrollAcceleration,
|
|
3257
|
+
...options
|
|
3258
|
+
}) {
|
|
3259
|
+
super(ctx, {
|
|
3260
|
+
flexDirection: "row",
|
|
3261
|
+
alignItems: "stretch",
|
|
3262
|
+
...options,
|
|
3263
|
+
...rootOptions
|
|
3264
|
+
});
|
|
3265
|
+
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
3266
|
+
this._stickyScroll = stickyScroll;
|
|
3267
|
+
this._stickyStart = stickyStart;
|
|
3268
|
+
if (scrollAcceleration) {
|
|
3269
|
+
this.scrollAccel = scrollAcceleration;
|
|
3270
|
+
} else if (process.platform === "darwin") {
|
|
3271
|
+
this.scrollAccel = new MacOSScrollAccel;
|
|
3272
|
+
}
|
|
3273
|
+
this.scrollAccel ??= new LinearScrollAccel;
|
|
3274
|
+
this.wrapper = new BoxRenderable(ctx, {
|
|
3275
|
+
flexDirection: "column",
|
|
3276
|
+
flexGrow: 1,
|
|
3277
|
+
...wrapperOptions,
|
|
3278
|
+
id: `scroll-box-wrapper-${this.internalId}`
|
|
3279
|
+
});
|
|
3280
|
+
super.add(this.wrapper);
|
|
3281
|
+
this.viewport = new BoxRenderable(ctx, {
|
|
3282
|
+
flexDirection: "column",
|
|
3283
|
+
flexGrow: 1,
|
|
3284
|
+
overflow: "hidden",
|
|
3285
|
+
onSizeChange: () => {
|
|
3286
|
+
this.recalculateBarProps();
|
|
3287
|
+
},
|
|
3288
|
+
...viewportOptions,
|
|
3289
|
+
id: `scroll-box-viewport-${this.internalId}`
|
|
3290
|
+
});
|
|
3291
|
+
this.wrapper.add(this.viewport);
|
|
3292
|
+
this.content = new ContentRenderable(ctx, this.viewport, {
|
|
3293
|
+
alignSelf: "flex-start",
|
|
3294
|
+
flexShrink: 0,
|
|
3295
|
+
...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
|
|
3296
|
+
...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
|
|
3297
|
+
onSizeChange: () => {
|
|
3298
|
+
this.recalculateBarProps();
|
|
3299
|
+
},
|
|
3300
|
+
...contentOptions,
|
|
3301
|
+
id: `scroll-box-content-${this.internalId}`
|
|
3302
|
+
});
|
|
3303
|
+
this.viewport.add(this.content);
|
|
3304
|
+
this.verticalScrollBar = new ScrollBarRenderable(ctx, {
|
|
3305
|
+
...scrollbarOptions,
|
|
3306
|
+
...verticalScrollbarOptions,
|
|
3307
|
+
arrowOptions: {
|
|
3308
|
+
...scrollbarOptions?.arrowOptions,
|
|
3309
|
+
...verticalScrollbarOptions?.arrowOptions
|
|
3310
|
+
},
|
|
3311
|
+
id: `scroll-box-vertical-scrollbar-${this.internalId}`,
|
|
3312
|
+
orientation: "vertical",
|
|
3313
|
+
onChange: (position) => {
|
|
3314
|
+
this.content.translateY = -position;
|
|
3315
|
+
this._hasManualScroll = true;
|
|
3316
|
+
this.updateStickyState();
|
|
3317
|
+
}
|
|
3318
|
+
});
|
|
3319
|
+
super.add(this.verticalScrollBar);
|
|
3320
|
+
this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
|
|
3321
|
+
...scrollbarOptions,
|
|
3322
|
+
...horizontalScrollbarOptions,
|
|
3323
|
+
arrowOptions: {
|
|
3324
|
+
...scrollbarOptions?.arrowOptions,
|
|
3325
|
+
...horizontalScrollbarOptions?.arrowOptions
|
|
3326
|
+
},
|
|
3327
|
+
id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
|
|
3328
|
+
orientation: "horizontal",
|
|
3329
|
+
onChange: (position) => {
|
|
3330
|
+
this.content.translateX = -position;
|
|
3331
|
+
this._hasManualScroll = true;
|
|
3332
|
+
this.updateStickyState();
|
|
3333
|
+
}
|
|
3334
|
+
});
|
|
3335
|
+
this.wrapper.add(this.horizontalScrollBar);
|
|
3336
|
+
this.recalculateBarProps();
|
|
3337
|
+
if (stickyStart && stickyScroll) {
|
|
3338
|
+
this.applyStickyStart(stickyStart);
|
|
3339
|
+
}
|
|
3340
|
+
this.selectionListener = () => {
|
|
3341
|
+
const selection = this._ctx.getSelection();
|
|
3342
|
+
if (!selection || !selection.isSelecting) {
|
|
3343
|
+
this.stopAutoScroll();
|
|
3344
|
+
}
|
|
3345
|
+
};
|
|
3346
|
+
this._ctx.on("selection", this.selectionListener);
|
|
3190
3347
|
}
|
|
3191
|
-
|
|
3192
|
-
this.
|
|
3348
|
+
onUpdate(deltaTime) {
|
|
3349
|
+
this.handleAutoScroll(deltaTime);
|
|
3193
3350
|
}
|
|
3194
|
-
|
|
3195
|
-
|
|
3351
|
+
scrollBy(delta, unit = "absolute") {
|
|
3352
|
+
if (typeof delta === "number") {
|
|
3353
|
+
this.verticalScrollBar.scrollBy(delta, unit);
|
|
3354
|
+
} else {
|
|
3355
|
+
this.verticalScrollBar.scrollBy(delta.y, unit);
|
|
3356
|
+
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
3357
|
+
}
|
|
3358
|
+
this._hasManualScroll = true;
|
|
3196
3359
|
}
|
|
3197
|
-
|
|
3198
|
-
if (
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3360
|
+
scrollTo(position) {
|
|
3361
|
+
if (typeof position === "number") {
|
|
3362
|
+
this.scrollTop = position;
|
|
3363
|
+
} else {
|
|
3364
|
+
this.scrollTop = position.y;
|
|
3365
|
+
this.scrollLeft = position.x;
|
|
3366
|
+
}
|
|
3204
3367
|
}
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
var defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
|
|
3208
|
-
var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
3209
|
-
|
|
3210
|
-
class SliderRenderable extends Renderable {
|
|
3211
|
-
orientation;
|
|
3212
|
-
_value;
|
|
3213
|
-
_min;
|
|
3214
|
-
_max;
|
|
3215
|
-
_viewPortSize;
|
|
3216
|
-
_backgroundColor;
|
|
3217
|
-
_foregroundColor;
|
|
3218
|
-
_onChange;
|
|
3219
|
-
constructor(ctx, options) {
|
|
3220
|
-
super(ctx, { flexShrink: 0, ...options });
|
|
3221
|
-
this.orientation = options.orientation;
|
|
3222
|
-
this._min = options.min ?? 0;
|
|
3223
|
-
this._max = options.max ?? 100;
|
|
3224
|
-
this._value = options.value ?? this._min;
|
|
3225
|
-
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
3226
|
-
this._onChange = options.onChange;
|
|
3227
|
-
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
3228
|
-
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
3229
|
-
this.setupMouseHandling();
|
|
3368
|
+
add(obj, index) {
|
|
3369
|
+
return this.content.add(obj, index);
|
|
3230
3370
|
}
|
|
3231
|
-
|
|
3232
|
-
return this.
|
|
3371
|
+
insertBefore(obj, anchor) {
|
|
3372
|
+
return this.content.insertBefore(obj, anchor);
|
|
3233
3373
|
}
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
if (clamped !== this._value) {
|
|
3237
|
-
this._value = clamped;
|
|
3238
|
-
this._onChange?.(clamped);
|
|
3239
|
-
this.emit("change", { value: clamped });
|
|
3240
|
-
this.requestRender();
|
|
3241
|
-
}
|
|
3374
|
+
remove(id) {
|
|
3375
|
+
this.content.remove(id);
|
|
3242
3376
|
}
|
|
3243
|
-
|
|
3244
|
-
return this.
|
|
3377
|
+
getChildren() {
|
|
3378
|
+
return this.content.getChildren();
|
|
3245
3379
|
}
|
|
3246
|
-
|
|
3247
|
-
if (
|
|
3248
|
-
|
|
3249
|
-
if (
|
|
3250
|
-
|
|
3380
|
+
onMouseEvent(event) {
|
|
3381
|
+
if (event.type === "scroll") {
|
|
3382
|
+
let dir = event.scroll?.direction;
|
|
3383
|
+
if (event.modifiers.shift)
|
|
3384
|
+
dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up";
|
|
3385
|
+
const baseDelta = event.scroll?.delta ?? 0;
|
|
3386
|
+
const now = Date.now();
|
|
3387
|
+
const multiplier = this.scrollAccel.tick(now);
|
|
3388
|
+
if (dir === "up") {
|
|
3389
|
+
this.scrollTop -= baseDelta * multiplier;
|
|
3390
|
+
} else if (dir === "down") {
|
|
3391
|
+
this.scrollTop += baseDelta * multiplier;
|
|
3392
|
+
} else if (dir === "left") {
|
|
3393
|
+
this.scrollLeft -= baseDelta * multiplier;
|
|
3394
|
+
} else if (dir === "right") {
|
|
3395
|
+
this.scrollLeft += baseDelta * multiplier;
|
|
3251
3396
|
}
|
|
3252
|
-
this.
|
|
3397
|
+
this._hasManualScroll = true;
|
|
3253
3398
|
}
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
set max(newMax) {
|
|
3259
|
-
if (newMax !== this._max) {
|
|
3260
|
-
this._max = newMax;
|
|
3261
|
-
if (this._value > newMax) {
|
|
3262
|
-
this.value = newMax;
|
|
3263
|
-
}
|
|
3264
|
-
this.requestRender();
|
|
3399
|
+
if (event.type === "drag" && event.isSelecting) {
|
|
3400
|
+
this.updateAutoScroll(event.x, event.y);
|
|
3401
|
+
} else if (event.type === "up") {
|
|
3402
|
+
this.stopAutoScroll();
|
|
3265
3403
|
}
|
|
3266
3404
|
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
this.
|
|
3271
|
-
|
|
3405
|
+
handleKeyPress(key) {
|
|
3406
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
3407
|
+
this._hasManualScroll = true;
|
|
3408
|
+
this.scrollAccel.reset();
|
|
3409
|
+
return true;
|
|
3272
3410
|
}
|
|
3411
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
3412
|
+
this._hasManualScroll = true;
|
|
3413
|
+
this.scrollAccel.reset();
|
|
3414
|
+
return true;
|
|
3415
|
+
}
|
|
3416
|
+
return false;
|
|
3273
3417
|
}
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3418
|
+
startAutoScroll(mouseX, mouseY) {
|
|
3419
|
+
this.stopAutoScroll();
|
|
3420
|
+
this.autoScrollMouseX = mouseX;
|
|
3421
|
+
this.autoScrollMouseY = mouseY;
|
|
3422
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
|
|
3423
|
+
this.isAutoScrolling = true;
|
|
3424
|
+
if (!this.live) {
|
|
3425
|
+
this.live = true;
|
|
3426
|
+
}
|
|
3283
3427
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3428
|
+
updateAutoScroll(mouseX, mouseY) {
|
|
3429
|
+
this.autoScrollMouseX = mouseX;
|
|
3430
|
+
this.autoScrollMouseY = mouseY;
|
|
3431
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
|
|
3432
|
+
const scrollX = this.getAutoScrollDirectionX(mouseX);
|
|
3433
|
+
const scrollY = this.getAutoScrollDirectionY(mouseY);
|
|
3434
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
3435
|
+
this.stopAutoScroll();
|
|
3436
|
+
} else if (!this.isAutoScrolling) {
|
|
3437
|
+
this.startAutoScroll(mouseX, mouseY);
|
|
3438
|
+
}
|
|
3286
3439
|
}
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
this.
|
|
3440
|
+
stopAutoScroll() {
|
|
3441
|
+
const wasAutoScrolling = this.isAutoScrolling;
|
|
3442
|
+
this.isAutoScrolling = false;
|
|
3443
|
+
this.autoScrollAccumulatorX = 0;
|
|
3444
|
+
this.autoScrollAccumulatorY = 0;
|
|
3445
|
+
if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
|
|
3446
|
+
this.live = false;
|
|
3447
|
+
}
|
|
3290
3448
|
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
3294
|
-
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
3295
|
-
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3296
|
-
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3297
|
-
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
3449
|
+
hasOtherLiveReasons() {
|
|
3450
|
+
return false;
|
|
3298
3451
|
}
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
this.
|
|
3312
|
-
|
|
3313
|
-
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
3452
|
+
handleAutoScroll(deltaTime) {
|
|
3453
|
+
if (!this.isAutoScrolling)
|
|
3454
|
+
return;
|
|
3455
|
+
const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
|
|
3456
|
+
const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
|
|
3457
|
+
const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
|
|
3458
|
+
let scrolled = false;
|
|
3459
|
+
if (scrollX !== 0) {
|
|
3460
|
+
this.autoScrollAccumulatorX += scrollX * scrollAmount;
|
|
3461
|
+
const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
|
|
3462
|
+
if (integerScrollX !== 0) {
|
|
3463
|
+
this.scrollLeft += integerScrollX;
|
|
3464
|
+
this.autoScrollAccumulatorX -= integerScrollX;
|
|
3465
|
+
scrolled = true;
|
|
3314
3466
|
}
|
|
3315
|
-
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
if (isDragging) {
|
|
3324
|
-
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
3467
|
+
}
|
|
3468
|
+
if (scrollY !== 0) {
|
|
3469
|
+
this.autoScrollAccumulatorY += scrollY * scrollAmount;
|
|
3470
|
+
const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
|
|
3471
|
+
if (integerScrollY !== 0) {
|
|
3472
|
+
this.scrollTop += integerScrollY;
|
|
3473
|
+
this.autoScrollAccumulatorY -= integerScrollY;
|
|
3474
|
+
scrolled = true;
|
|
3325
3475
|
}
|
|
3326
|
-
isDragging = false;
|
|
3327
|
-
};
|
|
3328
|
-
}
|
|
3329
|
-
updateValueFromMouseDirect(event) {
|
|
3330
|
-
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3331
|
-
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
3332
|
-
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3333
|
-
const relativeMousePos = mousePos - trackStart;
|
|
3334
|
-
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3335
|
-
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
3336
|
-
const range = this._max - this._min;
|
|
3337
|
-
const newValue = this._min + ratio * range;
|
|
3338
|
-
this.value = newValue;
|
|
3339
|
-
}
|
|
3340
|
-
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
3341
|
-
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
3342
|
-
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
3343
|
-
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
3344
|
-
const virtualTrackSize = trackSize * 2;
|
|
3345
|
-
const relativeMousePos = mousePos - trackStart;
|
|
3346
|
-
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
3347
|
-
const virtualMousePos = clampedMousePos * 2;
|
|
3348
|
-
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3349
|
-
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
3350
|
-
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
3351
|
-
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
3352
|
-
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
3353
|
-
const range = this._max - this._min;
|
|
3354
|
-
const newValue = this._min + ratio * range;
|
|
3355
|
-
this.value = newValue;
|
|
3356
|
-
}
|
|
3357
|
-
getThumbRect() {
|
|
3358
|
-
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3359
|
-
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3360
|
-
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
3361
|
-
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
3362
|
-
if (this.orientation === "vertical") {
|
|
3363
|
-
return {
|
|
3364
|
-
x: this.x,
|
|
3365
|
-
y: this.y + realThumbStart,
|
|
3366
|
-
width: this.width,
|
|
3367
|
-
height: Math.max(1, realThumbSize)
|
|
3368
|
-
};
|
|
3369
|
-
} else {
|
|
3370
|
-
return {
|
|
3371
|
-
x: this.x + realThumbStart,
|
|
3372
|
-
y: this.y,
|
|
3373
|
-
width: Math.max(1, realThumbSize),
|
|
3374
|
-
height: this.height
|
|
3375
|
-
};
|
|
3376
3476
|
}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
if (this.orientation === "horizontal") {
|
|
3380
|
-
this.renderHorizontal(buffer);
|
|
3381
|
-
} else {
|
|
3382
|
-
this.renderVertical(buffer);
|
|
3477
|
+
if (scrolled) {
|
|
3478
|
+
this._ctx.requestSelectionUpdate();
|
|
3383
3479
|
}
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3387
|
-
const virtualThumbStart = this.getVirtualThumbStart();
|
|
3388
|
-
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
3389
|
-
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
3390
|
-
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
3391
|
-
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
3392
|
-
const startX = Math.max(0, realStartCell);
|
|
3393
|
-
const endX = Math.min(this.width - 1, realEndCell);
|
|
3394
|
-
for (let realX = startX;realX <= endX; realX++) {
|
|
3395
|
-
const virtualCellStart = realX * 2;
|
|
3396
|
-
const virtualCellEnd = virtualCellStart + 2;
|
|
3397
|
-
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3398
|
-
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3399
|
-
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3400
|
-
let char = " ";
|
|
3401
|
-
if (coverage >= 2) {
|
|
3402
|
-
char = "\u2588";
|
|
3403
|
-
} else {
|
|
3404
|
-
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
3405
|
-
if (isLeftHalf) {
|
|
3406
|
-
char = "\u258C";
|
|
3407
|
-
} else {
|
|
3408
|
-
char = "\u2590";
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
for (let y = 0;y < this.height; y++) {
|
|
3412
|
-
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
3413
|
-
}
|
|
3480
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
3481
|
+
this.stopAutoScroll();
|
|
3414
3482
|
}
|
|
3415
3483
|
}
|
|
3416
|
-
|
|
3417
|
-
const
|
|
3418
|
-
const
|
|
3419
|
-
const
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
for (let realY = startY;realY <= endY; realY++) {
|
|
3426
|
-
const virtualCellStart = realY * 2;
|
|
3427
|
-
const virtualCellEnd = virtualCellStart + 2;
|
|
3428
|
-
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
3429
|
-
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
3430
|
-
const coverage = thumbEndInCell - thumbStartInCell;
|
|
3431
|
-
let char = " ";
|
|
3432
|
-
if (coverage >= 2) {
|
|
3433
|
-
char = "\u2588";
|
|
3434
|
-
} else if (coverage > 0) {
|
|
3435
|
-
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
3436
|
-
if (virtualPositionInCell === 0) {
|
|
3437
|
-
char = "\u2580";
|
|
3438
|
-
} else {
|
|
3439
|
-
char = "\u2584";
|
|
3440
|
-
}
|
|
3441
|
-
}
|
|
3442
|
-
for (let x = 0;x < this.width; x++) {
|
|
3443
|
-
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
3444
|
-
}
|
|
3484
|
+
getAutoScrollDirectionX(mouseX) {
|
|
3485
|
+
const relativeX = mouseX - this.x;
|
|
3486
|
+
const distToLeft = relativeX;
|
|
3487
|
+
const distToRight = this.width - relativeX;
|
|
3488
|
+
if (distToLeft <= this.autoScrollThresholdHorizontal) {
|
|
3489
|
+
return this.scrollLeft > 0 ? -1 : 0;
|
|
3490
|
+
} else if (distToRight <= this.autoScrollThresholdHorizontal) {
|
|
3491
|
+
const maxScrollLeft = this.scrollWidth - this.viewport.width;
|
|
3492
|
+
return this.scrollLeft < maxScrollLeft ? 1 : 0;
|
|
3445
3493
|
}
|
|
3494
|
+
return 0;
|
|
3446
3495
|
}
|
|
3447
|
-
|
|
3448
|
-
const
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
return
|
|
3456
|
-
const thumbRatio = viewportSize / contentSize;
|
|
3457
|
-
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
3458
|
-
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
3459
|
-
}
|
|
3460
|
-
getVirtualThumbStart() {
|
|
3461
|
-
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
3462
|
-
const range = this._max - this._min;
|
|
3463
|
-
if (range === 0)
|
|
3464
|
-
return 0;
|
|
3465
|
-
const valueRatio = (this._value - this._min) / range;
|
|
3466
|
-
const virtualThumbSize = this.getVirtualThumbSize();
|
|
3467
|
-
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
|
-
// src/renderables/ScrollBar.ts
|
|
3472
|
-
class ScrollBarRenderable extends Renderable {
|
|
3473
|
-
slider;
|
|
3474
|
-
startArrow;
|
|
3475
|
-
endArrow;
|
|
3476
|
-
orientation;
|
|
3477
|
-
_focusable = true;
|
|
3478
|
-
_scrollSize = 0;
|
|
3479
|
-
_scrollPosition = 0;
|
|
3480
|
-
_viewportSize = 0;
|
|
3481
|
-
_showArrows = false;
|
|
3482
|
-
_manualVisibility = false;
|
|
3483
|
-
_onChange;
|
|
3484
|
-
scrollStep = null;
|
|
3485
|
-
get visible() {
|
|
3486
|
-
return super.visible;
|
|
3487
|
-
}
|
|
3488
|
-
set visible(value) {
|
|
3489
|
-
this._manualVisibility = true;
|
|
3490
|
-
super.visible = value;
|
|
3491
|
-
}
|
|
3492
|
-
resetVisibilityControl() {
|
|
3493
|
-
this._manualVisibility = false;
|
|
3494
|
-
this.recalculateVisibility();
|
|
3495
|
-
}
|
|
3496
|
-
get scrollSize() {
|
|
3497
|
-
return this._scrollSize;
|
|
3498
|
-
}
|
|
3499
|
-
get scrollPosition() {
|
|
3500
|
-
return this._scrollPosition;
|
|
3501
|
-
}
|
|
3502
|
-
get viewportSize() {
|
|
3503
|
-
return this._viewportSize;
|
|
3504
|
-
}
|
|
3505
|
-
set scrollSize(value) {
|
|
3506
|
-
if (value === this.scrollSize)
|
|
3507
|
-
return;
|
|
3508
|
-
this._scrollSize = value;
|
|
3509
|
-
this.recalculateVisibility();
|
|
3510
|
-
this.updateSliderFromScrollState();
|
|
3511
|
-
this.scrollPosition = this.scrollPosition;
|
|
3512
|
-
}
|
|
3513
|
-
set scrollPosition(value) {
|
|
3514
|
-
const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
|
|
3515
|
-
if (newPosition !== this._scrollPosition) {
|
|
3516
|
-
this._scrollPosition = newPosition;
|
|
3517
|
-
this.updateSliderFromScrollState();
|
|
3496
|
+
getAutoScrollDirectionY(mouseY) {
|
|
3497
|
+
const relativeY = mouseY - this.y;
|
|
3498
|
+
const distToTop = relativeY;
|
|
3499
|
+
const distToBottom = this.height - relativeY;
|
|
3500
|
+
if (distToTop <= this.autoScrollThresholdVertical) {
|
|
3501
|
+
return this.scrollTop > 0 ? -1 : 0;
|
|
3502
|
+
} else if (distToBottom <= this.autoScrollThresholdVertical) {
|
|
3503
|
+
const maxScrollTop = this.scrollHeight - this.viewport.height;
|
|
3504
|
+
return this.scrollTop < maxScrollTop ? 1 : 0;
|
|
3518
3505
|
}
|
|
3506
|
+
return 0;
|
|
3519
3507
|
}
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
this.
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
this._showArrows = value;
|
|
3536
|
-
this.startArrow.visible = value;
|
|
3537
|
-
this.endArrow.visible = value;
|
|
3508
|
+
getAutoScrollSpeed(mouseX, mouseY) {
|
|
3509
|
+
const relativeX = mouseX - this.x;
|
|
3510
|
+
const relativeY = mouseY - this.y;
|
|
3511
|
+
const distToLeft = relativeX;
|
|
3512
|
+
const distToRight = this.width - relativeX;
|
|
3513
|
+
const distToTop = relativeY;
|
|
3514
|
+
const distToBottom = this.height - relativeY;
|
|
3515
|
+
const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
|
|
3516
|
+
if (minDistance <= 1) {
|
|
3517
|
+
return this.autoScrollSpeedFast;
|
|
3518
|
+
} else if (minDistance <= 2) {
|
|
3519
|
+
return this.autoScrollSpeedMedium;
|
|
3520
|
+
} else {
|
|
3521
|
+
return this.autoScrollSpeedSlow;
|
|
3522
|
+
}
|
|
3538
3523
|
}
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
height: "100%",
|
|
3566
|
-
marginLeft: "auto"
|
|
3567
|
-
} : {
|
|
3568
|
-
width: "100%",
|
|
3569
|
-
height: 1,
|
|
3570
|
-
marginTop: "auto"
|
|
3571
|
-
},
|
|
3572
|
-
flexGrow: 1,
|
|
3573
|
-
flexShrink: 1,
|
|
3574
|
-
...trackOptions
|
|
3575
|
-
});
|
|
3576
|
-
this.updateSliderFromScrollState();
|
|
3577
|
-
const arrowOpts = arrowOptions ? {
|
|
3578
|
-
foregroundColor: arrowOptions.backgroundColor,
|
|
3579
|
-
backgroundColor: arrowOptions.backgroundColor,
|
|
3580
|
-
attributes: arrowOptions.attributes,
|
|
3581
|
-
...arrowOptions
|
|
3582
|
-
} : {};
|
|
3583
|
-
this.startArrow = new ArrowRenderable(ctx, {
|
|
3584
|
-
alignSelf: "center",
|
|
3585
|
-
visible: this.showArrows,
|
|
3586
|
-
direction: this.orientation === "vertical" ? "up" : "left",
|
|
3587
|
-
height: this.orientation === "vertical" ? 1 : 1,
|
|
3588
|
-
...arrowOpts
|
|
3589
|
-
});
|
|
3590
|
-
this.endArrow = new ArrowRenderable(ctx, {
|
|
3591
|
-
alignSelf: "center",
|
|
3592
|
-
visible: this.showArrows,
|
|
3593
|
-
direction: this.orientation === "vertical" ? "down" : "right",
|
|
3594
|
-
height: this.orientation === "vertical" ? 1 : 1,
|
|
3595
|
-
...arrowOpts
|
|
3596
|
-
});
|
|
3597
|
-
this.add(this.startArrow);
|
|
3598
|
-
this.add(this.slider);
|
|
3599
|
-
this.add(this.endArrow);
|
|
3600
|
-
let startArrowMouseTimeout = undefined;
|
|
3601
|
-
let endArrowMouseTimeout = undefined;
|
|
3602
|
-
this.startArrow.onMouseDown = (event) => {
|
|
3603
|
-
event.stopPropagation();
|
|
3604
|
-
event.preventDefault();
|
|
3605
|
-
this.scrollBy(-0.5, "viewport");
|
|
3606
|
-
startArrowMouseTimeout = setTimeout(() => {
|
|
3607
|
-
this.scrollBy(-0.5, "viewport");
|
|
3608
|
-
startArrowMouseTimeout = setInterval(() => {
|
|
3609
|
-
this.scrollBy(-0.2, "viewport");
|
|
3610
|
-
}, 200);
|
|
3611
|
-
}, 500);
|
|
3612
|
-
};
|
|
3613
|
-
this.startArrow.onMouseUp = (event) => {
|
|
3614
|
-
event.stopPropagation();
|
|
3615
|
-
clearInterval(startArrowMouseTimeout);
|
|
3616
|
-
};
|
|
3617
|
-
this.endArrow.onMouseDown = (event) => {
|
|
3618
|
-
event.stopPropagation();
|
|
3619
|
-
event.preventDefault();
|
|
3620
|
-
this.scrollBy(0.5, "viewport");
|
|
3621
|
-
endArrowMouseTimeout = setTimeout(() => {
|
|
3622
|
-
this.scrollBy(0.5, "viewport");
|
|
3623
|
-
endArrowMouseTimeout = setInterval(() => {
|
|
3624
|
-
this.scrollBy(0.2, "viewport");
|
|
3625
|
-
}, 200);
|
|
3626
|
-
}, 500);
|
|
3627
|
-
};
|
|
3628
|
-
this.endArrow.onMouseUp = (event) => {
|
|
3629
|
-
event.stopPropagation();
|
|
3630
|
-
clearInterval(endArrowMouseTimeout);
|
|
3631
|
-
};
|
|
3524
|
+
recalculateBarProps() {
|
|
3525
|
+
this.verticalScrollBar.scrollSize = this.content.height;
|
|
3526
|
+
this.verticalScrollBar.viewportSize = this.viewport.height;
|
|
3527
|
+
this.horizontalScrollBar.scrollSize = this.content.width;
|
|
3528
|
+
this.horizontalScrollBar.viewportSize = this.viewport.width;
|
|
3529
|
+
if (this._stickyScroll) {
|
|
3530
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3531
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3532
|
+
if (this._stickyStart && !this._hasManualScroll) {
|
|
3533
|
+
this.applyStickyStart(this._stickyStart);
|
|
3534
|
+
} else {
|
|
3535
|
+
if (this._stickyScrollTop) {
|
|
3536
|
+
this.scrollTop = 0;
|
|
3537
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
3538
|
+
this.scrollTop = newMaxScrollTop;
|
|
3539
|
+
}
|
|
3540
|
+
if (this._stickyScrollLeft) {
|
|
3541
|
+
this.scrollLeft = 0;
|
|
3542
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
3543
|
+
this.scrollLeft = newMaxScrollLeft;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
process.nextTick(() => {
|
|
3548
|
+
this.requestRender();
|
|
3549
|
+
});
|
|
3632
3550
|
}
|
|
3633
|
-
set
|
|
3634
|
-
Object.assign(this
|
|
3635
|
-
Object.assign(this.endArrow, options);
|
|
3551
|
+
set rootOptions(options) {
|
|
3552
|
+
Object.assign(this, options);
|
|
3636
3553
|
this.requestRender();
|
|
3637
3554
|
}
|
|
3638
|
-
set
|
|
3639
|
-
Object.assign(this.
|
|
3555
|
+
set wrapperOptions(options) {
|
|
3556
|
+
Object.assign(this.wrapper, options);
|
|
3640
3557
|
this.requestRender();
|
|
3641
3558
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
this.
|
|
3645
|
-
this.slider.max = scrollRange;
|
|
3646
|
-
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
3559
|
+
set viewportOptions(options) {
|
|
3560
|
+
Object.assign(this.viewport, options);
|
|
3561
|
+
this.requestRender();
|
|
3647
3562
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
this.scrollPosition += resolvedDelta;
|
|
3563
|
+
set contentOptions(options) {
|
|
3564
|
+
Object.assign(this.content, options);
|
|
3565
|
+
this.requestRender();
|
|
3652
3566
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
}
|
|
3567
|
+
set scrollbarOptions(options) {
|
|
3568
|
+
Object.assign(this.verticalScrollBar, options);
|
|
3569
|
+
Object.assign(this.horizontalScrollBar, options);
|
|
3570
|
+
this.requestRender();
|
|
3658
3571
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
return false;
|
|
3678
|
-
this.scrollBy(-1 / 5, "viewport");
|
|
3679
|
-
return true;
|
|
3680
|
-
case "down":
|
|
3681
|
-
case "j":
|
|
3682
|
-
if (this.orientation !== "vertical")
|
|
3683
|
-
return false;
|
|
3684
|
-
this.scrollBy(1 / 5, "viewport");
|
|
3685
|
-
return true;
|
|
3686
|
-
case "pageup":
|
|
3687
|
-
this.scrollBy(-1 / 2, "viewport");
|
|
3688
|
-
return true;
|
|
3689
|
-
case "pagedown":
|
|
3690
|
-
this.scrollBy(1 / 2, "viewport");
|
|
3691
|
-
return true;
|
|
3692
|
-
case "home":
|
|
3693
|
-
this.scrollBy(-1, "content");
|
|
3694
|
-
return true;
|
|
3695
|
-
case "end":
|
|
3696
|
-
this.scrollBy(1, "content");
|
|
3697
|
-
return true;
|
|
3572
|
+
set verticalScrollbarOptions(options) {
|
|
3573
|
+
Object.assign(this.verticalScrollBar, options);
|
|
3574
|
+
this.requestRender();
|
|
3575
|
+
}
|
|
3576
|
+
set horizontalScrollbarOptions(options) {
|
|
3577
|
+
Object.assign(this.horizontalScrollBar, options);
|
|
3578
|
+
this.requestRender();
|
|
3579
|
+
}
|
|
3580
|
+
get scrollAcceleration() {
|
|
3581
|
+
return this.scrollAccel;
|
|
3582
|
+
}
|
|
3583
|
+
set scrollAcceleration(value) {
|
|
3584
|
+
this.scrollAccel = value;
|
|
3585
|
+
}
|
|
3586
|
+
destroySelf() {
|
|
3587
|
+
if (this.selectionListener) {
|
|
3588
|
+
this._ctx.off("selection", this.selectionListener);
|
|
3589
|
+
this.selectionListener = undefined;
|
|
3698
3590
|
}
|
|
3699
|
-
|
|
3591
|
+
super.destroySelf();
|
|
3700
3592
|
}
|
|
3701
3593
|
}
|
|
3594
|
+
// src/renderables/Select.ts
|
|
3595
|
+
var SelectRenderableEvents;
|
|
3596
|
+
((SelectRenderableEvents2) => {
|
|
3597
|
+
SelectRenderableEvents2["SELECTION_CHANGED"] = "selectionChanged";
|
|
3598
|
+
SelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
|
|
3599
|
+
})(SelectRenderableEvents ||= {});
|
|
3702
3600
|
|
|
3703
|
-
class
|
|
3704
|
-
|
|
3705
|
-
|
|
3601
|
+
class SelectRenderable extends Renderable {
|
|
3602
|
+
_focusable = true;
|
|
3603
|
+
_options = [];
|
|
3604
|
+
selectedIndex = 0;
|
|
3605
|
+
scrollOffset = 0;
|
|
3606
|
+
maxVisibleItems;
|
|
3706
3607
|
_backgroundColor;
|
|
3707
|
-
|
|
3708
|
-
|
|
3608
|
+
_textColor;
|
|
3609
|
+
_focusedBackgroundColor;
|
|
3610
|
+
_focusedTextColor;
|
|
3611
|
+
_selectedBackgroundColor;
|
|
3612
|
+
_selectedTextColor;
|
|
3613
|
+
_descriptionColor;
|
|
3614
|
+
_selectedDescriptionColor;
|
|
3615
|
+
_showScrollIndicator;
|
|
3616
|
+
_wrapSelection;
|
|
3617
|
+
_showDescription;
|
|
3618
|
+
_font;
|
|
3619
|
+
_itemSpacing;
|
|
3620
|
+
linesPerItem;
|
|
3621
|
+
fontHeight;
|
|
3622
|
+
_fastScrollStep;
|
|
3623
|
+
_defaultOptions = {
|
|
3624
|
+
backgroundColor: "transparent",
|
|
3625
|
+
textColor: "#FFFFFF",
|
|
3626
|
+
focusedBackgroundColor: "#1a1a1a",
|
|
3627
|
+
focusedTextColor: "#FFFFFF",
|
|
3628
|
+
selectedBackgroundColor: "#334455",
|
|
3629
|
+
selectedTextColor: "#FFFF00",
|
|
3630
|
+
descriptionColor: "#888888",
|
|
3631
|
+
selectedDescriptionColor: "#CCCCCC",
|
|
3632
|
+
showScrollIndicator: false,
|
|
3633
|
+
wrapSelection: false,
|
|
3634
|
+
showDescription: true,
|
|
3635
|
+
itemSpacing: 0,
|
|
3636
|
+
fastScrollStep: 5
|
|
3637
|
+
};
|
|
3709
3638
|
constructor(ctx, options) {
|
|
3710
|
-
super(ctx, options);
|
|
3711
|
-
this.
|
|
3712
|
-
this.
|
|
3713
|
-
this.
|
|
3714
|
-
this.
|
|
3715
|
-
this.
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
};
|
|
3722
|
-
|
|
3723
|
-
|
|
3639
|
+
super(ctx, { ...options, buffered: true });
|
|
3640
|
+
this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
|
|
3641
|
+
this._textColor = parseColor(options.textColor || this._defaultOptions.textColor);
|
|
3642
|
+
this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || this._defaultOptions.focusedBackgroundColor);
|
|
3643
|
+
this._focusedTextColor = parseColor(options.focusedTextColor || this._defaultOptions.focusedTextColor);
|
|
3644
|
+
this._options = options.options || [];
|
|
3645
|
+
this._showScrollIndicator = options.showScrollIndicator ?? this._defaultOptions.showScrollIndicator;
|
|
3646
|
+
this._wrapSelection = options.wrapSelection ?? this._defaultOptions.wrapSelection;
|
|
3647
|
+
this._showDescription = options.showDescription ?? this._defaultOptions.showDescription;
|
|
3648
|
+
this._font = options.font;
|
|
3649
|
+
this._itemSpacing = options.itemSpacing || this._defaultOptions.itemSpacing;
|
|
3650
|
+
this.fontHeight = this._font ? measureText({ text: "A", font: this._font }).height : 1;
|
|
3651
|
+
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
3652
|
+
this.linesPerItem += this._itemSpacing;
|
|
3653
|
+
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
3654
|
+
this._selectedBackgroundColor = parseColor(options.selectedBackgroundColor || this._defaultOptions.selectedBackgroundColor);
|
|
3655
|
+
this._selectedTextColor = parseColor(options.selectedTextColor || this._defaultOptions.selectedTextColor);
|
|
3656
|
+
this._descriptionColor = parseColor(options.descriptionColor || this._defaultOptions.descriptionColor);
|
|
3657
|
+
this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || this._defaultOptions.selectedDescriptionColor);
|
|
3658
|
+
this._fastScrollStep = options.fastScrollStep || this._defaultOptions.fastScrollStep;
|
|
3659
|
+
this.requestRender();
|
|
3660
|
+
}
|
|
3661
|
+
renderSelf(buffer, deltaTime) {
|
|
3662
|
+
if (!this.visible || !this.frameBuffer)
|
|
3663
|
+
return;
|
|
3664
|
+
if (this.isDirty) {
|
|
3665
|
+
this.refreshFrameBuffer();
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
refreshFrameBuffer() {
|
|
3669
|
+
if (!this.frameBuffer || this._options.length === 0)
|
|
3670
|
+
return;
|
|
3671
|
+
const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
3672
|
+
this.frameBuffer.clear(bgColor);
|
|
3673
|
+
const contentX = 0;
|
|
3674
|
+
const contentY = 0;
|
|
3675
|
+
const contentWidth = this.width;
|
|
3676
|
+
const contentHeight = this.height;
|
|
3677
|
+
const visibleOptions = this._options.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleItems);
|
|
3678
|
+
for (let i = 0;i < visibleOptions.length; i++) {
|
|
3679
|
+
const actualIndex = this.scrollOffset + i;
|
|
3680
|
+
const option = visibleOptions[i];
|
|
3681
|
+
const isSelected = actualIndex === this.selectedIndex;
|
|
3682
|
+
const itemY = contentY + i * this.linesPerItem;
|
|
3683
|
+
if (itemY + this.linesPerItem - 1 >= contentY + contentHeight)
|
|
3684
|
+
break;
|
|
3685
|
+
if (isSelected) {
|
|
3686
|
+
const contentHeight2 = this.linesPerItem - this._itemSpacing;
|
|
3687
|
+
this.frameBuffer.fillRect(contentX, itemY, contentWidth, contentHeight2, this._selectedBackgroundColor);
|
|
3688
|
+
}
|
|
3689
|
+
const nameContent = `${isSelected ? "\u25B6 " : " "}${option.name}`;
|
|
3690
|
+
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
3691
|
+
const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
|
|
3692
|
+
let descX = contentX + 3;
|
|
3693
|
+
if (this._font) {
|
|
3694
|
+
const indicator = isSelected ? "\u25B6 " : " ";
|
|
3695
|
+
this.frameBuffer.drawText(indicator, contentX + 1, itemY, nameColor);
|
|
3696
|
+
const indicatorWidth = 2;
|
|
3697
|
+
renderFontToFrameBuffer(this.frameBuffer, {
|
|
3698
|
+
text: option.name,
|
|
3699
|
+
x: contentX + 1 + indicatorWidth,
|
|
3700
|
+
y: itemY,
|
|
3701
|
+
fg: nameColor,
|
|
3702
|
+
bg: isSelected ? this._selectedBackgroundColor : bgColor,
|
|
3703
|
+
font: this._font
|
|
3704
|
+
});
|
|
3705
|
+
descX = contentX + 1 + indicatorWidth;
|
|
3706
|
+
} else {
|
|
3707
|
+
this.frameBuffer.drawText(nameContent, contentX + 1, itemY, nameColor);
|
|
3708
|
+
}
|
|
3709
|
+
if (this._showDescription && itemY + this.fontHeight < contentY + contentHeight) {
|
|
3710
|
+
const descColor = isSelected ? this._selectedDescriptionColor : this._descriptionColor;
|
|
3711
|
+
const descBg = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
3712
|
+
this.frameBuffer.drawText(option.description, descX, itemY + this.fontHeight, descColor);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
if (this._showScrollIndicator && this._options.length > this.maxVisibleItems) {
|
|
3716
|
+
this.renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
|
|
3724
3717
|
}
|
|
3725
3718
|
}
|
|
3726
|
-
|
|
3727
|
-
|
|
3719
|
+
renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
|
|
3720
|
+
if (!this.frameBuffer)
|
|
3721
|
+
return;
|
|
3722
|
+
const scrollPercent = this.selectedIndex / Math.max(1, this._options.length - 1);
|
|
3723
|
+
const indicatorHeight = Math.max(1, contentHeight - 2);
|
|
3724
|
+
const indicatorY = contentY + 1 + Math.floor(scrollPercent * indicatorHeight);
|
|
3725
|
+
const indicatorX = contentX + contentWidth - 1;
|
|
3726
|
+
this.frameBuffer.drawText("\u2588", indicatorX, indicatorY, parseColor("#666666"));
|
|
3727
|
+
}
|
|
3728
|
+
get options() {
|
|
3729
|
+
return this._options;
|
|
3730
|
+
}
|
|
3731
|
+
set options(options) {
|
|
3732
|
+
this._options = options;
|
|
3733
|
+
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
|
|
3734
|
+
this.updateScrollOffset();
|
|
3735
|
+
this.requestRender();
|
|
3736
|
+
}
|
|
3737
|
+
getSelectedOption() {
|
|
3738
|
+
return this._options[this.selectedIndex] || null;
|
|
3739
|
+
}
|
|
3740
|
+
getSelectedIndex() {
|
|
3741
|
+
return this.selectedIndex;
|
|
3728
3742
|
}
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
this.
|
|
3743
|
+
moveUp(steps = 1) {
|
|
3744
|
+
const newIndex = this.selectedIndex - steps;
|
|
3745
|
+
if (newIndex >= 0) {
|
|
3746
|
+
this.selectedIndex = newIndex;
|
|
3747
|
+
} else if (this._wrapSelection && this._options.length > 0) {
|
|
3748
|
+
this.selectedIndex = this._options.length - 1;
|
|
3749
|
+
} else {
|
|
3750
|
+
this.selectedIndex = 0;
|
|
3733
3751
|
}
|
|
3752
|
+
this.updateScrollOffset();
|
|
3753
|
+
this.requestRender();
|
|
3754
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3734
3755
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
if (this.
|
|
3740
|
-
this.
|
|
3741
|
-
|
|
3756
|
+
moveDown(steps = 1) {
|
|
3757
|
+
const newIndex = this.selectedIndex + steps;
|
|
3758
|
+
if (newIndex < this._options.length) {
|
|
3759
|
+
this.selectedIndex = newIndex;
|
|
3760
|
+
} else if (this._wrapSelection && this._options.length > 0) {
|
|
3761
|
+
this.selectedIndex = 0;
|
|
3762
|
+
} else {
|
|
3763
|
+
this.selectedIndex = this._options.length - 1;
|
|
3742
3764
|
}
|
|
3765
|
+
this.updateScrollOffset();
|
|
3766
|
+
this.requestRender();
|
|
3767
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3743
3768
|
}
|
|
3744
|
-
|
|
3745
|
-
|
|
3769
|
+
selectCurrent() {
|
|
3770
|
+
const selected = this.getSelectedOption();
|
|
3771
|
+
if (selected) {
|
|
3772
|
+
this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
|
|
3773
|
+
}
|
|
3746
3774
|
}
|
|
3747
|
-
|
|
3748
|
-
if (this.
|
|
3749
|
-
this.
|
|
3775
|
+
setSelectedIndex(index) {
|
|
3776
|
+
if (index >= 0 && index < this._options.length) {
|
|
3777
|
+
this.selectedIndex = index;
|
|
3778
|
+
this.updateScrollOffset();
|
|
3750
3779
|
this.requestRender();
|
|
3780
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
3751
3781
|
}
|
|
3752
3782
|
}
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3783
|
+
updateScrollOffset() {
|
|
3784
|
+
if (!this._options)
|
|
3785
|
+
return;
|
|
3786
|
+
const halfVisible = Math.floor(this.maxVisibleItems / 2);
|
|
3787
|
+
const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleItems));
|
|
3788
|
+
if (newScrollOffset !== this.scrollOffset) {
|
|
3789
|
+
this.scrollOffset = newScrollOffset;
|
|
3759
3790
|
this.requestRender();
|
|
3760
3791
|
}
|
|
3761
3792
|
}
|
|
3762
|
-
|
|
3763
|
-
this.
|
|
3764
|
-
|
|
3765
|
-
...value
|
|
3766
|
-
};
|
|
3793
|
+
onResize(width, height) {
|
|
3794
|
+
this.maxVisibleItems = Math.max(1, Math.floor(height / this.linesPerItem));
|
|
3795
|
+
this.updateScrollOffset();
|
|
3767
3796
|
this.requestRender();
|
|
3768
3797
|
}
|
|
3769
|
-
|
|
3770
|
-
const
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
getArrowChar() {
|
|
3774
|
-
switch (this._direction) {
|
|
3798
|
+
handleKeyPress(key) {
|
|
3799
|
+
const keyName = typeof key === "string" ? key : key.name;
|
|
3800
|
+
const isShift = typeof key !== "string" && key.shift;
|
|
3801
|
+
switch (keyName) {
|
|
3775
3802
|
case "up":
|
|
3776
|
-
|
|
3803
|
+
case "k":
|
|
3804
|
+
this.moveUp(isShift ? this._fastScrollStep : 1);
|
|
3805
|
+
return true;
|
|
3777
3806
|
case "down":
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
return
|
|
3781
|
-
case "
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
return
|
|
3807
|
+
case "j":
|
|
3808
|
+
this.moveDown(isShift ? this._fastScrollStep : 1);
|
|
3809
|
+
return true;
|
|
3810
|
+
case "return":
|
|
3811
|
+
case "enter":
|
|
3812
|
+
this.selectCurrent();
|
|
3813
|
+
return true;
|
|
3785
3814
|
}
|
|
3815
|
+
return false;
|
|
3786
3816
|
}
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
// src/renderables/ScrollBox.ts
|
|
3790
|
-
class ContentRenderable extends BoxRenderable {
|
|
3791
|
-
viewport;
|
|
3792
|
-
constructor(ctx, viewport, options) {
|
|
3793
|
-
super(ctx, options);
|
|
3794
|
-
this.viewport = viewport;
|
|
3795
|
-
}
|
|
3796
|
-
_getChildren() {
|
|
3797
|
-
return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis);
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
|
|
3801
|
-
class ScrollBoxRenderable extends BoxRenderable {
|
|
3802
|
-
static idCounter = 0;
|
|
3803
|
-
internalId = 0;
|
|
3804
|
-
wrapper;
|
|
3805
|
-
viewport;
|
|
3806
|
-
content;
|
|
3807
|
-
horizontalScrollBar;
|
|
3808
|
-
verticalScrollBar;
|
|
3809
|
-
_focusable = true;
|
|
3810
|
-
selectionListener;
|
|
3811
|
-
autoScrollMouseX = 0;
|
|
3812
|
-
autoScrollMouseY = 0;
|
|
3813
|
-
autoScrollThresholdVertical = 3;
|
|
3814
|
-
autoScrollThresholdHorizontal = 3;
|
|
3815
|
-
autoScrollSpeedSlow = 6;
|
|
3816
|
-
autoScrollSpeedMedium = 36;
|
|
3817
|
-
autoScrollSpeedFast = 72;
|
|
3818
|
-
isAutoScrolling = false;
|
|
3819
|
-
cachedAutoScrollSpeed = 3;
|
|
3820
|
-
autoScrollAccumulatorX = 0;
|
|
3821
|
-
autoScrollAccumulatorY = 0;
|
|
3822
|
-
_stickyScroll;
|
|
3823
|
-
_stickyScrollTop = false;
|
|
3824
|
-
_stickyScrollBottom = false;
|
|
3825
|
-
_stickyScrollLeft = false;
|
|
3826
|
-
_stickyScrollRight = false;
|
|
3827
|
-
_stickyStart;
|
|
3828
|
-
_hasManualScroll = false;
|
|
3829
|
-
scrollAccel;
|
|
3830
|
-
get stickyScroll() {
|
|
3831
|
-
return this._stickyScroll;
|
|
3832
|
-
}
|
|
3833
|
-
set stickyScroll(value) {
|
|
3834
|
-
this._stickyScroll = value;
|
|
3835
|
-
this.updateStickyState();
|
|
3836
|
-
}
|
|
3837
|
-
get stickyStart() {
|
|
3838
|
-
return this._stickyStart;
|
|
3839
|
-
}
|
|
3840
|
-
set stickyStart(value) {
|
|
3841
|
-
this._stickyStart = value;
|
|
3842
|
-
this.updateStickyState();
|
|
3843
|
-
}
|
|
3844
|
-
get scrollTop() {
|
|
3845
|
-
return this.verticalScrollBar.scrollPosition;
|
|
3817
|
+
get showScrollIndicator() {
|
|
3818
|
+
return this._showScrollIndicator;
|
|
3846
3819
|
}
|
|
3847
|
-
set
|
|
3848
|
-
this.
|
|
3849
|
-
this.
|
|
3850
|
-
this.updateStickyState();
|
|
3820
|
+
set showScrollIndicator(show) {
|
|
3821
|
+
this._showScrollIndicator = show;
|
|
3822
|
+
this.requestRender();
|
|
3851
3823
|
}
|
|
3852
|
-
get
|
|
3853
|
-
return this.
|
|
3824
|
+
get showDescription() {
|
|
3825
|
+
return this._showDescription;
|
|
3854
3826
|
}
|
|
3855
|
-
set
|
|
3856
|
-
this.
|
|
3857
|
-
|
|
3858
|
-
|
|
3827
|
+
set showDescription(show) {
|
|
3828
|
+
if (this._showDescription !== show) {
|
|
3829
|
+
this._showDescription = show;
|
|
3830
|
+
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
3831
|
+
this.linesPerItem += this._itemSpacing;
|
|
3832
|
+
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
3833
|
+
this.updateScrollOffset();
|
|
3834
|
+
this.requestRender();
|
|
3835
|
+
}
|
|
3859
3836
|
}
|
|
3860
|
-
get
|
|
3861
|
-
return this.
|
|
3837
|
+
get wrapSelection() {
|
|
3838
|
+
return this._wrapSelection;
|
|
3862
3839
|
}
|
|
3863
|
-
|
|
3864
|
-
|
|
3840
|
+
set wrapSelection(wrap) {
|
|
3841
|
+
this._wrapSelection = wrap;
|
|
3865
3842
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
if (this.scrollTop <= 0) {
|
|
3872
|
-
this._stickyScrollTop = true;
|
|
3873
|
-
this._stickyScrollBottom = false;
|
|
3874
|
-
} else if (this.scrollTop >= maxScrollTop) {
|
|
3875
|
-
this._stickyScrollTop = false;
|
|
3876
|
-
this._stickyScrollBottom = true;
|
|
3877
|
-
} else {
|
|
3878
|
-
this._stickyScrollTop = false;
|
|
3879
|
-
this._stickyScrollBottom = false;
|
|
3843
|
+
set backgroundColor(value) {
|
|
3844
|
+
const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
|
|
3845
|
+
if (this._backgroundColor !== newColor) {
|
|
3846
|
+
this._backgroundColor = newColor;
|
|
3847
|
+
this.requestRender();
|
|
3880
3848
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
this.
|
|
3886
|
-
this.
|
|
3887
|
-
} else {
|
|
3888
|
-
this._stickyScrollLeft = false;
|
|
3889
|
-
this._stickyScrollRight = false;
|
|
3849
|
+
}
|
|
3850
|
+
set textColor(value) {
|
|
3851
|
+
const newColor = parseColor(value ?? this._defaultOptions.textColor);
|
|
3852
|
+
if (this._textColor !== newColor) {
|
|
3853
|
+
this._textColor = newColor;
|
|
3854
|
+
this.requestRender();
|
|
3890
3855
|
}
|
|
3891
3856
|
}
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
this.verticalScrollBar.scrollPosition = 0;
|
|
3898
|
-
break;
|
|
3899
|
-
case "bottom":
|
|
3900
|
-
this._stickyScrollTop = false;
|
|
3901
|
-
this._stickyScrollBottom = true;
|
|
3902
|
-
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
3903
|
-
break;
|
|
3904
|
-
case "left":
|
|
3905
|
-
this._stickyScrollLeft = true;
|
|
3906
|
-
this._stickyScrollRight = false;
|
|
3907
|
-
this.horizontalScrollBar.scrollPosition = 0;
|
|
3908
|
-
break;
|
|
3909
|
-
case "right":
|
|
3910
|
-
this._stickyScrollLeft = false;
|
|
3911
|
-
this._stickyScrollRight = true;
|
|
3912
|
-
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
3913
|
-
break;
|
|
3857
|
+
set focusedBackgroundColor(value) {
|
|
3858
|
+
const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
|
|
3859
|
+
if (this._focusedBackgroundColor !== newColor) {
|
|
3860
|
+
this._focusedBackgroundColor = newColor;
|
|
3861
|
+
this.requestRender();
|
|
3914
3862
|
}
|
|
3915
3863
|
}
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
scrollbarOptions,
|
|
3922
|
-
verticalScrollbarOptions,
|
|
3923
|
-
horizontalScrollbarOptions,
|
|
3924
|
-
stickyScroll = false,
|
|
3925
|
-
stickyStart,
|
|
3926
|
-
scrollX = false,
|
|
3927
|
-
scrollY = true,
|
|
3928
|
-
scrollAcceleration,
|
|
3929
|
-
...options
|
|
3930
|
-
}) {
|
|
3931
|
-
super(ctx, {
|
|
3932
|
-
flexDirection: "row",
|
|
3933
|
-
alignItems: "stretch",
|
|
3934
|
-
...options,
|
|
3935
|
-
...rootOptions
|
|
3936
|
-
});
|
|
3937
|
-
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
3938
|
-
this._stickyScroll = stickyScroll;
|
|
3939
|
-
this._stickyStart = stickyStart;
|
|
3940
|
-
if (scrollAcceleration) {
|
|
3941
|
-
this.scrollAccel = scrollAcceleration;
|
|
3942
|
-
} else if (process.platform === "darwin") {
|
|
3943
|
-
this.scrollAccel = new MacOSScrollAccel;
|
|
3864
|
+
set focusedTextColor(value) {
|
|
3865
|
+
const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
|
|
3866
|
+
if (this._focusedTextColor !== newColor) {
|
|
3867
|
+
this._focusedTextColor = newColor;
|
|
3868
|
+
this.requestRender();
|
|
3944
3869
|
}
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
});
|
|
3952
|
-
super.add(this.wrapper);
|
|
3953
|
-
this.viewport = new BoxRenderable(ctx, {
|
|
3954
|
-
flexDirection: "column",
|
|
3955
|
-
flexGrow: 1,
|
|
3956
|
-
overflow: "hidden",
|
|
3957
|
-
onSizeChange: () => {
|
|
3958
|
-
this.recalculateBarProps();
|
|
3959
|
-
},
|
|
3960
|
-
...viewportOptions,
|
|
3961
|
-
id: `scroll-box-viewport-${this.internalId}`
|
|
3962
|
-
});
|
|
3963
|
-
this.wrapper.add(this.viewport);
|
|
3964
|
-
this.content = new ContentRenderable(ctx, this.viewport, {
|
|
3965
|
-
alignSelf: "flex-start",
|
|
3966
|
-
flexShrink: 0,
|
|
3967
|
-
...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
|
|
3968
|
-
...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
|
|
3969
|
-
onSizeChange: () => {
|
|
3970
|
-
this.recalculateBarProps();
|
|
3971
|
-
},
|
|
3972
|
-
...contentOptions,
|
|
3973
|
-
id: `scroll-box-content-${this.internalId}`
|
|
3974
|
-
});
|
|
3975
|
-
this.viewport.add(this.content);
|
|
3976
|
-
this.verticalScrollBar = new ScrollBarRenderable(ctx, {
|
|
3977
|
-
...scrollbarOptions,
|
|
3978
|
-
...verticalScrollbarOptions,
|
|
3979
|
-
arrowOptions: {
|
|
3980
|
-
...scrollbarOptions?.arrowOptions,
|
|
3981
|
-
...verticalScrollbarOptions?.arrowOptions
|
|
3982
|
-
},
|
|
3983
|
-
id: `scroll-box-vertical-scrollbar-${this.internalId}`,
|
|
3984
|
-
orientation: "vertical",
|
|
3985
|
-
onChange: (position) => {
|
|
3986
|
-
this.content.translateY = -position;
|
|
3987
|
-
this._hasManualScroll = true;
|
|
3988
|
-
this.updateStickyState();
|
|
3989
|
-
}
|
|
3990
|
-
});
|
|
3991
|
-
super.add(this.verticalScrollBar);
|
|
3992
|
-
this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
|
|
3993
|
-
...scrollbarOptions,
|
|
3994
|
-
...horizontalScrollbarOptions,
|
|
3995
|
-
arrowOptions: {
|
|
3996
|
-
...scrollbarOptions?.arrowOptions,
|
|
3997
|
-
...horizontalScrollbarOptions?.arrowOptions
|
|
3998
|
-
},
|
|
3999
|
-
id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
|
|
4000
|
-
orientation: "horizontal",
|
|
4001
|
-
onChange: (position) => {
|
|
4002
|
-
this.content.translateX = -position;
|
|
4003
|
-
this._hasManualScroll = true;
|
|
4004
|
-
this.updateStickyState();
|
|
4005
|
-
}
|
|
4006
|
-
});
|
|
4007
|
-
this.wrapper.add(this.horizontalScrollBar);
|
|
4008
|
-
this.recalculateBarProps();
|
|
4009
|
-
if (stickyStart && stickyScroll) {
|
|
4010
|
-
this.applyStickyStart(stickyStart);
|
|
3870
|
+
}
|
|
3871
|
+
set selectedBackgroundColor(value) {
|
|
3872
|
+
const newColor = parseColor(value ?? this._defaultOptions.selectedBackgroundColor);
|
|
3873
|
+
if (this._selectedBackgroundColor !== newColor) {
|
|
3874
|
+
this._selectedBackgroundColor = newColor;
|
|
3875
|
+
this.requestRender();
|
|
4011
3876
|
}
|
|
4012
|
-
this.selectionListener = () => {
|
|
4013
|
-
const selection = this._ctx.getSelection();
|
|
4014
|
-
if (!selection || !selection.isSelecting) {
|
|
4015
|
-
this.stopAutoScroll();
|
|
4016
|
-
}
|
|
4017
|
-
};
|
|
4018
|
-
this._ctx.on("selection", this.selectionListener);
|
|
4019
3877
|
}
|
|
4020
|
-
|
|
4021
|
-
this.
|
|
3878
|
+
set selectedTextColor(value) {
|
|
3879
|
+
const newColor = parseColor(value ?? this._defaultOptions.selectedTextColor);
|
|
3880
|
+
if (this._selectedTextColor !== newColor) {
|
|
3881
|
+
this._selectedTextColor = newColor;
|
|
3882
|
+
this.requestRender();
|
|
3883
|
+
}
|
|
4022
3884
|
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
this.
|
|
4028
|
-
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
3885
|
+
set descriptionColor(value) {
|
|
3886
|
+
const newColor = parseColor(value ?? this._defaultOptions.descriptionColor);
|
|
3887
|
+
if (this._descriptionColor !== newColor) {
|
|
3888
|
+
this._descriptionColor = newColor;
|
|
3889
|
+
this.requestRender();
|
|
4029
3890
|
}
|
|
4030
|
-
this._hasManualScroll = true;
|
|
4031
3891
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
this.
|
|
4037
|
-
this.scrollLeft = position.x;
|
|
3892
|
+
set selectedDescriptionColor(value) {
|
|
3893
|
+
const newColor = parseColor(value ?? this._defaultOptions.selectedDescriptionColor);
|
|
3894
|
+
if (this._selectedDescriptionColor !== newColor) {
|
|
3895
|
+
this._selectedDescriptionColor = newColor;
|
|
3896
|
+
this.requestRender();
|
|
4038
3897
|
}
|
|
4039
3898
|
}
|
|
4040
|
-
|
|
4041
|
-
|
|
3899
|
+
set font(font) {
|
|
3900
|
+
this._font = font;
|
|
3901
|
+
this.fontHeight = measureText({ text: "A", font: this._font }).height;
|
|
3902
|
+
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
3903
|
+
this.linesPerItem += this._itemSpacing;
|
|
3904
|
+
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
3905
|
+
this.updateScrollOffset();
|
|
3906
|
+
this.requestRender();
|
|
4042
3907
|
}
|
|
4043
|
-
|
|
4044
|
-
|
|
3908
|
+
set itemSpacing(spacing) {
|
|
3909
|
+
this._itemSpacing = spacing;
|
|
3910
|
+
this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
|
|
3911
|
+
this.linesPerItem += this._itemSpacing;
|
|
3912
|
+
this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
|
|
3913
|
+
this.updateScrollOffset();
|
|
3914
|
+
this.requestRender();
|
|
4045
3915
|
}
|
|
4046
|
-
|
|
4047
|
-
this.
|
|
3916
|
+
set fastScrollStep(step) {
|
|
3917
|
+
this._fastScrollStep = step;
|
|
4048
3918
|
}
|
|
4049
|
-
|
|
4050
|
-
|
|
3919
|
+
}
|
|
3920
|
+
// src/renderables/TabSelect.ts
|
|
3921
|
+
var TabSelectRenderableEvents;
|
|
3922
|
+
((TabSelectRenderableEvents2) => {
|
|
3923
|
+
TabSelectRenderableEvents2["SELECTION_CHANGED"] = "selectionChanged";
|
|
3924
|
+
TabSelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
|
|
3925
|
+
})(TabSelectRenderableEvents ||= {});
|
|
3926
|
+
function calculateDynamicHeight(showUnderline, showDescription) {
|
|
3927
|
+
let height = 1;
|
|
3928
|
+
if (showUnderline) {
|
|
3929
|
+
height += 1;
|
|
4051
3930
|
}
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
3931
|
+
if (showDescription) {
|
|
3932
|
+
height += 1;
|
|
3933
|
+
}
|
|
3934
|
+
return height;
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
class TabSelectRenderable extends Renderable {
|
|
3938
|
+
_focusable = true;
|
|
3939
|
+
_options = [];
|
|
3940
|
+
selectedIndex = 0;
|
|
3941
|
+
scrollOffset = 0;
|
|
3942
|
+
_tabWidth;
|
|
3943
|
+
maxVisibleTabs;
|
|
3944
|
+
_backgroundColor;
|
|
3945
|
+
_textColor;
|
|
3946
|
+
_focusedBackgroundColor;
|
|
3947
|
+
_focusedTextColor;
|
|
3948
|
+
_selectedBackgroundColor;
|
|
3949
|
+
_selectedTextColor;
|
|
3950
|
+
_selectedDescriptionColor;
|
|
3951
|
+
_showScrollArrows;
|
|
3952
|
+
_showDescription;
|
|
3953
|
+
_showUnderline;
|
|
3954
|
+
_wrapSelection;
|
|
3955
|
+
constructor(ctx, options) {
|
|
3956
|
+
const calculatedHeight = calculateDynamicHeight(options.showUnderline ?? true, options.showDescription ?? true);
|
|
3957
|
+
super(ctx, { ...options, height: calculatedHeight, buffered: true });
|
|
3958
|
+
this._backgroundColor = parseColor(options.backgroundColor || "transparent");
|
|
3959
|
+
this._textColor = parseColor(options.textColor || "#FFFFFF");
|
|
3960
|
+
this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || options.backgroundColor || "#1a1a1a");
|
|
3961
|
+
this._focusedTextColor = parseColor(options.focusedTextColor || options.textColor || "#FFFFFF");
|
|
3962
|
+
this._options = options.options || [];
|
|
3963
|
+
this._tabWidth = options.tabWidth || 20;
|
|
3964
|
+
this._showDescription = options.showDescription ?? true;
|
|
3965
|
+
this._showUnderline = options.showUnderline ?? true;
|
|
3966
|
+
this._showScrollArrows = options.showScrollArrows ?? true;
|
|
3967
|
+
this._wrapSelection = options.wrapSelection ?? false;
|
|
3968
|
+
this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
|
|
3969
|
+
this._selectedBackgroundColor = parseColor(options.selectedBackgroundColor || "#334455");
|
|
3970
|
+
this._selectedTextColor = parseColor(options.selectedTextColor || "#FFFF00");
|
|
3971
|
+
this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || "#CCCCCC");
|
|
3972
|
+
}
|
|
3973
|
+
calculateDynamicHeight() {
|
|
3974
|
+
return calculateDynamicHeight(this._showUnderline, this._showDescription);
|
|
3975
|
+
}
|
|
3976
|
+
renderSelf(buffer, deltaTime) {
|
|
3977
|
+
if (!this.visible || !this.frameBuffer)
|
|
3978
|
+
return;
|
|
3979
|
+
if (this.isDirty) {
|
|
3980
|
+
this.refreshFrameBuffer();
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
refreshFrameBuffer() {
|
|
3984
|
+
if (!this.frameBuffer || this._options.length === 0)
|
|
3985
|
+
return;
|
|
3986
|
+
const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
|
|
3987
|
+
this.frameBuffer.clear(bgColor);
|
|
3988
|
+
const contentX = 0;
|
|
3989
|
+
const contentY = 0;
|
|
3990
|
+
const contentWidth = this.width;
|
|
3991
|
+
const contentHeight = this.height;
|
|
3992
|
+
const visibleOptions = this._options.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleTabs);
|
|
3993
|
+
for (let i = 0;i < visibleOptions.length; i++) {
|
|
3994
|
+
const actualIndex = this.scrollOffset + i;
|
|
3995
|
+
const option = visibleOptions[i];
|
|
3996
|
+
const isSelected = actualIndex === this.selectedIndex;
|
|
3997
|
+
const tabX = contentX + i * this._tabWidth;
|
|
3998
|
+
if (tabX >= contentX + contentWidth)
|
|
3999
|
+
break;
|
|
4000
|
+
const actualTabWidth = Math.min(this._tabWidth, contentWidth - i * this._tabWidth);
|
|
4001
|
+
if (isSelected) {
|
|
4002
|
+
this.frameBuffer.fillRect(tabX, contentY, actualTabWidth, 1, this._selectedBackgroundColor);
|
|
4003
|
+
}
|
|
4004
|
+
const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
|
|
4005
|
+
const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
|
|
4006
|
+
const nameContent = this.truncateText(option.name, actualTabWidth - 2);
|
|
4007
|
+
this.frameBuffer.drawText(nameContent, tabX + 1, contentY, nameColor);
|
|
4008
|
+
if (isSelected && this._showUnderline && contentHeight >= 2) {
|
|
4009
|
+
const underlineY = contentY + 1;
|
|
4010
|
+
const underlineBg = isSelected ? this._selectedBackgroundColor : bgColor;
|
|
4011
|
+
this.frameBuffer.drawText("\u25AC".repeat(actualTabWidth), tabX, underlineY, nameColor, underlineBg);
|
|
4068
4012
|
}
|
|
4069
|
-
this._hasManualScroll = true;
|
|
4070
|
-
}
|
|
4071
|
-
if (event.type === "drag" && event.isSelecting) {
|
|
4072
|
-
this.updateAutoScroll(event.x, event.y);
|
|
4073
|
-
} else if (event.type === "up") {
|
|
4074
|
-
this.stopAutoScroll();
|
|
4075
4013
|
}
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4014
|
+
if (this._showDescription && contentHeight >= (this._showUnderline ? 3 : 2)) {
|
|
4015
|
+
const selectedOption = this.getSelectedOption();
|
|
4016
|
+
if (selectedOption) {
|
|
4017
|
+
const descriptionY = contentY + (this._showUnderline ? 2 : 1);
|
|
4018
|
+
const descColor = this._selectedDescriptionColor;
|
|
4019
|
+
const descContent = this.truncateText(selectedOption.description, contentWidth - 2);
|
|
4020
|
+
this.frameBuffer.drawText(descContent, contentX + 1, descriptionY, descColor);
|
|
4021
|
+
}
|
|
4082
4022
|
}
|
|
4083
|
-
if (this.
|
|
4084
|
-
this.
|
|
4085
|
-
this.scrollAccel.reset();
|
|
4086
|
-
return true;
|
|
4023
|
+
if (this._showScrollArrows && this._options.length > this.maxVisibleTabs) {
|
|
4024
|
+
this.renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
|
|
4087
4025
|
}
|
|
4088
|
-
return false;
|
|
4089
4026
|
}
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
|
|
4095
|
-
this.isAutoScrolling = true;
|
|
4096
|
-
if (!this.live) {
|
|
4097
|
-
this.live = true;
|
|
4098
|
-
}
|
|
4027
|
+
truncateText(text, maxWidth) {
|
|
4028
|
+
if (text.length <= maxWidth)
|
|
4029
|
+
return text;
|
|
4030
|
+
return text.substring(0, Math.max(0, maxWidth - 1)) + "\u2026";
|
|
4099
4031
|
}
|
|
4100
|
-
|
|
4101
|
-
this.
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
const
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
this.stopAutoScroll();
|
|
4108
|
-
} else if (!this.isAutoScrolling) {
|
|
4109
|
-
this.startAutoScroll(mouseX, mouseY);
|
|
4032
|
+
renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
|
|
4033
|
+
if (!this.frameBuffer)
|
|
4034
|
+
return;
|
|
4035
|
+
const hasMoreLeft = this.scrollOffset > 0;
|
|
4036
|
+
const hasMoreRight = this.scrollOffset + this.maxVisibleTabs < this._options.length;
|
|
4037
|
+
if (hasMoreLeft) {
|
|
4038
|
+
this.frameBuffer.drawText("\u2039", contentX, contentY, parseColor("#AAAAAA"));
|
|
4110
4039
|
}
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
const wasAutoScrolling = this.isAutoScrolling;
|
|
4114
|
-
this.isAutoScrolling = false;
|
|
4115
|
-
this.autoScrollAccumulatorX = 0;
|
|
4116
|
-
this.autoScrollAccumulatorY = 0;
|
|
4117
|
-
if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
|
|
4118
|
-
this.live = false;
|
|
4040
|
+
if (hasMoreRight) {
|
|
4041
|
+
this.frameBuffer.drawText("\u203A", contentX + contentWidth - 1, contentY, parseColor("#AAAAAA"));
|
|
4119
4042
|
}
|
|
4120
4043
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4044
|
+
setOptions(options) {
|
|
4045
|
+
this._options = options;
|
|
4046
|
+
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
|
|
4047
|
+
this.updateScrollOffset();
|
|
4048
|
+
this.requestRender();
|
|
4123
4049
|
}
|
|
4124
|
-
|
|
4125
|
-
|
|
4050
|
+
getSelectedOption() {
|
|
4051
|
+
return this._options[this.selectedIndex] || null;
|
|
4052
|
+
}
|
|
4053
|
+
getSelectedIndex() {
|
|
4054
|
+
return this.selectedIndex;
|
|
4055
|
+
}
|
|
4056
|
+
moveLeft() {
|
|
4057
|
+
if (this.selectedIndex > 0) {
|
|
4058
|
+
this.selectedIndex--;
|
|
4059
|
+
} else if (this._wrapSelection && this._options.length > 0) {
|
|
4060
|
+
this.selectedIndex = this._options.length - 1;
|
|
4061
|
+
} else {
|
|
4126
4062
|
return;
|
|
4127
|
-
const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
|
|
4128
|
-
const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
|
|
4129
|
-
const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
|
|
4130
|
-
let scrolled = false;
|
|
4131
|
-
if (scrollX !== 0) {
|
|
4132
|
-
this.autoScrollAccumulatorX += scrollX * scrollAmount;
|
|
4133
|
-
const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
|
|
4134
|
-
if (integerScrollX !== 0) {
|
|
4135
|
-
this.scrollLeft += integerScrollX;
|
|
4136
|
-
this.autoScrollAccumulatorX -= integerScrollX;
|
|
4137
|
-
scrolled = true;
|
|
4138
|
-
}
|
|
4139
|
-
}
|
|
4140
|
-
if (scrollY !== 0) {
|
|
4141
|
-
this.autoScrollAccumulatorY += scrollY * scrollAmount;
|
|
4142
|
-
const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
|
|
4143
|
-
if (integerScrollY !== 0) {
|
|
4144
|
-
this.scrollTop += integerScrollY;
|
|
4145
|
-
this.autoScrollAccumulatorY -= integerScrollY;
|
|
4146
|
-
scrolled = true;
|
|
4147
|
-
}
|
|
4148
|
-
}
|
|
4149
|
-
if (scrolled) {
|
|
4150
|
-
this._ctx.requestSelectionUpdate();
|
|
4151
4063
|
}
|
|
4152
|
-
|
|
4153
|
-
|
|
4064
|
+
this.updateScrollOffset();
|
|
4065
|
+
this.requestRender();
|
|
4066
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
4067
|
+
}
|
|
4068
|
+
moveRight() {
|
|
4069
|
+
if (this.selectedIndex < this._options.length - 1) {
|
|
4070
|
+
this.selectedIndex++;
|
|
4071
|
+
} else if (this._wrapSelection && this._options.length > 0) {
|
|
4072
|
+
this.selectedIndex = 0;
|
|
4073
|
+
} else {
|
|
4074
|
+
return;
|
|
4154
4075
|
}
|
|
4076
|
+
this.updateScrollOffset();
|
|
4077
|
+
this.requestRender();
|
|
4078
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
4155
4079
|
}
|
|
4156
|
-
|
|
4157
|
-
const
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
if (distToLeft <= this.autoScrollThresholdHorizontal) {
|
|
4161
|
-
return this.scrollLeft > 0 ? -1 : 0;
|
|
4162
|
-
} else if (distToRight <= this.autoScrollThresholdHorizontal) {
|
|
4163
|
-
const maxScrollLeft = this.scrollWidth - this.viewport.width;
|
|
4164
|
-
return this.scrollLeft < maxScrollLeft ? 1 : 0;
|
|
4080
|
+
selectCurrent() {
|
|
4081
|
+
const selected = this.getSelectedOption();
|
|
4082
|
+
if (selected) {
|
|
4083
|
+
this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
|
|
4165
4084
|
}
|
|
4166
|
-
return 0;
|
|
4167
4085
|
}
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
} else if (distToBottom <= this.autoScrollThresholdVertical) {
|
|
4175
|
-
const maxScrollTop = this.scrollHeight - this.viewport.height;
|
|
4176
|
-
return this.scrollTop < maxScrollTop ? 1 : 0;
|
|
4086
|
+
setSelectedIndex(index) {
|
|
4087
|
+
if (index >= 0 && index < this._options.length) {
|
|
4088
|
+
this.selectedIndex = index;
|
|
4089
|
+
this.updateScrollOffset();
|
|
4090
|
+
this.requestRender();
|
|
4091
|
+
this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
|
|
4177
4092
|
}
|
|
4178
|
-
return 0;
|
|
4179
4093
|
}
|
|
4180
|
-
|
|
4181
|
-
const
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
const distToBottom = this.height - relativeY;
|
|
4187
|
-
const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
|
|
4188
|
-
if (minDistance <= 1) {
|
|
4189
|
-
return this.autoScrollSpeedFast;
|
|
4190
|
-
} else if (minDistance <= 2) {
|
|
4191
|
-
return this.autoScrollSpeedMedium;
|
|
4192
|
-
} else {
|
|
4193
|
-
return this.autoScrollSpeedSlow;
|
|
4094
|
+
updateScrollOffset() {
|
|
4095
|
+
const halfVisible = Math.floor(this.maxVisibleTabs / 2);
|
|
4096
|
+
const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleTabs));
|
|
4097
|
+
if (newScrollOffset !== this.scrollOffset) {
|
|
4098
|
+
this.scrollOffset = newScrollOffset;
|
|
4099
|
+
this.requestRender();
|
|
4194
4100
|
}
|
|
4195
4101
|
}
|
|
4196
|
-
|
|
4197
|
-
this.
|
|
4198
|
-
this.
|
|
4199
|
-
this.
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4102
|
+
onResize(width, height) {
|
|
4103
|
+
this.maxVisibleTabs = Math.max(1, Math.floor(width / this._tabWidth));
|
|
4104
|
+
this.updateScrollOffset();
|
|
4105
|
+
this.requestRender();
|
|
4106
|
+
}
|
|
4107
|
+
setTabWidth(tabWidth) {
|
|
4108
|
+
if (this._tabWidth === tabWidth)
|
|
4109
|
+
return;
|
|
4110
|
+
this._tabWidth = tabWidth;
|
|
4111
|
+
this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
|
|
4112
|
+
this.updateScrollOffset();
|
|
4113
|
+
this.requestRender();
|
|
4114
|
+
}
|
|
4115
|
+
getTabWidth() {
|
|
4116
|
+
return this._tabWidth;
|
|
4117
|
+
}
|
|
4118
|
+
handleKeyPress(key) {
|
|
4119
|
+
const keyName = typeof key === "string" ? key : key.name;
|
|
4120
|
+
switch (keyName) {
|
|
4121
|
+
case "left":
|
|
4122
|
+
case "[":
|
|
4123
|
+
this.moveLeft();
|
|
4124
|
+
return true;
|
|
4125
|
+
case "right":
|
|
4126
|
+
case "]":
|
|
4127
|
+
this.moveRight();
|
|
4128
|
+
return true;
|
|
4129
|
+
case "return":
|
|
4130
|
+
case "enter":
|
|
4131
|
+
this.selectCurrent();
|
|
4132
|
+
return true;
|
|
4218
4133
|
}
|
|
4219
|
-
|
|
4220
|
-
this.requestRender();
|
|
4221
|
-
});
|
|
4134
|
+
return false;
|
|
4222
4135
|
}
|
|
4223
|
-
|
|
4224
|
-
|
|
4136
|
+
get options() {
|
|
4137
|
+
return this._options;
|
|
4138
|
+
}
|
|
4139
|
+
set options(options) {
|
|
4140
|
+
this._options = options;
|
|
4141
|
+
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
|
|
4142
|
+
this.updateScrollOffset();
|
|
4225
4143
|
this.requestRender();
|
|
4226
4144
|
}
|
|
4227
|
-
set
|
|
4228
|
-
|
|
4145
|
+
set backgroundColor(color) {
|
|
4146
|
+
this._backgroundColor = parseColor(color);
|
|
4229
4147
|
this.requestRender();
|
|
4230
4148
|
}
|
|
4231
|
-
set
|
|
4232
|
-
|
|
4149
|
+
set textColor(color) {
|
|
4150
|
+
this._textColor = parseColor(color);
|
|
4233
4151
|
this.requestRender();
|
|
4234
4152
|
}
|
|
4235
|
-
set
|
|
4236
|
-
|
|
4153
|
+
set focusedBackgroundColor(color) {
|
|
4154
|
+
this._focusedBackgroundColor = parseColor(color);
|
|
4237
4155
|
this.requestRender();
|
|
4238
4156
|
}
|
|
4239
|
-
set
|
|
4240
|
-
|
|
4241
|
-
Object.assign(this.horizontalScrollBar, options);
|
|
4157
|
+
set focusedTextColor(color) {
|
|
4158
|
+
this._focusedTextColor = parseColor(color);
|
|
4242
4159
|
this.requestRender();
|
|
4243
4160
|
}
|
|
4244
|
-
set
|
|
4245
|
-
|
|
4161
|
+
set selectedBackgroundColor(color) {
|
|
4162
|
+
this._selectedBackgroundColor = parseColor(color);
|
|
4246
4163
|
this.requestRender();
|
|
4247
4164
|
}
|
|
4248
|
-
set
|
|
4249
|
-
|
|
4165
|
+
set selectedTextColor(color) {
|
|
4166
|
+
this._selectedTextColor = parseColor(color);
|
|
4250
4167
|
this.requestRender();
|
|
4251
4168
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4169
|
+
set selectedDescriptionColor(color) {
|
|
4170
|
+
this._selectedDescriptionColor = parseColor(color);
|
|
4171
|
+
this.requestRender();
|
|
4172
|
+
}
|
|
4173
|
+
get showDescription() {
|
|
4174
|
+
return this._showDescription;
|
|
4175
|
+
}
|
|
4176
|
+
set showDescription(show) {
|
|
4177
|
+
if (this._showDescription !== show) {
|
|
4178
|
+
this._showDescription = show;
|
|
4179
|
+
const newHeight = this.calculateDynamicHeight();
|
|
4180
|
+
this.height = newHeight;
|
|
4181
|
+
this.requestRender();
|
|
4256
4182
|
}
|
|
4257
|
-
super.destroySelf();
|
|
4258
4183
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
}
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
}
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
}
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4184
|
+
get showUnderline() {
|
|
4185
|
+
return this._showUnderline;
|
|
4186
|
+
}
|
|
4187
|
+
set showUnderline(show) {
|
|
4188
|
+
if (this._showUnderline !== show) {
|
|
4189
|
+
this._showUnderline = show;
|
|
4190
|
+
const newHeight = this.calculateDynamicHeight();
|
|
4191
|
+
this.height = newHeight;
|
|
4192
|
+
this.requestRender();
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
get showScrollArrows() {
|
|
4196
|
+
return this._showScrollArrows;
|
|
4197
|
+
}
|
|
4198
|
+
set showScrollArrows(show) {
|
|
4199
|
+
if (this._showScrollArrows !== show) {
|
|
4200
|
+
this._showScrollArrows = show;
|
|
4201
|
+
this.requestRender();
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
get wrapSelection() {
|
|
4205
|
+
return this._wrapSelection;
|
|
4206
|
+
}
|
|
4207
|
+
set wrapSelection(wrap) {
|
|
4208
|
+
this._wrapSelection = wrap;
|
|
4209
|
+
}
|
|
4210
|
+
get tabWidth() {
|
|
4211
|
+
return this._tabWidth;
|
|
4212
|
+
}
|
|
4213
|
+
set tabWidth(tabWidth) {
|
|
4214
|
+
if (this._tabWidth === tabWidth)
|
|
4215
|
+
return;
|
|
4216
|
+
this._tabWidth = tabWidth;
|
|
4217
|
+
this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
|
|
4218
|
+
this.updateScrollOffset();
|
|
4219
|
+
this.requestRender();
|
|
4294
4220
|
}
|
|
4295
|
-
return textNode;
|
|
4296
4221
|
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
strikethrough: (...children) => StyledText2({ attributes: TextAttributes.STRIKETHROUGH }, ...children),
|
|
4306
|
-
boldItalic: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC }, ...children),
|
|
4307
|
-
boldUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE }, ...children),
|
|
4308
|
-
italicUnderline: (...children) => StyledText2({ attributes: TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4309
|
-
boldItalicUnderline: (...children) => StyledText2({ attributes: TextAttributes.BOLD | TextAttributes.ITALIC | TextAttributes.UNDERLINE }, ...children),
|
|
4310
|
-
color: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4311
|
-
bgColor: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4312
|
-
fg: (color, ...children) => StyledText2({ fg: color }, ...children),
|
|
4313
|
-
bg: (bgColor, ...children) => StyledText2({ bg: bgColor }, ...children),
|
|
4314
|
-
styled: (attributes = 0, ...children) => StyledText2({ attributes }, ...children)
|
|
4315
|
-
};
|
|
4316
|
-
// src/renderables/composition/VRenderable.ts
|
|
4317
|
-
class VRenderable extends Renderable {
|
|
4318
|
-
options;
|
|
4222
|
+
// src/renderables/Text.ts
|
|
4223
|
+
class TextRenderable extends TextBufferRenderable {
|
|
4224
|
+
_text;
|
|
4225
|
+
_hasManualStyledText = false;
|
|
4226
|
+
rootTextNode;
|
|
4227
|
+
_contentDefaultOptions = {
|
|
4228
|
+
content: ""
|
|
4229
|
+
};
|
|
4319
4230
|
constructor(ctx, options) {
|
|
4320
4231
|
super(ctx, options);
|
|
4321
|
-
|
|
4232
|
+
const content = options.content ?? this._contentDefaultOptions.content;
|
|
4233
|
+
const styledText = typeof content === "string" ? stringToStyledText(content) : content;
|
|
4234
|
+
this._text = styledText;
|
|
4235
|
+
this._hasManualStyledText = options.content !== undefined && content !== "";
|
|
4236
|
+
this.rootTextNode = new RootTextNodeRenderable(ctx, {
|
|
4237
|
+
id: `${this.id}-root`,
|
|
4238
|
+
fg: this._defaultFg,
|
|
4239
|
+
bg: this._defaultBg,
|
|
4240
|
+
attributes: this._defaultAttributes
|
|
4241
|
+
}, this);
|
|
4242
|
+
this.updateTextBuffer(styledText);
|
|
4322
4243
|
}
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4244
|
+
updateTextBuffer(styledText) {
|
|
4245
|
+
this.textBuffer.setStyledText(styledText);
|
|
4246
|
+
this.clearChunks(styledText);
|
|
4247
|
+
}
|
|
4248
|
+
clearChunks(styledText) {}
|
|
4249
|
+
get content() {
|
|
4250
|
+
return this._text;
|
|
4251
|
+
}
|
|
4252
|
+
get chunks() {
|
|
4253
|
+
return this._text.chunks;
|
|
4254
|
+
}
|
|
4255
|
+
get textNode() {
|
|
4256
|
+
return this.rootTextNode;
|
|
4257
|
+
}
|
|
4258
|
+
set content(value) {
|
|
4259
|
+
this._hasManualStyledText = true;
|
|
4260
|
+
const styledText = typeof value === "string" ? stringToStyledText(value) : value;
|
|
4261
|
+
if (this._text !== styledText) {
|
|
4262
|
+
this._text = styledText;
|
|
4263
|
+
this.updateTextBuffer(styledText);
|
|
4264
|
+
this.updateTextInfo();
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
insertChunk(chunk, index) {
|
|
4268
|
+
super.insertChunk(chunk, index);
|
|
4269
|
+
this.clearChunks(this._text);
|
|
4270
|
+
}
|
|
4271
|
+
removeChunkByObject(chunk) {
|
|
4272
|
+
const index = this._text.chunks.indexOf(chunk);
|
|
4273
|
+
if (index === -1)
|
|
4274
|
+
return;
|
|
4275
|
+
super.removeChunk(index);
|
|
4276
|
+
this.clearChunks(this._text);
|
|
4277
|
+
}
|
|
4278
|
+
replaceChunkByObject(chunk, oldChunk) {
|
|
4279
|
+
const index = this._text.chunks.indexOf(oldChunk);
|
|
4280
|
+
if (index === -1)
|
|
4281
|
+
return;
|
|
4282
|
+
super.replaceChunk(index, chunk);
|
|
4283
|
+
this.clearChunks(this._text);
|
|
4284
|
+
}
|
|
4285
|
+
updateTextFromNodes() {
|
|
4286
|
+
if (this.rootTextNode.isDirty && !this._hasManualStyledText) {
|
|
4287
|
+
const chunks = this.rootTextNode.gatherWithInheritedStyle({
|
|
4288
|
+
fg: this._defaultFg,
|
|
4289
|
+
bg: this._defaultBg,
|
|
4290
|
+
attributes: this._defaultAttributes
|
|
4291
|
+
});
|
|
4292
|
+
this.textBuffer.setStyledText(new StyledText(chunks));
|
|
4293
|
+
this.refreshLocalSelection();
|
|
4294
|
+
this.yogaNode.markDirty();
|
|
4326
4295
|
}
|
|
4327
4296
|
}
|
|
4297
|
+
add(obj, index) {
|
|
4298
|
+
return this.rootTextNode.add(obj, index);
|
|
4299
|
+
}
|
|
4300
|
+
remove(id) {
|
|
4301
|
+
this.rootTextNode.remove(id);
|
|
4302
|
+
}
|
|
4303
|
+
insertBefore(obj, anchor) {
|
|
4304
|
+
this.rootTextNode.insertBefore(obj, anchor);
|
|
4305
|
+
return this.rootTextNode.children.indexOf(obj);
|
|
4306
|
+
}
|
|
4307
|
+
getTextChildren() {
|
|
4308
|
+
return this.rootTextNode.getChildren();
|
|
4309
|
+
}
|
|
4310
|
+
clear() {
|
|
4311
|
+
this.rootTextNode.clear();
|
|
4312
|
+
const emptyStyledText = stringToStyledText("");
|
|
4313
|
+
this._text = emptyStyledText;
|
|
4314
|
+
this.updateTextBuffer(emptyStyledText);
|
|
4315
|
+
this.updateTextInfo();
|
|
4316
|
+
this.requestRender();
|
|
4317
|
+
}
|
|
4318
|
+
onLifecyclePass = () => {
|
|
4319
|
+
this.updateTextFromNodes();
|
|
4320
|
+
};
|
|
4321
|
+
onFgChanged(newColor) {
|
|
4322
|
+
this.rootTextNode.fg = newColor;
|
|
4323
|
+
}
|
|
4324
|
+
onBgChanged(newColor) {
|
|
4325
|
+
this.rootTextNode.bg = newColor;
|
|
4326
|
+
}
|
|
4327
|
+
onAttributesChanged(newAttributes) {
|
|
4328
|
+
this.rootTextNode.attributes = newAttributes;
|
|
4329
|
+
}
|
|
4330
|
+
destroy() {
|
|
4331
|
+
this.rootTextNode.children.length = 0;
|
|
4332
|
+
super.destroy();
|
|
4333
|
+
}
|
|
4328
4334
|
}
|
|
4329
4335
|
export {
|
|
4330
4336
|
yellow,
|
|
@@ -4450,6 +4456,7 @@ export {
|
|
|
4450
4456
|
TabSelect,
|
|
4451
4457
|
SyntaxStyle,
|
|
4452
4458
|
StyledText,
|
|
4459
|
+
SliderRenderable,
|
|
4453
4460
|
Selection,
|
|
4454
4461
|
SelectRenderableEvents,
|
|
4455
4462
|
SelectRenderable,
|
|
@@ -4458,6 +4465,7 @@ export {
|
|
|
4458
4465
|
ScrollBarRenderable,
|
|
4459
4466
|
RootTextNodeRenderable,
|
|
4460
4467
|
RootRenderable,
|
|
4468
|
+
RendererControlState,
|
|
4461
4469
|
RenderableEvents,
|
|
4462
4470
|
Renderable,
|
|
4463
4471
|
RGBA,
|
|
@@ -4500,5 +4508,5 @@ export {
|
|
|
4500
4508
|
ASCIIFont
|
|
4501
4509
|
};
|
|
4502
4510
|
|
|
4503
|
-
//# debugId=
|
|
4511
|
+
//# debugId=6F0864CECE58138064756E2164756E21
|
|
4504
4512
|
//# sourceMappingURL=index.js.map
|