Mercurial
comparison infinite_canvas/web_surface_native.cc @ 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 |
comparison
equal
deleted
inserted
replaced
| 277:1d99147f520c | 278:8d560f50ed4c |
|---|---|
| 3 } | 3 } |
| 4 | 4 |
| 5 #include "include/cef_app.h" | 5 #include "include/cef_app.h" |
| 6 #include "include/cef_browser.h" | 6 #include "include/cef_browser.h" |
| 7 #include "include/cef_client.h" | 7 #include "include/cef_client.h" |
| 8 #include "include/cef_display_handler.h" | |
| 8 #include "include/cef_render_handler.h" | 9 #include "include/cef_render_handler.h" |
| 9 #include "include/capi/cef_app_capi.h" | 10 #include "include/capi/cef_app_capi.h" |
| 10 | 11 |
| 11 #include <algorithm> | 12 #include <algorithm> |
| 12 #include <cmath> | 13 #include <cmath> |
| 38 char g_locales_switch[PATH_MAX + 64] = {}; | 39 char g_locales_switch[PATH_MAX + 64] = {}; |
| 39 #endif | 40 #endif |
| 40 | 41 |
| 41 void FindResourcePaths(); | 42 void FindResourcePaths(); |
| 42 | 43 |
| 44 void ApplyColorScheme(CefRefPtr<CefBrowser> browser, boolean dark_mode) { | |
| 45 if (!browser) return; | |
| 46 CefRefPtr<CefDictionaryValue> params = CefDictionaryValue::Create(); | |
| 47 CefRefPtr<CefListValue> features = CefListValue::Create(); | |
| 48 CefRefPtr<CefDictionaryValue> feature = CefDictionaryValue::Create(); | |
| 49 feature->SetString("name", "prefers-color-scheme"); | |
| 50 feature->SetString("value", dark_mode ? "dark" : "light"); | |
| 51 features->SetDictionary(0, feature); | |
| 52 params->SetList("features", features); | |
| 53 browser->GetHost()->ExecuteDevToolsMethod( | |
| 54 0, | |
| 55 "Emulation.setEmulatedMedia", | |
| 56 params); | |
| 57 | |
| 58 CefRefPtr<CefFrame> frame = browser->GetMainFrame(); | |
| 59 const char *script = dark_mode | |
| 60 ? "document.documentElement.style.colorScheme='dark'" | |
| 61 : "document.documentElement.style.colorScheme='light'"; | |
| 62 frame->ExecuteJavaScript(script, frame->GetURL(), 0); | |
| 63 } | |
| 64 | |
| 43 const Canvas_Entity *FindSurfaceEntity( | 65 const Canvas_Entity *FindSurfaceEntity( |
| 44 const Canvas_Scene *scene, | 66 const Canvas_Scene *scene, |
| 45 uint32 entity_id) { | 67 uint32 entity_id) { |
| 46 for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) { | 68 for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) { |
| 47 const Canvas_Entity *entity = &scene->p_entities[index]; | 69 const Canvas_Entity *entity = &scene->p_entities[index]; |
| 54 return nullptr; | 76 return nullptr; |
| 55 } | 77 } |
| 56 | 78 |
| 57 Rectangle ContentBounds( | 79 Rectangle ContentBounds( |
| 58 const Canvas_Camera *camera, | 80 const Canvas_Camera *camera, |
| 81 const Canvas_Scene *scene, | |
| 59 const Canvas_Entity *entity) { | 82 const Canvas_Entity *entity) { |
| 83 (void)scene; | |
| 60 float frame_inset = CANVAS_WEB_FRAME_INSET / camera->zoom; | 84 float frame_inset = CANVAS_WEB_FRAME_INSET / camera->zoom; |
| 85 float amount = std::clamp(entity->web_chrome_amount, 0.0f, 1.0f); | |
| 86 float eased = amount * amount * (3.0f - 2.0f * amount); | |
| 87 float toolbar_height = CANVAS_WEB_TOOLBAR_HEIGHT * eased / camera->zoom; | |
| 61 float resize_handle = CANVAS_WEB_RESIZE_HANDLE / camera->zoom; | 88 float resize_handle = CANVAS_WEB_RESIZE_HANDLE / camera->zoom; |
| 62 Vector2 position = Canvas_Camera_World_To_Screen( | 89 Vector2 position = Canvas_Camera_World_To_Screen( |
| 63 camera, | 90 camera, |
| 64 Vector2{ | 91 Vector2{ |
| 65 entity->position.x + frame_inset, | 92 entity->position.x + frame_inset, |
| 66 entity->position.y + frame_inset}); | 93 entity->position.y + frame_inset + toolbar_height}); |
| 67 return Rectangle{ | 94 return Rectangle{ |
| 68 position.x, | 95 position.x, |
| 69 position.y, | 96 position.y, |
| 70 (entity->size.x - frame_inset - resize_handle) * camera->zoom, | 97 (entity->size.x - frame_inset - resize_handle) * camera->zoom, |
| 71 (entity->size.y - frame_inset - resize_handle) * camera->zoom, | 98 (entity->size.y - frame_inset - resize_handle - toolbar_height) * |
| 99 camera->zoom, | |
| 72 }; | 100 }; |
| 73 } | 101 } |
| 74 | 102 |
| 75 boolean IsVisible(const Canvas_Camera *camera, Rectangle bounds) { | 103 boolean IsVisible(const Canvas_Camera *camera, Rectangle bounds) { |
| 76 return camera->zoom >= 0.30f && | 104 return camera->zoom >= 0.30f && |
| 77 bounds.x + bounds.width > 0.0f && | 105 bounds.x + bounds.width > 0.0f && |
| 78 bounds.y + bounds.height > 0.0f && | 106 bounds.y + bounds.height > 0.0f && |
| 79 bounds.x < static_cast<float>(camera->viewport_width) && | 107 bounds.x < static_cast<float>(camera->viewport_width) && |
| 80 bounds.y < static_cast<float>(camera->viewport_height); | 108 bounds.y < static_cast<float>(camera->viewport_height); |
| 109 } | |
| 110 | |
| 111 float EntityOpacity(const Canvas_Entity *entity) { | |
| 112 float amount = std::clamp(entity->visibility_amount, 0.0f, 1.0f); | |
| 113 return amount * amount * (3.0f - 2.0f * amount); | |
| 81 } | 114 } |
| 82 | 115 |
| 83 int32 FrameRateForView( | 116 int32 FrameRateForView( |
| 84 const Canvas_Web_Surface *surface, | 117 const Canvas_Web_Surface *surface, |
| 85 const Canvas_Web_View *view, | 118 const Canvas_Web_View *view, |
| 131 DISALLOW_COPY_AND_ASSIGN(CanvasCefApp); | 164 DISALLOW_COPY_AND_ASSIGN(CanvasCefApp); |
| 132 }; | 165 }; |
| 133 | 166 |
| 134 class CanvasCefClient : public CefClient, | 167 class CanvasCefClient : public CefClient, |
| 135 public CefRenderHandler, | 168 public CefRenderHandler, |
| 169 public CefDisplayHandler, | |
| 136 public CefLifeSpanHandler { | 170 public CefLifeSpanHandler { |
| 137 public: | 171 public: |
| 138 explicit CanvasCefClient(Canvas_Web_View *view) : view_(view) {} | 172 explicit CanvasCefClient(Canvas_Web_View *view) : view_(view) {} |
| 139 | 173 |
| 140 CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; } | 174 CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; } |
| 175 CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; } | |
| 141 CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } | 176 CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } |
| 177 | |
| 178 void OnAddressChange( | |
| 179 CefRefPtr<CefBrowser> browser, | |
| 180 CefRefPtr<CefFrame> frame, | |
| 181 const CefString &url) override { | |
| 182 if (!frame->IsMain()) return; | |
| 183 std::snprintf( | |
| 184 view_->url, | |
| 185 sizeof(view_->url), | |
| 186 "%s", | |
| 187 url.ToString().c_str()); | |
| 188 view_->can_go_back = browser->CanGoBack() ? TRUE : FALSE; | |
| 189 view_->can_go_forward = browser->CanGoForward() ? TRUE : FALSE; | |
| 190 } | |
| 142 | 191 |
| 143 void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override { | 192 void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override { |
| 144 (void)browser; | 193 (void)browser; |
| 145 rect = CefRect(0, 0, view_->texture_width, view_->texture_height); | 194 rect = CefRect(0, 0, view_->texture_width, view_->texture_height); |
| 146 } | 195 } |
| 256 } | 305 } |
| 257 } | 306 } |
| 258 return nullptr; | 307 return nullptr; |
| 259 } | 308 } |
| 260 | 309 |
| 310 const Canvas_Web_View *FindView( | |
| 311 const Canvas_Web_Surface *surface, | |
| 312 uint32 entity_id) { | |
| 313 for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) { | |
| 314 if (surface->views[index].entity_id == entity_id) { | |
| 315 return &surface->views[index]; | |
| 316 } | |
| 317 } | |
| 318 return nullptr; | |
| 319 } | |
| 320 | |
| 261 Canvas_Web_View *FindInactiveView(Canvas_Web_Surface *surface) { | 321 Canvas_Web_View *FindInactiveView(Canvas_Web_Surface *surface) { |
| 262 for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) { | 322 for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) { |
| 263 if (!surface->views[index].active) return &surface->views[index]; | 323 if (!surface->views[index].active) return &surface->views[index]; |
| 264 } | 324 } |
| 265 return nullptr; | 325 return nullptr; |
| 268 boolean SceneHasVisibleEntity( | 328 boolean SceneHasVisibleEntity( |
| 269 const Canvas_Scene *scene, | 329 const Canvas_Scene *scene, |
| 270 const Canvas_Camera *camera, | 330 const Canvas_Camera *camera, |
| 271 uint32 entity_id) { | 331 uint32 entity_id) { |
| 272 const Canvas_Entity *entity = FindSurfaceEntity(scene, entity_id); | 332 const Canvas_Entity *entity = FindSurfaceEntity(scene, entity_id); |
| 273 return entity && IsVisible(camera, ContentBounds(camera, entity)); | 333 return entity && IsVisible(camera, ContentBounds(camera, scene, entity)); |
| 274 } | 334 } |
| 275 | 335 |
| 276 boolean EnsureViewResolution( | 336 boolean EnsureViewResolution( |
| 277 Canvas_Web_Surface *surface, | 337 Canvas_Web_Surface *surface, |
| 278 Canvas_Web_View *view, | 338 Canvas_Web_View *view, |
| 329 state->clients[index] = new CanvasCefClient(view); | 389 state->clients[index] = new CanvasCefClient(view); |
| 330 CefWindowInfo window_info; | 390 CefWindowInfo window_info; |
| 331 window_info.SetAsWindowless(kNullWindowHandle); | 391 window_info.SetAsWindowless(kNullWindowHandle); |
| 332 CefBrowserSettings browser_settings; | 392 CefBrowserSettings browser_settings; |
| 333 browser_settings.windowless_frame_rate = 30; | 393 browser_settings.windowless_frame_rate = 30; |
| 334 browser_settings.background_color = CefColorSetARGB(0, 255, 255, 255); | 394 browser_settings.background_color = surface->dark_mode |
| 395 ? CefColorSetARGB(255, 15, 15, 17) | |
| 396 : CefColorSetARGB(255, 255, 255, 255); | |
| 335 const char *initial_url = | 397 const char *initial_url = |
| 336 view->entity_type == CANVAS_ENTITY_IMAGE ? "about:blank" : url; | 398 view->entity_type == CANVAS_ENTITY_IMAGE ? "about:blank" : url; |
| 337 if (!CefBrowserHost::CreateBrowser( | 399 if (!CefBrowserHost::CreateBrowser( |
| 338 window_info, | 400 window_info, |
| 339 state->clients[index], | 401 state->clients[index], |
| 429 view->active = FALSE; | 491 view->active = FALSE; |
| 430 view->entity_id = 0; | 492 view->entity_id = 0; |
| 431 return; | 493 return; |
| 432 } | 494 } |
| 433 if (entity->type == CANVAS_ENTITY_WEB_CONTENT) { | 495 if (entity->type == CANVAS_ENTITY_WEB_CONTENT) { |
| 496 std::snprintf( | |
| 497 view->source_url, | |
| 498 sizeof(view->source_url), | |
| 499 "%s", | |
| 500 entity->text); | |
| 434 std::snprintf(view->url, sizeof(view->url), "%s", entity->text); | 501 std::snprintf(view->url, sizeof(view->url), "%s", entity->text); |
| 435 } | 502 } |
| 436 return; | 503 return; |
| 437 } | 504 } |
| 438 EnsureViewResolution(surface, view, bounds); | 505 EnsureViewResolution(surface, view, bounds); |
| 439 | 506 |
| 440 if (type_changed || std::strcmp(view->url, entity->text) != 0) { | 507 if (type_changed || std::strcmp(view->source_url, entity->text) != 0) { |
| 441 CefRefPtr<CanvasCefClient> client = state->clients[index]; | 508 CefRefPtr<CanvasCefClient> client = state->clients[index]; |
| 442 if (client && client->browser()) { | 509 if (client && client->browser()) { |
| 443 if (entity->type == CANVAS_ENTITY_IMAGE) { | 510 if (entity->type == CANVAS_ENTITY_IMAGE) { |
| 444 LoadImageSource(client->browser(), entity->text); | 511 LoadImageSource(client->browser(), entity->text); |
| 445 } else { | 512 } else { |
| 446 client->browser()->GetMainFrame()->LoadURL(entity->text); | 513 client->browser()->GetMainFrame()->LoadURL(entity->text); |
| 447 } | 514 } |
| 515 std::snprintf( | |
| 516 view->source_url, | |
| 517 sizeof(view->source_url), | |
| 518 "%s", | |
| 519 entity->text); | |
| 448 std::snprintf(view->url, sizeof(view->url), "%s", entity->text); | 520 std::snprintf(view->url, sizeof(view->url), "%s", entity->text); |
| 449 } | 521 } |
| 450 } | 522 } |
| 451 } | 523 } |
| 452 | 524 |
| 462 | 534 |
| 463 for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) { | 535 for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) { |
| 464 const Canvas_Entity *entity = &scene->p_entities[index]; | 536 const Canvas_Entity *entity = &scene->p_entities[index]; |
| 465 if ((entity->type != CANVAS_ENTITY_WEB_CONTENT && | 537 if ((entity->type != CANVAS_ENTITY_WEB_CONTENT && |
| 466 entity->type != CANVAS_ENTITY_IMAGE) || | 538 entity->type != CANVAS_ENTITY_IMAGE) || |
| 467 !IsVisible(camera, ContentBounds(camera, entity))) { | 539 !IsVisible(camera, ContentBounds(camera, scene, entity))) { |
| 468 continue; | 540 continue; |
| 469 } | 541 } |
| 470 Rectangle bounds = ContentBounds(camera, entity); | 542 Rectangle bounds = ContentBounds(camera, scene, entity); |
| 471 Canvas_Web_View *view = FindView(surface, entity->id); | 543 Canvas_Web_View *view = FindView(surface, entity->id); |
| 472 if (view) { | 544 if (view) { |
| 473 BindView(surface, view, entity, bounds); | 545 BindView(surface, view, entity, bounds); |
| 474 continue; | 546 continue; |
| 475 } | 547 } |
| 492 host->WasHidden(!view->active); | 564 host->WasHidden(!view->active); |
| 493 if (view->active) { | 565 if (view->active) { |
| 494 const Canvas_Entity *entity = FindSurfaceEntity(scene, view->entity_id); | 566 const Canvas_Entity *entity = FindSurfaceEntity(scene, view->entity_id); |
| 495 if (entity) { | 567 if (entity) { |
| 496 host->SetWindowlessFrameRate( | 568 host->SetWindowlessFrameRate( |
| 497 FrameRateForView(surface, view, ContentBounds(camera, entity))); | 569 FrameRateForView( |
| 570 surface, | |
| 571 view, | |
| 572 ContentBounds(camera, scene, entity))); | |
| 498 } | 573 } |
| 499 } | 574 } |
| 500 } | 575 } |
| 501 } | 576 } |
| 502 } | 577 } |
| 538 return CefExecuteProcess(args, app, nullptr); | 613 return CefExecuteProcess(args, app, nullptr); |
| 539 } | 614 } |
| 540 | 615 |
| 541 extern "C" boolean Canvas_Web_Surface_Init( | 616 extern "C" boolean Canvas_Web_Surface_Init( |
| 542 Canvas_Web_Surface *p_surface, | 617 Canvas_Web_Surface *p_surface, |
| 543 Dowa_Arena *p_arena) { | 618 Dowa_Arena *p_arena, |
| 619 boolean dark_mode) { | |
| 544 std::memset(p_surface, 0, sizeof(*p_surface)); | 620 std::memset(p_surface, 0, sizeof(*p_surface)); |
| 545 p_surface->p_arena = p_arena; | 621 p_surface->p_arena = p_arena; |
| 622 p_surface->dark_mode = dark_mode; | |
| 546 NativeState *state = static_cast<NativeState *>( | 623 NativeState *state = static_cast<NativeState *>( |
| 547 Dowa_Arena_Allocate(p_arena, sizeof(NativeState))); | 624 Dowa_Arena_Allocate(p_arena, sizeof(NativeState))); |
| 548 if (!state) return FALSE; | 625 if (!state) return FALSE; |
| 549 | 626 |
| 550 new (state) NativeState(); | 627 new (state) NativeState(); |
| 575 state->cef_initialized = TRUE; | 652 state->cef_initialized = TRUE; |
| 576 p_surface->initialized = TRUE; | 653 p_surface->initialized = TRUE; |
| 577 return TRUE; | 654 return TRUE; |
| 578 } | 655 } |
| 579 | 656 |
| 657 extern "C" void Canvas_Web_Surface_Set_Dark_Mode( | |
| 658 Canvas_Web_Surface *p_surface, | |
| 659 boolean dark_mode) { | |
| 660 if (!p_surface->initialized || p_surface->dark_mode == dark_mode) return; | |
| 661 p_surface->dark_mode = dark_mode; | |
| 662 } | |
| 663 | |
| 580 extern "C" boolean Canvas_Web_Surface_Update_Input( | 664 extern "C" boolean Canvas_Web_Surface_Update_Input( |
| 581 Canvas_Web_Surface *p_surface, | 665 Canvas_Web_Surface *p_surface, |
| 582 const Canvas_Camera *p_camera, | 666 const Canvas_Camera *p_camera, |
| 583 const Canvas_Scene *p_scene) { | 667 const Canvas_Scene *p_scene) { |
| 584 if (!p_surface->initialized || !p_surface->p_native) return FALSE; | 668 if (!p_surface->initialized || !p_surface->p_native) return FALSE; |
| 586 Vector2 mouse = GetMousePosition(); | 670 Vector2 mouse = GetMousePosition(); |
| 587 Canvas_Web_View *pointer_view = nullptr; | 671 Canvas_Web_View *pointer_view = nullptr; |
| 588 const Canvas_Entity *pointer_entity = nullptr; | 672 const Canvas_Entity *pointer_entity = nullptr; |
| 589 Rectangle pointer_bounds = {}; | 673 Rectangle pointer_bounds = {}; |
| 590 | 674 |
| 591 for (int32 index = CANVAS_MAX_WEB_VIEWS - 1; index >= 0; --index) { | 675 Vector2 world_mouse = Canvas_Camera_Screen_To_World(p_camera, mouse); |
| 592 Canvas_Web_View *view = &p_surface->views[index]; | 676 int32 top_index = Canvas_Scene_Topmost_Visual( |
| 593 if (!view->active) continue; | 677 p_scene, |
| 594 const Canvas_Entity *entity = FindSurfaceEntity(p_scene, view->entity_id); | 678 world_mouse, |
| 595 if (!entity || entity->type != CANVAS_ENTITY_WEB_CONTENT) continue; | 679 p_camera->zoom); |
| 596 Rectangle bounds = ContentBounds(p_camera, entity); | 680 if (top_index >= 0) { |
| 597 if (CheckCollisionPointRec(mouse, bounds)) { | 681 const Canvas_Entity *entity = &p_scene->p_entities[top_index]; |
| 598 pointer_view = view; | 682 if (entity->type == CANVAS_ENTITY_WEB_CONTENT && |
| 599 pointer_entity = entity; | 683 !entity->removing && |
| 600 pointer_bounds = bounds; | 684 EntityOpacity(entity) >= 0.99f) { |
| 601 break; | 685 Canvas_Web_View *view = FindView(p_surface, entity->id); |
| 686 Rectangle bounds = ContentBounds(p_camera, p_scene, entity); | |
| 687 if (view && | |
| 688 view->active && | |
| 689 CheckCollisionPointRec(mouse, bounds)) { | |
| 690 pointer_view = view; | |
| 691 pointer_entity = entity; | |
| 692 pointer_bounds = bounds; | |
| 693 } | |
| 602 } | 694 } |
| 603 } | 695 } |
| 604 | 696 |
| 605 if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { | 697 if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { |
| 606 p_surface->focused_entity_id = pointer_view ? pointer_view->entity_id : 0; | 698 p_surface->focused_entity_id = pointer_view ? pointer_view->entity_id : 0; |
| 623 const Canvas_Entity *input_entity = pointer_entity ? | 715 const Canvas_Entity *input_entity = pointer_entity ? |
| 624 pointer_entity : | 716 pointer_entity : |
| 625 FindSurfaceEntity(p_scene, input_view->entity_id); | 717 FindSurfaceEntity(p_scene, input_view->entity_id); |
| 626 if (!browser || !input_entity) return pointer_view != nullptr; | 718 if (!browser || !input_entity) return pointer_view != nullptr; |
| 627 | 719 |
| 628 Rectangle bounds = pointer_view ? pointer_bounds : ContentBounds(p_camera, input_entity); | 720 Rectangle bounds = pointer_view |
| 721 ? pointer_bounds | |
| 722 : ContentBounds(p_camera, p_scene, input_entity); | |
| 629 boolean inside = pointer_view == input_view; | 723 boolean inside = pointer_view == input_view; |
| 630 float scale_x = static_cast<float>(input_view->texture_width) / | 724 float scale_x = static_cast<float>(input_view->texture_width) / |
| 631 std::max(bounds.width, 1.0f); | 725 std::max(bounds.width, 1.0f); |
| 632 float scale_y = static_cast<float>(input_view->texture_height) / | 726 float scale_y = static_cast<float>(input_view->texture_height) / |
| 633 std::max(bounds.height, 1.0f); | 727 std::max(bounds.height, 1.0f); |
| 645 host->SendMouseClickEvent(event, MBT_LEFT, true, 1); | 739 host->SendMouseClickEvent(event, MBT_LEFT, true, 1); |
| 646 } | 740 } |
| 647 if (inside) { | 741 if (inside) { |
| 648 float wheel = GetMouseWheelMove(); | 742 float wheel = GetMouseWheelMove(); |
| 649 if (wheel != 0.0f) { | 743 if (wheel != 0.0f) { |
| 650 host->SendMouseWheelEvent(event, 0, static_cast<int>(wheel * 80.0f)); | 744 boolean control_zoom = |
| 745 IsKeyDown(KEY_LEFT_CONTROL) || | |
| 746 IsKeyDown(KEY_RIGHT_CONTROL) || | |
| 747 IsKeyDown(KEY_LEFT_SUPER) || | |
| 748 IsKeyDown(KEY_RIGHT_SUPER); | |
| 749 if (!control_zoom) { | |
| 750 host->SendMouseWheelEvent(event, 0, static_cast<int>(wheel * 80.0f)); | |
| 751 } | |
| 651 } | 752 } |
| 652 } | 753 } |
| 653 | 754 |
| 654 if (p_surface->focused_entity_id == input_view->entity_id) { | 755 if (p_surface->focused_entity_id == input_view->entity_id) { |
| 655 int32 codepoint = GetCharPressed(); | 756 int32 codepoint = GetCharPressed(); |
| 685 host->SendKeyEvent(key); | 786 host->SendKeyEvent(key); |
| 686 key.type = KEYEVENT_KEYUP; | 787 key.type = KEYEVENT_KEYUP; |
| 687 host->SendKeyEvent(key); | 788 host->SendKeyEvent(key); |
| 688 } | 789 } |
| 689 } | 790 } |
| 690 return inside || | 791 if (inside && |
| 691 p_surface->focused_entity_id == input_view->entity_id; | 792 (IsKeyDown(KEY_LEFT_CONTROL) || |
| 793 IsKeyDown(KEY_RIGHT_CONTROL) || | |
| 794 IsKeyDown(KEY_LEFT_SUPER) || | |
| 795 IsKeyDown(KEY_RIGHT_SUPER)) && | |
| 796 GetMouseWheelMove() != 0.0f) { | |
| 797 return FALSE; | |
| 798 } | |
| 799 return inside; | |
| 692 } | 800 } |
| 693 | 801 |
| 694 extern "C" void Canvas_Web_Surface_Sync( | 802 extern "C" void Canvas_Web_Surface_Sync( |
| 695 Canvas_Web_Surface *p_surface, | 803 Canvas_Web_Surface *p_surface, |
| 696 const Canvas_Camera *p_camera, | 804 const Canvas_Camera *p_camera, |
| 718 } | 826 } |
| 719 if (view->texture_ready && view->pixels_dirty) { | 827 if (view->texture_ready && view->pixels_dirty) { |
| 720 UpdateTexture(view->texture, view->p_pixels); | 828 UpdateTexture(view->texture, view->p_pixels); |
| 721 view->pixels_dirty = FALSE; | 829 view->pixels_dirty = FALSE; |
| 722 } | 830 } |
| 831 NativeState *state = static_cast<NativeState *>(p_surface->p_native); | |
| 832 CefRefPtr<CanvasCefClient> client = state->clients[index]; | |
| 833 if (client && client->browser() && | |
| 834 view->entity_type == CANVAS_ENTITY_WEB_CONTENT) { | |
| 835 if (!view->color_scheme_applied || | |
| 836 view->applied_dark_mode != p_surface->dark_mode) { | |
| 837 ApplyColorScheme(client->browser(), p_surface->dark_mode); | |
| 838 view->color_scheme_applied = TRUE; | |
| 839 view->applied_dark_mode = p_surface->dark_mode; | |
| 840 } | |
| 841 view->can_go_back = client->browser()->CanGoBack() ? TRUE : FALSE; | |
| 842 view->can_go_forward = client->browser()->CanGoForward() ? TRUE : FALSE; | |
| 843 } | |
| 723 } | 844 } |
| 724 } | 845 } |
| 725 | 846 |
| 726 extern "C" void Canvas_Web_Surface_Draw( | 847 extern "C" void Canvas_Web_Surface_Draw( |
| 727 const Canvas_Web_Surface *p_surface, | 848 const Canvas_Web_Surface *p_surface, |
| 728 const Canvas_Camera *p_camera, | 849 const Canvas_Camera *p_camera, |
| 729 const Canvas_Scene *p_scene) { | 850 const Canvas_Scene *p_scene) { |
| 730 for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) { | 851 for (size_t index = 0; |
| 731 const Canvas_Web_View *view = &p_surface->views[index]; | 852 index < Dowa_Array_Length(p_scene->p_entities); |
| 732 if (!view->active || !view->texture_ready) continue; | 853 ++index) { |
| 733 const Canvas_Entity *entity = FindSurfaceEntity(p_scene, view->entity_id); | 854 Canvas_Web_Surface_Draw_Entity( |
| 734 if (!entity) continue; | 855 p_surface, |
| 735 Rectangle bounds = ContentBounds(p_camera, entity); | 856 p_camera, |
| 736 float left = std::max(bounds.x, 0.0f); | 857 p_scene, |
| 737 float top = std::max(bounds.y, 0.0f); | 858 index); |
| 738 float right = std::min( | 859 } |
| 739 bounds.x + bounds.width, | 860 } |
| 740 static_cast<float>(p_camera->viewport_width)); | 861 |
| 741 float bottom = std::min( | 862 extern "C" void Canvas_Web_Surface_Draw_Entity( |
| 742 bounds.y + bounds.height, | 863 const Canvas_Web_Surface *p_surface, |
| 743 static_cast<float>(p_camera->viewport_height)); | 864 const Canvas_Camera *p_camera, |
| 744 if (right <= left || bottom <= top) continue; | 865 const Canvas_Scene *p_scene, |
| 745 | 866 size_t entity_index) { |
| 746 BeginScissorMode( | 867 if (entity_index >= Dowa_Array_Length(p_scene->p_entities)) return; |
| 747 static_cast<int>(left), | 868 const Canvas_Entity *entity = &p_scene->p_entities[entity_index]; |
| 748 static_cast<int>(top), | 869 if (entity->type != CANVAS_ENTITY_WEB_CONTENT && |
| 749 static_cast<int>(right - left), | 870 entity->type != CANVAS_ENTITY_IMAGE) { |
| 750 static_cast<int>(bottom - top)); | 871 return; |
| 751 DrawTexturePro( | 872 } |
| 752 view->texture, | 873 const Canvas_Web_View *view = FindView(p_surface, entity->id); |
| 753 Rectangle{ | 874 if (!view || !view->active || !view->texture_ready) return; |
| 754 0.0f, | 875 |
| 755 0.0f, | 876 Rectangle bounds = ContentBounds(p_camera, p_scene, entity); |
| 756 static_cast<float>(view->texture_width), | 877 float left = std::max(bounds.x, 0.0f); |
| 757 static_cast<float>(view->texture_height), | 878 float top = std::max(bounds.y, 0.0f); |
| 758 }, | 879 float right = std::min( |
| 759 bounds, | 880 bounds.x + bounds.width, |
| 760 Vector2{0.0f, 0.0f}, | 881 static_cast<float>(p_camera->viewport_width)); |
| 761 0.0f, | 882 float bottom = std::min( |
| 762 WHITE); | 883 bounds.y + bounds.height, |
| 763 EndScissorMode(); | 884 static_cast<float>(p_camera->viewport_height)); |
| 885 if (right <= left || bottom <= top) return; | |
| 886 | |
| 887 BeginScissorMode( | |
| 888 static_cast<int>(left), | |
| 889 static_cast<int>(top), | |
| 890 static_cast<int>(right - left), | |
| 891 static_cast<int>(bottom - top)); | |
| 892 DrawTexturePro( | |
| 893 view->texture, | |
| 894 Rectangle{ | |
| 895 0.0f, | |
| 896 0.0f, | |
| 897 static_cast<float>(view->texture_width), | |
| 898 static_cast<float>(view->texture_height), | |
| 899 }, | |
| 900 bounds, | |
| 901 Vector2{0.0f, 0.0f}, | |
| 902 0.0f, | |
| 903 Color{ | |
| 904 255, | |
| 905 255, | |
| 906 255, | |
| 907 static_cast<unsigned char>(EntityOpacity(entity) * 255.0f)}); | |
| 908 EndScissorMode(); | |
| 909 } | |
| 910 | |
| 911 extern "C" const char *Canvas_Web_Surface_URL( | |
| 912 const Canvas_Web_Surface *p_surface, | |
| 913 uint32 entity_id) { | |
| 914 const Canvas_Web_View *view = FindView(p_surface, entity_id); | |
| 915 return view && view->url[0] ? view->url : nullptr; | |
| 916 } | |
| 917 | |
| 918 extern "C" boolean Canvas_Web_Surface_Can_Go_Back( | |
| 919 const Canvas_Web_Surface *p_surface, | |
| 920 uint32 entity_id) { | |
| 921 const Canvas_Web_View *view = FindView(p_surface, entity_id); | |
| 922 return view ? view->can_go_back : FALSE; | |
| 923 } | |
| 924 | |
| 925 extern "C" boolean Canvas_Web_Surface_Can_Go_Forward( | |
| 926 const Canvas_Web_Surface *p_surface, | |
| 927 uint32 entity_id) { | |
| 928 const Canvas_Web_View *view = FindView(p_surface, entity_id); | |
| 929 return view ? view->can_go_forward : FALSE; | |
| 930 } | |
| 931 | |
| 932 extern "C" void Canvas_Web_Surface_Go_Back( | |
| 933 Canvas_Web_Surface *p_surface, | |
| 934 uint32 entity_id) { | |
| 935 if (!p_surface->p_native) return; | |
| 936 Canvas_Web_View *view = FindView(p_surface, entity_id); | |
| 937 if (!view) return; | |
| 938 NativeState *state = static_cast<NativeState *>(p_surface->p_native); | |
| 939 CefRefPtr<CanvasCefClient> client = state->clients[ViewIndex(p_surface, view)]; | |
| 940 if (client && client->browser() && client->browser()->CanGoBack()) { | |
| 941 client->browser()->GoBack(); | |
| 942 } | |
| 943 } | |
| 944 | |
| 945 extern "C" void Canvas_Web_Surface_Go_Forward( | |
| 946 Canvas_Web_Surface *p_surface, | |
| 947 uint32 entity_id) { | |
| 948 if (!p_surface->p_native) return; | |
| 949 Canvas_Web_View *view = FindView(p_surface, entity_id); | |
| 950 if (!view) return; | |
| 951 NativeState *state = static_cast<NativeState *>(p_surface->p_native); | |
| 952 CefRefPtr<CanvasCefClient> client = state->clients[ViewIndex(p_surface, view)]; | |
| 953 if (client && client->browser() && client->browser()->CanGoForward()) { | |
| 954 client->browser()->GoForward(); | |
| 764 } | 955 } |
| 765 } | 956 } |
| 766 | 957 |
| 767 extern "C" boolean Canvas_Web_Surface_Is_Focused( | 958 extern "C" boolean Canvas_Web_Surface_Is_Focused( |
| 768 const Canvas_Web_Surface *p_surface) { | 959 const Canvas_Web_Surface *p_surface) { |