#include "pch.h" #include "app.h" #include "app_core/document_platform_io.h" #include "app_core/document_sharing.h" #include "platform_api/platform_services.h" #include "platform_legacy/legacy_platform_services.h" #include "renderer_gl/opengl_capabilities.h" namespace { [[nodiscard]] GLint rgba8_internal_format() noexcept { return static_cast(pp::renderer::gl::rgba8_internal_format()); } [[nodiscard]] bool should_dispatch_keyboard_visibility(bool visible) noexcept { const auto action = pp::app::plan_virtual_keyboard(visible); return visible ? action == pp::app::VirtualKeyboardAction::show_keyboard : action == pp::app::VirtualKeyboardAction::hide_keyboard; } [[nodiscard]] bool should_dispatch_cursor_visibility(bool visible) noexcept { const auto action = pp::app::plan_cursor_visibility(visible); return visible ? action == pp::app::CursorVisibilityAction::show_cursor : action == pp::app::CursorVisibilityAction::hide_cursor; } } namespace { [[nodiscard]] pp::platform::PlatformServices& active_platform_services() { if (App::I) { if (auto* services = App::I->platform_services()) return *services; } return pp::platform::legacy::platform_services(); } } void App::set_platform_services(pp::platform::PlatformServices* services) noexcept { platform_services_ = services; } pp::platform::PlatformServices* App::platform_services() const noexcept { return platform_services_; } std::string App::clipboard_get_text() { if (pp::app::plan_clipboard_read() != pp::app::ClipboardReadAction::read_text) return {}; return active_platform_services().clipboard_text(); } bool App::clipboard_set_text(const std::string& s) { if (pp::app::plan_clipboard_write(s) != pp::app::ClipboardWriteAction::write_text) return false; return active_platform_services().set_clipboard_text(s); } void App::stacktrace() { #if __OSX__ NSString* callstack = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; LOG("callstack:\n%s", [callstack cStringUsingEncoding:NSUTF8StringEncoding]); #endif } void App::crash_test() { #ifdef __IOS__ [ios_view crash]; #elif __OSX__ [osx_view hockeyapp_crash]; #elif defined(_WIN32) __debugbreak(); #elif defined(__ANDROID__) int *x = nullptr; *x = 42; LOG("%d", *x); #endif } void App::tick(float dt) { if (auto* main = layout_designer[main_id]) main->tick(dt); if (auto* main = layout[main_id]) main->tick(dt); } void App::resize(float w, float h) { LOG("App::resize %d %d", (int)w, (int)h); uirtt.create(static_cast(w), static_cast(h), -1, rgba8_internal_format(), true); redraw = true; width = w; height = h; } void App::show_cursor() { if (!should_dispatch_cursor_visibility(true)) return; active_platform_services().set_cursor_visible(true); } void App::hide_cursor() { if (!should_dispatch_cursor_visibility(false)) return; active_platform_services().set_cursor_visible(false); } void App::showKeyboard() { LOG("show keyboard"); redraw = true; if (!should_dispatch_keyboard_visibility(true)) return; active_platform_services().set_virtual_keyboard_visible(true); } void App::hideKeyboard() { LOG("hide keyboard"); redraw = true; if (!should_dispatch_keyboard_visibility(false)) return; active_platform_services().set_virtual_keyboard_visible(false); } void App::pick_image(std::function callback) { redraw = true; active_platform_services().pick_image(std::move(callback)); } void App::pick_file(std::vector types, std::function callback) { redraw = true; active_platform_services().pick_file(std::move(types), std::move(callback)); } #if __IOS__ void App::pick_file_save(const std::string& type, const std::string& default_name, std::function writer, std::function callback) { redraw = true; std::string ext = "." + type; std::string path = tmp_path + "/" + default_name + ext; std::thread([=]{ writer(path); save_prepared_file(path, default_name + ext, callback); }).detach(); } #elif __WEB__ void App::pick_file_save(const std::string& type, const std::string& default_name, std::function writer, std::function callback) { redraw = true; auto path = data_path + "/" + default_name + "." + type; LOG("App::pick_file_save %s", path.c_str()); writer(path); save_prepared_file(path, default_name + "." + type, std::move(callback)); } #else void App::pick_file_save(std::vector types, std::function callback) { redraw = true; active_platform_services().pick_save_file(std::move(types), std::move(callback)); } #endif void App::pick_dir(std::function callback) { redraw = true; active_platform_services().pick_directory(std::move(callback)); } void App::display_file(std::string path) { if (pp::app::plan_display_file(path) == pp::app::DisplayFileAction::ignore_empty_path) return; active_platform_services().display_file(path); } void App::share_file(std::string path) { const auto plan = pp::app::plan_document_share(path); if (plan == pp::app::DocumentShareAction::show_save_required_warning) { message_box("Sharing failed", "Please save the document before sharing it."); return; } active_platform_services().share_file(path); } void App::save_prepared_file( std::string path, std::string suggested_name, std::function callback) { active_platform_services().save_prepared_file( path, suggested_name, [callback = std::move(callback)](std::string saved_path, bool saved) { callback(saved_path, saved); }); } bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser) { redraw = true; MouseEvent e; e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL; e.m_pos = { x / zoom, y / zoom }; e.m_pressure = pressure; e.m_source = source; e.m_eraser = eraser; kEventResult ret = kEventResult::Available; if (auto* main = layout_designer[main_id]) return main->on_event(&e) == kEventResult::Consumed; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::mouse_move(float x, float y, float pressure, kEventSource source, bool eraser) { cursor = { x / zoom, y / zoom }; redraw = true; MouseEvent e; e.m_type = kEventType::MouseMove; e.m_pos = { x / zoom, y / zoom }; e.m_pressure = pressure; e.m_source = source; e.m_eraser = eraser; kEventResult ret = kEventResult::Available; if (auto* main = layout_designer[main_id]) return main->on_event(&e) == kEventResult::Consumed; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::mouse_up(int button, float x, float y, kEventSource source, bool eraser) { redraw = true; MouseEvent e; e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL; e.m_pos = { x / zoom, y / zoom }; e.m_source = source; e.m_eraser = eraser; kEventResult ret = kEventResult::Available; if (auto* main = layout_designer[main_id]) return main->on_event(&e) == kEventResult::Consumed; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::mouse_scroll(float x, float y, float delta) { redraw = true; MouseEvent e; e.m_type = kEventType::MouseScroll; e.m_pos = { x / zoom, y / zoom }; e.m_scroll_delta = delta; kEventResult ret = kEventResult::Available; if (auto* main = layout_designer[main_id]) return main->on_event(&e) == kEventResult::Consumed; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::mouse_cancel(int button) { redraw = true; MouseEvent e; e.m_type = kEventType::MouseCancel; kEventResult ret = kEventResult::Available; if (auto* main = layout_designer[main_id]) return main->on_event(&e) == kEventResult::Consumed; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1) { redraw = true; GestureEvent e; glm::vec2 p = glm::lerp(p0, p1, 0.5f); e.m_type = kEventType::GestureStart; e.m_pos = p / glm::vec2(zoom); e.m_distance = glm::distance(p0, p1); gesture_p0 = p0; gesture_p1 = p1; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1) { redraw = true; GestureEvent e; glm::vec2 p = glm::lerp(p0, p1, 0.5f); e.m_type = kEventType::GestureMove; e.m_pos = p / glm::vec2(zoom); e.m_distance = glm::distance(p0, p1); e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1); e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f); kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::gesture_end() { redraw = true; GestureEvent e; e.m_type = kEventType::GestureEnd; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::touch_tap(const glm::vec2& pos, int fingers, int tap_count) { redraw = true; TouchEvent e; e.m_type = kEventType::TouchTap; e.m_finger_count = fingers; e.m_tap_count = tap_count; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::key_down(kKey key) { if (key == kKey::KeySpacebar && vr_active) canvas->m_canvas->m_cam_rot = vr_rot; redraw = true; keys[(int)key] = true; KeyEvent e; e.m_type = kEventType::KeyDown; e.m_key = key; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::key_up(kKey key) { redraw = true; keys[(int)key] = false; KeyEvent e; e.m_type = kEventType::KeyUp; e.m_key = key; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } bool App::key_char(char key) { redraw = true; KeyEvent e; e.m_type = kEventType::KeyChar; e.m_char = key; kEventResult ret = kEventResult::Available; if (auto* main = layout[main_id]) ret = main->on_event(&e); return ret == kEventResult::Consumed; } void App::toggle_ui() { auto m = layout[main_id]->m_children[1]; ui_visible = !ui_visible; for (int i = 1; i < m->m_children.size(); i++) m->m_children[i]->m_display = ui_visible; } void App::set_stylus() { has_stylus = true; if (canvas) canvas->m_canvas->m_touch_lock = true; }