Files
ViperOS/vpsdk/vpui.vp
T

1520 lines
63 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import vpsdk.window as window
import vpsdk.process as process
import vpsdk.syscall as _syscall
from stdint import *
import t, c
import string
define TITLE_H = 32
define MAX_WIDGETS = 24
define CHAR_W = 8
define CHAR_H = 16
define WIDGET_LABEL = 1
define WIDGET_BUTTON = 2
define WIDGET_ENTRY = 3
define WIDGET_TEXT = 4
define WIDGET_SCROLLBAR = 5
define WIDGET_FBCANVAS = 6
define VERTICAL = 1
define HORIZONTAL = 2
define TEXT_MAX_LINES = 200
define PACK_TOP = 1
define PACK_BOTTOM = 2
define PACK_LEFT = 3
define PACK_RIGHT = 4
define FILL_NONE = 0
define FILL_X = 1
define FILL_Y = 2
define FILL_BOTH = 3
define STICKY_NONE = 0
define STICKY_W = 1
define STICKY_E = 2
define STICKY_N = 4
define STICKY_S = 8
define STICKY_NSEW = 15
_g_wl_type: t.CArray[int, MAX_WIDGETS]
_g_wl_side: t.CArray[int, MAX_WIDGETS]
_g_wl_fill: t.CArray[int, MAX_WIDGETS]
_g_wl_expand: t.CArray[int, MAX_WIDGETS]
_g_wl_padx: t.CArray[int, MAX_WIDGETS]
_g_wl_pady: t.CArray[int, MAX_WIDGETS]
_g_wl_row: t.CArray[int, MAX_WIDGETS]
_g_wl_col: t.CArray[int, MAX_WIDGETS]
_g_wl_rowspan: t.CArray[int, MAX_WIDGETS]
_g_wl_colspan: t.CArray[int, MAX_WIDGETS]
_g_wl_sticky: t.CArray[int, MAX_WIDGETS]
_g_layout_init: int
_hex_tbl: t.CArray[char, 17] = "0123456789ABCDEF"
_hex_buf: t.CArray[char, 20] = "0000000000000000000"
def _serial_puts(s: const str):
_syscall._syscall1(uint64(_syscall.SERIAL_PUTS), uint64(s))
def _ptr_to_hex(val: uint64, buf: t.CPtr):
buf[0] = 48
buf[1] = 120
i: int = 15
while i >= 0:
nibble: int = int(val & uint64(0xF))
buf[2 + i] = _hex_tbl[nibble]
val = val >> 4
i -= 1
buf[18] = 0
def _fill_rect(fb: UINT32PTR, fw: int, fh: int, x1: int, y1: int, w: int, h: int, color: uint32):
if not fb: return
if x1 < 0: w += x1; x1 = 0
if y1 < 0: h += y1; y1 = 0
if w <= 0 or h <= 0: return
for py in range(y1, y1 + h):
if py >= 0 and py < fh:
for px in range(x1, x1 + w):
if px >= 0 and px < fw:
fb[py * fw + px] = color
def _draw_hline(fb: UINT32PTR, fw: int, fh: int, x1: int, y: int, w: int, color: uint32):
if not fb: return
if y < 0 or y >= fh: return
for px in range(x1, x1 + w):
if px >= 0 and px < fw:
fb[y * fw + px] = color
def _draw_vline(fb: UINT32PTR, fw: int, fh: int, x: int, y1: int, h: int, color: uint32):
if not fb: return
if x < 0 or x >= fw: return
for py in range(y1, y1 + h):
if py >= 0 and py < fh:
fb[py * fw + x] = color
def _lighten(color: uint32, amount: int) -> uint32:
r: int = int(color & 0xFF)
g: int = int((color >> 8) & 0xFF)
b: int = int((color >> 16) & 0xFF)
r = r + ((255 - r) * amount) / 256
g = g + ((255 - g) * amount) / 256
b = b + ((255 - b) * amount) / 256
return uint32(r | (g << 8) | (b << 16))
def _darken(color: uint32, amount: int) -> uint32:
r: int = int(color & 0xFF)
g: int = int((color >> 8) & 0xFF)
b: int = int((color >> 16) & 0xFF)
r = r - (r * amount) / 256
g = g - (g * amount) / 256
b = b - (b * amount) / 256
return uint32(r | (g << 8) | (b << 16))
font: t.CArray[t.CArray[uint8, 16], 256] = [
{0x00,0x00,0x00,0x00,0x00,0x40,0x55,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x02,0x56,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x42,0x56,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x55,0x55,0x57,0x71,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x54,0x57,0x71,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x54,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x51,0x52,0x72,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x55,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x55,0x57,0x71,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x56,0x51,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x54,0x54,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x50,0x50,0x57,0x74,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x51,0x51,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x57,0x55,0x57,0x74,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x53,0x54,0x56,0x74,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x15,0x15,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x32,0x16,0x12,0x12,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x20,0x6E,0x22,0x2E,0x28,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x11,0x13,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x35,0x15,0x17,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x14,0x17,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x14,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x11,0x12,0x12,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x15,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x15,0x17,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x36,0x11,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x34,0x14,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x30,0x10,0x17,0x14,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x31,0x11,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x37,0x15,0x17,0x14,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x33,0x14,0x16,0x14,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00},
{0x6C,0x6C,0x6C,0x6C,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x36,0x36,0x36,0x7F,0x7F,0x36,0x36,0x36,0x7F,0x7F,0x36,0x36,0x36,0x00,0x00,0x00},
{0x0C,0x0C,0x3E,0x7F,0x68,0x68,0x3E,0x0B,0x0B,0x7F,0x3E,0x18,0x18,0x00,0x00,0x00},
{0x60,0x60,0x66,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x66,0x06,0x06,0x00,0x00,0x00},
{0x38,0x6C,0x6C,0x6C,0x6C,0x6C,0x38,0x6C,0x6D,0x66,0x66,0x6F,0x3B,0x00,0x00,0x00},
{0x18,0x18,0x18,0x18,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x1C,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x1C,0x00,0x00},
{0x38,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x38,0x00,0x00},
{0x00,0x00,0x18,0x18,0x7E,0x3C,0x3C,0x3C,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x18,0x18,0x18,0x7E,0x7E,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x30,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00},
{0x00,0x00,0x06,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x6E,0x6E,0x7E,0x76,0x76,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x18,0x38,0x78,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x06,0x06,0x0E,0x1C,0x38,0x70,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x06,0x06,0x1C,0x1C,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x0C,0x0C,0x1C,0x1C,0x3C,0x2C,0x6C,0x7E,0x7E,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x00},
{0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x06,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x7E,0x7E,0x06,0x06,0x0C,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x66,0x7E,0x3C,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x30,0x00},
{0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x7E,0x7E,0x00,0x00,0x7E,0x7E,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x40,0x60,0x30,0x18,0x0C,0x06,0x06,0x0C,0x18,0x30,0x60,0x40,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x6E,0x6A,0x6A,0x6A,0x6E,0x60,0x60,0x7C,0x3C,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x66,0x7E,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x7C,0x7E,0x66,0x66,0x66,0x7C,0x7C,0x66,0x66,0x66,0x66,0x7E,0x7C,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x78,0x7C,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0x7C,0x78,0x00,0x00,0x00},
{0x7E,0x7E,0x60,0x60,0x60,0x7C,0x7C,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00},
{0x7E,0x7E,0x60,0x60,0x60,0x7C,0x7C,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x60,0x60,0x60,0x6E,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x66,0x7E,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00},
{0x3E,0x3E,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x6C,0x7C,0x38,0x00,0x00,0x00},
{0x66,0x66,0x66,0x6C,0x6C,0x78,0x78,0x78,0x6C,0x6C,0x66,0x66,0x66,0x00,0x00,0x00},
{0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00},
{0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x76,0x76,0x7E,0x6E,0x6E,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x6A,0x6A,0x6C,0x7E,0x36,0x00,0x00,0x00},
{0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x6C,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x3C,0x7E,0x66,0x60,0x60,0x78,0x3C,0x0E,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x18,0x00,0x00,0x00},
{0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x77,0x77,0x22,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x3C,0x3C,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x66,0x66,0x66,0x66,0x7E,0x3C,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00},
{0x7E,0x7E,0x06,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00},
{0x78,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78,0x78,0x00,0x00},
{0xC0,0xC0,0x60,0x60,0x30,0x30,0x18,0x0C,0x0C,0x06,0x06,0x03,0x03,0x00,0x00,0x00},
{0x1E,0x1E,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x1E,0x1E,0x00,0x00},
{0x3C,0x7E,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00},
{0x30,0x38,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3C,0x3E,0x06,0x3E,0x7E,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00},
{0x60,0x60,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x7C,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x60,0x60,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x06,0x06,0x06,0x06,0x3E,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x7E,0x7E,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x1C,0x3E,0x30,0x30,0x30,0x7C,0x7C,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3E,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x3E,0x3C,0x00},
{0x60,0x60,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x18,0x18,0x00,0x00,0x38,0x38,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00},
{0x0C,0x0C,0x00,0x00,0x3C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C,0x78,0x00},
{0x60,0x60,0x60,0x60,0x66,0x66,0x6C,0x78,0x78,0x6C,0x6C,0x66,0x66,0x00,0x00,0x00},
{0x38,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x60,0x60,0x60,0x60,0x00},
{0x00,0x00,0x00,0x00,0x3E,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x07,0x07,0x00},
{0x00,0x00,0x00,0x00,0x6C,0x7E,0x76,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x3C,0x7E,0x62,0x70,0x3C,0x0E,0x46,0x7E,0x3C,0x00,0x00,0x00},
{0x30,0x30,0x30,0x30,0x7C,0x7C,0x30,0x30,0x30,0x30,0x30,0x3C,0x1C,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x18,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x7F,0x36,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x66,0x66,0x3C,0x3C,0x18,0x3C,0x3C,0x66,0x66,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x7E,0x7C,0x00},
{0x00,0x00,0x00,0x00,0x7E,0x7E,0x0C,0x0C,0x18,0x30,0x30,0x7E,0x7E,0x00,0x00,0x00},
{0x0E,0x1E,0x18,0x18,0x18,0x18,0x30,0x30,0x18,0x18,0x18,0x18,0x1E,0x0E,0x00,0x00},
{0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00},
{0x70,0x78,0x18,0x18,0x18,0x18,0x0C,0x0C,0x18,0x18,0x18,0x18,0x78,0x70,0x00,0x00},
{0x31,0x79,0x6B,0x4F,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x13,0x24,0x26,0x24,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
]
def _draw_char(buf: UINT32PTR, bw: int, bh: int, x: int, y: int, ch: char, color: uint32):
if not buf: return
if bw <= 0 or bh <= 0: return
ci: uint8 = uint8(ch)
if ci >= 128: ci = 0
fy: int = 0
while fy < CHAR_H:
if y + fy < 0 or y + fy >= bh:
fy += 1
continue
row: uint8 = font[ci][fy]
if row == 0:
fy += 1
continue
for fx in range(CHAR_W):
if row & (0x80 >> fx):
px: int = x + fx
if px >= 0 and px < bw:
buf[(y + fy) * bw + px] = color
fy += 1
def _draw_text(buf: UINT32PTR, bw: int, bh: int, x: int, y: int, s: const str, color: uint32):
if not buf: return
if bw <= 0 or bh <= 0: return
cx: int = x
cy: int = y
i: int = 0
while True:
ch: char = s[i]
if ch == 0: break
if ch == 10:
cy += 16
cx = x
else:
_draw_char(buf, bw, bh, cx, cy, ch, color)
cx += 8
i += 1
def _register_widget(master: t.CPtr, widget: Widget*) -> int:
tk: Tk* = t.CType(master, Tk, t.CPtr)
idx: int = tk.widget_count
if idx < MAX_WIDGETS:
tk.widgets[idx] = widget
tk.widget_count = idx + 1
return idx
virtual class Widget:
x: int
y: int
w: int
h: int
visible: int
master: t.CPtr
widget_type: int
focused: int
hovered: int
def draw(self, fb: UINT32PTR, fw: int, fh: int):
pass
def handle_event(self, evt: window.InputEvent*) -> int:
return 0
def contains(self, px: int, py: int) -> int:
if px >= self.x and px < self.x + self.w and py >= self.y and py < self.y + self.h:
return 1
return 0
def place(self, x: int = 0, y: int = 0, w: int = 0, h: int = 0):
self.x = x
self.y = y
if w > 0:
self.w = w
if h > 0:
self.h = h
virtual class Label(Widget):
text: t.CArray[char, 64]
color: uint32
def __init__(self, master: t.CPtr, text: str, color: uint32):
self.x = 0
self.y = 0
self.w = 0
self.h = 0
self.visible = 1
self.master = master
self.widget_type = WIDGET_LABEL
self.focused = 0
self.hovered = 0
self.color = color
i: int = 0
for ch in text:
if i >= 63: break
self.text[i] = ch
i += 1
self.text[i] = 0
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
_draw_text(fb, fw, fh, self.x, self.y, &self.text[0], self.color)
def handle_event(self, evt: window.InputEvent*) -> int:
return 0
def config(self, text: const str = None, color: uint32 = None):
if text is not None:
i: int = 0
while i < 63:
ch: char = *(text + i)
self.text[i] = ch
if ch == 0: break
i += 1
self.text[i] = 0
if color is not None:
self.color = color
virtual class Button(Widget):
text: t.CArray[char, 32]
fill_color: uint32
text_color: uint32
pressed: int
callback: t.CPtr
def __init__(self, master: t.CPtr, w: int, h: int, text: str, callback: t.CPtr):
self.x = 0
self.y = 0
self.w = w
self.h = h
self.visible = 1
self.master = master
self.widget_type = WIDGET_BUTTON
self.focused = 0
self.hovered = 0
self.fill_color = uint32(0x3C3C5A)
self.text_color = uint32(0xFFFFFFFF)
self.pressed = 0
self.callback = callback
i: int = 0
for ch in text:
if i >= 31: break
self.text[i] = ch
i += 1
self.text[i] = 0
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
fill: uint32 = self.fill_color
border: uint32 = uint32(0x8080B0)
if self.pressed:
fill = _lighten(self.fill_color, 60)
border = uint32(0xA0A0D0)
elif self.hovered:
fill = _lighten(self.fill_color, 30)
border = uint32(0xA0A0D0)
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, fill)
top_left: uint32 = _lighten(fill, 80)
bottom_right: uint32 = _darken(fill, 80)
if self.pressed:
top_left = _darken(fill, 60)
bottom_right = _lighten(fill, 40)
_draw_hline(fb, fw, fh, self.x, self.y, self.w, top_left)
_draw_hline(fb, fw, fh, self.x, self.y + 1, self.w, top_left)
_draw_vline(fb, fw, fh, self.x, self.y, self.h, top_left)
_draw_vline(fb, fw, fh, self.x + 1, self.y, self.h, top_left)
_draw_hline(fb, fw, fh, self.x, self.y + self.h - 1, self.w, bottom_right)
_draw_hline(fb, fw, fh, self.x, self.y + self.h - 2, self.w, bottom_right)
_draw_vline(fb, fw, fh, self.x + self.w - 1, self.y, self.h, bottom_right)
_draw_vline(fb, fw, fh, self.x + self.w - 2, self.y, self.h, bottom_right)
_draw_hline(fb, fw, fh, self.x - 1, self.y - 1, self.w + 2, border)
_draw_hline(fb, fw, fh, self.x - 1, self.y + self.h, self.w + 2, border)
_draw_vline(fb, fw, fh, self.x - 1, self.y - 1, self.h + 2, border)
_draw_vline(fb, fw, fh, self.x + self.w, self.y - 1, self.h + 2, border)
tl: int = int(string.strlen(&self.text[0]))
tx: int = self.x + (self.w - 8 * tl) / 2
ty: int = self.y + (self.h - CHAR_H) / 2
_draw_text(fb, fw, fh, tx, ty, &self.text[0], self.text_color)
def handle_event(self, evt: window.InputEvent*) -> int:
etype: uint32 = evt.type
ex: int = evt.x
ey: int = evt.y
if etype == window.EVENT_TYPE_HOVER:
if self.contains(ex, ey):
if not self.hovered:
self.hovered = 1
return 1
else:
if self.hovered and not self.pressed:
self.hovered = 0
return 1
elif etype == window.EVENT_TYPE_HOVER_LEAVE:
if self.hovered:
self.hovered = 0
return 1
elif etype == window.EVENT_TYPE_CLICK:
if self.contains(ex, ey):
self.pressed = 1
return 1
elif etype == window.EVENT_TYPE_RELEASE:
if self.pressed:
self.pressed = 0
if self.contains(ex, ey) and self.callback is not None:
# 闭包调用约定(VPC 生成):callback 是堆上的 {fn, env} 对象,fn 期望 rdi = env。
# 之前直接 `call r13`(r13 = 闭包对象地址)会跳到堆上执行
# (现场:rip=0x2040400a 在堆区、cr2=0x1、err=0x5)。
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(self.callback, t.ASM_DESCR.REG_ANY)}
mov rdi, qword ptr [r13 + 8]
mov r12, rsp
and rsp, -16
call qword ptr [r13]
mov rsp, r12
pop r13
pop r12""",
op=[t.ASM_DESCR.CLOBBER_MEMORY,
t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX,
t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_RDI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
return 1
return 0
def config(self, text: const str = None, fill_color: uint32 = None, text_color: uint32 = None, callback: t.CPtr = None):
if text is not None:
i: int = 0
while i < 31:
ch: char = *(text + i)
self.text[i] = ch
if ch == 0: break
i += 1
self.text[i] = 0
if fill_color is not None:
self.fill_color = fill_color
if text_color is not None:
self.text_color = text_color
if callback is not None:
self.callback = callback
virtual class Entry(Widget):
text: t.CArray[char, 64]
text_len: int
cursor_pos: int
max_len: int
bg_color: uint32
border_color: uint32
text_color: uint32
cursor_color: uint32
def __init__(self, master: t.CPtr, w: int, h: int):
self.x = 0
self.y = 0
self.w = w
self.h = h
self.visible = 1
self.master = master
self.widget_type = WIDGET_ENTRY
self.focused = 0
self.hovered = 0
self.text_len = 0
self.cursor_pos = 0
self.max_len = 30
self.bg_color = uint32(0xFF231919)
self.border_color = uint32(0x606080)
self.text_color = uint32(0x000000)
self.cursor_color = uint32(0x000000)
for i in range(64):
self.text[i] = 0
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
bg: uint32 = self.bg_color
bdr: uint32 = self.border_color
if self.focused:
bg = uint32(0xFFFFFF)
bdr = uint32(0x4488FF)
elif self.hovered:
bdr = uint32(0x8080B0)
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, bg)
_draw_hline(fb, fw, fh, self.x, self.y, self.w, bdr)
_draw_hline(fb, fw, fh, self.x, self.y + self.h - 1, self.w, bdr)
_draw_vline(fb, fw, fh, self.x, self.y, self.h, bdr)
_draw_vline(fb, fw, fh, self.x + self.w - 1, self.y, self.h, bdr)
if self.text_len > 0:
tc: uint32 = self.text_color
if not self.focused:
tc = uint32(0xCCCCCC)
_draw_text(fb, fw, fh, self.x + 6, self.y + 7, &self.text[0], tc)
elif not self.focused:
_draw_text(fb, fw, fh, self.x + 6, self.y + 7, "Type here...", uint32(0x888888))
if self.focused:
cx_pos: int = self.x + 6 + self.cursor_pos * CHAR_W
if cx_pos < self.x + self.w - 2:
_draw_vline(fb, fw, fh, cx_pos, self.y + 5, self.h - 10, self.cursor_color)
def handle_event(self, evt: window.InputEvent*) -> int:
etype: uint32 = evt.type
ex: int = evt.x
ey: int = evt.y
if etype == window.EVENT_TYPE_HOVER:
if self.contains(ex, ey):
if not self.hovered:
self.hovered = 1
tk: Tk* = t.CType(self.master, Tk, t.CPtr)
window.set_cursor_type(tk.win_id, window.CURSOR_IBEAM)
return 1
else:
if self.hovered:
self.hovered = 0
tk2: Tk* = t.CType(self.master, Tk, t.CPtr)
window.set_cursor_type(tk2.win_id, window.CURSOR_DEFAULT)
return 1
elif etype == window.EVENT_TYPE_HOVER_LEAVE:
if self.hovered:
self.hovered = 0
tk3: Tk* = t.CType(self.master, Tk, t.CPtr)
window.set_cursor_type(tk3.win_id, window.CURSOR_DEFAULT)
return 1
elif etype == window.EVENT_TYPE_CLICK:
if self.contains(ex, ey):
if not self.focused:
self.focused = 1
tk4: Tk* = t.CType(self.master, Tk, t.CPtr)
tk4.focused_widget = Widget*(self)
rel_x: int = ex - self.x - 6
if rel_x < 0: rel_x = 0
new_pos: int = (rel_x + CHAR_W / 2) / CHAR_W
if new_pos > self.text_len: new_pos = self.text_len
self.cursor_pos = new_pos
return 1
else:
if self.focused:
self.focused = 0
tk5: Tk* = t.CType(self.master, Tk, t.CPtr)
tk5.focused_widget = None
return 1
elif etype == window.EVENT_TYPE_KEY:
if self.focused:
ebtn: uint8 = evt.button
if ebtn == window.KEY_STATE_RELEASE:
return 0
ch: int = ex
if ch == 8:
if self.cursor_pos > 0:
for i in range(self.cursor_pos, self.text_len):
self.text[i - 1] = self.text[i]
self.text_len -= 1
self.cursor_pos -= 1
self.text[self.text_len] = 0
return 1
elif ch == window.VK_LEFT:
if self.cursor_pos > 0:
self.cursor_pos -= 1
return 1
elif ch == window.VK_RIGHT:
if self.cursor_pos < self.text_len:
self.cursor_pos += 1
return 1
elif ch == window.VK_HOME:
self.cursor_pos = 0
return 1
elif ch == window.VK_END:
self.cursor_pos = self.text_len
return 1
elif ch == window.VK_DELETE:
if self.cursor_pos < self.text_len:
for i in range(self.cursor_pos, self.text_len - 1):
self.text[i] = self.text[i + 1]
self.text_len -= 1
self.text[self.text_len] = 0
return 1
elif ch >= 32 and ch < 127:
if self.text_len < self.max_len:
i: int = self.text_len
while i > self.cursor_pos:
self.text[i] = self.text[i - 1]
i -= 1
self.text[self.cursor_pos] = char(ch)
self.text_len += 1
self.cursor_pos += 1
self.text[self.text_len] = 0
return 1
return 0
def get(self) -> const str:
return &self.text[0]
def delete(self, start: int, end: int):
self.text_len = 0
for i in range(64):
self.text[i] = 0
def config(self, bg_color: uint32 = None, text_color: uint32 = None, border_color: uint32 = None):
if bg_color is not None:
self.bg_color = bg_color
if text_color is not None:
self.text_color = text_color
if border_color is not None:
self.border_color = border_color
virtual class Scrollbar(Widget):
orient: int
command: t.CPtr
slider_first: int
slider_last: int
action: int
action_value: int
pressed: int
dragging: int
drag_start_y: int
drag_start_first: int
trough_color: uint32
slider_color: uint32
arrow_color: uint32
slider_hover: int
def __init__(self, master: t.CPtr, w: int, h: int, orient: int):
self.x = 0
self.y = 0
self.w = w
self.h = h
self.visible = 1
self.master = master
self.widget_type = WIDGET_SCROLLBAR
self.focused = 0
self.hovered = 0
self.orient = orient
self.command = None
self.slider_first = 0
self.slider_last = 1000
self.action = 0
self.action_value = 0
self.pressed = 0
self.dragging = 0
self.drag_start_y = 0
self.drag_start_first = 0
self.trough_color = uint32(0x1E1E2E)
self.slider_color = uint32(0x4A4A6A)
self.arrow_color = uint32(0xAAAAAA)
self.slider_hover = 0
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, self.trough_color)
if self.orient != VERTICAL: return
arrow_sz: int = self.w
if arrow_sz > 16: arrow_sz = 16
self._draw_arrow_btn(fb, fw, fh, self.x, self.y, self.w, arrow_sz, 0, 1 if self.pressed == 1 else 0)
self._draw_arrow_btn(fb, fw, fh, self.x, self.y + self.h - arrow_sz, self.w, arrow_sz, 1, 1 if self.pressed == 2 else 0)
trough_y: int = self.y + arrow_sz
trough_h: int = self.h - 2 * arrow_sz
if trough_h <= 0: return
if self.slider_last > self.slider_first:
sl_h: int = (self.slider_last - self.slider_first) * trough_h / 1000
if sl_h < 8: sl_h = 8
sl_y: int = trough_y + self.slider_first * trough_h / 1000
if sl_y + sl_h > trough_y + trough_h:
sl_y = trough_y + trough_h - sl_h
if sl_y < trough_y: sl_y = trough_y
color: uint32 = self.slider_color
if self.dragging:
color = _lighten(self.slider_color, 40)
elif self.slider_hover:
color = _lighten(self.slider_color, 20)
_fill_rect(fb, fw, fh, self.x + 1, sl_y, self.w - 2, sl_h, color)
top_c: uint32 = _lighten(color, 60)
bot_c: uint32 = _darken(color, 60)
_draw_hline(fb, fw, fh, self.x + 1, sl_y, self.w - 2, top_c)
_draw_hline(fb, fw, fh, self.x + 1, sl_y + sl_h - 1, self.w - 2, bot_c)
_draw_vline(fb, fw, fh, self.x + 1, sl_y, sl_h, top_c)
_draw_vline(fb, fw, fh, self.x + self.w - 2, sl_y, sl_h, bot_c)
def _draw_arrow_btn(self, fb: UINT32PTR, fw: int, fh: int, bx: int, by: int, bw: int, bh: int, direction: int, is_pressed: int):
color: uint32 = self.slider_color
if is_pressed:
color = _lighten(self.slider_color, 30)
_fill_rect(fb, fw, fh, bx, by, bw, bh, color)
top_c: uint32 = _lighten(color, 60)
bot_c: uint32 = _darken(color, 60)
if is_pressed:
top_c = _darken(color, 40)
bot_c = _lighten(color, 30)
_draw_hline(fb, fw, fh, bx, by, bw, top_c)
_draw_hline(fb, fw, fh, bx, by + bh - 1, bw, bot_c)
_draw_vline(fb, fw, fh, bx, by, bh, top_c)
_draw_vline(fb, fw, fh, bx + bw - 1, by, bh, bot_c)
cx: int = bx + bw / 2
cy: int = by + bh / 2
if direction == 0:
_fill_rect(fb, fw, fh, cx - 1, cy - 3, 3, 1, self.arrow_color)
_fill_rect(fb, fw, fh, cx - 2, cy - 2, 5, 1, self.arrow_color)
_fill_rect(fb, fw, fh, cx - 3, cy - 1, 7, 1, self.arrow_color)
else:
_fill_rect(fb, fw, fh, cx - 3, cy - 1, 7, 1, self.arrow_color)
_fill_rect(fb, fw, fh, cx - 2, cy, 5, 1, self.arrow_color)
_fill_rect(fb, fw, fh, cx - 1, cy + 1, 3, 1, self.arrow_color)
def handle_event(self, evt: window.InputEvent*) -> int:
etype: uint32 = evt.type
ex: int = evt.x
ey: int = evt.y
if etype == window.EVENT_TYPE_HOVER:
if self.dragging:
self._handle_drag(ey)
return 1
if self.contains(ex, ey):
if not self.slider_hover:
self.slider_hover = 1
return 1
else:
if self.slider_hover and self.pressed == 0:
self.slider_hover = 0
return 1
return 0
elif etype == window.EVENT_TYPE_HOVER_LEAVE:
if self.slider_hover:
self.slider_hover = 0
if not self.dragging:
self.pressed = 0
return 1
return 0
elif etype == window.EVENT_TYPE_CLICK:
if self.contains(ex, ey):
if self.orient == VERTICAL:
arrow_sz: int = self.w
if arrow_sz > 16: arrow_sz = 16
if ey < self.y + arrow_sz:
self.pressed = 1
self.action = 1
self._fire_command()
return 1
elif ey >= self.y + self.h - arrow_sz:
self.pressed = 2
self.action = 2
self._fire_command()
return 1
else:
trough_y: int = self.y + arrow_sz
trough_h: int = self.h - 2 * arrow_sz
if trough_h <= 0: return 1
sl_h: int = (self.slider_last - self.slider_first) * trough_h / 1000
if sl_h < 8: sl_h = 8
sl_y: int = trough_y + self.slider_first * trough_h / 1000
if sl_y + sl_h > trough_y + trough_h:
sl_y = trough_y + trough_h - sl_h
if ey >= sl_y and ey < sl_y + sl_h:
self.pressed = 4
self.dragging = 1
self.drag_start_y = ey
self.drag_start_first = self.slider_first
else:
self.pressed = 3
if ey < sl_y:
self.action = 3
else:
self.action = 4
self._fire_command()
return 1
return 0
elif etype == window.EVENT_TYPE_RELEASE:
if self.pressed:
self.pressed = 0
self.dragging = 0
return 1
return 0
return 0
def _handle_drag(self, ey: int):
arrow_sz: int = self.w
if arrow_sz > 16: arrow_sz = 16
trough_y: int = self.y + arrow_sz
trough_h: int = self.h - 2 * arrow_sz
if trough_h <= 0: return
delta_y: int = ey - self.drag_start_y
delta_first: int = delta_y * 1000 / trough_h
new_first: int = self.drag_start_first + delta_first
slider_size: int = self.slider_last - self.slider_first
if slider_size <= 0: slider_size = 1
if new_first < 0: new_first = 0
if new_first + slider_size > 1000: new_first = 1000 - slider_size
if new_first != self.slider_first:
self.slider_first = new_first
self.slider_last = new_first + slider_size
self.action = 5
self.action_value = new_first
self._fire_command()
def _fire_command(self):
if self.command is not None:
# 同 Button.handle_event:command 是闭包 {fn, env},必须 rdi = env 后 call fn。
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(self.command, t.ASM_DESCR.REG_ANY)}
mov rdi, qword ptr [r13 + 8]
mov r12, rsp
and rsp, -16
call qword ptr [r13]
mov rsp, r12
pop r13
pop r12""",
op=[t.ASM_DESCR.CLOBBER_MEMORY,
t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX,
t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_RDI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
def set(self, first: int, last: int):
self.slider_first = first
self.slider_last = last
virtual class FBCanvas(Widget):
_fb: UINT32PTR
_cw: int
_ch: int
def __init__(self, master: t.CPtr, w: int, h: int):
self.x = 0
self.y = 0
self.w = w
self.h = h
self.visible = 1
self.master = master
self.widget_type = WIDGET_FBCANVAS
self.focused = 0
self.hovered = 0
self._fb = None
self._cw = w
self._ch = h
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
if not self._fb: return
if self._cw <= 0 or self._ch <= 0: return
copy_w: int = self._cw
if copy_w > self.w: copy_w = self.w
copy_h: int = self._ch
if copy_h > self.h: copy_h = self.h
for py in range(copy_h):
ty: int = self.y + py
if ty < 0 or ty >= fh: continue
for px in range(copy_w):
tx: int = self.x + px
if tx < 0 or tx >= fw: continue
fb[ty * fw + tx] = self._fb[py * self._cw + px]
def handle_event(self, evt: window.InputEvent*) -> int:
return 0
def set_fb(self, fb: UINT32PTR, cw: int, ch: int):
self._fb = fb
self._cw = cw
self._ch = ch
def get_fb(self) -> UINT32PTR:
return self._fb
def get_w(self) -> int:
return self._cw
def get_h(self) -> int:
return self._ch
virtual class Text(Widget):
lines: t.CArray[t.CArray[char, 80], TEXT_MAX_LINES]
line_count: int
input_buf: t.CArray[char, 80]
input_len: int
cursor_pos: int
bg_color: uint32
text_color: uint32
prompt_color: uint32
cursor_color: uint32
border_color: uint32
on_command: t.CPtr
history: t.CArray[t.CArray[char, 80], 16]
history_count: int
history_idx: int
scroll_offset: int
max_lines: int
yscrollbar: t.CPtr
def __init__(self, master: t.CPtr, w: int, h: int):
self.x = 0
self.y = 0
self.w = w
self.h = h
self.visible = 1
self.master = master
self.widget_type = WIDGET_TEXT
self.focused = 0
self.hovered = 0
self.line_count = 0
self.input_len = 0
self.cursor_pos = 0
self.bg_color = uint32(0x0C0C0C)
self.text_color = uint32(0xCCCCCC)
self.prompt_color = uint32(0x00FF00)
self.cursor_color = uint32(0x00FF00)
self.border_color = uint32(0x333333)
self.on_command = None
self.history_count = 0
self.history_idx = 0
self.scroll_offset = 0
self.max_lines = TEXT_MAX_LINES
self.yscrollbar = None
for i in range(TEXT_MAX_LINES):
for j in range(79 + 1):
self.lines[i][j] = 0
for i in range(80):
self.input_buf[i] = 0
_register_widget(master, Widget*(self))
def draw(self, fb: UINT32PTR, fw: int, fh: int):
self._update_scrollbar()
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, self.bg_color)
_fill_rect(fb, fw, fh, self.x, self.y + self.h - 2, self.w, 2, self.border_color)
visible: int = self._visible_lines()
for i in range(visible):
line_idx: int = self.scroll_offset + i
if line_idx < self.line_count:
_draw_text(fb, fw, fh, self.x + 4, self.y + i * CHAR_H, &self.lines[line_idx][0], self.text_color)
prompt_y: int = self.y + self.h - CHAR_H - 4
_draw_text(fb, fw, fh, self.x + 4, prompt_y, "> ", self.prompt_color)
if self.input_len > 0:
_draw_text(fb, fw, fh, self.x + 4 + 2 * CHAR_W, prompt_y, &self.input_buf[0], uint32(0xFFFFFF))
cx_pos: int = self.x + 4 + (2 + self.cursor_pos) * CHAR_W
if cx_pos < self.x + self.w - 4:
_fill_rect(fb, fw, fh, cx_pos, prompt_y, CHAR_W // 2, CHAR_H, self.cursor_color)
def handle_event(self, evt: window.InputEvent*) -> int:
etype: uint32 = evt.type
ex: int = evt.x
ey: int = evt.y
if etype == window.EVENT_TYPE_CLICK:
if self.contains(ex, ey):
if not self.focused:
self.focused = 1
tk: Tk* = t.CType(self.master, Tk, t.CPtr)
tk.focused_widget = Widget*(self)
prompt_y: int = self.y + self.h - CHAR_H - 4
if ey >= prompt_y and ey < prompt_y + CHAR_H:
rel_x: int = ex - self.x - 4 - 2 * CHAR_W
if rel_x < 0: rel_x = 0
new_pos: int = (rel_x + CHAR_W / 2) / CHAR_W
if new_pos > self.input_len: new_pos = self.input_len
self.cursor_pos = new_pos
return 1
else:
if self.focused:
self.focused = 0
tk2: Tk* = t.CType(self.master, Tk, t.CPtr)
tk2.focused_widget = None
return 1
elif etype == window.EVENT_TYPE_KEY:
if self.focused:
ebtn2: uint8 = evt.button
if ebtn2 == window.KEY_STATE_RELEASE:
return 0
ch: int = ex
if ch == 8:
if self.cursor_pos > 0:
i: int = self.cursor_pos
while i < self.input_len:
self.input_buf[i - 1] = self.input_buf[i]
i += 1
self.input_len -= 1
self.cursor_pos -= 1
self.input_buf[self.input_len] = 0
return 1
elif ch == 13:
self._exec_input()
self.input_len = 0
self.cursor_pos = 0
for i in range(80):
self.input_buf[i] = 0
return 1
elif ch == window.VK_LEFT:
if self.cursor_pos > 0:
self.cursor_pos -= 1
return 1
elif ch == window.VK_RIGHT:
if self.cursor_pos < self.input_len:
self.cursor_pos += 1
return 1
elif ch == window.VK_UP:
if self.history_count > 0:
if self.history_idx < self.history_count:
self.history_idx += 1
hi: int = self.history_count - self.history_idx
if hi < 0: hi = 0
for j in range(80):
self.input_buf[j] = self.history[hi][j]
self.input_len = 0
while self.input_len < 79 and self.input_buf[self.input_len] != 0:
self.input_len += 1
self.cursor_pos = self.input_len
return 1
elif ch == window.VK_DOWN:
if self.history_count > 0:
if self.history_idx > 0:
self.history_idx -= 1
if self.history_idx == 0:
self.input_len = 0
self.cursor_pos = 0
for j2 in range(80):
self.input_buf[j2] = 0
else:
hi2: int = self.history_count - self.history_idx
for j3 in range(80):
self.input_buf[j3] = self.history[hi2][j3]
self.input_len = 0
while self.input_len < 79 and self.input_buf[self.input_len] != 0:
self.input_len += 1
self.cursor_pos = self.input_len
return 1
elif ch == window.VK_HOME:
self.cursor_pos = 0
return 1
elif ch == window.VK_END:
self.cursor_pos = self.input_len
return 1
elif ch == window.VK_DELETE:
if self.cursor_pos < self.input_len:
i: int = self.cursor_pos
while i < self.input_len - 1:
self.input_buf[i] = self.input_buf[i + 1]
i += 1
self.input_len -= 1
self.input_buf[self.input_len] = 0
return 1
elif ch == window.VK_PRIOR:
if self.scroll_offset > 0:
vis: int = self._visible_lines()
self.scroll_offset -= vis
if self.scroll_offset < 0: self.scroll_offset = 0
self._update_scrollbar()
return 1
elif ch == window.VK_NEXT:
vis2: int = self._visible_lines()
max_off: int = self.line_count - vis2
if max_off < 0: max_off = 0
if self.scroll_offset < max_off:
self.scroll_offset += vis2
if self.scroll_offset > max_off: self.scroll_offset = max_off
self._update_scrollbar()
return 1
elif ch >= 32 and ch < 127:
if self.input_len < 79:
i: int = self.input_len
while i > self.cursor_pos:
self.input_buf[i] = self.input_buf[i - 1]
i -= 1
self.input_buf[self.cursor_pos] = char(ch)
self.input_len += 1
self.cursor_pos += 1
self.input_buf[self.input_len] = 0
return 1
return 0
def _exec_input(self):
self.insert("> ")
if self.input_len > 0:
last: int = self.line_count - 1
j: int = 0
while j < self.input_len and j < 77:
self.lines[last][2 + j] = self.input_buf[j]
j += 1
self.lines[last][2 + j] = 0
if self.history_count < 16:
for k in range(80):
self.history[self.history_count][k] = self.input_buf[k]
self.history_count += 1
else:
for i in range(15):
for k2 in range(80):
self.history[i][k2] = self.history[i + 1][k2]
for k3 in range(80):
self.history[15][k3] = self.input_buf[k3]
self.history_idx = 0
cmd: t.CPtr = self.on_command
if cmd is not None:
# 同 Button.handle_event:on_command 是闭包 {fn, env},必须 rdi = env 后 call fn。
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(cmd, t.ASM_DESCR.REG_ANY)}
mov rdi, qword ptr [r13 + 8]
mov r12, rsp
and rsp, -16
call qword ptr [r13]
mov rsp, r12
pop r13
pop r12""",
op=[t.ASM_DESCR.CLOBBER_MEMORY,
t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX,
t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_RDI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
else:
self.insert("(no callback)")
def insert(self, text: str):
start: int = 0
pos: int = 0
while True:
ch: char = *(text + pos)
if ch == 0:
if pos > start:
self._insert_substring(text, start, pos)
break
if ch == 10:
self._insert_substring(text, start, pos)
start = pos + 1
pos += 1
def insert_ptr(self, text: const str):
start: int = 0
pos: int = 0
while True:
ch: char = *(text + pos)
if ch == 0:
if pos > start:
self._insert_substring(text, start, pos)
break
if ch == 10:
self._insert_substring(text, start, pos)
start = pos + 1
pos += 1
def _insert_substring(self, text: const str, start: int, end: int):
if self.line_count >= self.max_lines:
for i in range(self.max_lines - 1):
for j in range(79 + 1):
self.lines[i][j] = self.lines[i + 1][j]
self.line_count -= 1
if self.scroll_offset > 0:
self.scroll_offset -= 1
idx: int = self.line_count
j: int = 0
k: int = start
while k < end and j < 79:
ch: char = *(text + k)
if ch == 9:
tab_stop: int = 8 - (j % 8)
while tab_stop > 0 and j < 79:
self.lines[idx][j] = 32
j += 1
tab_stop -= 1
elif ch == 13:
pass
else:
self.lines[idx][j] = ch
j += 1
k += 1
self.lines[idx][j] = 0
self.line_count += 1
vis: int = self._visible_lines()
if self.line_count > vis:
self.scroll_offset = self.line_count - vis
else:
self.scroll_offset = 0
self._update_scrollbar()
def delete(self, start: int, end: int):
self.line_count = 0
self.scroll_offset = 0
self._update_scrollbar()
def get(self) -> const str:
return &self.input_buf[0]
def config(self, text_color: uint32 = None, bg_color: uint32 = None, prompt_color: uint32 = None, cursor_color: uint32 = None, border_color: uint32 = None):
if text_color is not None:
self.text_color = text_color
if bg_color is not None:
self.bg_color = bg_color
if prompt_color is not None:
self.prompt_color = prompt_color
if cursor_color is not None:
self.cursor_color = cursor_color
if border_color is not None:
self.border_color = border_color
def _visible_lines(self) -> int:
return (self.h - CHAR_H - 4) / CHAR_H
def _update_scrollbar(self):
vis: int = self._visible_lines()
if self.line_count <= vis:
self.scroll_offset = 0
else:
max_off: int = self.line_count - vis
if self.scroll_offset > max_off:
self.scroll_offset = max_off
if self.yscrollbar is not None:
sb: Scrollbar* = t.CType(self.yscrollbar, Scrollbar, t.CPtr)
if self.line_count <= vis:
sb.set(0, 1000)
else:
first: int = self.scroll_offset * 1000 / self.line_count
last: int = (self.scroll_offset + vis) * 1000 / self.line_count
sb.set(first, last)
@t.Object
class Tk:
win_id: int
buf: UINT32PTR
bw: int
bh: int
style: int
bg_color: uint32
widgets: t.CArray[Widget*, MAX_WIDGETS]
widget_count: int
focused_widget: Widget*
_win_x: int
_win_y: int
_title: t.CArray[char, 32]
resizable_w: int
resizable_h: int
_grid_rows: int
_grid_cols: int
def __init__(self):
global _g_layout_init
self.widget_count = 0
self.focused_widget = None
self.win_id = -1
self._win_x = 0
self._win_y = 0
self.style = 0
self.bg_color = uint32(0xFF231919)
self.resizable_w = 1
self.resizable_h = 1
self._grid_rows = 0
self._grid_cols = 0
if _g_layout_init == 0:
for i in range(MAX_WIDGETS):
_g_wl_type[i] = 0
_g_wl_side[i] = 0
_g_wl_fill[i] = 0
_g_wl_expand[i] = 0
_g_wl_padx[i] = 0
_g_wl_pady[i] = 0
_g_wl_row[i] = 0
_g_wl_col[i] = 0
_g_wl_rowspan[i] = 0
_g_wl_colspan[i] = 1
_g_wl_sticky[i] = 0
_g_layout_init = 1
for j in range(32):
self._title[j] = 0
def pack(self, idx: int, side: int, fill: int, expand: int, padx: int, pady: int):
_g_wl_type[idx] = 2
_g_wl_side[idx] = side
_g_wl_fill[idx] = fill
_g_wl_expand[idx] = expand
_g_wl_padx[idx] = padx
_g_wl_pady[idx] = pady
def grid(self, idx: int, row: int, col: int, rowspan: int, colspan: int, sticky: int):
_g_wl_type[idx] = 3
_g_wl_row[idx] = row
_g_wl_col[idx] = col
_g_wl_rowspan[idx] = rowspan
_g_wl_colspan[idx] = colspan
_g_wl_sticky[idx] = sticky
def title(self, text: str):
i: int = 0
while i < 31:
ch: char = text[i]
if ch == 0: break
self._title[i] = ch
i += 1
self._title[i] = 0
if self.win_id >= 0:
window.set_title(self.win_id, &self._title[0])
def set_style(self, s: int):
self.style = s
def resizable(self, rw: int, rh: int):
self.resizable_w = rw
self.resizable_h = rh
if self.win_id >= 0:
window.set_resizable(self.win_id, rw, rh)
def geometry(self, x: int, y: int, w: int, h: int):
self._win_x = x
self._win_y = y
self.bw = w
self.bh = h - TITLE_H - 2
if self.win_id < 0:
self.win_id = window.create_window(x, y, w, h, &self._title[0], self.style)
self.buf = window.get_winbuf(self.win_id)
window.set_resizable(self.win_id, self.resizable_w, self.resizable_h)
else:
window.set_geometry(self.win_id, x, y, w, h)
self.buf = window.get_winbuf(self.win_id)
def setup(self, x: int, y: int, w: int, h: int, title: const str, style: int):
self._win_x = x
self._win_y = y
self.win_id = window.create_window(x, y, w, h, title, style)
self.buf = window.get_winbuf(self.win_id)
self.bw = w
self.bh = h - TITLE_H - 2
self.style = style
self.bg_color = uint32(0xFF231919)
def redraw(self):
self._do_layout()
self.buf = window.get_winbuf(self.win_id)
sz: int = window.get_win_size(self.win_id)
if sz != 0:
self.bw = sz >> 16
self.bh = sz & 0xFFFF
if not self.buf: return
if self.bw <= 0 or self.bh <= 0: return
_fill_rect(self.buf, self.bw, self.bh, 0, 0, self.bw, self.bh, self.bg_color)
for i in range(self.widget_count):
w: Widget* = self.widgets[i]
if w.visible:
w.draw(self.buf, self.bw, self.bh)
window.flush(self.win_id)
def _do_layout(self):
self._grid_rows = 0
self._grid_cols = 0
cav_x: int = 0
cav_y: int = 0
cav_w: int = self.bw
cav_h: int = self.bh
for i in range(self.widget_count):
w: Widget* = self.widgets[i]
lt: int = _g_wl_type[i]
if lt == 2:
side: int = _g_wl_side[i]
fill: int = _g_wl_fill[i]
expand: int = _g_wl_expand[i]
px: int = _g_wl_padx[i]
py: int = _g_wl_pady[i]
ww: int = w.w
wh: int = w.h
if side == PACK_TOP:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
if expand:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.x = cav_x + px
w.y = cav_y + py
w.w = ww
w.h = wh
cav_y += wh + 2 * py
cav_h -= wh + 2 * py
elif side == PACK_BOTTOM:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
if expand:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.x = cav_x + px
w.y = cav_y + cav_h - wh - py
w.w = ww
w.h = wh
cav_h -= wh + 2 * py
elif side == PACK_LEFT:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
if expand:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + px
w.y = cav_y + py
w.w = ww
w.h = wh
cav_x += ww + 2 * px
cav_w -= ww + 2 * px
elif side == PACK_RIGHT:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
if expand:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + cav_w - ww - px
w.y = cav_y + py
w.w = ww
w.h = wh
cav_w -= ww + 2 * px
elif lt == 3:
r: int = _g_wl_row[i] + _g_wl_rowspan[i]
c: int = _g_wl_col[i] + _g_wl_colspan[i]
if r > self._grid_rows: self._grid_rows = r
if c > self._grid_cols: self._grid_cols = c
for i in range(self.widget_count):
w: Widget* = self.widgets[i]
if _g_wl_type[i] == 3:
if self._grid_rows <= 0: self._grid_rows = 1
if self._grid_cols <= 0: self._grid_cols = 1
cell_w: int = self.bw / self._grid_cols
cell_h: int = self.bh / self._grid_rows
wx: int = _g_wl_col[i] * cell_w
wy: int = _g_wl_row[i] * cell_h
ww: int = _g_wl_colspan[i] * cell_w
wh: int = _g_wl_rowspan[i] * cell_h
sticky: int = _g_wl_sticky[i]
if sticky == 0:
ox: int = (ww - w.w) / 2
oy: int = (wh - w.h) / 2
w.x = wx + ox
w.y = wy + oy
else:
w.x = wx
w.y = wy
if (sticky & 1) != 0:
w.w = ww
if (sticky & 2) != 0:
w.h = wh
def update(self):
self.redraw()
def winfo_exists(self) -> int:
if self.win_id < 0: return 0
return window.is_active(self.win_id)
def mainloop(self):
self.redraw()
evt: window.InputEvent
while window.is_active(self.win_id):
result: int = window.poll_event(&evt)
if result != 0:
if evt.win_id == self.win_id:
need_redraw: int = 0
etype: uint32 = evt.type
if etype == window.EVENT_TYPE_PING:
window.flush(self.win_id)
elif etype == window.EVENT_TYPE_RESIZE:
self.buf = window.get_winbuf(self.win_id)
sz: int = window.get_win_size(self.win_id)
self.bw = sz >> 16
self.bh = sz & 0xFFFF
need_redraw = 1
elif etype == window.EVENT_TYPE_KEY or etype == window.EVENT_TYPE_KEY_RELEASE:
if self.focused_widget is not None:
handled: int = self.focused_widget.handle_event(&evt)
if handled: need_redraw = 1
else:
if etype == window.EVENT_TYPE_HOVER:
i: int = self.widget_count - 1
while i >= 0:
w: Widget* = self.widgets[i]
if w.visible:
handled2: int = w.handle_event(&evt)
if handled2:
need_redraw = 1
break
i -= 1
else:
for i in range(self.widget_count):
w: Widget* = self.widgets[i]
if w.visible:
handled2: int = w.handle_event(&evt)
if handled2: need_redraw = 1
if need_redraw:
self.redraw()
else:
process.yield_cpu()