ugui.c3l/src/main.c3

417 lines
11 KiB
Plaintext
Raw Normal View History

2024-10-29 22:45:47 +01:00
import std::io;
import vtree;
import cache;
import ugui;
2024-12-01 00:25:43 +01:00
import std::time;
import std::collections::ringbuffer;
2024-12-16 17:06:46 +01:00
import std::core::string;
2025-06-17 18:23:36 +02:00
import std::ascii;
import std::core::mem::allocator;
2025-06-12 19:49:43 +02:00
import sdlrenderer::ren;
import sdl3::sdl;
2024-12-01 00:25:43 +01:00
2025-05-05 16:23:26 +02:00
alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]};
2024-12-01 00:25:43 +01:00
fn void Times.print_stats(&times)
{
time::NanoDuration min, max, avg, x;
min = times.get(0);
for (usz i = 0; i < times.written; i++) {
x = times.get(i);
if (x < min) { min = x; }
if (x > max) { max = x; }
avg += x;
}
avg = (NanoDuration)((ulong)avg/128.0);
io::printfn("min=%s, max=%s, avg=%s", min, max, avg);
}
2024-10-29 22:45:47 +01:00
2024-12-16 17:06:46 +01:00
struct TimeStats {
time::NanoDuration min, max, avg;
}
fn TimeStats Times.get_stats(&times)
{
time::NanoDuration min, max, avg, x;
min = times.get(0);
for (usz i = 0; i < times.written; i++) {
x = times.get(i);
if (x < min) { min = x; }
if (x > max) { max = x; }
avg += x;
}
avg = (NanoDuration)((ulong)avg/128.0);
2025-05-05 16:23:26 +02:00
return {.min = min, .max = max, .avg = avg};
2024-12-16 17:06:46 +01:00
}
2025-06-12 19:49:43 +02:00
const char[*] MSDF_FS_PATH = "resources/shaders/compiled/msdf.frag.spv";
const char[*] SPRITE_FS_PATH = "resources/shaders/compiled/sprite.frag.spv";
const char[*] FONT_FS_PATH = "resources/shaders/compiled/font.frag.spv";
const char[*] RECT_FS_PATH = "resources/shaders/compiled/rect.frag.spv";
2025-02-04 22:40:44 +01:00
2025-06-12 19:49:43 +02:00
const char[*] SPRITE_VS_PATH = "resources/shaders/compiled/sprite.vert.spv";
const char[*] RECT_VS_PATH = "resources/shaders/compiled/rect.vert.spv";
2025-02-04 22:40:44 +01:00
2025-07-07 11:37:56 +02:00
const char[*] STYLESHEET_PATH = "resources/style.css";
2024-10-29 22:45:47 +01:00
fn int main(String[] args)
{
ArenaAllocator arena;
char[] mem = mem::new_array(char, 1024*1024);
defer (void)mem::free(mem);
arena.init(mem);
2024-11-17 21:36:46 +01:00
ugui::Ctx ui;
ui.init(&arena)!!;
2025-01-30 18:36:47 +01:00
defer ui.free();
2025-06-12 19:49:43 +02:00
ren::Renderer ren;
2025-06-30 13:10:00 +02:00
ren.init("Ugui Test", 800, 600, true);
2025-06-12 19:49:43 +02:00
defer ren.free();
2025-06-30 13:10:00 +02:00
ui.input_window_size(800, 600)!!;
2025-06-12 19:49:43 +02:00
//
// FONT LOADING
//
{
// import font in the ui context
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
// create the rendering pipeline
ren.font_atlas_id = ui.get_font_id("font1");
ren.load_spirv_shader_from_file("UGUI_PIPELINE_FONT", SPRITE_VS_PATH, FONT_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_FONT", SPRITE);
// send the atlas to the gpu
Atlas* font_atlas = ui.get_font_atlas("font1")!!;
ren.new_texture("font1", JUST_ALPHA, font_atlas.buffer, font_atlas.width, font_atlas.height);
}
//
// ICON LOADING
//
{
// create the atlas and upload some icons
ui.sprite_atlas_create("icons", AtlasType.ATLAS_R8G8B8A8, 512, 512)!!;
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
// create the rendering pipelines
ren.sprite_atlas_id = ui.get_sprite_atlas_id("icons");
// normal sprite pipeline
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE", SPRITE_VS_PATH, SPRITE_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_SPRITE", SPRITE);
// msdf sprite pipeline
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE_MSDF", SPRITE_VS_PATH, MSDF_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_SPRITE_MSDF", SPRITE);
// upload the atlas to the gpu
Atlas atlas = ui.sprite_atlas.atlas;
ren.new_texture("icons", FULL_COLOR, atlas.buffer, atlas.width, atlas.height);
}
//
// RECT PIPELINE
//
ren.load_spirv_shader_from_file("UGUI_PIPELINE_RECT", RECT_VS_PATH, RECT_FS_PATH, 0, 0);
ren.create_pipeline("UGUI_PIPELINE_RECT", RECT);
2024-10-29 22:45:47 +01:00
2025-07-05 16:37:08 +02:00
// CSS INPUT
2025-07-07 11:37:56 +02:00
io::printfn("imported %d styles", ui.import_style_from_file(STYLESHEET_PATH));
2024-10-29 22:45:47 +01:00
isz frame;
2025-06-15 23:47:09 +02:00
double fps;
2024-12-01 00:25:43 +01:00
time::Clock clock;
2025-06-15 23:47:09 +02:00
time::Clock fps_clock;
2025-06-17 18:23:36 +02:00
time::Clock sleep_clock;
2024-12-01 00:25:43 +01:00
Times ui_times;
Times draw_times;
2024-10-29 22:45:47 +01:00
2025-06-12 19:49:43 +02:00
//
// MAIN LOOP
//
2025-06-17 18:23:36 +02:00
sdl::start_text_input(ren.win);
2025-06-12 19:49:43 +02:00
sdl::Event e;
bool quit = false;
2025-06-17 18:23:36 +02:00
ugui::ModKeys mod;
ugui::MouseButtons btn;
2025-06-12 19:49:43 +02:00
while (!quit) {
2024-12-01 00:25:43 +01:00
clock.mark();
2025-06-15 23:47:09 +02:00
fps_clock.mark();
2025-06-17 18:23:36 +02:00
sleep_clock.mark();
2025-06-12 19:49:43 +02:00
2025-06-17 18:23:36 +02:00
do {
2025-06-12 19:49:43 +02:00
switch (e.type) {
case EVENT_QUIT:
quit = true;
2025-06-17 18:23:36 +02:00
case EVENT_KEY_UP: nextcase;
2025-06-12 19:49:43 +02:00
case EVENT_KEY_DOWN:
2025-06-17 18:23:36 +02:00
mod.rctrl = e.key.key == K_RCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.rctrl;
mod.lctrl = e.key.key == K_LCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.lctrl;
2025-06-30 13:10:00 +02:00
mod.bkspc = e.key.key == K_BACKSPACE ? !!(e.type == EVENT_KEY_DOWN) : mod.bkspc;
2025-06-17 18:23:36 +02:00
// pressing ctrl+key or alt+key does not generate a character as such no
// TEXT_INPUT event is generated. When those keys are pressed we have to
// do manual text input, bummer
if (e.type == EVENT_KEY_DOWN && (mod.lctrl || mod.rctrl)) {
if (ascii::is_alnum_m((uint)e.key.key)) {
ui.input_char((char)e.key.key);
}
}
2025-06-30 13:10:00 +02:00
if (e.type == EVENT_KEY_DOWN && e.key.key == K_RETURN) ui.input_char('\n');
2025-06-17 18:23:36 +02:00
case EVENT_TEXT_INPUT:
ui.input_text_utf8(e.text.text.str_view());
2025-06-12 19:49:43 +02:00
case EVENT_WINDOW_RESIZED:
ui.input_window_size((short)e.window.data1, (short)e.window.data2)!!;
case EVENT_WINDOW_FOCUS_GAINED:
ui.input_changefocus(true);
case EVENT_WINDOW_FOCUS_LOST:
ui.input_changefocus(false);
case EVENT_MOUSE_MOTION:
ui.input_mouse_abs((short)e.motion.x, (short)e.motion.y);
case EVENT_MOUSE_WHEEL:
ui.input_mouse_wheel((short)e.wheel.integer_x, (short)e.wheel.integer_y);
case EVENT_MOUSE_BUTTON_DOWN: nextcase;
case EVENT_MOUSE_BUTTON_UP:
sdl::MouseButtonFlags mb = sdl::get_mouse_state(null, null);
2025-06-17 18:23:36 +02:00
btn = {
2025-06-12 19:49:43 +02:00
.btn_left = !!(mb & BUTTON_LMASK),
.btn_right = !!(mb & BUTTON_RMASK),
.btn_middle = !!(mb & BUTTON_MMASK),
.btn_4 = !!(mb & BUTTON_X1MASK),
.btn_5 = !!(mb & BUTTON_X2MASK),
};
2025-06-17 18:23:36 +02:00
case EVENT_POLL_SENTINEL: break;
2025-06-12 19:49:43 +02:00
default:
io::eprintfn("unhandled event: %s", e.type);
}
2025-06-17 18:23:36 +02:00
} while(sdl::poll_event(&e));
2025-01-30 18:36:47 +01:00
ui.input_mod_keys(mod);
2025-06-17 18:23:36 +02:00
ui.input_mouse_button(btn);
2024-10-31 00:04:49 +01:00
/* End Input Handling */
2024-10-29 22:45:47 +01:00
2024-10-31 00:04:49 +01:00
/* Start UI Handling */
2024-11-17 21:36:46 +01:00
ui.frame_begin()!!;
2024-10-29 22:45:47 +01:00
2025-06-12 19:49:43 +02:00
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
2025-07-14 12:59:07 +02:00
const String APPLICATION = "calculator";
$switch APPLICATION:
$case "debug":
2025-07-14 12:59:07 +02:00
debug_app(&ui);
$case "calculator":
2025-07-14 12:59:07 +02:00
calculator(&ui);
$endswitch
2024-12-16 17:06:46 +01:00
// Timings counter
TimeStats dts = draw_times.get_stats();
TimeStats uts = ui_times.get_stats();
/*
2024-12-16 17:06:46 +01:00
ui.layout_set_floating()!!;
ui.@div({0, ui.height-150, -300, 150}) {
2024-12-25 12:30:35 +01:00
ui.layout_set_column()!!;
ui.text_unbounded(string::tformat("frame %d, fps = %.2f", frame, fps))!!;
ui.text_unbounded(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
2025-07-10 11:05:39 +02:00
ui.text_unbounded(string::tformat("%s %s", mod.lctrl, (String)ui.input.keyboard.text[:ui.input.keyboard.text_len]))!!;
}!!;
*/
2024-12-16 17:06:46 +01:00
2024-11-17 21:36:46 +01:00
ui.frame_end()!!;
2024-10-31 00:04:49 +01:00
/* End UI Handling */
2024-12-01 00:25:43 +01:00
ui_times.push(clock.mark());
2024-12-17 11:26:59 +01:00
//ui_times.print_stats();
2024-10-29 22:45:47 +01:00
2024-10-31 00:04:49 +01:00
/* Start UI Drawing */
2025-06-12 19:49:43 +02:00
ren.begin_render(true);
ren.render_ugui(&ui.cmd_queue);
ren.end_render();
2024-12-01 00:25:43 +01:00
draw_times.push(clock.mark());
2024-12-17 11:26:59 +01:00
//draw_times.print_stats();
2024-12-01 00:25:43 +01:00
/* End Drawing */
2025-06-14 14:49:53 +02:00
2025-06-17 18:23:36 +02:00
// wait for the next event, timeout after 100ms
2025-06-15 23:47:09 +02:00
2025-06-17 18:23:36 +02:00
sdl::wait_event_timeout(&e, (int)(100.0-sleep_clock.mark().to_ms()-0.5));
2025-06-15 23:47:09 +02:00
fps = 1.0 / fps_clock.mark().to_sec();
2025-06-14 14:49:53 +02:00
frame++;
2024-10-29 22:45:47 +01:00
}
return 0;
}
2025-07-14 12:59:07 +02:00
/*
2025-07-14 12:59:07 +02:00
fn void debug_app(ugui::Ctx* ui)
{
static bool toggle;
ui.div_begin({.w=-100})!!;
{
ui.layout_set_column()!!;
if (ui.button(icon:"tux")!!.mouse_press) {
io::printn("press button0");
toggle = !toggle;
}
//ui.layout_next_column()!!;
if (ui.button(label: "ciao", icon: "tick")!!.mouse_press) {
io::printn("press button1");
}
//ui.layout_next_column()!!;
if (ui.button()!!.mouse_release) {
io::printn("release button2");
}
ui.layout_set_row()!!;
ui.layout_next_row()!!;
static float rf, gf, bf, af;
ui.slider_ver({0,0,30,100}, &rf)!!;
ui.slider_ver({0,0,30,100}, &gf)!!;
ui.slider_ver({0,0,30,100}, &bf)!!;
ui.slider_ver({0,0,30,100}, &af)!!;
ui.layout_next_column()!!;
ui.text_unbounded("Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!;
ui.layout_next_column()!!;
ui.button("Continua!")!!;
ui.layout_next_row()!!;
static bool check;
ui.checkbox("", {}, &check, "tick")!!;
ui.checkbox("", {}, &check)!!;
ui.toggle("", {}, &toggle)!!;
ui.sprite("tux")!!;
static char[128] text_box = "ciao mamma";
static usz text_len = "ciao mamma".len;
ui.text_box({0,0,200,200}, text_box[..], &text_len)!!;
2025-07-14 12:59:07 +02:00
};
ui.div_end()!!;
ui.div_begin(ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!;
{
ui.layout_set_column()!!;
static float slider2 = 0.5;
if (ui.slider_ver({0,0,30,100}, &slider2)!!.update) {
io::printfn("other slider: %f", slider2);
}
ui.button()!!;
ui.button()!!;
ui.button()!!;
ui.button()!!;
if (toggle) {
ui.button()!!;
ui.button()!!;
ui.button()!!;
ui.button()!!;
}
ui.layout_next_column()!!;
ui.layout_set_row()!!;
static float f1, f2;
ui.slider_hor({0,0,100,30}, &f1)!!;
ui.slider_hor({0,0,100,30}, &f2)!!;
};
ui.div_end()!!;
}
*/
2025-07-14 12:59:07 +02:00
2025-08-20 17:33:25 +02:00
import std::os::process;
2025-07-14 12:59:07 +02:00
fn void calculator(ugui::Ctx* ui)
{
2025-08-20 17:33:25 +02:00
static char[128] buffer;
static usz len;
bool eval;
// keyboard input
switch(ui.get_keys()) {
case "+": nextcase;
case "-": nextcase;
case "*": nextcase;
case "/": nextcase;
case "(": nextcase;
case ")": nextcase;
case "0": nextcase;
case "1": nextcase;
case "2": nextcase;
case "3": nextcase;
case "4": nextcase;
case "5": nextcase;
case "6": nextcase;
case "7": nextcase;
case "8": nextcase;
case "9":
buffer[len++] = ui.get_keys()[0];
case "\n":
eval = true;
case "c":
len = 0;
case "d":
len--;
}
// ui input/output
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) {
/*
ui.@div({0,0,250,50}) {
2025-08-20 17:33:25 +02:00
ui.text_unbounded((String)buffer[:len])!!;
}!!;
*/
2025-07-14 12:59:07 +02:00
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
2025-08-20 17:33:25 +02:00
ui.button("7")!!.mouse_press ? buffer[len++] = '7' : 0;
ui.button("4")!!.mouse_press ? buffer[len++] = '4' : 0;
2025-09-06 12:50:36 +02:00
ui.button("1")!!.mouse_press ? buffer[len++] = '1' : 0;
ui.button("0")!!.mouse_press ? buffer[len++] = '0' : 0;
}!!;
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
2025-09-06 12:50:36 +02:00
ui.button("8")!!.mouse_press ? buffer[len++] = '8' : 0;
ui.button("5")!!.mouse_press ? buffer[len++] = '5' : 0;
2025-08-20 17:33:25 +02:00
ui.button("2")!!.mouse_press ? buffer[len++] = '2' : 0;
2025-09-06 12:50:36 +02:00
ui.button(".")!!.mouse_press ? buffer[len++] = '.' : 0;
}!!;
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
2025-09-06 12:50:36 +02:00
ui.button("9")!!.mouse_press ? buffer[len++] = '9' : 0;
ui.button("6")!!.mouse_press ? buffer[len++] = '6' : 0;
ui.button("3")!!.mouse_press ? buffer[len++] = '3' : 0;
2025-08-20 17:33:25 +02:00
ui.button("(")!!.mouse_press ? buffer[len++] = '(' : 0;
}!!;
2025-09-06 12:50:36 +02:00
ui.@div(ugui::@exact(10), ugui::@exact(10)) {}!!;
2025-09-06 12:50:36 +02:00
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
ui.button("x")!!.mouse_press ? buffer[len++] = '*' : 0;
ui.button("/")!!.mouse_press ? buffer[len++] = '/' : 0;
ui.button("+")!!.mouse_press ? buffer[len++] = '+' : 0;
ui.button(")")!!.mouse_press ? buffer[len++] = ')' : 0;
}!!;
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
ui.button("C")!!.mouse_press ? len = 0 : 0;
ui.button("D")!!.mouse_press ? len-- : 0;
ui.button("-")!!.mouse_press ? buffer[len++] = '-' : 0;
// eval the expression with 'bc'
if (ui.button("=")!!.mouse_press || eval) {
char[128] out;
String y = string::tformat("echo '%s' | bc", (String)buffer[:len]);
String x = process::execute_stdout_to_buffer(out[:128], (String[]){"sh", "-c", y}) ?? "";
buffer[:x.len] = x[..];
len = x.len;
}
}!!;
}!!;
2025-07-14 12:59:07 +02:00
}