couple commits

This commit is contained in:
Krzosa Karol
2026-03-20 00:09:50 +01:00
parent bb3859b537
commit 3d91a1f924
17 changed files with 253 additions and 127 deletions

93
README.md Normal file
View File

@@ -0,0 +1,93 @@
# wasm_transcript_browser
This project generates a searchable transcript browser from local `.srt` files and ships it as a WebAssembly web app.
Type a word or phrase, click a match, and it opens the exact timestamp in the matching YouTube video. You can also copy a direct timestamped link.
## What it does
- Parses subtitle files (`.srt`) from a local folder.
- Generates a packed transcript index (`build/entries.inc`) at build time.
- Compiles a C codebase to `main.wasm` and serves it with a small HTML/JS shell.
- Provides instant text search over all transcript text.
- Creates timestamped YouTube links from search hits.
## How the pipeline works
1. `build_file.c` defines a hard-coded source folder:
- `folder_to_create_transcript_for`
2. During build, `src/prototype/prototype.meta.c`:
- scans `.srt` files in that folder,
- parses subtitle entries,
- normalizes text (lowercase, removes punctuation, turns `-` into spaces),
- writes generated data to `build/entries.inc`.
3. `src/prototype/main.c` includes that generated file and compiles to WASM.
4. The browser app (`package/index.html` + `package/main.wasm`) renders a custom UI and handles link open/copy actions.
## Transcript filename format
To build correct YouTube links, filenames are expected to include the 11-char YouTube ID wrapped in one character on each side at the end of the name (commonly brackets).
Example:
- `My Video Title [dQw4w9WgXcQ].en.srt`
- `My Video Title [dQw4w9WgXcQ].srt`
The app extracts the video ID from the ending token and creates links like:
- `https://youtu.be/<id>?feature=shared&t=<seconds>`
## Build
### Prerequisites
- `clang` (or `gcc`/MSVC depending on your platform)
- Python 3 (for local static server)
### Linux
```bash
./build.sh
```
### Windows
```bat
build.bat
```
Build output of interest:
- `build/entries.inc` (generated transcript index)
- `package/index.html`
- `package/main.wasm`
## Run locally
From `package/`:
```bash
python3 -m http.server 8080
```
Then open:
- `http://localhost:8080`
Windows helper script:
- `package/run_server.bat`
## Configuration notes
- Update transcript source folder in `build_file.c` before building.
- The current build file also contains hard-coded deploy commands (`ssh`/`scp`) in `build_prototype_wasm_target`; remove or update those for your own environment.
## Project status
This is an older personal project with a custom C build/codegen stack. The rough edges are expected, but the core idea works: local transcript ingestion + fast search + one-click timestamped YouTube links.

View File

@@ -19,12 +19,22 @@
#include "src/meta/meta_table_format.c"
#include "src/meta/meta_cfiles.c"
s8_t folder_to_create_transcript_for = s8("D:/videos/jblow");
#include "src/app/app.meta.c"
#include "src/ui/ui.meta.c"
#include "src/render/render.meta.c"
#include "src/prototype/prototype.meta.c"
#include "src/testing/testing.meta.c"
b32 run_win32_app_base_target = false;
b32 run_testing_target = false;
b32 run_prototype_dll_target = false;
b32 run_prototype_standalone_target = false;
b32 run_prototype_wasm_target = true;
b32 run_server = false;
void build_testing_target(void) {
if (!cache_code_modified(s8("../src/testing/testing_main.c"), s8("testing.exe"))) {
return;
@@ -102,6 +112,13 @@ void build_prototype_wasm_target(void) {
);
os_copy(s8("main.wasm"), s8("../package/main.wasm"), os_copy_overwrite);
if (ok != 0) exit(ok);
// os_systemf("ssh root@65.21.244.51 rm /website/index.html /website/main.wasm");
// os_systemf("scp ../package/index.html root@65.21.244.51:/website/index.html");
// os_systemf("scp ../package/main.wasm root@65.21.244.51:/website/main.wasm");
os_systemf("ssh root@157.90.144.237 rm /var/www/html/jiang/index.html /var/www/html/jiang/main.wasm");
os_systemf("scp ../package/index.html root@157.90.144.237:/var/www/html/jiang/index.html");
os_systemf("scp ../package/main.wasm root@157.90.144.237:/var/www/html/jiang/main.wasm");
}
}
@@ -151,13 +168,6 @@ int main(int argc, char **argv) {
generate_render_code(&tcx->temp);
generate_testing_code(&tcx->temp);
b32 run_win32_app_base_target = true;
b32 run_testing_target = false;
b32 run_prototype_dll_target = false;
b32 run_prototype_wasm_target = true;
b32 run_prototype_standalone_target = false;
b32 run_server = false;
if (run_server) {
os_systemf("start /D ..\\package ..\\package\\run_server.bat");
}

View File

@@ -1,5 +0,0 @@
function CC(cmd)
Cmd { working_dir = GetProjectPath(), destination = "console", cmd = cmd }
end

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>Hello traveller!</title>
</head>
<body>
@@ -60,6 +60,20 @@ class stream_t {
}
}
function unsecuredCopyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
}
class memory_t {
exports = null; // this is set after wasm module created
constructor(wasm_memory) {
@@ -108,6 +122,9 @@ const wasm_app_imports = {
wasm_trap: () => { throw new Error(); },
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
// gfx
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
@@ -137,8 +154,14 @@ const wasm_app_imports = {
ctx2d.rect(x, y, w, h);
ctx2d.clip();
},
wasm_open_link: (str, len) => {
window.open(mem.read_cstr(str, len));
wasm_set_cursor: (cursor_num) => {
let cursor = 'auto';
if (cursor_num == 0) {
cursor = 'text';
} else if (cursor_num == 1) {
cursor = 'pointer';
}
document.getElementById("canvas").style.cursor = cursor;
},
};
@@ -201,14 +224,20 @@ const wasm_app_imports = {
}
});
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
addEventListener("resize", (event) => { wake_up(); });
addEventListener("keydown", (event) => {
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](64);
mem.write_string_into_cmemory(memory, 64, event.key);
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("keyup", (event) => {
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](64);
mem.write_string_into_cmemory(memory, 64, event.key);
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("mousemove", (event) => {
@@ -216,7 +245,7 @@ const wasm_app_imports = {
wake_up();
});
addEventListener("mousedown", (event) => {
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
wake_up();
});
addEventListener("mouseup", (event) => {

View File

@@ -266,13 +266,14 @@ type_t type__app_event_t = { type_kind_struct, s8_const("app_event_t"), sizeof(a
{.name = s8_const("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key), .dont_serialize = 0},
{.name = s8_const("text"), .type = &type__s8_t, .offset = offsetof(app_event_t, text), .dont_serialize = 0},
{.name = s8_const("mouse_wheel_delta"), .type = &type__v3f32_t, .offset = offsetof(app_event_t, mouse_wheel_delta), .dont_serialize = 0},
{.name = s8_const("clicks"), .type = &type__i8, .offset = offsetof(app_event_t, clicks), .dont_serialize = 0},
{.name = s8_const("ctrl"), .type = &type__b8, .offset = offsetof(app_event_t, ctrl), .dont_serialize = 0},
{.name = s8_const("shift"), .type = &type__b8, .offset = offsetof(app_event_t, shift), .dont_serialize = 0},
{.name = s8_const("alt"), .type = &type__b8, .offset = offsetof(app_event_t, alt), .dont_serialize = 0},
{.name = s8_const("mouse_pos"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_pos), .dont_serialize = 0},
{.name = s8_const("mouse_delta"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_delta), .dont_serialize = 0},
},
.count = 12,
.count = 13,
};
type_t type__app_frame_t = { type_kind_struct, s8_const("app_frame_t"), sizeof(app_frame_t),
.members = (type_member_t[]){

View File

@@ -104,6 +104,7 @@ struct app_event_t {
app_key_t key;
s8_t text;
v3f32_t mouse_wheel_delta;
i8 clicks;
b8 ctrl;
b8 shift;
b8 alt;

View File

@@ -170,6 +170,7 @@ void generate_app_code(ma_arena_t *arena) {
v3f32_t mouse_wheel_delta; // @mouse_wheel
// always present data
i8 clicks;
b8 ctrl;
b8 shift;
b8 alt;

View File

@@ -1,9 +1,3 @@
gb_wasm_export char wasm_temp_buff1[128] = {[127] = 0x13};
gb_wasm_export i32 wasm_temp_buff1_len = 127;
gb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
gb_wasm_export i32 wasm_temp_buff2_len = 127;
fn_wasm_import void wasm_open_link(isize str, i32 len);
gb f32 wasm_dpr;
gb f32 wasm_delta_time;
gb f32 wasm_time;
@@ -55,7 +49,7 @@ fn_wasm_export void wasm_mouse_move(f32 x, f32 y, b32 ctrl, b32 shift, b32 alt,
wasm_set_cached_buttons(ctrl, shift, alt);
}
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) {
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta, i32 clicks) {
button += 1;
assert(button >= app_mouse_button_left && button <= app_mouse_button_right);
wasm_set_cached_buttons(ctrl, shift, alt);
@@ -63,6 +57,7 @@ fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shif
wasm_add_event((app_event_t){
.kind = app_event_kind_mouse_down,
.mouse_button = button,
.clicks = clicks,
});
}
@@ -88,8 +83,6 @@ fn_wasm_export void wasm_mouse_wheel(f32 x, f32 y, f32 delta_x, f32 delta_y, f32
fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
wasm_set_cached_buttons(ctrl, shift, alt);
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
assert(wasm_temp_buff2[127] == 0x13);
s8_t key8 = s8_from_char(key);
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
if (map.key != app_key_null) {
@@ -112,8 +105,6 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
fn_wasm_export void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
wasm_set_cached_buttons(ctrl, shift, alt);
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
assert(wasm_temp_buff2[127] == 0x13);
s8_t key8 = s8_from_char(key);
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
@@ -159,7 +150,15 @@ fn_wasm_export void wasm_init(void) {
os_core_init();
}
fn_wasm_export isize wasm_temp_alloc(isize size) {
return (isize)ma_push_size(&tcx->temp, size);
}
fn_wasm_import void wasm_open_link(isize str, i32 len);
fn_wasm_import void wasm_set_clipboard(isize str, i32 len);
fn_wasm_import void wasm_set_cursor(i32 cursor_num);
// @todo: os?
fn void open_link(s8_t url) {
wasm_open_link((isize)url.str, (i32)url.len);
}
fn void open_link(s8_t url) { wasm_open_link((isize)url.str, (i32)url.len); }
fn void set_clipboard(s8_t url) { wasm_set_clipboard((isize)url.str, (i32)url.len); }
fn void set_cursor(i32 cursor_num) { wasm_set_cursor(cursor_num); }

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>Hello traveller!</title>
</head>
<body>
@@ -60,6 +60,20 @@ class stream_t {
}
}
function unsecuredCopyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
}
class memory_t {
exports = null; // this is set after wasm module created
constructor(wasm_memory) {
@@ -108,6 +122,9 @@ const wasm_app_imports = {
wasm_trap: () => { throw new Error(); },
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
// gfx
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
@@ -137,8 +154,14 @@ const wasm_app_imports = {
ctx2d.rect(x, y, w, h);
ctx2d.clip();
},
wasm_open_link: (str, len) => {
window.open(mem.read_cstr(str, len));
wasm_set_cursor: (cursor_num) => {
let cursor = 'auto';
if (cursor_num == 0) {
cursor = 'text';
} else if (cursor_num == 1) {
cursor = 'pointer';
}
document.getElementById("canvas").style.cursor = cursor;
},
};
@@ -201,14 +224,20 @@ const wasm_app_imports = {
}
});
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
addEventListener("resize", (event) => { wake_up(); });
addEventListener("keydown", (event) => {
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](8);
mem.write_string_into_cmemory(memory, 8, event.key);
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("keyup", (event) => {
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](8);
mem.write_string_into_cmemory(memory, 8, event.key);
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("mousemove", (event) => {
@@ -216,7 +245,7 @@ const wasm_app_imports = {
wake_up();
});
addEventListener("mousedown", (event) => {
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
wake_up();
});
addEventListener("mouseup", (event) => {

View File

@@ -83,7 +83,6 @@ fn void *ma_push_size_ex(ma_arena_t *arena, usize size) {
}
}
if (new_len > arena->commit) {
debugf("expression failed(new_len > arena->commit) len:%llu base_len:%llu reserve:%llu commit:%llu, align: %llu", arena->len, arena->base_len, arena->reserve, arena->commit, arena->align);
invalid_codepath;
return NULL;
}

View File

@@ -4479,8 +4479,11 @@ CL_PRIVATE_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token) {
CL_Advance(T);
}
uint64_t len = T->stream - token->str;
CL_ASSERT(len > 2);
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
if (len <= 2) {
CL_ReportError(T, token, "Invalid hex number");
} else {
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
}
break;
}
}

View File

@@ -15,19 +15,17 @@
// #include "win32_transcript_browser.c"
#include "prototype.gen.c"
// @todo: how to pack the data so it's available on wasm?
// @todo: cursor doesn't change when hovering on text
// @todo: scrolling when clicked on bar bugs out and is slow
// @todo: copy paste
// @todo: selecting and writing is bugged
// @todo: make it possible without executable
// https://drive.proton.me/urls/F9X0GWGH38#t7MaKdLbvOfN
// @todo @ui scrolling when focused button goes out of screen
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
tcx = thread_ctx;
if (frame->first_event->kind == app_event_kind_init) {
run_all_tests();
mt_tweak_f32(font_size, 30, 4, 200);
mt_tweak_f32(_font_size, 30, 30, 30);
@@ -38,7 +36,7 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
res_init(tcx->user_ctx);
res_t *res = tcx->user_ctx;
res_report(res, "type in the path to folder with videos and press enter, for me it would be: D:/videos/jiang");
res_report(res, "type something!");
return true;
} else if (frame->first_event->kind == app_event_kind_reload) {
ui_reload();
@@ -57,4 +55,4 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
// ui_demo_update(frame, tweak_table, lengthof(tweak_table));
transcript_browser_update(frame, tweak_table, lengthof(tweak_table));
return true;
}
}

View File

@@ -20,7 +20,8 @@ void generate_transcript(s8_t folder) {
i64 i1 = 0;
for (i64 i0 = 0; i0 < it->string.len; i0 += 1) {
if (it->string.str[i0] == '"' || it->string.str[i0] == '\'' ||
it->string.str[i0] == ',' || it->string.str[i0] == '.') {
it->string.str[i0] == ',' || it->string.str[i0] == '.' ||
it->string.str[i0] == '\\') {
continue;
} else if (it->string.str[i0] == '-') {
temp_string[i1++] = ' ';
@@ -77,7 +78,7 @@ void generate_transcript(s8_t folder) {
}
void generate_prototype_code(ma_arena_t *arena) {
generate_transcript(s8("D:/videos/jiang"));
generate_transcript(folder_to_create_transcript_for);
sb8_t *include_paths = sb8(arena);
sb8_append(include_paths, os_abs(arena, s8("../src")));
mt_files_t files = mt_lex_files(arena, s8("../src/prototype/main.c"), include_paths);

View File

@@ -31,7 +31,7 @@ fn void res_search_matches(res_t *res) {
const i64 search_iter = 4096*16;
for (i64 i = res->search_idx; i < res->search_idx + search_iter && i < the_text_itself.len; i += 1) {
s8_t text = s8_skip(the_text_itself, i);
if (s8_starts_with(text, res->search_text_input.string)) {
if (s8_starts_with_ex(text, res->search_text_input.string, true)) {
s8_t found = s8_make(text.str, res->search_text_input.len);
array_add(&res->search_matches, found);
}
@@ -64,11 +64,11 @@ fn srt_entry_t res_find_entry(res_t *res, s8_t res_string) {
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
ui_begin_frame(frame);
rn_begin_frame(frame);
res_t *res = (res_t *)tcx->user_ctx;
res_search_matches(res);
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
locl b8 set_focus;
@@ -130,24 +130,30 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
s8_t middle = it;
s8_t string = s8_printf(scratch.arena, "%S**%S**%S", left, middle, right);
ui_box_t *box = ui_box(.string = string, .flags = {.draw_rect = true, .draw_text = true, .keyboard_nav = true});
ui_box_t *box = ui_box(.string = string, .flags = {.set_click_cursor = res->search_text_input.len != 0, .draw_rect = true, .draw_text = true, .keyboard_nav = true});
ui_signal_t sig = ui_signal_from_box(box);
if (sig.clicked && matches.data != res->error_messages.data) {
srt_entry_t entry = res_find_entry(res, middle);
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
debugf("time = %u %u %u seconds = %d", entry.hour, entry.minute, entry.second, seconds);
s8_t id = s8_get_postfix(entry.filepath, 13);
debugf("id = %S", id);
if (id.str[0] != '[') {
fatalf("internal error: dear user, it appears that youtube id link is not composed of exactly 11 letters, quite a pickle, if you could report that to me would be very nice");
}
id = s8_skip(id, 1);
id = s8_chop(id, 1);
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d", id, seconds);
debugf("url = %S", url);
open_link(url);
}
b32 right_clicked = (ui->hot.value == box->id.value && ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_right);
b32 copied = (ui->focus.value == box->id.value && ev->kind == app_event_kind_key_down && ev->key == app_key_c && ev->ctrl);
if (right_clicked || copied) {
srt_entry_t entry = res_find_entry(res, middle);
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
s8_t id = s8_get_postfix(entry.filepath, 13);
id = s8_skip(id, 1);
id = s8_chop(id, 1);
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d %S%S%S", id, seconds, left, middle, right);
set_clipboard(url);
}
if (set_focus) {
ui->focus = box->id;
set_focus = false;

View File

@@ -402,6 +402,12 @@ fn r2f32_t ui_get_appear_rect(ui_box_t *box) {
fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
ui_draw_compute_t co = {0};
if (ui->set_cursor_for_frame == false && box->flags.set_click_cursor &&
(ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
set_cursor(1);
ui->set_cursor_for_frame = true;
}
co.rect = ui_get_appear_rect(box);
co.background_color = box->background_color;
co.text_color = box->text_color;
@@ -431,6 +437,10 @@ fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
}
fn void ui_text_input_draw(ui_box_t *box) {
if (ui->set_cursor_for_frame == false && (ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
set_cursor(0);
ui->set_cursor_for_frame = true;
}
ui_draw_compute_t co = ui_draw_compute(box);
rn_draw_rect(co.rect, co.background_color);
@@ -535,11 +545,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
if (ev->kind == app_event_kind_text) {
signal.text_changed = true;
ui_text_replace(ti, ti->caret.range, ev->text);
if (sel_size) {
ti->caret = ui_carets(ti->caret.range.min);
} else {
ti->caret = ui_carets(ti->caret.range.min + 1);
}
ti->caret = ui_carets(ti->caret.range.min + 1);
} else if (ev->kind == app_event_kind_key_down) {
if (ev->key == app_key_backspace) {
signal.text_changed = true;
@@ -564,6 +570,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
} else {
ui_text_replace(ti, r1i32(ti->caret.e[0], ti->caret.e[0] + 1), s8_null);
}
} else if (ev->key == app_key_left) {
signal.text_changed = true;
if (ev->shift) {
@@ -580,13 +587,20 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
}
} else if (ev->key == app_key_enter) {
signal.text_commit = true;
} else if (ev->key == app_key_a && ev->ctrl) {
ti->caret = ui_caret(0, ti->len);
}
}
v2f32_t size = rn_measure_string(rn->main_font, s8("_"));
v2f32_t pos = v2f32_sub(ev->mouse_pos, box->final_rect.min);
i32 p = (i32)f32_round(pos.x / size.x);
if (ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_left) {
ti->caret = ui_carets(p);
if (ev->clicks == 2) {
// ti->caret = ui_caret(0, ti->len);
} else {
ti->caret = ui_carets(p);
}
}
if (signal.dragging) {
ti->caret = ui_caret_set_front(ti->caret, p);
@@ -1203,6 +1217,7 @@ fn void ui_draw(void) {
fn void ui_begin_frame(app_frame_t *frame) {
ui = tcx->ui_ctx;
ui->frame = frame;
ui->set_cursor_for_frame = false;
}
fn void ui_end_frame(void) {
@@ -1230,6 +1245,10 @@ fn void ui_end_frame(void) {
ui->active = ui_null_id;
}
}
if (ui->set_cursor_for_frame == false) {
set_cursor(32323);
}
}
fn void ui_reload(void) {

View File

@@ -18,6 +18,7 @@ struct ui_box_flags_t {
b8 draw_border: 1;
b8 draw_text: 1;
b8 clip_rect: 1;
b8 set_click_cursor : 1;
b8 animate_appear: 1;
@@ -154,6 +155,8 @@ struct ui_t {
// drawing
r2f32_t clip_rect;
b32 set_cursor_for_frame;
};
gb ui_t *ui;

View File

@@ -1,61 +0,0 @@
[ ] app
[ ] event playback
[ ] win32
[ ] hot reload / plugins
[ ] tests using yield
[ ] touchpad gestures: https://learn.microsoft.com/en-us/windows/win32/wintouch/windows-touch-gestures-overview
[ ] linux
[ ] wasm
[ ] drag and drop
[ ] open file dialog
[ ] os
[?] wasm (explore)
[ ] linux
[ ] render
[ ] wasm (maybe try first without opengl and then opengl)
[ ] opengl
[ ] maybe copy package stuff to build?
[ ] ui
[ ] sleep when not animating
[ ] concept of active panel
[ ] separate root so can't focus off with keyboard
[ ] clicking away from panel, closes it or changes focus
[ ] hot to move between panels?
[ ] table
[ ] scrolling with keyboard
[ ] context menu
[ ] maybe a separate root for context menu?
[ ] hover tooltips
[ ] separate root which gets drawn on top of all ui
[ ] upper left menu dynamics
[ ] text input
[ ] ctrl backspace, ctrl delete
[ ] set mouse cursor on hover
[ ] maybe copy over text editor code from other project?
[ ] everything lister (with edits etc.)
[ ] pernament storage?
[ ] push pop flag
[ ] how to do colors?
- css like based on ids and pre built palettes
- fat box with stacks (now)
[ ] demo style, with different buttons and controls, headings
[ ] color picker
[ ] slider
[ ] draw image in box ui
[ ] push flags?
[ ] core
[ ] ast
[ ] move to core layer at some point as the abstract format for different types of serialization
[ ] use bit fields instead of ast_flag
[ ] mt_tag syntax // f32 value; mt_tag(min = 0, min = 10) // mt_tag(something)
[ ] remove most flags
[ ] explore metadata flags for common formats aka (key_value, list etc.. will
[ ] json format
[ ] ini format
[ ] build system
[ ] use threads
[ ] meta
[ ] s8_bin