erythros/Media/Themes/Umami/Theme.HC

1725 lines
65 KiB
HolyC

Context2D* umami_min_max_close =
@image_file_to_context2d("M:/Media/Themes/Umami/Window/min_max_close.png");
Context2D* umami_min_max_close_pressed = Image.FileToContext2D(
"M:/Media/Themes/Umami/Window/min_max_close_pressed.png");
Context2D* umami_title_bar_button = NewContext2D(16, 14);
Context2D* umami_default_icon =
@image_file_to_context2d("M:/Media/Themes/Umami/Icon/default.png");
Context2D* umami_corner_resize =
@image_file_to_context2d("M:/Media/Themes/Umami/Window/corner_resize.png");
Context2D* umami_widget_checkbox =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/checkbox.png");
Context2D* umami_widget_checkbox_active =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/checkbox_active.png");
Context2D* umami_widget_checkbox_checked =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/checkbox_checked.png");
Context2D* umami_widget_checkbox_checked_active = @image_file_to_context2d(
"M:/Media/Themes/Umami/Widget/checkbox_checked_active.png");
Context2D* umami_widget_radio =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/radio.png");
Context2D* umami_widget_radio_active =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/radio_active.png");
Context2D* umami_widget_radio_selected =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/radio_selected.png");
Context2D* umami_widget_horz_slider =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/horz_slider.png");
Context2D* umami_widget_vert_slider =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/vert_slider.png");
Context2D* umami_widget_sb_up_button =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/sb_up_button.png");
Context2D* umami_widget_sb_down_button =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/sb_down_button.png");
Context2D* umami_widget_sb_left_button =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/sb_left_button.png");
Context2D* umami_widget_sb_right_button =
@image_file_to_context2d("M:/Media/Themes/Umami/Widget/sb_right_button.png");
U0 @umami_button_repaint(Window* win, ButtonWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
Context2D tmpctx;
I64 text_offset;
I64 text_width;
if (!widget->color)
widget->color = Color(0, 0, 0);
if (!widget->font)
widget->font = Compositor.theme.font.sans;
if (widget->image) {
widget->width = Max(widget->image->width + 8, widget->width);
widget->height = Max(widget->image->height + 8, widget->height);
}
I64 color = widget->color;
color.u8[3] = win->opacity;
ctx->fill_rect(x + 1, y + 1, widget->width - 2, widget->height - 2,
Color(232, 232, 232, win->opacity));
Line2D(win->render_ctx, x + 1, y, x + widget->width - 1, y,
Color(96, 96, 96, win->opacity));
Line2D(win->render_ctx, x + 1, y + widget->height - 1, x + widget->width - 1,
y + widget->height - 1, Color(96, 96, 96, win->opacity));
Line2D(win->render_ctx, x, y + 1, x, y + widget->height - 2,
Color(96, 96, 96, win->opacity));
Line2D(win->render_ctx, x + widget->width - 1, y + 1, x + widget->width - 1,
y + widget->height - 2, Color(96, 96, 96, win->opacity));
if (!widget->disabled && widget == win->mouse_down_widget && Mouse.left &&
@widget_is_hovered(win->x + x, win->y + y, widget)) {
if (widget->image) {
MemCpy(&tmpctx, widget->image, sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x + 5, y + 5, &tmpctx);
}
Line2D(win->render_ctx, x + 1, y + 1, x + widget->width - 2, y + 1,
Color(152, 152, 152, win->opacity));
Line2D(win->render_ctx, x + 1, y + 1, x + 1, y + widget->height - 3,
Color(152, 152, 152, win->opacity));
Line2D(win->render_ctx, x + 2, y + 2, x + widget->width - 3, y + 2,
Color(216, 216, 216, win->opacity));
Line2D(win->render_ctx, x + 2, y + 2, x + 2, y + widget->height - 4,
Color(216, 216, 216, win->opacity));
Line2D(win->render_ctx, x + 2, y + widget->height - 3,
x + widget->width - 2, y + widget->height - 3, Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + 3, y + widget->height - 4,
x + widget->width - 2, y + widget->height - 4, Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + widget->width - 3, y + 2, x + widget->width - 3,
y + widget->height - 3, Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + widget->width - 4, y + 3, x + widget->width - 4,
y + widget->height - 3, Color(255, 255, 255, win->opacity));
} else {
if (widget->image) {
MemCpy(&tmpctx, @t(widget->disabled, widget->disabled_image, widget->image), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x + 4, y + 4, &tmpctx);
}
Line2D(win->render_ctx, x + 2, y + 2, x + widget->width - 2, y + 2,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + 2, y + 3, x + widget->width - 3, y + 3,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + 2, y + 2, x + 2, y + widget->height - 3,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + 3, y + 2, x + 3, y + widget->height - 4,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, x + 3, y + widget->height - 3,
x + widget->width - 3, y + widget->height - 3, Color(216, 216, 216, win->opacity));
Line2D(win->render_ctx, x + widget->width - 3, y + 3, x + widget->width - 3,
y + widget->height - 3, Color(216, 216, 216, win->opacity));
Line2D(win->render_ctx, x + 2, y + widget->height - 2,
x + widget->width - 2, y + widget->height - 2, Color(152, 152, 152, win->opacity));
Line2D(win->render_ctx, x + widget->width - 2, y + 2, x + widget->width - 2,
y + widget->height - 2, Color(152, 152, 152, win->opacity));
}
if (StrLen(&widget->text) && win->opacity > 0) {
text_offset = T(widget == win->mouse_down_widget && Mouse.left &&
@widget_is_hovered(win->x + x, win->y + y, widget),
1, 0);
text_width = PutS2D(NULL, widget->font, 0, 0, Color(0, 0, 0), , &widget->text);
PutS2D(win->render_ctx, widget->font,
x + text_offset + (widget->width / 2) - (text_width / 2),
y + text_offset + (widget->height / 2) - (widget->font->line_height / 2),
color, , &widget->text);
}
}
U0 @umami_checkbox_repaint(Window* win, CheckBoxWidget* widget, I64 x, I64 y)
{
Context2D tmpctx;
Context2D* ctx = win->render_ctx;
if (widget == win->mouse_down_widget &&
@widget_is_hovered(win->x + x, win->y + y, widget)) {
MemCpy(&tmpctx, T(widget->checked, umami_widget_checkbox_checked_active, umami_widget_checkbox_active), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
return;
}
if (widget == win->mouse_down_widget->echo && widget != win->mouse_down_widget &&
@widget_is_hovered(win->x + x, win->y + y, win->mouse_down_widget)) {
MemCpy(&tmpctx, T(widget->checked, umami_widget_checkbox_checked_active, umami_widget_checkbox_active), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
return;
}
MemCpy(&tmpctx,
T(widget->checked, umami_widget_checkbox_checked, umami_widget_checkbox), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
}
U0 @umami_terminal_calculate_size(Window* win, TerminalWidget* widget)
{
widget->size.cols = RoundI64(win->width, 8) / 8;
widget->size.rows = (RoundI64(win->height, 16) / 16) - 2;
}
U32 @umami_terminal_256_color_lookup(I64 index)
{
U32* color_table = TERMINAL_COLOR_TABLE;
return color_table[index];
}
U0 @umami_terminal_set_256_color_foreground(Window* win, TerminalWidget* widget,
I64 argc, U8** argv)
{
if (argc != 3 && !StrCmp(argv[1], "5"))
return;
widget->color.foreground = @umami_terminal_256_color_lookup(Str2I64(argv[2]));
}
U0 @umami_terminal_set_256_color_background(Window* win, TerminalWidget* widget,
I64 argc, U8** argv)
{
if (argc != 3 && !StrCmp(argv[1], "5"))
return;
widget->color.background = @umami_terminal_256_color_lookup(Str2I64(argv[2]));
widget->color.background.u8[3] = win->opacity;
}
U0 @umami_terminal_cleanup_strings(I64 argc, U8** argv)
{
I64 i;
for (i = 0; i < argc - 1; i++)
Free(argv[i]);
}
I64 @umami_ansi_16_color_table[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
U0 @umami_terminal_set_16_color_fg_value(TerminalWidget* widget)
{
I64 val = widget->last_fg_color_set;
if (val > 15)
return;
widget->color.foreground = Color(T(widget->attr.bold,
gr_palette_std[8 + @umami_ansi_16_color_table[val]].r & 0xFF,
gr_palette_std[@umami_ansi_16_color_table[val]].r & 0xFF),
T(widget->attr.bold,
gr_palette_std[8 + @umami_ansi_16_color_table[val]].g & 0xFF,
gr_palette_std[@umami_ansi_16_color_table[val]].g & 0xFF),
T(widget->attr.bold,
gr_palette_std[8 + @umami_ansi_16_color_table[val]].b & 0xFF,
gr_palette_std[@umami_ansi_16_color_table[val]].b & 0xFF));
}
U0 @umami_terminal_ctrl_seq_sgr(Window* win, TerminalWidget* widget)
{
I64 argc;
I64 i;
I64 val;
U8** argv = String.Split(&widget->consumed_chars, ';', &argc);
if (!argc && StrLen(&widget->consumed_chars)) {
argc = 1;
argv[0] = &widget->consumed_chars;
}
if (!argc && !StrLen(&widget->consumed_chars)) {
widget->color.background = Color(0, 0, 0);
widget->color.foreground = Color(217, 217, 217);
return;
}
for (i = 0; i < argc; i++) {
val = Str2I64(argv[i]);
switch (val) {
case 0:
widget->color.background = Color(0, 0, 0);
widget->color.foreground = Color(217, 217, 217);
break;
case 1:
widget->attr.bold = TRUE;
if (widget->last_fg_color_set < 16)
@umami_terminal_set_16_color_fg_value(widget);
break;
case 2:
widget->attr.bold = FALSE;
if (widget->last_fg_color_set < 16)
@umami_terminal_set_16_color_fg_value(widget);
break;
// 16 colors
case 30...37:
widget->last_fg_color_set = val - 30;
@umami_terminal_set_16_color_fg_value(widget);
break;
case 40...47:
widget->color.background = Color(gr_palette_std[@umami_ansi_16_color_table[(val - 40)]].r & 0xFF,
gr_palette_std[@umami_ansi_16_color_table[(val - 40)]].g & 0xFF,
gr_palette_std[@umami_ansi_16_color_table[(val - 40)]].b & 0xFF,
win->opacity);
break;
// 256 colors
case 38:
widget->last_fg_color_set = 256;
@umami_terminal_set_256_color_foreground(win, widget, argc, argv);
i += 2;
break;
case 48:
@umami_terminal_set_256_color_background(win, widget, argc, argv);
i += 2;
break;
case 39:
// FIXME: Set default foreground color
widget->color.foreground = Color(217, 217, 217);
break;
case 49:
// FIXME: Set default background color
widget->color.background = Color(0, 0, 0);
break;
default:
System.Log("Unrecognized attribute in ctrl_seq_sgr: %d", val);
break;
}
}
// if (argc)
// @umami_terminal_cleanup_strings(argc, argv);
}
U0 @umami_terminal_set_col(Window* win, TerminalWidget* widget, I64 row,
I64 col, U8 char)
{
widget->row[row + widget->scroll.y].col[col + widget->scroll.x].foreground = widget->color.foreground;
widget->row[row + widget->scroll.y].col[col + widget->scroll.x].background = widget->color.background;
widget->row[row + widget->scroll.y].col[col + widget->scroll.x].char = char;
}
U0 @umami_terminal_set_row(Window* win, TerminalWidget* widget, I64 row,
U8 char)
{
I64 col;
for (col = 0; col < TERMINAL_MAX_COLS - 1; col++)
@umami_terminal_set_col(win, widget, row, col, char);
}
U0 @umami_terminal_set_rows(Window* win, TerminalWidget* widget, U8 char)
{
I64 row;
for (row = 0; row < widget->size.rows - 1; row++)
@umami_terminal_set_row(win, widget, row, char);
}
U0 @umami_terminal_repaint_col(Window* win, TerminalWidget* widget, I64 row,
I64 col)
{
U8 char[2];
char[1] = NULL;
U32 col_bg_color;
char[0] = widget->row[row + widget->scroll.y].col[col + widget->scroll.x].char;
col_bg_color = widget->row[row + widget->scroll.y]
.col[col + widget->scroll.x]
.background;
col_bg_color.u8[3] = win->opacity;
widget->backing_store->fill_rect(2 + (col * 8), 2 + (row * 16), 8, 16,
col_bg_color);
PutS2D(widget->backing_store, Compositor.theme.font.monospace, 2 + (col * 8),
2 + (row * 16),
widget->row[row + widget->scroll.y]
.col[col + widget->scroll.x]
.foreground,
, &char);
}
U0 @umami_terminal_repaint_row(Window* win, TerminalWidget* widget, I64 row)
{
I64 col;
for (col = 0; col < TERMINAL_MAX_COLS - 1; col++)
@umami_terminal_repaint_col(win, widget, row, col);
}
U0 @umami_terminal_repaint_rows(Window* win, TerminalWidget* widget)
{
I64 row;
for (row = 0; row < widget->size.rows - 1; row++)
@umami_terminal_repaint_row(win, widget, row);
}
U0 @umami_terminal_ctrl_seq_cursor_move(Window* win, TerminalWidget* widget,
I64 dir)
{
I64 pos = Str2I64(&widget->consumed_chars);
I64 prev_row = widget->cursor.y;
switch (dir) {
case 'A':
widget->cursor.y = Max(0, widget->cursor.y - pos);
break;
case 'B':
widget->cursor.y = Min(widget->cursor.y + pos, widget->size.rows - 1);
break;
case 'C':
widget->cursor.x = Min(widget->cursor.x + pos, widget->size.cols - 1);
break;
case 'D':
widget->cursor.x = Max(0, widget->cursor.x - pos);
break;
};
@umami_terminal_repaint_row(win, widget, prev_row);
}
U0 @umami_terminal_ctrl_seq_cursor_pos(Window* win, TerminalWidget* widget)
{
I64 argc;
I64 i;
I64 val;
U8** argv = String.Split(&widget->consumed_chars, ';', &argc);
if (!argc) {
@umami_terminal_repaint_col(win, widget, widget->cursor.y,
widget->cursor.x);
widget->cursor.x = 0;
widget->cursor.y = 0;
return;
}
if (argc == 2) {
@umami_terminal_repaint_col(win, widget, widget->cursor.y,
widget->cursor.x);
widget->cursor.y = Str2I64(argv[0]) - 1;
widget->cursor.x = Str2I64(argv[1]) - 1;
widget->cursor.y = Min(widget->size.rows - 2, widget->cursor.y);
widget->cursor.x = Min(widget->size.cols - 2, widget->cursor.x);
widget->cursor.y = Max(0, widget->cursor.y);
widget->cursor.x = Max(0, widget->cursor.x);
//@umami_terminal_cleanup_strings(argc, argv);
return;
}
}
U0 @umami_terminal_ctrl_seq_erase_screen(Window* win, TerminalWidget* widget)
{
I64 i;
if (!StrLen(&widget->consumed_chars)) {
// TODO: clear from cursor to end of the screen
return;
}
I64 val = Str2I64(&widget->consumed_chars);
switch (val) {
case 0:
// TODO: clear from cursor to end of the screen
break;
case 1:
// TODO: clear from cursor to beginning of the screen
break;
case 2:
widget->scroll.x = 0;
widget->scroll.y = 0;
widget->max.x = 0;
widget->max.y = 0;
for (i = 0; i < 2000; i++)
@umami_terminal_set_row(win, widget, i, NULL);
@umami_terminal_repaint_rows(win, widget);
break;
default:
System.Log("Unrecognized attribute in ctrl_seq_erase_screen: %d", val);
break;
};
}
U0 @umami_terminal_ctrl_seq_erase_line(Window* win, TerminalWidget* widget)
{
if (!StrLen(&widget->consumed_chars)) {
// TODO: clear from cursor to end of the line
return;
}
I64 val = Str2I64(&widget->consumed_chars);
switch (val) {
case 0:
// TODO: clear from cursor to end of the line
break;
case 1:
// TODO: clear from cursor to beginning of the line
break;
case 2:
@umami_terminal_set_row(win, widget, widget->cursor.y, ' ');
@umami_terminal_repaint_rows(win, widget);
break;
default:
System.Log("Unrecognized attribute in ctrl_seq_erase_line: %d", val);
break;
};
}
U0 @umami_terminal_send_response_string(Window* win, TerminalWidget* widget,
U8* string)
{
if (!string || !widget->output)
return;
I64 i;
for (i = 0; i < StrLen(string); i++)
FifoU8Ins(widget->output, string[i]);
}
U0 @umami_terminal_ctrl_seq_set_func(Window* win, TerminalWidget* widget,
Bool bool)
{
if (!StrCmp(&widget->consumed_chars, "?25")) {
@umami_terminal_repaint_col(win, widget, widget->cursor.y,
widget->cursor.x);
widget->cursor.hidden = T(bool, FALSE, TRUE);
}
}
U0 @umami_terminal_ctrl_seq_dev_attr_report(Window* win,
TerminalWidget* widget)
{
if (!widget->output)
return;
if (StrFind("=", &widget->consumed_chars)) {
// Tertiary device attributes
@umami_terminal_send_response_string(win, widget, "\x1bP!|7E565445\x1b\\");
return;
}
if (StrFind(">", &widget->consumed_chars)) {
// Secondary device attributes
@umami_terminal_send_response_string(win, widget, "\x1b[>65;6201;1c");
return;
}
// Primary device attributes
@umami_terminal_send_response_string(win, widget, "\x1b[?65;1;9c");
}
U0 @umami_terminal_ctrl_seq_ecp_report(Window* win, TerminalWidget* widget)
{
if (!widget->output)
return;
U8 resp_str[32];
StrPrint(&resp_str, "\x1b[%d;%dR", widget->cursor.y + 1,
widget->cursor.x + 1);
if (!StrCmp(&widget->consumed_chars, "5"))
@umami_terminal_send_response_string(win, widget, "\x1b[0n");
if (!StrCmp(&widget->consumed_chars, "6"))
@umami_terminal_send_response_string(win, widget, &resp_str);
}
U0 @umami_terminal_ctrl_seq_req_tparm(Window* win, TerminalWidget* widget)
{
// FIXME: allow unsolicited DECREPTPARMs
if (!StrCmp(&widget->consumed_chars, "1")) {
@umami_terminal_send_response_string(win, widget,
"\x1b[3;1;1;120;120;1;0x");
return;
}
@umami_terminal_send_response_string(win, widget, "\x1b[2;1;1;120;120;1;0x");
}
U0 @umami_terminal_os_cmd(Window* win, TerminalWidget* widget)
{
I64 argc;
I64 i;
I64 val;
U8** argv = String.Split(&widget->consumed_chars, ';', &argc);
if (!argc)
return;
val = Str2I64(argv[0]);
switch (val) {
case 0:
// Test set window title
StrPrint(win->title, "Terminal - %s", argv[1]);
break;
default:
break;
}
//@umami_terminal_cleanup_strings(argc, argv);
}
U0 @umami_terminal_consume_char(Window* win, TerminalWidget* widget)
{
U8 char[2];
char[1] = NULL;
FifoU8Rem(widget->input, &char);
switch (widget->state) {
case TERMINAL_STATE_CONSUME_BEGIN:
StrCpy(&widget->consumed_chars, "");
switch (char[0]) {
case '7': // DEC Save Cursor
MemCpy(&widget->stored.attr, &widget->attr,
sizeof(@terminal_widget_attr));
MemCpy(&widget->stored.color, &widget->color,
sizeof(@terminal_widget_color));
MemCpy(&widget->stored.cursor, &widget->cursor,
sizeof(@terminal_widget_cursor));
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case '8': // Dec Restore Cursor
MemCpy(&widget->attr, &widget->stored.attr,
sizeof(@terminal_widget_attr));
MemCpy(&widget->color, &widget->stored.color,
sizeof(@terminal_widget_color));
MemCpy(&widget->cursor, &widget->stored.cursor,
sizeof(@terminal_widget_cursor));
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case '[':
widget->state = TERMINAL_STATE_CONSUME_CTRL_SEQ;
break;
case ']':
widget->state = TERMINAL_STATE_CONSUME_OS_CMD;
break;
default:
System.Log(Fs, "Undefined C1 control code: %c", char[0]);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
};
break;
case TERMINAL_STATE_CONSUME_CTRL_SEQ:
switch (char[0]) {
case '0' ... '9':
case ';':
case '?':
case '>':
case '=':
String.Append(&widget->consumed_chars, &char);
break;
case 'A' ... 'D':
@umami_terminal_ctrl_seq_cursor_move(win, widget, char[0]);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'H':
@umami_terminal_ctrl_seq_cursor_pos(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'J':
@umami_terminal_ctrl_seq_erase_screen(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'K':
@umami_terminal_ctrl_seq_erase_line(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'c':
@umami_terminal_ctrl_seq_dev_attr_report(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'h':
@umami_terminal_ctrl_seq_set_func(win, widget, 1);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'l':
@umami_terminal_ctrl_seq_set_func(win, widget, 0);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'm':
@umami_terminal_ctrl_seq_sgr(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'n':
@umami_terminal_ctrl_seq_ecp_report(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 't':
widget->state = TERMINAL_STATE_CONSUME_END;
break;
case 'x':
@umami_terminal_ctrl_seq_req_tparm(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
default:
System.Log(Fs,
"Invalid or unimplemented character in control sequence: '%c'",
char[0]);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
}
break;
case TERMINAL_STATE_CONSUME_OS_CMD:
switch (char[0]) {
case 7:
case '\\':
@umami_terminal_os_cmd(win, widget);
widget->state = TERMINAL_STATE_CONSUME_END;
break;
default:
String.Append(&widget->consumed_chars, &char);
break;
};
break;
};
}
U0 @umami_terminal_output_char(Window* win, TerminalWidget* widget)
{
if (widget->scroll.y != widget->max.y) {
widget->scroll.y = widget->max.y;
@umami_terminal_repaint_rows(win, widget);
} else {
widget->scroll.y = widget->max.y;
}
U8 char[2];
char[1] = NULL;
FifoU8Rem(widget->input, &char);
switch (char[0]) {
case 27:
StrCpy(&widget->consumed_chars, "");
widget->state = TERMINAL_STATE_CONSUME_BEGIN;
return;
break;
case 7:
// Audio.Beep();
break;
case 8:
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.char
= ' ';
widget->cursor.x--;
if (widget->cursor.x < 0) {
widget->cursor.y--;
widget->cursor.x = widget->size.cols - 2;
}
break;
case 10:
widget->cursor.y++;
widget->cursor.x = 0;
break;
case 13:
widget->cursor.x = 0;
break;
case 32...127:
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.foreground
= widget->color.foreground;
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.background
= widget->color.background;
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.char
= char[0];
widget->cursor.x++;
break;
default:
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.foreground
= widget->color.foreground;
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.background
= widget->color.background;
widget->row[widget->cursor.y + widget->scroll.y]
.col[widget->cursor.x + widget->scroll.x]
.char
= '?';
widget->cursor.x++;
break;
};
if (widget->cursor.x >= widget->size.cols - 1) {
widget->cursor.y++;
widget->cursor.x = 0;
}
if (widget->cursor.y >= widget->size.rows - 1) {
widget->cursor.y--;
widget->max.y++;
widget->scroll.y = widget->max.y;
@umami_terminal_set_row(win, widget, widget->cursor.y, NULL);
@umami_terminal_repaint_rows(win, widget);
}
@umami_terminal_set_col(win, widget, widget->cursor.y, widget->cursor.x, ' ');
}
U0 @umami_terminal_repaint(Window* win, TerminalWidget* widget, I64 x, I64 y)
{
I64 current_col = NULL;
I64 current_row = NULL;
@umami_terminal_calculate_size(win, widget);
if (widget->refresh) {
@umami_terminal_repaint_rows(win, widget);
widget->refresh = FALSE;
}
while (FifoU8Cnt(widget->input)) {
current_col = widget->cursor.x;
current_row = widget->cursor.y;
switch (widget->state) {
case TERMINAL_STATE_OUTPUT:
@umami_terminal_output_char(win, widget);
break;
case TERMINAL_STATE_CONSUME_BEGIN:
case TERMINAL_STATE_CONSUME_CTRL_SEQ:
case TERMINAL_STATE_CONSUME_OS_CMD:
@umami_terminal_consume_char(win, widget);
break;
}
if (widget->state == TERMINAL_STATE_OUTPUT)
@umami_terminal_repaint_col(win, widget, current_row, current_col);
if (widget->state == TERMINAL_STATE_CONSUME_END)
widget->state = TERMINAL_STATE_OUTPUT;
}
if (widget->state == TERMINAL_STATE_OUTPUT && !widget->cursor.hidden && widget->scroll.y == widget->max.y)
widget->backing_store->fill_rect(2 + (widget->cursor.x * 8),
2 + (widget->cursor.y * 16), 8, 16, widget->color.cursor);
}
I64 @umami_input_get_text_width(BitmapFontTextInputWidget* widget)
{
I64 x;
I64 y;
I64 text_width = PutS2D(NULL, widget->font, 0, 0, Color(0, 0, 0), -1, &widget->text);
Context2D* ctx = NewContext2D(text_width, widget->font->line_height);
ctx->fill(0);
PutS2D(ctx, widget->font, 0, 0, Color(128, 255, 255), -1, &widget->text);
for (x = ctx->width - 1; x > -1; x--) {
for (y = 0; y < ctx->height - 1; y++) {
if (ctx->fb[(ctx->width * y) + x]) {
DelContext2D(ctx);
return x + 2;
}
}
}
DelContext2D(ctx);
return x;
}
U0 @umami_input_draw_cursor(Window* win, BitmapFontTextInputWidget* widget,
I64 index, I64 x, I64 y, I64 pos)
{
if (widget->selected_region_start != -1 && widget->selected_region_end != -1)
return;
if (widget == win->focused_widget) {
if (Blink && index == widget->cursor_index) {
VLine2D(win->render_ctx, x + 2 + pos, y + 2,
y + 2 + widget->font->line_height, Color(0, 0, 0));
}
}
}
U0 @umami_input_repaint(Window* win, I64 event,
BitmapFontTextInputWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
if (!widget->font)
widget->font = Compositor.theme.font.sans;
U8* text;
U8 ch[2];
I64 index;
I64 text_width;
I64 text_x_pos;
ch[1] = NULL;
widget->height = widget->font->line_height + 6;
HLine2D(win->render_ctx, x, y, x + widget->width, Color(156, 156, 156, win->opacity));
VLine2D(win->render_ctx, x, y, y + widget->height, Color(156, 156, 156, win->opacity));
HLine2D(win->render_ctx, x + 1, y + widget->height, x + widget->width,
Color(190, 190, 190, win->opacity));
VLine2D(win->render_ctx, x + widget->width, y + 1, y + widget->height,
Color(190, 190, 190, win->opacity));
ctx->fill_rect(x + 1, y + 1, widget->width - 1, widget->height - 1,
Color(255, 255, 255, win->opacity));
if (widget == win->focused_widget) {
HLine2D(win->render_ctx, x + 1, y + 1, x + widget->width - 1,
Color(0, 0, 0, win->opacity));
VLine2D(win->render_ctx, x + 1, y + 1, y + widget->height - 2,
Color(0, 0, 0, win->opacity));
HLine2D(win->render_ctx, x + 1, y + widget->height - 1,
x + widget->width - 1, Color(0, 0, 0, win->opacity));
VLine2D(win->render_ctx, x + widget->width - 1, y + 1,
y + widget->height - 1, Color(0, 0, 0, win->opacity));
} else {
widget->in_drag = FALSE;
}
if (!Mouse.left) {
widget->in_drag = FALSE;
widget->mouse_drag_index = -1;
}
Context2D* text_input_ctx = NewContext2D(widget->width - 4, widget->height);
text_input_ctx->opacity = win->opacity;
if (widget->is_password) {
StrCpy(widget->password, widget->text);
for (text_x_pos = 0; text_x_pos < StrLen(widget->password); text_x_pos++)
widget->password[text_x_pos] = '*';
}
text = T(widget->is_password, &widget->password, &widget->text);
index = 0;
text_width;
text_x_pos = 0;
while (*text) {
ch[0] = *text;
text_width = PutS2D(NULL, widget->font, 0, 0, Color(0, 0, 0), -1, &ch) + 2;
if (event == CPZ_MSG_WIN_LEFT_BTN_DOWN && !widget->in_drag && Mouse.x >= win->x + widget->x + -widget->x_offset + text_x_pos && Mouse.x <= win->x + widget->x + -widget->x_offset + text_x_pos + text_width) {
widget->cursor_index = index;
widget->mouse_drag_index = widget->cursor_index - 1;
widget->mouse_drag_origin_x = Mouse.x - win->x - widget->x + widget->x_offset;
}
if (widget->in_drag) {
if (Mouse.x - win->x - widget->x < widget->mouse_drag_origin_x && Mouse.x >= win->x + widget->x + -widget->x_offset + text_x_pos && Mouse.x <= win->x + widget->x + -widget->x_offset + text_x_pos + text_width) {
widget->selected_region_start = index;
widget->selected_region_end = widget->mouse_drag_index;
}
if (Mouse.x - win->x - widget->x >= widget->mouse_drag_origin_x && Mouse.x >= win->x + widget->x + -widget->x_offset + text_x_pos + text_width) {
widget->selected_region_start = widget->mouse_drag_index + 1;
widget->selected_region_end = index;
}
}
if (index >= widget->selected_region_start && index <= widget->selected_region_end) {
text_input_ctx->fill_rect(-widget->x_offset + text_x_pos, 0, text_width,
widget->font->line_height + 3, Compositor.theme.color.hilight);
}
if (index >= widget->selected_region_start && index <= widget->selected_region_end)
PutS2D(text_input_ctx, widget->font, -widget->x_offset + text_x_pos, 2,
Color(255, 255, 255), -1, &ch);
else
PutS2D(text_input_ctx, widget->font, -widget->x_offset + text_x_pos, 2,
widget->color, -1, &ch);
@umami_input_draw_cursor(win, widget, index, x, y,
-widget->x_offset + text_x_pos);
text_x_pos += text_width;
text++;
index++;
}
if (event == CPZ_MSG_WIN_LEFT_BTN_DOWN && !widget->in_drag && Mouse.x > win->x + widget->x + -widget->x_offset + text_x_pos && widget == win->focused_widget) {
widget->cursor_index = StrLen(&widget->text);
widget->mouse_drag_index = widget->cursor_index;
widget->mouse_drag_origin_x = Mouse.x - win->x - widget->x;
}
if (event == CPZ_MSG_WIN_LEFT_BTN_DOWN && widget == win->focused_widget) {
@widget_input_clear_selected_region(widget);
widget->in_drag = TRUE;
}
if (-widget->x_offset + text_x_pos < text_input_ctx->width)
@umami_input_draw_cursor(win, widget, index, x, y,
-widget->x_offset + text_x_pos);
ctx->blot(x + 3, y + 2, text_input_ctx);
DelContext2D(text_input_ctx);
}
U0 @umami_label_repaint(Window* win, BitmapFontTextLabelWidget* widget, I64 x,
I64 y)
{
I64 color = widget->color;
color.u8[3] = win->opacity;
if (!widget->font)
widget->font = Compositor.theme.font.sans;
if (widget->text && win->opacity > 0)
PutS2D(win->render_ctx, widget->font, x, y, color, -1,
widget->text);
}
U0 @umami_list_view_repaint(Window* win, ListViewWidget* widget, I64 x, I64 y)
{
}
U0 @umami_menu_item_repaint(Window* win, MenuItemWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
U32 text_color = Color(0, 0, 0);
I64 text_x = 8;
I64 text_y = 4 + (widget->height / 2) - (16 / 2);
I64 icon_x = 4;
I64 icon_y = 0;
if (widget->icon) {
icon_y = 4 + (widget->height / 2) - (widget->icon->height / 2) - 3;
}
if (@widget_is_hovered(win->x + x, win->y + y, widget)) {
ctx->fill_rect(x, y, widget->width, widget->height,
Compositor.theme.color.hilight);
text_color = Color(255, 255, 255);
if (widget->submenu) {
Gui.Window.Show(widget->submenu);
Gui.Window.Refresh(widget->submenu);
if (Compositor.GetWindowByZIndex(Compositor.max_z_index - 3) != widget->submenu) {
Gui.Window.SetZIndex(widget->submenu, Compositor.max_z_index - 3);
}
}
}
if (!(@widget_is_hovered(win->x + x, win->y + y, widget))) {
if (widget->submenu) {
Gui.Window.Hide(widget->submenu);
}
}
if (!widget->font)
widget->font = Compositor.theme.font.menu;
if (widget->icon) {
ctx->blot(x + icon_x, y + icon_y, widget->icon);
text_x += 20;
}
if (widget->text)
PutS2D(win->render_ctx, widget->font, x + text_x, y + text_y,
T(widget->color, widget->color, text_color), -1, widget->text);
}
U0 @umami_radio_repaint(Window* win, RadioButtonWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
Context2D tmpctx;
if (widget == win->mouse_down_widget &&
@widget_is_hovered(win->x + x, win->y + y, widget)) {
MemCpy(&tmpctx,
T(widget->selected, umami_widget_radio_selected,
umami_widget_radio_active),
sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
return;
}
if (widget == win->mouse_down_widget->echo && widget != win->mouse_down_widget && !widget->selected &&
@widget_is_hovered(win->x + x, win->y + y, win->mouse_down_widget)) {
MemCpy(&tmpctx, T(win->mouse_down_widget(RadioButtonWidget*)->selected, umami_widget_radio_selected, umami_widget_radio_active), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
return;
}
MemCpy(&tmpctx, T(widget->selected, umami_widget_radio_selected, umami_widget_radio), sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, y, &tmpctx);
}
U0 @umami_radio_select(Window* win, RadioButtonWidget* widget)
{
@window_widgets_list* widgets_list;
widgets_list = win->widget;
while (widgets_list) {
if (widgets_list->widget && widgets_list->widget->type == WIDGET_TYPE_RADIO)
if (widgets_list->widget(RadioButtonWidget*)->group == widget->group)
widgets_list->widget(RadioButtonWidget*)->selected = T(widgets_list->widget == widget, TRUE, FALSE);
widgets_list = widgets_list->next;
}
}
Bool @umami_horz_slider_bar_is_hovered(I64 x, I64 y, Widget* widget)
{
no_warn y;
if (Mouse.x > x - 7 && Mouse.x < x + widget->width + 7)
return TRUE;
return FALSE;
}
Bool @umami_horz_slider_is_hovered(Window* win, HorizontalSliderWidget* widget,
I64 x, I64 y, I64 scroll_pos)
{
if (@widget_is_hovered(win->x + x, win->y + y - 24, widget) && Mouse.x > -4 + (win->x + widget->x - 7) + scroll_pos && Mouse.x < (win->x + widget->x - 7) + scroll_pos + 16)
return TRUE;
return FALSE;
}
U0 @umami_horz_slider_repaint(Window* win, I64 event,
HorizontalSliderWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
Context2D tmpctx;
I64 scroll = widget->scroll;
widget->height = 12;
if (widget->in_drag &&
@umami_horz_slider_bar_is_hovered(win->x + x, win->y + y - 24, widget)) {
if (widget->origin.mouse_x != Mouse.x) {
widget->scroll += (Mouse.x - widget->origin.mouse_x);
widget->origin.mouse_x = Mouse.x;
}
}
ctx->fill_rect(x, y + 5, widget->width, 4, Color(156, 156, 156, win->opacity));
if (@umami_horz_slider_is_hovered(win, widget, x, y, widget->scroll) && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
win->mouse_down_widget = widget;
widget->origin.mouse_x = Mouse.x;
widget->in_drag = TRUE;
} else {
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
widget->scroll = (Mouse.x - win->x - x + 2);
widget->origin.mouse_x = Mouse.x;
widget->in_drag = TRUE;
}
}
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_MOUSE_WHEEL) {
widget->scroll -= Compositor.mouse.z;
}
widget->scroll = Max(0, widget->scroll);
widget->scroll = Min(widget->width - 1, widget->scroll);
widget->value = ToI64((widget->max * 1.0) / (widget->width * 1.0) * (widget->scroll * 1.0));
if (!Mouse.left)
widget->in_drag = FALSE;
HLine2D(win->render_ctx, x, y + 1, x + widget->width, Color(190, 190, 190, win->opacity));
HLine2D(win->render_ctx, x + 1, y + 4, x + widget->width, Color(0, 0, 0, win->opacity));
VLine2D(win->render_ctx, x + 1, y + 4, y + 8, Color(0, 0, 0, win->opacity));
VLine2D(win->render_ctx, x, y + 4, y + 9, Color(190, 190, 190, win->opacity));
HLine2D(win->render_ctx, x, y + 9, x + widget->width, Color(255, 255, 255, win->opacity));
VLine2D(win->render_ctx, x + widget->width, y + 4, y + 9,
Color(255, 255, 255, win->opacity));
VLine2D(win->render_ctx, x, y + 11, y + 16, Color(190, 190, 190, win->opacity));
VLine2D(win->render_ctx, x + 1, y + 11, y + 16, Color(255, 255, 255, win->opacity));
VLine2D(win->render_ctx, x + (widget->width / 2) - 1, y + 11, y + 16,
Color(190, 190, 190, win->opacity));
VLine2D(win->render_ctx, x + (widget->width / 2), y + 11, y + 16,
Color(255, 255, 255, win->opacity));
VLine2D(win->render_ctx, x + widget->width - 1, y + 11, y + 16,
Color(190, 190, 190, win->opacity));
VLine2D(win->render_ctx, x + widget->width, y + 11, y + 16,
Color(255, 255, 255, win->opacity));
MemCpy(&tmpctx, umami_widget_horz_slider, sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot((x - 7) + widget->scroll, y, &tmpctx);
if (widget->scroll != scroll)
widget->change = TRUE;
}
Bool @umami_vert_slider_bar_is_hovered(I64 x, I64 y, Widget* widget)
{
no_warn x;
if (Mouse.y > y - 7 && Mouse.y < y + widget->height + 7)
return TRUE;
return FALSE;
}
Bool @umami_vert_slider_is_hovered(Window* win, VerticalSliderWidget* widget,
I64 x, I64 y, I64 scroll_pos)
{
if (@widget_is_hovered(win->x + x - 24, win->y + y, widget) && Mouse.y > -4 + (win->y + widget->y - 7) + scroll_pos && Mouse.y < (win->y + widget->y - 7) + scroll_pos + 16)
return TRUE;
return FALSE;
}
U0 @umami_vert_slider_repaint(Window* win, I64 event,
VerticalSliderWidget* widget, I64 x, I64 y)
{
Context2D* ctx = win->render_ctx;
Context2D tmpctx;
I64 scroll = widget->scroll;
widget->width = 12;
if (widget->in_drag &&
@umami_vert_slider_bar_is_hovered(win->x + x, win->y + y, widget)) {
if (widget->origin.mouse_y != Mouse.y) {
widget->scroll += (Mouse.y - widget->origin.mouse_y);
widget->origin.mouse_y = Mouse.y;
}
}
ctx->fill_rect(x + 5, y, 4, widget->height, Color(156, 156, 156, win->opacity));
if (@umami_horz_slider_is_hovered(win, widget, x, y, widget->scroll) && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
win->mouse_down_widget = widget;
widget->origin.mouse_y = Mouse.y;
widget->in_drag = TRUE;
} else {
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
widget->scroll = (Mouse.y - win->y - y + 1);
widget->origin.mouse_y = Mouse.y;
widget->in_drag = TRUE;
}
}
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_MOUSE_WHEEL) {
widget->scroll += Compositor.mouse.z;
}
widget->scroll = Max(0, widget->scroll);
widget->scroll = Min(widget->height - 1, widget->scroll);
widget->value = ToI64((widget->max * 1.0) / (widget->height * 1.0) * (widget->scroll * 1.0));
if (!Mouse.left)
widget->in_drag = FALSE;
VLine2D(win->render_ctx, x + 1, y, y + widget->height, Color(190, 190, 190, win->opacity));
VLine2D(win->render_ctx, x + 4, y + 1, y + widget->height, Color(0, 0, 0, win->opacity));
HLine2D(win->render_ctx, x + 4, y, x + 9, Color(0, 0, 0, win->opacity));
VLine2D(win->render_ctx, x + 9, y, y + widget->height, Color(255, 255, 255, win->opacity));
HLine2D(win->render_ctx, x + 4, y + widget->height, x + 9,
Color(255, 255, 255, win->opacity));
HLine2D(win->render_ctx, x + 11, y, x + 16, Color(190, 190, 190, win->opacity));
HLine2D(win->render_ctx, x + 11, y + 1, x + 16, Color(255, 255, 255, win->opacity));
HLine2D(win->render_ctx, x + 11, y + (widget->height / 2) - 1, x + 16,
Color(190, 190, 190, win->opacity));
HLine2D(win->render_ctx, x + 11, y + (widget->height / 2), x + 16,
Color(255, 255, 255, win->opacity));
HLine2D(win->render_ctx, x + 11, y + widget->height - 1, x + 16,
Color(190, 190, 190, win->opacity));
HLine2D(win->render_ctx, x + 11, y + widget->height, x + 16,
Color(255, 255, 255, win->opacity));
MemCpy(&tmpctx, umami_widget_vert_slider, sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(x, (y - 7) + widget->scroll, &tmpctx);
if (widget->scroll != scroll)
widget->change = TRUE;
}
U0 @umami_vert_scroll_button_repaint(Window* win, I64 x, I64 y, I64 width,
I64 height)
{
I64 i;
Context2D* ctx = win->render_ctx;
ctx->fill_rect(x, y, width, height, Color(192, 192, 192));
for (i = 0; i < height; i++) {
Line2D(win->render_ctx, x, y, x, y + height - 1, Color(192, 192, 192));
Line2D(win->render_ctx, x + 1, y, x + 1, y + height - 1,
Color(255, 255, 255));
Line2D(win->render_ctx, x + 14, y, x + width - 2, y + height - 1,
Color(128, 128, 128));
Line2D(win->render_ctx, x + 15, y, x + width - 1, y + height - 1,
Color(64, 64, 64));
}
ctx->plot(x + 1, y, Color(192, 192, 192));
ctx->plot(x + 14, y, Color(192, 192, 192));
ctx->plot(x + 15, y, Color(64, 64, 64));
Line2D(win->render_ctx, x + 1, y + 1, x + width - 2, y + 1,
Color(255, 255, 255));
ctx->plot(x + 14, y + 1, Color(128, 128, 128));
ctx->plot(x + 15, y + 1, Color(64, 64, 64));
Line2D(win->render_ctx, x + 1, y + height - 2, x + width - 2, y + height - 2,
Color(128, 128, 128));
Line2D(win->render_ctx, x, y + height - 1, x + width - 1, y + height - 1,
Color(64, 64, 64));
}
U0 @umami_vert_scrollbar_repaint(Window* win, I64 event,
VerticalScrollBarWidget* widget, I64 x,
I64 y)
{
I64 scroll = widget->scroll;
I64 segment_length = widget->length;
I64 i, j;
Bool fill = TRUE;
Context2D* ctx = win->render_ctx;
if (widget->in_drag && Mouse.y > win->y + widget->y && Mouse.y < win->y + widget->y + widget->height) {
if (widget->origin.mouse_y != Mouse.y) {
widget->scroll += (Mouse.y - widget->origin.mouse_y);
widget->origin.mouse_y = Mouse.y;
}
}
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
if (Mouse.y > win->y + y + 16 && Mouse.y < win->y + y + widget->height - 16) {
widget->scroll = (Mouse.y - win->y - y - (widget->length / 2));
widget->origin.mouse_y = Mouse.y;
widget->in_drag = TRUE;
}
}
if (@widget_is_hovered(win->x + x, win->y + y, widget) && event == CPZ_MSG_WIN_MOUSE_WHEEL) {
widget->scroll += Compositor.mouse.z;
}
widget->scroll = Max(16, widget->scroll);
widget->scroll = Min(widget->height - 1, widget->scroll);
while (widget->scroll + segment_length > widget->height - 16)
widget->scroll--;
widget->value = ToI64((widget->max * 1.0) / (widget->height * 1.0) * (widget->scroll * 1.0));
if (!Mouse.left)
widget->in_drag = FALSE;
ctx->fill_rect(x, y, widget->width, widget->height,
Color(127, 127, 127));
for (i = 0; i < widget->width; i++) {
for (j = 16; j < widget->height - 16; j++) {
if (fill)
ctx->plot(i + x, j + y, Color(255, 255, 255));
fill = !fill;
}
fill = !fill;
}
@umami_vert_scroll_button_repaint(win, x, y, widget->width, 16);
@umami_vert_scroll_button_repaint(win, x, y + widget->height - 16,
widget->width, 16);
ctx->blot(x, y, umami_widget_sb_up_button);
ctx->blot(x, y + widget->height - 16,
umami_widget_sb_down_button);
@umami_vert_scroll_button_repaint(win, x, y + widget->scroll, widget->width,
segment_length);
if (widget->scroll != scroll)
widget->change = TRUE;
}
U0 @umami_set_focused_and_hovered_widget(Window* win, I64 event)
{
I64 title_bar_height = 0;
if (@gui_window_flag_is_set(win, WIN_FLAGS_TITLE_BAR)) {
title_bar_height = 17;
}
I64 x, y;
I64 origin_x = 4;
I64 origin_y = 4;
if (title_bar_height)
origin_y = 7 + title_bar_height;
@window_widgets_list* widgets_list;
Widget* widget;
Bool widget_is_hovered = FALSE;
if (event == CPZ_MSG_WIN_LEFT_BTN_DOWN)
win->focused_widget = NULL;
widget_is_hovered = FALSE;
widgets_list = win->widget;
if (widgets_list) {
// Skip to most recently created
while (widgets_list->next)
widgets_list = widgets_list->next;
// Check hovered/focused, front-to-back
while (widgets_list && !widget_is_hovered) {
widget = widgets_list->widget;
if (widget) {
x = win->x + origin_x + widget->x;
y = win->y + origin_y + widget->y;
if (@gui_window_is_hovered(win) && @widget_is_hovered(x, y, widget)) {
widget_is_hovered = TRUE;
win->hovered_widget = widget;
if (widget_is_hovered && event == CPZ_MSG_WIN_LEFT_BTN_DOWN) {
win->focused_widget = widget;
win->mouse_down_widget = widget;
}
}
}
widgets_list = widgets_list->prev;
}
}
if (!widget_is_hovered)
win->hovered_widget = NULL;
if (!Mouse.left && win->mouse_down_widget) {
widget = win->mouse_down_widget;
x = win->x + origin_x + widget->x;
y = win->y + origin_y + widget->y;
if (@widget_is_hovered(x, y, widget) && widget->callback.clicked)
widget->callback.clicked(widget);
if (@widget_is_hovered(x, y, widget) && widget->echo) {
if (widget->echo->callback.clicked)
widget->echo->callback.clicked(widget->echo);
}
if (@widget_is_hovered(x, y, widget)) {
switch (widget->type) {
case WIDGET_TYPE_CHECKBOX:
widget(CheckBoxWidget*)->checked = !widget(CheckBoxWidget*)->checked;
break;
case WIDGET_TYPE_RADIO:
@umami_radio_select(win, widget);
break;
default:
break;
}
}
if (@widget_is_hovered(x, y, widget) && widget->echo) {
switch (widget->echo->type) {
case WIDGET_TYPE_CHECKBOX:
widget->echo(CheckBoxWidget*)->checked = !widget->echo(CheckBoxWidget*)->checked;
break;
case WIDGET_TYPE_RADIO:
@umami_radio_select(win, widget->echo);
break;
default:
break;
}
}
win->mouse_down_widget = NULL;
}
}
U0 @umami_bordered_rect_repaint(Window* win, BorderedRectWidget* widget, I64 x, I64 y)
{
I64 rw = widget->left.size + widget->width + widget->right.size;
I64 rh = widget->top.size + widget->height + widget->bottom.size;
Rect2D(win->render_ctx, x, y, rw, rh, widget->color);
// Draw borders
// FIXME: style
if (widget->top.size) {
Rect2D(win->render_ctx, x + 0, y + 0, rw, widget->top.size, widget->top.color);
}
if (widget->bottom.size) {
Rect2D(win->render_ctx, x + 0, y + rh - widget->bottom.size, rw, widget->bottom.size, widget->bottom.color);
}
if (widget->left.size) {
Rect2D(win->render_ctx, x + 0, y + 0, widget->left.size, rh, widget->left.color);
}
if (widget->right.size) {
Rect2D(win->render_ctx, x + rw - widget->right.size, y + 0, widget->right.size, rh, widget->right.color);
}
}
U0 @umami_widgets_repaint(Window* win, I64 event, I64 origin_x, I64 origin_y)
{
@window_widgets_list* widgets_list;
Widget* widget;
Context2D* ctx;
// Bool widget_is_hovered;
I64 x, y;
F64 ttf_point_to_size;
no_warn ctx;
no_warn ttf_point_to_size;
widgets_list = win->widget;
while (widgets_list) {
if (widgets_list->widget) {
widget = widgets_list->widget;
x = origin_x + widget->x;
y = origin_y + widget->y;
if (x > win->width - 12 || y > win->height - 12)
goto @umami_skip_widget_repaint;
switch (widget->type) {
case NULL:
break;
case WIDGET_TYPE_BORDERED_RECT:
if ((widget->width && widget->height) || (widget(BorderedRectWidget*)->top.size && widget(BorderedRectWidget*)->bottom.size))
@umami_bordered_rect_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_RECT:
if (widget->width && widget->height)
Rect2D(win->render_ctx, x, y, widget->width, widget->height, widget(RectWidget*)->color);
break;
case WIDGET_TYPE_BUTTON:
@umami_button_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_CHECKBOX:
@umami_checkbox_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_TERMINAL:
@umami_terminal_repaint(win, widget, x, y);
if (widget->backing_store)
CopyRect2D(win->render_ctx, x, y, widget->backing_store);
break;
case WIDGET_TYPE_CONTEXT2D:
if (widget(Context2DWidget*)->ctx)
if (widget(Context2DWidget*)->fast_copy)
CopyRect2D(win->render_ctx, x, y, widget(Context2DWidget*)->ctx);
else
win->render_ctx->blot(x, y, widget(Context2DWidget*)->ctx);
break;
case WIDGET_TYPE_RADIO:
@umami_radio_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_INPUT:
@umami_input_repaint(win, event, widget, x, y);
break;
case WIDGET_TYPE_LABEL:
@umami_label_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_LISTVIEW:
@umami_list_view_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_MENU_ITEM:
@umami_menu_item_repaint(win, widget, x, y);
break;
case WIDGET_TYPE_HORZ_SLIDER:
@umami_horz_slider_repaint(win, event, widget, x, y);
break;
case WIDGET_TYPE_VERT_SLIDER:
@umami_vert_slider_repaint(win, event, widget, x, y);
break;
case WIDGET_TYPE_HORZ_SCROLLBAR:
//@umami_horz_scrollbar_repaint(win, event, widget, x, y);
break;
case WIDGET_TYPE_VERT_SCROLLBAR:
@umami_vert_scrollbar_repaint(win, event, widget, x, y);
break;
default:
break;
}
if (widget->change) {
if (widget->callback.change)
widget->callback.change(widget);
if (widget->echo) {
if (widget->echo->callback.change)
widget->echo->callback.change(widget);
}
widget->change = FALSE;
}
}
@umami_skip_widget_repaint : widgets_list = widgets_list->next;
}
}
U0 @umami_window_repaint(Window* win, I64 event)
{
if (!win)
return;
win->repainting = TRUE;
Context2D* ctx = win->render_ctx;
Context2D tmpctx;
win->render_ctx->width = win->width;
win->render_ctx->height = win->height;
if (!@gui_window_flag_is_set(win, WIN_FLAGS_NOFILL))
MemSetU32(win->render_ctx->fb, Color(204, 204, 204, win->opacity),
win->width * win->height);
I64 title_bar_x;
I64 title_bar_y;
I64 title_bar_width;
I64 title_bar_height = 0;
I64 window_button_x;
I64 origin_title_width;
I64 pass;
Bool repaint;
U8 window_title[1024];
if (@gui_window_flag_is_set(win, WIN_FLAGS_TITLE_BAR)) {
title_bar_height = 17;
}
I64 widget_origin_x = 4;
I64 widget_origin_y = 4;
if (title_bar_height)
widget_origin_y = 7 + title_bar_height;
@umami_widgets_repaint(win, event, widget_origin_x, widget_origin_y);
// Draw window margins
if (!@gui_window_flag_is_set(win, WIN_FLAGS_NOFILL)) {
ctx->fill_rect(0, 0, win->width, 4,
Color(204, 204, 204, win->opacity));
ctx->fill_rect(0, win->height - 4, win->width, 4,
Color(204, 204, 204, win->opacity));
ctx->fill_rect(0, 0, 4, win->height,
Color(204, 204, 204, win->opacity));
ctx->fill_rect(win->width - 5, 0, 4, win->height,
Color(204, 204, 204, win->opacity));
}
// Draw window border
Line2D(win->render_ctx, 0, 0, win->width - 1, 0,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, 0, 0, 0, win->height - 2,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, 1, win->height - 1, win->width - 1, win->height - 1,
Color(156, 156, 156, win->opacity));
Line2D(win->render_ctx, win->width - 1, 1, win->width - 1, win->height - 1,
Color(156, 156, 156, win->opacity));
title_bar_width = 0;
if (@gui_window_flag_is_set(win, WIN_FLAGS_TITLE_BAR)) {
title_bar_x = 4;
title_bar_y = 4;
title_bar_width = win->width - 8;
ctx->fill_rect(4, 4, win->width - 8, 16, Color(204, 204, 204, win->opacity));
if (@gui_window_flag_is_set(win, WIN_FLAGS_ICON)) {
if (win->icon) {
MemCpy(&tmpctx, win->icon, sizeof(Context2D));
} else {
MemCpy(&tmpctx, umami_default_icon, sizeof(Context2D));
}
tmpctx.opacity = win->opacity;
ctx->blot(4, 4, &tmpctx);
title_bar_x = 24;
title_bar_width -= 20;
}
origin_title_width = title_bar_width;
repaint = FALSE;
for (pass = 0; pass < 2; pass++) {
title_bar_width = origin_title_width;
window_button_x = win->width - 20;
if (@gui_window_flag_is_set(win, WIN_FLAGS_CLOSE_BUTTON)) {
if (pass) {
if (win->mouse.x > window_button_x && win->mouse.x < window_button_x + 16 && win->mouse.y > 5 && win->mouse.y < 21 && win->button.close) {
CopyRect2D(umami_title_bar_button, -32, -1,
umami_min_max_close_pressed);
if (!Mouse.left && win->callback.close)
win->callback.close(win);
} else
CopyRect2D(umami_title_bar_button, -32, -1, umami_min_max_close);
MemCpy(&tmpctx, umami_title_bar_button, sizeof(Context2D));
tmpctx.opacity = win->opacity;
win->render_ctx->blot(window_button_x, 5, &tmpctx);
}
if (win->left_btn_down.x > window_button_x && win->left_btn_down.x < window_button_x + 16 && win->left_btn_down.y > 5 && win->left_btn_down.y < 21 && Mouse.left && !win->button.close) {
win->button.close = TRUE;
repaint = TRUE;
}
window_button_x -= 16;
title_bar_width -= 17;
}
if (@gui_window_flag_is_set(win, WIN_FLAGS_MAX_BUTTON)) {
if (pass) {
if (win->mouse.x > window_button_x && win->mouse.x < window_button_x + 16 && win->mouse.y > 5 && win->mouse.y < 21 && win->button.maximize) {
CopyRect2D(umami_title_bar_button, -16, -1,
umami_min_max_close_pressed);
if (!Mouse.left && win->callback.maximize)
win->callback.maximize(win);
}
else
CopyRect2D(umami_title_bar_button, -16, -1, umami_min_max_close);
MemCpy(&tmpctx, umami_title_bar_button, sizeof(Context2D));
tmpctx.opacity = win->opacity;
win->render_ctx->blot(window_button_x, 5, &tmpctx);
}
if (win->left_btn_down.x > window_button_x && win->left_btn_down.x < window_button_x + 16 && win->left_btn_down.y > 5 && win->left_btn_down.y < 21 && Mouse.left && !win->button.maximize) {
win->button.maximize = TRUE;
repaint = TRUE;
}
window_button_x -= 16;
title_bar_width -= 17;
}
if (@gui_window_flag_is_set(win, WIN_FLAGS_MIN_BUTTON)) {
if (pass) {
if (win->mouse.x > window_button_x && win->mouse.x < window_button_x + 16 && win->mouse.y > 5 && win->mouse.y < 21 && win->button.minimize) {
CopyRect2D(umami_title_bar_button, 0, -1,
umami_min_max_close_pressed);
if (!Mouse.left && win->callback.minimize)
win->callback.minimize(win);
} else
CopyRect2D(umami_title_bar_button, 0, -1, umami_min_max_close);
MemCpy(&tmpctx, umami_title_bar_button, sizeof(Context2D));
tmpctx.opacity = win->opacity;
win->render_ctx->blot(window_button_x, 5, &tmpctx);
}
if (win->left_btn_down.x > window_button_x && win->left_btn_down.x < window_button_x + 16 && win->left_btn_down.y > 5 && win->left_btn_down.y < 21 && Mouse.left && !win->button.minimize) {
win->button.minimize = TRUE;
repaint = TRUE;
}
window_button_x -= 16;
title_bar_width -= 17;
}
}
if (!Mouse.left) {
if (win->button.close)
repaint = TRUE;
if (win->button.maximize)
repaint = TRUE;
if (win->button.minimize)
repaint = TRUE;
win->button.close = FALSE;
win->button.maximize = FALSE;
win->button.minimize = FALSE;
}
if (repaint) {
@umami_window_repaint(win, event);
return;
}
if (title_bar_width) {
win->title_bar_x = title_bar_x;
win->title_bar_width = title_bar_width;
}
StrCpy(&window_title, &win->title);
if (win == Compositor.active_win) {
HGradientRect2D(win->render_ctx, title_bar_x, title_bar_y,
title_bar_width, title_bar_height,
Color(10, 36, 106, win->opacity),
Color(165, 201, 239, win->opacity));
// Color(85, 18, 132, win->opacity),
// Color(233, 15, 240, win->opacity));
PutS2D(win->render_ctx, Compositor.theme.font.menu, title_bar_x + 4,
title_bar_y + 4, Color(255, 255, 255, win->opacity),
title_bar_width, &window_title);
} else {
ctx->fill_rect(title_bar_x, title_bar_y, title_bar_width,
title_bar_height, Color(235, 235, 235, win->opacity));
PutS2D(win->render_ctx, Compositor.theme.font.menu, title_bar_x + 4,
title_bar_y + 4, Color(156, 156, 156, win->opacity),
title_bar_width, &window_title);
}
Line2D(win->render_ctx, title_bar_x, title_bar_y,
title_bar_x + title_bar_width - 1, title_bar_y,
Color(156, 156, 156, win->opacity));
Line2D(win->render_ctx, title_bar_x, title_bar_y, title_bar_x,
title_bar_y + title_bar_height - 2,
Color(156, 156, 156, win->opacity));
Line2D(win->render_ctx, title_bar_x + title_bar_width - 1, title_bar_y,
title_bar_x + title_bar_width - 1,
title_bar_y + title_bar_height - 1,
Color(255, 255, 255, win->opacity));
Line2D(win->render_ctx, title_bar_x, title_bar_y + title_bar_height - 1,
title_bar_x + title_bar_width - 1,
title_bar_y + title_bar_height - 1,
Color(255, 255, 255, win->opacity));
if (@gui_window_flag_is_set(win, WIN_FLAGS_RESIZABLE)) {
MemCpy(&tmpctx, umami_corner_resize, sizeof(Context2D));
tmpctx.opacity = win->opacity;
ctx->blot(win->width - 16, win->height - 16, &tmpctx);
}
}
win->backing_store->width = win->render_ctx->width;
win->backing_store->height = win->render_ctx->height;
if (!@gui_window_flag_is_set(win, WIN_FLAGS_NOFILL))
MemCpyU32(win->backing_store->fb, win->render_ctx->fb,
win->render_ctx->width * win->render_ctx->height);
if (@gui_window_flag_is_set(win, WIN_FLAGS_NOFILL))
MemCpyU32(win->backing_store->fb, win->render_ctx->fb, (win->width) * 20);
win->repainting = FALSE;
}
Compositor.theme.color.active_border = Color(0, 0, 0);
Compositor.theme.color.hilight = Color(8, 33, 107);
BitmapFont* @umami_theme_bitmapfont_001 = @bitmapfont_new_from_context2d(
Image.FileToContext2D("M:/Media/Themes/Umami/BitmapFont/menu.png"),
"Eight Bit Dragon",
"`1234567890-=[]\;',./"
"~!@#\d%^&*()_+{}|:\"<>?"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
@umami_theme_bitmapfont_001->line_height = 12;
BitmapFonts.Add(@umami_theme_bitmapfont_001);
BitmapFont* @umami_theme_bitmapfont_002 = @bitmapfont_new_from_context2d(
Image.FileToContext2D("M:/Media/Themes/Umami/BitmapFont/sans.png"),
"Nineteen Ninety Three",
"`1234567890-=[]\;',./"
"~!@#\d%^&*()_+{}|:\"<>?"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
@umami_theme_bitmapfont_002->line_height = 10;
BitmapFonts.Add(@umami_theme_bitmapfont_002);
BitmapFont* @umami_theme_bitmapfont_003 = @bitmapfont_new_from_context2d(
Image.FileToContext2D("M:/Media/Themes/Umami/BitmapFont/ibm3161.png"),
"IBM3161",
" !\"#\d%&'()*+,-./"
"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
"abcdefghijklmnopqrstuvwxyz{|}~",
7);
@umami_theme_bitmapfont_003->line_height = 16;
BitmapFonts.Add(@umami_theme_bitmapfont_003);
Compositor.theme.font.menu = BitmapFonts.GetByName("Eight Bit Dragon");
Compositor.theme.font.sans = BitmapFonts.GetByName("Nineteen Ninety Three");
Compositor.theme.font.monospace = BitmapFonts.GetByName("IBM3161");
Compositor.theme.window.min_width = 128;
Compositor.theme.window.min_height = 128;
Compositor.theme.window_repaint = &@umami_window_repaint;
Context2D** umami_pointer_wait_frames = CAlloc(sizeof(Context2D*) * 18);
Context2D* umami_pointer_wait_anim = Image.FileToContext2D("M:/Media/Themes/Umami/Pointer/wait.png");
U0 @umami_pointer_wait_anim_init()
{
I64 i;
for (i = 0; i < 18; i++) {
umami_pointer_wait_frames[i] = NewContext2D(24, 24);
umami_pointer_wait_frames[i]->blot(0, -(24 * i), umami_pointer_wait_anim);
}
DelContext2D(umami_pointer_wait_anim);
}
@umami_pointer_wait_anim_init;
Compositor.theme.pointer.wait = Animation2D.NewFromFrames(umami_pointer_wait_frames, 18, 50);