Files
ViperOS/vpsdk/vpui.py
2026-07-19 12:38:20 +08:00

1557 lines
64 KiB
Python

import vpsdk.window as window
import vpsdk.process as process
import vpsdk.syscall as _syscall
from stdint import *
import t, c
import string
TITLE_H: t.CDefine = 32
MAX_WIDGETS: t.CDefine = 24
CHAR_W: t.CDefine = 8
CHAR_H: t.CDefine = 16
WIDGET_LABEL: t.CDefine = 1
WIDGET_BUTTON: t.CDefine = 2
WIDGET_ENTRY: t.CDefine = 3
WIDGET_TEXT: t.CDefine = 4
WIDGET_SCROLLBAR: t.CDefine = 5
WIDGET_FBCANVAS: t.CDefine = 6
VERTICAL: t.CDefine = 1
HORIZONTAL: t.CDefine = 2
TEXT_MAX_LINES: t.CDefine = 200
PACK_TOP: t.CDefine = 1
PACK_BOTTOM: t.CDefine = 2
PACK_LEFT: t.CDefine = 3
PACK_RIGHT: t.CDefine = 4
FILL_NONE: t.CDefine = 0
FILL_X: t.CDefine = 1
FILL_Y: t.CDefine = 2
FILL_BOTH: t.CDefine = 3
STICKY_NONE: t.CDefine = 0
STICKY_W: t.CDefine = 1
STICKY_E: t.CDefine = 2
STICKY_N: t.CDefine = 4
STICKY_S: t.CDefine = 8
STICKY_NSEW: t.CDefine = 15
_g_wl_type: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_side: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_fill: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_expand: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_padx: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_pady: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_row: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_col: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_rowspan: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_colspan: t.CArray[t.CInt, MAX_WIDGETS]
_g_wl_sticky: t.CArray[t.CInt, MAX_WIDGETS]
_g_layout_init: t.CInt
_hex_tbl: t.CArray[t.CChar, 17] = "0123456789ABCDEF"
_hex_buf: t.CArray[t.CChar, 20] = "0000000000000000000"
def _serial_puts(s: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
_syscall._syscall1(t.CUInt64T(_syscall.SERIAL_PUTS), t.CUInt64T(s))
def _ptr_to_hex(val: t.CUInt64T, buf: t.CVoid | t.CPtr) -> t.CVoid:
buf[0] = 48
buf[1] = 120
i: t.CInt = 15
while i >= 0:
nibble: t.CInt = t.CInt(val & t.CUInt64T(0xF))
buf[2 + i] = _hex_tbl[nibble]
val = val >> 4
i -= 1
buf[18] = 0
def _fill_rect(fb: UINT32PTR, fw: t.CInt, fh: t.CInt, x1: t.CInt, y1: t.CInt, w: t.CInt, h: t.CInt, color: t.CUInt32T):
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: t.CInt, fh: t.CInt, x1: t.CInt, y: t.CInt, w: t.CInt, color: t.CUInt32T):
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: t.CInt, fh: t.CInt, x: t.CInt, y1: t.CInt, h: t.CInt, color: t.CUInt32T):
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: t.CUInt32T, amount: t.CInt) -> t.CUInt32T:
r: t.CInt = t.CInt(color & 0xFF)
g: t.CInt = t.CInt((color >> 8) & 0xFF)
b: t.CInt = t.CInt((color >> 16) & 0xFF)
r = r + ((255 - r) * amount) / 256
g = g + ((255 - g) * amount) / 256
b = b + ((255 - b) * amount) / 256
return t.CUInt32T(r | (g << 8) | (b << 16))
def _darken(color: t.CUInt32T, amount: t.CInt) -> t.CUInt32T:
r: t.CInt = t.CInt(color & 0xFF)
g: t.CInt = t.CInt((color >> 8) & 0xFF)
b: t.CInt = t.CInt((color >> 16) & 0xFF)
r = r - (r * amount) / 256
g = g - (g * amount) / 256
b = b - (b * amount) / 256
return t.CUInt32T(r | (g << 8) | (b << 16))
font: t.CArray[t.CArray[t.CUInt8T, 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: t.CInt, bh: t.CInt, x: t.CInt, y: t.CInt, ch: t.CChar, color: t.CUInt32T):
if not buf: return
if bw <= 0 or bh <= 0: return
ci: t.CUInt8T = t.CUInt8T(ch)
if ci >= 128: ci = 0
fy: t.CInt = 0
while fy < CHAR_H:
if y + fy < 0 or y + fy >= bh:
fy += 1
continue
row: t.CUInt8T = font[ci][fy]
if row == 0:
fy += 1
continue
fx: t.CInt = 0
while fx < CHAR_W:
if row & (0x80 >> fx):
px: t.CInt = x + fx
if px >= 0 and px < bw:
buf[(y + fy) * bw + px] = color
fx += 1
fy += 1
def _draw_text(buf: UINT32PTR, bw: t.CInt, bh: t.CInt, x: t.CInt, y: t.CInt, s: t.CConst | str, color: t.CUInt32T):
if not buf: return
if bw <= 0 or bh <= 0: return
cx: t.CInt = x
cy: t.CInt = y
i: t.CInt = 0
while True:
ch: t.CChar = 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.CVoid | t.CPtr, widget: Widget | t.CPtr) -> t.CInt:
tk: Tk | t.CPtr = t.CType(master, Tk, t.CPtr)
idx: t.CInt = tk.widget_count
if idx < MAX_WIDGETS:
tk.widgets[idx] = widget
tk.widget_count = idx + 1
return idx
@t.Object
@t.CVTable
class Widget:
x: t.CInt
y: t.CInt
w: t.CInt
h: t.CInt
visible: t.CInt
master: t.CVoid | t.CPtr
widget_type: t.CInt
focused: t.CInt
hovered: t.CInt
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
pass
def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt:
return 0
def contains(self, px: t.CInt, py: t.CInt) -> t.CInt:
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: t.CInt = 0, y: t.CInt = 0, w: t.CInt = 0, h: t.CInt = 0):
self.x = x
self.y = y
if w > 0:
self.w = w
if h > 0:
self.h = h
@t.Object
@t.CVTable
class Label(Widget):
text: t.CArray[t.CChar, 64]
color: t.CUInt32T
def __init__(self, master: t.CVoid | t.CPtr, text: str, color: t.CUInt32T):
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: t.CInt = 0
for ch in text:
if i >= 63: break
self.text[i] = ch
i += 1
self.text[i] = 0
_register_widget(master, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
_draw_text(fb, fw, fh, self.x, self.y, c.Addr(self.text[0]), self.color)
def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt:
return 0
def config(self, text: t.CConst | t.CChar | t.CPtr = None, color: t.CUInt32T = None):
if text is not None:
i: t.CInt = 0
while i < 63:
ch: t.CChar = c.Deref(text + i)
self.text[i] = ch
if ch == 0: break
i += 1
self.text[i] = 0
if color is not None:
self.color = color
@t.Object
@t.CVTable
class Button(Widget):
text: t.CArray[t.CChar, 32]
fill_color: t.CUInt32T
text_color: t.CUInt32T
pressed: t.CInt
callback: t.CVoid | t.CPtr
def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt, text: str, callback: t.CVoid | 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 = t.CUInt32T(0x3C3C5A)
self.text_color = t.CUInt32T(0xFFFFFFFF)
self.pressed = 0
self.callback = callback
i: t.CInt = 0
for ch in text:
if i >= 31: break
self.text[i] = ch
i += 1
self.text[i] = 0
_register_widget(master, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
fill: t.CUInt32T = self.fill_color
border: t.CUInt32T = t.CUInt32T(0x8080B0)
if self.pressed:
fill = _lighten(self.fill_color, 60)
border = t.CUInt32T(0xA0A0D0)
elif self.hovered:
fill = _lighten(self.fill_color, 30)
border = t.CUInt32T(0xA0A0D0)
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, fill)
top_left: t.CUInt32T = _lighten(fill, 80)
bottom_right: t.CUInt32T = _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: t.CInt = string.strlen(c.Addr(self.text[0]))
tx: t.CInt = self.x + (self.w - 8 * tl) / 2
ty: t.CInt = self.y + (self.h - CHAR_H) / 2
_draw_text(fb, fw, fh, tx, ty, c.Addr(self.text[0]), self.text_color)
def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt:
etype: t.CUInt32T = evt.type
ex: t.CInt = evt.x
ey: t.CInt = 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:
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(self.callback, t.ASM_DESCR.REG_ANY)}
mov r12, rsp
and rsp, -16
call 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: t.CConst | t.CChar | t.CPtr = None, fill_color: t.CUInt32T = None, text_color: t.CUInt32T = None, callback: t.CVoid | t.CPtr = None):
if text is not None:
i: t.CInt = 0
while i < 31:
ch: t.CChar = c.Deref(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
@t.Object
@t.CVTable
class Entry(Widget):
text: t.CArray[t.CChar, 64]
text_len: t.CInt
cursor_pos: t.CInt
max_len: t.CInt
bg_color: t.CUInt32T
border_color: t.CUInt32T
text_color: t.CUInt32T
cursor_color: t.CUInt32T
def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt):
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 = t.CUInt32T(0xFF231919)
self.border_color = t.CUInt32T(0x606080)
self.text_color = t.CUInt32T(0x000000)
self.cursor_color = t.CUInt32T(0x000000)
for i in range(64):
self.text[i] = 0
_register_widget(master, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
bg: t.CUInt32T = self.bg_color
bdr: t.CUInt32T = self.border_color
if self.focused:
bg = t.CUInt32T(0xFFFFFF)
bdr = t.CUInt32T(0x4488FF)
elif self.hovered:
bdr = t.CUInt32T(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: t.CUInt32T = self.text_color
if not self.focused:
tc = t.CUInt32T(0xCCCCCC)
_draw_text(fb, fw, fh, self.x + 6, self.y + 7, c.Addr(self.text[0]), tc)
elif not self.focused:
_draw_text(fb, fw, fh, self.x + 6, self.y + 7, "Type here...", t.CUInt32T(0x888888))
if self.focused:
cx_pos: t.CInt = 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 | t.CPtr) -> t.CInt:
etype: t.CUInt32T = evt.type
ex: t.CInt = evt.x
ey: t.CInt = evt.y
if etype == window.EVENT_TYPE_HOVER:
if self.contains(ex, ey):
if not self.hovered:
self.hovered = 1
tk: Tk | t.CPtr = 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.CPtr = 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.CPtr = 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.CPtr = t.CType(self.master, Tk, t.CPtr)
tk4.focused_widget = t.CType(self, Widget, t.CPtr)
rel_x: t.CInt = ex - self.x - 6
if rel_x < 0: rel_x = 0
new_pos: t.CInt = (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.CPtr = t.CType(self.master, Tk, t.CPtr)
tk5.focused_widget = None
return 1
elif etype == window.EVENT_TYPE_KEY:
if self.focused:
ebtn: t.CUInt8T = evt.button
if ebtn == window.KEY_STATE_RELEASE:
return 0
ch: t.CInt = 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: t.CInt = self.text_len
while i > self.cursor_pos:
self.text[i] = self.text[i - 1]
i -= 1
self.text[self.cursor_pos] = t.CChar(ch)
self.text_len += 1
self.cursor_pos += 1
self.text[self.text_len] = 0
return 1
return 0
def get(self) -> t.CConst | t.CChar | t.CPtr:
return c.Addr(self.text[0])
def delete(self, start: t.CInt, end: t.CInt):
self.text_len = 0
for i in range(64):
self.text[i] = 0
def config(self, bg_color: t.CUInt32T = None, text_color: t.CUInt32T = None, border_color: t.CUInt32T = 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
@t.Object
@t.CVTable
class Scrollbar(Widget):
orient: t.CInt
command: t.CVoid | t.CPtr
slider_first: t.CInt
slider_last: t.CInt
action: t.CInt
action_value: t.CInt
pressed: t.CInt
dragging: t.CInt
drag_start_y: t.CInt
drag_start_first: t.CInt
trough_color: t.CUInt32T
slider_color: t.CUInt32T
arrow_color: t.CUInt32T
slider_hover: t.CInt
def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt, orient: t.CInt):
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 = t.CUInt32T(0x1E1E2E)
self.slider_color = t.CUInt32T(0x4A4A6A)
self.arrow_color = t.CUInt32T(0xAAAAAA)
self.slider_hover = 0
_register_widget(master, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
_fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, self.trough_color)
if self.orient != VERTICAL: return
arrow_sz: t.CInt = 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: t.CInt = self.y + arrow_sz
trough_h: t.CInt = self.h - 2 * arrow_sz
if trough_h <= 0: return
if self.slider_last > self.slider_first:
sl_h: t.CInt = (self.slider_last - self.slider_first) * trough_h / 1000
if sl_h < 8: sl_h = 8
sl_y: t.CInt = 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: t.CUInt32T = 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: t.CUInt32T = _lighten(color, 60)
bot_c: t.CUInt32T = _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: t.CInt, fh: t.CInt, bx: t.CInt, by: t.CInt, bw: t.CInt, bh: t.CInt, direction: t.CInt, is_pressed: t.CInt):
color: t.CUInt32T = self.slider_color
if is_pressed:
color = _lighten(self.slider_color, 30)
_fill_rect(fb, fw, fh, bx, by, bw, bh, color)
top_c: t.CUInt32T = _lighten(color, 60)
bot_c: t.CUInt32T = _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: t.CInt = bx + bw / 2
cy: t.CInt = 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 | t.CPtr) -> t.CInt:
etype: t.CUInt32T = evt.type
ex: t.CInt = evt.x
ey: t.CInt = 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: t.CInt = 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: t.CInt = self.y + arrow_sz
trough_h: t.CInt = self.h - 2 * arrow_sz
if trough_h <= 0: return 1
sl_h: t.CInt = (self.slider_last - self.slider_first) * trough_h / 1000
if sl_h < 8: sl_h = 8
sl_y: t.CInt = 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: t.CInt):
arrow_sz: t.CInt = self.w
if arrow_sz > 16: arrow_sz = 16
trough_y: t.CInt = self.y + arrow_sz
trough_h: t.CInt = self.h - 2 * arrow_sz
if trough_h <= 0: return
delta_y: t.CInt = ey - self.drag_start_y
delta_first: t.CInt = delta_y * 1000 / trough_h
new_first: t.CInt = self.drag_start_first + delta_first
slider_size: t.CInt = 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:
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(self.command, t.ASM_DESCR.REG_ANY)}
mov r12, rsp
and rsp, -16
call 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: t.CInt, last: t.CInt):
self.slider_first = first
self.slider_last = last
@t.Object
@t.CVTable
class FBCanvas(Widget):
_fb: UINT32PTR
_cw: t.CInt
_ch: t.CInt
def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt):
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, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
if not self._fb: return
if self._cw <= 0 or self._ch <= 0: return
copy_w: t.CInt = self._cw
if copy_w > self.w: copy_w = self.w
copy_h: t.CInt = self._ch
if copy_h > self.h: copy_h = self.h
for py in range(copy_h):
ty: t.CInt = self.y + py
if ty < 0 or ty >= fh: continue
for px in range(copy_w):
tx: t.CInt = 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 | t.CPtr) -> t.CInt:
return 0
def set_fb(self, fb: UINT32PTR, cw: t.CInt, ch: t.CInt):
self._fb = fb
self._cw = cw
self._ch = ch
def get_fb(self) -> UINT32PTR:
return self._fb
def get_w(self) -> t.CInt:
return self._cw
def get_h(self) -> t.CInt:
return self._ch
@t.Object
@t.CVTable
class Text(Widget):
lines: t.CArray[t.CArray[t.CChar, 80], TEXT_MAX_LINES]
line_count: t.CInt
input_buf: t.CArray[t.CChar, 80]
input_len: t.CInt
cursor_pos: t.CInt
bg_color: t.CUInt32T
text_color: t.CUInt32T
prompt_color: t.CUInt32T
cursor_color: t.CUInt32T
border_color: t.CUInt32T
on_command: t.CVoid | t.CPtr
history: t.CArray[t.CArray[t.CChar, 80], 16]
history_count: t.CInt
history_idx: t.CInt
scroll_offset: t.CInt
max_lines: t.CInt
yscrollbar: t.CVoid | t.CPtr
def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt):
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 = t.CUInt32T(0x0C0C0C)
self.text_color = t.CUInt32T(0xCCCCCC)
self.prompt_color = t.CUInt32T(0x00FF00)
self.cursor_color = t.CUInt32T(0x00FF00)
self.border_color = t.CUInt32T(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
i: t.CInt = 0
while i < TEXT_MAX_LINES:
j: t.CInt = 0
while j <= 79:
self.lines[i][j] = 0
j += 1
i += 1
i = 0
while i < 80:
self.input_buf[i] = 0
i += 1
_register_widget(master, t.CType(self, Widget, t.CPtr))
def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt):
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: t.CInt = self._visible_lines()
for i in range(visible):
line_idx: t.CInt = self.scroll_offset + i
if line_idx < self.line_count:
_draw_text(fb, fw, fh, self.x + 4, self.y + i * CHAR_H, c.Addr(self.lines[line_idx][0]), self.text_color)
prompt_y: t.CInt = 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, c.Addr(self.input_buf[0]), t.CUInt32T(0xFFFFFF))
cx_pos: t.CInt = 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 | t.CPtr) -> t.CInt:
etype: t.CUInt32T = evt.type
ex: t.CInt = evt.x
ey: t.CInt = evt.y
if etype == window.EVENT_TYPE_CLICK:
if self.contains(ex, ey):
if not self.focused:
self.focused = 1
tk: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr)
tk.focused_widget = t.CType(self, Widget, t.CPtr)
prompt_y: t.CInt = self.y + self.h - CHAR_H - 4
if ey >= prompt_y and ey < prompt_y + CHAR_H:
rel_x: t.CInt = ex - self.x - 4 - 2 * CHAR_W
if rel_x < 0: rel_x = 0
new_pos: t.CInt = (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.CPtr = t.CType(self.master, Tk, t.CPtr)
tk2.focused_widget = None
return 1
elif etype == window.EVENT_TYPE_KEY:
if self.focused:
ebtn2: t.CUInt8T = evt.button
if ebtn2 == window.KEY_STATE_RELEASE:
return 0
ch: t.CInt = ex
if ch == 8:
if self.cursor_pos > 0:
i: t.CInt = 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: t.CInt = 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: t.CInt = 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: t.CInt = 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: t.CInt = 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: t.CInt = self._visible_lines()
max_off: t.CInt = 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: t.CInt = 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] = t.CChar(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: t.CInt = self.line_count - 1
j: t.CInt = 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:
k: t.CInt = 0
while k < 80:
self.history[self.history_count][k] = self.input_buf[k]
k += 1
self.history_count += 1
else:
i: t.CInt = 0
while i < 15:
k2: t.CInt = 0
while k2 < 80:
self.history[i][k2] = self.history[i + 1][k2]
k2 += 1
i += 1
k3: t.CInt = 0
while k3 < 80:
self.history[15][k3] = self.input_buf[k3]
k3 += 1
self.history_idx = 0
cmd: t.CVoid | t.CPtr = self.on_command
if cmd is not None:
c.Asm(f"""push r12
push r13
mov r13, {c.AsmInp(cmd, t.ASM_DESCR.REG_ANY)}
mov r12, rsp
and rsp, -16
call 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: t.CInt = 0
pos: t.CInt = 0
while True:
ch: t.CChar = c.Deref(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: t.CConst | t.CChar | t.CPtr):
start: t.CInt = 0
pos: t.CInt = 0
while True:
ch: t.CChar = c.Deref(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: t.CConst | t.CChar | t.CPtr, start: t.CInt, end: t.CInt):
if self.line_count >= self.max_lines:
i: t.CInt = 0
while i < self.max_lines - 1:
j: t.CInt = 0
while j <= 79:
self.lines[i][j] = self.lines[i + 1][j]
j += 1
i += 1
self.line_count -= 1
if self.scroll_offset > 0:
self.scroll_offset -= 1
idx: t.CInt = self.line_count
j: t.CInt = 0
k: t.CInt = start
while k < end and j < 79:
ch: t.CChar = c.Deref(text + k)
if ch == 9:
tab_stop: t.CInt = 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: t.CInt = 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: t.CInt, end: t.CInt):
self.line_count = 0
self.scroll_offset = 0
self._update_scrollbar()
def get(self) -> t.CConst | t.CChar | t.CPtr:
return c.Addr(self.input_buf[0])
def config(self, text_color: t.CUInt32T = None, bg_color: t.CUInt32T = None, prompt_color: t.CUInt32T = None, cursor_color: t.CUInt32T = None, border_color: t.CUInt32T = 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) -> t.CInt:
return (self.h - CHAR_H - 4) / CHAR_H
def _update_scrollbar(self):
vis: t.CInt = self._visible_lines()
if self.line_count <= vis:
self.scroll_offset = 0
else:
max_off: t.CInt = self.line_count - vis
if self.scroll_offset > max_off:
self.scroll_offset = max_off
if self.yscrollbar is not None:
sb: Scrollbar | t.CPtr = t.CType(self.yscrollbar, Scrollbar, t.CPtr)
if self.line_count <= vis:
sb.set(0, 1000)
else:
first: t.CInt = self.scroll_offset * 1000 / self.line_count
last: t.CInt = (self.scroll_offset + vis) * 1000 / self.line_count
sb.set(first, last)
@t.Object
class Tk:
win_id: t.CInt
buf: UINT32PTR
bw: t.CInt
bh: t.CInt
style: t.CInt
bg_color: t.CUInt32T
widgets: t.CArray[Widget | t.CPtr, MAX_WIDGETS]
widget_count: t.CInt
focused_widget: Widget | t.CPtr
_win_x: t.CInt
_win_y: t.CInt
_title: t.CArray[t.CChar, 32]
resizable_w: t.CInt
resizable_h: t.CInt
_grid_rows: t.CInt
_grid_cols: t.CInt
def __init__(self):
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 = t.CUInt32T(0xFF231919)
self.resizable_w = 1
self.resizable_h = 1
self._grid_rows = 0
self._grid_cols = 0
if _g_layout_init == 0:
i: t.CInt = 0
while i < 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
i += 1
_g_layout_init = 1
j: t.CInt = 0
while j < 32:
self._title[j] = 0
j += 1
def pack(self, idx: t.CInt, side: t.CInt, fill: t.CInt, expand: t.CInt, padx: t.CInt, pady: t.CInt):
_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: t.CInt, row: t.CInt, col: t.CInt, rowspan: t.CInt, colspan: t.CInt, sticky: t.CInt):
_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: t.CInt = 0
while i < 31:
ch: t.CChar = 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, c.Addr(self._title[0]))
def set_style(self, s: t.CInt):
self.style = s
def resizable(self, rw: t.CInt, rh: t.CInt):
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: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt):
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, c.Addr(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: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | t.CChar | t.CPtr, style: t.CInt):
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 = t.CUInt32T(0xFF231919)
def redraw(self):
self._do_layout()
self.buf = window.get_winbuf(self.win_id)
sz: t.CInt = 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)
i: t.CInt = 0
while i < self.widget_count:
w: Widget | t.CPtr = self.widgets[i]
if w.visible:
w.draw(self.buf, self.bw, self.bh)
i += 1
window.flush(self.win_id)
def _do_layout(self):
self._grid_rows = 0
self._grid_cols = 0
cav_x: t.CInt = 0
cav_y: t.CInt = 0
cav_w: t.CInt = self.bw
cav_h: t.CInt = self.bh
i: t.CInt = 0
while i < self.widget_count:
w: Widget | t.CPtr = self.widgets[i]
lt: t.CInt = _g_wl_type[i]
if lt == 2:
side: t.CInt = _g_wl_side[i]
fill: t.CInt = _g_wl_fill[i]
expand: t.CInt = _g_wl_expand[i]
px: t.CInt = _g_wl_padx[i]
py: t.CInt = _g_wl_pady[i]
ww: t.CInt = w.w
wh: t.CInt = 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: t.CInt = _g_wl_row[i] + _g_wl_rowspan[i]
c: t.CInt = _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
i += 1
i = 0
while i < self.widget_count:
w: Widget | t.CPtr = 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: t.CInt = self.bw / self._grid_cols
cell_h: t.CInt = self.bh / self._grid_rows
wx: t.CInt = _g_wl_col[i] * cell_w
wy: t.CInt = _g_wl_row[i] * cell_h
ww: t.CInt = _g_wl_colspan[i] * cell_w
wh: t.CInt = _g_wl_rowspan[i] * cell_h
sticky: t.CInt = _g_wl_sticky[i]
if sticky == 0:
ox: t.CInt = (ww - w.w) / 2
oy: t.CInt = (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
i += 1
def update(self):
self.redraw()
def winfo_exists(self) -> t.CInt:
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: t.CInt = window.poll_event(c.Addr(evt))
if result != 0:
if evt.win_id == self.win_id:
need_redraw: t.CInt = 0
etype: t.CUInt32T = 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: t.CInt = 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: t.CInt = self.focused_widget.handle_event(c.Addr(evt))
if handled: need_redraw = 1
else:
if etype == window.EVENT_TYPE_HOVER:
i: t.CInt = self.widget_count - 1
while i >= 0:
w: Widget | t.CPtr = self.widgets[i]
if w.visible:
handled2: t.CInt = w.handle_event(c.Addr(evt))
if handled2:
need_redraw = 1
break
i -= 1
else:
i: t.CInt = 0
while i < self.widget_count:
w: Widget | t.CPtr = self.widgets[i]
if w.visible:
handled2: t.CInt = w.handle_event(c.Addr(evt))
if handled2: need_redraw = 1
i += 1
if need_redraw:
self.redraw()
else:
process.yield_cpu()