├── .github ├── FUNDING.yaml ├── auto-issue-template.md ├── dependabot.yml ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── feature-request.md │ └── bug-report.md └── workflows │ └── release.yaml ├── .gitignore ├── UImGuiString.hpp ├── LICENSE ├── Common.h ├── README.md ├── C ├── CUImGuiTextUtils.h └── CUImGuiTextUtils.cpp ├── UImGuiTextUtils.cpp ├── UImGuiTextUtilsTypefaces.cpp ├── UImGuiTextUtilsTextMarkdown.cpp └── UImGuiTextUtils.hpp /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: MadLadSquad 2 | ko_fi: madladsquad 3 | -------------------------------------------------------------------------------- /.github/auto-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Daily automatic dependency update issue! 3 | assignees: Madman10K 4 | labels: enhancement 5 | --- 6 | Automatic issue for updating the submodules to latest! 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | groups: 8 | github-actions: 9 | patterns: 10 | - "*" 11 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing guidelines 2 | These are the guidelines 3 | - Follow the ToS 4 | - Follow the CoC 5 | - Contribute useful information, i.e. fixing a bug, adding a feature, etc, not removing whitespaces, etc 6 | - Check if your code works and test for any vulnerabilities before contributing 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /UImGuiString.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 3 | #ifdef UIMGUI_TEXT_UTILS_CUSTOM_STRING 4 | #ifdef UIMGUI_TEXT_UTILS_CUSTOM_STRING_INCLUDE 5 | #include UIMGUI_TEXT_UTILS_CUSTOM_STRING_INCLUDE 6 | namespace UImGui 7 | { 8 | typedef UIMGUI_TEXT_UTILS_CUSTOM_STRING TString; 9 | } 10 | #else 11 | #error UIMGUI_TEXT_UTILS_CUSTOM_STRING defined but UEXEC_CUSTOM_STRING_INCLUDE not defined, it is needed to include the necessary headers for the string, and should contain the name of the header wrapped in "" 12 | #endif 13 | #else 14 | #include 15 | namespace UImGui 16 | { 17 | typedef std::string TString; 18 | } 19 | #endif 20 | #endif 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behaviour: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See an error 19 | 20 | **Expected behaviour** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | Share screenshots if any 25 | 26 | **Technical information** 27 | - OS: (eg. Windows) 28 | - Release: (eg. 0.5.1) 29 | - Any other applicable technical information 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 - 2026 MadLadSquad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | name: Create Release 6 | 7 | jobs: 8 | build: 9 | name: Create Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v6 14 | with: 15 | lfs: true 16 | submodules: true 17 | - name: Checkout submodules 18 | shell: bash 19 | run: | 20 | git submodule sync --recursive 21 | git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 22 | - name: Get tag 23 | shell: bash 24 | id: mod 25 | run: | 26 | tag="${{ github.ref_name }}" 27 | echo "out=untitled-imgui-text-utils-${tag:1}" >> $GITHUB_OUTPUT 28 | - name: Create archive 29 | shell: bash 30 | run: | 31 | rm -rf .git/ 32 | mkdir "${{ steps.mod.outputs.out }}" || exit 33 | mv * ${{ steps.mod.outputs.out }}/ || echo "Errors in move" 34 | mv .* "${{ steps.mod.outputs.out }}" || echo "Errors in move" 35 | 36 | tar cfJ ${{ steps.mod.outputs.out }}.tar.xz ${{ steps.mod.outputs.out }} || echo "Might have failed" 37 | - name: Create Release 38 | uses: softprops/action-gh-release@v2 39 | with: 40 | body: | 41 | Check our discord for patch notes: https://discord.gg/4kyWu2Vu 42 | More on what is done this month can be found on the latest newsletter entry: https://madladsquad.com/#monthly-newsletter 43 | draft: false 44 | prerelease: false 45 | files: ${{ steps.mod.outputs.out }}.tar.xz 46 | generate_release_notes: false 47 | -------------------------------------------------------------------------------- /Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" 9 | { 10 | #endif 11 | #ifdef _WIN32 12 | #ifdef MLS_EXPORT_LIBRARY 13 | #ifdef MLS_LIB_COMPILE 14 | #define MLS_PUBLIC_API __declspec(dllexport) 15 | #else 16 | #define MLS_PUBLIC_API __declspec(dllimport) 17 | #endif 18 | #else 19 | #define MLS_PUBLIC_API 20 | #endif 21 | #else 22 | #define MLS_PUBLIC_API 23 | #endif 24 | 25 | typedef struct UImGui_TextUtils_Colour 26 | { 27 | union 28 | { 29 | float coords[4]; 30 | struct 31 | { 32 | float x; 33 | float y; 34 | float z; 35 | float w; 36 | }; 37 | }; 38 | } UImGui_TextUtils_Colour; 39 | 40 | #define UIMGUI_COL32_R_SHIFT 0 41 | #define UIMGUI_COL32_G_SHIFT 8 42 | #define UIMGUI_COL32_B_SHIFT 16 43 | #define UIMGUI_COL32_A_SHIFT 24 44 | #define UIMGUI_COL32(R,G,B,A) (((UImGui_TextUtils_Colour)(A)<(&a); 13 | } 14 | 15 | UImGui::TextUtils::Colour::operator UImGui_TextUtils_Colour() const noexcept 16 | { 17 | return data; 18 | } 19 | 20 | UImGui::TextUtils::Colour::operator ImU32() const noexcept 21 | { 22 | return ImGui::ColorConvertFloat4ToU32(*reinterpret_cast(&data)); 23 | } 24 | 25 | UImGui::TextUtils::Colour::operator ImVec4() const noexcept 26 | { 27 | return *reinterpret_cast(&data); 28 | } 29 | 30 | UImGui::TextUtils::Colour UImGui::TextUtils::getDefaultTextColour() noexcept 31 | { 32 | return *reinterpret_cast(&ImGui::GetStyle().Colors[ImGuiCol_Text]); 33 | } 34 | 35 | void UImGui::TextUtils::initTextUtilsData(TextUtilsData* data) noexcept 36 | { 37 | *getData() = data; 38 | } 39 | 40 | UImGui::TextUtilsData* UImGui::TextUtils::getTextUtilsData() noexcept 41 | { 42 | return *getData(); 43 | } 44 | 45 | void UImGui::TextUtils::ShowDemoWindow(void* bOpen) noexcept 46 | { 47 | ImGui::Begin("UntitledImGuiTextUtils Demo Window", static_cast(bOpen)); 48 | 49 | ImGui::TextWrapped("Welcome to the UntitledImGuiTextUtils demo. This window showcases all widgets."); 50 | 51 | static int i = 0; 52 | 53 | ImGui::SliderInt("Modify integer", &i, 0, 200); 54 | if (ImGui::CollapsingHeader("Text Rendering")) 55 | { 56 | ImGui::SeparatorText("Rendering using additional fonts"); 57 | 58 | Bold("This is bold text, it has formatting: %d.", i); 59 | Italic("This is italic text, it has formatting: %d.", i); 60 | BoldItalic("This is bold italic text, it has formatting: %d.", i); 61 | Monospace("This is monospaced text, it has formatting: %d.", i); 62 | Small("This is small text, it has formatting: %d.", i); 63 | 64 | 65 | ImGui::SeparatorText("Versions with text wrapping"); 66 | 67 | BoldWrapped("This is bold text, it has formatting: %d. The quick brown fox jumps over the lazy dog. " 68 | "The quick brown fox jumps over the lazy dog", i); 69 | ItalicWrapped("This is italic text, it has formatting: %d. The quick brown fox jumps over the lazy dog. " 70 | "The quick brown fox jumps over the lazy dog", i); 71 | BoldItalicWrapped("This is bold italic text, it has formatting: %d. The quick brown fox jumps over the " 72 | "lazy dog. The quick brown fox jumps over the lazy dog", i); 73 | MonospaceWrapped("This is monospaced text, it has formatting: %d. The quick brown fox jumps over the lazy" 74 | " dog. The quick brown fox jumps over the lazy dog", i); 75 | SmallWrapped("This is small text, it has formatting: %d. The quick brown fox jumps over the lazy dog. " 76 | "The quick brown fox jumps over the lazy dog", i); 77 | } 78 | 79 | if (ImGui::CollapsingHeader("Annotations")) 80 | { 81 | static const TString subscript = "3"; 82 | static const TString superscript = "6"; 83 | static const TString rubyText = "Kanji"; 84 | static const TString rubyAnnotationText = "han4zi4"; 85 | static const TString codeInlinePadding = "ImGuiStyleVar_WindowPadding"; 86 | static const TString codeInlineSpacing = "ImGuiStyleVar_ItemSpacing"; 87 | static const TString codeInlineSettings = "{ 0.0f, 0.0f }"; 88 | 89 | ImGui::SeparatorText("Subscript and superscript"); 90 | 91 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0.0f, 0.0f }); 92 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0.0f, 0.0f }); 93 | 94 | ImGui::Text("C"); 95 | ImGui::SameLine(); 96 | SubSuperscript(subscript, superscript); 97 | ImGui::SameLine(); 98 | ImGui::Text("= 20"); 99 | 100 | ImGui::PopStyleVar(2); 101 | 102 | ImGui::SeparatorText("Ruby text"); 103 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0.0f, 0.0f }); 104 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0.0f, 0.0f }); 105 | 106 | Ruby(rubyText, rubyAnnotationText); 107 | ImGui::PopStyleVar(2); 108 | 109 | ImGui::SeparatorText("* Note"); 110 | ImGui::Text("All widgets here were rendered with"); 111 | CodeInline(codeInlinePadding); 112 | ImGui::SameLine(); 113 | ImGui::Text("and"); 114 | ImGui::SameLine(); 115 | CodeInline(codeInlineSpacing); 116 | ImGui::SameLine(); 117 | ImGui::Text("set to"); 118 | ImGui::SameLine(); 119 | CodeInline(codeInlineSettings); 120 | } 121 | 122 | if (ImGui::CollapsingHeader("Rich text")) 123 | { 124 | static const TString underlineWrapped = "Underline with text wrapping. The quick brown fox jumps over the " 125 | "lazy dog. The quick brown fox jumps over the lazy dog."; 126 | static const TString strikethroughWrapped = "Strikethrough with text wrapping. The quick brown fox jumps " 127 | "over the lazy dog. The quick brown fox jumps over the lazy dog."; 128 | static const TString linkWrapped = "Link with text wrapping. The quick brown fox jumps over the lazy dog. " 129 | "The quick brown fox jumps over the lazy dog."; 130 | static const TString highlightWrapped = "Highlight with text wrapping. The quick brown fox jumps over the " 131 | "lazy dog. The quick brown fox jumps over the lazy dog."; 132 | 133 | static auto underlineColour = ImGui::GetStyle().Colors[ImGuiCol_Text]; 134 | static auto strikethroughColour = ImGui::GetStyle().Colors[ImGuiCol_Text]; 135 | static auto linkColour = UIMGUI_LINK_TEXT_UNVISITED; 136 | static auto highlightColour = UIMGUI_HIGHLIGHT_TEXT_COLOUR; 137 | 138 | ImGui::SeparatorText("Underlined text"); 139 | ImGui::ColorEdit4("Underline colour", reinterpret_cast(&underlineColour)); 140 | 141 | ImGui::TextWrapped("Normal text with underlining after it's rendered"); 142 | Underline(underlineColour); 143 | Underline("Underline wrapper with formatting: %d", underlineColour, i); 144 | UnderlineWrapped(underlineWrapped, underlineColour); 145 | 146 | ImGui::SeparatorText("Strikethrough"); 147 | ImGui::ColorEdit4("Strikethrough colour", reinterpret_cast(&strikethroughColour)); 148 | 149 | ImGui::TextWrapped("Normal text with strikethrough after it's rendered"); 150 | Strikethrough(strikethroughColour); 151 | Strikethrough("Strikethrough wrapper with formatting: %d", strikethroughColour, i); 152 | StrikethroughWrapped(strikethroughWrapped, strikethroughColour); 153 | 154 | ImGui::SeparatorText("Links"); 155 | ImGui::ColorEdit4("Link colour", reinterpret_cast(&linkColour)); 156 | 157 | Link("Link", linkColour); 158 | LinkWrapped(linkWrapped, linkColour); 159 | 160 | ImGui::SeparatorText("Highlight"); 161 | ImGui::ColorEdit4("Highlight colour", reinterpret_cast(&highlightColour)); 162 | 163 | ImGui::TextWrapped("Normal text with highlight after it's rendered"); 164 | Highlight(highlightColour); 165 | Highlight("Highlight wrapper with formatting: %d", highlightColour, i); 166 | HighlightWrapped(highlightWrapped, highlightColour); 167 | } 168 | 169 | if (ImGui::CollapsingHeader("Code")) 170 | { 171 | static const TString codeInline = "This can be in the middle of a sentence"; 172 | static const TString codeInlineWrapped = "This is inline code with wrapping. The quick brown fox jumps over" 173 | " the lazy dog. The quick brown fox jumps over the lazy dog."; 174 | static const TString codeBlock = R"(#include 175 | 176 | int main(int argc, char** argv) 177 | { 178 | std::cout << "Hello, World!" << std::endl; 179 | return 0; 180 | } 181 | )"; 182 | 183 | static auto backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR; 184 | ImGui::ColorEdit4("Code background colour", reinterpret_cast(&backgroundColour)); 185 | 186 | ImGui::SeparatorText("Code blocks"); 187 | static bool bWrapCodeBlock = false; 188 | ImGui::Checkbox("Code block text wrapping", &bWrapCodeBlock); 189 | CodeBlock(codeBlock, bWrapCodeBlock, backgroundColour); 190 | 191 | ImGui::SeparatorText("Inline code"); 192 | CodeInline(codeInline); 193 | CodeInlineWrapped(codeInlineWrapped); 194 | } 195 | 196 | if (ImGui::CollapsingHeader("Blockquotes")) 197 | { 198 | static const TString blockquoteWrapped = "Blockquote with wrapping. The quick brown fox jumps over the lazy" 199 | " dog. The quick brown fox jumps over the lazy dog. The quick " 200 | "brown fox jumps over the lazy dog"; 201 | static auto backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR; 202 | ImGui::ColorEdit4("Blockquote colour", reinterpret_cast(&backgroundColour)); 203 | 204 | ImGui::Text("Independent blockquote rect: "); 205 | ImGui::SameLine(); 206 | Blockquote(backgroundColour); 207 | 208 | Blockquote("Blockquote with formatting: %d", backgroundColour, i); 209 | 210 | BlockquoteWrapped(blockquoteWrapped,backgroundColour); 211 | } 212 | 213 | ImGui::End(); 214 | } 215 | -------------------------------------------------------------------------------- /UImGuiTextUtilsTypefaces.cpp: -------------------------------------------------------------------------------- 1 | // This is for typeface related features such as: 2 | // - Bold, Italic, bold + italic 3 | // - Monospace 4 | // - Ruby, superscript, subscript 5 | #include "UImGuiTextUtils.hpp" 6 | 7 | // All font calculations for this file are in the small font so small font calculations are already done here. 8 | // 9 | // Because dear imgui's new font API is not font-specific we're calculating the font using the pushed font size / 2. 10 | // The font size is the height, and we're after that here too. For small fonts we want to cover half the size of the 11 | // normal glyphs + the frame padding Y space. This is because normal glyphs have some padding already applied and also 12 | // because we're using FramePadding.y - (the small font's size / 2) = FramePadding.y - ((FontSize / 2 + FramePadding.y) / 2) 13 | // which gives us a nice offset that places us between the half of the font size and the half of the frame padding 14 | #define SMALL_FONT_SIZE(x) ((ImGui::GetFontSize() / 2) + ImGui::GetStyle().FramePadding.y) 15 | 16 | #define CUSTOM_FONT_BOILERPLATE(x, y) va_list args; \ 17 | va_start(args, x); \ 18 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->y, args); \ 19 | va_end(args) 20 | 21 | 22 | #define CUSTOM_FONT_BOILERPLATE_WRAPPED(x, y) va_list args; \ 23 | va_start(args, x); \ 24 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->y, args); \ 25 | va_end(args) 26 | 27 | void UImGui::TextUtils::Bold(const char* fmt, ...) noexcept 28 | { 29 | CUSTOM_FONT_BOILERPLATE(fmt, bold); 30 | } 31 | 32 | void UImGui::TextUtils::BoldV(const char* fmt, va_list list) noexcept 33 | { 34 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->bold, list); 35 | } 36 | 37 | void UImGui::TextUtils::BoldWrapped(const char* fmt, ...) noexcept 38 | { 39 | CUSTOM_FONT_BOILERPLATE_WRAPPED(fmt, bold); 40 | } 41 | 42 | void UImGui::TextUtils::BoldWrappedV(const char* fmt, va_list list) noexcept 43 | { 44 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->bold, list); 45 | } 46 | 47 | void UImGui::TextUtils::Italic(const char* fmt, ...) noexcept 48 | { 49 | CUSTOM_FONT_BOILERPLATE(fmt, italic); 50 | } 51 | 52 | void UImGui::TextUtils::ItalicV(const char* fmt, va_list list) noexcept 53 | { 54 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->italic, list); 55 | } 56 | 57 | void UImGui::TextUtils::ItalicWrapped(const char* fmt, ...) noexcept 58 | { 59 | CUSTOM_FONT_BOILERPLATE_WRAPPED(fmt, italic); 60 | } 61 | 62 | void UImGui::TextUtils::ItalicWrappedV(const char* fmt, va_list list) noexcept 63 | { 64 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->italic, list); 65 | } 66 | 67 | void UImGui::TextUtils::BoldItalic(const char* fmt, ...) noexcept 68 | { 69 | CUSTOM_FONT_BOILERPLATE(fmt, boldItalic); 70 | } 71 | 72 | void UImGui::TextUtils::BoldItalicV(const char* fmt, va_list list) noexcept 73 | { 74 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->boldItalic, list); 75 | } 76 | 77 | void UImGui::TextUtils::BoldItalicWrapped(const char* fmt, ...) noexcept 78 | { 79 | CUSTOM_FONT_BOILERPLATE_WRAPPED(fmt, boldItalic); 80 | } 81 | 82 | void UImGui::TextUtils::BoldItalicWrappedV(const char* fmt, va_list list) noexcept 83 | { 84 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->boldItalic, list); 85 | } 86 | 87 | void UImGui::TextUtils::Monospace(const char* fmt, ...) noexcept 88 | { 89 | CUSTOM_FONT_BOILERPLATE(fmt, monospace); 90 | } 91 | 92 | void UImGui::TextUtils::MonospaceV(const char* fmt, va_list list) noexcept 93 | { 94 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->monospace, list); 95 | } 96 | 97 | void UImGui::TextUtils::MonospaceWrapped(const char* fmt, ...) noexcept 98 | { 99 | CUSTOM_FONT_BOILERPLATE_WRAPPED(fmt, monospace); 100 | } 101 | 102 | void UImGui::TextUtils::MonospaceWrappedV(const char* fmt, va_list list) noexcept 103 | { 104 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->monospace, list); 105 | } 106 | 107 | void UImGui::TextUtils::Small(const char* fmt, ...) noexcept 108 | { 109 | CUSTOM_FONT_BOILERPLATE(fmt, smallFont); 110 | } 111 | 112 | void UImGui::TextUtils::SmallV(const char* fmt, va_list list) noexcept 113 | { 114 | customFontGenericText(fmt, UIMGUI_TEXT_UTILS_DATA->smallFont, list); 115 | } 116 | 117 | void UImGui::TextUtils::SmallWrapped(const char* fmt, ...) noexcept 118 | { 119 | CUSTOM_FONT_BOILERPLATE_WRAPPED(fmt, smallFont); 120 | } 121 | 122 | void UImGui::TextUtils::SmallWrappedV(const char* fmt, va_list list) noexcept 123 | { 124 | customFontGenericTextWrapped(fmt, UIMGUI_TEXT_UTILS_DATA->smallFont, list); 125 | } 126 | 127 | void UImGui::TextUtils::customFontGenericText(const char* fmt, ImFont* font, va_list args) noexcept 128 | { 129 | ImGui::PushFont(font, 0.0f); 130 | ImGui::TextV(fmt, args); 131 | ImGui::PopFont(); 132 | } 133 | 134 | void UImGui::TextUtils::customFontGenericTextWrapped(const char* fmt, ImFont* font, va_list args) noexcept 135 | { 136 | float scale = 0.0f; 137 | if (font == UIMGUI_TEXT_UTILS_DATA->smallFont) 138 | scale = SMALL_FONT_SIZE(smallFont); 139 | ImGui::PushFont(font, scale); 140 | ImGui::TextWrappedV(fmt, args); 141 | ImGui::PopFont(); 142 | } 143 | 144 | void UImGui::TextUtils::Ruby(const char* textBegin, const char* textEnd, const char* annotationBegin, const char* annotationEnd, const bool bWrapAnnotation, const bool bWrapText) noexcept 145 | { 146 | static float width = 0.0f; 147 | 148 | ImGui::BeginGroup(); 149 | const auto offset = (SMALL_FONT_SIZE(smallFont) / 2); 150 | 151 | ImGui::PushID(textBegin, textEnd); 152 | // Render 153 | { 154 | auto min = ImGui::GetCursorScreenPos(); 155 | min.y -= offset; 156 | 157 | const auto textSize = UIMGUI_TEXT_UTILS_DATA->smallFont->CalcTextSizeA(SMALL_FONT_SIZE(smallFont), FLT_MAX, width, annotationBegin, annotationEnd); 158 | 159 | const ImVec2 max = { min.x + textSize.x, min.y + textSize.y }; 160 | const ImVec2 size = {max.x - min.x, max.y - min.y }; 161 | ImGui::GetWindowDrawList()->AddText(UIMGUI_TEXT_UTILS_DATA->smallFont, SMALL_FONT_SIZE(smallFont), min, 162 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 163 | annotationBegin, annotationEnd, width); 164 | // Render an invisible button, which will act as our element 165 | ImGui::InvisibleButton("##rubyannotation", size); 166 | } 167 | { 168 | auto min = ImGui::GetItemRectMin(); 169 | min.y += offset; 170 | 171 | const auto textSize = ImGui::CalcTextSize(textBegin, textEnd, false, bWrapText ? ImGui::GetContentRegionAvail().x : -1.0f); 172 | width = bWrapAnnotation ? std::min(textSize.x, ImGui::GetContentRegionAvail().x) : -1.0f; 173 | 174 | const ImVec2 max = { min.x + textSize.x, min.y + textSize.y }; 175 | const ImVec2 size = {max.x - min.x, max.y - min.y }; 176 | ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), 177 | #if IMGUI_VERSION_NUM > 19197 178 | ImGui::GetFontSize(), 179 | #else 180 | ImGui::GetFont()->FontSize, 181 | #endif 182 | min, 183 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 184 | textBegin, textEnd, width); 185 | // Render an invisible button, which will act as our element 186 | ImGui::InvisibleButton("##ruby", size); 187 | } 188 | ImGui::PopID(); 189 | ImGui::EndGroup(); 190 | } 191 | 192 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 193 | void UImGui::TextUtils::Ruby(const TString& text, const TString& annotation, const bool bWrapAnnotation, const bool bWrapText) noexcept 194 | { 195 | Ruby(text.c_str(), text.c_str() + text.size(), annotation.c_str(), annotation.c_str() + annotation.size(), bWrapAnnotation, bWrapText); 196 | } 197 | 198 | 199 | void UImGui::TextUtils::SubSuperscript(const TString& subscript, const TString& superscript) noexcept 200 | { 201 | SubSuperscript(subscript.c_str(), subscript.c_str() + subscript.size(), 202 | superscript.c_str(), superscript.c_str() + superscript.size()); 203 | } 204 | #endif 205 | 206 | void UImGui::TextUtils::SubSuperscript(const char* subscriptBegin, const char* subscriptEnd, 207 | const char* superscriptBegin, const char* superscriptEnd) noexcept 208 | { 209 | const auto offset = (SMALL_FONT_SIZE(smallFont) / 2); 210 | const auto superscriptTextSize = UIMGUI_TEXT_UTILS_DATA->smallFont->CalcTextSizeA(SMALL_FONT_SIZE(smallFont), FLT_MAX, 211 | -1.0f, superscriptBegin, superscriptEnd); 212 | const auto subscriptTextSize = UIMGUI_TEXT_UTILS_DATA->smallFont->CalcTextSizeA(SMALL_FONT_SIZE(smallFont), FLT_MAX, 213 | -1.0f, subscriptBegin, subscriptEnd); 214 | ImVec2 min = ImGui::GetCursorScreenPos(); 215 | min.y -= offset; 216 | ImGui::PushID(subscriptBegin, subscriptEnd); 217 | ImGui::PushID(superscriptBegin, superscriptEnd); 218 | { 219 | 220 | const ImVec2 max = { min.x + superscriptTextSize.x, min.y + superscriptTextSize.y + ImGui::GetStyle().FramePadding.y - offset }; 221 | const ImVec2 size = { max.x - min.x, max.y - min.y }; 222 | ImGui::GetWindowDrawList()->AddText(UIMGUI_TEXT_UTILS_DATA->smallFont, SMALL_FONT_SIZE(smallFont), min, 223 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 224 | superscriptBegin, superscriptEnd, -1.0f); 225 | 226 | ImGui::InvisibleButton("##superscript", size); 227 | } 228 | ImGui::SameLine(); 229 | { 230 | const ImVec2 max = { min.x + subscriptTextSize.x, min.y + subscriptTextSize.y + ImGui::GetStyle().FramePadding.y - offset }; 231 | const ImVec2 size = { max.x - min.x, max.y - min.y }; 232 | 233 | ImGui::GetWindowDrawList()->AddText(UIMGUI_TEXT_UTILS_DATA->smallFont, SMALL_FONT_SIZE(smallFont), { min.x, max.y }, 234 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 235 | subscriptBegin, subscriptEnd, -1.0f); 236 | 237 | ImGui::InvisibleButton("##subscript", size); 238 | } 239 | ImGui::PopID(); 240 | ImGui::PopID(); 241 | } 242 | -------------------------------------------------------------------------------- /C/CUImGuiTextUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "CUImGuiTextUtils.h" 2 | #include "../UImGuiTextUtils.hpp" 3 | 4 | #define WRAP_VARIADIC_LIST(x) va_list args; \ 5 | va_start(args, fmt); \ 6 | x; \ 7 | va_end(args) 8 | 9 | #define dt_cast(x) static_cast(x) 10 | 11 | UImGui_CTextUtilsData* UImGui_TextUtilsData_allocate() 12 | { 13 | return new UImGui::TextUtilsData{}; 14 | } 15 | 16 | void UImGui_TextUtilsData_setFonts(UImGui_CTextUtilsData* data, void* bold, void* italic, void* boldItalic, void* monospace, void* smallFont) 17 | { 18 | dt_cast(data)->bold = static_cast(bold); 19 | dt_cast(data)->italic = static_cast(italic); 20 | dt_cast(data)->boldItalic = static_cast(boldItalic); 21 | dt_cast(data)->monospace = static_cast(monospace); 22 | dt_cast(data)->smallFont = static_cast(smallFont); 23 | } 24 | 25 | void UImGui_TextUtils_initTextUtilsData(UImGui_CTextUtilsData* data) 26 | { 27 | UImGui::TextUtils::initTextUtilsData(dt_cast(data)); 28 | } 29 | 30 | void UImGui_TextUtilsData_free(UImGui_CTextUtilsData* data) 31 | { 32 | delete dt_cast(data); 33 | } 34 | 35 | UImGui_TextUtils_Colour UImGui_TextUtils_getDefaultTextColour() 36 | { 37 | return UImGui::TextUtils::getDefaultTextColour(); 38 | } 39 | 40 | void UImGui_TextUtils_Bold(const char* fmt, ...) 41 | { 42 | WRAP_VARIADIC_LIST(UImGui::TextUtils::BoldV(fmt, args)); 43 | } 44 | 45 | void UImGui_TextUtils_BoldWrapped(const char* fmt, ...) 46 | { 47 | WRAP_VARIADIC_LIST(UImGui::TextUtils::BoldWrappedV(fmt, args)); 48 | } 49 | 50 | void UImGui_TextUtils_BoldV(const char* fmt, va_list args) 51 | { 52 | UImGui::TextUtils::BoldV(fmt, args); 53 | } 54 | 55 | void UImGui_TextUtils_BoldWrappedV(const char* fmt, va_list args) 56 | { 57 | UImGui::TextUtils::BoldWrappedV(fmt, args); 58 | } 59 | 60 | void UImGui_TextUtils_Italic(const char* fmt, ...) 61 | { 62 | WRAP_VARIADIC_LIST(UImGui::TextUtils::ItalicV(fmt, args)); 63 | } 64 | 65 | void UImGui_TextUtils_ItalicWrapped(const char* fmt, ...) 66 | { 67 | WRAP_VARIADIC_LIST(UImGui::TextUtils::ItalicWrappedV(fmt, args)); 68 | } 69 | 70 | void UImGui_TextUtils_ItalicV(const char* fmt, va_list args) 71 | { 72 | UImGui::TextUtils::ItalicV(fmt, args); 73 | } 74 | 75 | void UImGui_TextUtils_ItalicWrappedV(const char* fmt, va_list args) 76 | { 77 | UImGui::TextUtils::ItalicWrappedV(fmt, args); 78 | } 79 | 80 | void UImGui_TextUtils_BoldItalic(const char* fmt, ...) 81 | { 82 | WRAP_VARIADIC_LIST(UImGui::TextUtils::BoldItalicV(fmt, args)); 83 | } 84 | 85 | void UImGui_TextUtils_BoldItalicWrapped(const char* fmt, ...) 86 | { 87 | WRAP_VARIADIC_LIST(UImGui::TextUtils::BoldItalicWrappedV(fmt, args)); 88 | } 89 | 90 | void UImGui_TextUtils_BoldItalicV(const char* fmt, va_list args) 91 | { 92 | UImGui::TextUtils::BoldItalicV(fmt, args); 93 | } 94 | 95 | void UImGui_TextUtils_BoldItalicWrappedV(const char* fmt, va_list args) 96 | { 97 | UImGui::TextUtils::BoldItalicWrappedV(fmt, args); 98 | } 99 | 100 | void UImGui_TextUtils_Monospace(const char* fmt, ...) 101 | { 102 | WRAP_VARIADIC_LIST(UImGui::TextUtils::MonospaceV(fmt, args)); 103 | } 104 | 105 | void UImGui_TextUtils_MonospaceWrapped(const char* fmt, ...) 106 | { 107 | WRAP_VARIADIC_LIST(UImGui::TextUtils::MonospaceWrappedV(fmt, args)); 108 | } 109 | 110 | void UImGui_TextUtils_MonospaceV(const char* fmt, va_list args) 111 | { 112 | UImGui::TextUtils::MonospaceV(fmt, args); 113 | } 114 | 115 | void UImGui_TextUtils_MonospaceWrappedV(const char* fmt, va_list args) 116 | { 117 | UImGui::TextUtils::MonospaceWrappedV(fmt, args); 118 | } 119 | 120 | void UImGui_TextUtils_Small(const char* fmt, ...) 121 | { 122 | WRAP_VARIADIC_LIST(UImGui::TextUtils::SmallV(fmt, args)); 123 | } 124 | 125 | void UImGui_TextUtils_SmallWrapped(const char* fmt, ...) 126 | { 127 | WRAP_VARIADIC_LIST(UImGui::TextUtils::SmallWrappedV(fmt, args)); 128 | } 129 | 130 | void UImGui_TextUtils_Small(const char* fmt, va_list args) 131 | { 132 | UImGui::TextUtils::SmallV(fmt, args); 133 | } 134 | 135 | void UImGui_TextUtils_SmallWrapped(const char* fmt, va_list args) 136 | { 137 | UImGui::TextUtils::SmallWrappedV(fmt, args); 138 | } 139 | 140 | void UImGui_TextUtils_SubSuperscript(const char* subscriptBegin, const char* subscriptEnd, 141 | const char* superscriptBegin, const char* superscriptEnd) 142 | { 143 | UImGui::TextUtils::SubSuperscript(subscriptBegin, subscriptEnd, superscriptBegin, superscriptEnd); 144 | } 145 | 146 | void UImGui_TextUtils_Ruby(const char* textBegin, const char* textEnd, 147 | const char* annotationBegin, const char* annotationEnd, 148 | const bool bWrapAnnotation, const bool bWrapText) 149 | { 150 | UImGui::TextUtils::Ruby(textBegin, textEnd, annotationBegin, annotationEnd, bWrapAnnotation, bWrapText); 151 | } 152 | 153 | void UImGui_TextUtils_Underline(const UImGui_TextUtils_Colour colour) 154 | { 155 | UImGui::TextUtils::Underline(colour); 156 | } 157 | 158 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Underline_fmt(const UImGui_TextUtils_Colour colour, const char* fmt, ...) 159 | { 160 | va_list args; 161 | va_start(args, fmt); 162 | ImGui::TextV(fmt, args); 163 | va_end(args); 164 | 165 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 166 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 167 | UImGui::TextUtils::Underline(colour); 168 | 169 | return static_cast(bHovered | bClicked); 170 | } 171 | 172 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Underline_fmtV(const UImGui_TextUtils_Colour colour, const char* fmt, va_list args) 173 | { 174 | ImGui::TextV(fmt, args); 175 | 176 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 177 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 178 | UImGui::TextUtils::Underline(colour); 179 | 180 | return static_cast(bHovered | bClicked); 181 | } 182 | 183 | UImGui_TextUtils_WidgetState UImGui_TextUtils_UnderlineWrapped(const char* text, const char* end, const UImGui_TextUtils_Colour colour) 184 | { 185 | return UImGui::TextUtils::UnderlineWrapped(text, end, colour); 186 | } 187 | 188 | void UImGui_TextUtils_Strikethrough(const UImGui_TextUtils_Colour colour) 189 | { 190 | UImGui::TextUtils::Strikethrough(colour); 191 | } 192 | 193 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Strikethrough_fmt(const UImGui_TextUtils_Colour colour, const char* fmt, ...) 194 | { 195 | va_list args; 196 | va_start(args, fmt); 197 | ImGui::TextV(fmt, args); 198 | va_end(args); 199 | 200 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 201 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 202 | UImGui::TextUtils::Strikethrough(colour); 203 | 204 | return static_cast(bHovered | bClicked); 205 | } 206 | 207 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Strikethrough_fmtV(const UImGui_TextUtils_Colour colour, const char* fmt, va_list args) 208 | { 209 | ImGui::TextV(fmt, args); 210 | 211 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 212 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 213 | UImGui::TextUtils::Strikethrough(colour); 214 | 215 | return static_cast(bHovered | bClicked); 216 | } 217 | 218 | UImGui_TextUtils_WidgetState UimGui_TextUtils_StrikethroughWrapped(const char* text, const char* end, const UImGui_TextUtils_Colour colour) 219 | { 220 | return UImGui::TextUtils::StrikethroughWrapped(text, end, colour); 221 | } 222 | 223 | void UImGui_TextUtils_Link(const char* text, const UImGui_TextUtils_Colour colour, const UImGui_TextUtils_LinkCallback callback) 224 | { 225 | UImGui::TextUtils::Link(text, colour, callback); 226 | } 227 | 228 | void UImGui_TextUtils_LinkWrapped(const char* text, const char* end, const UImGui_TextUtils_Colour colour, const UImGui_TextUtils_LinkCallback callback) 229 | { 230 | UImGui::TextUtils::LinkWrapped(text, end, colour, callback); 231 | } 232 | 233 | void UImGui_TextUtils_Highlight(const UImGui_TextUtils_Colour colour) 234 | { 235 | return UImGui::TextUtils::Highlight(colour); 236 | } 237 | 238 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Highlight_fmt(const UImGui_TextUtils_Colour colour, const char* fmt, ...) 239 | { 240 | va_list args; 241 | va_start(args, fmt); 242 | ImGui::TextV(fmt, args); 243 | va_end(args); 244 | 245 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 246 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 247 | UImGui::TextUtils::Highlight(colour); 248 | 249 | return static_cast(bHovered | bClicked); 250 | } 251 | 252 | UImGui_TextUtils_WidgetState UImGui_TextUtils_Highlight_fmtV(const UImGui_TextUtils_Colour colour, const char* fmt, va_list args) 253 | { 254 | ImGui::TextV(fmt, args); 255 | 256 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 257 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 258 | UImGui::TextUtils::Highlight(colour); 259 | 260 | return static_cast(bHovered | bClicked); 261 | } 262 | 263 | UImGui_TextUtils_WidgetState UImGui_TextUtils_HighlightWrapped(const char* begin, const char* end, const UImGui_TextUtils_Colour colour) 264 | { 265 | return UImGui::TextUtils::HighlightWrapped(begin, end, colour); 266 | } 267 | 268 | void UImGui_TextUtils_Blockquote(const UImGui_TextUtils_Colour colour) 269 | { 270 | UImGui::TextUtils::Blockquote(colour); 271 | } 272 | 273 | void UImGui_TextUtils_Blockquote_fmt(const UImGui_TextUtils_Colour colour, const char* fmt, ...) 274 | { 275 | UImGui::TextUtils::Blockquote(colour); 276 | ImGui::SameLine(); 277 | 278 | va_list args; 279 | va_start(args, fmt); 280 | ImGui::TextV(fmt, args); 281 | va_end(args); 282 | } 283 | 284 | void UImGui_TextUtils_Blockquote_fmtV(const UImGui_TextUtils_Colour colour, const char* fmt, va_list args) 285 | { 286 | UImGui::TextUtils::Blockquote(colour); 287 | ImGui::SameLine(); 288 | ImGui::TextV(fmt, args); 289 | } 290 | 291 | void UImGui_TextUtils_BlockquoteWrapped(const char* text, const char* end, const UImGui_TextUtils_Colour colour) 292 | { 293 | UImGui::TextUtils::BlockquoteWrapped(text, end, colour); 294 | } 295 | 296 | void UImGui_TextUtils_CodeBlock(const char* begin, const char* end, const bool bWrapText, const UImGui_TextUtils_Colour colour) 297 | { 298 | UImGui::TextUtils::CodeBlock(begin, end, bWrapText, colour); 299 | } 300 | 301 | void UImGui_TextUtils_CodeInline(const char* begin, const char* end, const UImGui_TextUtils_Colour colour) 302 | { 303 | UImGui::TextUtils::CodeInline(begin, end, colour); 304 | } 305 | 306 | void UImGui_TextUtils_CodeInlineWrapped(const char* begin, const char* end, const UImGui_TextUtils_Colour colour) 307 | { 308 | UImGui::TextUtils::CodeInlineWrapped(begin, end, colour); 309 | } 310 | 311 | void UImGui_TextUtils_ShowDemoWindow(void* bOpen) 312 | { 313 | UImGui::TextUtils::ShowDemoWindow(bOpen); 314 | } 315 | -------------------------------------------------------------------------------- /UImGuiTextUtilsTextMarkdown.cpp: -------------------------------------------------------------------------------- 1 | // This is for text markdown features such as: 2 | // - Underlined text 3 | // - Strikethrough text 4 | // - Highlight text 5 | #include "UImGuiTextUtils.hpp" 6 | 7 | void UImGui::TextUtils::Underline(const Colour colour) noexcept 8 | { 9 | auto min = ImGui::GetItemRectMin(); 10 | const auto max = ImGui::GetItemRectMax(); 11 | 12 | min.y = max.y; 13 | 14 | ImGui::GetWindowDrawList()->AddLine(min, max, colour, 1.0f); 15 | } 16 | 17 | UImGui::TextUtils::WidgetState UImGui::TextUtils::UnderlineWrapped(const char* text, const char* end, const Colour colour) noexcept 18 | { 19 | return renderWrappedTextGeneric(text, end, colour, [](const Colour col) -> void { Underline(col); }, [](Colour) -> void {}); 20 | } 21 | 22 | bool UImGui::TextUtils::isPartOfWord(const char character) noexcept 23 | { 24 | return character != ' ' && character != '.' && character != ',' && character != '!' && character != '?' && 25 | character != '\'' && character != '\"' && character != '`'; 26 | } 27 | 28 | void UImGui::TextUtils::Link(const char* text, const Colour colour, const std::function& clicked) noexcept 29 | { 30 | ImGui::PushStyleColor(ImGuiCol_Text, ImU32{colour}); 31 | const auto state = Underline(text, colour, ""); 32 | if (state & UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED) 33 | { 34 | ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 35 | if (state & UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED) 36 | clicked(text); 37 | } 38 | 39 | ImGui::PopStyleColor(); 40 | } 41 | 42 | void UImGui::TextUtils::LinkWrapped(const char* text, const char* end, const Colour colour, const std::function& clicked) noexcept 43 | { 44 | ImGui::PushStyleColor(ImGuiCol_Text, ImU32{colour}); 45 | const auto state = UnderlineWrapped(text, end, colour); 46 | if (state & UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED) 47 | { 48 | ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 49 | if (state & UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED) 50 | clicked(text); 51 | } 52 | ImGui::PopStyleColor(); 53 | } 54 | 55 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 56 | UImGui::TextUtils::WidgetState UImGui::TextUtils::UnderlineWrapped(const TString& text, const Colour colour) noexcept 57 | { 58 | return UnderlineWrapped(text.c_str(), text.c_str() + text.size(), colour); 59 | } 60 | 61 | void UImGui::TextUtils::LinkWrapped(const TString& text, const Colour colour, const std::function& clicked) noexcept 62 | { 63 | return LinkWrapped(text.c_str(), text.c_str() + text.size(), colour, clicked); 64 | } 65 | #endif 66 | 67 | void UImGui::TextUtils::Strikethrough(const Colour colour) noexcept 68 | { 69 | ImVec2 min = ImGui::GetItemRectMin(); 70 | ImVec2 max = ImGui::GetItemRectMax(); 71 | 72 | // Do this casting to round up automatically 73 | max.y -= static_cast(static_cast((max.y - min.y) / 2)); 74 | min.y = max.y; 75 | 76 | ImGui::GetWindowDrawList()->AddLine(min, max, colour, 0.25f); 77 | } 78 | 79 | UImGui::TextUtils::WidgetState UImGui::TextUtils::StrikethroughWrapped(const char* text, const char* end, const Colour colour) noexcept 80 | { 81 | 82 | return renderWrappedTextGeneric(text, end, colour, [](const Colour col) -> void { Strikethrough(col); }, [](Colour) -> void {}); 83 | } 84 | 85 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 86 | UImGui::TextUtils::WidgetState UImGui::TextUtils::StrikethroughWrapped(const TString& text, const Colour colour) noexcept 87 | { 88 | return StrikethroughWrapped(text.c_str(), text.c_str() + text.size(), colour); 89 | } 90 | #endif 91 | 92 | void UImGui::TextUtils::Highlight(const Colour colour) noexcept 93 | { 94 | ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), colour); 95 | } 96 | 97 | UImGui::TextUtils::WidgetState UImGui::TextUtils::HighlightWrapped(const char* text, const char* end, const Colour colour) noexcept 98 | { 99 | return renderWrappedTextGeneric(text, end, colour, [](const Colour col) -> void { Highlight(col); }, [](Colour) -> void {}); 100 | } 101 | 102 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 103 | UImGui::TextUtils::WidgetState UImGui::TextUtils::HighlightWrapped(const TString& text, const Colour colour) noexcept 104 | { 105 | return HighlightWrapped(text.c_str(), text.c_str() + text.size(), colour); 106 | } 107 | #endif 108 | 109 | void UImGui::TextUtils::Blockquote(const Colour colour) noexcept 110 | { 111 | static int i = 0; 112 | 113 | // Get the font size 114 | const float scale = ImGui::GetFontSize(); 115 | 116 | // Calculate rect size and coordinates 117 | const auto min = ImGui::GetCursorScreenPos(); 118 | const auto max = ImVec2(min.x + 119 | static_cast(static_cast(scale / 4)) + 120 | static_cast(static_cast(scale / 6)), 121 | min.y + scale + static_cast(static_cast(scale / 2))); 122 | const auto size = ImVec2(max.x - min.x, max.y - min.y); 123 | 124 | // Add rectangle with min, max and the colour 125 | ImGui::GetWindowDrawList()->AddRectFilled(min, max, colour); 126 | 127 | // Render an invisible button, which will act as our element 128 | ImGui::PushID(i); 129 | ImGui::InvisibleButton("##bq", size); 130 | ImGui::PopID(); 131 | i++; 132 | } 133 | 134 | void UImGui::TextUtils::BlockquoteWrapped(const char* text, const char* end, const Colour colour) noexcept 135 | { 136 | renderWrappedTextGeneric(text, end, colour, [](Colour) -> void {}, [](const Colour col) -> void { 137 | Blockquote(col); 138 | ImGui::SameLine(); 139 | }); 140 | } 141 | 142 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 143 | void UImGui::TextUtils::BlockquoteWrapped(const TString& text, const Colour colour) noexcept 144 | { 145 | BlockquoteWrapped(text.c_str(), text.c_str() + text.size(), colour); 146 | } 147 | #endif 148 | 149 | void UImGui::TextUtils::CodeBlock(const char* begin, const char* end, const bool bWrapText, const Colour backgroundColour) noexcept 150 | { 151 | const float wrapWidth = bWrapText ? ImGui::GetContentRegionAvail().x : -1.0f; 152 | 153 | // Get the font size 154 | const auto textSize = UIMGUI_TEXT_UTILS_DATA->monospace->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, wrapWidth, begin, end); 155 | 156 | // Calculate rect size and coordinates 157 | const auto min = ImGui::GetCursorScreenPos(); 158 | const ImVec2 max = { min.x + textSize.x, min.y + textSize.y + ImGui::GetStyle().FramePadding.y }; 159 | const ImVec2 size = {max.x - min.x, max.y - min.y }; 160 | 161 | // Add rectangle with min, max and the colour 162 | ImGui::GetWindowDrawList()->AddRectFilled(min, max, backgroundColour); 163 | ImGui::GetWindowDrawList()->AddText(UIMGUI_TEXT_UTILS_DATA->monospace, ImGui::GetFontSize(), min, 164 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 165 | begin, end, wrapWidth); 166 | 167 | // Render an invisible button, which will act as our element 168 | ImGui::PushID(begin, end); 169 | ImGui::InvisibleButton("##code", size); 170 | ImGui::PopID(); 171 | } 172 | 173 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 174 | void UImGui::TextUtils::CodeBlock(const TString& text, const bool bWrapText, const Colour backgroundColour) noexcept 175 | { 176 | CodeBlock(text.c_str(), text.c_str() + text.size(), bWrapText, backgroundColour); 177 | } 178 | #endif 179 | 180 | void UImGui::TextUtils::CodeInlineWrapped(const char* begin, const char* end, const Colour backgroundColour) noexcept 181 | { 182 | renderWrappedTextGeneric(begin, end, backgroundColour, 183 | [](Colour) -> void {}, [](Colour) -> void {}, [](TextUtilsData const* data, const char* s, const char* e, const Colour colour) -> void 184 | { 185 | // Get the font size 186 | const auto textSize = data->monospace->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, s, e); 187 | 188 | // Calculate rect size and coordinates 189 | const auto min = ImGui::GetCursorScreenPos(); 190 | const ImVec2 max = { min.x + textSize.x, min.y + textSize.y + ImGui::GetStyle().FramePadding.y }; 191 | const ImVec2 size = {max.x - min.x, max.y - min.y }; 192 | 193 | // Add rectangle with min, max and the colour 194 | ImGui::GetWindowDrawList()->AddRectFilled(min, max, colour); 195 | ImGui::GetWindowDrawList()->AddText(data->monospace, ImGui::GetFontSize(), min, 196 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 197 | s, e, -1.0f); 198 | 199 | // Render an invisible button, which will act as our element 200 | ImGui::PushID(s, e); 201 | ImGui::InvisibleButton("##code", size); 202 | ImGui::PopID(); 203 | }); 204 | } 205 | 206 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 207 | void UImGui::TextUtils::CodeInlineWrapped(const TString& text, const Colour backgroundColour) noexcept 208 | { 209 | CodeInlineWrapped(text.c_str(), text.c_str() + text.size(), backgroundColour); 210 | } 211 | #endif 212 | 213 | void UImGui::TextUtils::CodeInline(const char* begin, const char* end, const Colour backgroundColour) noexcept 214 | { 215 | // Get the font size 216 | const auto textSize = UIMGUI_TEXT_UTILS_DATA->monospace->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, begin, end); 217 | 218 | // Calculate rect size and coordinates 219 | const auto min = ImGui::GetCursorScreenPos(); 220 | const ImVec2 max = { min.x + textSize.x, min.y + textSize.y + ImGui::GetStyle().FramePadding.y }; 221 | const ImVec2 size = {max.x - min.x, max.y - min.y }; 222 | 223 | // Add rectangle with min, max and the colour 224 | ImGui::GetWindowDrawList()->AddRectFilled(min, max, backgroundColour); 225 | ImGui::GetWindowDrawList()->AddText(UIMGUI_TEXT_UTILS_DATA->monospace, ImGui::GetFontSize(), min, 226 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), 227 | begin, end, -1.0f); 228 | 229 | // Render an invisible button, which will act as our element 230 | ImGui::PushID(begin, end); 231 | ImGui::InvisibleButton("##code", size); 232 | ImGui::PopID(); 233 | } 234 | 235 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 236 | void UImGui::TextUtils::CodeInline(const TString& text, const Colour backgroundColour) noexcept 237 | { 238 | CodeInline(text.c_str(), text.c_str() + text.size(), backgroundColour); 239 | } 240 | #endif 241 | 242 | UImGui::TextUtils::WidgetState UImGui::TextUtils::renderWrappedTextGeneric(const char* text, const char* end, const Colour colour, 243 | const std::function& after, 244 | const std::function& before, 245 | const std::function& render) noexcept 248 | { 249 | const float scale = 250 | #if IMGUI_VERSION_NUM > 19197 251 | ImGui::GetStyle().FontScaleMain; 252 | #else 253 | ImGui::GetIO().FontGlobalScale; 254 | #endif 255 | const float size = ImGui::GetFontSize(); 256 | float widthAvail = ImGui::GetContentRegionAvail().x; 257 | const char* endLine = text; 258 | 259 | if (widthAvail > 0.0f) 260 | #if IMGUI_VERSION_NUM > 19196 261 | endLine = ImGui::GetFont()->CalcWordWrapPosition(size * scale, text, end, widthAvail); 262 | #else 263 | endLine = ImGui::GetFont()->CalcWordWrapPositionA(scale, text, end, widthAvail); 264 | #endif 265 | if (endLine > text && endLine < end) 266 | { 267 | if (isPartOfWord(*endLine)) 268 | { 269 | // Get maximum line width like this because dear imgui rc 1.90.9+ deprecated these 270 | const float nextLineWidth = (ImGui::GetContentRegionAvail() + ImGui::GetCursorScreenPos()).x; 271 | #if IMGUI_VERSION_NUM > 19196 272 | const char* nextLineEnd = ImGui::GetFont()->CalcWordWrapPosition(size * scale, text, end, nextLineWidth); 273 | #else 274 | const char* nextLineEnd = ImGui::GetFont()->CalcWordWrapPositionA(scale, text, end, nextLineWidth); 275 | #endif 276 | if (nextLineEnd == end || (nextLineEnd <= end && !isPartOfWord(*nextLineEnd))) 277 | endLine = text; 278 | } 279 | } 280 | 281 | ImGui::PushTextWrapPos(-1.0f); 282 | before(colour); 283 | render(*getData(), text, endLine, colour); 284 | 285 | auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 286 | auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 287 | 288 | after(colour); 289 | ImGui::PopTextWrapPos(); 290 | 291 | widthAvail = ImGui::GetContentRegionAvail().x; 292 | auto result = static_cast(bHovered | bClicked); 293 | while (endLine < end) 294 | { 295 | text = endLine; 296 | if (*text == ' ') 297 | ++text; 298 | #if IMGUI_VERSION_NUM > 19196 299 | endLine = ImGui::GetFont()->CalcWordWrapPosition(size * scale, text, end, widthAvail); 300 | #else 301 | endLine = ImGui::GetFont()->CalcWordWrapPositionA(scale, text, end, widthAvail); 302 | #endif 303 | if (text == endLine) 304 | endLine++; 305 | ImGui::PushTextWrapPos(-1.0f); 306 | before(colour); 307 | render(*getData(), text, endLine, colour); 308 | 309 | bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 310 | bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 311 | 312 | after(colour); 313 | 314 | result = static_cast(result | bHovered | bClicked); 315 | 316 | ImGui::PopTextWrapPos(); 317 | } 318 | return result; 319 | } 320 | -------------------------------------------------------------------------------- /UImGuiTextUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __has_include 3 | #if !__has_include() 4 | #error "Couldn't find imgui.h in the header include path, please add it to the path!" 5 | #endif // ! 6 | #endif 7 | #include 8 | #include 9 | #include "UImGuiString.hpp" 10 | #include "Common.h" 11 | 12 | // To shorten things somewhat 13 | #define UIMGUI_TEXT_COLOUR getDefaultTextColour() 14 | 15 | #define UIMGUI_TEXT_UTILS_DATA (*getData()) 16 | 17 | namespace UImGui 18 | { 19 | struct MLS_PUBLIC_API TextUtilsData 20 | { 21 | ImFont* bold; 22 | ImFont* italic; 23 | ImFont* boldItalic; 24 | ImFont* monospace; 25 | ImFont* smallFont; 26 | 27 | std::function defaultLinkClickEvent = [](const char*) -> void {}; 28 | }; 29 | 30 | class MLS_PUBLIC_API TextUtils 31 | { 32 | public: 33 | typedef UImGui_TextUtils_WidgetState WidgetState; 34 | 35 | struct Colour 36 | { 37 | public: 38 | Colour() noexcept = default; 39 | Colour(const UImGui_TextUtils_Colour& col) noexcept : data(col) {}; 40 | Colour(const UImGui_TextUtils_Colour&& col) noexcept : data(col) {}; 41 | Colour(ImVec4 col) noexcept : data(*reinterpret_cast(&col)) {}; 42 | Colour(ImU32 col) noexcept; 43 | 44 | operator UImGui_TextUtils_Colour() const noexcept; 45 | operator ImU32() const noexcept; 46 | operator ImVec4() const noexcept; 47 | private: 48 | mutable UImGui_TextUtils_Colour data{}; 49 | }; 50 | 51 | static Colour getDefaultTextColour() noexcept; 52 | 53 | static void initTextUtilsData(TextUtilsData* data) noexcept; 54 | static TextUtilsData* getTextUtilsData() noexcept; 55 | 56 | // Renders bold text 57 | static void Bold(const char* fmt, ...) noexcept; 58 | static void BoldV(const char* fmt, va_list list) noexcept; 59 | 60 | // Renders bold text with word wrapping 61 | static void BoldWrapped(const char* fmt, ...) noexcept; 62 | static void BoldWrappedV(const char* fmt, va_list list) noexcept; 63 | 64 | // Renders bold text 65 | static void Italic(const char* fmt, ...) noexcept; 66 | static void ItalicV(const char* fmt, va_list list) noexcept; 67 | 68 | // Renders bold text with word wrapping 69 | static void ItalicWrapped(const char* fmt, ...) noexcept; 70 | static void ItalicWrappedV(const char* fmt, va_list list) noexcept; 71 | 72 | // Renders bold text 73 | static void BoldItalic(const char* fmt, ...) noexcept; 74 | static void BoldItalicV(const char* fmt, va_list list) noexcept; 75 | 76 | // Renders bold text with word wrapping 77 | static void BoldItalicWrapped(const char* fmt, ...) noexcept; 78 | static void BoldItalicWrappedV(const char* fmt, va_list list) noexcept; 79 | 80 | // Renders monospace text 81 | static void Monospace(const char* fmt, ...) noexcept; 82 | static void MonospaceV(const char* fmt, va_list list) noexcept; 83 | 84 | // Renders monospace text with word wrapping 85 | static void MonospaceWrapped(const char* fmt, ...) noexcept; 86 | static void MonospaceWrappedV(const char* fmt, va_list list) noexcept; 87 | 88 | // Renders small text 89 | static void Small(const char* fmt, ...) noexcept; 90 | static void SmallV(const char* fmt, va_list list) noexcept; 91 | 92 | // Renders small text with word wrapping 93 | static void SmallWrapped(const char* fmt, ...) noexcept; 94 | static void SmallWrappedV(const char* fmt, va_list list) noexcept; 95 | 96 | /** 97 | * @brief Renders superscript and subscript characters in 1 function. This is because it's hard to render both 98 | * due to coordinate shifts, which is needed if we want to e.g. render mathematical syntax for 99 | * combinations, which looks like this: 100 | * n 101 | * C 102 | * k 103 | * Setting either text blocks as an empty string will not render it. 104 | * @param subscriptBegin Pointer to the first element of the subscript text array 105 | * @param subscriptEnd Pointer to the last element of the subscript text array 106 | * @param superscriptBegin Pointer to the first element of the superscript text array 107 | * @param superscriptEnd Pointer to the last element of the superscript text array 108 | */ 109 | static void SubSuperscript(const char* subscriptBegin, const char* subscriptEnd, 110 | const char* superscriptBegin, const char* superscriptEnd) noexcept; 111 | 112 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 113 | // C++ TString variant of the normal SubSuperscript function 114 | static void SubSuperscript(const TString& subscript, const TString& superscript) noexcept; 115 | #endif 116 | /** 117 | * @brief Renders ruby text, small text on top of a word. Commonly used for Furigana, a way of annotating 118 | * Japanese Kanji. Looks like this: 119 | * かん じ 120 | * 漢  字 121 | * Where the Hiragana above the kanji is rendered with the small font. 122 | * @param textBegin - Pointer to the start of the main text string, 漢字 in our example 123 | * @param textEnd - Pointer to the end of the main text string, 漢字 in our example 124 | * @param annotationBegin - Pointer to the start of the main annotation text string, かんじ in our example 125 | * @param annotationEnd - Pointer to the end of the main annotation text string, かんじ in our example 126 | * @param bWrapAnnotation - Whether to apply word wrapping to the annotation text, かんじ in our example 127 | * @param bWrapText - Whether to apply word wrapping to the main text, 漢字 in our example 128 | */ 129 | static void Ruby(const char* textBegin, const char* textEnd, 130 | const char* annotationBegin, const char* annotationEnd, 131 | bool bWrapAnnotation = true, bool bWrapText = false) noexcept; 132 | 133 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 134 | // C++ TString version of the normal Ruby function 135 | static void Ruby(const TString& text, const TString& annotation, 136 | bool bWrapAnnotation = true, bool bWrapText = false) noexcept; 137 | #endif 138 | 139 | /** 140 | * @brief Underlines the element above 141 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 142 | * UIMGUI_TEXT_COLOUR macro 143 | */ 144 | static void Underline(Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 145 | 146 | /** 147 | * @brief Abstraction on top of ImGui::Text for underlined text. Renders underlined text without word wrapping 148 | * @tparam Args - A templated variadic list of elements to be formatted by the format string 149 | * @param fmt - The format string 150 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 151 | * UIMGUI_TEXT_COLOUR macro 152 | * @param args - Variadic arguments passed to ImGui::Text 153 | * @return State of the text, represented using the WidgetState bitmask 154 | */ 155 | template 156 | static WidgetState Underline(const char* fmt, const Colour colour = UIMGUI_TEXT_COLOUR, Args... args) noexcept 157 | { 158 | ImGui::Text(fmt, (args)...); 159 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 160 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 161 | Underline(colour); 162 | 163 | return static_cast(bHovered | bClicked); 164 | } 165 | 166 | /** 167 | * @brief Renders underlined text with word wrapping 168 | * @param text - The text pointer 169 | * @param end - Text pointer to the end of the text 170 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 171 | * UIMGUI_TEXT_COLOUR macro 172 | * @return State of the text, represented using the WidgetState bitmask 173 | */ 174 | static WidgetState UnderlineWrapped(const char* text, const char* end, Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 175 | 176 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 177 | // TString wrapper on top of UnderlineWrapped 178 | static WidgetState UnderlineWrapped(const TString& text, Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 179 | #endif 180 | 181 | /** 182 | * @brief Cross out the element above 183 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 184 | * UIMGUI_TEXT_COLOUR macro 185 | */ 186 | static void Strikethrough(Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 187 | 188 | /** 189 | * @brief Abstraction on top of ImGui::Text for strikethrough text. Renders strikethrough text without word wrapping 190 | * @tparam Args - A templated variadic list of elements to be formatted by the format string 191 | * @param fmt - The format string 192 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 193 | * UIMGUI_TEXT_COLOUR macro 194 | * @param args - Variadic arguments passed to ImGui::Text 195 | * @return State of the text, represented using the WidgetState bitmask 196 | */ 197 | template 198 | static WidgetState Strikethrough(const char* fmt, const Colour colour = UIMGUI_TEXT_COLOUR, Args... args) noexcept 199 | { 200 | ImGui::Text(fmt, (args)...); 201 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 202 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 203 | Strikethrough(colour); 204 | 205 | return static_cast(bHovered | bClicked); 206 | } 207 | 208 | /** 209 | * @brief Renders strikethrough text with word wrapping 210 | * @param text - The text pointer 211 | * @param end - Text pointer to the end of the text 212 | * @param colour - Colour of the line, defaults to the current text colour, represented by the 213 | * UIMGUI_TEXT_COLOUR macro 214 | * @return State of the text, represented using the WidgetState bitmask 215 | */ 216 | static WidgetState StrikethroughWrapped(const char* text, const char* end, Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 217 | 218 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 219 | // TString wrapper on top of StrikethroughWrapped 220 | static WidgetState StrikethroughWrapped(const TString& text, Colour colour = UIMGUI_TEXT_COLOUR) noexcept; 221 | #endif 222 | 223 | 224 | /** 225 | * @brief Renders a link without text wrapping 226 | * @param text - The text for the link 227 | * @param colour - The colour of the link, defaults to UIMGUI_LINK_TEXT_UNVISITED 228 | * @param clicked - A callback to be called if the link is clicked, defaults to TextUtilsData::defaultLinkClickEvent 229 | */ 230 | static void Link(const char* text, Colour colour = UIMGUI_LINK_TEXT_UNVISITED, 231 | const std::function& clicked = (*getData())->defaultLinkClickEvent) noexcept; 232 | 233 | /** 234 | * @brief Renders a link with text wrapping 235 | * @param text - The text for the link 236 | * @param end - The end position of the text 237 | * @param colour - The colour of the link, defaults to UIMGUI_LINK_TEXT_UNVISITED 238 | * @param clicked - A callback to be called if the link is clicked, defaults to TextUtilsData::defaultLinkClickEvent 239 | */ 240 | static void LinkWrapped(const char* text, const char* end, Colour colour = UIMGUI_LINK_TEXT_UNVISITED, 241 | const std::function& clicked = (*getData())->defaultLinkClickEvent) noexcept; 242 | 243 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 244 | // TString wrapper on top of LinkWrapped 245 | static void LinkWrapped(const TString& text, Colour colour = UIMGUI_LINK_TEXT_UNVISITED, 246 | const std::function& clicked = (*getData())->defaultLinkClickEvent) noexcept; 247 | #endif 248 | 249 | /** 250 | * @brief Highlight the element above 251 | * @param colour - Colour of the highlight, defaults to the default highlight text colour, represented by the 252 | * UIMGUI_HIGHLIGHT_TEXT_COLOUR macro 253 | */ 254 | static void Highlight(Colour colour = UIMGUI_HIGHLIGHT_TEXT_COLOUR) noexcept; 255 | 256 | /** 257 | * @brief Abstraction on top of ImGui::Text for highlighted text. Renders highlighted text without word wrapping 258 | * @tparam Args - A templated variadic list of elements to be formatted by the format string 259 | * @param fmt - The format string 260 | * @param colour - Colour of the highlight, defaults to the default highlight text colour, represented by the 261 | * UIMGUI_HIGHLIGHT_TEXT_COLOUR macro 262 | * @param args - Variadic arguments passed to ImGui::Text 263 | * @return State of the text, represented using the WidgetState bitmask 264 | */ 265 | template 266 | static WidgetState Highlight(const char* fmt, const Colour colour = UIMGUI_HIGHLIGHT_TEXT_COLOUR, Args... args) noexcept 267 | { 268 | ImGui::Text(fmt, (args)...); 269 | const auto bHovered = ImGui::IsItemHovered() ? UIMGUI_TEXT_UTILS_WIDGET_STATE_HOVERED : 0; 270 | const auto bClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left) ? UIMGUI_TEXT_UTILS_WIDGET_STATE_CLICKED : 0; 271 | Highlight(colour); 272 | 273 | return static_cast(bHovered | bClicked); 274 | } 275 | 276 | /** 277 | * @brief Renders highlighted text with word wrapping 278 | * @param text - The text pointer 279 | * @param end - Text pointer to the end of the text 280 | * @param colour - Colour of the highlight, defaults to the default highlight text colour, represented by the 281 | * UIMGUI_HIGHLIGHT_TEXT_COLOUR macro 282 | * @return State of the text, represented using the WidgetState bitmask 283 | */ 284 | static WidgetState HighlightWrapped(const char* text, const char* end, Colour colour = UIMGUI_HIGHLIGHT_TEXT_COLOUR) noexcept; 285 | 286 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 287 | // TString wrapper on top of HighlightWrapped 288 | static WidgetState HighlightWrapped(const TString& text, Colour colour = UIMGUI_HIGHLIGHT_TEXT_COLOUR) noexcept; 289 | #endif 290 | 291 | /** 292 | * @brief Renders a single blockquote rectangle 293 | * @param colour - Colour of the blockquote, defaults to the default blockquote colour, represented by the 294 | * UIMGUI_BLOCKQUOTE_TEXT_COLOUR macro 295 | */ 296 | static void Blockquote(Colour colour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 297 | 298 | /** 299 | * @brief Abstraction on top of ImGui::Text for blockquotes. Renders text without word wrapping 300 | * @tparam Args - A templated variadic list of elements to be formatted by the format string 301 | * @param fmt - The format string 302 | * @param colour - Colour of the blockquote, defaults to the default blockquote colour, represented by the 303 | * UIMGUI_BLOCKQUOTE_TEXT_COLOUR macro 304 | * @param args - Variadic arguments passed to ImGui::Text 305 | */ 306 | template 307 | static void Blockquote(const char* fmt, const Colour colour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR, Args... args) noexcept 308 | { 309 | Blockquote(colour); 310 | ImGui::SameLine(); 311 | ImGui::Text(fmt, (args)...); 312 | } 313 | 314 | /** 315 | * @brief Renders text in blockquote with word wrapping 316 | * @param text - The text pointer 317 | * @param end - Text pointer to the end of the text 318 | * @param colour - Colour of the blockquote, defaults to the default blockquote colour, represented by the 319 | * UIMGUI_BLOCKQUOTE_TEXT_COLOUR macro 320 | */ 321 | static void BlockquoteWrapped(const char* text, const char* end, Colour colour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 322 | 323 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 324 | // TString wrapper on top of HighlightWrapped 325 | static void BlockquoteWrapped(const TString& text, Colour colour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 326 | #endif 327 | 328 | /** 329 | * @brief Renders text code as a markdown code block 330 | * @param begin - Pointer to the beginning of the string 331 | * @param end - Pointer to the end of the string 332 | * @param bWrapText - Whether to enable word wrapping 333 | * @param backgroundColour - The background colour of the code block, defaults to UIMGUI_BLOCKQUOTE_TEXT_COLOUR 334 | */ 335 | static void CodeBlock(const char* begin, const char* end, bool bWrapText, 336 | Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 337 | 338 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 339 | // C++ TString abstraction on top of the normal CodeBlock function 340 | static void CodeBlock(const TString& text, bool bWrapText, 341 | Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 342 | #endif 343 | 344 | 345 | /** 346 | * @brief Renders inline code 347 | * @param begin - Pointer to the beginning of the string 348 | * @param end - Pointer to the end of the string 349 | * @param backgroundColour - The background colour of the code block, defaults to UIMGUI_BLOCKQUOTE_TEXT_COLOUR 350 | */ 351 | static void CodeInline(const char* begin, const char* end, Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 352 | 353 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 354 | // C++ TString abstraction on top of the normal CodeInline function 355 | static void CodeInline(const TString& text, Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 356 | #endif 357 | 358 | /** 359 | * @brief Renders inline code with word wrapping 360 | * @param begin - Pointer to the beginning of the string 361 | * @param end - Pointer to the end of the string 362 | * @param backgroundColour - The background colour of the code block, defaults to UIMGUI_BLOCKQUOTE_TEXT_COLOUR 363 | */ 364 | static void CodeInlineWrapped(const char* begin, const char* end, Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 365 | 366 | #ifndef UIMGUI_TEXT_UTILS_DISABLE_STRING 367 | // C++ TString abstraction on top of the normal CodeInlineWrapped function 368 | static void CodeInlineWrapped(const TString& text, Colour backgroundColour = UIMGUI_BLOCKQUOTE_TEXT_COLOUR) noexcept; 369 | #endif 370 | 371 | // dear imgui's ImGui::ShowDemoWindow equivalent. While the only argument is a void*, treat it as a boolean 372 | static void ShowDemoWindow(void* bOpen = nullptr) noexcept; 373 | private: 374 | static void customFontGenericText(const char* fmt, ImFont* font, va_list args) noexcept; 375 | static void customFontGenericTextWrapped(const char* fmt, ImFont* font, va_list args) noexcept; 376 | 377 | static bool isPartOfWord(char character) noexcept; 378 | static TextUtilsData** getData() noexcept; 379 | 380 | static WidgetState renderWrappedTextGeneric(const char* text, const char* end, Colour colour, 381 | const std::function& after, 382 | const std::function& before, 383 | const std::function& render = 384 | [](UImGui::TextUtilsData* data, const char* s, const char* e, Colour) -> void { 385 | ImGui::TextUnformatted(s, e); 386 | }) noexcept; 387 | }; 388 | } --------------------------------------------------------------------------------