Mercurial
diff infinite_canvas/web_surface_web.c @ 278:8d560f50ed4c
Improve infinite canvas interactions and browser chrome
Render Lucide icons directly with Raylib, add searchable icon browsing, robust text editing, entity lifecycle animations, z-order-safe input, semantic themes, and animated editable browser controls. Document rendering, pinning, context, and component extension for future agents.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:16:14 -0700 |
| parents | b55c22cff335 |
| children | 49e9e591c9bb |
line wrap: on
line diff
--- a/infinite_canvas/web_surface_web.c Mon Aug 17 17:01:40 2026 -0700 +++ b/infinite_canvas/web_surface_web.c Mon Aug 17 22:16:14 2026 -0700 @@ -15,7 +15,9 @@ float x, float y, float width, - float height), { + float height, + float opacity, + int dark_mode), { const canvas = Module.canvas; const surfaceId = `canvas-web-surface-${entity_id}`; let surface = document.getElementById(surfaceId); @@ -27,7 +29,7 @@ surface.style.zIndex = "4"; surface.style.overflow = "hidden"; surface.style.borderRadius = "8px"; - surface.style.background = "#fff"; + surface.style.background = dark_mode ? "#0f0f11" : "#fff"; const frame = document.createElement("iframe"); frame.title = `Canvas web content ${entity_id}`; @@ -43,6 +45,10 @@ surface.dataset.canvasActive = "true"; const frame = surface.firstElementChild; + const colorScheme = dark_mode ? "dark" : "light"; + surface.style.colorScheme = colorScheme; + surface.style.background = dark_mode ? "#0f0f11" : "#fff"; + frame.style.colorScheme = colorScheme; const url = UTF8ToString(p_url); if (frame.dataset.url !== url) { frame.dataset.url = url; @@ -56,6 +62,25 @@ surface.style.top = `${canvasBounds.top + y * scaleY}px`; surface.style.width = `${Math.max(1, width * scaleX)}px`; surface.style.height = `${Math.max(1, height * scaleY)}px`; + surface.style.opacity = `${opacity}`; + surface.style.pointerEvents = opacity >= 0.99 ? "auto" : "none"; +}); + +EM_JS(void, Canvas_Web_History_Go, (uint32 entity_id, int direction), { + const surface = document.getElementById( + `canvas-web-surface-${entity_id}`); + const frame = surface && surface.firstElementChild; + if (!frame || !frame.contentWindow) return; + try { + frame.contentWindow.history.go(direction); + } catch (error) { + console.warn("Canvas iframe history navigation is unavailable", error); + } +}); + +EM_JS(int, Canvas_Web_Has_Surface, (uint32 entity_id), { + return document.getElementById( + `canvas-web-surface-${entity_id}`) ? 1 : 0; }); EM_JS(void, Canvas_Image_Create_Or_Update, ( @@ -64,7 +89,8 @@ float x, float y, float width, - float height), { + float height, + float opacity), { const canvas = Module.canvas; const surfaceId = `canvas-web-surface-${entity_id}`; let surface = document.getElementById(surfaceId); @@ -121,6 +147,7 @@ surface.style.top = `${canvasBounds.top + y * scaleY}px`; surface.style.width = `${Math.max(1, width * scaleX)}px`; surface.style.height = `${Math.max(1, height * scaleY)}px`; + surface.style.opacity = `${opacity}`; }); EM_JS(void, Canvas_Web_End_Frame, (), { @@ -147,21 +174,30 @@ static Rectangle Canvas_Web_Content_Bounds( const Canvas_Camera *p_camera, + const Canvas_Scene *p_scene, const Canvas_Entity *p_entity) { + (void)p_scene; float frame_inset = CANVAS_WEB_FRAME_INSET / p_camera->zoom; + float amount = p_entity->web_chrome_amount; + if (amount < 0.0f) amount = 0.0f; + if (amount > 1.0f) amount = 1.0f; + float eased = amount * amount * (3.0f - 2.0f * amount); + float toolbar_height = + CANVAS_WEB_TOOLBAR_HEIGHT * eased / p_camera->zoom; float resize_handle = CANVAS_WEB_RESIZE_HANDLE / p_camera->zoom; Vector2 position = Canvas_Camera_World_To_Screen( p_camera, (Vector2){ p_entity->position.x + frame_inset, - p_entity->position.y + frame_inset, + p_entity->position.y + frame_inset + toolbar_height, }); return (Rectangle){ position.x, position.y, (p_entity->size.x - frame_inset - resize_handle) * p_camera->zoom, - (p_entity->size.y - frame_inset - resize_handle) * p_camera->zoom, + (p_entity->size.y - frame_inset - resize_handle - toolbar_height) * + p_camera->zoom, }; } @@ -176,6 +212,14 @@ bounds.y < (float)p_camera->viewport_height; } +static float Canvas_Web_Entity_Opacity(const Canvas_Entity *p_entity) +{ + float amount = p_entity->visibility_amount; + if (amount < 0.0f) amount = 0.0f; + if (amount > 1.0f) amount = 1.0f; + return amount * amount * (3.0f - 2.0f * amount); +} + int32 Canvas_Web_Surface_Execute_Subprocess(int argc, char **p_argv) { (void)argc; @@ -185,14 +229,23 @@ boolean Canvas_Web_Surface_Init( Canvas_Web_Surface *p_surface, - Dowa_Arena *p_arena) + Dowa_Arena *p_arena, + boolean dark_mode) { memset(p_surface, 0, sizeof(*p_surface)); p_surface->p_arena = p_arena; + p_surface->dark_mode = dark_mode; p_surface->initialized = TRUE; return TRUE; } +void Canvas_Web_Surface_Set_Dark_Mode( + Canvas_Web_Surface *p_surface, + boolean dark_mode) +{ + p_surface->dark_mode = dark_mode; +} + boolean Canvas_Web_Surface_Update_Input( Canvas_Web_Surface *p_surface, const Canvas_Camera *p_camera, @@ -222,8 +275,12 @@ p_entity->type != CANVAS_ENTITY_IMAGE) { continue; } - Rectangle bounds = Canvas_Web_Content_Bounds(p_camera, p_entity); + Rectangle bounds = Canvas_Web_Content_Bounds( + p_camera, + p_scene, + p_entity); if (!Canvas_Web_Is_Visible(p_camera, bounds)) continue; + float opacity = Canvas_Web_Entity_Opacity(p_entity); if (p_entity->type == CANVAS_ENTITY_IMAGE) { Canvas_Image_Create_Or_Update( p_entity->id, @@ -231,7 +288,8 @@ bounds.x, bounds.y, bounds.width, - bounds.height); + bounds.height, + opacity); } else { Canvas_Web_Create_Or_Update( p_entity->id, @@ -239,7 +297,9 @@ bounds.x, bounds.y, bounds.width, - bounds.height); + bounds.height, + opacity, + p_surface->dark_mode ? 1 : 0); } visible_views++; } @@ -256,6 +316,59 @@ (void)p_scene; } +void Canvas_Web_Surface_Draw_Entity( + const Canvas_Web_Surface *p_surface, + const Canvas_Camera *p_camera, + const Canvas_Scene *p_scene, + size_t entity_index) +{ + (void)p_surface; + (void)p_camera; + (void)p_scene; + (void)entity_index; +} + +const char *Canvas_Web_Surface_URL( + const Canvas_Web_Surface *p_surface, + uint32 entity_id) +{ + (void)p_surface; + (void)entity_id; + return NULL; +} + +boolean Canvas_Web_Surface_Can_Go_Back( + const Canvas_Web_Surface *p_surface, + uint32 entity_id) +{ + (void)p_surface; + return Canvas_Web_Has_Surface(entity_id) ? TRUE : FALSE; +} + +boolean Canvas_Web_Surface_Can_Go_Forward( + const Canvas_Web_Surface *p_surface, + uint32 entity_id) +{ + (void)p_surface; + return Canvas_Web_Has_Surface(entity_id) ? TRUE : FALSE; +} + +void Canvas_Web_Surface_Go_Back( + Canvas_Web_Surface *p_surface, + uint32 entity_id) +{ + (void)p_surface; + Canvas_Web_History_Go(entity_id, -1); +} + +void Canvas_Web_Surface_Go_Forward( + Canvas_Web_Surface *p_surface, + uint32 entity_id) +{ + (void)p_surface; + Canvas_Web_History_Go(entity_id, 1); +} + boolean Canvas_Web_Surface_Is_Focused(const Canvas_Web_Surface *p_surface) { (void)p_surface;