├── src ├── .gitignore ├── diagram.png ├── assets │ └── fonts │ │ └── Inconsolata-Regular.ttf ├── imgui_demo_hellorun │ ├── CMakeLists.txt │ └── imgui_demo_hellorun.main.cpp ├── imgui_utilities │ ├── HyperlinkHelper.h │ ├── MarkdownHelper.h │ ├── ImGuiExt.h │ ├── CMakeLists.txt │ ├── HyperlinkHelper.cpp │ ├── MarkdownHelper.cpp │ └── ImGuiExt.cpp ├── source_parse │ ├── ImGuiRepoUrl.h │ ├── ImGuiCodeParser.h │ ├── ImGuiDemoParser.h │ ├── tests │ │ ├── ImGuiDemoParser_test.cpp │ │ ├── ImGuiHeaderDocParser_test.cpp │ │ ├── CMakeLists.txt │ │ ├── Tree_test.cpp │ │ └── HeaderTree_test.cpp │ ├── HeaderTree.h │ ├── CMakeLists.txt │ ├── Sources.h │ ├── GuiHeaderTree.h │ ├── HeaderTree.cpp │ ├── Sources.cpp │ ├── GuiHeaderTree.cpp │ ├── ImGuiDemoParser.cpp │ ├── Tree.h │ └── ImGuiCodeParser.cpp ├── imgui_example_run │ ├── imgui_example_run.sh │ └── imgui_example_run_emscripten.sh ├── ImGuiCppDocBrowser.h ├── ImGuiHeaderDocBrowser.h ├── LibrariesCodeBrowser.h ├── JsClipboardTricks.h ├── CMakeLists.txt ├── WindowWithEditor.h ├── ImGuiCppDocBrowser.cpp ├── ImGuiDemoBrowser.h ├── populate_assets.sh ├── ImGuiHeaderDocBrowser.cpp ├── shell.emscripten.html ├── LibrariesCodeBrowser.cpp ├── JsClipboardTricks.cpp ├── diagram.md ├── ImGuiDemoBrowser.cpp ├── ImGuiManual.cpp └── WindowWithEditor.cpp ├── .gitignore ├── doc └── images │ ├── link_manual.png │ ├── link_youtube_demo.png │ └── link_youtube_demo_nav.png ├── .theia └── settings.json ├── tools ├── deploy_imgui_tree.sh ├── gitpod │ ├── gitpod_emsdk_set_env.sh │ ├── gitpod.Dockerfile │ ├── gitpod_help.sh │ └── index.build_emscripten.html ├── emscripten_build.sh └── deploy_imgui_manual.sh ├── .gitmodules ├── .gitpod.yml ├── LICENSE ├── CMakeLists.txt ├── .clang-format └── Readme.md /src/.gitignore: -------------------------------------------------------------------------------- 1 | assets/code/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build_*/ 3 | cmake-build*/ 4 | .idea/ 5 | imgui_manual_online/ 6 | -------------------------------------------------------------------------------- /src/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pthom/imgui_manual/HEAD/src/diagram.png -------------------------------------------------------------------------------- /doc/images/link_manual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pthom/imgui_manual/HEAD/doc/images/link_manual.png -------------------------------------------------------------------------------- /doc/images/link_youtube_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pthom/imgui_manual/HEAD/doc/images/link_youtube_demo.png -------------------------------------------------------------------------------- /doc/images/link_youtube_demo_nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pthom/imgui_manual/HEAD/doc/images/link_youtube_demo_nav.png -------------------------------------------------------------------------------- /src/assets/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pthom/imgui_manual/HEAD/src/assets/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /src/imgui_demo_hellorun/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(hello_imgui_add_app) 2 | hello_imgui_add_app(imgui_demo_hellorun imgui_demo_hellorun.main.cpp) 3 | -------------------------------------------------------------------------------- /src/imgui_utilities/HyperlinkHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace HyperlinkHelper 4 | { 5 | void OpenUrl(const std::string &url); 6 | } 7 | -------------------------------------------------------------------------------- /src/source_parse/ImGuiRepoUrl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | inline std::string ImGuiRepoUrl() 5 | { 6 | return "https://github.com/pthom/imgui/blob/DemoCodeDocking/"; 7 | } 8 | -------------------------------------------------------------------------------- /src/source_parse/ImGuiCodeParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Sources.h" 4 | 5 | namespace SourceParse 6 | { 7 | AnnotatedSource ReadImGuiHeaderDoc(); 8 | AnnotatedSource ReadImGuiCppDoc(); 9 | } -------------------------------------------------------------------------------- /src/imgui_example_run/imgui_example_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | THIS_SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | IMGUI_DIR=$THIS_SCRIPT_DIR/../../external/imgui 5 | cd $IMGUI_DIR/examples/example_sdl_opengl3 6 | make && ./example_sdl_opengl3 7 | -------------------------------------------------------------------------------- /.theia/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpp.buildConfigurations": [ 3 | { 4 | "name": "Debug", 5 | "directory": "/workspace/imgui_manual/build_emscripten" 6 | } 7 | ], 8 | "cpp.clangdArgs": "--compile-commands-dir=/workspace/imgui_manual/build_emscripten", 9 | } -------------------------------------------------------------------------------- /src/imgui_utilities/MarkdownHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "imgui.h" 3 | #include 4 | 5 | namespace MarkdownHelper 6 | { 7 | extern ImFont *fontH1, *fontH2, *fontH3; 8 | 9 | void LoadFonts(); 10 | void Markdown(const std::string &markdown_); 11 | } -------------------------------------------------------------------------------- /tools/deploy_imgui_tree.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | REPO_DIR=$THIS_DIR/.. 5 | cd $REPO_DIR 6 | 7 | ./tools/emscripten_build.sh 8 | cd build_emscripten 9 | rsync -vaz src pascal@traineq.org:HTML/imgui_manual_tree 10 | -------------------------------------------------------------------------------- /src/imgui_example_run/imgui_example_run_emscripten.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | THIS_SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | IMGUI_DIR=$THIS_SCRIPT_DIR/../../external/imgui 5 | 6 | source ~/Utils/emsdk/emsdk_env.sh 7 | cd $IMGUI_DIR/examples/example_emscripten_opengl3 8 | USE_FILE_SYSTEM=1 make && make serve 9 | -------------------------------------------------------------------------------- /src/source_parse/ImGuiDemoParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "source_parse/Sources.h" 4 | 5 | namespace SourceParse 6 | { 7 | AnnotatedSource ReadImGuiDemoCode(); 8 | AnnotatedSource ReadImGuiDemoCodePython(); 9 | std::unordered_map FindExampleAppsCode(); 10 | } // namespace SourceParse 11 | -------------------------------------------------------------------------------- /tools/gitpod/gitpod_emsdk_set_env.sh: -------------------------------------------------------------------------------- 1 | export PATH=$PATH:/home/gitpod/emsdk/upstream/emscripten:/home/gitpod/emsdk/node/12.18.1_64bit/bin 2 | export EMSDK="/home/gitpod/emsdk" 3 | export EM_CONFIG="/home/gitpod/emsdk/.emscripten" 4 | export EM_CACHE="/home/gitpod/emsdk/upstream/emscripten/cache" 5 | export EMSDK_NODE="/home/gitpod/emsdk/node/12.18.1_64bit/bin/node" 6 | -------------------------------------------------------------------------------- /tools/emscripten_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | REPO_DIR=$THIS_DIR/.. 5 | cd $REPO_DIR 6 | 7 | if [ ! -d build_emscripten ]; then 8 | mkdir build_emscripten 9 | fi 10 | 11 | cd build_emscripten 12 | source ~/emsdk/emsdk_env.sh 13 | emcmake cmake .. -DCMAKE_BUILD_TYPE=Release 14 | make -j 4 15 | -------------------------------------------------------------------------------- /src/source_parse/tests/ImGuiDemoParser_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest.h" 3 | 4 | #include "source_parse/ImGuiDemoParser.h" 5 | #include 6 | 7 | using namespace SourceParse; 8 | 9 | TEST_CASE("Test FindExampleAppsCode") 10 | { 11 | std::unordered_map apps = FindExampleAppsCode(); 12 | CHECK_GE(apps.size(), 13); 13 | } 14 | -------------------------------------------------------------------------------- /src/source_parse/HeaderTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/Tree.h" 3 | #include "source_parse/Sources.h" 4 | 5 | namespace SourceParse 6 | { 7 | 8 | using HeaderTree = Tree; 9 | 10 | HeaderTree makeHeaderTree( 11 | const LinesWithTags& linesWithTags, 12 | const LineWithTag& treeTopLeaf = { -1, "Table Of Content", -1} ); 13 | 14 | } // namespace SourceParse 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/source_parse/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FILE(GLOB sources 2 | ${CMAKE_CURRENT_LIST_DIR}/*.h 3 | ${CMAKE_CURRENT_LIST_DIR}/*.cpp 4 | ) 5 | add_library(source_parse STATIC ${sources}) 6 | target_include_directories(source_parse PUBLIC 7 | ${fplus_dir} 8 | ${CMAKE_CURRENT_LIST_DIR}/.. 9 | ) 10 | target_link_libraries(source_parse PRIVATE hello_imgui) 11 | 12 | if (IMGUI_MANUAL_BUILD_TESTS) 13 | add_subdirectory(tests) 14 | endif() 15 | -------------------------------------------------------------------------------- /src/source_parse/tests/ImGuiHeaderDocParser_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest.h" 3 | 4 | #include "source_parse/HeaderTree.h" 5 | #include "source_parse/ImGuiCodeParser.h" 6 | #include "source_parse/Sources.h" 7 | #include 8 | 9 | TEST_CASE("Test ReadImGuiHeaderDoc") 10 | { 11 | auto annotatedSource = SourceParse::ReadImGuiHeaderDoc(); 12 | CHECK(annotatedSource.linesWithTags.size() > 10); 13 | } 14 | -------------------------------------------------------------------------------- /src/imgui_utilities/ImGuiExt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace ImGuiExt 5 | { 6 | void SameLine_IfPossible(float minRightMargin = 60.f); 7 | bool ClickableText(const std::string &txt); 8 | 9 | bool Button_WithEnabledFlag(const char *label, bool enabled, const char *tooltip = nullptr, bool sameLineAfter = false); 10 | bool SmallButton_WithEnabledFlag(const char *label, bool enabled, const char *tooltip = nullptr, bool sameLineAfter = false); 11 | } 12 | -------------------------------------------------------------------------------- /tools/gitpod/gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | 3 | # Install custom tools, runtimes, etc. 4 | # For example "bastet", a command-line tetris clone: 5 | # RUN brew install bastet 6 | # 7 | # More information: https://www.gitpod.io/docs/config-docker/ 8 | 9 | # Install emscripten toolchain 10 | RUN cd ~ \ 11 | && git clone https://github.com/emscripten-core/emsdk.git \ 12 | && cd emsdk \ 13 | && git pull \ 14 | && ./emsdk install latest \ 15 | && ./emsdk activate latest 16 | -------------------------------------------------------------------------------- /tools/gitpod/gitpod_help.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "" 4 | echo "In order to compile the project just enter:" 5 | echo " make" 6 | echo "" 7 | echo "You can quickly add a new program by adding a new cpp file in the playground/ folder." 8 | echo "After adding a new cpp file, just enter:" 9 | echo " cmake ." 10 | echo "Then enter:" 11 | echo " make" 12 | echo "" 13 | echo "In order to run the programs, click the \"Open Ports\" tab a the bottom, and click on \"Open Browser\"" 14 | echo "" 15 | -------------------------------------------------------------------------------- /src/imgui_utilities/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FILE(GLOB sources 2 | ${CMAKE_CURRENT_LIST_DIR}/*.h 3 | ${CMAKE_CURRENT_LIST_DIR}/*.cpp 4 | ${imgui_markdown_dir}/imgui_markdown.h 5 | ${textedit_dir}/TextEditor.h 6 | ${textedit_dir}/TextEditor.cpp 7 | ) 8 | add_library(imgui_utilities STATIC ${sources}) 9 | target_include_directories(imgui_utilities PUBLIC 10 | ${textedit_dir} 11 | ${fplus_dir} 12 | ${imgui_markdown_dir} 13 | ) 14 | target_link_libraries(imgui_utilities PRIVATE hello_imgui) 15 | -------------------------------------------------------------------------------- /src/ImGuiCppDocBrowser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/Sources.h" 3 | #include "WindowWithEditor.h" 4 | #include "source_parse/GuiHeaderTree.h" 5 | 6 | // This windows shows the docs contained in imgui.cpp 7 | class ImGuiCppDocBrowser: public WindowWithEditor 8 | { 9 | public: 10 | ImGuiCppDocBrowser(); 11 | void gui(); 12 | 13 | private: 14 | void guiTags(); 15 | void guiGithubButton(); 16 | 17 | SourceParse::AnnotatedSource mAnnotatedSource; 18 | SourceParse::GuiHeaderTree mGuiHeaderTree; 19 | }; 20 | -------------------------------------------------------------------------------- /src/ImGuiHeaderDocBrowser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/GuiHeaderTree.h" 3 | #include "source_parse/Sources.h" 4 | #include "WindowWithEditor.h" 5 | 6 | // This windows shows the docs contained in imgui.cpp 7 | class ImGuiHeaderDocBrowser: public WindowWithEditor 8 | { 9 | public: 10 | ImGuiHeaderDocBrowser(); 11 | void gui(); 12 | private: 13 | void guiTags(); 14 | void guiGithubButton(); 15 | 16 | SourceParse::AnnotatedSource mAnnotatedSource; 17 | SourceParse::GuiHeaderTree mGuiHeaderTree; 18 | }; 19 | -------------------------------------------------------------------------------- /tools/deploy_imgui_manual.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | REPO_DIR=$THIS_DIR/.. 5 | cd $REPO_DIR 6 | 7 | if [ ! -d imgui_manual_online ]; then 8 | echo "Sorry, this script is reserved for the author!" 9 | exit 1 10 | fi 11 | 12 | ./tools/emscripten_build.sh 13 | cp build_emscripten/src/imgui_manual* imgui_manual_online/static/manual 14 | cd imgui_manual_online 15 | hugo -d docs 16 | git add static/manual docs/manual 17 | git commit -m "update / build on $(date)" 18 | git push 19 | -------------------------------------------------------------------------------- /src/imgui_demo_hellorun/imgui_demo_hellorun.main.cpp: -------------------------------------------------------------------------------- 1 | #include "hello_imgui/hello_imgui.h" 2 | int main(int , char *[]) 3 | { 4 | HelloImGui::RunnerParams runnerParams; // runnerParams will contains all the application params and callbacks 5 | runnerParams.callbacks.ShowGui = // ShowGui contains a lambda function with the Gui code 6 | [&runnerParams]{ 7 | ImGui::ShowDemoWindow(); 8 | }; 9 | runnerParams.appWindowParams.windowTitle = "imgui_demo_hellorun"; 10 | runnerParams.appWindowParams.windowGeometry.size = {1280, 720}; 11 | HelloImGui::Run(runnerParams); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/LibrariesCodeBrowser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/Sources.h" 3 | #include "WindowWithEditor.h" 4 | #include "hello_imgui/hello_imgui.h" 5 | #include 6 | 7 | 8 | class LibrariesCodeBrowser: public WindowWithEditor 9 | { 10 | public: 11 | LibrariesCodeBrowser( 12 | const std::string & windowName, 13 | const std::vector& librarySources, 14 | std::string currentSourcePath 15 | ); 16 | void gui(); 17 | private: 18 | bool guiSelectLibrarySource(); 19 | 20 | std::vector mLibraries; 21 | SourceParse::SourceFile mCurrentSource; 22 | }; 23 | -------------------------------------------------------------------------------- /src/source_parse/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(add_one_cpp_test test_cpp_file) 2 | get_filename_component(test_exe_file ${test_cpp_file} NAME_WE) 3 | 4 | add_executable(${test_exe_file} ${test_cpp_file}) 5 | target_link_libraries(${test_exe_file} PRIVATE source_parse) 6 | target_include_directories(${test_exe_file} PRIVATE ${doctest_dir}) 7 | add_test(NAME ${test_exe_file} COMMAND ${test_exe_file}) 8 | endfunction() 9 | 10 | file(CREATE_LINK ${CMAKE_BINARY_DIR}/src/assets ${CMAKE_CURRENT_BINARY_DIR}/assets SYMBOLIC) 11 | 12 | add_one_cpp_test(ImGuiHeaderDocParser_test.cpp) 13 | add_one_cpp_test(ImGuiDemoParser_test.cpp) 14 | add_one_cpp_test(HeaderTree_test.cpp) 15 | add_one_cpp_test(Tree_test.cpp) 16 | -------------------------------------------------------------------------------- /src/JsClipboardTricks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The clipboard handling features take inspiration from sokol 4 | // https://github.com/floooh/sokol 5 | // 6 | // Unused since we switched to glfw3 via pongasoft/emscripten-glfw 7 | // which has clipboard support built-in (see https://github.com/ocornut/imgui/issues/7851) 8 | 9 | #ifdef __EMSCRIPTEN__ 10 | #define IMGUIMANUAL_CLIPBOARD_EXPORT_TO_BROWSER 11 | // #define IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER # This still needs work... 12 | #endif 13 | 14 | 15 | #ifdef IMGUIMANUAL_CLIPBOARD_EXPORT_TO_BROWSER 16 | void JsClipboard_InstallPlaformSetClipboardText(); 17 | #endif 18 | 19 | #ifdef IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER 20 | void JsClipboard_AddJsHook(); 21 | #endif 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/hello_imgui"] 2 | path = external/hello_imgui 3 | url = https://github.com/pthom/hello_imgui.git 4 | [submodule "external/ImGuiColorTextEdit"] 5 | path = external/ImGuiColorTextEdit 6 | url = https://github.com/BalazsJako/ImGuiColorTextEdit.git 7 | [submodule "external/FunctionalPlus"] 8 | path = external/FunctionalPlus 9 | url = https://github.com/Dobiasd/FunctionalPlus.git 10 | [submodule "external/imgui_markdown"] 11 | path = external/imgui_markdown 12 | url = https://github.com/juliettef/imgui_markdown.git 13 | [submodule "ImGuiColorTextEdit"] 14 | url = https://github.com/pthom/ImGuiColorTextEdit.git 15 | [submodule "external/imgui"] 16 | path = external/imgui 17 | url = https://github.com/pthom/imgui.git 18 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: tools/gitpod/gitpod.Dockerfile 3 | 4 | tasks: 5 | - command: | 6 | echo "Server Terminal" 7 | sleep 5 8 | cd build_emscripten 9 | python -m http.server 10 | name: http server 11 | - command: echo "Main Terminal - You are in the top folder of the repository" 12 | name: main 13 | - command: | 14 | echo "Build Terminal" 15 | source ./tools/gitpod/gitpod_emsdk_set_env.sh 16 | mkdir build_emscripten 17 | cd build_emscripten 18 | cp ../tools/gitpod/index.build_emscripten.html index.html 19 | emcmake cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 20 | make -j 4 21 | /workspace/imgui_manual/tools/gitpod/gitpod_help.sh 22 | name: build 23 | 24 | ports: 25 | - port: 8000 26 | onOpen: open-preview 27 | -------------------------------------------------------------------------------- /src/imgui_utilities/HyperlinkHelper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined(__EMSCRIPTEN__) 4 | #include 5 | #elif defined(_WIN32) 6 | #include 7 | #include 8 | #elif defined(__APPLE__) 9 | #include 10 | #include 11 | #endif 12 | 13 | #include "HyperlinkHelper.h" 14 | 15 | 16 | namespace HyperlinkHelper 17 | { 18 | void OpenUrl(const std::string &url) 19 | { 20 | bool isAbsoluteUrl = fplus::is_prefix_of(std::string("http"), url); 21 | if (!isAbsoluteUrl) 22 | return; 23 | #if defined(__EMSCRIPTEN__) 24 | std::string js_command = "window.open(\"" + url + "\");"; 25 | emscripten_run_script(js_command.c_str()); 26 | #elif defined(_WIN32) 27 | ShellExecuteA( NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL ); 28 | #elif defined(TARGET_OS_MAC) 29 | std::string cmd = std::string("open ") + url.c_str(); 30 | system(cmd.c_str()); 31 | #endif 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(hello_imgui_add_app) 2 | 3 | execute_process(COMMAND bash -c ${CMAKE_CURRENT_LIST_DIR}/populate_assets.sh) 4 | 5 | set(textedit_dir ${CMAKE_CURRENT_LIST_DIR}/../external/ImGuiColorTextEdit) 6 | set(fplus_dir ${CMAKE_CURRENT_LIST_DIR}/../external/FunctionalPlus/include) 7 | set(imgui_markdown_dir ${CMAKE_CURRENT_LIST_DIR}/../external/imgui_markdown) 8 | set(doctest_dir ${CMAKE_CURRENT_LIST_DIR}/../external/doctest/include) 9 | 10 | add_subdirectory(imgui_utilities) 11 | add_subdirectory(source_parse) 12 | #add_subdirectory(make_example_apps) 13 | add_subdirectory(imgui_demo_hellorun) 14 | 15 | FILE(GLOB sources_imgui_manual 16 | ${CMAKE_CURRENT_LIST_DIR}/*.h 17 | ${CMAKE_CURRENT_LIST_DIR}/*.cpp 18 | ) 19 | hello_imgui_add_app(imgui_manual ${sources_imgui_manual}) 20 | target_link_libraries(imgui_manual PRIVATE imgui_utilities source_parse) 21 | 22 | if (IMGUI_MANUAL_CAN_WRITE_IMGUI_DEMO_CPP) 23 | target_compile_definitions(imgui_manual 24 | PRIVATE 25 | IMGUI_MANUAL_CAN_WRITE_IMGUI_DEMO_CPP 26 | -DIMGUI_MANUAL_REPO_DIR="${CMAKE_SOURCE_DIR}" 27 | ) 28 | endif() 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2020 Pascal Thomet 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. -------------------------------------------------------------------------------- /src/WindowWithEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/Sources.h" 3 | #include "TextEditor.h" 4 | #include "hello_imgui/hello_imgui.h" 5 | 6 | 7 | using VoidFunction = std::function; 8 | 9 | class WindowWithEditor 10 | { 11 | public: 12 | WindowWithEditor(const std::string & windowLabel); 13 | 14 | void setEditorAnnotatedSource(const SourceParse::AnnotatedSource &annotatedSource); 15 | void RenderEditor(const std::string& filename, VoidFunction additionalGui = {}); 16 | 17 | void searchForFirstOccurence(const std::string& search); 18 | static void searchForFirstOccurenceAndFocusWindow( 19 | const std::string& search, 20 | const std::string& windowName 21 | ); 22 | 23 | std::string windowLabel() const { return mWindowLabel; } 24 | 25 | TextEditor * _GetTextEditorPtr() { return &mEditor; } 26 | TextEditor& InnerTextEditor() { return mEditor; } 27 | 28 | private: 29 | void guiStatusLine(const std::string& filename); 30 | void guiFind(); 31 | void guiIconBar(VoidFunction additionalGui); 32 | void editorContextMenu(); 33 | 34 | protected: 35 | std::string mWindowLabel; 36 | TextEditor mEditor; 37 | ImGuiTextFilter mFilter; 38 | int mNbFindMatches = 0; 39 | bool mShowLongLinesOverlay = true; 40 | }; 41 | 42 | void menuEditorTheme(); 43 | void LoadMonospaceFont(); 44 | 45 | -------------------------------------------------------------------------------- /src/source_parse/Sources.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace SourceParse 9 | { 10 | using SourceCode = std::string; 11 | using SourcePath = std::string; 12 | using LibraryFolderPath = std::string; 13 | 14 | struct SourceFile 15 | { 16 | SourcePath sourcePath; 17 | SourceCode sourceCode; 18 | }; 19 | 20 | struct LineWithTag 21 | { 22 | int lineNumber; 23 | std::string tag; // tag can be an Id or a title 24 | int level = 0; // optional title level 25 | std::string _original_tag_full; // Copy of the full tag if tag was simplified 26 | }; 27 | using LinesWithTags = std::vector; 28 | 29 | using NumberedLine = std::pair; 30 | using NumberedLines = std::vector; 31 | 32 | struct AnnotatedSource 33 | { 34 | SourceFile source; 35 | LinesWithTags linesWithTags; 36 | }; 37 | 38 | struct Library 39 | { 40 | LibraryFolderPath path; 41 | std::string name; 42 | std::string url; 43 | std::string shortDoc; // markdown 44 | std::vector sourcePaths; 45 | }; 46 | 47 | std::vector imguiLibrary(); 48 | std::vector helloImGuiLibrary(); 49 | std::vector imguiManualLibrary(); 50 | std::vector otherLibraries(); 51 | std::vector acknowldegmentLibraries(); 52 | 53 | 54 | SourceFile ReadSource(const std::string sourcePath); 55 | 56 | inline std::ostream& operator<<(std::ostream& os, const SourceParse::LineWithTag& t) 57 | { 58 | os << "{line:" << t.lineNumber << ", " << t.tag << ", level:" << t.level << "}"; 59 | return os; 60 | } 61 | 62 | } // namespace SourceParse 63 | -------------------------------------------------------------------------------- /src/ImGuiCppDocBrowser.cpp: -------------------------------------------------------------------------------- 1 | #include "source_parse/ImGuiRepoUrl.h" 2 | 3 | #include "ImGuiCppDocBrowser.h" 4 | #include "hello_imgui/hello_imgui.h" 5 | #include "imgui_utilities/HyperlinkHelper.h" 6 | #include "imgui_utilities/ImGuiExt.h" 7 | #include "source_parse/ImGuiCodeParser.h" 8 | #include "hello_imgui/icons_font_awesome_4.h" 9 | 10 | ImGuiCppDocBrowser::ImGuiCppDocBrowser() 11 | : WindowWithEditor("imgui.cpp - Doc") 12 | , mAnnotatedSource(SourceParse::ReadImGuiCppDoc()) 13 | , mGuiHeaderTree(mAnnotatedSource.linesWithTags) 14 | { 15 | setEditorAnnotatedSource(mAnnotatedSource); 16 | } 17 | 18 | void ImGuiCppDocBrowser::gui() 19 | { 20 | static bool showHelp = true; 21 | if (showHelp) 22 | { 23 | std::string help = 24 | "The high level doc for Dear ImGui is simply stored inside imgui.cpp\n"; 25 | ImGui::TextWrapped("%s", help.c_str()); 26 | if (ImGui::Button(ICON_FA_THUMBS_UP " Got it")) 27 | showHelp = false; 28 | } 29 | guiTags(); 30 | RenderEditor("imgui.cpp", [this] { this->guiGithubButton(); }); 31 | } 32 | 33 | void ImGuiCppDocBrowser::guiTags() 34 | { 35 | int currentEditorLineNumber = mEditor.GetCursorPosition().mLine; 36 | int selectedLine = mGuiHeaderTree.gui(currentEditorLineNumber); 37 | if (selectedLine >= 0) 38 | mEditor.SetCursorPosition({selectedLine, 0}, 3); 39 | } 40 | 41 | void ImGuiCppDocBrowser::guiGithubButton() 42 | { 43 | if (ImGui::SmallButton("View on github at this line")) 44 | { 45 | std::string url = ImGuiRepoUrl() + "imgui.cpp#L" 46 | + std::to_string(mEditor.GetCursorPosition().mLine + 1); 47 | HyperlinkHelper::OpenUrl(url); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ImGuiDemoBrowser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "WindowWithEditor.h" 5 | #include "source_parse/Sources.h" 6 | #include "source_parse/GuiHeaderTree.h" 7 | 8 | enum class ViewPythonOrCpp { Cpp, Python }; 9 | 10 | // This is the window that shows imgui_demo.cpp code, 11 | // with a callback that will point to the correct line number 12 | // (see implImGuiDemoCallbackDemoCallback) 13 | class ImGuiDemoBrowser 14 | { 15 | public: 16 | ImGuiDemoBrowser(); 17 | void gui(); 18 | void ImGuiDemoCallback(const char* file, int line_number, const char* demo_title); 19 | 20 | private: 21 | void guiHelp(); 22 | void guiSave(); 23 | void guiEditorAdditional(); 24 | void guiDemoCodeTags(); 25 | 26 | struct SourceElements 27 | { 28 | SourceParse::AnnotatedSource AnnotatedSource; 29 | SourceParse::GuiHeaderTree_FollowDemo mGuiHeaderTree; 30 | WindowWithEditor mWindowWithEditor; 31 | 32 | SourceElements(const SourceParse::AnnotatedSource& as, const std::string& editorLabel) 33 | : AnnotatedSource(as), mGuiHeaderTree(as.linesWithTags), mWindowWithEditor(editorLabel) 34 | { 35 | mWindowWithEditor.InnerTextEditor().SetText(as.source.sourceCode); 36 | } 37 | }; 38 | 39 | SourceElements& CurrentSourceElements() 40 | { 41 | return mViewPythonOrCpp == ViewPythonOrCpp::Cpp ? mSourceElementsCpp : mSourceElementsPython; 42 | } 43 | 44 | std::vector AllSourceElements(){ return {&mSourceElementsCpp, &mSourceElementsPython}; } 45 | 46 | SourceElements mSourceElementsCpp; 47 | SourceElements mSourceElementsPython; 48 | 49 | ViewPythonOrCpp mViewPythonOrCpp = ViewPythonOrCpp::Cpp; 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /src/populate_assets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Populating assets" 4 | 5 | THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 6 | cd $THIS_DIR/../external || exit 1 7 | SRC_DIR=$THIS_DIR 8 | EXTERNAL_DIR=$THIS_DIR/../external 9 | IMGUI_DIR=$THIS_DIR/../external/imgui/ 10 | 11 | echo "THIS_DIR=$THIS_DIR" 12 | cd $THIS_DIR 13 | if [ ! -d assets/code ]; then 14 | mkdir -p assets/code 15 | fi 16 | cd assets/code 17 | 18 | 19 | if [ ! -d imgui_manual ]; then 20 | mkdir imgui_manual 21 | fi 22 | cd imgui_manual 23 | cp -f $SRC_DIR/*.* . 24 | cp -f $SRC_DIR/../Readme.md . 25 | cp -f $SRC_DIR/../LICENSE . 26 | cp -r $SRC_DIR/source_parse . 27 | cp -r $SRC_DIR/imgui_demo_python . 28 | cd .. 29 | 30 | if [ ! -d ImGuiColorTextEdit ]; then 31 | mkdir ImGuiColorTextEdit 32 | fi 33 | cd ImGuiColorTextEdit 34 | cp -f $EXTERNAL_DIR/ImGuiColorTextEdit/*.* . 35 | cd .. 36 | 37 | 38 | if [ ! -d imgui_markdown ]; then 39 | mkdir imgui_markdown 40 | fi 41 | cd imgui_markdown 42 | cp -f $EXTERNAL_DIR/imgui_markdown/* . 43 | cd .. 44 | 45 | 46 | if [ ! -d hello_imgui ]; then 47 | mkdir hello_imgui 48 | fi 49 | cd hello_imgui 50 | cp -f $EXTERNAL_DIR/hello_imgui/src/hello_imgui/*.h . 51 | cp -f $EXTERNAL_DIR/hello_imgui/src/hello_imgui/*.md . 52 | cp -f $EXTERNAL_DIR/hello_imgui/LICENSE . 53 | cp -f $EXTERNAL_DIR/hello_imgui/README.md . 54 | rm *.src.md 55 | cd .. 56 | 57 | if [ ! -d imgui ]; then 58 | mkdir imgui 59 | fi 60 | cd imgui 61 | cp $IMGUI_DIR/*.* . 62 | cp $IMGUI_DIR/docs/README.md . 63 | cp $IMGUI_DIR/docs/FAQ.md . 64 | cd .. 65 | 66 | if [ ! -d fplus ]; then 67 | mkdir fplus 68 | fi 69 | cd fplus 70 | fplus_dir=$EXTERNAL_DIR/FunctionalPlus 71 | cp $fplus_dir/*.md . 72 | cp $fplus_dir/LICENSE . 73 | cp $fplus_dir/include/fplus/fplus.hpp . 74 | cd .. 75 | -------------------------------------------------------------------------------- /src/ImGuiHeaderDocBrowser.cpp: -------------------------------------------------------------------------------- 1 | #include "source_parse/ImGuiRepoUrl.h" 2 | 3 | #include "hello_imgui/hello_imgui.h" 4 | #include "imgui_utilities/HyperlinkHelper.h" 5 | #include "imgui_utilities/ImGuiExt.h" 6 | #include "source_parse/ImGuiCodeParser.h" 7 | #include "source_parse/Sources.h" 8 | 9 | #include "ImGuiHeaderDocBrowser.h" 10 | 11 | ImGuiHeaderDocBrowser * gInstance; 12 | 13 | ImGuiHeaderDocBrowser::ImGuiHeaderDocBrowser() 14 | : WindowWithEditor("imgui.h - Doc") 15 | , mAnnotatedSource(SourceParse::ReadImGuiHeaderDoc()) 16 | , mGuiHeaderTree(mAnnotatedSource.linesWithTags) 17 | { 18 | setEditorAnnotatedSource(mAnnotatedSource); 19 | gInstance = this; 20 | } 21 | 22 | void ImGuiHeaderDocBrowser::gui() 23 | { 24 | std::string help = 25 | "imgui.h contains the detailed doc for the different widgets and features.\n" 26 | "See the searchable table of content below (and for example, search for \"docking\")"; 27 | ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.f, 1.0f), "(?)"); 28 | if (ImGui::IsItemHovered()) 29 | ImGui::SetTooltip("%s", help.c_str()); 30 | ImGui::SameLine(50.f); 31 | guiTags(); 32 | RenderEditor("imgui.h", [this] { this->guiGithubButton(); }); 33 | } 34 | 35 | void ImGuiHeaderDocBrowser::guiTags() 36 | { 37 | int currentEditorLineNumber = mEditor.GetCursorPosition().mLine; 38 | int selectedLine = mGuiHeaderTree.gui(currentEditorLineNumber); 39 | if (selectedLine >= 0) 40 | mEditor.SetCursorPosition({selectedLine, 0}, 3); 41 | } 42 | 43 | void ImGuiHeaderDocBrowser::guiGithubButton() 44 | { 45 | if (ImGui::SmallButton("View on github at this line")) 46 | { 47 | std::string url = ImGuiRepoUrl() + "imgui.h#L" 48 | + std::to_string(mEditor.GetCursorPosition().mLine + 1); 49 | HyperlinkHelper::OpenUrl(url); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tools/gitpod/index.build_emscripten.html: -------------------------------------------------------------------------------- 1 | 2 |

Welcome to the interactive playground.

3 |
4 | This interactive playground uses emscripten: all the C++ code is compiled into javascript, so that you 5 | can test everything from the browser. 6 |

7 | At the bottom, of the window, you will see 3 terminals 8 | 9 |
    10 |
  • A terminal named "http.server": it runs a web server on the port 8000. 11 | The text you are reading (i.e this text) is located on a page of this web server. 12 |
  • A terminal name "build": the code should have already started to build automatically in this terminal. 13 |
  • A terminal named "main": use it to run any command 14 |
15 | 16 | Wait until the build is finished (the first build takes about 1 minute) and you will then be able to run: 17 | 18 | 24 | 25 |

How to use the playground

26 | 27 | You can quickly add a new program by adding a new cpp file in the playground/ folder. 28 | For example: 29 |
30 |     cp playground/button.cpp playground/my_test.cpp
31 | 
32 | 33 | Then edit this file and add any code you wish. 34 | After adding a new cpp file, just run cmake, so that your new file gets discovered: 35 |
36 |     cmake .
37 | 
38 | 39 | And then compile it: 40 |
41 |     make
42 | 
43 | 44 | After the build, your program should be visible in the build playground folder 45 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(ImGuiManual LANGUAGES C CXX) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | option(IMGUI_MANUAL_BUILD_TESTS "Build tests" OFF) 6 | option(IMGUI_MANUAL_CAN_WRITE_IMGUI_DEMO_CPP "Allow writing to imgui_demo.cpp" OFF) 7 | 8 | # Provide our own fork of imgui, disable the one provided by hello_imgui 9 | set (HELLOIMGUI_BUILD_IMGUI OFF CACHE BOOL "" FORCE) 10 | set(HELLOIMGUI_MACOS_NO_BUNDLE ON CACHE BOOL "Don't create a bundle on macOS") 11 | set(imgui_dir ${CMAKE_CURRENT_LIST_DIR}/external/imgui) 12 | add_library(imgui 13 | ${imgui_dir}/imgui_demo.cpp 14 | ${imgui_dir}/imgui_draw.cpp 15 | ${imgui_dir}/imgui_tables.cpp 16 | ${imgui_dir}/imgui_widgets.cpp 17 | ${imgui_dir}/imgui_internal.h 18 | ${imgui_dir}/imgui.cpp 19 | ${imgui_dir}/imgui.h 20 | ${imgui_dir}/imgui_demo_marker_hooks.cpp 21 | ${imgui_dir}/imgui_demo_marker_hooks.h 22 | ${imgui_dir}/misc/freetype/imgui_freetype.cpp 23 | ${imgui_dir}/misc/freetype/imgui_freetype.h 24 | ) 25 | target_include_directories(imgui PUBLIC ${imgui_dir} ${imgui_dir}/misc/freetype) 26 | target_include_directories(imgui PUBLIC ${imgui_dir} ${imgui_dir}/misc/cpp) 27 | target_include_directories(imgui PUBLIC ${imgui_dir} ${imgui_dir}/backends) 28 | 29 | if (IMGUI_MANUAL_BUILD_TESTS) 30 | enable_testing() 31 | endif() 32 | 33 | # We use OpenGL3 + GLFW3 for desktop *and* emscripten 34 | # (using pongasoft/emscripten-glfw, which has a good support for the clipboard) 35 | set(HELLOIMGUI_USE_GLFW3 ON CACHE BOOL "" FORCE) 36 | set(HELLOIMGUI_HAS_OPENGL3 ON CACHE BOOL "" FORCE) 37 | 38 | set(HELLOIMGUI_IMGUI_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/external/imgui CACHE STRING "" FORCE) 39 | add_subdirectory(external/hello_imgui) 40 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${HELLOIMGUI_BASEPATH}/hello_imgui_cmake) 41 | if (EMSCRIPTEN) 42 | include(${HELLOIMGUI_BASEPATH}/hello_imgui_cmake/emscripten/hello_imgui_emscripten_global_options.cmake) 43 | endif() 44 | add_subdirectory(src) 45 | -------------------------------------------------------------------------------- /src/source_parse/GuiHeaderTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "source_parse/HeaderTree.h" 3 | #include "imgui.h" 4 | 5 | extern bool GDemoMarker_FlagFollowSource; 6 | 7 | namespace SourceParse 8 | { 9 | 10 | class GuiHeaderTree 11 | { 12 | public: 13 | GuiHeaderTree(const LinesWithTags & linesWithTags); 14 | 15 | // Show a tree gui with all the tags 16 | // return a line number if the user selected a tag, returns -1 otherwise 17 | virtual int gui(int currentEditorLineNumber); 18 | 19 | void setShowToc(bool v) { mShowToc = v; } 20 | 21 | protected: 22 | int guiImpl(int currentEditorLineNumber, const HeaderTree& headerTree, bool isRootNode); 23 | void applyTocFilter(); 24 | void showExpandCollapseButtons(); 25 | void showCommandLine(); 26 | 27 | HeaderTree mHeaderTree; 28 | HeaderTree mFilteredHeaderTree; 29 | ImGuiTextFilter mFilter; 30 | bool mShowToc = true; 31 | bool mScrollToSelectedNextTime = false; // only valid for "follow" mode (in subclass) 32 | 33 | enum class ExpandCollapseAction 34 | { 35 | ExpandAll, 36 | CollapseAll, 37 | NoAction 38 | }; 39 | ExpandCollapseAction mExpandCollapseAction = ExpandCollapseAction::CollapseAll; 40 | }; 41 | 42 | 43 | class GuiHeaderTree_FollowDemo: public GuiHeaderTree 44 | { 45 | public: 46 | GuiHeaderTree_FollowDemo(const LinesWithTags & linesWithTags) : GuiHeaderTree(linesWithTags) { 47 | GDemoMarker_FlagFollowSource = true; 48 | } 49 | 50 | int gui(int currentEditorLineNumber) override 51 | { 52 | ImGui::SameLine(); 53 | return GuiHeaderTree::gui(GDemoMarker_FlagFollowSource ? mCurrentFollowedLine : currentEditorLineNumber); 54 | } 55 | 56 | void followShowTocElementForLine(int sourceLineNumber) 57 | { 58 | mCurrentFollowedLine = sourceLineNumber; 59 | mScrollToSelectedNextTime = true; 60 | } 61 | 62 | private: 63 | int mCurrentFollowedLine = -1; 64 | }; 65 | 66 | 67 | } // namespace SourceParse 68 | 69 | 70 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated from CLion C/C++ Code Style settings 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignOperands: true 7 | AllowAllArgumentsOnNextLine: false 8 | AllowAllConstructorInitializersOnNextLine: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: Always 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: Always 14 | AllowShortLambdasOnASingleLine: All 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakTemplateDeclarations: Yes 18 | BreakBeforeBraces: Custom 19 | BraceWrapping: 20 | AfterCaseLabel: false 21 | AfterClass: false 22 | AfterControlStatement: Never 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterUnion: false 27 | BeforeCatch: false 28 | BeforeElse: false 29 | IndentBraces: false 30 | SplitEmptyFunction: false 31 | SplitEmptyRecord: true 32 | BreakBeforeBinaryOperators: None 33 | BreakBeforeTernaryOperators: true 34 | BreakConstructorInitializers: BeforeColon 35 | BreakInheritanceList: BeforeColon 36 | ColumnLimit: 0 37 | CompactNamespaces: false 38 | ContinuationIndentWidth: 4 39 | IndentCaseLabels: true 40 | IndentPPDirectives: None 41 | IndentWidth: 4 42 | KeepEmptyLinesAtTheStartOfBlocks: true 43 | MaxEmptyLinesToKeep: 2 44 | NamespaceIndentation: All 45 | ObjCSpaceAfterProperty: false 46 | ObjCSpaceBeforeProtocolList: true 47 | PointerAlignment: Right 48 | ReflowComments: false 49 | SpaceAfterCStyleCast: true 50 | SpaceAfterLogicalNot: false 51 | SpaceAfterTemplateKeyword: false 52 | SpaceBeforeAssignmentOperators: true 53 | SpaceBeforeCpp11BracedList: false 54 | SpaceBeforeCtorInitializerColon: true 55 | SpaceBeforeInheritanceColon: true 56 | SpaceBeforeParens: ControlStatements 57 | SpaceBeforeRangeBasedForLoopColon: true 58 | SpaceInEmptyParentheses: false 59 | SpacesBeforeTrailingComments: 0 60 | SpacesInAngles: false 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInContainerLiterals: false 63 | SpacesInParentheses: false 64 | SpacesInSquareBrackets: false 65 | TabWidth: 4 66 | UseTab: Never 67 | -------------------------------------------------------------------------------- /src/imgui_utilities/MarkdownHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "hello_imgui/hello_imgui.h" 2 | #include "hello_imgui/icons_font_awesome_4.h" 3 | 4 | #include "imgui_markdown.h" // https://github.com/juliettef/imgui_markdown 5 | #include "MarkdownHelper.h" 6 | #include "HyperlinkHelper.h" 7 | #include 8 | 9 | namespace MarkdownHelper 10 | { 11 | 12 | ImFont *fontH1, *fontH2, *fontH3; 13 | 14 | void LoadFonts() 15 | { 16 | std::string fontFilename = "fonts/DroidSans.ttf"; 17 | float fontSizeStep = 4.; 18 | fontH3 = HelloImGui::LoadFontTTF_WithFontAwesomeIcons(fontFilename, 14.f + fontSizeStep * 1.f); 19 | fontH2 = HelloImGui::LoadFontTTF_WithFontAwesomeIcons(fontFilename, 14.f + fontSizeStep * 2.f); 20 | fontH1 = HelloImGui::LoadFontTTF_WithFontAwesomeIcons(fontFilename, 14.f + fontSizeStep * 3.f); 21 | } 22 | 23 | 24 | void LinkCallback(ImGui::MarkdownLinkCallbackData data_) 25 | { 26 | (void) data_; 27 | std::string url(data_.link, data_.linkLength); 28 | if (!data_.isImage) 29 | HyperlinkHelper::OpenUrl(url); 30 | } 31 | 32 | inline ImGui::MarkdownImageData ImageCallback(ImGui::MarkdownLinkCallbackData data_) 33 | { 34 | (void)data_; 35 | // In your application you would load an image based on data_ input. Here we just use the imgui font texture. 36 | ImTextureID image = -1; // ImGui::GetIO().Fonts->TexID; 37 | ImGui::MarkdownImageData imageData{true, false, image, ImVec2(40.0f, 20.0f)}; 38 | return imageData; 39 | } 40 | 41 | // You can make your own Markdown function with your prefered string container and markdown config. 42 | ImGui::MarkdownConfig factorMarkdownConfig() 43 | { 44 | return { 45 | LinkCallback, 46 | ImageCallback, 47 | ICON_FA_LINK, 48 | { 49 | {fontH1, true}, 50 | {fontH2, true}, 51 | {fontH3, false} 52 | } 53 | }; 54 | } 55 | 56 | 57 | void Markdown(const std::string &markdown_) 58 | { 59 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 1.f, 1.f)); // Hack MarkDown links color, which use ImGuiCol_ButtonHovered 60 | static ImGui::MarkdownConfig markdownConfig = factorMarkdownConfig(); 61 | ImGui::Markdown(markdown_.c_str(), markdown_.length(), markdownConfig); 62 | ImGui::PopStyleColor(); 63 | } 64 | 65 | 66 | } // namespace MarkdownHelper 67 | -------------------------------------------------------------------------------- /src/shell.emscripten.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ImGui Interactive Manual 7 | 8 | 9 | 11 | 33 | 34 | 35 | 36 | 66 | {{{ SCRIPT }}} 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/source_parse/tests/Tree_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest.h" 3 | 4 | #include "source_parse/Tree.h" 5 | #include 6 | #include 7 | 8 | using namespace SourceParse; 9 | using namespace std::literals; 10 | 11 | struct StringAndBool 12 | { 13 | std::string s; 14 | bool b; 15 | }; 16 | 17 | std::ostream& operator<<(std::ostream &os, const StringAndBool &v) 18 | { 19 | os << "{" << v.s << " : " << v.b << "}"; 20 | return os; 21 | } 22 | 23 | TEST_CASE("tree_transform_values") 24 | { 25 | Tree treeString = tree_from_string( 26 | fplus::trim_whitespace(R"( 27 | Top 28 | A1 29 | A2 30 | A2i 31 | A3i 32 | B 33 | C 34 | )"s)); 35 | // std::cout << tree_show(treeString); 36 | auto toStringAndBool = [](const std::string &s) -> StringAndBool { 37 | return StringAndBool {s, true}; 38 | }; 39 | auto treeStringAndBool = tree_transform_values(toStringAndBool, treeString); 40 | treeStringAndBool.value_.b = false; 41 | std::string expected = R"( 42 | {Top : 0} 43 | {A1 : 1} 44 | {A2 : 1} 45 | {A2i : 1} 46 | {A3i : 1} 47 | {B : 1} 48 | {C : 1} 49 | )"; 50 | // std::cout << tree_show(treeStringAndBool) << "\n"; 51 | CHECK("\n"s + tree_show(treeStringAndBool) + "\n"s == expected ); 52 | } 53 | 54 | TEST_CASE("tree_keepIfAnyAncesterMatches") 55 | { 56 | Tree treeString = tree_from_string( 57 | fplus::trim_whitespace(R"( 58 | Top 59 | A1 60 | A2 61 | A2i 62 | A3i 63 | B 64 | C 65 | )"s)); 66 | 67 | auto containsA2 = [](const std::string &s) -> bool { 68 | return fplus::is_infix_of("A2"s, s); 69 | }; 70 | 71 | Tree treeFiltered = tree_keep_wholebranch_if(containsA2, treeString); 72 | // std::cout << tree_show(treeFiltered) << "\n"; 73 | std::string expected = R"( 74 | Top 75 | A2 76 | A2i 77 | A3i 78 | )"; 79 | CHECK("\n"s + tree_show(treeFiltered) + "\n"s == expected ); 80 | } 81 | 82 | 83 | TEST_CASE("tree_from_string") 84 | { 85 | std::string treeAsString = R"( 86 | Top 87 | A 88 | A1 89 | A1i 90 | A1ii 91 | B 92 | B1 93 | B2 94 | B3 95 | C 96 | D 97 | )"; 98 | auto tree = tree_from_string(treeAsString); 99 | // std::cout << tree_show(tree) << "\n"; 100 | std::string treeAsString2 = "\n"s + tree_show(tree) + "\n"; 101 | CHECK(treeAsString == treeAsString2); 102 | } -------------------------------------------------------------------------------- /src/imgui_utilities/ImGuiExt.cpp: -------------------------------------------------------------------------------- 1 | #include "ImGuiExt.h" 2 | #include "imgui.h" 3 | #include "hello_imgui/hello_imgui.h" 4 | #include "HyperlinkHelper.h" 5 | #include "MarkdownHelper.h" 6 | #include 7 | 8 | namespace ImGuiExt 9 | { 10 | bool ClickableText(const std::string &txt) 11 | { 12 | auto blue = ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]; 13 | ImGui::TextColored(blue, "%s", txt.c_str()); 14 | if (ImGui::IsItemHovered() || ImGui::IsItemFocused()) 15 | ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 16 | return (ImGui::IsItemHovered() && ImGui::IsMouseDown(0)); 17 | } 18 | 19 | void SameLine_IfPossible(float minRightMargin) 20 | { 21 | auto lastXPos = ImGui::GetItemRectMax().x - ImGui::GetWindowPos().x; 22 | auto windowWidth = ImGui::GetWindowSize().x; 23 | if (lastXPos < windowWidth - minRightMargin ) 24 | ImGui::SameLine(); 25 | } 26 | 27 | bool Button_WithEnabledFlag(const char *label, bool enabled, const char *tooltip, bool sameLineAfter) 28 | { 29 | if (!enabled) 30 | { 31 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyle().Colors[ImGuiCol_Button]); 32 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyle().Colors[ImGuiCol_Button]); 33 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 34 | } 35 | bool clicked = ImGui::Button(label); 36 | if ((strlen(tooltip) > 0) && ImGui::IsItemHovered()) 37 | ImGui::SetTooltip("%s", tooltip); 38 | if (!enabled) 39 | ImGui::PopStyleColor(3); 40 | if (sameLineAfter) 41 | ImGui::SameLine(); 42 | return enabled ? clicked : false; 43 | } 44 | 45 | bool SmallButton_WithEnabledFlag(const char *label, bool enabled, const char *tooltip, bool sameLineAfter) 46 | { 47 | if (!enabled) 48 | { 49 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyle().Colors[ImGuiCol_Button]); 50 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyle().Colors[ImGuiCol_Button]); 51 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 52 | } 53 | bool clicked = ImGui::SmallButton(label); 54 | if ((strlen(tooltip) > 0) && ImGui::IsItemHovered()) 55 | ImGui::SetTooltip("%s", tooltip); 56 | if (!enabled) 57 | ImGui::PopStyleColor(3); 58 | if (sameLineAfter) 59 | ImGui::SameLine(); 60 | return enabled ? clicked : false; 61 | } 62 | } -------------------------------------------------------------------------------- /src/source_parse/HeaderTree.cpp: -------------------------------------------------------------------------------- 1 | #include "HeaderTree.h" 2 | 3 | namespace SourceParse 4 | { 5 | 6 | HeaderTree * searchParent( 7 | HeaderTree* treeRoot, 8 | HeaderTree* currentNode, 9 | int siblingHeaderLevel) 10 | { 11 | HeaderTree *possibleSibling = currentNode; 12 | while (possibleSibling->value_.level > siblingHeaderLevel) 13 | { 14 | auto parent = tree_find_parent(*treeRoot, *possibleSibling); 15 | if (parent != nullptr) 16 | possibleSibling = parent; 17 | else 18 | break; 19 | } 20 | assert(possibleSibling != nullptr); 21 | 22 | HeaderTree *parent = tree_find_parent(*treeRoot, *possibleSibling); 23 | if (possibleSibling->value_.level < siblingHeaderLevel) 24 | parent = possibleSibling; 25 | 26 | return parent; 27 | } 28 | 29 | void makeHeaderTrees_Impl( 30 | std::deque& headerLinesStream, 31 | HeaderTree* treeRoot, 32 | HeaderTree* outHeaderTreeParent 33 | ) 34 | { 35 | if (headerLinesStream.empty()) 36 | return; 37 | 38 | LineWithTag nextHeader = headerLinesStream.front(); 39 | headerLinesStream.pop_front(); 40 | 41 | HeaderTree *parent = nullptr; 42 | // Search for correct parent 43 | { 44 | if (nextHeader.level > outHeaderTreeParent->value_.level) 45 | { 46 | // Append child 47 | parent = outHeaderTreeParent; 48 | } 49 | else if (nextHeader.level == outHeaderTreeParent->value_.level) 50 | { 51 | // Create a new sibling 52 | parent = tree_find_parent(*treeRoot, *outHeaderTreeParent); // Arg, bad perf 53 | } 54 | else // if (nextHeader.level < outHeaderTreeParent.value_.level) 55 | { 56 | // Search for parent by going up in the parents 57 | parent = searchParent(treeRoot, outHeaderTreeParent, nextHeader.level); 58 | } 59 | } 60 | 61 | assert(parent != nullptr); 62 | HeaderTree newTree({nextHeader, {}}); 63 | parent->children_.push_back(newTree); 64 | HeaderTree* newTreeRef = &(parent->children_.back()); 65 | makeHeaderTrees_Impl(headerLinesStream, treeRoot, newTreeRef); 66 | } 67 | 68 | HeaderTree makeHeaderTree(const LinesWithTags& linesWithTags, const LineWithTag& treeTopLeaf) 69 | { 70 | std::deque headerLinesStream; 71 | for (const auto& v: linesWithTags) 72 | headerLinesStream.push_back(v); 73 | 74 | HeaderTree treeTop {treeTopLeaf, {}}; 75 | makeHeaderTrees_Impl(headerLinesStream, &treeTop, &treeTop); 76 | 77 | return treeTop; 78 | } 79 | 80 | } // namespace SourceParse 81 | -------------------------------------------------------------------------------- /src/LibrariesCodeBrowser.cpp: -------------------------------------------------------------------------------- 1 | #include "LibrariesCodeBrowser.h" 2 | #include "imgui_utilities/MarkdownHelper.h" 3 | #include "imgui_utilities/ImGuiExt.h" 4 | #include "hello_imgui/hello_imgui.h" 5 | #include 6 | 7 | namespace 8 | { 9 | std::string MarkdownLinkString(const std::string &url) 10 | { 11 | return std::string("[") + url + "]" + "(" + url + ")"; 12 | } 13 | } 14 | 15 | 16 | LibrariesCodeBrowser::LibrariesCodeBrowser( 17 | const std::string & windowName, 18 | const std::vector &librarySources, 19 | std::string currentSourcePath) 20 | : WindowWithEditor(windowName) 21 | , mLibraries(librarySources) 22 | { 23 | if (!currentSourcePath.empty()) 24 | mCurrentSource = SourceParse::ReadSource(currentSourcePath); 25 | mEditor.SetText(mCurrentSource.sourceCode); 26 | } 27 | 28 | void LibrariesCodeBrowser::gui() 29 | { 30 | if (guiSelectLibrarySource()) 31 | mEditor.SetText(mCurrentSource.sourceCode); 32 | 33 | std::string sourcePath = mCurrentSource.sourcePath; 34 | if (fplus::is_suffix_of(std::string(".md"), sourcePath)) 35 | MarkdownHelper::Markdown(mCurrentSource.sourceCode); 36 | else if (fplus::is_suffix_of(std::string(".png"), sourcePath)) 37 | { 38 | HelloImGui::ImageFromAsset(sourcePath.c_str(), ImVec2(ImGui::GetWindowSize().x - 30.f, 0.f)); 39 | } 40 | else 41 | RenderEditor(mCurrentSource.sourcePath.c_str()); 42 | } 43 | 44 | bool LibrariesCodeBrowser::guiSelectLibrarySource() 45 | { 46 | bool changed = false; 47 | for (const auto & librarySource: mLibraries) 48 | { 49 | ImGui::Text("%s", librarySource.name.c_str()); 50 | ImGui::SameLine(150.f); 51 | MarkdownHelper::Markdown(MarkdownLinkString(librarySource.url)); 52 | MarkdownHelper::Markdown(librarySource.shortDoc); 53 | for (const auto & source: librarySource.sourcePaths) 54 | { 55 | std::string currentSourcePath = librarySource.path + "/" + source; 56 | bool isSelected = (currentSourcePath == mCurrentSource.sourcePath); 57 | std::string buttonLabel = source + "##" + librarySource.path; 58 | if (isSelected) 59 | ImGui::TextDisabled("%s", source.c_str()); 60 | else if (ImGui::Button(buttonLabel.c_str())) 61 | { 62 | mCurrentSource = SourceParse::ReadSource(currentSourcePath); 63 | changed = true; 64 | } 65 | ImGuiExt::SameLine_IfPossible(150.f); 66 | } 67 | ImGui::NewLine(); 68 | ImGui::Separator(); 69 | } 70 | return changed; 71 | } 72 | -------------------------------------------------------------------------------- /src/JsClipboardTricks.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui.h" 2 | #include "imgui_internal.h" 3 | #include "JsClipboardTricks.h" 4 | #ifdef __EMSCRIPTEN__ 5 | #include 6 | 7 | // The clipboard handling features take inspiration from sokol 8 | // https://github.com/floooh/sokol 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | 15 | #ifdef IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER 16 | 17 | EM_JS(void, sapp_add_js_hook_clipboard, (void), { 18 | // See also https://whatwebcando.today/clipboard.html 19 | // for the new async api with user permissions dialog 20 | Module.sokol_paste = function(event) { 21 | // console.log("Got paste event "); 22 | var pasted_str = event.clipboardData.getData('text'); 23 | ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); 24 | }; 25 | // console.log("sapp_add_js_hook_clipboard 4"); 26 | window.addEventListener('paste', Module.sokol_paste); 27 | }); 28 | 29 | EM_JS(void, sapp_remove_js_hook_clipboard, (void), { 30 | window.removeEventListener('paste', Module.sokol_paste); 31 | }); 32 | 33 | void EMSCRIPTEN_KEEPALIVE _sapp_emsc_onpaste(const char *str) 34 | { 35 | // std::cout << "_sapp_emsc_onpaste " << str << std::endl; 36 | ImGui::SetClipboardText(str); 37 | } 38 | 39 | #endif // #ifdef IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER 40 | 41 | 42 | #ifdef IMGUIMANUAL_CLIPBOARD_EXPORT_TO_BROWSER 43 | 44 | EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { 45 | var str = UTF8ToString(c_str); 46 | var ta = document.createElement('textarea'); 47 | ta.setAttribute('autocomplete', 'off'); 48 | ta.setAttribute('autocorrect', 'off'); 49 | ta.setAttribute('autocapitalize', 'off'); 50 | ta.setAttribute('spellcheck', 'false'); 51 | ta.style.left = -100 + 'px'; 52 | ta.style.top = -100 + 'px'; 53 | ta.style.height = 1; 54 | ta.style.width = 1; 55 | ta.value = str; 56 | document.body.appendChild(ta); 57 | ta.select(); 58 | document.execCommand('copy'); 59 | document.body.removeChild(ta); 60 | //console.log("Set clipboard to " + str); 61 | }); 62 | 63 | #endif // #ifdef IMGUIMANUAL_CLIPBOARD_EXPORT_TO_BROWSER 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #ifdef IMGUIMANUAL_CLIPBOARD_EXPORT_TO_BROWSER 70 | void _Emscripten_SetClipboardTextFn(ImGuiContext* ctx, const char* text) 71 | { 72 | (void)ctx; 73 | sapp_js_write_clipboard(text); 74 | } 75 | 76 | void JsClipboard_InstallPlaformSetClipboardText() 77 | { 78 | auto &g = *ImGui::GetCurrentContext(); 79 | g.PlatformIO.Platform_SetClipboardTextFn = _Emscripten_SetClipboardTextFn; 80 | } 81 | 82 | #endif 83 | 84 | #ifdef IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER 85 | void JsClipboard_AddJsHook() 86 | { 87 | sapp_add_js_hook_clipboard(); 88 | } 89 | #endif 90 | 91 | #endif // __EMSCRIPTEN__ 92 | -------------------------------------------------------------------------------- /src/diagram.md: -------------------------------------------------------------------------------- 1 | View this with [markdown preview enhanced](https://shd101wyy.github.io/markdown-preview-enhanced/#/) or refer to diagram.png 2 | 3 | ````puml 4 | @startuml 5 | namespace ExternalLibs { 6 | class ImGuiColorTextEdit 7 | class Markdown 8 | } 9 | 10 | 11 | class WindowWithEditor { 12 | --- 13 | + RenderEditor() 14 | --- 15 | Members 16 | - TextEditor mEditor 17 | } 18 | WindowWithEditor *-- ExternalLibs.ImGuiColorTextEdit 19 | WindowWithEditor *-- ExternalLibs.Markdown 20 | 21 | class LibrariesCodeBrowser { 22 | + gui() 23 | - bool guiSelectLibrarySource() 24 | 25 | - vector mLibraries 26 | - mCurrentSource 27 | } 28 | 29 | LibrariesCodeBrowser --|> WindowWithEditor 30 | LibrariesCodeBrowser *-- SourceParse.Library: vector 31 | LibrariesCodeBrowser *-- SourceParse.Source 32 | 33 | class ImGuiCodeBrowser { 34 | + ImGuiCodeBrowser() 35 | + gui() 36 | 37 | - mLibrariesCodeBrowser 38 | } 39 | ImGuiCodeBrowser *-- LibrariesCodeBrowser 40 | 41 | class ImGuiCppDocBrowser { 42 | + gui() 43 | - mAnnotatedSource 44 | } 45 | ImGuiCppDocBrowser --|> WindowWithEditor 46 | ImGuiCppDocBrowser *-- SourceParse.AnnotatedSource 47 | 48 | 49 | class ImGuiDemoBrowser { 50 | + gui() 51 | 52 | - mAnnotatedSource 53 | } 54 | 55 | ImGuiDemoBrowser --|> WindowWithEditor 56 | ImGuiDemoBrowser *-- SourceParse.AnnotatedSource 57 | 58 | class Acknowledgments { 59 | + gui() 60 | - mLibrariesCodeBrowser 61 | } 62 | 63 | Acknowledgments *-- LibrariesCodeBrowser 64 | 65 | 66 | 67 | class AboutWindow { 68 | + gui() 69 | - mLibrariesCodeBrowser 70 | } 71 | 72 | AboutWindow *-- LibrariesCodeBrowser 73 | 74 | 75 | 76 | namespace SourceParse { 77 | class Source { 78 | sourcePath 79 | sourceCode 80 | } 81 | class LineWithTag { 82 | lineNumber 83 | tag 84 | } 85 | 86 | class AnnotatedSource { 87 | Source source 88 | LinesWithTags linesWithTags 89 | } 90 | AnnotatedSource *-- LineWithTag: vector 91 | AnnotatedSource *-- Source 92 | 93 | class Library { 94 | path 95 | name 96 | url 97 | shortDoc // markdown 98 | vector sourcePaths 99 | } 100 | 101 | } 102 | 103 | class ImGuiManual
{ 104 | - imGuiDemoBrowser 105 | - imGuiCppDocBrowser 106 | - imGuiCodeBrowser 107 | - acknowledgments 108 | - aboutWindow 109 | } 110 | ImGuiManual *-- ImGuiDemoBrowser 111 | ImGuiManual *-- ImGuiCppDocBrowser 112 | ImGuiManual *-- ImGuiCodeBrowser 113 | ImGuiManual *-- ImGuiDemoBrowser 114 | ImGuiManual *-- Acknowledgments 115 | ImGuiManual *-- AboutWindow 116 | ImGuiManual *-- HelloImGui.RunnerParams 117 | 118 | namespace HelloImGui { 119 | class RunnerParams 120 | RunnerParams *-- RunnerCallbacks 121 | RunnerParams *-- DockingParams 122 | RunnerParams *-- WindowParams 123 | } 124 | 125 | @enduml 126 | ```` 127 | 128 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Dear ImGui: an interactive manual 2 | 3 | Source for an online interactive manual for [Dear ImGui](https://github.com/ocornut/imgui). 4 | 5 | Click on the image below to open it: 6 | 7 | [![ImGui Manual](doc/images/link_manual.png)](https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html) 8 | 9 | See some demo videos with more explanations at https://pthom.github.io/imgui_manual_online. 10 | 11 | --- 12 | This interactive manual was developed using [Hello ImGui](https://github.com/pthom/hello_imgui), which provided the emscripten port, as well as the assets embedding and image loading. [ImGuiManual.cpp](src/ImGuiManual.cpp) gives a good overview of [Hello Imgui API](https://github.com/pthom/hello_imgui/blob/master/src/hello_imgui/hello_imgui_api.md). 13 | 14 | See also a [related demo for Implot](https://traineq.org/implot_demo/src/implot_demo.html), which also provides code navigation. 15 | 16 | [I'd love to read your feedback!](https://github.com/pthom/imgui_manual/issues/1). 17 | 18 | 19 | # Test ImGui from your browser: no installation required! 20 | 21 | This repository also enables you to get a feel of how easy it is to write ImGui applications by allowing you to write and run your own code in 3 minutes, *without even downloading or installing anything* : it runs on a dedicated cloud server on gitpod.io (which is free to use), so that you do not even need a compiler. 22 | 23 | For example, you could write and run this code online: 24 | 25 | ````cpp 26 | #include "playground.h" 27 | void Playground() { 28 | static int counter = 0; 29 | if (ImGui::Button("Click me")) 30 | ++counter; 31 | ImGui::Text("counter=%i", counter); 32 | } 33 | ```` 34 | 35 | Just click on the link below: 36 | 37 | [Open this repo in gitpod.io](https://gitpod.io/#https://github.com/pthom/imgui_manual) 38 | 39 | More info: [demo on youtube](https://www.youtube.com/watch?v=FJgObNNmuzo&feature=youtu.be) 40 | 41 | 42 | # Build instructions 43 | 44 | First, init the submodules: 45 | ```` 46 | git submodule update --init --recursive 47 | ```` 48 | 49 | 50 | ### Build instructions for emscripten 51 | 52 | Install emscripten if not present 53 | 54 | ```` 55 | ./external/hello_imgui/tools/emscripten/install_emscripten.sh 56 | ```` 57 | 58 | Build for emscripten using [tools/emscripten_build.sh](tools/emscripten_build.sh) 59 | ```` 60 | ./tools/emscripten_build.sh 61 | ```` 62 | 63 | Launch a web server 64 | ```` 65 | python3 -m http.server 66 | ```` 67 | 68 | Then, browse to http://localhost:8000/src/imgui_manual.html 69 | 70 | ### Build instructions on desktop (linux, MacOS, Windows) 71 | 72 | Init the submodules: 73 | ```` 74 | git submodule update --init --recursive 75 | ```` 76 | 77 | Install third parties via vcpkg (SDL) 78 | ```` 79 | python external/hello_imgui/tools/vcpkg_install_third_parties.py 80 | ```` 81 | 82 | Run cmake, using vcpkg toolchain 83 | ```` 84 | mkdir build 85 | cd build 86 | cmake .. -DCMAKE_TOOLCHAIN_FILE=../external/hello_imgui/vcpkg/scripts/buildsystems/vcpkg.cmake 87 | ```` 88 | 89 | Build and run 90 | ```` 91 | make -j 4 92 | ./src/imgui_manual 93 | ```` 94 | 95 | --- 96 | 97 | _ETFM! (Enjoy The Fine Manual!)_ 98 | -------------------------------------------------------------------------------- /src/source_parse/tests/HeaderTree_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest.h" 3 | 4 | #include "source_parse/HeaderTree.h" 5 | 6 | 7 | TEST_CASE("makeHeaderTrees") 8 | { 9 | SourceParse::LinesWithTags linesWithTags = { 10 | // { lineNumber, tag, headerLevel } 11 | { 0, "A", 1 }, 12 | { 10, "A-1", 2 }, 13 | { 20, "A-1-i", 3 }, 14 | { 30, "A-2", 2 }, 15 | { 40, "A-2-i", 3 }, 16 | { 50, "A-2-ii", 3 }, 17 | { 60, "B", 1 }, 18 | { 70, "B-1", 2 }, 19 | { 75, "B-1-i", 3 }, 20 | { 80, "B-2", 2 }, 21 | { 90, "B-3", 2 }, 22 | { 100, "C", 1 }, 23 | { 110, "C-i", 3 }, 24 | { 120, "D", 1 }, 25 | }; 26 | 27 | std::string expected =R"( 28 | {line:-1, TOC, level:-1} 29 | {line:0, A, level:1} 30 | {line:10, A-1, level:2} 31 | {line:20, A-1-i, level:3} 32 | {line:30, A-2, level:2} 33 | {line:40, A-2-i, level:3} 34 | {line:50, A-2-ii, level:3} 35 | {line:60, B, level:1} 36 | {line:70, B-1, level:2} 37 | {line:75, B-1-i, level:3} 38 | {line:80, B-2, level:2} 39 | {line:90, B-3, level:2} 40 | {line:100, C, level:1} 41 | {line:110, C-i, level:3} 42 | {line:120, D, level:1} 43 | )"; 44 | 45 | auto sourceHeadersTree = SourceParse::makeHeaderTree(linesWithTags); 46 | auto computed = std::string("\n") + tree_show(sourceHeadersTree) + std::string("\n"); 47 | //std::cout << computed; 48 | CHECK(computed == expected); 49 | } 50 | 51 | TEST_CASE("sortHeaderTrees") 52 | { 53 | SourceParse::LinesWithTags linesWithTags = { 54 | // { lineNumber, tag, headerLevel } 55 | { 0, "A", 1 }, 56 | { 10, "A-1", 2 }, 57 | { 20, "A-2", 2 }, 58 | { 21, "A-2-i", 3 }, 59 | { 22, "A-2-ii", 3 }, 60 | { 23, "A-2-iii", 3 }, 61 | { 30, "B", 1 }, 62 | { 40, "B-1", 2 }, 63 | { 50, "B-2", 2 }, 64 | }; 65 | 66 | std::string expected = R"( 67 | {line:-1, TOC, level:-1} 68 | {line:30, B, level:1} 69 | {line:50, B-2, level:2} 70 | {line:40, B-1, level:2} 71 | {line:0, A, level:1} 72 | {line:20, A-2, level:2} 73 | {line:23, A-2-iii, level:3} 74 | {line:22, A-2-ii, level:3} 75 | {line:21, A-2-i, level:3} 76 | {line:10, A-1, level:2} 77 | )"; 78 | 79 | auto sourceHeadersTrees = SourceParse::makeHeaderTree(linesWithTags); 80 | auto fnComparator = [](const SourceParse::LineWithTag& t1, const SourceParse::LineWithTag& t2) 81 | { 82 | return t2.lineNumber < t1.lineNumber; 83 | }; 84 | auto sourceHeadersTreesReversed = SourceParse::tree_sort(fnComparator, sourceHeadersTrees); 85 | auto computed = std::string("\n") + SourceParse::tree_show(sourceHeadersTreesReversed) + std::string("\n"); 86 | //std::cout << computed; 87 | CHECK(computed == expected); 88 | } 89 | 90 | 91 | TEST_CASE("badHierarchyTree") 92 | { 93 | SourceParse::LinesWithTags linesWithTags = { 94 | // { lineNumber, tag, headerLevel } 95 | { 0, "A", 1 }, 96 | { 20, "A-1-i", 3 }, // bad hierarchy, because level 3 is reached without level 2 97 | { 30, "A-2", 2 }, // and this level 2 now should becomes a sibling of previous level 3 98 | { 40, "A-2-i", 3 }, 99 | { 60, "B", 1 }, 100 | }; 101 | std::string expected = R"( 102 | {line:-1, TOC, level:-1} 103 | {line:0, A, level:1} 104 | {line:20, A-1-i, level:3} 105 | {line:30, A-2, level:2} 106 | {line:40, A-2-i, level:3} 107 | {line:60, B, level:1} 108 | )"; 109 | 110 | auto sourceHeadersTree = SourceParse::makeHeaderTree(linesWithTags); 111 | auto computed = std::string("\n") + tree_show(sourceHeadersTree) + std::string("\n"); 112 | //std::cout << computed; 113 | CHECK(computed == expected); 114 | } 115 | -------------------------------------------------------------------------------- /src/source_parse/Sources.cpp: -------------------------------------------------------------------------------- 1 | #include "hello_imgui/hello_imgui_assets.h" 2 | #include 3 | #include 4 | #include "Sources.h" 5 | 6 | using namespace std::literals; 7 | 8 | namespace { 9 | auto make_string_vec = [](const std::string &s) -> std::vector { 10 | std::vector r = fplus::split('\n', false, s); 11 | r = fplus::transform(fplus::trim_whitespace, r); 12 | r = fplus::keep_if([](auto s) { return !s.empty();}, r); 13 | return r; 14 | }; 15 | } 16 | 17 | namespace SourceParse 18 | { 19 | 20 | 21 | std::vector imguiLibrary() 22 | { 23 | return 24 | { 25 | { 26 | "imgui", "Dear ImGui", "https://github.com/ocornut/imgui", 27 | "Dear ImGui: Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies", 28 | make_string_vec(R"( 29 | README.md 30 | FAQ.md 31 | LICENSE.txt 32 | imgui_demo.cpp 33 | imgui.h 34 | imgui.cpp 35 | imconfig.h 36 | imgui_draw.cpp 37 | imgui_internal.h 38 | imgui_widgets.cpp 39 | imstb_rectpack.h 40 | imstb_textedit.h 41 | imstb_truetype.h 42 | )") 43 | } 44 | }; 45 | } 46 | 47 | std::vector otherLibraries() 48 | { 49 | return 50 | { 51 | { 52 | "ImGuiColorTextEdit", "ImGuiColorTextEdit", "https://github.com/BalazsJako/ImGuiColorTextEdit", 53 | "Syntax highlighting text editor for ImGui. Demo project: [https://github.com/BalazsJako/ColorTextEditorDemo](https://github.com/BalazsJako/ColorTextEditorDemo)", 54 | make_string_vec(R"( 55 | README.md 56 | LICENSE 57 | CONTRIBUTING 58 | TextEditor.h 59 | TextEditor.cpp 60 | )") 61 | }, 62 | { 63 | "imgui_markdown", "imgui_markdown", "https://github.com/juliettef/imgui_markdown", 64 | "Markdown for Dear ImGui. [Become a Patron](https://www.patreon.com/enkisoftware)", 65 | make_string_vec(R"( 66 | README.md 67 | License.txt 68 | imgui_markdown.h 69 | )") 70 | }, 71 | { 72 | "fplus", "FunctionalPlus", "https://github.com/Dobiasd/FunctionalPlus", 73 | "Functional Programming Library for C++. Write concise and readable C++ code. [API Browser](http://www.editgym.com/fplus-api-search/) - [Udemy course](https://www.udemy.com/course/functional-programming-using-cpp/) - [Browse the code](https://sourcegraph.com/github.com/Dobiasd/FunctionalPlus/-/tree/include/fplus). ", 74 | make_string_vec(R"( 75 | README.md 76 | LICENSE 77 | CONTRIBUTING.md 78 | INSTALL.md 79 | fplus.hpp 80 | )") 81 | }, 82 | }; 83 | } 84 | 85 | 86 | std::vector helloImGuiLibrary() 87 | { 88 | return 89 | { 90 | { 91 | "hello_imgui", "Hello ImGui", "https://github.com/pthom/hello_imgui", 92 | "Hello, Dear ImGui: cross-platform Gui apps for Windows / Mac / Linux / iOS / Android / Emscripten with the simplicity of a \"Hello World\" app", 93 | make_string_vec(R"( 94 | README.md 95 | LICENSE 96 | hello_imgui.h 97 | hello_imgui_api.md 98 | )") 99 | }, 100 | }; 101 | 102 | } 103 | 104 | 105 | std::vector imguiManualLibrary() 106 | { 107 | return 108 | { 109 | { 110 | "imgui_manual", "", "https://github.com/pthom/imgui_manual", 111 | "Dear ImGui Manual: an interactive manual for ImGui", 112 | make_string_vec(R"( 113 | Readme.md 114 | LICENSE 115 | ImGuiManual.cpp 116 | diagram.png 117 | diagram.md 118 | source_parse/Sources.h 119 | ImGuiDemoBrowser.h 120 | ImGuiDemoBrowser.cpp 121 | )") 122 | }, 123 | }; 124 | } 125 | 126 | std::vector acknowldegmentLibraries() 127 | { 128 | auto r = fplus::append(otherLibraries(), helloImGuiLibrary()); 129 | return r; 130 | } 131 | 132 | 133 | SourceFile ReadSource(const std::string sourcePath) 134 | { 135 | std::string assetPath = std::string("code/") + sourcePath; 136 | auto assetData = HelloImGui::LoadAssetFileData(assetPath.c_str()); 137 | assert(assetData.data != nullptr); 138 | 139 | SourceFile r; 140 | r.sourcePath = sourcePath; 141 | r.sourceCode = std::string((const char *) assetData.data); 142 | HelloImGui::FreeAssetFileData(&assetData); 143 | return r; 144 | } 145 | 146 | 147 | 148 | } // namespace SourceParse 149 | -------------------------------------------------------------------------------- /src/source_parse/GuiHeaderTree.cpp: -------------------------------------------------------------------------------- 1 | #include "hello_imgui/hello_imgui.h" 2 | #include "hello_imgui/icons_font_awesome_4.h" 3 | #include "GuiHeaderTree.h" 4 | 5 | namespace SourceParse 6 | { 7 | 8 | GuiHeaderTree::GuiHeaderTree(const LinesWithTags & linesWithTags) 9 | { 10 | mHeaderTree = makeHeaderTree(linesWithTags); 11 | mFilteredHeaderTree = mHeaderTree; 12 | } 13 | 14 | ImGuiTreeNodeFlags makeTreeNodeFlags(bool isLeafNode, bool isSelected) 15 | { 16 | ImGuiTreeNodeFlags treeNodeFlags; 17 | 18 | static ImGuiTreeNodeFlags baseTreeFlags = 19 | ImGuiTreeNodeFlags_OpenOnArrow 20 | | ImGuiTreeNodeFlags_OpenOnDoubleClick 21 | | ImGuiTreeNodeFlags_SpanAvailWidth; 22 | 23 | static ImGuiTreeNodeFlags leafFlags = baseTreeFlags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; 24 | treeNodeFlags = isLeafNode ? leafFlags : baseTreeFlags; 25 | 26 | if (isSelected) 27 | treeNodeFlags |= ImGuiTreeNodeFlags_Selected; 28 | 29 | return treeNodeFlags; 30 | } 31 | 32 | int GuiHeaderTree::guiImpl(int currentEditorLineNumber, const HeaderTree& headerTree, bool isRootNode) 33 | { 34 | using namespace std::literals; 35 | 36 | const auto &lineWithTag = headerTree.value_; 37 | int clickedLineNumber = -1; 38 | 39 | bool isLeafNode = headerTree.children_.empty(); 40 | bool isSelected = false; 41 | { 42 | int diffLine = currentEditorLineNumber - lineWithTag.lineNumber; 43 | if ((diffLine >= 0) && (diffLine < 5)) 44 | isSelected = true; 45 | } 46 | if (mScrollToSelectedNextTime && isSelected) 47 | { 48 | ImGui::SetScrollHereY(); 49 | mScrollToSelectedNextTime = false; 50 | } 51 | 52 | ImGuiTreeNodeFlags treeNodeFlags = makeTreeNodeFlags(isLeafNode, isSelected); 53 | 54 | std::string title = lineWithTag.tag 55 | + "##" + std::to_string(lineWithTag.lineNumber); 56 | 57 | if (mExpandCollapseAction == ExpandCollapseAction::CollapseAll) 58 | ImGui::SetNextItemOpen(false, ImGuiCond_Always); 59 | if (mExpandCollapseAction == ExpandCollapseAction::ExpandAll) 60 | ImGui::SetNextItemOpen(true, ImGuiCond_Always); 61 | 62 | bool isNodeOpen; 63 | if (isRootNode) 64 | isNodeOpen = true; 65 | else 66 | { 67 | isNodeOpen = ImGui::TreeNodeEx(title.c_str(), treeNodeFlags); 68 | if (ImGui::IsItemClicked()) 69 | clickedLineNumber = lineWithTag.lineNumber; 70 | } 71 | 72 | if (isNodeOpen && !isLeafNode) 73 | { 74 | auto showChildrenNodes = [this, &headerTree, currentEditorLineNumber]() 75 | { 76 | int clickedLineNumber_Child = -1; 77 | for(const auto& headerTreeChild: headerTree.children_) 78 | { 79 | int line = guiImpl(currentEditorLineNumber, headerTreeChild, false); 80 | if (line > 0) 81 | clickedLineNumber_Child = line; 82 | } 83 | return clickedLineNumber_Child; 84 | }; 85 | 86 | int selectedChildLine = showChildrenNodes(); 87 | if (selectedChildLine > 0) 88 | clickedLineNumber = selectedChildLine; 89 | 90 | if (!isRootNode) 91 | ImGui::TreePop(); 92 | } 93 | return clickedLineNumber; 94 | } 95 | 96 | bool DrawImGuiTextFilterWithTooltip( 97 | ImGuiTextFilter &imGuiTextFilter, 98 | const std::string &tooltipText = 99 | "Filter usage:[-excl],incl\n" 100 | "For example:\n" 101 | " \"button\" will search for \"button\"\n" 102 | " \"-widget,button\" will search for \"button\" without \"widget\"", 103 | const std::string& filterLabel = 104 | "Filter usage:[-excl],incl" 105 | ) 106 | { 107 | bool showTooltip = false; 108 | ImGui::TextDisabled("?"); ImGui::SameLine(); 109 | if (ImGui::IsItemHovered()) 110 | showTooltip = true; 111 | if (showTooltip) 112 | { 113 | ImGui::BeginTooltip(); 114 | ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 115 | ImGui::TextUnformatted(tooltipText.c_str()); 116 | ImGui::PopTextWrapPos(); 117 | ImGui::EndTooltip(); 118 | } 119 | ImGui::SetNextItemWidth(200.f); 120 | return imGuiTextFilter.Draw(filterLabel.c_str()); 121 | } 122 | 123 | 124 | // Show a tree gui with all the tags 125 | // return a line number if the user selected a tag, returns -1 otherwise 126 | int GuiHeaderTree::gui(int currentEditorLineNumber) 127 | { 128 | ImGui::Checkbox("Show Table Of Content", &mShowToc); 129 | if (!mShowToc) 130 | return -1; 131 | 132 | ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar; 133 | bool border = true; 134 | ImVec2 guiSize(0, ImGui::GetWindowHeight() / 4.f); 135 | ImGui::BeginChild("ChildR", guiSize, border, window_flags); 136 | ImGui::BeginMenuBar(); 137 | showCommandLine(); 138 | ImGui::EndMenuBar(); 139 | 140 | int lineNumber = guiImpl(currentEditorLineNumber, mFilteredHeaderTree, true); 141 | ImGui::EndChild(); 142 | 143 | mExpandCollapseAction = ExpandCollapseAction::NoAction; 144 | 145 | return lineNumber; 146 | } 147 | 148 | void GuiHeaderTree::showCommandLine() 149 | { 150 | ImGui::Text("Table Of Contents"); 151 | ImGui::SameLine(); 152 | if (DrawImGuiTextFilterWithTooltip(mFilter)) 153 | applyTocFilter(); 154 | ImGui::SameLine(); 155 | showExpandCollapseButtons(); 156 | } 157 | 158 | void GuiHeaderTree::showExpandCollapseButtons() 159 | { 160 | if (ImGui::Button(ICON_FA_PLUS_SQUARE " Expand all")) 161 | mExpandCollapseAction = ExpandCollapseAction::ExpandAll; 162 | ImGui::SameLine(); 163 | if (ImGui::Button(ICON_FA_MINUS_SQUARE " Collapse all")) 164 | mExpandCollapseAction = ExpandCollapseAction::CollapseAll; 165 | } 166 | 167 | void GuiHeaderTree::applyTocFilter() 168 | { 169 | auto lambdaPassFilter = [this](const LineWithTag& t) { 170 | return mFilter.PassFilter(t.tag.c_str()); 171 | }; 172 | mFilteredHeaderTree = tree_keep_wholebranch_if( 173 | lambdaPassFilter, mHeaderTree); 174 | } 175 | 176 | 177 | } // namespace SourceParse -------------------------------------------------------------------------------- /src/ImGuiDemoBrowser.cpp: -------------------------------------------------------------------------------- 1 | #include "source_parse/ImGuiRepoUrl.h" 2 | 3 | #include "imgui_utilities/HyperlinkHelper.h" 4 | #include "imgui_utilities/ImGuiExt.h" 5 | #include "source_parse/ImGuiDemoParser.h" 6 | 7 | #include "hello_imgui/hello_imgui.h" 8 | 9 | #include 10 | 11 | #include "ImGuiDemoBrowser.h" 12 | 13 | // Redefinition of ImGuiDemoMarkerCallback, as defined in imgui_demo.cpp 14 | typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); 15 | extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; 16 | 17 | 18 | // implImGuiDemoCallbackDemoCallback is the implementation 19 | // of imgui_demo.cpp's global callback (gImGuiDemoCallback) 20 | // And gImGuiDemoBrowser is a global reference to the browser used by this callback 21 | ImGuiDemoBrowser *gImGuiDemoBrowser = nullptr; 22 | extern HelloImGui::RunnerParams runnerParams; // defined in ImGuiManual.cpp 23 | void implImGuiDemoCallbackDemoCallback(const char* file, int line, const char* section, void* /*user_data*/) 24 | { 25 | gImGuiDemoBrowser->ImGuiDemoCallback(file, line, section); 26 | } 27 | 28 | 29 | ImGuiDemoBrowser::ImGuiDemoBrowser(): 30 | mSourceElementsCpp(SourceParse::ReadImGuiDemoCode(), "imgui_demo.cpp"), 31 | mSourceElementsPython(SourceParse::ReadImGuiDemoCodePython(), "imgui_demo.py") 32 | { 33 | // Setup of imgui_demo.cpp's global callback 34 | // (GImGuiDemoMarkerCallback belongs to imgui.cpp!) 35 | GImGuiDemoMarkerCallback = implImGuiDemoCallbackDemoCallback; 36 | 37 | gImGuiDemoBrowser = this; 38 | } 39 | 40 | 41 | std::optional findLineWithOriginalTag(const SourceParse::AnnotatedSource& as, const std::string& tag) 42 | { 43 | auto matchingTags = fplus::keep_if( 44 | [&tag](const SourceParse::LineWithTag& lt) { return lt._original_tag_full == tag; }, 45 | as.linesWithTags 46 | ); 47 | if (matchingTags.size() == 1) 48 | return matchingTags[0]; 49 | else 50 | return std::nullopt; 51 | } 52 | 53 | void ImGuiDemoBrowser::ImGuiDemoCallback(const char* /*file*/, int /*line_number*/, const char* tag) 54 | { 55 | if (strcmp(tag, "Menu") == 0) 56 | return; 57 | for (auto sourceElements: AllSourceElements()) 58 | { 59 | auto lineWithTag = findLineWithOriginalTag(sourceElements->AnnotatedSource, tag); 60 | if (!lineWithTag.has_value()) 61 | continue ; 62 | 63 | int line_number = lineWithTag->lineNumber + 1; 64 | int cursorLineOnPage = 3; 65 | sourceElements->mGuiHeaderTree.followShowTocElementForLine(line_number); 66 | sourceElements->mWindowWithEditor.InnerTextEditor().SetCursorPosition({line_number, 0}, cursorLineOnPage); 67 | } 68 | } 69 | 70 | void ImGuiDemoBrowser::gui() 71 | { 72 | guiHelp(); 73 | guiDemoCodeTags(); 74 | guiSave(); 75 | 76 | CurrentSourceElements().mWindowWithEditor.RenderEditor("imgui_demo", [this] { this->guiEditorAdditional(); }); 77 | } 78 | 79 | 80 | void ImGuiDemoBrowser::guiHelp() 81 | { 82 | std::string help = 83 | "This is the code of imgui_demo.cpp. It is the best way to learn about Dear ImGui! \n" 84 | "\n" 85 | "* On the left, you can see a demo that showcases all the widgets and features of ImGui:\n" 86 | " If 'Code Lookup' is checked, the code editor and the the table of content will follow\n" 87 | " the mouse whenever you hover a demo.\n" 88 | "\n" 89 | "* Below, the table of content (TOC) shows all the available demos: click on any item to see its code.\n" 90 | " Alternatively, you can also search for some features (try searching for \"widgets\", \"layout\", \"drag\", etc)\n" 91 | ; 92 | ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.f, 1.0f), "(?)"); 93 | if (ImGui::IsItemHovered()) 94 | ImGui::SetTooltip("%s", help.c_str()); 95 | ImGui::SameLine(50.f); 96 | } 97 | 98 | void ImGuiDemoBrowser::guiSave() 99 | { 100 | #ifdef IMGUI_MANUAL_CAN_WRITE_IMGUI_DEMO_CPP 101 | if (mViewPythonOrCpp == ViewPythonOrCpp::Cpp) 102 | { 103 | if (ImGui::Button("Save")) 104 | { 105 | auto& currentEditor = CurrentSourceElements().mWindowWithEditor.InnerTextEditor(); 106 | std::string fileSrc = IMGUI_MANUAL_REPO_DIR "/external/imgui/imgui_demo.cpp"; 107 | fplus::write_text_file(fileSrc, currentEditor.GetText())(); 108 | } 109 | } 110 | #endif 111 | } 112 | 113 | void ImGuiDemoBrowser::guiEditorAdditional() 114 | { 115 | if (ImGui::SmallButton("View on github at this line")) 116 | { 117 | auto& currentEditor = CurrentSourceElements().mWindowWithEditor.InnerTextEditor(); 118 | int lineNumber = currentEditor.GetCursorPosition().mLine + 1; 119 | 120 | std::string demoFileUrl; 121 | if (mViewPythonOrCpp == ViewPythonOrCpp::Cpp) 122 | demoFileUrl = ImGuiRepoUrl() + "imgui_demo.cpp"; 123 | else 124 | demoFileUrl = "https://github.com/pthom/imgui_manual/blob/master/src/imgui_demo_python/imgui_demo.py"; 125 | 126 | std::string url = demoFileUrl + "#L" + std::to_string(lineNumber); 127 | HyperlinkHelper::OpenUrl(url); 128 | } 129 | 130 | ImGui::SameLine(); 131 | { 132 | bool checked = (mViewPythonOrCpp == ViewPythonOrCpp::Python); 133 | bool changed = ImGui::Checkbox("Python", &checked); 134 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 135 | ImGui::SetTooltip("View python demo code, using \"Dear ImGui Bundle\" bindings: https://pthom.github.io/imgui_bundle/"); 136 | if (changed) 137 | mViewPythonOrCpp = checked ? ViewPythonOrCpp::Python : ViewPythonOrCpp::Cpp; 138 | } 139 | } 140 | 141 | void ImGuiDemoBrowser::guiDemoCodeTags() 142 | { 143 | auto& currentEditor = CurrentSourceElements().mWindowWithEditor.InnerTextEditor(); 144 | auto& currentGuiHeaderTree = CurrentSourceElements().mGuiHeaderTree; 145 | 146 | int currentEditorLineNumber = currentEditor.GetCursorPosition().mLine; 147 | int selectedLine = currentGuiHeaderTree.gui(currentEditorLineNumber); 148 | if (selectedLine >= 0) 149 | currentEditor.SetCursorPosition({selectedLine, 0}, 3); 150 | } 151 | -------------------------------------------------------------------------------- /src/source_parse/ImGuiDemoParser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "source_parse/Sources.h" 3 | 4 | using namespace std::literals; 5 | 6 | namespace SourceParse 7 | { 8 | 9 | LinesWithTags findImGuiDemoCodeLines(const std::string &sourceCode) 10 | { 11 | static std::string regionToken = "IMGUI_DEMO_MARKER"; 12 | 13 | auto lines = fplus::split('\n', true, sourceCode); 14 | auto numberedLines = fplus::enumerate(lines); 15 | 16 | auto demoCodeLines = fplus::keep_if([](const NumberedLine& v) { 17 | return fplus::is_prefix_of("IMGUI_DEMO_MARKER("s, fplus::trim_whitespace_left(v.second)); 18 | }, numberedLines); 19 | 20 | LinesWithTags tags = fplus::transform([](const NumberedLine& v) { 21 | LineWithTag r; 22 | // codeLine look like " IMGUI_DEMO_MARKER("Menu/Tools");" 23 | // And for a tag like this, the title should be "Tools", and the level should be 2 24 | std::string codeLine = v.second; 25 | 26 | r.lineNumber = (int)v.first; 27 | { 28 | // codeLine look like 29 | // IMGUI_DEMO_MARKER("Menu/Tools"); 30 | // And in this case, we return {"Menu/Tools"} 31 | auto tokens = fplus::split('"', true, codeLine); 32 | assert(tokens.size() >= 3); 33 | std::string tagsString = tokens[1]; 34 | 35 | r.tag = tagsString; 36 | r.level = fplus::count('/', r.tag) + 1; 37 | } 38 | return r; 39 | }, demoCodeLines); 40 | 41 | LinesWithTags tags_with_added_missing_headers; 42 | int last_level = 0; 43 | for (auto tag_copy: tags) 44 | { 45 | std::vector tag_levels = fplus::split('/', false, tag_copy.tag); 46 | while (tag_copy.level > last_level + 1) 47 | { 48 | LineWithTag missing_tag = tag_copy; 49 | missing_tag.level = last_level + 1; 50 | missing_tag.lineNumber = tag_copy.lineNumber; 51 | missing_tag.tag = tag_levels[last_level]; 52 | tags_with_added_missing_headers.push_back(missing_tag); 53 | 54 | last_level = missing_tag.level; 55 | } 56 | tag_copy._original_tag_full = tag_copy.tag; 57 | tag_copy.tag = tag_levels.back(); 58 | tags_with_added_missing_headers.push_back(tag_copy); 59 | last_level = tag_copy.level; 60 | } 61 | 62 | return tags_with_added_missing_headers; 63 | } 64 | 65 | 66 | AnnotatedSource ReadImGuiDemoCode() 67 | { 68 | std::string sourcePath = "imgui/imgui_demo.cpp"; 69 | AnnotatedSource r; 70 | r.source = ReadSource(sourcePath); 71 | r.linesWithTags = findImGuiDemoCodeLines(r.source.sourceCode); 72 | return r; 73 | } 74 | 75 | AnnotatedSource ReadImGuiDemoCodePython() 76 | { 77 | std::string sourcePath = "imgui_manual/imgui_demo_python/imgui_demo.py"; 78 | AnnotatedSource r; 79 | r.source = ReadSource(sourcePath); 80 | r.linesWithTags = findImGuiDemoCodeLines(r.source.sourceCode); 81 | return r; 82 | } 83 | 84 | 85 | std::unordered_map FindExampleAppsCode() 86 | { 87 | std::string sourcePath = "imgui/imgui_demo.cpp"; 88 | auto sourceFile = ReadSource(sourcePath); 89 | 90 | // Example apps sections look like this 91 | /* 92 | //----------------------------------------------------------------------------- 93 | // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() 94 | //----------------------------------------------------------------------------- 95 | // - ShowExampleAppMainMenuBar() 96 | // - ShowExampleMenuFile() 97 | //----------------------------------------------------------------------------- 98 | */ 99 | 100 | // Step 1 : find all section starts 101 | auto numberedLines = fplus::enumerate(fplus::split_lines(true, sourceFile.sourceCode)); 102 | auto isLineExampleAppSectionStarts = [&numberedLines](const NumberedLine& numberedLine) { 103 | size_t lineNumber = numberedLine.first; 104 | if (lineNumber >= numberedLines.size() - 1) 105 | return false; 106 | std::string codeLine = numberedLines[lineNumber].second; 107 | std::string nextCodeLine = numberedLines[lineNumber + 1].second; 108 | if (! fplus::is_prefix_of("//-----------------"s, codeLine)) 109 | return false; 110 | if (! fplus::is_prefix_of("// [SECTION] Example App:"s, nextCodeLine)) 111 | return false; 112 | return true; 113 | }; 114 | auto idxLineStartExampleSections = fplus::find_all_idxs_by(isLineExampleAppSectionStarts, numberedLines); 115 | 116 | // Step 2: add an additional index which is the end of the demo code in imgui_demo.cpp 117 | // it is denoted by the following line: 118 | /* 119 | // End of Demo code 120 | */ 121 | { 122 | auto endLineNumber = fplus::find_first_idx_by([](const NumberedLine& line){ 123 | return line.second == "// End of Demo code"; 124 | }, numberedLines); 125 | assert(endLineNumber.is_just()); 126 | idxLineStartExampleSections.push_back(endLineNumber.unsafe_get_just()); 127 | } 128 | 129 | // Step 3: find the sections scopes (i.e start/end lines) 130 | std::vector> sectionScopes = fplus::overlapping_pairs(idxLineStartExampleSections); 131 | 132 | // Step 4: Gather the sections into a map 133 | std::unordered_map exampleAppsCodes; 134 | auto extractSectionName = [&numberedLines](const std::pair& sectionScope) 135 | -> std::string { 136 | auto titleLine = numberedLines[sectionScope.first + 1].second; 137 | // titleLine looks like this: 138 | // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() 139 | auto items = fplus::split('/', true, titleLine); 140 | assert(items.size() > 1); 141 | 142 | std::string functionName = fplus::trim_whitespace(items.back()); 143 | // functionName should look like ShowExampleAppMainMenuBar() 144 | std::string appName = fplus::replace_tokens("Show"s, ""s, functionName); 145 | appName = fplus::replace_tokens("()"s, ""s, appName); 146 | return appName; 147 | }; 148 | 149 | for (const auto& sectionScope: sectionScopes) 150 | { 151 | std::string sectionName = extractSectionName(sectionScope); 152 | SourceCode sectionCode; 153 | { 154 | auto sectionNumberedLines = 155 | fplus::get_segment(sectionScope.first, sectionScope.second, numberedLines); 156 | std::vector sectionLines = 157 | fplus::transform([](const auto&v) { return v.second; }, sectionNumberedLines); 158 | sectionCode = fplus::join("\n"s, sectionLines); 159 | } 160 | exampleAppsCodes[sectionName] = sectionCode; 161 | } 162 | return exampleAppsCodes; 163 | } 164 | 165 | } -------------------------------------------------------------------------------- /src/ImGuiManual.cpp: -------------------------------------------------------------------------------- 1 | #include "ImGuiCppDocBrowser.h" 2 | #include "ImGuiHeaderDocBrowser.h" 3 | #include "ImGuiDemoBrowser.h" 4 | #include "imgui_utilities/HyperlinkHelper.h" 5 | #include "hello_imgui/hello_imgui.h" 6 | #include "imgui_utilities/MarkdownHelper.h" 7 | 8 | HelloImGui::RunnerParams runnerParams; 9 | 10 | int main(int, char **) 11 | { 12 | // Our gui providers for the different windows 13 | ImGuiDemoBrowser imGuiDemoBrowser; 14 | ImGuiCppDocBrowser imGuiCppDocBrowser; 15 | ImGuiHeaderDocBrowser imGuiHeaderDocBrowser; 16 | 17 | // 18 | // Below, we will define all our application parameters and callbacks 19 | // before starting it. 20 | // 21 | // runnerParams.imGuiWindowParams.tweakedTheme.Theme = ImGuiTheme::ImGuiTheme_DarculaDarker; 22 | runnerParams.imGuiWindowParams.tweakedTheme.Theme = ImGuiTheme::ImGuiTheme_ImGuiColorsDark; 23 | 24 | // App window params 25 | runnerParams.appWindowParams.windowTitle = "Dear ImGui Manual"; 26 | runnerParams.appWindowParams.windowGeometry.size = {1200, 800}; 27 | 28 | // ImGui window params 29 | runnerParams.imGuiWindowParams.defaultImGuiWindowType = 30 | HelloImGui::DefaultImGuiWindowType::ProvideFullScreenDockSpace; 31 | runnerParams.imGuiWindowParams.showMenuBar = true; 32 | runnerParams.imGuiWindowParams.showStatusBar = true; 33 | 34 | // Split the screen in two parts (two "DockSpaces") 35 | // This will split the preexisting default dockspace "MainDockSpace" 36 | // in two parts: 37 | // "MainDockSpace" will be on the left, "CodeSpace" will be on the right 38 | // and occupy 65% of the app width. 39 | runnerParams.dockingParams.dockingSplits = { 40 | { "MainDockSpace", "CodeSpace", ImGuiDir_Right, 0.65f }, 41 | }; 42 | 43 | // 44 | // Dockable windows definitions 45 | // 46 | { 47 | HelloImGui::DockableWindow dock_imguiDemoWindow; 48 | { 49 | dock_imguiDemoWindow.label = "Dear ImGui Demo"; 50 | dock_imguiDemoWindow.dockSpaceName = "MainDockSpace";// This window goes into "MainDockSpace" 51 | dock_imguiDemoWindow.GuiFunction = [&dock_imguiDemoWindow] { 52 | if (dock_imguiDemoWindow.isVisible) 53 | ImGui::ShowDemoWindow(nullptr); 54 | }; 55 | dock_imguiDemoWindow.callBeginEnd = false; 56 | dock_imguiDemoWindow.includeInViewMenu = false; 57 | }; 58 | 59 | HelloImGui::DockableWindow dock_imguiDemoCode; 60 | { 61 | dock_imguiDemoCode.label = "Demo Code"; 62 | dock_imguiDemoCode.dockSpaceName = "CodeSpace";// This window goes into "CodeSpace" 63 | dock_imguiDemoCode.isVisible = true; 64 | dock_imguiDemoCode.GuiFunction = [&imGuiDemoBrowser] { imGuiDemoBrowser.gui(); }; 65 | dock_imguiDemoCode.imGuiWindowFlags = ImGuiWindowFlags_HorizontalScrollbar; 66 | dock_imguiDemoCode.includeInViewMenu = false; 67 | }; 68 | 69 | HelloImGui::DockableWindow dock_imGuiCppDocBrowser; 70 | { 71 | dock_imGuiCppDocBrowser.label = imGuiCppDocBrowser.windowLabel(); 72 | dock_imGuiCppDocBrowser.dockSpaceName = "CodeSpace"; 73 | dock_imGuiCppDocBrowser.isVisible = false; 74 | dock_imGuiCppDocBrowser.GuiFunction = [&imGuiCppDocBrowser] { imGuiCppDocBrowser.gui(); }; 75 | }; 76 | 77 | HelloImGui::DockableWindow dock_imGuiHeaderDocBrowser; 78 | { 79 | dock_imGuiHeaderDocBrowser.label = imGuiHeaderDocBrowser.windowLabel(); 80 | dock_imGuiHeaderDocBrowser.dockSpaceName = "CodeSpace"; 81 | dock_imGuiHeaderDocBrowser.isVisible = false; 82 | dock_imGuiHeaderDocBrowser.GuiFunction = [&imGuiHeaderDocBrowser] { imGuiHeaderDocBrowser.gui(); }; 83 | }; 84 | 85 | // 86 | // Set our app dockable windows list 87 | // 88 | runnerParams.dockingParams.dockableWindows = { 89 | dock_imguiDemoCode, 90 | dock_imguiDemoWindow, 91 | dock_imGuiHeaderDocBrowser, 92 | dock_imGuiCppDocBrowser, 93 | }; 94 | } 95 | 96 | // Set the app menu 97 | runnerParams.callbacks.ShowMenus = []{ 98 | HelloImGui::DockableWindow *aboutWindow = 99 | runnerParams.dockingParams.dockableWindowOfName("About this manual"); 100 | HelloImGui::DockableWindow *acknowledgmentWindow = 101 | runnerParams.dockingParams.dockableWindowOfName("Acknowledgments"); 102 | if (ImGui::BeginMenu("Links & About")) 103 | { 104 | ImGui::SeparatorText("Links"); 105 | if (ImGui::MenuItem("Dear ImGui - Github repository")) 106 | HyperlinkHelper::OpenUrl("https://github.com/ocornut/imgui"); 107 | if (ImGui::MenuItem("Dear ImGui - FAQ")) 108 | HyperlinkHelper::OpenUrl("https://github.com/ocornut/imgui/blob/master/docs/FAQ.md"); 109 | if (ImGui::MenuItem("dearimgui.com")) 110 | HyperlinkHelper::OpenUrl("https://www.dearimgui.com/"); 111 | ImGui::SetItemTooltip("The exciting placeholder webpage! - Features Dear ImGui Logo"); 112 | 113 | // Not up to date anymore (2025-09), misses info about v1.92 114 | // if (ImGui::MenuItem("imgui-docs: nice third party ImGui Documentation")) 115 | // HyperlinkHelper::OpenUrl("https://possiblyashrub.github.io/imgui-docs/"); 116 | 117 | ImGui::SeparatorText("Wiki"); 118 | if (ImGui::MenuItem("Dear ImGui - wiki")) 119 | HyperlinkHelper::OpenUrl("https://github.com/ocornut/imgui/wiki"); 120 | if (ImGui::MenuItem("Bindings")) 121 | HyperlinkHelper::OpenUrl("https://github.com/ocornut/imgui/wiki/Bindings"); 122 | if (ImGui::MenuItem("Useful Extensions")) 123 | HyperlinkHelper::OpenUrl("https://github.com/ocornut/imgui/wiki/Useful-Extensions"); 124 | 125 | 126 | ImGui::SeparatorText("About this manual"); 127 | if (ImGui::MenuItem("Repository")) 128 | HyperlinkHelper::OpenUrl("https://github.com/pthom/imgui_manual"); 129 | ImGui::EndMenu(); 130 | } 131 | }; 132 | 133 | // Add some widgets in the status bar 134 | runnerParams.callbacks.ShowStatus = [] { 135 | //MarkdownHelper::Markdown("Dear ImGui Manual - [Repository](https://github.com/pthom/imgui_manual)"); 136 | MarkdownHelper::Markdown("Dear ImGui Manual, a manual for [Dear ImGui](https://github.com/ocornut/imgui) - Made with [Dear ImGui Bundle](https://github.com/pthom/imgui_bundle/) and [Hello ImGui](https://github.com/pthom/hello_imgui)"); 137 | }; 138 | 139 | // Set the custom fonts 140 | runnerParams.callbacks.LoadAdditionalFonts = []() { 141 | HelloImGui::ImGuiDefaultSettings::LoadDefaultFont_WithFontAwesomeIcons(); 142 | LoadMonospaceFont(); 143 | MarkdownHelper::LoadFonts(); 144 | 145 | // #ifdef __EMSCRIPTEN__ 146 | // JsClipboard_InstallPlaformSetClipboardText(); 147 | // #endif 148 | }; 149 | 150 | runnerParams.dockingParams.focusDockableWindow("Demo Code"); 151 | 152 | // #ifdef IMGUIMANUAL_CLIPBOARD_IMPORT_FROM_BROWSER 153 | // JsClipboard_AddJsHook(); 154 | // #endif 155 | 156 | HelloImGui::Run(runnerParams); 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /src/source_parse/Tree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace SourceParse 7 | { 8 | 9 | template 10 | struct Tree 11 | { 12 | T value_; 13 | std::vector> children_ = {}; 14 | }; 15 | 16 | template 17 | inline void tree_visit_breadth_first(Visitor_T visitor, Tree & xs_io) 18 | { 19 | visitor(xs_io); 20 | for (auto & child : xs_io.children_) 21 | tree_visit_breadth_first(visitor, child); 22 | } 23 | 24 | template 25 | inline void tree_visit_depth_first(Visitor_T visitor, Tree & xs_io) 26 | { 27 | for (auto & child : xs_io.children_) 28 | tree_visit_depth_first(visitor, child); 29 | visitor(xs_io); 30 | } 31 | 32 | template 33 | Tree* tree_find_parent(Tree& treeRoot, Tree& treeNode) 34 | { 35 | Tree * parent = nullptr; 36 | auto visitor = [&parent, &treeNode](Tree& xs) { 37 | for (auto& child : xs.children_) 38 | if (&child == &treeNode) 39 | parent = &xs; 40 | }; 41 | tree_visit_breadth_first(visitor, treeRoot); 42 | return parent; 43 | } 44 | 45 | // Apply a visitor to node ancestors (but *not* to the node itself) 46 | template 47 | inline void tree_visit_parents( 48 | Visitor_T visitor, 49 | Tree & tree_root, 50 | Tree & node) 51 | { 52 | Tree* parent = &node; 53 | do 54 | { 55 | parent = tree_find_parent(tree_root, *parent); 56 | if (parent) 57 | visitor(*parent); 58 | } while(parent != nullptr); 59 | } 60 | 61 | 62 | template 63 | inline Tree tree_sort( 64 | FunctionComparator functionComparatorT, 65 | const Tree& xs) 66 | { 67 | auto functionComparatorTreeT = 68 | [&functionComparatorT](const Tree & v1, const Tree & v2) 69 | -> bool { 70 | return functionComparatorT(v1.value_, v2.value_); 71 | }; 72 | 73 | auto treeSortFunction = [&functionComparatorT](const Tree& xxs) { 74 | return tree_sort(functionComparatorT, xxs); 75 | }; 76 | 77 | auto sorted_children = fplus::transform( 78 | treeSortFunction, 79 | xs.children_ 80 | ); 81 | 82 | Tree r{xs.value_, fplus::sort_by(functionComparatorTreeT, sorted_children)}; 83 | return r; 84 | } 85 | 86 | 87 | template 88 | inline std::string tree_show(const Tree& xs) 89 | { 90 | std::string siblingSeparator = "\n"; 91 | std::string childStart = "\n"; 92 | std::string childEnd = ""; 93 | 94 | auto indentLines = [](const std::string& s, int nbSpaces) -> std::string { 95 | auto lines = fplus::split_lines(true, s); 96 | auto indentation = fplus::repeat(nbSpaces, std::string(" ")); 97 | auto indentedLines = fplus::transform([indentation](const auto &ss) { return indentation + ss; }, lines); 98 | return fplus::join(std::string("\n"), indentedLines); 99 | }; 100 | 101 | std::string r = fplus::show(xs.value_); 102 | if (!xs.children_.empty()) 103 | { 104 | auto strings = fplus::transform(tree_show, xs.children_); 105 | r = r + childStart + indentLines(fplus::join(siblingSeparator, strings), 2) + childEnd; 106 | } 107 | return r; 108 | } 109 | 110 | 111 | template 112 | inline Tree tree_keep_if(UnaryPredicate f, const Tree & xs) 113 | { 114 | Tree result = xs; 115 | 116 | result.children_ = fplus::keep_if( 117 | [f](const Tree & xxs) { return f(xxs.value_); }, 118 | result.children_); 119 | 120 | std::vector> children_filtered_grandchhildren; 121 | for (auto & child : result.children_) 122 | { 123 | auto child_filtered = tree_keep_if(f, child); 124 | children_filtered_grandchhildren.push_back(child_filtered); 125 | } 126 | result.children_ = children_filtered_grandchhildren; 127 | return result; 128 | } 129 | 130 | 131 | template 132 | inline Tree tree_transform_values( 133 | Transformer_T transformer, const Tree & xs 134 | ) 135 | { 136 | Tree r; 137 | r.value_ = transformer(xs.value_); 138 | for (auto & child : xs.children_) 139 | r.children_.push_back(tree_transform_values(transformer, child)); 140 | return r; 141 | } 142 | 143 | template 144 | struct FlaggedValue 145 | { 146 | T value; 147 | bool flag; 148 | }; 149 | 150 | template 151 | std::ostream & operator<<(std::ostream& os, const FlaggedValue &v) 152 | { 153 | os << "{" << v.value << " : " << v.flag << "}"; 154 | return os; 155 | } 156 | 157 | 158 | // Filters a tree by keeping whole branches (from the root to the leafs) 159 | // where any node matches the predicate 160 | template 161 | inline Tree tree_keep_wholebranch_if(UnaryPredicate f, const Tree & xs) 162 | { 163 | // Step 1: transform the tree into the same tree, but with flags 164 | auto toFlaggedValue = [](const T& v) { 165 | return FlaggedValue{v, false}; 166 | }; 167 | Tree> flaggedTree = tree_transform_values>(toFlaggedValue, xs); 168 | 169 | // This visitor will apply the flag on a given node + all its parent and descendants 170 | // if the predicate returns true 171 | auto visitorFlagBranch = 172 | [&f, &flaggedTree](Tree>& v) 173 | { 174 | auto visitorSetFlagOn = [&f](Tree>& v) 175 | { 176 | v.value_.flag = true; 177 | }; 178 | 179 | bool flag = f(v.value_.value); 180 | if (flag) 181 | { 182 | tree_visit_parents(visitorSetFlagOn, flaggedTree, v); 183 | tree_visit_breadth_first(visitorSetFlagOn, v); 184 | } 185 | }; 186 | 187 | // We flag the whole tree by branch 188 | tree_visit_breadth_first(visitorFlagBranch, flaggedTree); 189 | //std::cout << tree_show(flaggedTree) << "\n"; 190 | 191 | // keep only the flagged node 192 | auto filteredFlaggedTree = tree_keep_if([](auto v) { return v.flag; }, flaggedTree); 193 | 194 | // And then we retransform the flagged tree into a standard tree, 195 | auto toT = [](const FlaggedValue& v) { 196 | return v.value; 197 | }; 198 | auto filteredTree = tree_transform_values(toT, filteredFlaggedTree); 199 | 200 | return filteredTree; 201 | } 202 | 203 | 204 | template 205 | T value_from_string(const std::string & s) 206 | { 207 | T value; 208 | std::istringstream ss(s); 209 | ss >> value; 210 | return value; 211 | } 212 | 213 | 214 | template 215 | Tree tree_from_string(const std::string &s, int indentation = 2) 216 | { 217 | Tree root; 218 | Tree *current_node = &root; 219 | auto lines = fplus::split_lines(false, s); 220 | int current_depth = 0; 221 | bool was_root_already_added = false; 222 | for (const auto& line: lines) 223 | { 224 | int line_depth = fplus::take_while([](auto c){return c == ' ';}, line).size() / indentation; 225 | std::string trimmed_line = fplus::trim_whitespace_left(line); 226 | T value = value_from_string(trimmed_line); 227 | Tree value_node{value}; 228 | if (line_depth == 0) 229 | { 230 | assert(!was_root_already_added); 231 | was_root_already_added = true; 232 | root.value_ = value; 233 | } 234 | else if (line_depth == current_depth) 235 | { 236 | current_node->children_.push_back(value_node); 237 | } 238 | else if (line_depth > current_depth) 239 | { 240 | assert((line_depth - current_depth) == 1); 241 | current_node = &(current_node->children_.back()); 242 | current_node->children_.push_back(value_node); 243 | } 244 | else if (line_depth < current_depth) 245 | { 246 | int nbUps = current_depth - line_depth; 247 | for (int i = 0; i < nbUps; ++i) 248 | { 249 | Tree* parent = tree_find_parent(root, *current_node); 250 | assert(parent != nullptr); 251 | current_node = parent; 252 | } 253 | current_node->children_.push_back(value_node); 254 | } 255 | current_depth = (line_depth == 0) ? 1 : line_depth; 256 | } 257 | return root; 258 | } 259 | 260 | 261 | } // namespace SourceParse 262 | -------------------------------------------------------------------------------- /src/WindowWithEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_utilities/ImGuiExt.h" 2 | #include "hello_imgui/hello_imgui.h" 3 | #include "hello_imgui/icons_font_awesome_4.h" 4 | #include 5 | #include 6 | #include "WindowWithEditor.h" 7 | #include "JsClipboardTricks.h" 8 | 9 | std::vector gAllEditors; 10 | std::vector gAllWindowWithEditors; 11 | 12 | ImFont * gMonospaceFont = nullptr; 13 | 14 | void LoadMonospaceFont() 15 | { 16 | float fontSize = 15.f; 17 | std::string fontFilename = "fonts/Inconsolata-Regular.ttf"; 18 | gMonospaceFont = HelloImGui::LoadFontTTF(fontFilename, fontSize); 19 | // MergeFontAwesomeToLastFont(fontSize); 20 | } 21 | 22 | WindowWithEditor::WindowWithEditor(const std::string& windowLabel) 23 | : mWindowLabel(windowLabel) 24 | { 25 | mEditor.SetPalette(TextEditor::GetLightPalette()); 26 | mEditor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); 27 | mEditor.SetReadOnly(true); 28 | gAllEditors.push_back(&mEditor); 29 | gAllWindowWithEditors.push_back(this); 30 | } 31 | 32 | void WindowWithEditor::setEditorAnnotatedSource(const SourceParse::AnnotatedSource &annotatedSource) 33 | { 34 | mEditor.SetText(annotatedSource.source.sourceCode); 35 | std::unordered_set lineNumbers; 36 | for (auto line : annotatedSource.linesWithTags) 37 | lineNumbers.insert(line.lineNumber + 1); 38 | mEditor.SetBreakpoints(lineNumbers); 39 | } 40 | 41 | void RenderLongLinesOverlay(const std::string& currentCodeLine) 42 | { 43 | using namespace std::literals; 44 | 45 | std::string code, comment; 46 | { 47 | auto commentPosition = fplus::find_first_instance_of_token("//"s, currentCodeLine); 48 | if (commentPosition.is_nothing()) 49 | code = currentCodeLine; 50 | else 51 | { 52 | code = fplus::trim_whitespace(fplus::take(commentPosition.unsafe_get_just(), currentCodeLine)); 53 | comment = fplus::drop(commentPosition.unsafe_get_just() + 2, currentCodeLine); 54 | } 55 | } 56 | 57 | ImGui::SetNextWindowBgAlpha(0.75f); // Transparent background 58 | ImGuiWindowFlags window_flags = 59 | ImGuiWindowFlags_NoDecoration 60 | | ImGuiWindowFlags_NoDocking 61 | | ImGuiWindowFlags_NoSavedSettings 62 | | ImGuiWindowFlags_NoFocusOnAppearing 63 | | ImGuiWindowFlags_NoMove 64 | | ImGuiWindowFlags_NoNav; 65 | 66 | ImVec2 commentOverlaySize(ImVec2(ImGui::GetWindowWidth(), 60.f)); 67 | ImGui::SetNextWindowSize( commentOverlaySize, ImGuiCond_Always ); 68 | ImGui::SetNextWindowPos( 69 | ImVec2(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x - commentOverlaySize.x, 70 | ImGui::GetWindowPos().y + ImGui::GetWindowSize().y - commentOverlaySize.y), 71 | ImGuiCond_Always 72 | ); 73 | 74 | if (ImGui::Begin("Comment Overlay", nullptr, window_flags)) 75 | { 76 | if (!code.empty()) 77 | { 78 | ImGui::PushFont(gMonospaceFont); 79 | ImGui::TextWrapped("%s", code.c_str()); 80 | ImGui::PopFont(); 81 | } 82 | if (!comment.empty()) 83 | { 84 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 1.f, 0.6f, 1.f)); 85 | ImGui::TextWrapped("%s", comment.c_str()); 86 | ImGui::PopStyleColor(); 87 | } 88 | ImGui::End(); 89 | } 90 | } 91 | 92 | 93 | 94 | void WindowWithEditor::RenderEditor(const std::string &filename, VoidFunction additionalGui) 95 | { 96 | guiIconBar(additionalGui); 97 | guiStatusLine(filename); 98 | 99 | ImGui::PushFont(gMonospaceFont); 100 | mEditor.Render(filename.c_str()); 101 | 102 | bool lineTooLong = false; 103 | { 104 | float editorWidth = ImGui::GetItemRectSize().x; 105 | float textWidth = ImGui::CalcTextSize(mEditor.GetCurrentLineText().c_str()).x + ImGui::GetFontSize() * 4.f; 106 | lineTooLong = textWidth > editorWidth; 107 | } 108 | if (mShowLongLinesOverlay && lineTooLong) 109 | RenderLongLinesOverlay(mEditor.GetCurrentLineText()); 110 | 111 | ImGui::PopFont(); 112 | 113 | editorContextMenu(); 114 | } 115 | 116 | void WindowWithEditor::editorContextMenu() 117 | { 118 | if (mEditor.GetSelectedText().empty()) 119 | return; 120 | if (ImGui::BeginPopupContextItem("item context menu")) 121 | { 122 | std::map fileAndEditorWindowName 123 | { 124 | {"imgui.h", "imgui.h - Doc"}, 125 | // {"imgui.cpp", "imgui.cpp - Doc"}, 126 | {"imgui_demo.cpp", "ImGui - Demo Code"} 127 | }; 128 | 129 | std::string selectionShort = mEditor.GetSelectedText(); 130 | if (selectionShort.size() > 30) 131 | selectionShort = fplus::take(30, selectionShort) + "..."; 132 | 133 | for (const auto& kv: fileAndEditorWindowName) 134 | { 135 | std::string label = "Search for \"" + selectionShort + "\" in " + kv.first; 136 | if (ImGui::Selectable(label.c_str())) 137 | WindowWithEditor::searchForFirstOccurenceAndFocusWindow( 138 | mEditor.GetSelectedText(), kv.second); 139 | } 140 | ImGui::EndPopup(); 141 | } 142 | } 143 | 144 | void WindowWithEditor::guiStatusLine(const std::string &filename) 145 | { 146 | auto & editor = mEditor; 147 | auto cpos = editor.GetCursorPosition(); 148 | ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(), 149 | editor.IsOverwrite() ? "Ovr" : "Ins", 150 | editor.CanUndo() ? "*" : " ", 151 | filename.c_str()); 152 | } 153 | 154 | void WindowWithEditor::guiFind() 155 | { 156 | ImGui::SameLine(); 157 | // Draw filter 158 | bool filterChanged = false; 159 | { 160 | ImGui::SetNextItemWidth(100.f); 161 | filterChanged = mFilter.Draw("Search code"); ImGui::SameLine(); 162 | ImGui::SameLine(); 163 | ImGui::TextDisabled("?"); 164 | if (ImGui::IsItemHovered()) 165 | ImGui::SetTooltip("Filter using -exc,inc. For example search for '-widgets,DEMO_MARKER'"); 166 | ImGui::SameLine(); 167 | } 168 | // If changed, check number of matches 169 | if (filterChanged) 170 | { 171 | const auto & lines = mEditor.GetTextLines(); 172 | mNbFindMatches = (int)fplus::count_if([this](auto s){ return mFilter.PassFilter(s.c_str());}, lines); 173 | } 174 | 175 | // Draw number of matches 176 | { 177 | if (mNbFindMatches > 0) 178 | { 179 | const auto & lines = mEditor.GetTextLines(); 180 | bool thisLineMatch = mFilter.PassFilter(mEditor.GetCurrentLineText().c_str()); 181 | if (!thisLineMatch) 182 | ImGui::Text("---/%3i", mNbFindMatches); 183 | else 184 | { 185 | std::vector allMatchingLinesNumbers = 186 | fplus::find_all_idxs_by( 187 | [this](const std::string& s) { 188 | return mFilter.PassFilter(s.c_str()); 189 | }, 190 | lines); 191 | auto matchNumber = fplus::find_first_idx( 192 | (size_t)mEditor.GetCursorPosition().mLine, 193 | allMatchingLinesNumbers); 194 | if (matchNumber.is_just()) 195 | ImGui::Text("%3i/%3i", (int)matchNumber.unsafe_get_just() + 1, mNbFindMatches); 196 | else 197 | ImGui::Text("Houston, we have a bug"); 198 | ImGui::SameLine(); 199 | } 200 | ImGui::SameLine(); 201 | } 202 | ImGui::SameLine(); 203 | } 204 | 205 | // Perform search down or up 206 | { 207 | bool searchDown = ImGui::SmallButton(ICON_FA_ARROW_DOWN); ImGui::SameLine(); 208 | bool searchUp = ImGui::SmallButton(ICON_FA_ARROW_UP); ImGui::SameLine(); 209 | std::vector linesToSearch; 210 | int currentLine = mEditor.GetCursorPosition().mLine ; 211 | if (searchUp) 212 | { 213 | const auto & lines = mEditor.GetTextLines(); 214 | linesToSearch = fplus::get_segment(0, currentLine, lines); 215 | auto line_idx = fplus::find_last_idx_by( 216 | [this](const std::string &line) { 217 | return mFilter.PassFilter(line.c_str()); 218 | }, 219 | linesToSearch); 220 | if (line_idx.is_just()) 221 | mEditor.SetCursorPosition({(int)line_idx.unsafe_get_just(), 0}, 3); 222 | } 223 | if (searchDown) 224 | { 225 | const auto &lines = mEditor.GetTextLines(); 226 | linesToSearch = fplus::get_segment(currentLine + 1, lines.size(), lines); 227 | auto line_idx = fplus::find_first_idx_by( 228 | [this](const std::string &line) { 229 | return mFilter.PassFilter(line.c_str()); 230 | }, 231 | linesToSearch); 232 | if (line_idx.is_just()) 233 | mEditor.SetCursorPosition({(int)line_idx.unsafe_get_just() + currentLine + 1, 0}, 3); 234 | } 235 | } 236 | 237 | ImGui::SameLine(); 238 | } 239 | 240 | void WindowWithEditor::guiIconBar(VoidFunction additionalGui) 241 | { 242 | auto & editor = mEditor; 243 | static bool canWrite = ! editor.IsReadOnly(); 244 | if (ImGui::Checkbox(ICON_FA_EDIT, &canWrite)) 245 | editor.SetReadOnly(!canWrite); 246 | if (ImGui::IsItemHovered()) 247 | ImGui::SetTooltip("Enable editing this file"); 248 | ImGui::SameLine(); 249 | 250 | ImGui::Checkbox(ICON_FA_BOOK, &mShowLongLinesOverlay); 251 | if (ImGui::IsItemHovered()) 252 | ImGui::SetTooltip("Display wrapped line below the editor"); 253 | 254 | ImGui::SameLine(); 255 | 256 | if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_UNDO, editor.CanUndo() && canWrite, "Undo", true)) 257 | editor.Undo(); 258 | if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_REDO, editor.CanRedo() && canWrite, "Redo", true)) 259 | editor.Redo(); 260 | if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_COPY, editor.HasSelection(), "Copy", true)) 261 | { 262 | editor.Copy(); 263 | } 264 | if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_CUT, editor.HasSelection() && canWrite, "Cut", true)) 265 | { 266 | editor.Cut(); 267 | } 268 | if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_PASTE, (ImGui::GetClipboardText() != nullptr) && canWrite, "Paste", true)) 269 | editor.Paste(); 270 | 271 | // missing icon from font awesome 272 | // if (ImGuiExt::SmallButton_WithEnabledFlag(ICON_FA_SELECT_ALL, ImGui::GetClipboardText() != nullptr, true)) 273 | // editor.PASTE(); 274 | 275 | #ifdef __EMSCRIPTEN__ 276 | ImGui::TextDisabled("?"); 277 | if (ImGui::IsItemHovered()) 278 | ImGui::SetTooltip("Due to browser security limitations, this app will not communicate \n" 279 | "with your computer's clipboard\n" 280 | "Use the \"view on github\" button if you want to copy-paste \n" 281 | "to an external IDE or application"); 282 | ImGui::SameLine(); 283 | #endif 284 | 285 | guiFind(); 286 | 287 | if (additionalGui) 288 | additionalGui(); 289 | 290 | ImGui::NewLine(); 291 | } 292 | 293 | void WindowWithEditor::searchForFirstOccurence(const std::string& search) 294 | { 295 | snprintf(mFilter.InputBuf, 256, "%s", search.c_str()); 296 | mFilter.Build(); 297 | const auto & lines = mEditor.GetTextLines(); 298 | mNbFindMatches = (int)fplus::count_if([this](auto s){ return mFilter.PassFilter(s.c_str());}, lines); 299 | 300 | const auto &linesToSearch = mEditor.GetTextLines(); 301 | auto line_idx = fplus::find_first_idx_by( 302 | [this](const std::string &line) { 303 | return mFilter.PassFilter(line.c_str()); 304 | }, 305 | linesToSearch); 306 | if (line_idx.is_just()) 307 | mEditor.SetCursorPosition({(int)line_idx.unsafe_get_just(), 0}, 3); 308 | } 309 | 310 | extern HelloImGui::RunnerParams runnerParams; // defined in ImGuiManual.cpp 311 | 312 | void WindowWithEditor::searchForFirstOccurenceAndFocusWindow( 313 | const std::string& search, 314 | const std::string& windowName 315 | ) 316 | { 317 | for (auto windowWithEditor: gAllWindowWithEditors) 318 | { 319 | if (windowWithEditor->windowLabel() == windowName) 320 | { 321 | windowWithEditor->searchForFirstOccurence(search); 322 | runnerParams.dockingParams.focusDockableWindow(windowName); 323 | } 324 | } 325 | 326 | } 327 | 328 | 329 | void menuEditorTheme() 330 | { 331 | ImGui::MenuItem("Editor", NULL, false, false); 332 | if (ImGui::MenuItem("Dark palette")) 333 | for (auto editor: gAllEditors) 334 | editor->SetPalette(TextEditor::GetDarkPalette()); 335 | if (ImGui::MenuItem("Light palette")) 336 | for (auto editor: gAllEditors) 337 | editor->SetPalette(TextEditor::GetLightPalette()); 338 | if (ImGui::MenuItem("Retro blue palette")) 339 | for (auto editor: gAllEditors) 340 | editor->SetPalette(TextEditor::GetRetroBluePalette()); 341 | } 342 | -------------------------------------------------------------------------------- /src/source_parse/ImGuiCodeParser.cpp: -------------------------------------------------------------------------------- 1 | #include "ImGuiCodeParser.h" 2 | #include 3 | #include 4 | 5 | using namespace std::literals; 6 | 7 | namespace SourceParse { 8 | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// 10 | // imgui.h parsing below 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | 14 | struct HeaderLineInfo 15 | { 16 | int headerLevel = 0; // 0 means "not a title" 17 | std::string title; 18 | }; 19 | HeaderLineInfo NotAHeader() { return {0, ""};} 20 | 21 | bool isEmptyLine(const NumberedLine & line) 22 | { 23 | return fplus::trim_whitespace(line.second).empty(); 24 | } 25 | 26 | bool isTopCommentLine(const NumberedLine & line) 27 | { 28 | using namespace std::literals; 29 | return fplus::is_prefix_of("// "s, line.second); 30 | } 31 | 32 | bool isInnerCommentLine(const NumberedLine & line) 33 | { 34 | using namespace std::literals; 35 | const std::string& lineCode = line.second; 36 | if (! fplus::is_prefix_of(" "s, lineCode)) 37 | return false; 38 | return fplus::is_prefix_of(std::string("//"), fplus::trim_whitespace_left(lineCode)); 39 | } 40 | 41 | bool isTopHeaderDashes(const NumberedLine & line) 42 | { 43 | std::string headerMark = "//------"; 44 | return fplus::is_prefix_of(headerMark, line.second); 45 | } 46 | 47 | bool isInnerHeaderDashes(const NumberedLine & line) 48 | { 49 | using namespace std::literals; 50 | std::string headerMark = "//------"; 51 | if (! fplus::is_prefix_of(" "s, line.second)) 52 | return false; 53 | return fplus::is_prefix_of(headerMark, fplus::trim_whitespace_left(line.second)); 54 | } 55 | 56 | bool isTopCommentOrHeaderDashes(const NumberedLine & line) 57 | { 58 | return isTopCommentLine(line) || (isTopHeaderDashes(line)); 59 | } 60 | bool isInnerCommentOrHeaderDashes(const NumberedLine & line) 61 | { 62 | return isInnerCommentLine(line) || (isInnerHeaderDashes(line)); 63 | } 64 | 65 | std::string cutStringBeforeParenthesis(const std::string& s) 66 | { 67 | return fplus::take_while([](char c) { return c != '('; }, s); 68 | } 69 | 70 | std::string extractTitleFromCommentLine(const std::string& line) 71 | { 72 | using namespace std::literals; 73 | std::string r = line; 74 | r = fplus::trim_whitespace(r); 75 | r = fplus::replace_tokens("//"s, ""s, r); 76 | r = cutStringBeforeParenthesis(r); 77 | return r; 78 | } 79 | 80 | 81 | 82 | 83 | // in imgui.h, H1 titles look like this: 84 | /* 85 | //----------------------------------------------------------------------------- 86 | // ImGuiIO <-- this is the title line 87 | // Communicate most settings and inputs/outputs to Dear ImGui using this structure. 88 | // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. 89 | //----------------------------------------------------------------------------- 90 | */ 91 | HeaderLineInfo isH1Header(const NumberedLines& numberedLines, size_t idxLine) 92 | { 93 | using namespace std::literals; 94 | std::string line = numberedLines[idxLine].second; 95 | 96 | if (!isTopCommentLine(numberedLines[idxLine])) 97 | return NotAHeader(); 98 | if (idxLine < 1) 99 | return NotAHeader(); 100 | if (! isTopHeaderDashes(numberedLines[idxLine - 1])) 101 | return NotAHeader(); 102 | if ((idxLine > 2) && (isTopCommentLine(numberedLines[idxLine - 2]))) 103 | return NotAHeader(); 104 | auto remainingLines = fplus::drop(idxLine, numberedLines); 105 | auto followingCommentLines = fplus::take_while(isTopCommentOrHeaderDashes, remainingLines); 106 | auto lastCommentLine = followingCommentLines.back(); 107 | bool isHeader = isTopHeaderDashes(lastCommentLine); 108 | if (!isHeader) 109 | return NotAHeader(); 110 | 111 | std::string title = extractTitleFromCommentLine(line); 112 | return {1, title}; 113 | }; 114 | 115 | 116 | // H2 titles have 2 types: 117 | // Case 1: Several left aligned single line comments (with an empty line before) 118 | // followed by a struct or enum 119 | // -> title = struct or enum name 120 | // See example below 121 | /* 122 | 123 | // Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. 124 | // The callback function should return 0 by default. 125 | struct ImGuiInputTextCallbackData 126 | { 127 | */ 128 | // Case 2: Several left aligned single line comments (with an empty line before) 129 | // -> title = first line, without parenthesis content 130 | // See example below 131 | /* 132 | 133 | // Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) 134 | // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! 135 | // In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. 136 | // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. 137 | typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for 138 | */ 139 | HeaderLineInfo isH2Header(const NumberedLines& numberedLines, size_t idxLine) 140 | { 141 | using namespace std::literals; 142 | std::string line = numberedLines[idxLine].second; 143 | 144 | if (!isTopCommentLine(numberedLines[idxLine])) 145 | return NotAHeader(); 146 | if (isTopHeaderDashes(numberedLines[idxLine])) 147 | return NotAHeader(); 148 | if (idxLine >= 1 ) 149 | { 150 | auto previousLine = numberedLines[idxLine - 1]; 151 | if (!isEmptyLine(previousLine)) 152 | return NotAHeader(); 153 | } 154 | 155 | auto remainingLines = fplus::drop(idxLine, numberedLines); 156 | auto followingCommentLines = fplus::take_while(isTopCommentLine, remainingLines); 157 | 158 | std::string title = ""; 159 | size_t nextSourceLineIdx = idxLine + followingCommentLines.size(); 160 | // See if a struct or enum is defined after the comment lines 161 | if (nextSourceLineIdx < numberedLines.size() - 1) 162 | { 163 | std::string nextSourceLine = numberedLines[nextSourceLineIdx].second; 164 | std::string nextSourceLine2 = numberedLines[nextSourceLineIdx + 1].second; 165 | if (fplus::is_prefix_of("struct "s, nextSourceLine) && fplus::is_prefix_of("{"s, nextSourceLine2)) 166 | title = nextSourceLine; 167 | if (fplus::is_prefix_of("enum "s, nextSourceLine) && fplus::is_prefix_of("{"s, nextSourceLine2)) 168 | title = nextSourceLine; 169 | } 170 | if (title.empty()) 171 | title = extractTitleFromCommentLine(line); 172 | 173 | return {2, title}; 174 | }; 175 | 176 | 177 | // H3 headers look like this (note the spacing at the beginning of lines; since we 178 | // are in a namespace or in a struct. 179 | /* 180 | //------------------------------------------------------------------ 181 | // Configuration (fill once) // Default value 182 | //------------------------------------------------------------------ 183 | */ 184 | HeaderLineInfo isH3Header(const NumberedLines& numberedLines, size_t idxLine) 185 | { 186 | std::string line = numberedLines[idxLine].second; 187 | 188 | if (!isInnerCommentLine(numberedLines[idxLine])) 189 | return NotAHeader(); 190 | 191 | if (idxLine < 1) 192 | return NotAHeader(); 193 | if (! isInnerHeaderDashes(numberedLines[idxLine - 1])) 194 | return NotAHeader(); 195 | auto remainingLines = fplus::drop(idxLine, numberedLines); 196 | auto followingCommentLines = fplus::take_while(isInnerCommentLine, remainingLines); 197 | auto lastCommentLine = followingCommentLines.back(); 198 | 199 | bool isHeader = isInnerHeaderDashes(lastCommentLine); 200 | if (!isHeader) 201 | return NotAHeader(); 202 | 203 | std::string title = fplus::drop(3, fplus::trim_whitespace_left(line)); 204 | return {3, title}; 205 | }; 206 | 207 | 208 | // H4 headers look like this (note the empty line before) 209 | /* 210 | 211 | // Arguments for the different callback events 212 | // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. 213 | // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. 214 | */ 215 | HeaderLineInfo isH4Header(const NumberedLines& numberedLines, size_t idxLine) 216 | { 217 | const std::string& line = numberedLines[idxLine].second; 218 | 219 | if (!isInnerCommentLine(numberedLines[idxLine])) 220 | return NotAHeader(); 221 | if (idxLine < 1) 222 | return NotAHeader(); 223 | //if (isInnerCommentLine(numberedLines[idxLine - 1])) 224 | //return NotAHeader(); 225 | std::string trimmedPreviousLine = fplus::trim_whitespace(numberedLines[idxLine - 1].second); 226 | if (!trimmedPreviousLine.empty()) 227 | return NotAHeader(); 228 | 229 | bool hasHeaderMark = isInnerHeaderDashes(numberedLines[idxLine]); 230 | if (hasHeaderMark) 231 | return NotAHeader(); 232 | 233 | std::string title = fplus::drop(3, fplus::trim_whitespace_left(line)); 234 | return {4, title}; 235 | }; 236 | 237 | 238 | auto isHeader = [](const NumberedLines& numberedLines, size_t idxLine) 239 | -> HeaderLineInfo 240 | { 241 | auto r = isH1Header(numberedLines, idxLine); 242 | if (r.headerLevel > 0) 243 | return r; 244 | 245 | r = isH2Header(numberedLines, idxLine); 246 | if (r.headerLevel > 0) 247 | return r; 248 | 249 | r = isH3Header(numberedLines, idxLine); 250 | if (r.headerLevel > 0) 251 | return r; 252 | 253 | r = isH4Header(numberedLines, idxLine); 254 | return r; 255 | }; 256 | 257 | // 258 | // Parse imgui.h and find H1 / H2 / H3 / H4 titles 259 | // 260 | LinesWithTags findImGuiHeaderDoc(const std::string &sourceCode) 261 | { 262 | using namespace std::literals; 263 | 264 | auto lines = fplus::split('\n', true, sourceCode); 265 | NumberedLines numberedLines = fplus::enumerate(lines); 266 | 267 | // imgui.h file content becomes indexable after the first H1 title ("Header mess") 268 | NumberedLines indexableLines = fplus::drop_while( 269 | [](const auto & line) { return ! isTopHeaderDashes(line); }, 270 | numberedLines 271 | ); 272 | 273 | auto cleanTitle = [](const HeaderLineInfo &headerLineInfo) { 274 | // cut title after first sentence ends (i.e after '.', '?', '!') 275 | std::string title = fplus::take_while( 276 | [](auto c) { 277 | return c != '.' && c != '!' && c != '?'; 278 | }, 279 | headerLineInfo.title); 280 | title = fplus::trim_whitespace(title); 281 | // remove after '(' except if the title is all in () 282 | if (!title.empty() && title[0] != '(') 283 | title = fplus::take_while([](auto c) { return c != '('; }, title); 284 | 285 | size_t maxTitleLength = 60; 286 | if (title.size() > maxTitleLength) 287 | title = fplus::take(maxTitleLength, title) + "..."; 288 | 289 | // title = std::to_string(headerLineInfo.headerLevel) + "-"s + title; 290 | 291 | // remove [SECTION] 292 | title = fplus::replace_tokens("[SECTION]"s, ""s, title); 293 | 294 | return title; 295 | }; 296 | 297 | LinesWithTags linesWithTags; 298 | for (size_t i = 0; i < indexableLines.size(); ++i) 299 | { 300 | const auto & lineWithNumber = indexableLines[i]; 301 | 302 | auto headerLineInfo = isHeader(indexableLines, i); 303 | if (headerLineInfo.headerLevel > 0) 304 | { 305 | int lineNumber = (int)lineWithNumber.first; 306 | linesWithTags.push_back({ 307 | lineNumber, 308 | cleanTitle(headerLineInfo), 309 | headerLineInfo.headerLevel}); 310 | } 311 | } 312 | return linesWithTags; 313 | } 314 | 315 | 316 | AnnotatedSource ReadImGuiHeaderDoc() 317 | { 318 | std::string sourcePath = "imgui/imgui.h"; 319 | AnnotatedSource r; 320 | r.source = ReadSource(sourcePath); 321 | r.linesWithTags = findImGuiHeaderDoc(r.source.sourceCode); 322 | return r; 323 | } 324 | 325 | 326 | //////////////////////////////////////////////////////////////////////////////////////////////////// 327 | // imgui.cpp parsing below 328 | //////////////////////////////////////////////////////////////////////////////////////////////////// 329 | 330 | std::string lowerCaseExceptFirstLetter(const std::string &s) 331 | { 332 | auto r = fplus::to_upper_case(fplus::take(1, s)) + fplus::to_lower_case(fplus::drop(1, s)); 333 | return r; 334 | } 335 | 336 | 337 | 338 | std::string upperCaseQAndA(const std::string &s) 339 | { 340 | if (fplus::take(3, s) == "Q&a") 341 | return "Q&A"s + fplus::drop(3, s); 342 | else 343 | return s; 344 | } 345 | 346 | std::string lowerCaseTitle(const std::string& s) 347 | { 348 | auto words = fplus::split(' ', true, s); 349 | words = fplus::transform(lowerCaseExceptFirstLetter, words); 350 | auto title = fplus::join(" "s, words); 351 | title = upperCaseQAndA(title); 352 | return title; 353 | } 354 | 355 | 356 | LinesWithTags findImGuiCppDoc(const std::string &sourceCode) 357 | { 358 | LinesWithTags r; 359 | /* 360 | H1 titles look like this 361 | MISSION STATEMENT 362 | ================= 363 | H2 titles look like this 364 | READ FIRST 365 | ---------- 366 | */ 367 | auto lines = fplus::split('\n', true, sourceCode); 368 | 369 | // Given two lines, we can check whether they are a header 370 | // and return 0 (not header) , 1 ("H1") or 2 ("H2") 371 | auto isHeaderLine = [](const std::pair &linePair) { 372 | int headerLevel = 0; 373 | if (fplus::is_prefix_of("===="s, fplus::trim_whitespace(linePair.second))) 374 | headerLevel = 1; 375 | if (fplus::is_prefix_of("----"s, fplus::trim_whitespace(linePair.second))) 376 | headerLevel = 2; 377 | return headerLevel; 378 | }; 379 | 380 | for (auto idx_lines : fplus::enumerate(fplus::overlapping_pairs(lines))) 381 | { 382 | int lineNumber = (int)idx_lines.first + 1; 383 | auto line_pair = idx_lines.second; 384 | int headerLevel = isHeaderLine(line_pair); 385 | std::string tag = fplus::trim_whitespace(line_pair.first); 386 | tag = lowerCaseTitle(tag); 387 | if (headerLevel > 0) 388 | r.push_back({lineNumber, tag, headerLevel}); 389 | } 390 | return r; 391 | } 392 | 393 | AnnotatedSource ReadImGuiCppDoc() 394 | { 395 | std::string sourcePath = "imgui/imgui.cpp"; 396 | AnnotatedSource r; 397 | r.source = ReadSource(sourcePath); 398 | r.linesWithTags = findImGuiCppDoc(r.source.sourceCode); 399 | return r; 400 | } 401 | 402 | 403 | 404 | } --------------------------------------------------------------------------------