Mercurial
diff infinite_canvas/canvas_test.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/canvas_test.c Mon Aug 17 17:01:40 2026 -0700 +++ b/infinite_canvas/canvas_test.c Mon Aug 17 22:16:14 2026 -0700 @@ -65,6 +65,17 @@ assert(dark.resolved_mode == CANVAS_THEME_DARK); assert(dark.background.r == 15); assert(dark.toast_surface.r == 24); + assert(light.switch_track_on.g > light.switch_track_on.b); + assert(dark.switch_track_on.g > dark.switch_track_on.b); + assert(light.switch_track_on.r != light.accent.r); + assert(dark.switch_track_on.r != dark.accent.r); + assert(light.pin.r > light.pin.b); + assert(dark.pin.r > dark.pin.b); + assert(light.pin.r != light.accent.r); + assert(dark.pin.r != dark.accent.r); + assert(light.button.r != dark.button.r); + assert(light.selection.r != dark.selection.r); + assert(light.danger.r != dark.danger.r); assert(Canvas_Theme_Mode_From_String("light") == CANVAS_THEME_LIGHT); assert(Canvas_Theme_Mode_From_String("dark") == CANVAS_THEME_DARK); assert(Canvas_Theme_Mode_From_String("invalid") == CANVAS_THEME_AUTO); @@ -157,6 +168,33 @@ Dowa_Arena_Free(p_arena); } +static void Test_Overlapping_Entities_Use_Topmost_Z_Order(void) +{ + Dowa_Arena *p_arena = Dowa_Arena_Create(4 * ONE_MEGA_BYTE); + assert(p_arena); + + Canvas_Scene scene; + Canvas_Scene_Init(&scene, p_arena); + Vector2 position = {0.0f, 0.0f}; + Vector2 pointer = {40.0f, 30.0f}; + assert(Canvas_Scene_Add(&scene, CANVAS_ENTITY_BUTTON, position)); + assert(Canvas_Scene_Add(&scene, CANVAS_ENTITY_BUTTON, position)); + assert(Canvas_Scene_Pick(&scene, pointer, 1.0f) == 1); + assert(Canvas_Scene_Topmost_Visual(&scene, pointer, 1.0f) == 1); + + scene.p_entities[1].removing = TRUE; + assert(Canvas_Scene_Pick(&scene, pointer, 1.0f) == 0); + assert(Canvas_Scene_Topmost_Visual(&scene, pointer, 1.0f) == 0); + + assert(Canvas_Scene_Add(&scene, CANVAS_ENTITY_WEB_CONTENT, position)); + assert(Canvas_Scene_Topmost_Visual(&scene, pointer, 1.0f) == 2); + assert(Canvas_Scene_Add(&scene, CANVAS_ENTITY_BUTTON, position)); + assert(Canvas_Scene_Pick(&scene, pointer, 1.0f) == 3); + assert(Canvas_Scene_Topmost_Visual(&scene, pointer, 1.0f) == 3); + + Dowa_Arena_Free(p_arena); +} + static void Test_New_Primitive_Defaults(void) { Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE); @@ -206,6 +244,9 @@ Canvas_Scene_Init(&scene, p_arena); assert(!Canvas_Scene_Is_Text_Editing(&scene)); assert(Canvas_Scene_Add(&scene, CANVAS_ENTITY_TEXT_AREA, (Vector2){0.0f, 0.0f})); + assert(sizeof(scene.p_entities[0].text) == CANVAS_ENTITY_TEXT_CAPACITY); + assert(scene.p_entities[0].text_cursor == 0); + assert(scene.p_entities[0].text_selection_anchor == 0); scene.selected_index = 0; assert(!Canvas_Scene_Is_Text_Editing(&scene)); @@ -233,6 +274,37 @@ Dowa_Arena_Free(p_arena); } +static void Test_Entity_Fade_Lifecycle_Commands(void) +{ + Dowa_Arena *p_arena = Dowa_Arena_Create(4 * ONE_MEGA_BYTE); + assert(p_arena); + + Canvas_Scene scene; + Canvas_Scene_Init(&scene, p_arena); + assert(Canvas_Scene_Add( + &scene, + CANVAS_ENTITY_RECTANGLE, + (Vector2){0.0f, 0.0f})); + assert(Canvas_Scene_Add( + &scene, + CANVAS_ENTITY_TEXT, + (Vector2){200.0f, 0.0f})); + assert(Near(scene.p_entities[0].visibility_amount, 0.0f)); + assert(!scene.p_entities[0].removing); + + scene.selected_index = 0; + Canvas_Scene_Fade_Out_Selected(&scene); + assert(scene.p_entities[0].removing); + assert(scene.selected_index == -1); + assert(!scene.p_entities[1].removing); + + Canvas_Scene_Fade_Out_All(&scene); + assert(scene.p_entities[0].removing); + assert(scene.p_entities[1].removing); + + Dowa_Arena_Free(p_arena); +} + static void Test_Browser_Grid(void) { Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE); @@ -251,6 +323,53 @@ Dowa_Arena_Free(p_arena); } +static void Test_Lucide_Gallery_Reuses_Entity_And_Fits_Camera(void) +{ + Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE); + assert(p_arena); + + Canvas_Camera camera; + Canvas_Camera_Init(&camera, 1920, 1200); + Canvas_Scene scene; + Canvas_Scene_Init(&scene, p_arena); + + assert(Canvas_Scene_Show_Lucide_Gallery(&scene, &camera)); + assert(Dowa_Array_Length(scene.p_entities) == 1); + assert(scene.selected_index == 0); + Canvas_Entity *p_gallery = &scene.p_entities[0]; + assert(p_gallery->type == CANVAS_ENTITY_LUCIDE_GALLERY); + assert(strcmp(p_gallery->text, "") == 0); + assert(Near(p_gallery->size.x, 1440.0f)); + assert(Near(p_gallery->size.y, 1040.0f)); + + Vector2 screen_min = + Canvas_Camera_World_To_Screen(&camera, p_gallery->position); + Vector2 screen_max = Canvas_Camera_World_To_Screen( + &camera, + (Vector2){ + p_gallery->position.x + p_gallery->size.x, + p_gallery->position.y + p_gallery->size.y, + }); + assert(screen_min.x >= 319.0f); + assert(screen_min.y >= 55.0f); + assert(screen_max.x <= 1865.0f); + assert(screen_max.y <= 1145.0f); + + assert(Canvas_Scene_Show_Lucide_Gallery(&scene, &camera)); + assert(Dowa_Array_Length(scene.p_entities) == 1); + + Dowa_Arena_Free(p_arena); +} + +static void Test_Lucide_Search_Is_Case_Insensitive(void) +{ + assert(Canvas_Lucide_Count_Matches("") == 1767); + assert(Canvas_Lucide_Count_Matches("pin") > 0); + assert(Canvas_Lucide_Count_Matches("PIN") == + Canvas_Lucide_Count_Matches("pin")); + assert(Canvas_Lucide_Count_Matches("not-a-real-lucide-icon") == 0); +} + static void Test_Demo_Contains_Every_Entity_Type(void) { Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE); @@ -259,13 +378,17 @@ Canvas_Scene scene; Canvas_Scene_Init(&scene, p_arena); Canvas_Scene_Add_Demo(&scene); - assert(Dowa_Array_Length(scene.p_entities) == CANVAS_ENTITY_TYPE_COUNT + 2); + assert(Dowa_Array_Length(scene.p_entities) == CANVAS_ENTITY_TYPE_COUNT + 1); int32 counts[CANVAS_ENTITY_TYPE_COUNT] = {0}; for (size_t index = 0; index < Dowa_Array_Length(scene.p_entities); index++) { counts[scene.p_entities[index].type]++; } for (int32 type = 0; type < CANVAS_ENTITY_TYPE_COUNT; type++) { + if (type == CANVAS_ENTITY_LUCIDE_GALLERY) { + assert(counts[type] == 0); + continue; + } int32 expected = type == CANVAS_ENTITY_NOTIFICATION ? 3 : 1; assert(counts[type] == expected); } @@ -348,6 +471,8 @@ snprintf(scene.p_entities[0].text, sizeof(scene.p_entities[0].text), "Visible note"); scene.p_entities[0].active = TRUE; + scene.p_entities[0].text_cursor = 7; + scene.p_entities[0].text_selection_anchor = 2; scene.p_entities[1].value = 2; scene.p_entities[1].active = TRUE; scene.p_entities[2].active = TRUE; @@ -362,6 +487,8 @@ assert(!snapshot.truncated); assert(strstr(context, "Visible note")); assert(strstr(context, "editing=true")); + assert(strstr(context, "cursor=7")); + assert(strstr(context, "selection_anchor=2")); assert(!strstr(context, "Research")); assert(!strstr(context, "checked=true")); @@ -407,10 +534,14 @@ Test_Viewport_Bounds(); Test_Grid_Spacing(); Test_Entity_Picking(); + Test_Overlapping_Entities_Use_Topmost_Z_Order(); Test_New_Primitive_Defaults(); Test_Text_Editing_Focus(); Test_Entity_Ids_Remain_Unique_After_Clear(); + Test_Entity_Fade_Lifecycle_Commands(); Test_Browser_Grid(); + Test_Lucide_Gallery_Reuses_Entity_And_Fits_Camera(); + Test_Lucide_Search_Is_Case_Insensitive(); Test_Demo_Contains_Every_Entity_Type(); Test_Pinned_Entity_Stays_In_Screen_Space(); Test_Visible_Context_Tracks_Camera_And_Values();