103 lines
3.6 KiB
C++
103 lines
3.6 KiB
C++
#pragma once
|
|
|
|
#include "imgui.h"
|
|
#include "AnimationUtils.h"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
struct RichTooltipState {
|
|
bool pinned = false;
|
|
ImVec2 pinnedPos = ImVec2(0, 0);
|
|
std::string pinnedContent;
|
|
};
|
|
|
|
inline std::unordered_map<std::string, RichTooltipState>& richTooltipStates() {
|
|
static std::unordered_map<std::string, RichTooltipState> states;
|
|
return states;
|
|
}
|
|
|
|
inline void richRenderMarkdownLine(const std::string& line, ImFont* monoFont) {
|
|
std::string trimmed = line;
|
|
while (!trimmed.empty() && (trimmed.back() == '\r' || trimmed.back() == '\n')) {
|
|
trimmed.pop_back();
|
|
}
|
|
if (trimmed.rfind("## ", 0) == 0) {
|
|
ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s", trimmed.substr(3).c_str());
|
|
} else if (trimmed.rfind("### ", 0) == 0) {
|
|
ImGui::TextColored(ImVec4(0.6f, 0.85f, 0.95f, 1.0f), "%s", trimmed.substr(4).c_str());
|
|
} else if (trimmed.rfind("- ", 0) == 0) {
|
|
ImGui::BulletText("%s", trimmed.substr(2).c_str());
|
|
} else if (trimmed.rfind("```", 0) == 0) {
|
|
ImGui::TextDisabled("%s", trimmed.c_str());
|
|
} else if (trimmed.rfind("`", 0) == 0 && trimmed.size() > 1) {
|
|
ImGui::PushFont(monoFont);
|
|
ImGui::TextUnformatted(trimmed.c_str());
|
|
ImGui::PopFont();
|
|
} else {
|
|
ImGui::TextWrapped("%s", trimmed.c_str());
|
|
}
|
|
}
|
|
|
|
inline void richRenderMarkdown(const std::string& text, ImFont* monoFont) {
|
|
std::istringstream ss(text);
|
|
std::string line;
|
|
while (std::getline(ss, line)) {
|
|
richRenderMarkdownLine(line, monoFont);
|
|
}
|
|
}
|
|
|
|
inline void renderRichTooltip(const std::string& id,
|
|
const std::string& content,
|
|
bool hovered,
|
|
bool reduceMotion,
|
|
ImFont* monoFont,
|
|
float maxWidth = 420.0f,
|
|
float maxHeight = 240.0f) {
|
|
auto& state = richTooltipStates()[id];
|
|
const double now = ImGui::GetTime();
|
|
float alpha = AnimationUtils::tooltipAlpha(id, hovered, now, reduceMotion);
|
|
if (alpha > 0.01f && hovered && !state.pinned) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * alpha);
|
|
ImGui::BeginTooltip();
|
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + maxWidth);
|
|
richRenderMarkdown(content, monoFont);
|
|
ImGui::PopTextWrapPos();
|
|
ImGui::Separator();
|
|
if (ImGui::SmallButton("Pin")) {
|
|
state.pinned = true;
|
|
state.pinnedPos = ImGui::GetMousePos();
|
|
state.pinnedContent = content;
|
|
}
|
|
ImGui::EndTooltip();
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
if (state.pinned) {
|
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDocking |
|
|
ImGuiWindowFlags_NoResize |
|
|
ImGuiWindowFlags_AlwaysAutoResize |
|
|
ImGuiWindowFlags_NoSavedSettings;
|
|
ImGui::SetNextWindowPos(state.pinnedPos, ImGuiCond_FirstUseEver);
|
|
bool open = true;
|
|
if (ImGui::Begin(("Tooltip##" + id).c_str(), &open, flags)) {
|
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + maxWidth);
|
|
ImGui::BeginChild(("##tip_scroll_" + id).c_str(), ImVec2(maxWidth, maxHeight), false);
|
|
richRenderMarkdown(state.pinnedContent, monoFont);
|
|
ImGui::EndChild();
|
|
ImGui::PopTextWrapPos();
|
|
if (ImGui::SmallButton("Close")) {
|
|
open = false;
|
|
}
|
|
}
|
|
ImGui::End();
|
|
if (!open) {
|
|
state.pinned = false;
|
|
state.pinnedContent.clear();
|
|
}
|
|
}
|
|
}
|