#include "markdown_helper.h" #include "core/style.h" #include "logger.h" #include "imgui/imgui_image.h" #include "resources.h" #include "common/image/image.h" #include #if defined(_WIN32) #include #elif defined(__ANDROID__) #include extern struct android_app *android_app_ptr; #endif namespace widgets { void MarkdownHelper::link_callback(ImGui::MarkdownLinkCallbackData data_) { std::string url(data_.link, data_.linkLength); if (!data_.isImage) { logger->info("Opening URL " + url); #if defined(_WIN32) ShellExecuteA(0, 0, url.c_str(), 0, 0, SW_SHOW); #elif defined(__APPLE__) system(std::string("open " + url).c_str()); #elif defined(__ANDROID__) JavaVM *java_vm = android_app_ptr->activity->vm; JNIEnv *java_env = NULL; jint jni_return = java_vm->GetEnv((void **)&java_env, JNI_VERSION_1_6); if (jni_return == JNI_ERR) throw std::runtime_error("Could not get JNI environement"); jni_return = java_vm->AttachCurrentThread(&java_env, NULL); if (jni_return != JNI_OK) throw std::runtime_error("Could not attach to thread"); jclass native_activity_clazz = java_env->GetObjectClass(android_app_ptr->activity->clazz); if (native_activity_clazz == NULL) throw std::runtime_error("Could not get MainActivity class"); jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "openURL", "(Ljava/lang/String;)V"); if (method_id == NULL) throw std::runtime_error("Could not get methode ID"); jstring jurl = java_env->NewStringUTF(url.c_str()); java_env->CallVoidMethod(android_app_ptr->activity->clazz, method_id, jurl); jni_return = java_vm->DetachCurrentThread(); if (jni_return != JNI_OK) throw std::runtime_error("Could not detach from thread"); #else if (system(std::string("xdg-open " + url).c_str()) != 0) logger->error("Error opening URL!"); #endif } } ImGui::MarkdownImageData MarkdownHelper::image_callback(ImGui::MarkdownLinkCallbackData data_) { MarkdownHelper *tthis = (MarkdownHelper *)data_.userData; std::string image_path(data_.link, data_.linkLength); ImGui::MarkdownImageData imageData; if (tthis->texture_buffer.count(image_path)) { imageData = tthis->texture_buffer[image_path]; } else { imageData.isValid = false; if (std::filesystem::exists(resources::getResourcePath(image_path))) { logger->trace("Loading image for markdown : " + image_path); image::Image img; img.load_img(resources::getResourcePath(image_path)); unsigned int text_id = makeImageTexture(); uint32_t *output_buffer = new uint32_t[img.width() * img.height()]; uchar_to_rgba(img.data(), output_buffer, img.width() * img.height(), img.channels()); updateImageTexture(text_id, output_buffer, img.width(), img.height()); delete[] output_buffer; imageData.isValid = true; imageData.useLinkCallback = false; imageData.user_texture_id = (ImTextureID)(intptr_t)text_id; imageData.size = ImVec2(img.width(), img.height()); } tthis->texture_buffer.emplace(image_path, imageData); } // For image resize when available size.x > image width, add ImVec2 const contentSize = ImGui::GetContentRegionAvail(); if (imageData.size.x > contentSize.x) { float const ratio = imageData.size.y / imageData.size.x; imageData.size.x = contentSize.x; imageData.size.y = contentSize.x * ratio; } return imageData; } MarkdownHelper::MarkdownHelper() { mdConfig.linkCallback = link_callback; mdConfig.imageCallback = image_callback; } void MarkdownHelper::render() { mdConfig.headingFormats[0] = {style::bigFont, true}; mdConfig.headingFormats[1] = {style::bigFont, true}; mdConfig.headingFormats[2] = {style::baseFont, true}; mdConfig.userData = (void *)this; ImGui::Markdown(markdown_.c_str(), markdown_.length(), mdConfig); } void MarkdownHelper::set_md(std::string md) { markdown_ = md; texture_buffer.clear(); } }