add color style and polish input field

This commit is contained in:
2025-10-12 13:02:56 +08:00
parent e4731a2cef
commit 069196dfbb
7 changed files with 340 additions and 173 deletions

View File

@@ -33,8 +33,9 @@ function getTextContent(node: UIObject): string {
*
* @param node - The node to draw
* @param focused - Whether this node has focus
* @param cursorBlinkState - Whether the cursor should be visible (for blinking)
*/
function drawNode(node: UIObject, focused: boolean): void {
function drawNode(node: UIObject, focused: boolean, cursorBlinkState: boolean): void {
if (!node.layout) return;
const { x, y, width } = node.layout;
@@ -43,6 +44,10 @@ function drawNode(node: UIObject, focused: boolean): void {
const [origX, origY] = term.getCursorPos();
try {
// Default colors that can be overridden by styleProps
let textColor = node.styleProps.textColor;
const bgColor = node.styleProps.backgroundColor;
switch (node.type) {
case "label":
case "h1":
@@ -50,17 +55,21 @@ function drawNode(node: UIObject, focused: boolean): void {
case "h3": {
const text = getTextContent(node);
// Set colors based on heading level
if (node.type === "h1") {
term.setTextColor(colors.yellow);
} else if (node.type === "h2") {
term.setTextColor(colors.orange);
} else if (node.type === "h3") {
term.setTextColor(colors.lightGray);
} else {
term.setTextColor(colors.white);
// Set colors based on heading level (if not overridden by styleProps)
if (textColor === undefined) {
if (node.type === "h1") {
textColor = colors.yellow;
} else if (node.type === "h2") {
textColor = colors.orange;
} else if (node.type === "h3") {
textColor = colors.lightGray;
} else {
textColor = colors.white;
}
}
term.setBackgroundColor(colors.black);
term.setTextColor(textColor);
term.setBackgroundColor(bgColor ?? colors.black);
term.setCursorPos(x, y);
term.write(text.substring(0, width));
@@ -70,13 +79,13 @@ function drawNode(node: UIObject, focused: boolean): void {
case "button": {
const text = getTextContent(node);
// Set colors based on focus
// Set colors based on focus (if not overridden by styleProps)
if (focused) {
term.setTextColor(colors.black);
term.setBackgroundColor(colors.yellow);
term.setTextColor(textColor ?? colors.black);
term.setBackgroundColor(bgColor ?? colors.yellow);
} else {
term.setTextColor(colors.white);
term.setBackgroundColor(colors.gray);
term.setTextColor(textColor ?? colors.white);
term.setBackgroundColor(bgColor ?? colors.gray);
}
term.setCursorPos(x, y);
@@ -96,11 +105,11 @@ function drawNode(node: UIObject, focused: boolean): void {
}
if (focused) {
term.setTextColor(colors.black);
term.setBackgroundColor(colors.white);
term.setTextColor(textColor ?? colors.black);
term.setBackgroundColor(bgColor ?? colors.white);
} else {
term.setTextColor(colors.white);
term.setBackgroundColor(colors.black);
term.setTextColor(textColor ?? colors.white);
term.setBackgroundColor(bgColor ?? colors.black);
}
term.setCursorPos(x, y);
@@ -112,33 +121,71 @@ function drawNode(node: UIObject, focused: boolean): void {
if (typeof valueProp === "function") {
value = (valueProp as Accessor<string>)();
}
const placeholder = node.props.placeholder as string | undefined;
const cursorPos = node.cursorPos ?? 0;
let displayText = value;
if (value === "" && placeholder !== undefined) {
let currentTextColor = textColor;
let showPlaceholder = false;
const focusedBgColor = bgColor ?? colors.white;
const unfocusedBgColor = bgColor ?? colors.black;
if (value === "" && placeholder !== undefined && !focused) {
displayText = placeholder;
term.setTextColor(colors.gray);
showPlaceholder = true;
currentTextColor = currentTextColor ?? colors.gray;
} else if (focused) {
term.setTextColor(colors.black);
currentTextColor = currentTextColor ?? colors.black;
} else {
term.setTextColor(colors.white);
currentTextColor = currentTextColor ?? colors.white;
}
if (focused) {
term.setBackgroundColor(colors.white);
} else {
term.setBackgroundColor(colors.black);
}
// Set background and clear the input area, creating a 1-character padding on the left
term.setBackgroundColor(focused ? focusedBgColor : unfocusedBgColor);
term.setCursorPos(x, y);
// Pad or truncate to fit width
if (displayText.length > width) {
displayText = displayText.substring(0, width);
} else {
displayText = displayText.padEnd(width, " ");
term.write(" ".repeat(width));
term.setTextColor(currentTextColor);
term.setCursorPos(x + 1, y); // Position cursor for text after padding
const renderWidth = width - 1;
let textToRender = displayText;
// Truncate text if it's too long for the padded area
if (textToRender.length > renderWidth) {
textToRender = textToRender.substring(0, renderWidth);
}
if (focused && !showPlaceholder && cursorBlinkState) {
// Draw text with a block cursor by inverting colors at the cursor position
for (let i = 0; i < textToRender.length; i++) {
const char = textToRender.substring(i, i + 1);
if (i === cursorPos) {
// Invert colors for cursor
term.setBackgroundColor(currentTextColor);
term.setTextColor(focusedBgColor);
term.write(char);
// Restore colors
term.setBackgroundColor(focusedBgColor);
term.setTextColor(currentTextColor);
} else {
term.write(char);
}
}
// Draw cursor at the end of the text if applicable
if (cursorPos === textToRender.length && cursorPos < renderWidth) {
term.setBackgroundColor(currentTextColor);
term.setTextColor(focusedBgColor);
term.write(" ");
// Restore colors
term.setBackgroundColor(focusedBgColor);
term.setTextColor(currentTextColor);
}
} else {
// Not focused or no cursor, just write the text
term.write(textToRender);
}
term.write(displayText);
}
break;
}
@@ -147,7 +194,16 @@ function drawNode(node: UIObject, focused: boolean): void {
case "form":
case "for":
case "show": {
// Container elements don't draw themselves, just their children
// Container elements may have background colors
if (bgColor !== undefined && node.layout !== undefined) {
const { x: divX, y: divY, width: divWidth, height: divHeight } = node.layout;
term.setBackgroundColor(bgColor);
// Fill the background area
for (let row = 0; row < divHeight; row++) {
term.setCursorPos(divX, divY + row);
term.write(string.rep(" ", divWidth));
}
}
break;
}
@@ -158,8 +214,8 @@ function drawNode(node: UIObject, focused: boolean): void {
? (node.textContent)()
: node.textContent;
term.setTextColor(colors.white);
term.setBackgroundColor(colors.black);
term.setTextColor(textColor ?? colors.white);
term.setBackgroundColor(bgColor ?? colors.black);
term.setCursorPos(x, y);
term.write(text.substring(0, width));
}
@@ -177,15 +233,16 @@ function drawNode(node: UIObject, focused: boolean): void {
*
* @param node - The root node to render
* @param focusedNode - The currently focused node (if any)
* @param cursorBlinkState - Whether the cursor should be visible (for blinking)
*/
export function render(node: UIObject, focusedNode?: UIObject): void {
export function render(node: UIObject, focusedNode?: UIObject, cursorBlinkState = false): void {
// Draw this node
const isFocused = node === focusedNode;
drawNode(node, isFocused);
drawNode(node, isFocused, cursorBlinkState);
// Recursively draw children
for (const child of node.children) {
render(child, focusedNode);
render(child, focusedNode, cursorBlinkState);
}
}