Skip to content

Commit fe40c81

Browse files
committed
Grid first test
1 parent 7f7c4cc commit fe40c81

File tree

5 files changed

+197
-21
lines changed

5 files changed

+197
-21
lines changed

libraries/src/app.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ export async function startApp() {
2727
await show(async ({ buffer, readkey, consolekeys }) => {
2828
buffer.secondary();
2929
buffer.clear();
30-
let grid = new Grid()
30+
let grid = new Grid();
31+
let grid2 = new Grid();
32+
grid2.uniformBorder(grid2.borders.SOLID);
33+
grid.children.push(grid2);
34+
let grid3 = new Grid();
35+
grid3.uniformBorder(grid3.borders.SOLID);
36+
grid.children.push(grid3);
3137
//grid.setViewport(process.stdout.columns, 10);
3238
grid.useConsoleViewport();
3339
grid.uniformBorder(grid.borders.SOLID);

libraries/src/gui/IRenderable.js

+103-11
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ import chalk from "chalk";
44
export default class IRenderable {
55

66
// Constructor
7-
constructor(viewport) {
7+
constructor(name) {
8+
/*
89
if (viewport) {
910
this._nominal.viewportWidth = viewport.width ?? this.sizes.UNSET;
1011
this._nominal.viewportHeight = viewport.height ?? this.sizes.UNSET;
12+
}*/
13+
if (name) {
14+
this.name = name;
15+
} else {
16+
throw new Error('Name is required for all renderables');
1117
}
1218
}
1319

@@ -48,9 +54,11 @@ export default class IRenderable {
4854
BOTTOM_RIGHT: 7
4955
}
5056

51-
scrollbarStyle = {
57+
styles = {
5258
railColor: [45, 55, 80],
5359
headColor: [80, 100, 120],
60+
borderBlur: [100, 110, 140],
61+
borderFocus: [255, 255, 255]
5462
}
5563

5664
// Getters and setters
@@ -229,6 +237,61 @@ export default class IRenderable {
229237
}
230238
}
231239

240+
_debug = {
241+
_enabled: false,
242+
enable: () => { this._debug._enabled = true; },
243+
disable: () => { this._debug._enabled = false; },
244+
getTitleBarInfo: (delim) => {
245+
let { width, height } = this.getSize();
246+
let viewport = this.getViewport(false);
247+
if (this._debug._enabled) {
248+
let fill = width - 1;
249+
let debugText = "";
250+
251+
useRealLength();
252+
//debugText = `${width}x${height}`;
253+
let perecedence = [
254+
{ text: `${width}x${height}`, prepend: false },
255+
{ text: `${viewport.width}x${viewport.height}`, prepend: false },
256+
{ text: `${this._nominal.scrollX}x${this._nominal.scrollY}`, prepend: false },
257+
{ text: this.name, prepend: true },
258+
];
259+
260+
let tempText = [];
261+
let tempFill = fill;
262+
for (let i = 0; i < perecedence.length; i++) {
263+
let { text, prepend } = perecedence[i];
264+
//let spacer = Math.min(tempText.length, 1);
265+
tempFill = i === 0 ? tempFill - 1 : tempFill;
266+
if (tempFill - 1 > text.realLength()) {
267+
tempFill -= text.realLength() + 1;
268+
if (prepend) {
269+
tempText.unshift(text);
270+
} else {
271+
tempText.push(text);
272+
}
273+
} else {
274+
break;
275+
}
276+
}
277+
debugText = chalk.bgRgb(0, 230, 140).black(" " + tempText.join(delim ?? "|") + " ");
278+
fill = tempFill;
279+
280+
useRealLength(false);
281+
282+
return {
283+
debugText,
284+
borderWidth: fill
285+
}
286+
} else {
287+
return {
288+
debugText: "",
289+
borderWidth: width - 1,
290+
}
291+
}
292+
}
293+
}
294+
232295
normalizePercents(value, axis, includeMargins) {
233296
if (typeof value === 'string') {
234297
if (value.endsWith('%')) {
@@ -495,6 +558,13 @@ export default class IRenderable {
495558
return size;
496559
}
497560

561+
_calculateRealWidth() {
562+
useRealLength();
563+
let wid = (this.lastBuffer ?? this.aBuffer).reduce((real, line) => Math.max(real, (line && line.realLength()) ?? 0), 0);
564+
useRealLength(false);
565+
return wid;
566+
}
567+
498568
// Applies the margin dimensions to the size
499569
applyMarginSize(size) {
500570
let { x, y } = this.getMargin();
@@ -576,10 +646,10 @@ export default class IRenderable {
576646
let headTop = Math.ceil(ratio * this._nominal.scrollX);
577647
let vBuffer = [];
578648
for (let i = 0; i < height; i++) {
579-
if (i < headTop || i >= headTop + headHeight) {
580-
vBuffer.push(chalk.bgRgb(...this.scrollbarStyle.railColor)(" "));
649+
if (i < headTop || i >= headTop + headHeight - 1) {
650+
vBuffer.push(chalk.bgRgb(...this.styles.railColor)(" "));
581651
} else {
582-
vBuffer.push(chalk.bgRgb(...this.scrollbarStyle.headColor)(" "));
652+
vBuffer.push(chalk.bgRgb(...this.styles.headColor)(" "));
583653
}
584654
}
585655
this.aBuffer = this.aBuffer.map((line, i) => { return line + vBuffer[i] });
@@ -589,6 +659,7 @@ export default class IRenderable {
589659
applyBorder() {
590660
let { top, right, bottom, left } = this.getBorder();
591661
let { width } = this.unapplyPaddingSize(this.getSize());
662+
let hasFocus = this.hasFocus();
592663
let borderTopLeft = this._native.getBorderCharacter(top, this.border_orientations.TOP_LEFT);
593664
let borderTop = this._native.getBorderCharacter(top, this.border_orientations.TOP);
594665
let borderTopRight = this._native.getBorderCharacter(top, this.border_orientations.TOP_RIGHT);
@@ -598,17 +669,29 @@ export default class IRenderable {
598669
let borderBottom = this._native.getBorderCharacter(bottom, this.border_orientations.BOTTOM);
599670
let borderBottomRight = this._native.getBorderCharacter(bottom, this.border_orientations.BOTTOM_RIGHT);
600671

672+
borderTopLeft = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderTopLeft) : chalk.rgb(...this.styles.borderBlur)(borderTopLeft);
673+
borderTop = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderTop) : chalk.rgb(...this.styles.borderBlur)(borderTop);
674+
borderTopRight = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderTopRight) : chalk.rgb(...this.styles.borderBlur)(borderTopRight);
675+
borderRight = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderRight) : chalk.rgb(...this.styles.borderBlur)(borderRight);
676+
borderLeft = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderLeft) : chalk.rgb(...this.styles.borderBlur)(borderLeft);
677+
borderBottomLeft = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderBottomLeft) : chalk.rgb(...this.styles.borderBlur)(borderBottomLeft);
678+
borderBottom = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderBottom) : chalk.rgb(...this.styles.borderBlur)(borderBottom);
679+
borderBottomRight = hasFocus ? chalk.rgb(...this.styles.borderFocus)(borderBottomRight) : chalk.rgb(...this.styles.borderBlur)(borderBottomRight);
680+
681+
601682
if (left !== this.borders.NONE) {
602683
this.aBuffer = this.aBuffer.map(x => borderLeft + x);
603684
}
604685
if (right !== this.borders.NONE) {
605686
this.aBuffer = this.aBuffer.map(x => x + borderRight);
606687
}
607688
if (top !== this.borders.NONE) {
608-
this.aBuffer.unshift(borderTopLeft + borderTop.repeat(width) + borderTopRight);
689+
let { debugText, borderWidth } = this._debug.getTitleBarInfo();
690+
691+
this.aBuffer.unshift(borderTopLeft + (new Array(borderWidth).fill(borderTop)).join("") + debugText + borderTop + borderTopRight);
609692
}
610693
if (bottom !== this.borders.NONE) {
611-
this.aBuffer.push(borderBottomLeft + borderBottom.repeat(width) + borderBottomRight);
694+
this.aBuffer.push(borderBottomLeft + (new Array(width).fill(borderBottom)).join("") + borderBottomRight);
612695
}
613696
}
614697

@@ -733,7 +816,7 @@ export default class IRenderable {
733816
if (str.realLength() < width) {
734817
out = s(str, this.getSize().width);
735818
} else if (str.realLength() >= width) {
736-
out = str.substring(0, width);
819+
out = __EXPERIMENTAL__cutEnd(str, Math.max(str.realLength() - width, 0));
737820
}
738821
useRealLength(false);
739822

@@ -752,7 +835,7 @@ export default class IRenderable {
752835
useRealLength(true);
753836

754837
this.aBuffer.forEach(line => {
755-
if (line.realLength() !== width) {
838+
if (line === null || line === undefined || line.realLength() !== width) {
756839
throw new Error("Renderable buffer dimensions must be equal to control dimensions");
757840
}
758841
});
@@ -783,6 +866,14 @@ export default class IRenderable {
783866
process.stdout.write(renderBuffer.join("\n"));
784867
}
785868

869+
scrollDown() {
870+
this._nominal.scrollX = Math.min(this.lastBuffer.length - this.getSize().height, this._nominal.scrollX + 1);
871+
}
872+
873+
scrollUp() {
874+
this._nominal.scrollX = Math.max(0, this._nominal.scrollX - 1);
875+
}
876+
786877
streamConsoleViewportAsync = async (getViewport, cancelCallback) => {
787878
const { readkey, consolekeys, cursor } = useContext();
788879

@@ -802,6 +893,7 @@ export default class IRenderable {
802893
this.setViewport(getViewport());
803894
}
804895

896+
this.focus();
805897
this.show();
806898
let key = await readkey(true);
807899
switch (key) {
@@ -814,10 +906,10 @@ export default class IRenderable {
814906
}
815907
break;
816908
case consolekeys.up:
817-
this._nominal.scrollX = Math.max(0, this._nominal.scrollX - 1);
909+
this.scrollUp();
818910
break;
819911
case consolekeys.down:
820-
this._nominal.scrollX = Math.min(this.lastBuffer.length - this.getViewport().height, this._nominal.scrollX + 1);
912+
this.scrollDown();
821913
break;
822914
}
823915
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import IRenderable from "./IRenderable.js";
2+
3+
export default class IRenderableWithChildren extends IRenderable {
4+
children = [];
5+
6+
renderChildren() {
7+
return this.children.map(child => child.invokeRender());
8+
}
9+
10+
getChildrenSizes() {
11+
return this.children.map(child => {
12+
return {
13+
height: child.lastBuffer.length,
14+
width: child._calculateRealWidth
15+
}
16+
});
17+
}
18+
}

libraries/src/gui/grid2.js

+57-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,70 @@
11
import chalk from "chalk";
2-
import IRenderable from "./IRenderable.js";
2+
import IRenderableWithChildren from "./IRenderableWithChildren.js";
3+
import { repeat, s } from "./saffronUtils.js";
34

4-
export default class Grid extends IRenderable {
5+
export default class Grid extends IRenderableWithChildren {
56
constructor() {
6-
super();
7+
super("Grid");
78
}
89

10+
columns = 3;
11+
rows = 3;
12+
gapX = 2;
13+
gapY = 1;
14+
915
render() {
1016
let { width, height } = this.getSize();
1117

12-
this.fillOnce("Hello world");
18+
// Grid
19+
/*
20+
First we get the widths of all the children and heights.
21+
Next we need to see how many objects can fit in the grid horizontally and vertically.
22+
Then we print
23+
*/
24+
25+
let cBuffers = this.renderChildren();
26+
let cSizes = this.getChildrenSizes();
27+
28+
let cellWidth = Math.floor((width - (this.columns - 1) * this.gapX) / this.columns);
29+
let cellHeight = Math.floor((height - (this.rows - 1) * this.gapY) / this.rows);
30+
31+
for (let row = 0; row < this.rows; row++) {
32+
for (let rowHeight = 0; rowHeight < cellHeight; rowHeight++) {
33+
let tempBuffer = [];
34+
for (let col = 0; col < this.columns; col++) {
35+
let tempStringBuffer = "";
36+
37+
let _cellWidth = cellWidth;
38+
39+
if (col === this.columns - 1) {
40+
// this is last, check and make sure the column widths + gaps are equal to the width of the grid
41+
let totalWidth = cellWidth * this.columns + this.gapX * (this.columns - 1);
42+
let remainingWidth = width - totalWidth;
43+
_cellWidth += remainingWidth;
44+
}
45+
46+
//tempStringBuffer += s("", _cellWidth, "A");
47+
repeat(_cellWidth, () => {
48+
tempStringBuffer += chalk.bgYellow("A");
49+
});
50+
51+
52+
if (col !== this.columns - 1) {
53+
tempStringBuffer += " ".repeat(this.gapX);
54+
}
1355

14-
for (let i = 0; i < height * 3; i++) {
15-
this.fillOnce("H".repeat(width));
56+
tempBuffer.push(tempStringBuffer);
57+
}
58+
this.fillOnce(tempBuffer.join(""));
59+
}
60+
if (row !== this.rows - 1) {
61+
for (let gap = 0; gap < this.gapY; gap++) {
62+
this.fillOnce(" ");
63+
}
64+
}
1665
}
1766

18-
//this.useTransform(chalk.strikethrough);
67+
//this.fillOnce(largestLength.toString());
68+
//this.fillOnce(widths.join(","));
1969
}
2070
}

libraries/src/gui/saffronUtils.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { getContext } from "../context.js";
22

3-
export function s(str, wid) {
3+
export function s(str, wid, char) {
44
useRealLength();
55
let length = str.realLength();
66
useRealLength(false);
7-
return str + " ".repeat(Math.max(0, wid - length));
7+
return str + (char ?? " ").repeat(Math.max(0, wid - length));
88
}
99

1010
export function useRealLength(value = true) {
@@ -17,6 +17,16 @@ export function useRealLength(value = true) {
1717
}
1818
}
1919

20+
export function repeat(times, func) {
21+
for (let i = 0; i < times; i++) {
22+
func({
23+
index: i,
24+
first: i == 0,
25+
last: i == times - 1
26+
});
27+
}
28+
}
29+
2030
export function __EXPERIMENTAL__cutEnd(str, length) {
2131
let parts = str.split(/(?=\x1b[a-zA-Z0-9\[\];]*m)/g);
2232
let requiredLength = length;

0 commit comments

Comments
 (0)