#include "pch.h" #include "app.h" #ifdef __ANDROID__ void displayKeyboard(bool pShow); void android_pick_file(std::function callback); void android_pick_file_save(std::function callback); std::string android_get_clipboard(); bool android_set_clipboard(const std::string& s); #elif _WIN32 std::string win32_open_file(const char* filter); std::string win32_save_file(const char* filter); std::string win32_open_dir(); void win32_show_cursor(bool visible); bool win32_clipboard_set_text(const std::string & s); std::string win32_clipboard_get_text(); #elif __APPLE__ #else #include #endif std::string App::clipboard_get_text() { #if _WIN32 return win32_clipboard_get_text(); #elif __IOS__ return [ios_view clipboard_get_string]; #elif __OSX__ return [osx_view clipboard_get_string]; #elif __ANDROID__ return android_get_clipboard(); #endif } bool App::clipboard_set_text(const std::string& s) { #if _WIN32 return win32_clipboard_set_text(s); #elif __IOS__ return [ios_view clipboard_set_string:s]; #elif __OSX__ return [osx_view clipboard_set_string:s]; #elif __ANDROID__ return android_set_clipboard(s); #endif } 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(w, h, -1, GL_RGBA8, true); redraw = true; width = w; height = h; } void App::show_cursor() { #ifdef _WIN32 win32_show_cursor(true); #elif __OSX__ [osx_view show_cursor:true]; #endif } void App::hide_cursor() { #ifdef _WIN32 win32_show_cursor(false); #elif __OSX__ [osx_view show_cursor:false]; #endif } void App::showKeyboard() { LOG("show keyboard"); redraw = true; #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [ios_view show_keyboard]; }); #elif __ANDROID__ displayKeyboard(true); #endif } void App::hideKeyboard() { LOG("hide keyboard"); redraw = true; #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [ios_view hide_keyboard]; }); #elif __ANDROID__ displayKeyboard(false); #endif } void App::pick_image(std::function callback) { redraw = true; #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [ios_view pick_photo:callback]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil]; std::string path = [osx_view pick_file:fileTypes]; if (!path.empty()) callback(path); }); #elif __ANDROID__ android_pick_file(callback); #elif _WIN32 std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png"); if (!path.empty()) callback(path); #endif } void App::pick_file(std::vector types, std::function callback) { redraw = true; #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:types.size()]; for (const auto& t : types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; [ios_view pick_file:fileTypes then:callback]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:types.size()]; for (const auto& t : types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; std::string path = [osx_view pick_file:fileTypes]; if (!path.empty()) callback(path); }); #elif __ANDROID__ android_pick_file(callback); #elif _WIN32 std::string filter = "Supported Files ("; bool first_type = true; for (auto& t : types) { filter.append(std::string(first_type ? "" : " ,") + "*." + t); first_type = false; } filter.append(")"); filter.push_back(0); first_type = true; for (auto& t : types) { filter.append(std::string(first_type ? "" : ";") + "*." + t); first_type = false; } filter.push_back(0); std::string path = win32_open_file(filter.c_str()); if (!path.empty()) callback(path); #else if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) callback(p); #endif } #if __IOS__ void App::pick_file_save(const std::string& type, std::function writer, std::function callback) { redraw = true; auto mb = input_box("Insert", "File name"); std::string placeholder = "name." + type; mb->m_field_text->set_text(placeholder.c_str()); mb->on_submit = [this, writer, type, callback, mb] (Node* target, std::string name) { std::string ext = "." + type; std::string path = data_path + "/" + name; if (name.find(ext) == std::string::npos) path += ext; std::thread([=]{ writer(path); dispatch_async(dispatch_get_main_queue(), ^{ [ios_view pick_file_save:path]; }); mb->destroy(); callback(true); }).detach(); }; mb->btn_cancel->on_click = [=] (Node* target) { callback(false); mb->destroy(); }; } #else void App::pick_file_save(std::vector types, std::function callback) { redraw = true; #if __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ //NSArray* fileTypes = [NSArray arrayWithObjects:@"ppi", @"PPI", nil]; NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:types.size()]; for (const auto& t : types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; std::string path = [osx_view pick_file_save:fileTypes]; if (!path.empty()) callback(path); }); #elif __ANDROID__ android_pick_file_save(callback); #elif _WIN32 std::string filter = "Supported Files ("; bool first_type = true; for (auto& t : types) { filter.append(std::string(first_type ? "" : " ,") + "*." + t); first_type = false; } filter.append(")"); filter.push_back(0); first_type = true; for (auto& t : types) { filter.append(std::string(first_type ? "" : ";") + "*." + t); first_type = false; } filter.push_back(0); std::string path = win32_save_file(filter.c_str()); if (!path.empty()) callback(path); #endif } #endif void App::pick_dir(std::function callback) { redraw = true; #ifdef __IOS__ // NOT IMPLEMENTED #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ std::string path = [osx_view pick_dir]; if (!path.empty()) callback(path); }); #elif __ANDROID__ // NOT IMPLEMENTED #elif _WIN32 // TODO: to be implemented std::string path = win32_open_dir(); if (!path.empty()) callback(path); #endif } void App::display_file(std::string path) { #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [ios_view display_file:path]; }); #elif __OSX__ [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithUTF8String:path.c_str()]]; // dispatch_async(dispatch_get_main_queue(), ^{ // std::string path = [osx_view pick_file]; // if (!path.empty()) // callback(path); // }); #elif __ANDROID__ //displayKeyboard(and_app, false); #elif _WIN32 // std::string path = win32_open_file(); // if (!path.empty()) // callback(path); #endif } void App::share_file(std::string path) { if (path.empty()) { message_box("Sharing failed", "Please save the document before sharing it."); return; } #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [ios_view share_file:[NSString stringWithUTF8String:path.c_str()]]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ [osx_view share_file:[NSString stringWithUTF8String:path.c_str()]]; }); #elif __ANDROID__ #elif _WIN32 // not implemented #endif } 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::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; }