├── .github └── workflows │ ├── msys2.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── .gitmodules ├── 3rdparty └── fa │ ├── IconsFontAwesome6.h │ ├── IconsMaterialDesign.h │ ├── MaterialIcons-Regular.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-Medium.ttf │ ├── Roboto-Regular.ttf │ ├── fa-regular-400.ttf │ └── fa-solid-900.ttf ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cmake ├── fa.cmake ├── freetype.cmake ├── glfw.cmake ├── imgui.cmake └── stb.cmake ├── doc └── screen1.png ├── release-linux ├── src ├── CMakeLists.txt ├── app.rc ├── binding_eval.h ├── binding_input.h ├── binding_property.cpp ├── binding_property.h ├── binding_type.h ├── cpp_parser.h ├── cppgen.cpp ├── cppgen.h ├── glfw_cursor.h ├── icon.ico ├── icon.png ├── imrad.cpp ├── imrad.h ├── node_container.cpp ├── node_container.h ├── node_extra.cpp ├── node_extra.h ├── node_standard.cpp ├── node_standard.h ├── node_window.cpp ├── node_window.h ├── stx.h ├── ui_about_dlg.cpp ├── ui_about_dlg.h ├── ui_binding.cpp ├── ui_binding.h ├── ui_class_wizard.cpp ├── ui_class_wizard.h ├── ui_combo_dlg.cpp ├── ui_combo_dlg.h ├── ui_error_box.cpp ├── ui_error_box.h ├── ui_explorer.cpp ├── ui_explorer.h ├── ui_horiz_layout.cpp ├── ui_horiz_layout.h ├── ui_input_name.cpp ├── ui_input_name.h ├── ui_message_box.cpp ├── ui_message_box.h ├── ui_new_field.cpp ├── ui_new_field.h ├── ui_settings_dlg.cpp ├── ui_settings_dlg.h ├── ui_table_cols.cpp ├── ui_table_cols.h ├── uicontext.cpp ├── uicontext.h ├── utils.cpp └── utils.h ├── style ├── dash.png ├── icon-100.png ├── icon-40.png └── test.ini └── template ├── android ├── AndroidManifest.xml ├── CMakeLists.txt ├── MainActivity.java └── main.cpp └── glfw └── main.cpp /.github/workflows/msys2.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml 3 | name: msys2 4 | 5 | on: 6 | push: 7 | branches: [ "main" ] 8 | pull_request: 9 | 10 | env: 11 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | strategy: 17 | matrix: 18 | platform: [windows-latest] # self-hosted 19 | sys: [ clang64, ucrt64, mingw32, mingw64 ] # clangarm64 20 | 21 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 22 | # You can convert this to a matrix build if you need cross-platform coverage. 23 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 24 | runs-on: ${{matrix.platform}} 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | submodules: recursive 30 | 31 | - uses: msys2/setup-msys2@v2 32 | if: ${{ matrix.sys == 'clang32' || matrix.sys == 'clang64' || matrix.sys == 'ucrt64' }} 33 | with: 34 | msystem: ${{matrix.sys}} 35 | release: true 36 | pacboy: >- 37 | cmake:p 38 | clang:p 39 | ninja:p 40 | 41 | - uses: msys2/setup-msys2@v2 42 | if: ${{ matrix.sys == 'mingw32' || matrix.sys == 'mingw64' }} 43 | with: 44 | msystem: ${{matrix.sys}} 45 | release: true 46 | pacboy: >- 47 | cmake:p 48 | gcc:p 49 | ninja:p 50 | 51 | - name: Configure CMake 52 | shell: msys2 {0} 53 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 54 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 55 | run: cmake -B build_${{matrix.sys}}_${{env.BUILD_TYPE}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 56 | 57 | - name: Build 58 | shell: msys2 {0} 59 | # Build your program with the given configuration 60 | run: cmake --build build_${{matrix.sys}}_${{env.BUILD_TYPE}} --config ${{env.BUILD_TYPE}} 61 | 62 | - name: Test 63 | shell: msys2 {0} 64 | working-directory: build_${{matrix.sys}}_${{env.BUILD_TYPE}} 65 | # Execute tests defined by the CMake configuration. 66 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 67 | run: ctest -C ${{env.BUILD_TYPE}} 68 | 69 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | submodules: recursive 24 | 25 | - name: apt install 26 | run: | 27 | sudo apt update 28 | sudo apt install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libgtk-3-dev libsystemd-dev libwebp-dev libzstd-dev 29 | 30 | 31 | - name: Configure CMake 32 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 33 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 34 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 35 | 36 | - name: Build 37 | # Build your program with the given configuration 38 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel 8 39 | 40 | - name: Test 41 | working-directory: ${{github.workspace}}/build 42 | # Execute tests defined by the CMake configuration. 43 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 44 | run: ctest -C ${{env.BUILD_TYPE}} 45 | 46 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml 3 | name: windows 4 | 5 | on: 6 | push: 7 | branches: [ "main" ] 8 | pull_request: 9 | 10 | env: 11 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 17 | # You can convert this to a matrix build if you need cross-platform coverage. 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | runs-on: windows-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: recursive 25 | 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Test 36 | working-directory: ${{github.workspace}}/build 37 | # Execute tests defined by the CMake configuration. 38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 39 | run: ctest -C ${{env.BUILD_TYPE}} 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /install/ 3 | /build-2022/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/glfw"] 2 | path = 3rdparty/glfw 3 | url = https://github.com/glfw/glfw.git 4 | branch = master 5 | [submodule "3rdparty/imgui"] 6 | path = 3rdparty/imgui 7 | url = https://github.com/ocornut/imgui.git 8 | branch = docking 9 | [submodule "3rdparty/stb"] 10 | path = 3rdparty/stb 11 | url = https://github.com/nothings/stb.git 12 | branch = master 13 | [submodule "3rdparty/nativefiledialog"] 14 | path = 3rdparty/nativefiledialog 15 | url = https://github.com/btzy/nativefiledialog-extended.git 16 | branch = master 17 | [submodule "3rdparty/freetype"] 18 | path = 3rdparty/freetype 19 | url = https://github.com/freetype/freetype.git 20 | -------------------------------------------------------------------------------- /3rdparty/fa/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /3rdparty/fa/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/Roboto-Bold.ttf -------------------------------------------------------------------------------- /3rdparty/fa/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/Roboto-Medium.ttf -------------------------------------------------------------------------------- /3rdparty/fa/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/Roboto-Regular.ttf -------------------------------------------------------------------------------- /3rdparty/fa/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/fa-regular-400.ttf -------------------------------------------------------------------------------- /3rdparty/fa/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/3rdparty/fa/fa-solid-900.ttf -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(imrad) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) # linux: don't export all symbols in .so 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) # linux: use -fPIC on .a so they can be used by .so 7 | 8 | # make use of runtime directory 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 10 | 11 | set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/latest") 12 | 13 | if(MSVC) 14 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 15 | add_definitions(-D_SCL_SECURE_NO_WARNINGS) 16 | add_definitions(-D_USE_MATH_DEFINES) 17 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 18 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 19 | else() 20 | add_definitions(-DGL_SILENCE_DEPRECATION=1) 21 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer") 22 | # Disable ASAN on Debug builds as it may cause issues on arm64 23 | # If you are getting DEADLY ADDRESS error use: sudo sysctl vm.mmap_rnd_bits=28 24 | # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") 25 | if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") 26 | find_library(MISC_FRAMEWORKS UniformTypeIdentifiers) 27 | set(CMAKE_OSX_ARCHITECTURES "arm64") 28 | elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") 29 | find_package(PkgConfig REQUIRED) 30 | pkg_check_modules(MISC REQUIRED gtk+-3.0 glib-2.0) 31 | set(MISC_FRAMEWORKS "${MISC_STATIC_LIBRARIES}") 32 | endif() 33 | endif() 34 | 35 | # before includes 36 | install( 37 | DIRECTORY "template/" 38 | DESTINATION "template" 39 | ) 40 | install( 41 | DIRECTORY "style/" 42 | DESTINATION "style" 43 | ) 44 | 45 | include(cmake/glfw.cmake) 46 | include(cmake/imgui.cmake) 47 | include(cmake/fa.cmake) 48 | include(cmake/stb.cmake) 49 | include(cmake/freetype.cmake) 50 | 51 | include(ExternalProject) 52 | set(NFD_PREFIX 3rdparty/nativefiledialog) 53 | set(NFD_BUILD_TESTS OFF) 54 | ExternalProject_Add(nativefiledialog 55 | PREFIX ${NFD_PREFIX} 56 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${NFD_PREFIX} 57 | INSTALL_COMMAND "" 58 | CMAKE_CACHE_ARGS 59 | -DCMAKE_OSX_ARCHITECTURES:STRING=arm64 60 | ) 61 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${NFD_PREFIX}/src/include) 62 | link_directories(${CMAKE_CURRENT_BINARY_DIR}/${NFD_PREFIX}/src/nativefiledialog-build/src/) 63 | 64 | add_subdirectory(src) 65 | 66 | file(COPY 67 | "${CMAKE_CURRENT_SOURCE_DIR}/template" 68 | DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/bin" 69 | ) 70 | file(COPY 71 | "${CMAKE_CURRENT_SOURCE_DIR}/style" 72 | DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/bin" 73 | ) 74 | 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | TLDR: The expected, merge into main, reference issues in PRs. 2 | 3 | ## Documentation 4 | 5 | Out of source (non-inline) documentation such as design diagrams should be added to the `docs` folder as should images for readmes or anything else that is not within the `docs` folder. 6 | 7 | ## Code 8 | All source code should be in C++ following best practices guidelines where possible (such as avoiding `new` and `delete`). 9 | Indentation should be done with spaces not tabs and a trailing new line should be added to the end of each file (this makes the diffs cleaner). 10 | 11 | 12 | 13 | 14 | ## PRs 15 | 16 | PRs should have a base branch of (and merge into) main with no merge conflicts. Please keep your branch as up to date as possible. 17 | 18 | Your PR should include the issue number (e.g. "closes #1234"), there is no need to do so in commit messages. 19 | 20 | ## Code of Conduct 21 | Our code of conduct is "be kind to eachother" unless otherwise stated. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![img](https://github.com/tpecholt/imrad/actions/workflows/windows.yml/badge.svg) 2 | ![img](https://github.com/tpecholt/imrad/actions/workflows/ubuntu.yml/badge.svg) 3 | ![img](https://github.com/tpecholt/imrad/actions/workflows/msys2.yml/badge.svg) 4 | 5 | # ImRAD 6 |
7 | logo 8 |
9 |
10 | 11 | ImRAD is a GUI builder for the ImGui library. It generates and parses C++ code which can be directly used in your application. 12 | 13 | Unlike other tools ImRAD can be used for continuous modification of the generated UI. Data binding, events and even manual 14 | UI code additions are well supported. 15 | 16 | ImRAD runs on Windows, Linux and MacOS. 17 | 18 |
19 | 20 | ![screen1](https://github.com/user-attachments/assets/952b400d-ba55-4015-8e47-aa7438af8956) 21 | 22 | *Take a note of the Toolbar section:* 23 | * *Configurable window style which is stored in an INI file. It contains definitions of colors, style variables and fonts. The example uses stock "Dark" style* 24 | * *Dialog units option which can also be set to DPI aware pixels useful when designing android apps* 25 | * *Code Preview for quick checks. Full generated code is saved in .h/cpp files* 26 | * *Class wizard allows to manage member variables of the generated class* 27 | * *Horizontal layout helper using invisible table. This is an alternative to the box layout functionality.* 28 | 29 | *Designed window shows:* 30 | * *Stretchable spacer between "Available fields" text and "show all" checkbox and between dialog buttons. ImRAD comes with box layout support not available in standard ImGui* 31 | * *Negative size_x/y support which makes the table widget expand vertically keeping 48px gap from the bottom edge of the dialog* 32 | * *Selectable widget bindings such as `{vars[i].first}`. Bind expressions will be put in the generated code showing content of the `vars` array* 33 | 34 | *Properties window contains:* 35 | * *Table.rowCount binding set to the `vars.size()` expression. This will generate code with a for loop over table rows. One-way or two-way binding is supported by most properties* 36 | 37 | *Events window* 38 | * *Allows to generate handlers for all kinds of events such as button click, window appearance, input text character filter etc.* 39 | 40 | # Main Features 41 | 42 | * Supports designing all kinds of windows 43 | * Floating windows, popups and modal popups. These are ImGui backend independent 44 | * MainWindow with GLFW integration. ImRAD generates GLFW calls which will synchronize ImGui window with its OS window (title bar, resizability flags, autosize etc.) 45 | * Activity. This is an undecorated window which fills the entire viewport area. Only one activity can be shown at the time. Used mainly for Android apps 46 | * contains a GLFW template for generating generic `main.cpp` 47 | * contains an android template for generating generic `MainActivity.java`, `AndroidManifest.xml`, `main.cpp` and `CMakeLists.txt` 48 | 49 | * Supports wide range of widgets 50 | 51 | * basic widgets like `Text`, `Checkbox`, `Combo`, `Button`, `Slider`, `ColorEdit` etc. 52 | * container widgets like `Child`, `Table`, `CollapsingHeader`, `TreeNode`, `TabBar`, 53 | * more exotic widgets such as `Splitter` and `DockSpace` 54 | * `MenuBar` and context menu editing 55 | * `CustomWidget` (a placeholder to user code) 56 | 57 | * Generates layout using `SameLine`/`Spacing`/`NextColumn` instead of absolute positioning 58 | 59 | * this ensures widgets respect item spacing and frame padding in a consistent way 60 | * there is a clear relationship between parent - child widget as well as children ordering which is important for container widgets like Table 61 | * alternative positioning where widget keeps relative distance from a selected parent's corner is available and can be used for overlay widgets 62 | 63 | * Supports box layout 64 | 65 | * powerful and simple to use layout mechanism implemented on top of ImGui functionality 66 | * stretch any sizeable widget in horizontal or vertical direction 67 | * insert spacers to achieve alignment 68 | * alternatively you can use Table Layout Helper to generate horizontal layout using invisible Table 69 | 70 | * Supports property binding 71 | 72 | * because ImGui is an immediate mode GUI library widget state like input text or combobox items must be set at the time of drawing from within the generated code through property binding. 73 | * class variables can be managed through simple class wizard or from binding dialog 74 | * using property binding generated UI becomes dynamic and yet it can still be designed at the same time 75 | 76 | * Supports generating event handlers and other support code 77 | 78 | * for example modal dialog will automatically generate an `OpenPopup` member function with a lambda callback called when the dialog is closed 79 | * event handlers allow event handling user code to be separated from the generated part so the designer still works 80 | 81 | * Generated code is delimited by comment markers and user can insert additional code around and continue to use ImRAD at the same time 82 | 83 | * this can be used to f.e. to call draw dependent popups or to calculate some variables 84 | * it is also possible to use `CustomWidget` which will delegate to a user callback 85 | 86 | * Target window style is fully configurable 87 | * apart from default styles provided by ImGui user can define new style and save it as an INI file under the `style` folder. Colors, style variables and used fonts can all be configured. 88 | * ImRAD will follow chosen style settings when designing your UI 89 | * stored style can be loaded in your app by using `LoadStyle` call 90 | 91 | * Generated code is ready to use in your project and depends only on ImGui library and one accompanying header file (imrad.h) 92 | 93 | * some features such as MainWindow or Image widget require GLFW dependency. Compile your code with `IMRAD_WITH_GLFW` to activate it 94 | * currently Image widget requires stb library as well. Compile your code with `IMRAD_WITH_STB` or supply your own `LoadTextureFromFile()` 95 | * in generated code `ImRad::Format` delegates to `std::format` by default but when requested popular `fmt` library can be used instead by defining `IMRAD_WITH_FMT`. If neither is available simple formatting routine which skips formatting flags will be used. 96 | 97 | * ImRAD tracks changes to the opened files so files can be designed in ImRAD and edited in your IDE of choice at the same time 98 | 99 | # License 100 | 101 | * ImRAD source code is licensed under the GPL license 102 | * Any code generated by the tool is excluded from GPL and can be included in any project either open-source or commercial and it's up to the user to decide the license for it. 103 | * Additionally since `imrad.h` is used by the generated code it is also excluded from the GPL license 104 | 105 | # Download binaries 106 | 107 | For up-to date version clone & build the repository using CMake. Don't forget to fetch submodules in the 3rdparty directory too. 108 | 109 | Somewhat older version can be downloaded from [Releases](https://github.com/tpecholt/imrad/releases) 110 | 111 | # How to build 112 | 113 | ## Windows 114 | 1. Use CMake GUI to configure and generate sln file 115 | 2. Open the generated sln file in Visual Studio 2017 or newer (you can use Express or Community editions which are downloadable for free) 116 | 3. Build the INSTALL project in Release mode. It may require running VS with admin rights 117 | 4. If you didn't alter CMAKE_INSTALL_PREFIX variable ImRAD will be installed into *C:\Program Files\imrad\latest* 118 | 119 | ## Linux, MacOS 120 | 1. *Ubuntu*: Due to the GTK FileOpen dialog dependency you need to `apt install` these packages first (exact list depends on your OS): 121 | 122 | *pkg-config gtk-3-dev libsystemd-dev libwebp-dev libzstd-dev* 123 | 124 | 3. Run the provided installation script (script parameter is the ImRAD version you want to name the folder) 125 | 126 | ```./release-linux 0.8``` 127 | 128 | 4. ImRAD will be installed into *./install/imrad-0.8* 129 | 130 | # How to debug 131 | 132 | ## Windows 133 | 134 | 1. Build the INSTALL target in VS as described above 135 | 2. Set imrad as startup project, set its working directory to the installed folder 136 | 3. Debug & Run 137 | 138 | # How to use generated code 139 | 140 | * Create a new project (C++17 and up) 141 | 142 | * Based on your setup set project-wide preprocessor defines `IMRAD_WITH_GLFW`/`IMRAD_WITH_STB`/ (`STBI_WINDOWS_UTF8`). This activates additional functionality such as Main Window or Image widget. 143 | 144 | * On MSVC you need to define `/Zc:__cplusplus` as well otherwise C++ version detection in imrad.h won't work. 145 | 146 | * Add all generated code. Generated code #includes `imrad.h` so you need to set an include directory and point it to the include folder in the ImRAD installation folder. 147 | 148 | * Add your `main.cpp`. The easiest way is to auto-generate it from ImRAD (New File menu). Then call `Draw()` methods of all generated UI classes from your UI loop. 149 | 150 | * If you supply your own `main.cpp` or the one downloadable from ImGui github repository make sure you define `ImRad::IOUserData` variable and pass it to `ImGui::GetIO().UserData`. This structure is to exchange various information such as current dpi scaling, active InputText IME type or reduced display size dimensions for android apps. 151 | 152 | * Finally add ImGui and GLFW dependencies. Whether you build it as separate libraries or directly add to your project is up to you. Stb and fmt dependencies are optional. 153 | 154 | # Tutorials 155 | 156 | Please check [wiki](https://github.com/tpecholt/imrad/wiki) for tutorials and more detailed content. There is a lot to discover! 157 | 158 | # Sponsorship 159 | 160 | Development of ImRAD happens in my free time outside of my regular job, yoga, badminton, volunteering and other activities. 161 | 162 | If you like the tool and want to support its further development please do. If you use it regularly you can even setup a monthly donation if it's convenient for you. 163 | 164 | [![kofi_button_black](https://github.com/user-attachments/assets/67a4a74a-0c06-425a-be98-55da20f05181)](https://ko-fi.com/tope99) 165 | 166 | # Credits 167 | 168 | Design and implementation - [Tomas Pecholt](https://github.com/tpecholt) 169 | 170 | Thanks to [Omar Cornut for Dear ImGui](https://github.com/ocornut/imgui) 171 | -------------------------------------------------------------------------------- /cmake/fa.cmake: -------------------------------------------------------------------------------- 1 | set(FA_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/fa) 2 | 3 | add_library(fa INTERFACE) 4 | 5 | target_include_directories(fa 6 | INTERFACE 7 | ${FA_INCLUDE_DIR}) 8 | 9 | install(FILES 10 | "${FA_INCLUDE_DIR}/fa-solid-900.ttf" 11 | "${FA_INCLUDE_DIR}/fa-regular-400.ttf" 12 | "${FA_INCLUDE_DIR}/MaterialIcons-Regular.ttf" 13 | "${FA_INCLUDE_DIR}/Roboto-Medium.ttf" 14 | "${FA_INCLUDE_DIR}/Roboto-Regular.ttf" 15 | "${FA_INCLUDE_DIR}/Roboto-Bold.ttf" 16 | DESTINATION "style/") -------------------------------------------------------------------------------- /cmake/freetype.cmake: -------------------------------------------------------------------------------- 1 | option(FT_DISABLE_BROTLI ON) 2 | option(FT_DISABLE_BZIP2 ON) 3 | option(FT_DISABLE_HARFBUZZ ON) 4 | option(FT_DISABLE_PNG ON) 5 | option(FT_DISABLE_ZLIB ON) 6 | 7 | # Force arm64 on macOS 8 | if(APPLE) 9 | set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "Build architectures for macOS" FORCE) 10 | endif() 11 | 12 | add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/freetype EXCLUDE_FROM_ALL) 13 | -------------------------------------------------------------------------------- /cmake/glfw.cmake: -------------------------------------------------------------------------------- 1 | option(GLFW_BUILD_DOCS OFF) 2 | option(GLFW_BUILD_EXAMPLES OFF) 3 | option(GLFW_BUILD_TESTS OFF) 4 | option(GLFW_INSTALL OFF) 5 | add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/glfw) 6 | 7 | set(GLFW_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/glfw/include) 8 | set(GLFW_DEFINITIONS -DGLFW_INCLUDE_NONE) 9 | -------------------------------------------------------------------------------- /cmake/imgui.cmake: -------------------------------------------------------------------------------- 1 | set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/3rdparty/imgui) 2 | file(GLOB IMGUI_SOURCES ${IMGUI_DIR}/*.cpp) 3 | file(GLOB IMGUI_MISC ${IMGUI_DIR}/misc/cpp/*.cpp) 4 | file(GLOB IMGUI_FT ${IMGUI_DIR}/misc/freetype/*.cpp) 5 | file(GLOB IMGUI_HEADERS ${IMGUI_DIR}/*.h) 6 | 7 | add_library(imgui STATIC 8 | ${IMGUI_SOURCES} 9 | ${IMGUI_HEADERS} 10 | ${IMGUI_MISC} 11 | ${IMGUI_FT} 12 | ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp 13 | ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp 14 | ) 15 | 16 | #add_definitions(-DIMGUI_DISABLE_OBSOLETE_FUNCTIONS) 17 | add_definitions(-DIMGUI_ENABLE_FREETYPE) 18 | #add_definitions(-DIMGUI_USE_WCHAR32) 19 | 20 | include_directories( 21 | ${IMGUI_DIR} 22 | ${OPENGL_INCLUDE_DIR} 23 | ${GLFW_INCLUDE_DIR}) 24 | 25 | target_link_libraries(imgui 26 | freetype 27 | ${OPENGL_LIBRARIES} 28 | ${GLFW_LIBRARIES}) 29 | 30 | set_target_properties(imgui PROPERTIES LINKER_LANGUAGE CXX) 31 | 32 | #add_custom_command(TARGET imgui POST_BUILD 33 | # COMMAND ${CMAKE_COMMAND} -E copy 34 | # "${IMGUI_DIR}/misc/fonts/Roboto-Medium.ttf" 35 | # "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/style/" 36 | #) 37 | -------------------------------------------------------------------------------- /cmake/stb.cmake: -------------------------------------------------------------------------------- 1 | set(STB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/stb) 2 | 3 | add_library(stb INTERFACE) 4 | 5 | target_include_directories(stb 6 | INTERFACE 7 | ${STB_INCLUDE_DIR}) 8 | -------------------------------------------------------------------------------- /doc/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/doc/screen1.png -------------------------------------------------------------------------------- /release-linux: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$#" -ne 1 ]; then 3 | echo "please specify release version" 4 | exit 1 5 | fi 6 | 7 | rm -rf install 8 | rm -rf build 9 | mkdir install 10 | mkdir build 11 | cd build 12 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install .. 13 | make -j8 install 14 | if [ "$?" -ne 0 ]; then 15 | exit 1 16 | fi 17 | cd ../install 18 | mv latest imrad-$1 19 | zip -r imrad-$1-linux64.zip . 20 | 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (imrad) 2 | 3 | file(GLOB_RECURSE SRC "*.cpp" "*.h") 4 | 5 | if (WIN32) 6 | set(SRC ${SRC} "app.rc") 7 | endif() 8 | 9 | if (MSVC) 10 | string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus") 11 | string(APPEND CMAKE_CXX_FLAGS " /utf-8") 12 | endif() 13 | 14 | add_executable(imrad WIN32 15 | ${SRC} 16 | ) 17 | 18 | set(OpenGL_GL_PREFERENCE "GLVND") 19 | find_package(OpenGL REQUIRED) 20 | 21 | add_definitions(-DIMRAD_WITH_GLFW) 22 | add_definitions(-DIMRAD_WITH_STB) 23 | add_definitions(-DSTBI_WINDOWS_UTF8) 24 | 25 | target_link_libraries(imrad 26 | fa 27 | glfw 28 | imgui 29 | nfd 30 | stb 31 | ${OPENGL_LIBRARIES} 32 | ${CMAKE_DL_LIBS} 33 | ${MISC_FRAMEWORKS} 34 | ) 35 | 36 | add_dependencies(imrad nativefiledialog) 37 | 38 | install(FILES "imrad.h" DESTINATION "include/") 39 | install(TARGETS imrad DESTINATION ".") 40 | -------------------------------------------------------------------------------- /src/app.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "icon.ico" -------------------------------------------------------------------------------- /src/binding_eval.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "binding_property.h" 3 | #include "cppgen.h" 4 | 5 | template 6 | T bindable::eval(const UIContext& ctx) const { 7 | if (empty()) 8 | return {}; 9 | if (has_value()) 10 | return value(); 11 | const auto *var = ctx.codeGen->GetVar(str); 12 | if (var) { 13 | T val; 14 | std::istringstream is(var->init); 15 | if (is >> std::boolalpha >> val) 16 | return val; 17 | } 18 | return {}; 19 | } 20 | 21 | template 22 | T field_ref::eval(const UIContext &ctx) const 23 | { 24 | if (empty()) 25 | return {}; 26 | const auto *var = ctx.codeGen->GetVar(str); 27 | if (!var) 28 | return {}; 29 | T val; 30 | std::istringstream is(var->init); 31 | if (is >> std::boolalpha >> val) 32 | return val; 33 | return {}; 34 | } 35 | -------------------------------------------------------------------------------- /src/binding_property.cpp: -------------------------------------------------------------------------------- 1 | #include "binding_property.h" 2 | #include "cppgen.h" 3 | #include 4 | 5 | float direct_val::eval_px(const UIContext& ctx) const 6 | { 7 | return val * ctx.zoomFactor; 8 | } 9 | 10 | float direct_val::eval_px(const UIContext& ctx) const 11 | { 12 | return val * ctx.zoomFactor; 13 | } 14 | 15 | ImVec2 direct_val::eval_px(const UIContext& ctx) const 16 | { 17 | return { val[0] * ctx.zoomFactor, val[1] * ctx.zoomFactor }; 18 | } 19 | 20 | float bindable::eval_px(int axis, const UIContext& ctx) const 21 | { 22 | if (empty()) { 23 | return 0; 24 | } 25 | else if (stretched()) { 26 | return ctx.stretchSize[axis]; 27 | } 28 | else if (has_value()) { 29 | return value() * ctx.zoomFactor; 30 | } 31 | else if (const auto* var = ctx.codeGen->GetVar(str)) { 32 | float val; 33 | std::istringstream is(var->init); 34 | if (is >> val) 35 | return val * ctx.zoomFactor; 36 | else 37 | return 0; 38 | } 39 | else { 40 | //experimental - currently parses: 41 | //num*dp 42 | //cond ? num1*dp : num2*dp 43 | //extracts min value as that may be the preferrable one when some widgets show/hide 44 | //e.g. -1 vs -20 or 20 vs 50 45 | std::istringstream is(str); 46 | int state = 1; //0 - skip, 1 - number, 2 - *, 3 - dp 47 | float val = 0, ret = 0; 48 | for (cpp::token_iterator it(is), ite; it != ite; ++it) { 49 | if (*it == "?") { 50 | state = 1; 51 | } 52 | else if (*it == ":") { 53 | if (state >= 2) 54 | ret = std::min(ret ? ret : 1e9f, val); 55 | state = 1; 56 | } 57 | else if (state == 1) { 58 | std::istringstream iss(*it); 59 | if (iss >> val) 60 | state = 2; 61 | else 62 | state = 0; 63 | } 64 | else if (state == 2) { 65 | if (*it == "*") 66 | state = 3; 67 | else 68 | state = 0; 69 | } 70 | else if (state == 3) { 71 | if (*it != "dp") 72 | state = 0; 73 | } 74 | } 75 | if (state >= 2) 76 | ret = std::min(ret ? ret : 1e9f, val); 77 | return ret * ctx.zoomFactor; 78 | } 79 | } 80 | 81 | ImU32 bindable::eval(int defClr, const UIContext& ctx) const 82 | { 83 | if (empty()) //default color 84 | return ImGui::ColorConvertFloat4ToU32(ctx.style.Colors[defClr]); 85 | 86 | int idx = style_color(); 87 | if (idx >= 0) 88 | return ImGui::ColorConvertFloat4ToU32(ctx.style.Colors[idx]); 89 | 90 | ImU32 clr; 91 | std::istringstream is(str); 92 | if (is.get() == '0' && is.get() == 'x' && is >> std::hex >> clr) 93 | return clr; 94 | 95 | return ImGui::ColorConvertFloat4ToU32(ctx.style.Colors[defClr]); 96 | } 97 | 98 | std::string bindable::eval(const UIContext&) const 99 | { 100 | if (!has_value()) 101 | return ""; 102 | return str.substr(22, str.size() - 22 - 2); 103 | } 104 | -------------------------------------------------------------------------------- /src/binding_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #ifndef WIN32 6 | #include 7 | #endif 8 | 9 | //negative values allowed 10 | struct dimension_t {}; 11 | 12 | //positive or zero dimension (so that neg values represent empty value) 13 | struct pzdimension_t {}; 14 | 15 | struct pzdimension2_t {}; 16 | 17 | struct font_name_t {}; 18 | 19 | struct shortcut_t {}; 20 | 21 | struct color_t {}; 22 | 23 | //------------------------------------------------------------------ 24 | 25 | template 26 | struct typeid_func_name; 27 | 28 | template 29 | std::string typeid_name() 30 | { 31 | if constexpr (std::is_const_v) 32 | return "const " + typeid_name>(); 33 | else if constexpr (std::is_pointer_v) 34 | return typeid_name>() + "*"; 35 | else if constexpr (std::is_lvalue_reference_v) 36 | return typeid_name>() + "&"; 37 | else if constexpr (std::is_rvalue_reference_v) 38 | return typeid_name>() + "&&"; 39 | else if constexpr (std::is_function_v) 40 | return typeid_func_name::str(); 41 | 42 | else if constexpr (std::is_same_v) 43 | return "void"; 44 | else if constexpr (std::is_same_v) 45 | return "std::string"; 46 | else if constexpr (std::is_same_v) 47 | return "color4"; 48 | else if constexpr (std::is_same_v) 49 | return "float"; 50 | else if constexpr (std::is_same_v) 51 | return "int"; 52 | else if constexpr (std::is_same_v) 53 | return "float"; 54 | else if constexpr (std::is_same_v) 55 | return "double"; 56 | else if constexpr (std::is_same_v) 57 | return "size_t"; 58 | else if constexpr (std::is_same_v) 59 | return "bool"; 60 | else if constexpr (std::is_same_v || std::is_same_v) 61 | return "ImVec2"; 62 | else if constexpr (std::is_same_v) 63 | return "ImFont*"; 64 | else if constexpr (std::is_same_v>) 65 | return "std::vector"; 66 | else 67 | { 68 | std::string str = typeid(T).name(); 69 | #ifdef WIN32 70 | auto i = str.find(' '); 71 | if (i != std::string::npos) 72 | str.erase(0, i + 1); //erase leading struct etc. 73 | #else 74 | int status; 75 | char* ptr = abi::__cxa_demangle(str.c_str(), nullptr, nullptr, &status); 76 | str = ptr; 77 | free(ptr); 78 | #endif 79 | return str; 80 | } 81 | } 82 | 83 | 84 | template 85 | struct typeid_func_name 86 | { 87 | static std::string str() 88 | { 89 | return typeid_name() + "()"; 90 | } 91 | }; 92 | 93 | template 94 | struct typeid_func_name 95 | { 96 | static std::string str() 97 | { 98 | return typeid_name() + "(" + typeid_name() + ")"; 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /src/cppgen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "node_window.h" 6 | 7 | //------------------------------------------------------ 8 | class CppGen 9 | { 10 | public: 11 | static const std::string_view INDENT; 12 | static const std::string_view FOR_VAR_NAME; 13 | static const std::string_view CUR_ITEM_VAR_NAME; 14 | static const std::string_view HBOX_NAME; 15 | static const std::string_view VBOX_NAME; 16 | 17 | CppGen(); 18 | bool ExportUpdate(const std::string& fname, TopWindow* node, const std::map& params, std::string& err); 19 | auto Import(const std::string& path, std::map& params, std::string& err) -> std::unique_ptr; 20 | auto AltFName(const std::string& path) const -> std::string; 21 | int ReadGenVersion(const std::string& fname) const; 22 | 23 | const std::string& GetName() const { return m_name; } 24 | const std::string& GetVName() const { return m_vname; } 25 | //void SetName(const std::string& name) { m_name = name; } 26 | //void SetVName(const std::string& name) { m_vname = name; } 27 | void SetNamesFromId(const std::string& fname); 28 | 29 | struct Var 30 | { 31 | std::string type; 32 | std::string name; 33 | std::string init; 34 | enum { UserCode = 0x1, Interface = 0x2, Impl = 0x4 }; 35 | int flags = 0; 36 | Var(const std::string& n, const std::string& t, const std::string& i, int f) 37 | : name(n), type(t), init(i), flags(f) {} 38 | }; 39 | 40 | std::string CreateVar(const std::string& type, const std::string& init, int flags, const std::string& scope = ""); 41 | bool CreateNamedVar(const std::string& name, const std::string& type, const std::string& init, int flags, const std::string& scope = ""); 42 | bool RenameVar(const std::string& oldn, const std::string& newn, const std::string& scope = ""); 43 | bool RemoveVar(const std::string& name, const std::string& scope = ""); 44 | void RemovePrefixedVars(const std::string& prefix, const std::string& scope = ""); 45 | bool ChangeVar(const std::string& name, const std::string& type, const std::string& init, int flags = -1, const std::string& scope = ""); 46 | const Var* GetVar(const std::string& name, const std::string& scope = "") const; 47 | bool RenameStruct(const std::string& oldn, const std::string& newn); 48 | const std::vector& GetVars(const std::string& scope = ""); 49 | std::vector GetLayoutVars(); 50 | std::vector GetStructTypes(); 51 | enum VarExprResult { SyntaxError, ConflictError, Existing, New, New_ImplicitStruct }; 52 | VarExprResult CheckVarExpr(const std::string& name, const std::string& type, const std::string& scope = ""); 53 | bool CreateVarExpr(std::string& name, const std::string& type, const std::string& init, int flags, const std::string& scope = ""); 54 | auto GetVarExprs(const std::string& type, bool reference, const std::string& curArray = "") ->std::vector>; 55 | 56 | private: 57 | Var* FindVar(const std::string& name, const std::string& scope); 58 | auto MatchType(const std::string& name, std::string_view type, std::string_view match, bool reference, const std::string& curArray) -> std::vector>; 59 | 60 | void CreateH(std::ostream& out); 61 | void CreateCpp(std::ostream& out); 62 | auto ExportH(std::ostream& out, std::istream& prev, const std::string& origHName, TopWindow* node) -> std::array; 63 | void ExportCpp(std::ostream& out, std::istream& prev, const std::array& origNames, const std::map& params, TopWindow* node, const std::string& code); 64 | bool WriteStub(std::ostream& fout, const std::string& id, TopWindow::Kind kind, TopWindow::Placement animPos, const std::map& params = {}, const std::string& code = {}); 65 | auto ImportCode(std::istream& in, const std::string& fname, std::map& params) -> std::unique_ptr; 66 | 67 | bool ParseFieldDecl(const std::string& stype, const std::vector& line, int flags); 68 | auto IsMemFun(const std::vector& line)->std::string; 69 | bool IsMemDrawFun(const std::vector& line); 70 | auto ParseDrawFun(const std::vector& line, cpp::token_iterator& iter, std::map& params) -> std::unique_ptr; 71 | 72 | std::map> m_fields; 73 | std::string m_name, m_vname, m_hname; 74 | std::string ctx_workingDir; 75 | int ctx_importVersion; 76 | std::string m_error; 77 | }; -------------------------------------------------------------------------------- /src/glfw_cursor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | const unsigned char CUSTOM_BUSY_CURSOR[] = { 7 | "\x11\x16\x0\x0" 8 | " XXXXXXXXXXXXXXX " 9 | "X.............. X" 10 | "X.............. X" 11 | " XXXXXXXXXXXXXXX " 12 | " X...........X " 13 | " X...........X " 14 | " X...........X " 15 | " X..X.X.X..X " 16 | " X...X.X...X " 17 | " X...X...X " 18 | " X.....X " 19 | " X..X..X " 20 | " X.......X " 21 | " X....X....X " 22 | " X.........X " 23 | " X.....X.....X " 24 | " X....X.X....X " 25 | " X...X.X.X...X " 26 | " XXXXXXXXXXXXXXX " 27 | "X...............X" 28 | "X...............X" 29 | " XXXXXXXXXXXXXXX " 30 | }; 31 | 32 | inline GLFWcursor* glfwCreateCursor(const unsigned char* data) 33 | { 34 | GLFWimage image; 35 | image.width = data[0]; 36 | image.height = data[1]; 37 | std::vector bmp; 38 | bmp.reserve(4 * image.width*image.height); 39 | for (int i = 0; i < image.width*image.height; ++i) 40 | { 41 | const char c = data[4 + i]; 42 | if (c == '.') 43 | bmp.push_back(0xffffffff); 44 | else if (c == 'X') 45 | bmp.push_back(0xff000000); 46 | else 47 | bmp.push_back(0x0); 48 | } 49 | image.pixels = (unsigned char *)bmp.data(); 50 | return glfwCreateCursor(&image, data[2], data[3]); 51 | } 52 | -------------------------------------------------------------------------------- /src/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/src/icon.ico -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/src/icon.png -------------------------------------------------------------------------------- /src/node_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "node_standard.h" 3 | 4 | struct Table : Widget 5 | { 6 | struct ColumnData 7 | { 8 | direct_val label = ""; 9 | bindable visible; 10 | direct_val sizingPolicy = 0; 11 | direct_val width = 0; 12 | direct_val flags; 13 | 14 | ColumnData(); 15 | ColumnData(const std::string& l, ImGuiTableColumnFlags_ sizingPolicy, float w = 0); 16 | const ColumnData& Defaults() const { static ColumnData cd; return cd; } 17 | auto Properties()->std::vector; 18 | bool PropertyUI(int i, UIContext& ctx); 19 | std::string SizingPolicyString(); 20 | }; 21 | 22 | direct_val flags = ImGuiTableFlags_Borders; 23 | std::vector columnData; 24 | direct_val header = true; 25 | bindable rowFilter; 26 | bindable rowHeight = 0; 27 | direct_val scrollFreeze_x = 0; 28 | direct_val scrollFreeze_y = 0; 29 | direct_val scrollWhenDragging = false; 30 | direct_val style_cellPadding; 31 | bindable style_headerBg; 32 | bindable style_rowBg; 33 | bindable style_rowBgAlt; 34 | bindable style_childBg; 35 | event<> onBeginRow; 36 | event<> onEndRow; 37 | event<> onSetup; 38 | 39 | Table(UIContext&); 40 | auto Clone(UIContext& ctx)->std::unique_ptr; 41 | int Behavior() { return Widget::Behavior() | SnapInterior | HasSizeX | HasSizeY; } 42 | ImDrawList* DoDraw(UIContext& ctx); 43 | auto Properties() ->std::vector; 44 | bool PropertyUI(int i, UIContext& ctx); 45 | auto Events()->std::vector; 46 | bool EventUI(int i, UIContext& ctx); 47 | void DoExport(std::ostream& os, UIContext& ctx); 48 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 49 | const char* GetIcon() const { return ICON_FA_TABLE_CELLS_LARGE; } 50 | const Table& Defaults() { static Table var(UIContext::Defaults()); return var; } 51 | int ColumnCount(UIContext& ctx) { return (int)columnData.size(); } 52 | }; 53 | 54 | struct Child : Widget 55 | { 56 | direct_val flags = ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_NavFlattened; 57 | direct_val wflags = ImGuiWindowFlags_NoSavedSettings; 58 | bindable columnCount = 1; 59 | direct_val columnBorder = true; 60 | direct_val scrollWhenDragging = false; 61 | direct_val style_padding; 62 | direct_val style_spacing; 63 | direct_val style_outerPadding = true; 64 | direct_val style_rounding; 65 | direct_val style_borderSize; 66 | bindable style_bg; 67 | 68 | Child(UIContext& ctx); 69 | auto Clone(UIContext& ctx)->std::unique_ptr; 70 | int Behavior(); 71 | ImDrawList* DoDraw(UIContext& ctx); 72 | auto Properties() ->std::vector; 73 | bool PropertyUI(int i, UIContext& ctx); 74 | void DoExport(std::ostream& os, UIContext& ctx); 75 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 76 | const char* GetIcon() const { return ICON_FA_SQUARE_FULL; } 77 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 78 | const Child& Defaults() { static Child var(UIContext::Defaults()); return var; } 79 | int ColumnCount(UIContext& ctx) { return columnCount.eval(ctx); } 80 | }; 81 | 82 | struct CollapsingHeader : Widget 83 | { 84 | bindable label = "label"; 85 | direct_val flags; 86 | bindable open; 87 | bindable style_header; 88 | bindable style_hovered; 89 | bindable style_active; 90 | 91 | CollapsingHeader(UIContext& ctx); 92 | auto Clone(UIContext& ctx)->std::unique_ptr; 93 | int Behavior() { return SnapSides | SnapInterior; } 94 | ImDrawList* DoDraw(UIContext& ctx); 95 | auto Properties()->std::vector; 96 | bool PropertyUI(int i, UIContext& ctx); 97 | void DoExport(std::ostream& os, UIContext& ctx); 98 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 99 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 100 | const char* GetIcon() const { return ICON_FA_ARROW_DOWN_WIDE_SHORT; } 101 | const CollapsingHeader& Defaults() { static CollapsingHeader var(UIContext::Defaults()); return var; } 102 | }; 103 | 104 | struct TabBar : Widget 105 | { 106 | direct_val flags; 107 | field_ref activeTab; 108 | bindable style_tab; 109 | bindable style_tabDimmed; 110 | bindable style_hovered; 111 | bindable style_selected; 112 | bindable style_dimmedSelected; 113 | bindable style_overline; 114 | direct_val style_regularWidth = false; 115 | 116 | TabBar(UIContext& ctx); 117 | auto Clone(UIContext& ctx)->std::unique_ptr; 118 | int Behavior() { return NoOverlayPos | SnapSides; } 119 | ImDrawList* DoDraw(UIContext& ctx); 120 | auto Properties()->std::vector; 121 | bool PropertyUI(int i, UIContext& ctx); 122 | void DoExport(std::ostream& os, UIContext& ctx); 123 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 124 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 125 | const char* GetIcon() const { return ICON_FA_FOLDER_CLOSED; } 126 | const TabBar& Defaults() { static TabBar var(UIContext::Defaults()); return var; } 127 | }; 128 | 129 | struct TabItem : Widget 130 | { 131 | bindable label = "TabItem"; 132 | direct_val closeButton = false; 133 | event<> onClose; 134 | 135 | TabItem(UIContext& ctx); 136 | auto Clone(UIContext& ctx)->std::unique_ptr; 137 | int Behavior() { return SnapInterior | SnapGrandparentClip | NoOverlayPos; } 138 | ImDrawList* DoDraw(UIContext& ctx); 139 | void DoDrawTools(UIContext& ctx); 140 | auto Properties()->std::vector; 141 | bool PropertyUI(int i, UIContext& ctx); 142 | auto Events()->std::vector; 143 | bool EventUI(int i, UIContext& ctx); 144 | void DoExport(std::ostream& os, UIContext& ctx); 145 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 146 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 147 | const char* GetIcon() const { return ICON_FA_FOLDER; } 148 | const TabItem& Defaults() { static TabItem var(UIContext::Defaults()); return var; } 149 | }; 150 | 151 | struct TreeNode : Widget 152 | { 153 | direct_val flags; 154 | bindable label = "Node"; 155 | bindable open; 156 | bool lastOpen; 157 | 158 | TreeNode(UIContext& ctx); 159 | auto Clone(UIContext& ctx)->std::unique_ptr; 160 | int Behavior() { return Widget::Behavior() | SnapInterior; } 161 | ImDrawList* DoDraw(UIContext& ctx); 162 | auto Properties()->std::vector; 163 | bool PropertyUI(int i, UIContext& ctx); 164 | void DoExport(std::ostream& os, UIContext& ctx); 165 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 166 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 167 | const char* GetIcon() const { return ICON_FA_SITEMAP; } 168 | const TreeNode& Defaults() { static TreeNode var(UIContext::Defaults()); return var; } 169 | }; 170 | 171 | struct MenuBar : Widget 172 | { 173 | MenuBar(UIContext& ctx); 174 | auto Clone(UIContext& ctx)->std::unique_ptr; 175 | int Behavior() { return NoOverlayPos; } 176 | ImDrawList* DoDraw(UIContext& ctx); 177 | auto Properties()->std::vector; 178 | bool PropertyUI(int i, UIContext& ctx); 179 | auto Events()->std::vector; 180 | bool EventUI(int i, UIContext& ctx); 181 | void DoExport(std::ostream& os, UIContext& ctx); 182 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 183 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 184 | const char* GetIcon() const { return ICON_FA_ELLIPSIS; } 185 | const MenuBar& Defaults() { static MenuBar var(UIContext::Defaults()); return var; } 186 | }; 187 | 188 | struct ContextMenu : Widget 189 | { 190 | direct_val label = "Item"; 191 | direct_val style_padding; 192 | direct_val style_spacing; 193 | direct_val style_rounding; 194 | 195 | ContextMenu(UIContext& ctx); 196 | auto Clone(UIContext& ctx)->std::unique_ptr; 197 | int Behavior() { return NoOverlayPos | NoContextMenu; } 198 | ImDrawList* DoDraw(UIContext& ctx); 199 | auto Properties()->std::vector; 200 | bool PropertyUI(int i, UIContext& ctx); 201 | auto Events()->std::vector; 202 | bool EventUI(int i, UIContext& ctx); 203 | void DoExport(std::ostream& os, UIContext& ctx); 204 | void ExportAllShortcuts(std::ostream& os, UIContext& ctx); 205 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 206 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 207 | const char* GetIcon() const { return ICON_FA_MESSAGE; } 208 | const ContextMenu& Defaults() { static ContextMenu var(UIContext::Defaults()); return var; } 209 | }; 210 | 211 | struct MenuIt : Widget 212 | { 213 | direct_val ownerDraw = false; 214 | direct_val label = "Item"; 215 | direct_val shortcut = ""; 216 | direct_val separator = false; 217 | bindable checked; 218 | event<> onChange; 219 | 220 | MenuIt(UIContext& ctx); 221 | auto Clone(UIContext& ctx)->std::unique_ptr; 222 | int Behavior() { return NoOverlayPos | NoContextMenu; } 223 | ImDrawList* DoDraw(UIContext& ctx); 224 | void DoDrawTools(UIContext& ctx); 225 | auto Properties()->std::vector; 226 | bool PropertyUI(int i, UIContext& ctx); 227 | auto Events()->std::vector; 228 | bool EventUI(int i, UIContext& ctx); 229 | void DoExport(std::ostream& os, UIContext& ctx); 230 | void ExportShortcut(std::ostream& os, UIContext& ctx); 231 | void ExportAllShortcuts(std::ostream& os, UIContext& ctx); 232 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 233 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 234 | const char* GetIcon() const { return ICON_FA_BARS; } 235 | const MenuIt& Defaults() { static MenuIt var(UIContext::Defaults()); return var; } 236 | }; 237 | 238 | struct Splitter : Widget 239 | { 240 | direct_val min_size1 = 10; 241 | direct_val min_size2 = 10; 242 | field_ref position; 243 | bindable style_active; 244 | bindable style_bg; 245 | 246 | Splitter(UIContext& ctx); 247 | auto Clone(UIContext& ctx)->std::unique_ptr; 248 | int Behavior() { return Widget::Behavior() | SnapInterior | HasSizeX | HasSizeY | NoOverlayPos; } 249 | ImDrawList* DoDraw(UIContext& ctx); 250 | auto Properties()->std::vector; 251 | bool PropertyUI(int i, UIContext& ctx); 252 | void DoExport(std::ostream& os, UIContext& ctx); 253 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 254 | //void CalcSizeEx(ImVec2 p1, UIContext& ctx); 255 | const char* GetIcon() const { return ICON_FA_TABLE_COLUMNS; } 256 | const Splitter& Defaults() { static Splitter var(UIContext::Defaults()); return var; } 257 | }; 258 | -------------------------------------------------------------------------------- /src/node_extra.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "node_standard.h" 3 | 4 | struct DockSpace : Widget 5 | { 6 | direct_val flags; 7 | bindable style_preview; 8 | bindable style_emptyBg; 9 | 10 | DockSpace(UIContext& ctx); 11 | uint32_t CalcHash(UIContext& ctx); 12 | auto Clone(UIContext& ctx)->std::unique_ptr; 13 | int Behavior() { return SnapSides | NoOverlayPos | HasSizeX | HasSizeY; } 14 | ImDrawList* DoDraw(UIContext& ctx); 15 | void DoDrawTools(UIContext& ctx); 16 | auto Properties()->std::vector; 17 | bool PropertyUI(int i, UIContext& ctx); 18 | void DoExport(std::ostream& os, UIContext& ctx); 19 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 20 | const char* GetIcon() const { return ICON_FA_TABLE; } 21 | const DockSpace& Defaults() { static DockSpace var(UIContext::Defaults()); return var; } 22 | }; 23 | 24 | struct DockNode : Widget 25 | { 26 | direct_val flags; 27 | direct_val splitDir = ImGuiDir_None; 28 | bindable splitRatio = 0; 29 | direct_val labels = ""; 30 | ImGuiID nodeId = 0; 31 | 32 | DockNode(UIContext& ctx); 33 | void CalcHash(uint32_t& hash); 34 | auto Clone(UIContext& ctx)->std::unique_ptr; 35 | int Behavior() { return NoOverlayPos; } 36 | ImGuiID SplitNode(ImGuiID parentId, UIContext& ctx); 37 | ImDrawList* DoDraw(UIContext& ctx); 38 | void CalcSizeEx(ImVec2 p1, UIContext& ctx); 39 | void DoDrawTools(UIContext& ctx); 40 | auto Properties()->std::vector; 41 | bool PropertyUI(int i, UIContext& ctx); 42 | auto Events()->std::vector; 43 | void DoExport(std::ostream& os, UIContext& ctx); 44 | void DoImport(const cpp::stmt_iterator& sit, UIContext& ctx); 45 | void ExportHelp(std::ostream& os, UIContext& ctx); 46 | const char* GetIcon() const { return "N"; } 47 | const DockNode& Defaults() { static DockNode var(UIContext::Defaults()); return var; } 48 | }; 49 | -------------------------------------------------------------------------------- /src/node_window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "node_standard.h" 3 | 4 | struct TopWindow : UINode 5 | { 6 | enum Kind { MainWindow, Window, Popup, ModalPopup, Activity }; 7 | enum Placement { None, Left, Right, Top, Bottom, Center, Maximize }; 8 | 9 | direct_val flags = ImGuiWindowFlags_NoCollapse; 10 | direct_val kind = Window; 11 | bindable title = "title"; 12 | bindable size_x = 640.f; 13 | bindable size_y = 480.f; 14 | bindable minSize_x = 0; 15 | bindable minSize_y = 0; 16 | bindable style_font; 17 | direct_val style_padding; 18 | direct_val style_spacing; 19 | direct_val style_innerSpacing; 20 | direct_val style_borderSize; 21 | direct_val style_rounding; 22 | direct_val style_scrollbarSize; 23 | direct_val style_titlePadding; 24 | bindable style_bg; 25 | bindable style_menuBg; 26 | direct_val placement = None; 27 | direct_val closeOnEscape = false; 28 | direct_val animate = false; 29 | direct_val initialActivity = false; 30 | 31 | event<> onBackButton; 32 | event<> onWindowAppearing; 33 | 34 | std::string userCodeBefore, userCodeAfter, userCodeMid; 35 | 36 | TopWindow(UIContext& ctx); 37 | void Draw(UIContext& ctx); 38 | void DrawTools(UIContext& ctx) {} 39 | void TreeUI(UIContext& ctx); 40 | bool EventUI(int, UIContext& ctx); 41 | auto Properties() ->std::vector; 42 | auto Events() ->std::vector; 43 | bool PropertyUI(int i, UIContext& ctx); 44 | void Export(std::ostream& os, UIContext& ctx); 45 | void Import(cpp::stmt_iterator& sit, UIContext& ctx); 46 | int Behavior() { return SnapInterior; } 47 | int ColumnCount(UIContext& ctx) { return 0; } 48 | std::string GetTypeName() { return "Window"; } 49 | const TopWindow& Defaults() { static TopWindow node(UIContext::Defaults()); return node; } 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /src/stx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace stx { 6 | 7 | template 8 | decltype(auto) find(C& c, const T& val) 9 | { 10 | return std::find(std::begin(c), std::end(c), val); 11 | } 12 | 13 | template 14 | decltype(auto) rfind(C& c, const T& val) 15 | { 16 | if (c.begin() == c.end()) 17 | return c.end(); 18 | for (auto it = --c.end(); it != c.begin(); --it) 19 | if (*it == val) 20 | return it; 21 | if (*c.begin() == val) 22 | return c.begin(); 23 | return c.end(); 24 | } 25 | 26 | template 27 | decltype(auto) find_if(C& c, F&& val) 28 | { 29 | return std::find_if(std::begin(c), std::end(c), std::forward(val)); 30 | } 31 | 32 | template 33 | decltype(auto) count(const C& c, const T& val) 34 | { 35 | return std::count(std::begin(c), std::end(c), val); 36 | } 37 | 38 | template 39 | decltype(auto) count_if(const C& c, F&& val) 40 | { 41 | return std::count_if(std::begin(c), std::end(c), std::forward(val)); 42 | } 43 | 44 | template 45 | decltype(auto) equal(const C& c, const D& d) 46 | { 47 | return std::equal(std::begin(c), std::end(c), std::begin(d), std::end(d)); 48 | } 49 | 50 | template 51 | decltype(auto) fill(C& c, const T& val) 52 | { 53 | return std::fill(std::begin(c), std::end(c), val); 54 | } 55 | 56 | template 57 | decltype(auto) replace(C& c, const T& oldv, const T& newv) 58 | { 59 | return std::replace(std::begin(c), std::end(c), oldv, newv); 60 | } 61 | 62 | template 63 | decltype(auto) unique(C& c) 64 | { 65 | return std::unique(std::begin(c), std::end(c)); 66 | } 67 | 68 | template 69 | decltype(auto) erase(C& c, const T& val) 70 | { 71 | return c.erase(std::remove(std::begin(c), std::end(c), val), c.end()); 72 | } 73 | 74 | template 75 | decltype(auto) erase_if(C& c, F&& fun) 76 | { 77 | return c.erase(std::remove_if(std::begin(c), std::end(c), std::forward(fun)), c.end()); 78 | } 79 | 80 | template 81 | decltype(auto) set_intersection(const C& c1, const C& c2, I dst) 82 | { 83 | return std::set_intersection(std::begin(c1), std::end(c1), std::begin(c2), std::end(c2), dst); 84 | } 85 | 86 | template 87 | decltype(auto) sort(C& c) 88 | { 89 | return std::sort(std::begin(c), std::end(c)); 90 | } 91 | 92 | template 93 | decltype(auto) sort(C& c, F&& f) 94 | { 95 | return std::sort(std::begin(c), std::end(c), std::forward(f)); 96 | } 97 | 98 | template 99 | decltype(auto) stable_sort(C& c) 100 | { 101 | return std::stable_sort(std::begin(c), std::end(c)); 102 | } 103 | 104 | template 105 | decltype(auto) stable_sort(C& c, F&& f) 106 | { 107 | return std::stable_sort(std::begin(c), std::end(c), std::forward(f)); 108 | } 109 | 110 | template 111 | decltype(auto) for_each(C& c, F&& f) 112 | { 113 | return std::for_each(std::begin(c), std::end(c), std::forward(f)); 114 | } 115 | 116 | template 117 | int ssize(const C& c) 118 | { 119 | return (int)std::size(c); 120 | } 121 | 122 | template 123 | std::string join(I beg, I end, std::string_view sep) 124 | { 125 | std::string s; 126 | for (; beg != end; ++beg) { 127 | s += *beg; 128 | s += sep; 129 | } 130 | if (!s.empty()) 131 | s.resize(s.size() - sep.size()); 132 | return s; 133 | } 134 | 135 | template 136 | std::string join(const C& c, std::string_view sep) 137 | { 138 | return join(std::begin(c), std::end(c), sep); 139 | } 140 | } -------------------------------------------------------------------------------- /src/ui_about_dlg.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.1 2 | // github.com/xyz 3 | 4 | #include "ui_about_dlg.h" 5 | #include "utils.h" 6 | 7 | AboutDlg aboutDlg; 8 | 9 | void AboutDlg::OpenPopup(std::function clb) 10 | { 11 | callback = clb; 12 | modalResult = ImRad::None; 13 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 14 | ioUserData->dimBgRatio = 1.f; 15 | ImGui::OpenPopup(ID); 16 | Init(); 17 | } 18 | 19 | void AboutDlg::ClosePopup(ImRad::ModalResult mr) 20 | { 21 | modalResult = mr; 22 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 23 | ioUserData->dimBgRatio = 0.f; 24 | } 25 | 26 | 27 | void AboutDlg::Init() 28 | { 29 | // TODO: Add your code here 30 | } 31 | 32 | void AboutDlg::Draw() 33 | { 34 | /// @style imrad 35 | /// @unit px 36 | /// @begin TopWindow 37 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 38 | ID = ImGui::GetID("###AboutDlg"); 39 | ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGui::GetStyleColorVec4(ImGuiCol_TitleBgActive)); 40 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 60, 15 }); 41 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 10, 7 }); 42 | ImGui::SetNextWindowPos(ioUserData->WorkRect().GetCenter(), 0, { 0.5f, 0.5f }); //Center 43 | ImGui::SetNextWindowSize({ 0, 0 }); //{ 0, 0 } 44 | bool tmpOpen = true; 45 | if (ImGui::BeginPopupModal("About###AboutDlg", &tmpOpen, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) 46 | { 47 | if (ioUserData->activeActivity != "") 48 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 49 | if (modalResult != ImRad::None) 50 | { 51 | ImGui::CloseCurrentPopup(); 52 | if (modalResult != ImRad::Cancel) 53 | callback(modalResult); 54 | } 55 | /// @separator 56 | 57 | // TODO: Add Draw calls of dependent popup windows here 58 | 59 | /// @begin Image 60 | if (!value1) 61 | value1 = ImRad::LoadTextureFromFile("style/icon-40.png"); 62 | ImGui::Image(value1.id, { (float)value1.w, (float)value1.h }); 63 | /// @end Image 64 | 65 | // TODO: Add Draw calls of dependent popup windows here 66 | 67 | /// @begin Text 68 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 69 | ImGui::PushFont(ImRad::GetFontByName("imrad.H1")); 70 | ImGui::TextUnformatted(ImRad::Format("{}", VER_STR).c_str()); 71 | ImGui::PopFont(); 72 | /// @end Text 73 | 74 | /// @begin Separator 75 | ImRad::Spacing(1); 76 | ImRect r1 = ImGui::GetCurrentWindow()->InnerRect; 77 | ImGui::PushClipRect(r1.Min, r1.Max, false); 78 | ImGui::SetCursorScreenPos({ r1.Min.x, ImGui::GetCursorScreenPos().y }); 79 | ImVec2 wr1 = ImGui::GetCurrentWindow()->WorkRect.Max; 80 | ImGui::GetCurrentWindow()->WorkRect.Max.x = r1.Max.x; 81 | ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal); 82 | ImGui::PopClipRect(); 83 | ImGui::GetCurrentWindow()->WorkRect.Max = wr1; 84 | /// @end Separator 85 | 86 | /// @begin Text 87 | ImRad::Spacing(2); 88 | ImGui::TextUnformatted(ImRad::Format("built with ImGui {}", IMGUI_VERSION).c_str()); 89 | /// @end Text 90 | 91 | /// @begin Text 92 | ImGui::TextUnformatted("Tomas Pecholt"); 93 | /// @end Text 94 | 95 | /// @begin Text 96 | ImRad::Spacing(2); 97 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); 98 | ImGui::TextUnformatted("For more information and updates visit"); 99 | ImGui::PopStyleColor(); 100 | /// @end Text 101 | 102 | /// @begin Table 103 | if (ImGui::BeginTable("table2", 2, ImGuiTableFlags_None, { -1, 0 })) 104 | { 105 | ImGui::TableSetupColumn("A", ImGuiTableColumnFlags_WidthFixed, 0); 106 | ImGui::TableSetupColumn("B", ImGuiTableColumnFlags_WidthStretch, 0); 107 | ImGui::TableNextRow(0, 0); 108 | ImGui::TableSetColumnIndex(0); 109 | /// @separator 110 | 111 | /// @begin Text 112 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff003398); 113 | ImGui::TextUnformatted("Project page"); 114 | ImGui::PopStyleColor(); 115 | if (ImGui::IsItemHovered()) 116 | ImGui::SetMouseCursor(7); 117 | if (ImGui::IsItemHovered()) 118 | HoverURL(); 119 | if (ImGui::IsItemClicked()) 120 | OpenURL(); 121 | /// @end Text 122 | 123 | /// @begin Text 124 | ImRad::TableNextColumn(2); 125 | ImRad::Spacing(1); 126 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff003398); 127 | ImGui::TextUnformatted("Tutorials & How To"); 128 | ImGui::PopStyleColor(); 129 | if (ImGui::IsItemHovered()) 130 | ImGui::SetMouseCursor(7); 131 | if (ImGui::IsItemHovered()) 132 | HoverURL(); 133 | if (ImGui::IsItemClicked()) 134 | OpenURL(); 135 | /// @end Text 136 | 137 | /// @begin Text 138 | ImRad::TableNextColumn(2); 139 | ImRad::Spacing(1); 140 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff003398); 141 | ImGui::TextUnformatted("Report a bug"); 142 | ImGui::PopStyleColor(); 143 | if (ImGui::IsItemHovered()) 144 | ImGui::SetMouseCursor(7); 145 | if (ImGui::IsItemHovered()) 146 | HoverURL(); 147 | if (ImGui::IsItemClicked()) 148 | OpenURL(); 149 | /// @end Text 150 | 151 | 152 | /// @separator 153 | ImGui::EndTable(); 154 | } 155 | /// @end Table 156 | 157 | /// @begin Table 158 | ImRad::Spacing(2); 159 | if (ImGui::BeginTable("table3", 2, ImGuiTableFlags_NoPadOuterX | ImGuiTableFlags_NoPadInnerX, { 0, 0 })) 160 | { 161 | ImGui::TableSetupColumn("left-stretch", ImGuiTableColumnFlags_WidthStretch, 0); 162 | ImGui::TableSetupColumn("content", ImGuiTableColumnFlags_WidthFixed, 0); 163 | ImGui::TableNextRow(0, 0); 164 | ImGui::TableSetColumnIndex(0); 165 | /// @separator 166 | 167 | /// @begin Button 168 | ImRad::TableNextColumn(1); 169 | if (ImGui::Button("OK", { 100, 30 }) || ImGui::Shortcut(ImGuiKey_Escape)) 170 | { 171 | ClosePopup(ImRad::Ok); 172 | } 173 | /// @end Button 174 | 175 | 176 | /// @separator 177 | ImGui::EndTable(); 178 | } 179 | /// @end Table 180 | 181 | /// @separator 182 | ImGui::EndPopup(); 183 | } 184 | ImGui::PopStyleVar(); 185 | ImGui::PopStyleVar(); 186 | ImGui::PopStyleColor(); 187 | /// @end TopWindow 188 | } 189 | 190 | 191 | void AboutDlg::OpenURL() 192 | { 193 | switch (ImGui::TableGetRowIndex()) 194 | { 195 | case 0: 196 | ShellExec(GITHUB_URL); 197 | break; 198 | case 1: 199 | ShellExec(GITHUB_URL + "/wiki"); 200 | break; 201 | case 2: 202 | ShellExec(GITHUB_URL + "/issues"); 203 | break; 204 | } 205 | } 206 | 207 | void AboutDlg::HoverURL() 208 | { 209 | ImVec2 p1 = ImGui::GetItemRectMin(); 210 | ImVec2 p2 = ImGui::GetItemRectMax(); 211 | auto* dl = ImGui::GetWindowDrawList(); 212 | dl->AddLine({ p1.x, p2.y }, { p2.x, p2.y }, 0xff003399); 213 | } 214 | -------------------------------------------------------------------------------- /src/ui_about_dlg.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.1 2 | // github.com/xyz 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include "imrad.h" 9 | 10 | class AboutDlg 11 | { 12 | public: 13 | /// @begin interface 14 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 15 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 16 | void Draw(); 17 | 18 | void OpenURL(); 19 | /// @end interface 20 | 21 | private: 22 | /// @begin impl 23 | void Init(); 24 | 25 | void HoverURL(); 26 | ImGuiID ID = 0; 27 | ImRad::ModalResult modalResult; 28 | std::function callback; 29 | ImRad::Texture value1; 30 | /// @end impl 31 | }; 32 | 33 | extern AboutDlg aboutDlg; 34 | -------------------------------------------------------------------------------- /src/ui_binding.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // github.com/xyz 3 | 4 | #include "ui_binding.h" 5 | #include "ui_new_field.h" 6 | #include "ui_message_box.h" 7 | #include "utils.h" 8 | 9 | BindingDlg bindingDlg; 10 | 11 | 12 | void BindingDlg::OpenPopup(std::function clb) 13 | { 14 | callback = clb; 15 | modalResult = ImRad::None; 16 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 17 | ioUserData->dimBgRatio = 1.f; 18 | ImGui::OpenPopup(ID); 19 | Init(); 20 | } 21 | 22 | void BindingDlg::ClosePopup(ImRad::ModalResult mr) 23 | { 24 | modalResult = mr; 25 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 26 | ioUserData->dimBgRatio = 0.f; 27 | } 28 | 29 | void BindingDlg::Draw() 30 | { 31 | /// @style Dark 32 | /// @unit px 33 | /// @begin TopWindow 34 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 35 | ID = ImGui::GetID("###BindingDlg"); 36 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 10, 10 }); 37 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 7, 7 }); 38 | ImGui::SetNextWindowSize({ 600, 480 }, ImGuiCond_FirstUseEver); //{ 600, 480 } 39 | ImGui::SetNextWindowSizeConstraints({ 0, 0 }, { FLT_MAX, FLT_MAX }); 40 | bool tmpOpen = true; 41 | if (ImGui::BeginPopupModal("Binding###BindingDlg", &tmpOpen, ImGuiWindowFlags_NoCollapse)) 42 | { 43 | if (ioUserData->activeActivity != "") 44 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 45 | if (modalResult != ImRad::None) 46 | { 47 | ImGui::CloseCurrentPopup(); 48 | if (modalResult != ImRad::Cancel) 49 | callback(modalResult); 50 | } 51 | /// @separator 52 | 53 | newFieldPopup.Draw(); 54 | messageBox.Draw(); 55 | 56 | /// @begin Text 57 | hb1.BeginLayout(); 58 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff4d4dff); 59 | ImGui::TextUnformatted(ImRad::Format(" {}=", name).c_str()); 60 | hb1.AddSize(0, ImRad::HBox::ItemSize); 61 | ImGui::PopStyleColor(); 62 | /// @end Text 63 | 64 | /// @begin Spacer 65 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 66 | ImRad::Dummy({ hb1.GetSize(), 20 }); 67 | hb1.AddSize(1, ImRad::HBox::Stretch(1)); 68 | /// @end Spacer 69 | 70 | /// @begin Text 71 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 72 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); 73 | ImGui::TextUnformatted(ImRad::Format("{}", type + (forceReference ? " &" : "")).c_str()); 74 | hb1.AddSize(1, ImRad::HBox::ItemSize); 75 | ImGui::PopStyleColor(); 76 | /// @end Text 77 | 78 | /// @begin Input 79 | ImGui::PushFont(font); 80 | ImGui::SetNextItemWidth(-1); 81 | ImGui::InputText("##expr", &expr, ImGuiInputTextFlags_CallbackCharFilter, IMRAD_INPUTTEXT_EVENT(BindingDlg, OnTextInputFilter)); 82 | if (ImGui::IsItemActive()) 83 | ioUserData->imeType = ImRad::ImeText; 84 | ImGui::PopFont(); 85 | if (ImGui::IsWindowAppearing()) 86 | ImGui::SetKeyboardFocusHere(-1); 87 | if (focusExpr) 88 | { 89 | //forceFocus 90 | if (ImGui::IsItemFocused()) 91 | focusExpr = false; 92 | ImGui::SetKeyboardFocusHere(-1); 93 | } 94 | /// @end Input 95 | 96 | /// @begin Text 97 | hb3.BeginLayout(); 98 | ImRad::Spacing(1); 99 | ImGui::AlignTextToFramePadding(); 100 | ImGui::TextUnformatted("Available fields"); 101 | hb3.AddSize(0, ImRad::HBox::ItemSize); 102 | /// @end Text 103 | 104 | /// @begin Spacer 105 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 106 | ImRad::Dummy({ hb3.GetSize(), 20 }); 107 | hb3.AddSize(1, ImRad::HBox::Stretch(1)); 108 | /// @end Spacer 109 | 110 | /// @begin CheckBox 111 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 112 | if (ImGui::Checkbox("show all", &showAll)) 113 | Refresh(); 114 | hb3.AddSize(1, ImRad::HBox::ItemSize); 115 | /// @end CheckBox 116 | 117 | /// @begin Table 118 | if (ImGui::BeginTable("table1", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_ScrollY, { 0, -48 })) 119 | { 120 | ImGui::TableSetupColumn("Name", 0, 0); 121 | ImGui::TableSetupColumn("Type", 0, 0); 122 | ImGui::TableSetupScrollFreeze(0, 1); 123 | ImGui::TableHeadersRow(); 124 | 125 | for (int i = 0; i < vars.size(); ++i) 126 | { 127 | ImGui::PushID(i); 128 | ImGui::TableNextRow(0, 0); 129 | ImGui::TableSetColumnIndex(0); 130 | /// @separator 131 | 132 | /// @begin Selectable 133 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 134 | if (ImRad::Selectable(ImRad::Format("{}", vars[i].first).c_str(), false, ImGuiSelectableFlags_NoAutoClosePopups | ImGuiSelectableFlags_SpanAllColumns, { 0, 0 })) 135 | { 136 | OnVarClicked(); 137 | } 138 | ImGui::PopStyleVar(); 139 | /// @end Selectable 140 | 141 | /// @begin Selectable 142 | ImRad::TableNextColumn(1); 143 | ImGui::BeginDisabled(true); 144 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 145 | ImRad::Selectable(ImRad::Format("{}", vars[i].second).c_str(), false, ImGuiSelectableFlags_NoAutoClosePopups, { 0, 0 }); 146 | ImGui::PopStyleVar(); 147 | ImGui::EndDisabled(); 148 | /// @end Selectable 149 | 150 | /// @separator 151 | ImGui::PopID(); 152 | } 153 | ImGui::EndTable(); 154 | } 155 | /// @end Table 156 | 157 | /// @begin Button 158 | hb5.BeginLayout(); 159 | ImRad::Spacing(1); 160 | if (ImGui::Button(" New Field... ", { 110, 30 })) 161 | { 162 | OnNewField(); 163 | } 164 | hb5.AddSize(0, 110); 165 | /// @end Button 166 | 167 | /// @begin Spacer 168 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 169 | ImRad::Dummy({ hb5.GetSize(), 20 }); 170 | hb5.AddSize(1, ImRad::HBox::Stretch(1)); 171 | /// @end Spacer 172 | 173 | bool exprValid = true; 174 | /*if (type == "bool" || type == "float" || type == "int") 175 | exprValid = stx::count_if(expr, [](char c) { return !std::isspace(c); });*/ 176 | //combo.items requires carefully embedded nulls so disable editing here 177 | if (type == "std::vector" && 178 | (expr.empty() || expr[0] != '{' || expr.find('{', 1) != std::string::npos || expr.back() != '}')) 179 | exprValid = false; 180 | if (forceReference && expr.empty()) 181 | exprValid = false; 182 | 183 | /// @begin Button 184 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 185 | ImGui::BeginDisabled(!exprValid); 186 | if (ImGui::Button("OK", { 90, 30 })) 187 | { 188 | OkButton_Change(); 189 | } 190 | hb5.AddSize(1, 90); 191 | ImGui::EndDisabled(); 192 | /// @end Button 193 | 194 | /// @begin Button 195 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 196 | if (ImGui::Button("Cancel", { 90, 30 }) || 197 | ImGui::Shortcut(ImGuiKey_Escape)) 198 | { 199 | ClosePopup(ImRad::Cancel); 200 | } 201 | hb5.AddSize(1, 90); 202 | /// @end Button 203 | 204 | /// @separator 205 | ImGui::EndPopup(); 206 | } 207 | ImGui::PopStyleVar(); 208 | ImGui::PopStyleVar(); 209 | /// @end TopWindow 210 | } 211 | 212 | void BindingDlg::Refresh() 213 | { 214 | vars = codeGen->GetVarExprs(showAll ? "" : type, true, curArray); 215 | } 216 | 217 | void BindingDlg::OnNewField() 218 | { 219 | newFieldPopup.mode = NewFieldPopup::NewField; 220 | newFieldPopup.varType = ""; //allow all types f.e. vector for taking its .size() 221 | newFieldPopup.scope = ""; 222 | newFieldPopup.codeGen = codeGen; 223 | newFieldPopup.OpenPopup([this] { 224 | Refresh(); 225 | }); 226 | } 227 | 228 | void BindingDlg::OnVarClicked() 229 | { 230 | int idx = ImGui::TableGetRowIndex() - 1; //header row 231 | if (type == "std::string") 232 | expr += "{" + vars[idx].first + "}"; 233 | else if (type == "std::vector") 234 | expr = "{" + vars[idx].first + "}"; 235 | else 236 | expr = vars[idx].first; 237 | focusExpr = true; 238 | } 239 | 240 | int BindingDlg::OnTextInputFilter(ImGuiInputTextCallbackData& data) 241 | { 242 | return DefaultCharFilter(&data); 243 | } 244 | 245 | void BindingDlg::Init() 246 | { 247 | showAll = type == "std::string"; 248 | Refresh(); 249 | } 250 | 251 | void BindingDlg::ResetLayout() 252 | { 253 | // ImGui::GetCurrentWindow()->HiddenFramesCannotSkipItems = 2; 254 | hb1.Reset(); 255 | hb3.Reset(); 256 | hb5.Reset(); 257 | } 258 | 259 | void BindingDlg::OkButton_Change() 260 | { 261 | if (forceReference && !cpp::is_lvalue(expr)) { 262 | messageBox.title = "Expression"; 263 | messageBox.message = "\"" + name + "\" field requires an l-value expression"; 264 | messageBox.buttons = ImRad::Ok; 265 | messageBox.OpenPopup(); 266 | return; 267 | } 268 | ClosePopup(ImRad::Ok); 269 | } 270 | -------------------------------------------------------------------------------- /src/ui_binding.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // github.com/xyz 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include "imrad.h" 9 | #include "cppgen.h" 10 | 11 | class BindingDlg 12 | { 13 | public: 14 | /// @begin interface 15 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 16 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 17 | void Draw(); 18 | 19 | void OnNewField(); 20 | void OnVarClicked(); 21 | int OnTextInputFilter(ImGuiInputTextCallbackData& args); 22 | void Refresh(); 23 | 24 | std::string name; 25 | std::string expr; 26 | std::string type; 27 | CppGen* codeGen; 28 | std::string curArray; 29 | bool forceReference; 30 | ImFont* font = nullptr; 31 | /// @end interface 32 | 33 | private: 34 | /// @begin impl 35 | void ResetLayout(); 36 | void Init(); 37 | 38 | void OkButton_Change(); 39 | 40 | ImGuiID ID = 0; 41 | ImRad::ModalResult modalResult; 42 | std::function callback; 43 | bool showAll; 44 | std::vector> vars; 45 | bool focusExpr = false; 46 | ImRad::HBox hb1; 47 | ImRad::HBox hb3; 48 | ImRad::HBox hb5; 49 | /// @end impl 50 | }; 51 | 52 | extern BindingDlg bindingDlg; 53 | -------------------------------------------------------------------------------- /src/ui_class_wizard.cpp: -------------------------------------------------------------------------------- 1 | // Generated with imgui-designer 0.1 2 | // github.com/xyz 3 | #include "ui_class_wizard.h" 4 | #include "ui_new_field.h" 5 | #include "ui_message_box.h" 6 | #include 7 | #include 8 | 9 | ClassWizard classWizard; 10 | 11 | //todo: need to call our own copy otherwise ImGui thinks messageBox is not ClassWizard's child 12 | //and hides CW when OpenPopup 13 | //static MessageBox msgBox; 14 | 15 | void ClassWizard::OpenPopup() 16 | { 17 | requestClose = false; 18 | ImGui::OpenPopup(ID); 19 | Refresh(); 20 | } 21 | 22 | void ClassWizard::ClosePopup() 23 | { 24 | requestClose = true; 25 | } 26 | 27 | void ClassWizard::Refresh() 28 | { 29 | className = codeGen->GetName(); 30 | varName = codeGen->GetVName(); 31 | 32 | std::string stype = className; 33 | if (stypeIdx < stypes.size()) 34 | stype = stypes[stypeIdx]; 35 | stypes = codeGen->GetStructTypes(); 36 | stypes.insert(stypes.begin(), className); 37 | stypeIdx = stx::find(stypes, stype) - stypes.begin(); 38 | if (stypeIdx == stypes.size()) 39 | stypeIdx = 0; 40 | 41 | fields = codeGen->GetVars(stypeIdx ? stypes[stypeIdx] : ""); 42 | stx::erase_if(fields, [](CppGen::Var& var) { 43 | //CppGen exports user code as-is so no modification allowed here 44 | if (var.flags & CppGen::Var::UserCode) 45 | return true; 46 | //skip member functions 47 | if (var.type.back() == ')') 48 | return true; 49 | return false; 50 | }); 51 | 52 | used.clear(); 53 | FindUsed(root, used); 54 | } 55 | 56 | void ClassWizard::FindUsed(UINode* node, std::vector& used) 57 | { 58 | for (int i = 0; i < 2; ++i) 59 | { 60 | const auto& props = i ? node->Events() : node->Properties(); 61 | for (const auto& p : props) { 62 | if (!p.property) 63 | continue; 64 | auto vars = p.property->used_variables(); 65 | for (const auto& var : vars) { 66 | assert(var.find_first_of("[.") == std::string::npos); 67 | used.push_back(var); 68 | } 69 | } 70 | } 71 | for (const auto& child : node->children) 72 | FindUsed(child.get(), used); 73 | } 74 | 75 | void ClassWizard::Draw() 76 | { 77 | const float BWIDTH = 150; 78 | bool doRenameField = false; 79 | /// @begin TopWindow 80 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 12, 12 }); 81 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 12, 5 }); 82 | ImGui::SetNextWindowSize({ 800, 600 }); 83 | ID = ImGui::GetID("Class Wizard"); 84 | bool tmpOpen = true; 85 | if (ImGui::BeginPopupModal("Class Wizard", &tmpOpen, ImGuiWindowFlags_None)) 86 | { 87 | if (requestClose) 88 | ImGui::CloseCurrentPopup(); 89 | /// @separator 90 | 91 | newFieldPopup.Draw(); 92 | messageBox.Draw(); //doesn't work from here 93 | 94 | /// @begin Text 95 | ImGui::Text("Class Name"); 96 | /// @end Text 97 | 98 | /// @begin ComboBox 99 | ImGui::SetNextItemWidth(200); 100 | if (ImGui::BeginCombo("##className", stypes[stypeIdx].c_str())) 101 | { 102 | for (size_t i = 0; i < stypes.size(); ++i) 103 | { 104 | if (ImGui::Selectable(stypes[i].c_str(), i == stypeIdx)) { 105 | stypeIdx = i; 106 | Refresh(); 107 | } 108 | } 109 | ImGui::EndCombo(); 110 | } 111 | /// @end ComboBox 112 | 113 | /// @begin Button 114 | ImGui::SameLine(); 115 | if (ImGui::Button(" Rename... ")) 116 | { 117 | newFieldPopup.codeGen = codeGen; 118 | newFieldPopup.mode = stypeIdx ? NewFieldPopup::RenameStruct : NewFieldPopup::RenameWindow; 119 | newFieldPopup.varOldName = stypes[stypeIdx]; 120 | newFieldPopup.OpenPopup([this] { 121 | *modified = true; 122 | stypes[stypeIdx] = newFieldPopup.varName; //Refresh will keep the selection 123 | Refresh(); 124 | }); 125 | } 126 | /// @end Button 127 | 128 | /// @being Button 129 | ImGui::SameLine(); 130 | if (ImGui::Button(" Add New... ")) 131 | { 132 | newFieldPopup.codeGen = codeGen; 133 | newFieldPopup.mode = NewFieldPopup::NewStruct; 134 | newFieldPopup.OpenPopup([this] { 135 | *modified = true; 136 | stypes.push_back(newFieldPopup.varName); 137 | stypeIdx = stypes.size() - 1; //Refresh will keep selection 138 | Refresh(); 139 | }); 140 | } 141 | /// @end Button 142 | 143 | /// @begin Child 144 | ImGui::Spacing(); 145 | ImGui::Spacing(); 146 | ImGui::BeginChild("child2551", { 0, -45 }, ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened, 0); 147 | float autoSizeW = (ImGui::GetContentRegionAvail().x - 150) / 1; 148 | ImGui::Columns(2, "", false); 149 | ImGui::SetColumnWidth(0, autoSizeW); 150 | ImGui::SetColumnWidth(1, BWIDTH + ImGui::GetStyle().ItemSpacing.x); 151 | 152 | /// @begin Text 153 | ImGui::Text("Fields"); 154 | /// @end Text 155 | 156 | /// @begin Table 157 | if (ImGui::BeginTable("table", 4, ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable /*| ImGuiTableFlags_RowBg*/, { 0, -1 })) 158 | { 159 | ImGui::TableSetupColumn("Section", ImGuiTableColumnFlags_WidthFixed, 20); 160 | ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 1); 161 | ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch, 1); 162 | ImGui::TableSetupColumn("Init", ImGuiTableColumnFlags_WidthStretch, 1); 163 | ImGui::TableSetupScrollFreeze(0, 1); 164 | ImGui::TableHeadersRow(); 165 | 166 | for (int i = 0; i < fields.size(); ++i) 167 | { 168 | ImGui::TableNextRow(); 169 | ImGui::TableSetColumnIndex(0); 170 | ImGui::PushID(i); 171 | /// @separator 172 | 173 | const auto& var = fields[i]; 174 | 175 | //ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(255, 255, 255, 255)); 176 | //ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(192, 192, 192, 255)); 177 | 178 | bool unused = !stx::count(used, var.name); 179 | if (unused) 180 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff400040); 181 | 182 | const char* icon = 183 | (var.flags & CppGen::Var::Impl) ? ICON_FA_LOCK : 184 | (var.flags & CppGen::Var::Interface) ? ICON_FA_CUBE : 185 | ""; 186 | const char* tooltip = 187 | (var.flags & CppGen::Var::Impl) ? "private" : 188 | (var.flags & CppGen::Var::Interface) ? "public" : 189 | "unknown"; 190 | /// @begin Selectable 191 | if (ImGui::Selectable(icon, selRow == i, ImGuiSelectableFlags_SpanAllColumns)) 192 | selRow = i; 193 | if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) 194 | doRenameField = true; 195 | //if (ImGui::IsItemHovered() && ImGui::GetMousePos().x < 20) 196 | // ImGui::SetTooltip(tooltip); 197 | /// @end Selectable 198 | 199 | /// @begin Selectable 200 | ImGui::TableNextColumn(); 201 | ImGui::Selectable(var.name.c_str(), selRow == i); 202 | /// @end Selectable 203 | 204 | /// @begin Selectable 205 | ImGui::TableNextColumn(); 206 | ImGui::Selectable(var.type.c_str(), selRow == i); 207 | /// @end Selectable 208 | 209 | /// @begin Selectable 210 | ImGui::TableNextColumn(); 211 | ImGui::Selectable(var.init.c_str(), selRow == i); 212 | /// @end Selectable 213 | 214 | if (unused) 215 | ImGui::PopStyleColor(); 216 | 217 | /// @separator 218 | ImGui::PopID(); 219 | } 220 | 221 | ImGui::EndTable(); 222 | } 223 | /// @end Table 224 | 225 | /// @begin Button 226 | ImGui::NextColumn(); 227 | if (ImGui::Button("Add Field...", { BWIDTH, 0 })) 228 | { 229 | newFieldPopup.codeGen = codeGen; 230 | newFieldPopup.mode = NewFieldPopup::NewField; 231 | newFieldPopup.scope = stypeIdx ? stypes[stypeIdx] : ""; 232 | newFieldPopup.varType = ""; 233 | newFieldPopup.OpenPopup([this] { 234 | *modified = true; 235 | Refresh(); 236 | }); 237 | } 238 | /// @end Button 239 | 240 | /// @begin Button 241 | ImRad::Spacing(4); 242 | ImGui::BeginDisabled(selRow < 0 || selRow >= (int)fields.size()); 243 | if (ImGui::Button("Rename Field...", { BWIDTH, 0 }) || 244 | (!ImRad::IsCurrentItemDisabled() && doRenameField)) 245 | { 246 | newFieldPopup.codeGen = codeGen; 247 | newFieldPopup.mode = NewFieldPopup::RenameField; 248 | newFieldPopup.scope = stypeIdx ? stypes[stypeIdx] : ""; 249 | newFieldPopup.varOldName = fields[selRow].name; 250 | newFieldPopup.OpenPopup([this] { 251 | *modified = true; 252 | root->RenameFieldVars(newFieldPopup.varOldName, newFieldPopup.varName); 253 | Refresh(); 254 | }); 255 | } 256 | ImGui::EndDisabled(); 257 | /// @end Button 258 | 259 | /// @begin Button 260 | ImRad::Spacing(4); 261 | ImGui::BeginDisabled(selRow < 0 || selRow >= (int)fields.size()); 262 | if (ImGui::Button("Remove Field", { BWIDTH, 0 })) 263 | { 264 | std::string name = fields[selRow].name; 265 | if (!stypeIdx && stx::count(used, name)) 266 | { 267 | messageBox.title = "Remove variable"; 268 | messageBox.message = "Remove used variable '" + name + "' ?"; 269 | messageBox.buttons = ImRad::Yes | ImRad::No; 270 | messageBox.OpenPopup([this,name](ImRad::ModalResult mr) { 271 | if (mr == ImRad::Yes) { 272 | *modified = true; 273 | codeGen->RemoveVar(name); 274 | Refresh(); 275 | } 276 | }); 277 | } 278 | else 279 | { 280 | *modified = true; 281 | codeGen->RemoveVar(name, stypeIdx ? stypes[stypeIdx] : ""); 282 | Refresh(); 283 | } 284 | } 285 | ImGui::EndDisabled(); 286 | /// @end Button 287 | 288 | /// @begin Button 289 | ImRad::Spacing(2); 290 | ImGui::BeginDisabled(stypeIdx); 291 | if (ImGui::Button("Remove Unused", { BWIDTH, 0 })) 292 | { 293 | for (const auto& fi : fields) 294 | { 295 | if (!stx::count(used, fi.name)) { 296 | *modified = true; 297 | codeGen->RemoveVar(fi.name); 298 | } 299 | } 300 | Refresh(); 301 | } 302 | ImGui::EndDisabled(); 303 | /// @end Button 304 | 305 | ImGui::EndChild(); 306 | /// @end Child 307 | 308 | /// @begin Dummy 309 | ImRad::Spacing(2); 310 | ImGui::Dummy({ ImGui::GetContentRegionAvail().x - 110, 0 }); 311 | /// @end Dummy 312 | 313 | /// @begin Button 314 | ImGui::SameLine(0, 0); 315 | if (ImGui::Button("OK", { 110, 30 }) || ImGui::Shortcut(ImGuiKey_Escape)) 316 | { 317 | ClosePopup(); 318 | } 319 | /// @end Button 320 | 321 | /// @separator 322 | ImGui::End(); 323 | /// @end TopWindow 324 | } 325 | ImGui::PopStyleVar(); 326 | ImGui::PopStyleVar(); 327 | } -------------------------------------------------------------------------------- /src/ui_class_wizard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Generated with imgui-designer 0.1 4 | // github.com/xyz 5 | 6 | #include 7 | #include 8 | #include 9 | #include "cppgen.h" 10 | 11 | class ClassWizard 12 | { 13 | public: 14 | void OpenPopup(); 15 | void ClosePopup(); 16 | void Draw(); 17 | 18 | public: 19 | CppGen* codeGen; 20 | UINode* root; 21 | bool* modified; 22 | 23 | private: 24 | void Refresh(); 25 | void FindUsed(UINode* node, std::vector& used); 26 | 27 | std::string varName; 28 | std::string className; 29 | std::vector stypes; 30 | std::vector fields; 31 | std::vector used; 32 | size_t stypeIdx; 33 | int selRow; 34 | 35 | private: 36 | ImGuiID ID; 37 | bool requestClose; 38 | }; 39 | 40 | extern ClassWizard classWizard; 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/ui_combo_dlg.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.1 2 | // github.com/tpecholt/imrad 3 | 4 | #include "ui_combo_dlg.h" 5 | 6 | ComboDlg comboDlg; 7 | 8 | 9 | void ComboDlg::Draw() 10 | { 11 | /// @begin TopWindow 12 | std::string id = title + "###" + std::to_string((uint64_t)this); 13 | ImGui::SetNextWindowSize({ 320, 420 }, ImGuiCond_Appearing); 14 | if (requestOpen) { 15 | requestOpen = false; 16 | ImGui::OpenPopup(id.c_str()); 17 | } 18 | ImVec2 center = ImGui::GetMainViewport()->GetCenter(); 19 | ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); 20 | bool tmpOpen = true; 21 | if (ImGui::BeginPopupModal(id.c_str(), &tmpOpen, ImGuiWindowFlags_NoCollapse)) 22 | { 23 | if (requestClose) 24 | ImGui::CloseCurrentPopup(); 25 | /// @separator 26 | 27 | /// @begin Input 28 | if (ImGui::IsWindowAppearing()) 29 | ImGui::SetKeyboardFocusHere(); 30 | ImGui::PushFont(font); 31 | ImGui::InputTextMultiline("##value", &value, { -1, -44 }, ImGuiInputTextFlags_Multiline); 32 | ImGui::PopFont(); 33 | /// @end Input 34 | 35 | /// @begin Table 36 | ImGui::Spacing(); 37 | ImGui::Spacing(); 38 | ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 1, 1 }); 39 | if (ImGui::BeginTable("table2416128940960", 2, ImGuiTableFlags_None, { 0, 0 })) 40 | { 41 | ImGui::TableSetupColumn("A", ImGuiTableColumnFlags_WidthStretch, 0); 42 | ImGui::TableSetupColumn("B", ImGuiTableColumnFlags_WidthFixed, 0); 43 | ImGui::TableNextRow(); 44 | ImGui::TableSetColumnIndex(0); 45 | /// @separator 46 | 47 | /// @begin Button 48 | ImGui::TableNextColumn(); 49 | if (ImGui::Button("OK", { 100, 30 })) 50 | { 51 | ClosePopup(); 52 | callback(ImRad::Ok); 53 | } 54 | /// @end Button 55 | 56 | /// @begin Button 57 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 58 | if (ImGui::Button("Cancel", { 100, 30 }) || 59 | ImGui::IsKeyPressed(ImGuiKey_Escape)) 60 | { 61 | ClosePopup(); 62 | } 63 | /// @end Button 64 | 65 | 66 | /// @separator 67 | ImGui::EndTable(); 68 | } 69 | ImGui::PopStyleVar(); 70 | /// @end Table 71 | 72 | /// @separator 73 | ImGui::EndPopup(); 74 | } 75 | /// @end TopWindow 76 | } 77 | 78 | void ComboDlg::OpenPopup(std::function clb) 79 | { 80 | callback = clb; 81 | requestOpen = true; 82 | requestClose = false; 83 | 84 | // Add your init code here 85 | } 86 | 87 | void ComboDlg::ClosePopup() 88 | { 89 | requestClose = true; 90 | } 91 | -------------------------------------------------------------------------------- /src/ui_combo_dlg.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.1 2 | // github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include "imrad.h" 9 | 10 | class ComboDlg 11 | { 12 | public: 13 | /// @interface 14 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 15 | void ClosePopup(); 16 | void Draw(); 17 | 18 | std::string title = "Items"; 19 | std::string value; 20 | ImFont* font = nullptr; 21 | 22 | private: 23 | /// @impl 24 | 25 | bool requestOpen = false; 26 | bool requestClose = false; 27 | std::function callback; 28 | 29 | }; 30 | 31 | extern ComboDlg comboDlg; 32 | -------------------------------------------------------------------------------- /src/ui_error_box.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #include "ui_error_box.h" 5 | #include 6 | 7 | ErrorBox errorBox; 8 | 9 | 10 | std::string WordWrap(const std::string& str, float multilineWidth) 11 | { 12 | float textSize = 0; 13 | std::string tmpStr = ""; 14 | std::string finalStr = ""; 15 | int curChr = 0; 16 | while (curChr < str.size()) { 17 | 18 | if (str[curChr] == '\n') { 19 | finalStr += tmpStr + "\n"; 20 | tmpStr = ""; 21 | } 22 | else 23 | tmpStr += str[curChr]; 24 | textSize = ImGui::CalcTextSize(tmpStr.c_str()).x; 25 | 26 | if (textSize > multilineWidth) { 27 | int lastSpace = (int)tmpStr.size() - 1; 28 | while (tmpStr[lastSpace] != ' ' && lastSpace > 0) 29 | lastSpace--; 30 | if (lastSpace == 0) 31 | lastSpace = (int)tmpStr.size() - 2; 32 | finalStr += tmpStr.substr(0, lastSpace + 1) + "\n"; 33 | if (lastSpace + 1 > tmpStr.size()) 34 | tmpStr = ""; 35 | else 36 | tmpStr = tmpStr.substr(lastSpace + 1); 37 | } 38 | curChr++; 39 | } 40 | if (tmpStr.size() > 0) 41 | finalStr += tmpStr; 42 | 43 | return finalStr; 44 | }; 45 | 46 | int TreeNode(const std::string& str, size_t& i, int level, bool skip) 47 | { 48 | struct ScopeGuard { 49 | ScopeGuard(std::function f) : func(f) {} 50 | ~ScopeGuard() { func(); } 51 | std::function func; 52 | }; 53 | for (int n = 0; i < str.size(); ++n) 54 | { 55 | ImGui::PushID(n); 56 | ScopeGuard g([] { ImGui::PopID(); }); 57 | 58 | size_t j = str.find("\n", i); 59 | int nlevel = 0; 60 | std::string label; 61 | if (j == std::string::npos) { 62 | label = str.substr(i); 63 | i = str.size(); 64 | } 65 | else { 66 | label = str.substr(i, j - i); 67 | i = j + 1; 68 | for (nlevel = 0; i < str.size() && str[i] == '\t'; ++i) 69 | ++nlevel; 70 | } 71 | 72 | if (nlevel > level) { 73 | bool open = true; 74 | if (!skip) { 75 | //auto clr = ImGui::ColorConvertFloat4ToU32(ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered)); 76 | ImGui::PushStyleColor(ImGuiCol_Text, !level ? 0xff600000 : 0xff000000); 77 | open = ImGui::TreeNodeEx(label.c_str(), ImGuiTreeNodeFlags_DefaultOpen); 78 | ImGui::PopStyleColor(); 79 | } 80 | nlevel = TreeNode(str, i, nlevel, skip || !open); 81 | if (!skip && open) 82 | ImGui::TreePop(); 83 | return nlevel; 84 | } 85 | else { 86 | if (!skip) { 87 | ImVec2 pad = ImGui::GetStyle().FramePadding; 88 | if (!level) { 89 | ImGui::Bullet(); 90 | ImGui::SameLine(0, pad.x * 3); 91 | } 92 | else { 93 | ImGui::Text(" "); 94 | ImGui::SameLine(0, pad.x * 3); 95 | } 96 | float w = ImGui::GetContentRegionAvail().x - pad.x; 97 | label = WordWrap(label, w); 98 | ImGui::Selectable(label.c_str(), false); 99 | } 100 | if (nlevel < level) 101 | return nlevel; 102 | } 103 | } 104 | return 0; 105 | } 106 | 107 | //----------------------------------------------------------- 108 | 109 | void ErrorBox::OpenPopup(std::function clb) 110 | { 111 | callback = clb; 112 | modalResult = ImRad::None; 113 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 114 | ioUserData->dimBgRatio = 1.f; 115 | ImGui::OpenPopup(ID); 116 | Init(); 117 | } 118 | 119 | void ErrorBox::ClosePopup(ImRad::ModalResult mr) 120 | { 121 | modalResult = mr; 122 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 123 | ioUserData->dimBgRatio = 0.f; 124 | } 125 | 126 | void ErrorBox::Init() 127 | { 128 | // TODO: Add your code here 129 | } 130 | 131 | void ErrorBox::Draw() 132 | { 133 | /// @style Dark 134 | /// @unit px 135 | /// @begin TopWindow 136 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 137 | ID = ImGui::GetID("###ErrorBox"); 138 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 10, 10 }); 139 | ImGui::SetNextWindowSize({ 640, 480 }, ImGuiCond_FirstUseEver); //{ 640, 480 } 140 | ImGui::SetNextWindowSizeConstraints({ 0, 0 }, { FLT_MAX, FLT_MAX }); 141 | bool tmpOpen = true; 142 | if (ImGui::BeginPopupModal(ImRad::Format("{}###ErrorBox", title).c_str(), &tmpOpen, ImGuiWindowFlags_NoCollapse)) 143 | { 144 | if (ioUserData->activeActivity != "") 145 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 146 | if (modalResult != ImRad::None) 147 | { 148 | ImGui::CloseCurrentPopup(); 149 | if (modalResult != ImRad::Cancel) 150 | callback(modalResult); 151 | } 152 | if (ImGui::Shortcut(ImGuiKey_Escape)) 153 | ClosePopup(); 154 | /// @separator 155 | 156 | // TODO: Add Draw calls of dependent popup windows here 157 | 158 | /// @begin Text 159 | vb1.BeginLayout(); 160 | ImRad::Spacing(1); 161 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff0099ff); 162 | ImGui::TextUnformatted(ImRad::Format(" {} ", ICON_FA_CIRCLE_EXCLAMATION).c_str()); 163 | vb1.AddSize(1, ImRad::VBox::ItemSize); 164 | ImGui::PopStyleColor(); 165 | /// @end Text 166 | 167 | /// @begin Text 168 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 169 | ImGui::PushTextWrapPos(0); 170 | ImGui::TextUnformatted(ImRad::Format("{}", message).c_str()); 171 | ImGui::PopTextWrapPos(); 172 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 173 | /// @end Text 174 | 175 | /// @begin Child 176 | ImRad::Spacing(3); 177 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0, 4 }); 178 | ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xffc0c0c0); 179 | ImGui::BeginChild("child1", { -1, vb1.GetSize() }, ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_NoSavedSettings); 180 | { 181 | /// @separator 182 | 183 | /// @begin CustomWidget 184 | CustomWidget_Draw({ -1, -1 }); 185 | /// @end CustomWidget 186 | 187 | /// @separator 188 | ImGui::EndChild(); 189 | } 190 | ImGui::PopStyleColor(); 191 | ImGui::PopStyleVar(); 192 | vb1.AddSize(4, ImRad::VBox::Stretch(1)); 193 | /// @end Child 194 | 195 | /// @begin Spacer 196 | hb3.BeginLayout(); 197 | ImRad::Spacing(1); 198 | ImRad::Dummy({ hb3.GetSize(), 0 }); 199 | vb1.AddSize(2, ImRad::VBox::ItemSize); 200 | hb3.AddSize(0, ImRad::HBox::Stretch(1)); 201 | /// @end Spacer 202 | 203 | /// @begin Button 204 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 205 | if (ImGui::Button("OK", { 100, 30 })) 206 | { 207 | ClosePopup(ImRad::Ok); 208 | } 209 | vb1.UpdateSize(0, 30); 210 | hb3.AddSize(1, 100); 211 | if (ImGui::IsWindowAppearing()) 212 | ImGui::SetKeyboardFocusHere(-1); 213 | /// @end Button 214 | 215 | /// @begin Spacer 216 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 217 | ImRad::Dummy({ hb3.GetSize(), 0 }); 218 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 219 | hb3.AddSize(1, ImRad::HBox::Stretch(1)); 220 | /// @end Spacer 221 | 222 | /// @separator 223 | ImGui::EndPopup(); 224 | } 225 | ImGui::PopStyleVar(); 226 | /// @end TopWindow 227 | } 228 | 229 | void ErrorBox::ResetLayout() 230 | { 231 | // ImGui::GetCurrentWindow()->HiddenFramesCannotSkipItems = 2; 232 | vb1.Reset(); 233 | hb3.Reset(); 234 | } 235 | 236 | void ErrorBox::CustomWidget_Draw(const ImRad::CustomWidgetArgs& args) 237 | { 238 | size_t i = 0; 239 | ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true); 240 | TreeNode(error, i, 0, false); 241 | ImGui::PopItemFlag(); 242 | } 243 | -------------------------------------------------------------------------------- /src/ui_error_box.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include "imrad.h" 6 | 7 | class ErrorBox 8 | { 9 | public: 10 | /// @begin interface 11 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 12 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 13 | void Draw(); 14 | 15 | std::string title = "title"; 16 | std::string message = "message"; 17 | std::string error; 18 | /// @end interface 19 | 20 | private: 21 | /// @begin impl 22 | void ResetLayout(); 23 | void Init(); 24 | 25 | void CustomWidget_Draw(const ImRad::CustomWidgetArgs& args); 26 | 27 | ImGuiID ID = 0; 28 | ImRad::ModalResult modalResult; 29 | std::function callback; 30 | ImRad::VBox vb1; 31 | ImRad::HBox hb3; 32 | /// @end impl 33 | }; 34 | 35 | extern ErrorBox errorBox; 36 | -------------------------------------------------------------------------------- /src/ui_explorer.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_explorer.h" 2 | #include "ui_message_box.h" 3 | #include "utils.h" 4 | #include "stx.h" 5 | #include "imrad.h" 6 | #include "cppgen.h" 7 | #include 8 | #include 9 | #include 10 | 11 | struct ExplorerEntry 12 | { 13 | std::string path; 14 | bool folder; 15 | bool generated; 16 | std::string fileName; 17 | std::time_t last_write_time; 18 | std::string modified; 19 | }; 20 | 21 | std::string explorerPath; 22 | int explorerFilter = 0; 23 | ImGuiTableColumnSortSpecs explorerSorting; 24 | 25 | static int pathSel = -1; 26 | static std::vector data; 27 | static std::vector suggestions; 28 | static int suggestionSel = -1; 29 | static std::string lastPath; 30 | static bool scrollBack = false; 31 | static bool showSuggestions = false; 32 | static bool autocompleted = false; 33 | enum MoveFocus { None, PathInput, FirstEntry }; 34 | static MoveFocus moveFocus = None; 35 | 36 | bool IsHeaderFile(const fs::path& p) 37 | { 38 | auto ext = u8string(p.extension()); 39 | return ext == ".h" || ext == ".hpp" || ext == ".hxx"; 40 | } 41 | 42 | bool IsCppFile(const fs::path& p) 43 | { 44 | auto ext = u8string(p.extension()); 45 | return ext == ".c" || ext == ".cpp" || ext == ".cxx"; 46 | } 47 | 48 | void ReloadExplorer(const CppGen& codeGen) 49 | { 50 | data.clear(); 51 | auto path = u8path(explorerPath); 52 | //+= '/' so it works with paths like "c:" 53 | if (!path.has_root_directory()) 54 | path = u8path(explorerPath + (char)fs::path::preferred_separator); 55 | path.make_preferred(); 56 | explorerPath = u8string(path); 57 | 58 | if (!fs::is_directory(path)) { 59 | messageBox.title = "Error"; 60 | messageBox.message = "Can't open \"" + explorerPath + "\""; 61 | messageBox.buttons = ImRad::Ok; 62 | messageBox.OpenPopup(); 63 | return; 64 | } 65 | 66 | scrollBack = true; 67 | std::ostringstream os; 68 | auto fsnow = fs::file_time_type::clock::now(); 69 | auto snow = std::chrono::system_clock::now(); 70 | os.imbue(std::locale("")); 71 | std::error_code ec; 72 | for (fs::directory_iterator it(path, ec); it != fs::directory_iterator(); ++it) 73 | { 74 | if (u8string(it->path().stem())[0] == '.') 75 | continue; 76 | if (!it->is_directory() && 77 | (!explorerFilter && !IsHeaderFile(it->path()))) 78 | continue; 79 | ExplorerEntry entry; 80 | entry.path = u8string(it->path()); 81 | entry.folder = it->is_directory(); 82 | entry.generated = (IsHeaderFile(it->path()) || IsCppFile(it->path())) && 83 | codeGen.ReadGenVersion(entry.path); 84 | entry.fileName = u8string(it->path().filename()); 85 | auto sctp = std::chrono::time_point_cast( 86 | it->last_write_time() - fsnow + snow); 87 | entry.last_write_time = std::chrono::system_clock::to_time_t(sctp); 88 | auto* tm = std::localtime(&entry.last_write_time); 89 | os.str(""); 90 | os << std::put_time(tm, "%x %X"); 91 | entry.modified = os.str(); 92 | data.push_back(std::move(entry)); 93 | } 94 | stx::sort(data, [](const ExplorerEntry& a, const ExplorerEntry& b) { 95 | if (a.folder != b.folder) 96 | return a.folder; 97 | if (!explorerSorting.ColumnIndex) { 98 | if (explorerSorting.SortDirection == ImGuiSortDirection_Ascending) 99 | return path_cmp(a.fileName, b.fileName); 100 | else 101 | return path_cmp(b.fileName, a.fileName); 102 | } 103 | else { 104 | if (explorerSorting.SortDirection == ImGuiSortDirection_Ascending) 105 | return a.last_write_time < b.last_write_time; 106 | else 107 | return a.last_write_time > b.last_write_time; 108 | } 109 | }); 110 | if (!u8path(explorerPath).relative_path().empty()) { 111 | std::string ppath = u8string(u8path(explorerPath).parent_path()); 112 | ExplorerEntry e; 113 | e.path = ppath; 114 | e.folder = true; 115 | e.fileName = ".."; 116 | data.insert(data.begin(), e); 117 | } 118 | } 119 | 120 | void GetSuggestions() 121 | { 122 | suggestions.clear(); 123 | std::error_code ec; 124 | auto path = u8path(explorerPath); 125 | //+= '/' so it works with paths like "c:" 126 | if (!path.has_root_directory()) 127 | path = u8path(explorerPath + (char)fs::path::preferred_separator); 128 | std::string match = u8string(path.stem()); 129 | for (fs::directory_iterator it(path.parent_path(), ec); it != fs::directory_iterator(); ++it) 130 | { 131 | std::string stem = u8string(it->path().stem()); 132 | if (!it->is_directory() || stem[0] == '.') 133 | continue; 134 | if (stem.size() > match.size()) 135 | stem.resize(match.size()); 136 | if (path_cmp(stem, match) || path_cmp(match, stem)) 137 | continue; 138 | std::string fileName = u8string(it->path().filename()); 139 | suggestions.push_back(std::move(fileName)); 140 | } 141 | stx::sort(suggestions, [](const std::string& a, const std::string& b) { 142 | return path_cmp(a, b); 143 | }); 144 | } 145 | 146 | int ExplorerPathCallback(ImGuiInputTextCallbackData* data) 147 | { 148 | if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) { 149 | return DefaultCharFilter(data); 150 | } 151 | else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) 152 | { 153 | if (data->EventKey == ImGuiKey_UpArrow && suggestions.size()) { 154 | if (suggestionSel < 0) { 155 | suggestionSel = (int)suggestions.size() - 1; 156 | lastPath = explorerPath; 157 | } 158 | else if (--suggestionSel < 0) 159 | suggestionSel = -1; 160 | } 161 | else if (data->EventKey == ImGuiKey_DownArrow && suggestions.size()) { 162 | if (suggestionSel < 0) { 163 | suggestionSel = 0; 164 | lastPath = explorerPath; 165 | } 166 | else if (++suggestionSel == suggestions.size()) 167 | suggestionSel = -1; 168 | } 169 | 170 | //commit suggestion 171 | std::string str = lastPath; 172 | if (suggestionSel >= 0) { 173 | size_t i = explorerPath.find_last_of("/\\"); 174 | if (i == std::string::npos) 175 | str = explorerPath + (char)fs::path::preferred_separator; 176 | else 177 | str = explorerPath.substr(0, i + 1); 178 | str += suggestions[suggestionSel]; 179 | } 180 | if (str.size() + 1 < data->BufSize) { //no reallocation occured 181 | autocompleted = true; 182 | data->BufDirty = true; 183 | strcpy(data->Buf, str.c_str()); 184 | data->BufTextLen = (int)str.size(); 185 | data->SelectionStart = data->SelectionEnd = data->CursorPos = data->BufTextLen; 186 | } 187 | } 188 | return 0; 189 | } 190 | 191 | void ExplorerUI(const CppGen& codeGen, std::function openFileFunc) 192 | { 193 | explorerPath.reserve(1024); //ImGuiInputText_HistoryCallback doesn't allow buffer reallocation?? 194 | if (explorerPath == "") 195 | explorerPath = u8string(fs::current_path()); 196 | 197 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 4, 4 }); 198 | ImGui::Begin("Explorer"); 199 | if (ImGui::IsWindowAppearing()) { 200 | ReloadExplorer(codeGen); 201 | showSuggestions = false; 202 | autocompleted = false; 203 | moveFocus = FirstEntry; 204 | } 205 | 206 | //clickable path box 207 | const int sp = 4; 208 | const std::string ELIDE_STR = "..."; 209 | std::string inputId = "##explorerCwd"; 210 | if (ImGui::GetFocusID() != ImGui::GetID(inputId.c_str())) 211 | { 212 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImGui::GetStyle().FramePadding); 213 | ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, ImGui::GetStyle().FrameRounding); 214 | ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xffd0d0d0); 215 | ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0); 216 | if (ImGui::BeginChild("cwdChild", { -ImGui::GetFrameHeight() - sp, ImGui::GetFrameHeight() }, ImGuiChildFlags_Borders, ImGuiWindowFlags_NavFlattened | ImGuiWindowFlags_NoScrollbar)) 217 | { 218 | fs::path path = u8path(explorerPath); 219 | char separator = (char)fs::path::preferred_separator; 220 | int elideStart = (int)path.has_root_name() + (int)path.has_root_directory(); 221 | int elideN = 0; 222 | ImVec2 wpad = ImGui::GetStyle().WindowPadding; 223 | int n = 0; 224 | for (const auto& p : path) 225 | ++n; 226 | for (; elideStart + elideN < n; ++elideN) { 227 | std::string tmp; 228 | int i = 0; 229 | for (const auto& p : path) { 230 | if (i < elideStart) 231 | tmp += u8string(p); 232 | else if (i == elideStart && elideN) 233 | tmp += ELIDE_STR + separator; 234 | else if (i >= elideStart + elideN) 235 | tmp += u8string(p) + separator; 236 | ++i; 237 | } 238 | if (tmp.back() == separator) 239 | tmp.pop_back(); 240 | if (elideStart + elideN + 1 == n || 241 | ImGui::CalcTextSize(tmp.c_str()).x + 2 * wpad.x < ImGui::GetWindowWidth()) 242 | break; 243 | } 244 | int i = 0; 245 | int rootDirIdx = path.has_root_directory() ? (int)path.has_root_name() : -1; 246 | int lastPathSel = pathSel; 247 | pathSel = -1; 248 | bool sepSel = false; 249 | fs::path subpath; 250 | for (const auto& p : path) { 251 | subpath /= p; 252 | if (i >= elideStart && i < elideStart + elideN) { 253 | ++i; 254 | continue; 255 | } 256 | if (p.has_root_directory()) { 257 | ImGui::SameLine(0, 0); 258 | ImGui::Text("%s", u8string(p).c_str()); 259 | if (ImGui::IsItemHovered()) 260 | sepSel = true; 261 | ++i; 262 | continue; 263 | } 264 | if (i && i != rootDirIdx + 1) { 265 | ImGui::SameLine(0, 0); 266 | std::string sep = elideN && i == elideStart + elideN ? ELIDE_STR : ""; 267 | sep += separator; 268 | ImGui::Text("%s", sep.c_str()); 269 | if (ImGui::IsItemHovered()) 270 | sepSel = true; 271 | } 272 | ImGui::SameLine(0, 0); 273 | ImGui::PushStyleColor(ImGuiCol_Text, i == lastPathSel ? 0xff0000ff : 0xff000000); 274 | ImGui::Text("%s", u8string(p).c_str()); 275 | ImGui::PopStyleColor(); 276 | if (ImGui::IsItemHovered() && i + 1 != n) 277 | pathSel = i; 278 | if (ImGui::IsItemClicked() && i + 1 != n) { 279 | explorerPath = u8string(subpath); 280 | ReloadExplorer(codeGen); 281 | moveFocus = FirstEntry; 282 | } 283 | ++i; 284 | } 285 | if (ImGui::IsWindowHovered() && !sepSel && pathSel < 0) 286 | { 287 | ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); 288 | if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) 289 | moveFocus = PathInput; 290 | } 291 | } 292 | ImGui::EndChild(); 293 | ImGui::PopStyleVar(2); 294 | ImGui::PopStyleColor(2); 295 | } 296 | 297 | ImGui::SameLine(0, 0); 298 | if (ImGui::GetContentRegionAvail().x - ImGui::GetFrameHeight() - sp <= 0) 299 | ImGui::SetNextItemWidth(0); 300 | else 301 | ImGui::SetNextItemWidth(-ImGui::GetFrameHeight() - sp); 302 | //ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xffd0d0d0); 303 | //ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0); 304 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 305 | int fl = ImGuiInputTextFlags_ElideLeft | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCharFilter; 306 | if (ImGui::InputText(inputId.c_str(), &explorerPath, fl, ExplorerPathCallback)) { 307 | if (autocompleted) 308 | autocompleted = false; 309 | else { 310 | GetSuggestions(); 311 | showSuggestions = true; 312 | suggestionSel = -1; 313 | } 314 | } 315 | //ImGui::PopStyleColor(2); 316 | ImGui::PopStyleVar(); 317 | if (moveFocus == PathInput) 318 | { 319 | ImGui::SetKeyboardFocusHere(-1); 320 | auto* state = ImGui::GetInputTextState(ImGui::GetItemID()); 321 | if (state) 322 | state->ClearSelection(); 323 | if (ImGui::IsItemFocused()) 324 | moveFocus = None; 325 | } 326 | if (ImGui::IsItemDeactivated()) { //AfterEdit()) 327 | ReloadExplorer(codeGen); 328 | showSuggestions = false; 329 | moveFocus = FirstEntry; 330 | } 331 | if (showSuggestions) 332 | { 333 | ImGui::PushFont(ImRad::GetFontByName("imrad.explorer")); 334 | ImGui::SetNextWindowPos({ ImGui::GetItemRectMin().x, ImGui::GetItemRectMax().y }); 335 | float h = ImGui::GetTextLineHeight() + ImGui::GetStyle().ItemInnerSpacing.y; 336 | float pad = ImGui::GetStyle().WindowPadding.y; 337 | const int N = 7; 338 | ImGui::SetNextWindowSize({ ImGui::GetItemRectSize().x, std::min(N, (int)suggestions.size()) * h + 2 * pad }); 339 | float selY = std::max(suggestionSel, 0) * h + pad; 340 | if (ImGui::BeginTooltip()) //Tooltip won't steal the focus as BeginPopup does 341 | { 342 | if (selY + h > ImGui::GetScrollY() + ImGui::GetWindowSize().y) 343 | ImGui::SetScrollY(selY - (N - 1) * h - pad); 344 | if (selY < ImGui::GetScrollY()) 345 | ImGui::SetScrollY(selY - pad); 346 | for (size_t i = 0; i < suggestions.size(); ++i) { 347 | /*ImGui::PushStyleColor(ImGuiCol_Text, 0xff40b0b0); 348 | ImGui::Selectable(ICON_FA_FOLDER, i == suggestionSel, ImGuiSelectableFlags_SpanAvailWidth); 349 | ImGui::PopStyleColor(); 350 | ImGui::SameLine(0, 4);*/ 351 | ImGui::Selectable(suggestions[i].c_str(), i == suggestionSel, 0); 352 | } 353 | ImGui::EndTooltip(); 354 | } 355 | ImGui::PopFont(); 356 | } 357 | 358 | ImGui::SameLine(0, sp); 359 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff404040); 360 | ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true); 361 | if (ImGui::Button(ICON_FA_ROTATE_RIGHT "##ego")) { 362 | ReloadExplorer(codeGen); 363 | moveFocus = FirstEntry; 364 | } 365 | ImGui::PopItemFlag(); 366 | ImGui::PopStyleColor(); 367 | 368 | ImGui::PushStyleColor(ImGuiCol_TableRowBg, 0xffffffff); 369 | ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, 0xffffffff); 370 | if (scrollBack) { 371 | scrollBack = false; 372 | ImGui::SetNextWindowScroll({ 0, 0 }); 373 | } 374 | float h = -ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.y; 375 | if (ImGui::BeginTable("files", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, { -1, h })) 376 | { 377 | ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); 378 | ImGui::TableSetupColumn("Modified", ImGuiTableColumnFlags_WidthFixed, 140); 379 | ImGui::TableSetupScrollFreeze(0, 1); 380 | auto* spec = ImGui::TableGetSortSpecs(); 381 | if (spec && spec->SpecsDirty) { 382 | spec->SpecsDirty = false; 383 | explorerSorting = *spec->Specs; 384 | ReloadExplorer(codeGen); 385 | } 386 | ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true); 387 | ImGui::TableHeadersRow(); 388 | ImGui::PopItemFlag(); 389 | ImGui::TableNextRow(); 390 | ImGui::TableSetColumnIndex(0); 391 | 392 | ImGui::PushFont(ImRad::GetFontByName("imrad.explorer")); 393 | int n = 0; 394 | for (const auto& entry : data) 395 | { 396 | ImGui::PushID(n++); 397 | bool source = IsHeaderFile(entry.path) || IsCppFile(entry.path); 398 | ImGui::PushStyleColor(ImGuiCol_Text, 399 | entry.folder ? 0xff40b0b0 : 400 | entry.generated ? 0xffe0a060 : 401 | source ? 0xff60a0d0 : 402 | 0xffa0a0a0 403 | ); 404 | ImGui::Selectable(entry.folder ? ICON_FA_FOLDER : 405 | entry.generated ? ICON_FA_DICE_D6 : 406 | source ? ICON_FA_FILE_CODE : 407 | ICON_FA_FILE, 408 | false, ImGuiSelectableFlags_SpanAllColumns); 409 | ImGui::PopStyleColor(); 410 | if (n == 1 && moveFocus == FirstEntry) { 411 | if (ImGui::IsItemFocused()) 412 | moveFocus = None; 413 | ImGui::SetKeyboardFocusHere(-1); 414 | } 415 | if (ImRad::IsItemDoubleClicked() || 416 | (ImGui::IsItemActive() && ImGui::IsKeyPressed(ImGuiKey_Enter))) 417 | { 418 | if (entry.folder) { 419 | explorerPath = entry.path; 420 | ReloadExplorer(codeGen); 421 | ImGui::PopID(); 422 | break; 423 | } 424 | else if (!codeGen.ReadGenVersion(entry.path)) { 425 | ShellExec(entry.path); 426 | } 427 | else { 428 | openFileFunc(entry.path); 429 | } 430 | } 431 | ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true); 432 | ImGui::SameLine(0, 4); 433 | ImGui::Selectable(entry.fileName.c_str(), false); 434 | ImGui::TableNextColumn(); 435 | ImGui::Selectable(entry.modified.c_str(), false, ImGuiSelectableFlags_Disabled); 436 | ImGui::TableNextColumn(); 437 | ImGui::PopItemFlag(); 438 | ImGui::PopID(); 439 | } 440 | ImGui::PopFont(); 441 | ImGui::EndTable(); 442 | } 443 | ImGui::PopStyleColor(2); 444 | 445 | ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xffd0d0d0); 446 | ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0); 447 | ImGui::SetNextItemWidth(-1); 448 | if (ImGui::Combo("##filter", &explorerFilter, "H Files (*.h,*.hpp,*.hxx)\0All Files (*.*)\0")) 449 | ReloadExplorer(codeGen); 450 | ImGui::PopStyleColor(2); 451 | 452 | ImGui::End(); 453 | ImGui::PopStyleVar(); 454 | } 455 | -------------------------------------------------------------------------------- /src/ui_explorer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "cppgen.h" 6 | 7 | extern int explorerFilter; 8 | extern std::string explorerPath; 9 | extern ImGuiTableColumnSortSpecs explorerSorting; 10 | 11 | void ExplorerUI(const CppGen& codeGen, std::function openFileFunc); 12 | -------------------------------------------------------------------------------- /src/ui_horiz_layout.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.8 2 | // visit github.com/tpecholt/imrad 3 | 4 | #include "ui_horiz_layout.h" 5 | #include "node_container.h" 6 | 7 | HorizLayout horizLayout; 8 | 9 | void HorizLayout::OpenPopup(std::function clb) 10 | { 11 | callback = clb; 12 | modalResult = ImRad::None; 13 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 14 | ioUserData->dimBgRatio = 1.f; 15 | ImGui::OpenPopup(ID); 16 | Init(); 17 | } 18 | 19 | void HorizLayout::ClosePopup(ImRad::ModalResult mr) 20 | { 21 | modalResult = mr; 22 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 23 | ioUserData->dimBgRatio = 0.f; 24 | } 25 | 26 | void HorizLayout::Draw() 27 | { 28 | /// @style imrad 29 | /// @unit px 30 | /// @begin TopWindow 31 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 32 | ID = ImGui::GetID("###HorizLayout"); 33 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 10, 10 }); 34 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 5, 10 }); 35 | ImGui::SetNextWindowSize({ 0, 0 }); //{ 640, 480 } 36 | bool tmpOpen = true; 37 | if (ImGui::BeginPopupModal("Table Layout Helper###HorizLayout", &tmpOpen, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse)) 38 | { 39 | if (ioUserData->activeActivity != "") 40 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 41 | if (modalResult != ImRad::None) 42 | { 43 | ImGui::CloseCurrentPopup(); 44 | if (modalResult != ImRad::Cancel) 45 | callback(modalResult); 46 | } 47 | /// @separator 48 | 49 | // TODO: Add Draw calls of dependent popup windows here 50 | 51 | /// @begin Text 52 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 53 | ImGui::TextUnformatted("Choose alignment"); 54 | ImGui::PopStyleColor(); 55 | /// @end Text 56 | 57 | /// @begin Button 58 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[alignment==0?ImGuiCol_ButtonHovered:ImGuiCol_Button]); 59 | if (ImGui::Button("\xef\x80\xb6", { 0, 0 })) 60 | { 61 | OnAlignment(); 62 | } 63 | ImGui::PopStyleColor(); 64 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 65 | ImGui::SetTooltip("Align left (removes table layout)"); 66 | /// @end Button 67 | 68 | /// @begin Button 69 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 70 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[alignment==1?ImGuiCol_ButtonHovered:ImGuiCol_Button]); 71 | if (ImGui::Button("\xef\x80\xb7", { 0, 0 })) 72 | { 73 | OnAlignment(); 74 | } 75 | ImGui::PopStyleColor(); 76 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 77 | ImGui::SetTooltip("Center"); 78 | /// @end Button 79 | 80 | /// @begin Button 81 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 82 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[alignment==2?ImGuiCol_ButtonHovered:ImGuiCol_Button]); 83 | if (ImGui::Button("\xef\x80\xb8", { 0, 0 })) 84 | { 85 | OnAlignment(); 86 | } 87 | ImGui::PopStyleColor(); 88 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 89 | ImGui::SetTooltip("Align right"); 90 | /// @end Button 91 | 92 | /// @begin Button 93 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 94 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[alignment==3?ImGuiCol_ButtonHovered:ImGuiCol_Button]); 95 | if (ImGui::Button("\xef\x80\xb9", { 0, 0 })) 96 | { 97 | OnAlignment(); 98 | } 99 | ImGui::PopStyleColor(); 100 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 101 | ImGui::SetTooltip("Align left and right"); 102 | /// @end Button 103 | 104 | /// @begin Button 105 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 106 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[alignment==4?ImGuiCol_ButtonHovered:ImGuiCol_Button]); 107 | if (ImGui::Button("\xef\x96\x8d", { 0, 0 })) 108 | { 109 | OnAlignment(); 110 | } 111 | ImGui::PopStyleColor(); 112 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) 113 | ImGui::SetTooltip("Regular spacing"); 114 | /// @end Button 115 | 116 | /// @begin Text 117 | ImRad::Spacing(1); 118 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 119 | ImGui::TextUnformatted("Item spacing"); 120 | ImGui::PopStyleColor(); 121 | /// @end Text 122 | 123 | /// @begin Selectable 124 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 1.f, 0 }); 125 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 126 | ImGui::AlignTextToFramePadding(); 127 | ImRad::Selectable("spacing", false, ImGuiSelectableFlags_DontClosePopups, { 65, 0 }); 128 | ImGui::PopItemFlag(); 129 | ImGui::PopStyleVar(); 130 | /// @end Selectable 131 | 132 | /// @begin Input 133 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 134 | if (ImGui::IsWindowAppearing()) 135 | ImGui::SetKeyboardFocusHere(); 136 | ImGui::SetNextItemWidth(100); 137 | ImGui::InputInt("##spacing", &spacing, 1); 138 | if (ImGui::IsItemActive()) 139 | ioUserData->imeType = ImRad::ImeNumber; 140 | /// @end Input 141 | 142 | /// @begin Selectable 143 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 1.f, 0 }); 144 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 145 | ImGui::AlignTextToFramePadding(); 146 | ImRad::Selectable("padding", false, ImGuiSelectableFlags_DontClosePopups, { 65, 0 }); 147 | ImGui::PopItemFlag(); 148 | ImGui::PopStyleVar(); 149 | /// @end Selectable 150 | 151 | /// @begin Input 152 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 153 | ImGui::SetNextItemWidth(100); 154 | ImGui::InputInt("##padding", &padding, 1); 155 | if (ImGui::IsItemActive()) 156 | ioUserData->imeType = ImRad::ImeNumber; 157 | /// @end Input 158 | 159 | 160 | /// @begin Text 161 | ImRad::Spacing(2); 162 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 163 | ImGui::PushTextWrapPos(0); 164 | ImGui::TextUnformatted(ImRad::Format("{} items will be aligned", selected.size()).c_str()); 165 | ImGui::PopTextWrapPos(); 166 | ImGui::PopStyleColor(); 167 | /// @end Text 168 | 169 | 170 | /// @begin Button 171 | ImRad::Spacing(2); 172 | ImGui::BeginDisabled(selected.empty()||alignment<0); 173 | if (ImGui::Button("OK", { 100, 30 })) 174 | { 175 | Work(); 176 | ClosePopup(ImRad::Ok); 177 | } 178 | ImGui::EndDisabled(); 179 | /// @end Button 180 | 181 | /// @begin Button 182 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 183 | if (ImGui::Button("Cancel", { 100, 30 }) || 184 | ImGui::Shortcut(ImGuiKey_Escape)) 185 | { 186 | ClosePopup(ImRad::Cancel); 187 | } 188 | /// @end Button 189 | 190 | /// @separator 191 | ImGui::EndPopup(); 192 | } 193 | ImGui::PopStyleVar(); 194 | ImGui::PopStyleVar(); 195 | /// @end TopWindow 196 | } 197 | 198 | void HorizLayout::OnAlignment() 199 | { 200 | if (ImGui::GetItemID() == ImGui::GetID(ICON_FA_ALIGN_LEFT)) 201 | alignment = 0; 202 | else if (ImGui::GetItemID() == ImGui::GetID(ICON_FA_ALIGN_CENTER)) 203 | alignment = 1; 204 | else if (ImGui::GetItemID() == ImGui::GetID(ICON_FA_ALIGN_RIGHT)) 205 | alignment = 2; 206 | else if (ImGui::GetItemID() == ImGui::GetID(ICON_FA_ALIGN_JUSTIFY)) 207 | alignment = 3; 208 | else if (ImGui::GetItemID() == ImGui::GetID(ICON_FA_GRIP)) 209 | alignment = 4; 210 | } 211 | 212 | void HorizLayout::ExpandSelection(std::vector& selected, UINode* root) 213 | { 214 | if (selected.empty()) 215 | return; 216 | selected.resize(1); 217 | auto* table = dynamic_cast(selected[0]); 218 | bool existingLayout = table && !table->header && !(table->flags & ImGuiTableFlags_Borders); 219 | if (existingLayout) { //layout table was selected 220 | if (table->children.empty()) { 221 | selected.clear(); 222 | return; 223 | } 224 | selected[0] = table->children[0].get(); 225 | return ExpandSelection(selected, root); 226 | } 227 | auto pos = root->FindChild(selected[0]); 228 | if (!pos) { 229 | selected.clear(); 230 | return; 231 | } 232 | auto* parent = pos->first; 233 | table = dynamic_cast(parent); 234 | existingLayout = table && !table->header && !(table->flags & ImGuiTableFlags_Borders); 235 | for (size_t i = pos->second; i >= 1; --i) { 236 | const auto& pch = parent->children[i - 1]; 237 | const auto& ch = parent->children[i]; 238 | if (!(ch->Behavior() & UINode::SnapSides) || 239 | !(pch->Behavior() & UINode::SnapSides)) 240 | break; 241 | if (!existingLayout && 242 | (!ch->sameLine || ch->nextColumn)) 243 | break; 244 | selected.insert(selected.begin(), pch.get()); 245 | } 246 | for (size_t i = pos->second + 1; i < parent->children.size(); ++i) { 247 | const auto& ch = parent->children[i]; 248 | if (!(ch->Behavior() & UINode::SnapSides)) 249 | break; 250 | if (!existingLayout && 251 | (!ch->sameLine || ch->nextColumn)) 252 | break; 253 | selected.push_back(ch.get()); 254 | } 255 | } 256 | 257 | void HorizLayout::Work() 258 | { 259 | if (selected.empty() || alignment < 0) 260 | return; 261 | auto pos = root->FindChild(selected[0]); 262 | auto* parent = pos->first; 263 | auto* table = dynamic_cast(parent); 264 | bool existingLayout = table && !table->header && !(table->flags & ImGuiTableFlags_Borders); 265 | float realPadding = padding * ctx->style.ItemSpacing.x; 266 | float realSpacing = spacing * ctx->style.ItemSpacing.x; 267 | 268 | if (alignment == 0) //left alignment 269 | { 270 | //remove table 271 | if (existingLayout) 272 | { 273 | pos = root->FindChild(table); 274 | parent = pos->first; 275 | int idx = pos->second; 276 | for (size_t i = 0; i < selected.size(); ++i) 277 | { 278 | auto* item = dynamic_cast(selected[i]); 279 | item->spacing = i ? spacing : (int)table->spacing; 280 | item->indent = !i ? padding : 0; //hack 281 | 282 | //reparent items 283 | for (auto it = table->children.begin(); it != table->children.end(); ++it) 284 | if (it->get() == item) { 285 | it->release(); 286 | table->children.erase(it); 287 | break; 288 | } 289 | 290 | item->sameLine = i ? true : false; 291 | item->nextColumn = false; 292 | parent->children.insert(parent->children.begin() + idx, std::unique_ptr(item)); 293 | ++idx; 294 | } 295 | assert(parent->children[idx].get() == table); 296 | parent->children.erase(parent->children.begin() + idx); 297 | } 298 | else 299 | { 300 | for (size_t i = 0; i < selected.size(); ++i) 301 | { 302 | auto* item = dynamic_cast(selected[i]); 303 | item->spacing = i ? spacing : (int)item->spacing; 304 | item->indent = !i ? padding : 0; //hack 305 | } 306 | } 307 | } 308 | else 309 | { 310 | int idx = pos->second; 311 | //create table 312 | if (!existingLayout) 313 | { 314 | auto tab = std::make_unique(*ctx); 315 | auto* item = dynamic_cast(selected[0]); 316 | tab->sameLine = false; 317 | tab->spacing = item->spacing; 318 | table = tab.get(); 319 | parent->children.insert(parent->children.begin() + idx, std::move(tab)); 320 | ++idx; 321 | } 322 | table->header = false; 323 | table->flags = ImGuiTableFlags_NoPadOuterX | ImGuiTableFlags_NoPadInnerX;; 324 | table->columnData.clear(); 325 | 326 | if (padding) //left padding 327 | table->columnData.push_back({ "left-margin", ImGuiTableColumnFlags_WidthFixed, realPadding }); 328 | if (alignment == 1 || alignment == 2) //center, right 329 | table->columnData.push_back({ "left-stretch", ImGuiTableColumnFlags_WidthStretch }); 330 | 331 | for (size_t i = 0; i < selected.size(); ++i) 332 | { 333 | //reparent items 334 | auto* item = dynamic_cast(selected[i]); 335 | if (!existingLayout) 336 | { 337 | for (auto it = parent->children.begin(); it != parent->children.end(); ++it) 338 | if (it->get() == item) { 339 | it->release(); 340 | parent->children.erase(it); 341 | break; 342 | } 343 | table->children.push_back(std::unique_ptr(item)); 344 | } 345 | item->indent = 0; 346 | if (alignment == 1 || alignment == 2) //center, right 347 | { 348 | if (!i) 349 | table->columnData.push_back({ "content", ImGuiTableColumnFlags_WidthFixed, 0 }); 350 | item->nextColumn = !i ? (int)table->columnData.size() - 1 : 0; 351 | item->sameLine = !i ? false : true; 352 | item->spacing = i ? spacing : 0; 353 | } 354 | else if (alignment == 3) //left&right 355 | { 356 | if (!i) 357 | table->columnData.push_back({ "left-content", ImGuiTableColumnFlags_WidthStretch }); 358 | else if (i == selected.size() - 1) 359 | table->columnData.push_back({ "right-content", ImGuiTableColumnFlags_WidthFixed, 0 }); 360 | 361 | item->nextColumn = !i ? (int)table->columnData.size() - 1 : i == selected.size() - 1 ? 1 : 0; 362 | item->sameLine = !i ? false : true; 363 | item->spacing = i ? spacing : 0; 364 | } 365 | else if (alignment == 4) //grid 366 | { 367 | if (i && spacing) 368 | table->columnData.push_back({ "spacing" + std::to_string(i), ImGuiTableColumnFlags_WidthFixed, realSpacing }); 369 | table->columnData.push_back({ "item" + std::to_string(i + 1), ImGuiTableColumnFlags_WidthStretch }); 370 | item->sameLine = false; 371 | item->nextColumn = !i ? (int)table->columnData.size() - 1 : spacing ? 2 : 1; 372 | item->spacing = 0; 373 | } 374 | } 375 | 376 | if (alignment == 1) //center 377 | table->columnData.push_back({ "right-stretch", ImGuiTableColumnFlags_WidthStretch }); 378 | if (padding) //right padding 379 | table->columnData.push_back({ "right-margin", ImGuiTableColumnFlags_WidthFixed, realPadding }); 380 | } 381 | } 382 | 383 | void HorizLayout::Init() 384 | { 385 | alignment = -1; //force user to choose 386 | ExpandSelection(selected, root); 387 | } 388 | -------------------------------------------------------------------------------- /src/ui_horiz_layout.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.8 2 | // visit github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include "imrad.h" 6 | #include "node_standard.h" 7 | 8 | class HorizLayout 9 | { 10 | public: 11 | /// @begin interface 12 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 13 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 14 | void Draw(); 15 | 16 | int spacing = 1; 17 | int padding = 0; 18 | int alignment; 19 | std::vector selected; 20 | UINode * root; 21 | UIContext * ctx; 22 | /// @end interface 23 | 24 | static void ExpandSelection(std::vector& selected, UINode* root); 25 | 26 | private: 27 | /// @begin impl 28 | void Init(); 29 | 30 | void OnAlignment(); 31 | void Work(); 32 | 33 | ImGuiID ID = 0; 34 | ImRad::ModalResult modalResult; 35 | std::function callback; 36 | /// @end impl 37 | }; 38 | 39 | extern HorizLayout horizLayout; 40 | -------------------------------------------------------------------------------- /src/ui_input_name.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.8 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #include "ui_input_name.h" 5 | #include "ui_message_box.h" 6 | 7 | InputName inputName; 8 | 9 | 10 | void InputName::OpenPopup(std::function clb) 11 | { 12 | callback = clb; 13 | modalResult = ImRad::None; 14 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 15 | ioUserData->dimBgRatio = 1.f; 16 | ImGui::OpenPopup(ID); 17 | Init(); 18 | } 19 | 20 | void InputName::ClosePopup(ImRad::ModalResult mr) 21 | { 22 | modalResult = mr; 23 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 24 | ioUserData->dimBgRatio = 0.f; 25 | } 26 | 27 | void InputName::Init() 28 | { 29 | // TODO: Add your code here 30 | name = ""; 31 | } 32 | 33 | void InputName::Draw() 34 | { 35 | /// @style imrad 36 | /// @unit px 37 | /// @begin TopWindow 38 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 39 | ID = ImGui::GetID("###InputName"); 40 | ImGui::SetNextWindowSize({ 250, 120 }, ImGuiCond_FirstUseEver); //{ 250, 120 } 41 | bool tmpOpen = true; 42 | if (ImGui::BeginPopupModal(ImRad::Format("{}###InputName", title).c_str(), &tmpOpen, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) 43 | { 44 | if (ioUserData->activeActivity != "") 45 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 46 | if (modalResult != ImRad::None) 47 | { 48 | ImGui::CloseCurrentPopup(); 49 | if (modalResult != ImRad::Cancel) 50 | callback(modalResult); 51 | } 52 | if (ImGui::Shortcut(ImGuiKey_Escape)) 53 | ClosePopup(); 54 | /// @separator 55 | 56 | // TODO: Add Draw calls of dependent popup windows here 57 | messageBox.Draw(); 58 | 59 | /// @begin Input 60 | if (ImGui::IsWindowAppearing()) 61 | ImGui::SetKeyboardFocusHere(); 62 | ImGui::SetNextItemWidth(-1); 63 | ImGui::InputTextWithHint("##name", ImRad::Format("{}", hint).c_str(), &name, ImGuiInputTextFlags_CharsNoBlank); 64 | if (ImGui::IsItemActive()) 65 | ioUserData->imeType = ImRad::ImeText; 66 | /// @end Input 67 | 68 | /// @begin Table 69 | ImRad::Spacing(3); 70 | if (ImGui::BeginTable("table1", 3, ImGuiTableFlags_NoPadOuterX | ImGuiTableFlags_NoPadInnerX, { 0, 0 })) 71 | { 72 | ImGui::TableSetupColumn("left-stretch", ImGuiTableColumnFlags_WidthStretch, 0); 73 | ImGui::TableSetupColumn("content", ImGuiTableColumnFlags_WidthFixed, 0); 74 | ImGui::TableSetupColumn("right-stretch", ImGuiTableColumnFlags_WidthStretch, 0); 75 | ImGui::TableNextRow(0, 0); 76 | ImGui::TableSetColumnIndex(0); 77 | /// @separator 78 | 79 | /// @begin Button 80 | ImRad::TableNextColumn(1); 81 | ImGui::BeginDisabled(name==""||name=="Classic"||name=="Dark"||name=="Light"); 82 | if (ImGui::Button("OK", { 100, 30 })) 83 | { 84 | ClosePopup(ImRad::Ok); 85 | } 86 | ImGui::EndDisabled(); 87 | /// @end Button 88 | 89 | 90 | /// @separator 91 | ImGui::EndTable(); 92 | } 93 | /// @end Table 94 | 95 | /// @separator 96 | ImGui::EndPopup(); 97 | } 98 | /// @end TopWindow 99 | } 100 | -------------------------------------------------------------------------------- /src/ui_input_name.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.8 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include "imrad.h" 6 | 7 | class InputName 8 | { 9 | public: 10 | /// @begin interface 11 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 12 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 13 | void Draw(); 14 | 15 | std::string name; 16 | std::string hint; 17 | std::string title; 18 | /// @end interface 19 | 20 | private: 21 | /// @begin impl 22 | void Init(); 23 | 24 | ImGuiID ID = 0; 25 | ImRad::ModalResult modalResult; 26 | std::function callback; 27 | /// @end impl 28 | }; 29 | 30 | extern InputName inputName; 31 | -------------------------------------------------------------------------------- /src/ui_message_box.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_message_box.h" 2 | #include "IconsFontAwesome6.h" 3 | 4 | MessageBox messageBox; 5 | 6 | void MessageBox::OpenPopup(std::function f) 7 | { 8 | callback = std::move(f); 9 | ImGui::OpenPopup(ID); 10 | } 11 | 12 | void MessageBox::Draw() 13 | { 14 | const float BWIDTH = 90; 15 | const float BHEIGHT = 30; 16 | 17 | ID = ImGui::GetID("###MessageBox"); 18 | ImVec2 center = ImGui::GetMainViewport()->GetCenter(); 19 | //todo: ImGuiCond_Appearing won't center 20 | ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, { 0.5f, 0.5f }); 21 | bool open = true; 22 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 10, 10 }); 23 | if (ImGui::BeginPopupModal((title + "###MessageBox").c_str(), &open, ImGuiWindowFlags_AlwaysAutoResize)) 24 | { 25 | wasOpen = ID; 26 | /*bool resizing = ImGui::IsWindowAppearing() || 27 | std::abs(ImGui::GetWindowSize().x - lastSize.x) >= 1 || 28 | std::abs(ImGui::GetWindowSize().y - lastSize.y) >= 1; 29 | lastSize = ImGui::GetWindowSize();*/ 30 | 31 | ImGui::BeginChild("ch", { 300, 50 }); 32 | ImGui::TextWrapped("%s", message.c_str()); 33 | ImGui::EndChild(); 34 | ImGui::Spacing(); 35 | ImGui::Spacing(); 36 | 37 | int n = (bool)(buttons & ImRad::Ok) + (bool)(buttons & ImRad::Cancel) + (bool)(buttons & ImRad::Yes) + (bool)(buttons & ImRad::No); 38 | float w = (n * BWIDTH) + (n - 1) * ImGui::GetStyle().ItemSpacing.x; 39 | float x = (ImGui::GetContentRegionAvail().x + ImGui::GetStyle().FramePadding.x - w) / 2.f; 40 | x += ImGui::GetStyle().WindowPadding.x; 41 | float y = ImGui::GetCursorPosY(); 42 | if (buttons & ImRad::Ok) 43 | { 44 | if (ImGui::IsWindowAppearing()) 45 | ImGui::SetKeyboardFocusHere(); 46 | ImGui::SetCursorPos({ x, y }); 47 | if (ImGui::Button("OK", { BWIDTH, BHEIGHT }) || 48 | (buttons == ImRad::Ok && ImGui::IsKeyPressed(ImGuiKey_Escape))) 49 | { 50 | ImGui::CloseCurrentPopup(); 51 | callback(ImRad::Ok); 52 | } 53 | x += BWIDTH + ImGui::GetStyle().ItemSpacing.x; 54 | } 55 | if (buttons & ImRad::Yes) 56 | { 57 | ImGui::SetCursorPos({ x, y }); 58 | if (ImGui::Button("Yes", { BWIDTH, BHEIGHT })) { 59 | ImGui::CloseCurrentPopup(); 60 | callback(ImRad::Yes); 61 | } 62 | x += BWIDTH + ImGui::GetStyle().ItemSpacing.x; 63 | } 64 | if (buttons & ImRad::No) 65 | { 66 | ImGui::SetCursorPos({ x, y }); 67 | if (ImGui::Button("No", { BWIDTH, BHEIGHT })) { 68 | ImGui::CloseCurrentPopup(); 69 | callback(ImRad::No); 70 | } 71 | x += BWIDTH + ImGui::GetStyle().ItemSpacing.x; 72 | } 73 | if (buttons & ImRad::Cancel) 74 | { 75 | ImGui::SetCursorPos({ x, y }); 76 | if (ImGui::Button("Cancel", { BWIDTH, BHEIGHT }) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { 77 | ImGui::CloseCurrentPopup(); 78 | callback(ImRad::Cancel); //currently used in Save File Confirmation upon Exit 79 | } 80 | x += BWIDTH + ImGui::GetStyle().ItemSpacing.x; 81 | } 82 | 83 | ImGui::EndPopup(); 84 | } 85 | else if (wasOpen == ID) 86 | { 87 | //hack - clear fields for next invocation 88 | wasOpen = 0; 89 | title = "title"; 90 | message = ""; 91 | buttons = ImRad::Ok; 92 | } 93 | ImGui::PopStyleVar(); 94 | } -------------------------------------------------------------------------------- /src/ui_message_box.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "imrad.h" 6 | 7 | class MessageBox 8 | { 9 | public: 10 | void OpenPopup(std::function f = [](ImRad::ModalResult){}); 11 | void Draw(); 12 | 13 | public: 14 | std::string title = "title"; 15 | std::string message; 16 | int buttons = ImRad::Ok; 17 | 18 | private: 19 | ImGuiID ID = 0; 20 | ImGuiID wasOpen; 21 | std::function callback; 22 | }; 23 | 24 | extern MessageBox messageBox; -------------------------------------------------------------------------------- /src/ui_new_field.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_new_field.h" 2 | #include "ui_message_box.h" 3 | #include "cppgen.h" 4 | #include 5 | #include 6 | 7 | NewFieldPopup newFieldPopup; 8 | 9 | void NewFieldPopup::OpenPopup(std::function clb) 10 | { 11 | callback = clb; 12 | requestClose = false; 13 | ImGui::OpenPopup(ID); 14 | 15 | if (mode == NewStruct) 16 | varType = "struct"; 17 | varTypeDisabled = varType != ""; 18 | varTypeArray = varType == "[]"; 19 | if (varTypeArray) 20 | varType = "std::vector<>"; 21 | if (mode != NewEvent) 22 | varName = ""; 23 | varInit = ""; 24 | varPublic = mode == NewEvent ? false : true; 25 | if (mode == RenameField) { 26 | varName = varOldName; 27 | auto* var = codeGen->GetVar(varOldName); 28 | if (var) { 29 | varInit = var->init; 30 | varType = var->type; 31 | varPublic = var->flags & CppGen::Var::Interface; 32 | } 33 | } 34 | CheckVarName(); 35 | } 36 | 37 | void NewFieldPopup::ClosePopup() 38 | { 39 | requestClose = true; 40 | } 41 | 42 | void NewFieldPopup::Draw() 43 | { 44 | std::string title = mode == NewField ? "New Field" : 45 | mode == NewStruct ? "New Struct" : 46 | mode == NewEvent ? "New Method" : 47 | mode == RenameField ? "Rename Field" : 48 | mode == RenameStruct ? "Rename Struct" : 49 | "Rename Window"; 50 | title += "###NewFieldPopup"; 51 | 52 | ID = ImGui::GetID("###NewFieldPopup"); 53 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 12, 12 }); 54 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 10, 10 }); 55 | ImVec2 center = ImGui::GetMainViewport()->GetCenter(); 56 | ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, { 0.5f, 0.5f }); 57 | ImGui::SetNextWindowSizeConstraints({ 400, 100 }, { FLT_MAX, FLT_MAX }); 58 | bool tmp = true; 59 | if (ImGui::BeginPopupModal(title.c_str(), &tmp, ImGuiWindowFlags_AlwaysAutoResize)) 60 | { 61 | if (requestClose) { 62 | ImGui::CloseCurrentPopup(); 63 | requestClose = false; 64 | } 65 | 66 | messageBox.Draw(); 67 | 68 | bool change = false; 69 | 70 | if (mode == RenameField || mode == RenameStruct || mode == RenameWindow) 71 | { 72 | ImGui::Text("Old name"); 73 | ImGui::BeginDisabled(true); 74 | ImGui::SetNextItemWidth(-1); 75 | ImGui::InputText("##varOldName", &varOldName, ImGuiInputTextFlags_CharsNoBlank); 76 | ImGui::EndDisabled(); 77 | ImGui::Spacing(); 78 | } 79 | 80 | ImGui::Text( 81 | mode == RenameWindow || mode == RenameStruct || mode == RenameField ? "New name" : 82 | mode == NewEvent ? "Method name" : 83 | mode == NewStruct ? "Struct name" : 84 | "Field name" 85 | ); 86 | 87 | ImGui::SameLine(); 88 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 89 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff0000ff); 90 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 1.0f, 0 }); 91 | ImGui::Selectable((nameError + "##nameError").c_str(), false); 92 | ImGui::PopStyleVar(); 93 | ImGui::PopItemFlag(); 94 | ImGui::PopStyleColor(); 95 | 96 | if (ImGui::IsWindowAppearing()) 97 | ImGui::SetKeyboardFocusHere(); 98 | ImGui::SetNextItemWidth(-1); 99 | if (ImGui::InputText("##varName", &varName, ImGuiInputTextFlags_CharsNoBlank)) 100 | change = true; 101 | 102 | if (mode == NewField || mode == NewStruct || mode == NewEvent) 103 | { 104 | ImGui::Text("Field type"); 105 | 106 | ImGui::SameLine(); 107 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 108 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff0000ff); 109 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 1.0f, 0 }); 110 | ImGui::Selectable((typeError + "##typeError").c_str(), false); 111 | ImGui::PopStyleVar(); 112 | ImGui::PopStyleColor(); 113 | ImGui::PopItemFlag(); 114 | 115 | ImGui::BeginDisabled(varTypeDisabled); 116 | ImGui::SetNextItemWidth(-1); 117 | if (ImGui::InputText("##varType", &varType, ImGuiInputTextFlags_CharsNoBlank)) 118 | change = true; 119 | ImGui::EndDisabled(); 120 | } 121 | 122 | if (mode == NewField || mode == RenameField) 123 | { 124 | ImGui::Spacing(); 125 | ImGui::Text("Initial value (optional)"); 126 | ImGui::SetNextItemWidth(-1); 127 | if (ImGui::InputText("##init", &varInit, ImGuiInputTextFlags_CallbackCharFilter, DefaultCharFilter)) 128 | change = true; 129 | } 130 | 131 | if (mode == NewField || mode == RenameField) 132 | { 133 | ImGui::Spacing(); 134 | ImGui::Text("Access"); 135 | if (ImGui::BeginChild("access", { -1, 0 }, ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY)) 136 | { 137 | if (ImGui::RadioButton("Interface", varPublic)) 138 | varPublic = true; 139 | ImGui::SameLine(0, 3 * ImGui::GetStyle().ItemSpacing.x); 140 | if (ImGui::RadioButton("Implementation", !varPublic)) 141 | varPublic = false; 142 | ImGui::EndChild(); 143 | } 144 | } 145 | 146 | if (change) 147 | { 148 | if (varTypeArray && varName != "") { 149 | std::string stype = (char)std::toupper(varName[0]) + varName.substr(1); 150 | if (stype.back() == 's' || stype.back() == 'S') 151 | stype.pop_back(); 152 | varType = "std::vector<" + stype + ">"; 153 | } 154 | 155 | CheckVarName(); 156 | } 157 | 158 | ImGui::Spacing(); 159 | ImGui::Spacing(); 160 | 161 | ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - 2 * 100)); 162 | ImGui::BeginDisabled(nameError != "" || typeError != ""); 163 | if (ImGui::Button("OK", { 100, 30 })) 164 | { 165 | CheckVarName(); 166 | int flags = varPublic ? CppGen::Var::Interface : CppGen::Var::Impl; 167 | 168 | if (mode == NewField || mode == NewStruct || mode == NewEvent) 169 | { 170 | std::string type = mode == NewStruct ? "struct" : varType; 171 | auto ret = codeGen->CheckVarExpr(varName, type, scope); 172 | if (ret == CppGen::New_ImplicitStruct) { 173 | messageBox.title = "New"; 174 | messageBox.message = "Create a new struct?"; 175 | messageBox.buttons = ImRad::Yes | ImRad::No; 176 | messageBox.OpenPopup([this, type, flags](ImRad::ModalResult mr) { 177 | if (mr != ImRad::Yes) 178 | return; 179 | codeGen->CreateVarExpr(varName, type, varInit, flags, scope); 180 | ClosePopup(); 181 | callback(); 182 | }); 183 | } 184 | else if (ret == CppGen::New) { 185 | codeGen->CreateVarExpr(varName, type, varInit, flags, scope); 186 | ClosePopup(); 187 | callback(); 188 | } 189 | } 190 | else if (mode == RenameField) 191 | { 192 | assert(varType != ""); 193 | codeGen->ChangeVar(varOldName, varType, varInit, flags, scope); 194 | codeGen->RenameVar(varOldName, varName, scope); 195 | ClosePopup(); 196 | callback(); 197 | } 198 | else if (mode == RenameStruct) 199 | { 200 | codeGen->RenameStruct(varOldName, varName); 201 | ClosePopup(); 202 | callback(); 203 | } 204 | else if (mode == RenameWindow) 205 | { 206 | codeGen->SetNamesFromId(varName); 207 | ClosePopup(); 208 | callback(); 209 | } 210 | } 211 | ImGui::EndDisabled(); 212 | 213 | ImGui::SameLine(); 214 | if (ImGui::Button("Cancel", { 100, 30 }) || ImGui::Shortcut(ImGuiKey_Escape)) 215 | { 216 | const auto& g = ImGui::GetCurrentContext(); 217 | ClosePopup(); 218 | } 219 | 220 | ImGui::EndPopup(); 221 | } 222 | ImGui::PopStyleVar(); 223 | ImGui::PopStyleVar(); 224 | } 225 | 226 | void NewFieldPopup::CheckVarName() 227 | { 228 | nameError = typeError = ""; 229 | if (mode == RenameWindow) 230 | { 231 | if (!cpp::is_id(varName)) { 232 | nameError = "expected simple id"; 233 | } 234 | } 235 | else if (mode == RenameStruct) 236 | { 237 | if (!cpp::is_id(varName)) { 238 | nameError = "expected simple id"; 239 | } 240 | else if (varName != varOldName && stx::count(codeGen->GetStructTypes(), varName)) { 241 | nameError = "same field already exists"; 242 | } 243 | } 244 | else if (mode == RenameField) 245 | { 246 | if (!cpp::is_id(varName)) { 247 | nameError = "expected simple id"; 248 | } 249 | else if (varName != varOldName && codeGen->CheckVarExpr(varName, varType, scope) != CppGen::New) { 250 | nameError = "same field already exists"; 251 | } 252 | } 253 | else if (mode == NewStruct || mode == NewEvent) 254 | { 255 | std::string type = mode == NewStruct ? "struct" : varType; 256 | if (!cpp::is_id(varName)) { 257 | nameError = "expected simple id"; 258 | } 259 | else if (codeGen->CheckVarExpr(varName, type, scope) != CppGen::New) { 260 | nameError = "same field already exists"; 261 | } 262 | } 263 | else if (mode == NewField) 264 | { 265 | switch (codeGen->CheckVarExpr(varName, varType, scope)) 266 | { 267 | case CppGen::SyntaxError: 268 | nameError = "expected simple id"; 269 | break; 270 | case CppGen::ConflictError: 271 | nameError = "field of different type already exists"; 272 | break; 273 | case CppGen::Existing: 274 | nameError = "same field already exists"; 275 | break; 276 | default: 277 | if (varTypeArray && !cpp::is_id(varName)) { 278 | nameError = "expected simple id"; 279 | } 280 | else if (varType == "") { 281 | typeError = "invalid type"; 282 | } 283 | break; 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/ui_new_field.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "cppgen.h" 3 | #include 4 | #include 5 | #include 6 | 7 | class NewFieldPopup 8 | { 9 | public: 10 | void OpenPopup(std::function = []{}); 11 | void ClosePopup(); 12 | void Draw(); 13 | 14 | public: 15 | std::string varType; 16 | std::string varOldName; 17 | std::string varName; //auto cleared except for NewEvent 18 | std::string varInit; //auto filled 19 | bool varPublic; 20 | std::string scope; 21 | CppGen* codeGen = nullptr; 22 | enum mode_t { NewField, NewStruct, NewEvent, RenameField, RenameStruct, RenameWindow }; 23 | mode_t mode = NewField; 24 | 25 | private: 26 | void CheckVarName(); 27 | 28 | ImGuiID ID; 29 | bool requestClose; 30 | std::function callback; 31 | 32 | bool varTypeDisabled = false; 33 | bool varTypeArray = false; 34 | std::string nameError, typeError; 35 | ImU32 clr; 36 | }; 37 | 38 | extern NewFieldPopup newFieldPopup; -------------------------------------------------------------------------------- /src/ui_settings_dlg.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #include "ui_settings_dlg.h" 5 | #include "utils.h" 6 | 7 | SettingsDlg settingsDlg; 8 | 9 | 10 | void SettingsDlg::OpenPopup(std::function clb) 11 | { 12 | callback = clb; 13 | modalResult = ImRad::None; 14 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 15 | ioUserData->dimBgRatio = 1.f; 16 | ImGui::OpenPopup(ID); 17 | Init(); 18 | } 19 | 20 | void SettingsDlg::ClosePopup(ImRad::ModalResult mr) 21 | { 22 | modalResult = mr; 23 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 24 | ioUserData->dimBgRatio = 0.f; 25 | } 26 | 27 | void SettingsDlg::ResetLayout() 28 | { 29 | // ImGui::GetCurrentWindow()->HiddenFramesCannotSkipItems = 2; 30 | vb1.Reset(); 31 | hb3.Reset(); 32 | } 33 | 34 | void SettingsDlg::Init() 35 | { 36 | // TODO: Add your code here 37 | } 38 | 39 | void SettingsDlg::Draw() 40 | { 41 | /// @style imrad 42 | /// @unit px 43 | /// @begin TopWindow 44 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 45 | ID = ImGui::GetID("###SettingsDlg"); 46 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 10, 10 }); 47 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 10, 5 }); 48 | ImGui::SetNextWindowSize({ 700, 520 }, ImGuiCond_FirstUseEver); //{ 700, 520 } 49 | ImGui::SetNextWindowSizeConstraints({ 0, 0 }, { FLT_MAX, FLT_MAX }); 50 | bool tmpOpen = true; 51 | if (ImGui::BeginPopupModal("Settings###SettingsDlg", &tmpOpen, ImGuiWindowFlags_NoCollapse)) 52 | { 53 | if (ioUserData->activeActivity != "") 54 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 55 | if (modalResult != ImRad::None) 56 | { 57 | ImGui::CloseCurrentPopup(); 58 | if (modalResult != ImRad::Cancel) 59 | callback(modalResult); 60 | } 61 | if (ImGui::Shortcut(ImGuiKey_Escape)) 62 | ClosePopup(); 63 | /// @separator 64 | 65 | // TODO: Add Draw calls of dependent popup windows here 66 | 67 | /// @begin Selectable 68 | vb1.BeginLayout(); 69 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); 70 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0.5f }); 71 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 72 | ImRad::Selectable("Category", false, ImGuiSelectableFlags_NoAutoClosePopups, { 140, 24 }); 73 | ImGui::PopItemFlag(); 74 | ImGui::PopStyleVar(); 75 | vb1.AddSize(0, 24); 76 | ImGui::PopStyleColor(); 77 | /// @end Selectable 78 | 79 | /// @begin Text 80 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 81 | ImGui::PushFont(ImRad::GetFontByName("imrad.H3")); 82 | ImGui::TextUnformatted("Environment Settings"); 83 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 84 | ImGui::PopFont(); 85 | /// @end Text 86 | 87 | // TODO: Add Draw calls of dependent popup windows here 88 | 89 | /// @begin Table 90 | if (ImGui::BeginTable("table1", 1, ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_BordersOuterV, { 140, vb1.GetSize() })) 91 | { 92 | ImGui::TableSetupColumn("A", 0, 0); 93 | ImGui::TableSetupScrollFreeze(0, 0); 94 | ImGui::TableNextRow(0, 0); 95 | ImGui::TableSetColumnIndex(0); 96 | /// @separator 97 | 98 | /// @begin Selectable 99 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0.5f }); 100 | ImRad::Selectable("\xee\x85\xa3 Environment", true, ImGuiSelectableFlags_NoAutoClosePopups, { -1, 30 }); 101 | ImGui::PopStyleVar(); 102 | /// @end Selectable 103 | 104 | 105 | /// @separator 106 | ImGui::EndTable(); 107 | } 108 | vb1.AddSize(1, ImRad::VBox::Stretch(1)); 109 | /// @end Table 110 | 111 | /// @begin Child 112 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 113 | ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xffd0d0d0); 114 | ImGui::BeginChild("child2", { -1, vb1.GetSize() }, ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_NoSavedSettings); 115 | { 116 | /// @separator 117 | 118 | /// @begin Text 119 | ImGui::PushFont(ImRad::GetFontByName("imrad.H3")); 120 | ImGui::AlignTextToFramePadding(); 121 | ImGui::TextUnformatted("UI font"); 122 | ImGui::PopFont(); 123 | /// @end Text 124 | 125 | /// @begin Selectable 126 | ImRad::Spacing(1); 127 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 128 | ImGui::AlignTextToFramePadding(); 129 | ImRad::Selectable("Regular:", false, ImGuiSelectableFlags_NoAutoClosePopups, { 60, 0 }); 130 | ImGui::PopStyleVar(); 131 | /// @end Selectable 132 | 133 | /// @begin Combo 134 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 135 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 136 | ImGui::SetNextItemWidth(200); 137 | ImRad::Combo("##uiFontName", &uiFontName, fontNames, 0); 138 | ImGui::PopStyleVar(); 139 | /// @end Combo 140 | 141 | /// @begin Text 142 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 143 | ImGui::TextUnformatted("Size:"); 144 | /// @end Text 145 | 146 | /// @begin Combo 147 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 148 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 149 | ImGui::SetNextItemWidth(100); 150 | ImRad::Combo("##uiFontSize", &uiFontSize, "12\00014\00016\00018\00019\00020\00021\00022\00024\00026\00028\00032\00036\000", 0); 151 | ImGui::PopStyleVar(); 152 | /// @end Combo 153 | 154 | /// @begin Text 155 | ImRad::Spacing(4); 156 | ImGui::PushFont(ImRad::GetFontByName("imrad.H3")); 157 | ImGui::AlignTextToFramePadding(); 158 | ImGui::TextUnformatted("Property Grid Fonts"); 159 | ImGui::PopFont(); 160 | /// @end Text 161 | 162 | /// @begin Selectable 163 | ImRad::Spacing(1); 164 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 165 | ImGui::AlignTextToFramePadding(); 166 | ImRad::Selectable("Regular:", false, ImGuiSelectableFlags_NoAutoClosePopups, { 60, 0 }); 167 | ImGui::PopStyleVar(); 168 | /// @end Selectable 169 | 170 | /// @begin Combo 171 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 172 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 173 | ImGui::SetNextItemWidth(200); 174 | ImRad::Combo("##pgFontName", &pgFontName, fontNames, 0); 175 | ImGui::PopStyleVar(); 176 | /// @end Combo 177 | 178 | /// @begin Text 179 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 180 | ImGui::TextUnformatted("Size:"); 181 | /// @end Text 182 | 183 | /// @begin Combo 184 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 185 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 186 | ImGui::SetNextItemWidth(100); 187 | ImRad::Combo("##pgFontSize", &pgFontSize, "12\00014\00016\00018\00019\00020\00021\00022\00024\00026\00028\00032\00036\000", 0); 188 | ImGui::PopStyleVar(); 189 | /// @end Combo 190 | 191 | /// @begin Selectable 192 | ImRad::Spacing(1); 193 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 194 | ImGui::AlignTextToFramePadding(); 195 | ImRad::Selectable("Bold:", false, ImGuiSelectableFlags_NoAutoClosePopups, { 60, 0 }); 196 | ImGui::PopStyleVar(); 197 | /// @end Selectable 198 | 199 | /// @begin Combo 200 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 201 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); 202 | ImGui::SetNextItemWidth(200); 203 | ImRad::Combo("##pgbFontName", &pgbFontName, fontNames, 0); 204 | ImGui::PopStyleVar(); 205 | /// @end Combo 206 | 207 | /// @separator 208 | ImGui::EndChild(); 209 | } 210 | ImGui::PopStyleColor(); 211 | vb1.UpdateSize(0, ImRad::VBox::Stretch(1)); 212 | /// @end Child 213 | 214 | /// @begin Spacer 215 | hb3.BeginLayout(); 216 | ImRad::Spacing(1); 217 | ImRad::Dummy({ hb3.GetSize(), 0 }); 218 | vb1.AddSize(2, ImRad::VBox::ItemSize); 219 | hb3.AddSize(0, ImRad::HBox::Stretch(1)); 220 | /// @end Spacer 221 | 222 | /// @begin Button 223 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 224 | if (ImGui::Button("OK", { 100, 30 })) 225 | { 226 | ClosePopup(ImRad::Ok); 227 | } 228 | vb1.UpdateSize(0, 30); 229 | hb3.AddSize(1, 100); 230 | /// @end Button 231 | 232 | /// @separator 233 | ImGui::EndPopup(); 234 | } 235 | ImGui::PopStyleVar(); 236 | ImGui::PopStyleVar(); 237 | /// @end TopWindow 238 | } 239 | -------------------------------------------------------------------------------- /src/ui_settings_dlg.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include "imrad.h" 6 | 7 | class SettingsDlg 8 | { 9 | public: 10 | /// @begin interface 11 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 12 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 13 | void Draw(); 14 | 15 | std::vector fontNames; 16 | std::string uiFontName; 17 | std::string uiFontSize; 18 | std::string pgFontName; 19 | std::string pgbFontName; 20 | std::string pgFontSize; 21 | /// @end interface 22 | 23 | private: 24 | /// @begin impl 25 | void ResetLayout(); 26 | void Init(); 27 | 28 | ImGuiID ID = 0; 29 | ImRad::ModalResult modalResult; 30 | std::function callback; 31 | ImRad::VBox vb1; 32 | ImRad::HBox hb3; 33 | /// @end impl 34 | }; 35 | 36 | extern SettingsDlg settingsDlg; 37 | -------------------------------------------------------------------------------- /src/ui_table_cols.cpp: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #include "ui_table_cols.h" 5 | #include "ui_binding.h" 6 | 7 | TableCols tableCols; 8 | 9 | 10 | void TableCols::OpenPopup(std::function clb) 11 | { 12 | callback = clb; 13 | modalResult = ImRad::None; 14 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 15 | ioUserData->dimBgRatio = 1.f; 16 | ImGui::OpenPopup(ID); 17 | Init(); 18 | } 19 | 20 | void TableCols::ClosePopup(ImRad::ModalResult mr) 21 | { 22 | modalResult = mr; 23 | auto *ioUserData = (ImRad::IOUserData *)ImGui::GetIO().UserData; 24 | ioUserData->dimBgRatio = 0.f; 25 | } 26 | 27 | void TableCols::Init() 28 | { 29 | sel = columns.size() ? 0 : -1; 30 | CheckErrors(); 31 | } 32 | 33 | void TableCols::Draw() 34 | { 35 | /// @style imrad 36 | /// @unit px 37 | /// @begin TopWindow 38 | auto* ioUserData = (ImRad::IOUserData*)ImGui::GetIO().UserData; 39 | ID = ImGui::GetID("###TableCols"); 40 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 8, 5 }); 41 | ImGui::SetNextWindowSize({ 550, 480 }, ImGuiCond_FirstUseEver); //{ 550, 480 } 42 | ImGui::SetNextWindowSizeConstraints({ 0, 0 }, { FLT_MAX, FLT_MAX }); 43 | bool tmpOpen = true; 44 | if (ImGui::BeginPopupModal("Table Columns###TableCols", &tmpOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs)) 45 | { 46 | if (ioUserData->activeActivity != "") 47 | ImRad::RenderDimmedBackground(ioUserData->WorkRect(), ioUserData->dimBgRatio); 48 | if (modalResult != ImRad::None) 49 | { 50 | ImGui::CloseCurrentPopup(); 51 | if (modalResult != ImRad::Cancel) 52 | callback(modalResult); 53 | } 54 | /// @separator 55 | 56 | // TODO: Add Draw calls of dependent popup windows here 57 | bindingDlg.Draw(); 58 | 59 | /// @begin Selectable 60 | vb1.BeginLayout(); 61 | ImRad::Spacing(1); 62 | ImGui::BeginDisabled(true); 63 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 1.f }); 64 | ImRad::Selectable("Columns:", false, ImGuiSelectableFlags_NoAutoClosePopups, { 0, 0 }); 65 | ImGui::PopStyleVar(); 66 | vb1.AddSize(1, ImRad::VBox::ItemSize); 67 | ImGui::EndDisabled(); 68 | /// @end Selectable 69 | 70 | /// @begin Splitter 71 | ImGui::BeginChild("splitter1", { -1, vb1.GetSize() }); 72 | { 73 | ImGui::PushStyleColor(ImGuiCol_Separator, 0x00000000); 74 | ImGui::PushStyleColor(ImGuiCol_SeparatorHovered, 0x00000000); 75 | ImRad::Splitter(true, 8, &sash, 10, 10); 76 | ImGui::PopStyleColor(); 77 | ImGui::PopStyleColor(); 78 | /// @separator 79 | 80 | /// @begin Table 81 | ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 7, 5 }); 82 | ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xffffffff); 83 | if (ImGui::BeginTable("table2", 1, ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_ScrollY, { sash, -1 })) 84 | { 85 | ImGui::TableSetupColumn("A", 0, 0); 86 | ImGui::TableSetupScrollFreeze(0, 0); 87 | 88 | for (int i = 0; i < columns.size(); ++i) 89 | { 90 | ImGui::PushID(i); 91 | ImGui::TableNextRow(0, 0); 92 | ImGui::TableSetColumnIndex(0); 93 | /// @separator 94 | 95 | /// @begin Selectable 96 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 0, 0 }); 97 | if (ImRad::Selectable(ImRad::Format("{}", columns[i].label.c_str()).c_str(), i==sel, ImGuiSelectableFlags_NoAutoClosePopups | ImGuiSelectableFlags_SpanAllColumns, { 0, 0 })) 98 | { 99 | Selectable_Change(); 100 | } 101 | ImGui::PopStyleVar(); 102 | /// @end Selectable 103 | 104 | /// @begin Text 105 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 106 | ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); 107 | ImGui::TextUnformatted(ImRad::Format("{}", columns[i].SizingPolicyString()).c_str()); 108 | ImGui::PopStyleColor(); 109 | /// @end Text 110 | 111 | /// @separator 112 | ImGui::PopID(); 113 | } 114 | ImGui::EndTable(); 115 | } 116 | ImGui::PopStyleColor(); 117 | ImGui::PopStyleVar(); 118 | /// @end Table 119 | 120 | /// @begin CustomWidget 121 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 122 | Properties_Draw({ -1, -1 }); 123 | /// @end CustomWidget 124 | 125 | /// @separator 126 | ImGui::EndChild(); 127 | } 128 | vb1.AddSize(1, ImRad::VBox::Stretch(1)); 129 | /// @end Splitter 130 | 131 | /// @begin Button 132 | if (ImGui::Button("\xef\x83\xbe", { 37, 0 })) 133 | { 134 | AddButton_Change(); 135 | } 136 | vb1.AddSize(1, ImRad::VBox::ItemSize); 137 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 138 | ImGui::SetTooltip("Add new column"); 139 | /// @end Button 140 | 141 | /// @begin Button 142 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 143 | ImGui::BeginDisabled(sel<0); 144 | if (ImGui::Button("\xef\x8b\xad", { 37, 0 })) 145 | { 146 | RemoveButton_Change(); 147 | } 148 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 149 | ImGui::EndDisabled(); 150 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 151 | ImGui::SetTooltip("Remove column"); 152 | /// @end Button 153 | 154 | /// @begin Button 155 | ImGui::SameLine(0, 2 * ImGui::GetStyle().ItemSpacing.x); 156 | ImGui::BeginDisabled(sel<=0); 157 | if (ImGui::ArrowButton("##button3", ImGuiDir_Up)) 158 | { 159 | UpButton_Change(); 160 | } 161 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 162 | ImGui::EndDisabled(); 163 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 164 | ImGui::SetTooltip("Move column up"); 165 | /// @end Button 166 | 167 | /// @begin Button 168 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 169 | ImGui::BeginDisabled(sel+1==columns.size()); 170 | if (ImGui::ArrowButton("##button4", ImGuiDir_Down)) 171 | { 172 | DownButton_Change(); 173 | } 174 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 175 | ImGui::EndDisabled(); 176 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 177 | ImGui::SetTooltip("Move column down"); 178 | /// @end Button 179 | 180 | /// @begin Selectable 181 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 182 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff0000ff); 183 | ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, { 1.f, 0 }); 184 | ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 185 | ImRad::Selectable(ImRad::Format("{}##error", error).c_str(), false, ImGuiSelectableFlags_NoAutoClosePopups, { 0, 0 }); 186 | ImGui::PopItemFlag(); 187 | ImGui::PopStyleVar(); 188 | vb1.UpdateSize(0, ImRad::VBox::ItemSize); 189 | ImGui::PopStyleColor(); 190 | /// @end Selectable 191 | 192 | /// @begin Spacer 193 | hb4.BeginLayout(); 194 | ImRad::Spacing(2); 195 | ImRad::Dummy({ hb4.GetSize(), 0 }); 196 | vb1.AddSize(3, ImRad::VBox::ItemSize); 197 | hb4.AddSize(0, ImRad::HBox::Stretch(1)); 198 | /// @end Spacer 199 | 200 | /// @begin Button 201 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 202 | if (ImGui::Button("OK", { 100, 30 })) 203 | { 204 | ClosePopup(ImRad::Ok); 205 | } 206 | vb1.UpdateSize(0, 30); 207 | hb4.AddSize(1, 100); 208 | /// @end Button 209 | 210 | /// @begin Button 211 | ImGui::SameLine(0, 1 * ImGui::GetStyle().ItemSpacing.x); 212 | if (ImGui::Button("Cancel", { 100, 30 }) || 213 | ImGui::Shortcut(ImGuiKey_Escape)) 214 | { 215 | ClosePopup(ImRad::Cancel); 216 | } 217 | vb1.UpdateSize(0, 30); 218 | hb4.AddSize(1, 100); 219 | /// @end Button 220 | 221 | /// @separator 222 | ImGui::EndPopup(); 223 | } 224 | ImGui::PopStyleVar(); 225 | /// @end TopWindow 226 | } 227 | 228 | void TableCols::ResetLayout() 229 | { 230 | // ImGui::GetCurrentWindow()->HiddenFramesCannotSkipItems = 2; 231 | vb1.Reset(); 232 | hb4.Reset(); 233 | } 234 | 235 | void TableCols::Selectable_Change() 236 | { 237 | sel = ImGui::TableGetRowIndex(); 238 | } 239 | 240 | void TableCols::AddButton_Change() 241 | { 242 | columns.push_back(Table::ColumnData("New Column", ImGuiTableColumnFlags_None)); 243 | sel = (int)columns.size() - 1; 244 | } 245 | 246 | void TableCols::RemoveButton_Change() 247 | { 248 | if (sel >= 0) 249 | columns.erase(columns.begin() + sel); 250 | if (sel >= columns.size()) 251 | --sel; 252 | } 253 | 254 | void TableCols::UpButton_Change() 255 | { 256 | if (sel) { 257 | std::swap(columns[sel], columns[sel - 1]); 258 | --sel; 259 | } 260 | } 261 | 262 | void TableCols::DownButton_Change() 263 | { 264 | if (sel + 1 < columns.size()) { 265 | std::swap(columns[sel], columns[sel + 1]); 266 | ++sel; 267 | } 268 | } 269 | 270 | void TableCols::Properties_Draw(const ImRad::CustomWidgetArgs& args) 271 | { 272 | ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xfffafafa); 273 | ImGui::PushStyleColor(ImGuiCol_TableBorderLight, 0xffe5e5e5); 274 | ImVec4 clr = ImGui::GetStyleColorVec4(ImGuiCol_Button); 275 | clr.w *= 0.5f; 276 | ImGui::PushStyleColor(ImGuiCol_Button, clr); 277 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0, 0 }); 278 | ImVec2 framePad = ImGui::GetStyle().FramePadding; 279 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { framePad.x * 0.5f, framePad.y * 0.5f }); 280 | 281 | ImVec2 pgMin = ImGui::GetCursorScreenPos(); 282 | static float pgHeight = 0; 283 | uint32_t borderClr = ImGui::ColorConvertFloat4ToU32(ImGui::GetStyleColorVec4(ImGuiCol_TableBorderLight)); 284 | ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY; 285 | if (ImGui::BeginTable("pg", 2, flags, args.size)) 286 | { 287 | ImGui::GetWindowDrawList()->AddRectFilled( 288 | pgMin, 289 | { pgMin.x + ImGui::GetStyle().IndentSpacing + 1, pgMin.y + pgHeight }, 290 | borderClr 291 | ); 292 | ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); 293 | ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch); 294 | 295 | ImGui::TableNextRow(); 296 | ImGui::TableSetColumnIndex(0); 297 | ImGui::AlignTextToFramePadding(); 298 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0.0f, ImGui::GetStyle().FramePadding.y }); 299 | ImGui::PushFont(ctx->pgbFont); 300 | ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, 301 | ImGui::ColorConvertFloat4ToU32(ImGui::GetStyleColorVec4(ImGuiCol_TableBorderLight))); 302 | ImGui::PushStyleColor(ImGuiCol_NavCursor, 0x0); 303 | ImGui::SetNextItemOpen(true); 304 | bool open = ImGui::TreeNodeEx("Behavior", ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen); 305 | ImGui::PopStyleColor(); 306 | ImGui::PopFont(); 307 | ImGui::PopStyleVar(); 308 | //ImGui::Indent(); 309 | 310 | if (sel >= 0 && open) 311 | { 312 | int n = (int)columns[sel].Properties().size(); 313 | for (int i = 0; i < n; ++i) 314 | { 315 | ImGui::TableNextRow(); 316 | ImGui::TableSetColumnIndex(0); 317 | ImGui::AlignTextToFramePadding(); 318 | if (columns[sel].PropertyUI(i, *ctx)) 319 | CheckErrors(); 320 | } 321 | } 322 | if (open) 323 | ImGui::TreePop(); 324 | 325 | ImGui::EndTable(); 326 | pgHeight = ImGui::GetItemRectSize().y; 327 | } 328 | 329 | ImGui::PopStyleVar(2); 330 | ImGui::PopStyleColor(3); 331 | } 332 | 333 | void TableCols::CheckErrors() 334 | { 335 | error = ""; 336 | int noPolicy = 0; 337 | for (const auto& cd : columns) 338 | if (!(cd.sizingPolicy & ImGuiTableColumnFlags_WidthMask_)) 339 | ++noPolicy; 340 | if (noPolicy && noPolicy != columns.size()) 341 | error = "specify sizingPolicy either for all columns or none"; 342 | } 343 | -------------------------------------------------------------------------------- /src/ui_table_cols.h: -------------------------------------------------------------------------------- 1 | // Generated with ImRAD 0.9 2 | // visit https://github.com/tpecholt/imrad 3 | 4 | #pragma once 5 | #include "imrad.h" 6 | #include "node_container.h" 7 | 8 | class TableCols 9 | { 10 | public: 11 | /// @begin interface 12 | void OpenPopup(std::function clb = [](ImRad::ModalResult){}); 13 | void ClosePopup(ImRad::ModalResult mr = ImRad::Cancel); 14 | void Draw(); 15 | 16 | std::vector columns; 17 | UIContext* ctx; 18 | float sash = 180; 19 | std::string error; 20 | /// @end interface 21 | 22 | private: 23 | /// @begin impl 24 | void ResetLayout(); 25 | void Init(); 26 | 27 | void CheckErrors(); 28 | void AddButton_Change(); 29 | void RemoveButton_Change(); 30 | void UpButton_Change(); 31 | void DownButton_Change(); 32 | void Properties_Draw(const ImRad::CustomWidgetArgs& args); 33 | void Selectable_Change(); 34 | 35 | ImGuiID ID = 0; 36 | ImRad::ModalResult modalResult; 37 | std::function callback; 38 | int sel; 39 | ImRad::VBox vb1; 40 | ImRad::HBox hb4; 41 | /// @end impl 42 | }; 43 | 44 | extern TableCols tableCols; 45 | -------------------------------------------------------------------------------- /src/uicontext.cpp: -------------------------------------------------------------------------------- 1 | #include "uicontext.h" 2 | #include "cppgen.h" 3 | #include "node_standard.h" 4 | 5 | UIContext& UIContext::Defaults() 6 | { 7 | static UIContext ctx; 8 | ctx.createVars = false; 9 | return ctx; 10 | } 11 | 12 | void UIContext::ind_up() 13 | { 14 | ind += codeGen->INDENT; 15 | } 16 | 17 | void UIContext::ind_down() 18 | { 19 | if (ind.size() >= codeGen->INDENT.size()) 20 | ind.resize(ind.size() - codeGen->INDENT.size()); 21 | } 22 | 23 | std::string UIContext::GetCurrentArray() 24 | { 25 | for (size_t i = parents.size() - 1; i < parents.size(); --i) { 26 | const auto* parent = dynamic_cast(parents[i]); 27 | if (!parent) 28 | break; 29 | std::string id = parent->itemCount.container_var(); 30 | if (id != "") 31 | return id; 32 | } 33 | return ""; 34 | } -------------------------------------------------------------------------------- /src/uicontext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct UINode; 8 | struct Widget; 9 | class CppGen; 10 | struct property_base; 11 | struct ImGuiWindow; 12 | 13 | struct UIContext 14 | { 15 | //set from outside 16 | enum Mode { 17 | NormalSelection, RectSelection, SnapInsert, SnapMove, PickPoint, 18 | ItemDragging, 19 | ItemSizingLeft = 0x100, ItemSizingRight = 0x200, 20 | ItemSizingTop = 0x400, ItemSizingBottom = 0x800, 21 | ItemSizingMask = ItemSizingLeft | ItemSizingRight | ItemSizingTop | ItemSizingBottom, 22 | }; 23 | Mode mode = NormalSelection; 24 | std::vector selected; 25 | CppGen* codeGen = nullptr; 26 | ImVec2 designAreaMin, designAreaMax; //ImRect is internal? 27 | std::string workingDir; 28 | enum Color { Hovered, Selected, Snap1, Snap2, Snap3, Snap4, Snap5, COUNT }; 29 | std::array colors; 30 | std::vector fontNames; 31 | ImFont* defaultStyleFont = nullptr; 32 | ImFont* pgFont = nullptr; 33 | ImFont* pgbFont = nullptr; 34 | std::string unit; //for dimension export 35 | bool createVars = true; //create variables etc. during contructor/Clone calls 36 | ImGuiStyle style; 37 | const ImGuiStyle* appStyle = nullptr; 38 | const property_base* setProp = nullptr; 39 | std::string setPropValue; 40 | ImTextureID dashTexId = 0; 41 | bool* modified = nullptr; 42 | 43 | //snap result 44 | UINode* snapParent = nullptr; 45 | size_t snapIndex; 46 | int snapNextColumn; 47 | bool snapSameLine; 48 | bool snapUseNextSpacing; 49 | bool snapSetNextSameLine; 50 | 51 | //recursive info 52 | int importState = 0; //0 - no import, 1 - within begin/end/separator, 2 - user code import 53 | UINode* hovered = nullptr; 54 | UINode* dragged = nullptr; 55 | ImVec2 lastSize; 56 | int importVersion; 57 | int importLevel; 58 | std::string userCode; 59 | UINode* root = nullptr; 60 | ImGuiWindow* rootWin = nullptr; 61 | bool isAutoSize; 62 | ImU32 layoutHash = 0, prevLayoutHash = 0; 63 | ImU32 prevDockspaceHash = 0; 64 | bool beingResized = false; 65 | std::vector activePopups; 66 | std::vector parents; 67 | std::vector contextMenus; 68 | int kind = 0; //TopWindow::Kind 69 | float zoomFactor = 1; //for dimension value scaling 70 | ImVec2 selStart, selEnd; 71 | std::string ind; 72 | int varCounter; 73 | std::string parentVarName; 74 | std::vector errors; 75 | ImVec2 stretchSize; 76 | std::array stretchSizeExpr; 77 | 78 | //convenience 79 | void ind_up(); 80 | void ind_down(); 81 | std::string GetCurrentArray(); 82 | 83 | static UIContext& Defaults(); 84 | }; 85 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "node_standard.h" 3 | #include "binding_input.h" 4 | #if WIN32 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | #endif 9 | #undef min 10 | #undef max 11 | 12 | bool ShellExec(const std::string& path) 13 | { 14 | #ifdef WIN32 15 | return (uintptr_t)ShellExecuteW(nullptr, L"open", u8path(path).c_str(), nullptr, nullptr, SW_SHOWNORMAL) > 32; 16 | #else 17 | return system(("xdg-open " + path).c_str()) == 0; 18 | #endif 19 | } 20 | 21 | int DefaultCharFilter(ImGuiInputTextCallbackData* data) 22 | { 23 | //always map numpad decimal point to . (all expressions are in C) 24 | //int localeDP = *localeconv()->decimal_point; 25 | if (data->EventChar == ',' && ImGui::IsKeyDown(ImGuiKey_KeypadDecimal)) 26 | data->EventChar = '.'; 27 | return 0; 28 | } 29 | 30 | std::string CodeShortcut(std::string_view sh) 31 | { 32 | if (sh.empty()) 33 | return ""; 34 | size_t i = -1; 35 | std::string code; 36 | while (true) 37 | { 38 | size_t j = sh.find_first_of("+-", i + 1); 39 | if (j == std::string::npos) 40 | j = sh.size(); 41 | std::string key(sh.substr(i + 1, j - i - 1)); 42 | if (key.empty() && j < sh.size()) //like in ctrl-+ 43 | key = sh[j]; 44 | stx::erase_if(key, [](char c) { return std::isspace(c); }); 45 | if (key.empty()) 46 | break; 47 | std::string lk = key; 48 | stx::for_each(lk, [](char& c) { c = std::tolower(c); }); 49 | //todo 50 | if (lk == "ctrl") 51 | code += "ImGuiMod_Ctrl"; 52 | else if (lk == "alt") 53 | code += "ImGuiMod_Alt"; 54 | else if (lk == "shift") 55 | code += "ImGuiMod_Shift"; 56 | else if (key == "+") 57 | code += "ImGuiKey_Equal"; 58 | else if (key == "-") 59 | code += "ImGuiKey_Minus"; 60 | else if (key == ".") 61 | code += "ImGuiKey_Period"; 62 | else if (key == ",") 63 | code += "ImGuiKey_Comma"; 64 | else if (key == "/") 65 | code += "ImGuiKey_Slash"; 66 | else 67 | code += std::string("ImGuiKey_") 68 | + (char)std::toupper(key[0]) 69 | + key.substr(1); 70 | code += " | "; 71 | i = j; 72 | if (j == sh.size()) 73 | break; 74 | } 75 | if (code.size()) 76 | code.resize(code.size() - 3); 77 | return code; 78 | } 79 | 80 | std::string ParseShortcut(std::string_view line) 81 | { 82 | std::string sh; 83 | size_t i = 0; 84 | while (true) 85 | { 86 | size_t j1 = line.find("ImGuiKey_", i); 87 | size_t j2 = line.find("ImGuiMod_", i); 88 | if (j1 == std::string::npos && j2 == std::string::npos) 89 | break; 90 | if (j1 < j2) 91 | i = j1 + 9; 92 | else 93 | i = j2 + 9; 94 | for (; i < line.size(); ++i) 95 | { 96 | if (!isalnum(line[i])) 97 | break; 98 | sh += line[i]; 99 | } 100 | sh += "+"; 101 | } 102 | if (sh.size()) 103 | sh.pop_back(); 104 | return sh; 105 | } 106 | 107 | bool IsAscii(std::string_view str) 108 | { 109 | return !stx::count_if(str, [](char c) { return c < 0; }); 110 | } 111 | 112 | std::string Trim(std::string_view str) 113 | { 114 | size_t i1, i2; 115 | for (i1 = 0; i1 < str.size(); ++i1) 116 | if (str[i1] >= 128 || !std::isspace(str[i1])) 117 | break; 118 | for (i2 = str.size() - 1; i2 < str.size(); --i2) 119 | if (str[i2] >= 128 || !std::isspace(str[i2])) 120 | break; 121 | return std::string(str.substr(i1, i2 - i1 + 1)); 122 | } 123 | 124 | std::string Replace(std::string_view s, std::string_view sold, std::string_view snew) 125 | { 126 | std::string out; 127 | size_t i = 0; 128 | while (i < s.size()) { 129 | size_t j = s.find(sold, i); 130 | if (j == std::string::npos) 131 | break; 132 | out += s.substr(i, j - i); 133 | out += snew; 134 | i = j + sold.size(); 135 | } 136 | out += s.substr(i); 137 | return out; 138 | } 139 | 140 | fs::path u8path(std::string_view s) 141 | { 142 | #if __cplusplus >= 202002L 143 | return fs::path((const char8_t*)s.data(), (const char8_t*)s.data() + s.size()); 144 | #else 145 | return fs::u8path(s); 146 | #endif 147 | } 148 | 149 | std::string u8string(const fs::path& p) 150 | { 151 | #if __cplusplus >= 202002L 152 | return std::string((const char*)p.u8string().data()); 153 | #else 154 | return p.u8string(); 155 | #endif 156 | } 157 | 158 | std::string generic_u8string(const fs::path& p) 159 | { 160 | #if __cplusplus >= 202002L 161 | return std::string((const char*)p.generic_u8string().data()); 162 | #else 163 | return p.generic_u8string(); 164 | #endif 165 | } 166 | 167 | bool path_cmp(const std::string& a, const std::string& b) 168 | { 169 | size_t n = std::min(a.size(), b.size()); 170 | for (size_t i = 0; i < n; ++i) 171 | { 172 | int ca = a[i]; 173 | int cb = b[i]; 174 | if (ca >= 'a' && ca <= 'z') 175 | ca += 'A' - 'a'; 176 | if (cb >= 'a' && cb <= 'z') 177 | cb += 'A' - 'a'; 178 | if (ca != cb) 179 | return ca < cb; 180 | } 181 | return b.size() > a.size(); 182 | } -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | namespace fs = std::filesystem; 9 | 10 | inline const std::string VER_STR = "ImRAD 0.9"; 11 | inline const std::string GITHUB_URL = "https://github.com/tpecholt/imrad"; 12 | inline const std::string CUR_ITEM_SYMBOL = "$item"; 13 | 14 | inline ImVec2 operator+ (const ImVec2& a, const ImVec2& b) 15 | { 16 | return ImVec2(a.x + b.x, a.y + b.y); 17 | } 18 | 19 | inline ImVec2 operator- (const ImVec2& a, const ImVec2& b) 20 | { 21 | return ImVec2(a.x - b.x, a.y - b.y); 22 | } 23 | 24 | inline ImVec2 operator* (const ImVec2& a, float f) 25 | { 26 | return { a.x * f, a.y * f }; 27 | } 28 | 29 | inline ImVec2 operator* (float f, const ImVec2& a) 30 | { 31 | return a * f; 32 | } 33 | 34 | inline ImVec2 operator/ (const ImVec2& a, float f) 35 | { 36 | return { a.x / f, a.y / f }; 37 | } 38 | 39 | inline ImVec2& operator+= (ImVec2& a, const ImVec2& b) 40 | { 41 | a = a + b; 42 | return a; 43 | } 44 | 45 | inline ImVec2& operator*= (ImVec2& a, float f) 46 | { 47 | a = a * f; 48 | return a; 49 | } 50 | 51 | inline ImVec2& operator/= (ImVec2& a, float f) 52 | { 53 | a = a / f; 54 | return a; 55 | } 56 | 57 | inline float Norm(const ImVec2& a) 58 | { 59 | return std::sqrt(a.x * a.x + a.y*a.y); 60 | } 61 | 62 | //----------------------------------------------------------------------- 63 | 64 | inline std::string operator+ (const std::string& s, std::string_view t) 65 | { 66 | std::string ss = s; 67 | ss += t; 68 | return ss; 69 | } 70 | 71 | inline std::string operator+ (std::string_view t, const std::string& s) 72 | { 73 | std::string ss = s; 74 | ss.insert(0, t); 75 | return ss; 76 | } 77 | 78 | inline std::ostream& operator<< (std::ostream& os, std::string_view t) 79 | { 80 | os.write(t.data(), t.size()); 81 | return os; 82 | } 83 | 84 | //---------------------------------------------------------------------- 85 | 86 | template 87 | inline void HashCombineData(ImU32& hash, T data) 88 | { 89 | extern IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed); 90 | hash = ImHashData(&data, sizeof(data), hash); 91 | } 92 | 93 | bool ShellExec(const std::string& path); 94 | 95 | int DefaultCharFilter(ImGuiInputTextCallbackData* data); 96 | 97 | std::string CodeShortcut(std::string_view sh); 98 | std::string ParseShortcut(std::string_view line); 99 | 100 | bool IsAscii(std::string_view str); 101 | std::string Trim(std::string_view str); 102 | std::string Replace(std::string_view s, std::string_view sold, std::string_view snew); 103 | 104 | fs::path u8path(std::string_view s); 105 | std::string u8string(const fs::path& p); 106 | std::string generic_u8string(const fs::path& p); 107 | bool path_cmp(const std::string& a, const std::string& b); -------------------------------------------------------------------------------- /style/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/style/dash.png -------------------------------------------------------------------------------- /style/icon-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/style/icon-100.png -------------------------------------------------------------------------------- /style/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpecholt/imrad/e4a8f66438c7182f7c6f334c3c6865c68b0a8cb5/style/icon-40.png -------------------------------------------------------------------------------- /style/test.ini: -------------------------------------------------------------------------------- 1 | [colors] 2 | Text = 0 0 0 255 3 | TextDisabled = 153 153 153 255 4 | WindowBg = 227 227 227 227 5 | ChildBg = 0 0 0 0 6 | PopupBg = 242 242 242 227 7 | Border = 0 0 0 94 8 | BorderShadow = 242 242 242 24 9 | FrameBg = 242 242 242 227 10 | FrameBgHovered = 62 142 237 96 11 | FrameBgActive = 62 142 237 162 12 | TitleBg = 244 244 244 255 13 | TitleBgActive = 209 209 209 255 14 | TitleBgCollapsed = 242 242 242 123 15 | MenuBarBg = 219 219 219 255 16 | ScrollbarBg = 237 237 237 128 17 | ScrollbarGrab = 175 175 175 255 18 | ScrollbarGrabHovered = 150 150 150 255 19 | ScrollbarGrabActive = 124 124 124 255 20 | CheckMark = 66 150 249 255 21 | SliderGrab = 61 132 224 255 22 | SliderGrabActive = 66 150 249 255 23 | Button = 142 142 142 121 24 | ButtonHovered = 249 99 91 255 25 | ButtonActive = 249 99 91 255 26 | Header = 62 142 237 75 27 | HeaderHovered = 62 142 237 188 28 | HeaderActive = 66 150 249 255 29 | Separator = 94 94 94 150 30 | SeparatorHovered = 33 106 193 188 31 | SeparatorActive = 35 112 204 255 32 | ResizeGrip = 242 242 242 121 33 | ResizeGripHovered = 62 142 237 162 34 | ResizeGripActive = 62 142 237 230 35 | Tab = 185 193 202 225 36 | TabHovered = 62 142 237 193 37 | TabActive = 151 185 225 255 38 | TabUnfocused = 223 224 226 238 39 | TabUnfocusedActive = 189 209 233 255 40 | DockingPreview = 62 142 237 52 41 | DockingEmptyBg = 51 51 51 255 42 | PlotLines = 99 99 99 255 43 | PlotLinesHovered = 255 109 89 255 44 | PlotHistogram = 229 178 0 255 45 | PlotHistogramHovered = 255 153 0 255 46 | TableHeaderBg = 198 221 249 255 47 | TableBorderStrong = 145 145 163 255 48 | TableBorderLight = 173 173 188 255 49 | TableRowBg = 0 0 0 0 50 | TableRowBgAlt = 72 72 72 21 51 | TextSelectedBg = 62 142 237 84 52 | DragDropTarget = 62 142 237 230 53 | NavHighlight = 62 142 237 193 54 | NavWindowingHighlight = 169 169 169 169 55 | NavWindowingDimBg = 48 48 48 48 56 | ModalWindowDimBg = 48 48 48 84 57 | 58 | [variables] 59 | Alpha = 1 60 | DisabledAlpha = 0.6 61 | WindowPadding = 8 8 62 | WindowRounding = 3 63 | WindowBorderSize = 1 64 | PopupRounding = 0 65 | PopupBorderSize = 1 66 | FramePadding = 4 3 67 | FrameRounding = 3 68 | FrameBorderSize = 0 69 | ItemSpacing = 8 4 70 | ItemInnerSpacing = 4 4 71 | CellPadding = 4 2 72 | 73 | [fonts] 74 | Default = "Roboto-Medium.ttf" size 20 75 | Default = "fa-regular-400.ttf" size 18 range 57349 63743 76 | Default = "fa-solid-900.ttf" size 18 range 57349 63743 77 | H1 = "Roboto-Medium.ttf" size 32 78 | #H1 = "fa-regular-400.ttf" size 30 range 57349 63743 79 | 80 | [imrad.colors] 81 | Hovered = 255 128 128 255 82 | Selected = 255 0 0 255 83 | Snap1 = 0 0 0 255 84 | Snap2 = 64 64 64 255 85 | Snap3 = 128 128 128 255 -------------------------------------------------------------------------------- /template/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /template/android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html. 3 | # For more examples on how to use CMake, see https://github.com/android/ndk-samples. 4 | 5 | # Sets the minimum CMake version required for this project. 6 | cmake_minimum_required(VERSION 3.22.1) 7 | 8 | # Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, 9 | # Since this is the top level CMakeLists.txt, the project name is also accessible 10 | # with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level 11 | # build script scope). 12 | project("${LIB_NAME}") 13 | 14 | # Creates and names a library, sets it as either STATIC 15 | # or SHARED, and provides the relative paths to its source code. 16 | # You can define multiple libraries, and CMake builds them for you. 17 | # Gradle automatically packages shared libraries with your APK. 18 | # 19 | # In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define 20 | # the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} 21 | # is preferred for the same purpose. 22 | # 23 | # In order to load a library into your app from Java/Kotlin, you must call 24 | # System.loadLibrary() and pass the name of the library defined here; 25 | # for GameActivity/NativeActivity derived applications, the same library name must be 26 | # used in the AndroidManifest.xml file. 27 | add_library(${CMAKE_PROJECT_NAME} SHARED 28 | # List C/C++ source files with relative paths to this CMakeLists.txt. 29 | main.cpp 30 | # 31 | imgui/imgui.cpp 32 | imgui/imgui_demo.cpp 33 | imgui/imgui_draw.cpp 34 | imgui/imgui_tables.cpp 35 | imgui/imgui_widgets.cpp 36 | imgui/misc/cpp/imgui_stdlib.cpp 37 | imgui/backends/imgui_impl_android.cpp 38 | imgui/backends/imgui_impl_opengl3.cpp 39 | ${CMAKE_ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) 40 | 41 | # Specifies libraries CMake should link to your target library. You 42 | # can link libraries from various origins, such as libraries defined in this 43 | # build script, prebuilt third-party libraries, or Android system libraries. 44 | target_link_libraries(${CMAKE_PROJECT_NAME} 45 | # List libraries link to the target library 46 | android 47 | log 48 | EGL 49 | GLESv3) 50 | 51 | target_include_directories(${CMAKE_PROJECT_NAME} 52 | PRIVATE 53 | ${CMAKE_ANDROID_NDK}/sources/android/native_app_glue 54 | ${CMAKE_CURRENT_SOURCE_DIR}/imgui 55 | ${IMRAD_INCLUDE} 56 | ) -------------------------------------------------------------------------------- /template/android/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ${JAVA_PACKAGE}; 2 | 3 | import android.app.Activity; 4 | import android.app.NativeActivity; 5 | import android.content.Context; 6 | import android.content.pm.ActivityInfo; 7 | import android.content.pm.PackageManager; 8 | import android.content.res.Configuration; 9 | import android.os.Bundle; 10 | import android.text.Editable; 11 | import android.text.InputType; 12 | import android.view.KeyEvent; 13 | import android.text.TextWatcher; 14 | import android.view.View; 15 | import android.view.inputmethod.EditorInfo; 16 | import android.view.inputmethod.InputMethodManager; 17 | import android.view.ViewTreeObserver; 18 | import android.widget.Toast; 19 | import android.widget.EditText; 20 | import android.widget.FrameLayout; 21 | import android.widget.TextView; 22 | import android.graphics.Rect; 23 | 24 | 25 | public class MainActivity extends NativeActivity 26 | implements TextWatcher, TextView.OnEditorActionListener 27 | { 28 | protected View mView; 29 | private EditText mEditText; 30 | 31 | private native void OnKeyboardShown(int h); 32 | private native void OnScreenRotation(int deg); 33 | private native void OnInputCharacter(int ch); 34 | private native void OnSpecialKey(int code); 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | 39 | super.onCreate(savedInstanceState); 40 | 41 | //load native library 42 | try { 43 | ActivityInfo ai = getPackageManager().getActivityInfo( 44 | getIntent().getComponent(), PackageManager.GET_META_DATA); 45 | if (ai.metaData != null) { 46 | String ln = ai.metaData.getString(META_DATA_LIB_NAME); 47 | System.loadLibrary(ln); 48 | } 49 | } 50 | catch (PackageManager.NameNotFoundException e) { 51 | } 52 | 53 | //create hidden EditText to access more soft keyboard options 54 | FrameLayout lay = new FrameLayout(this); 55 | setContentView(lay, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 56 | mEditText = new EditText(this); 57 | mEditText.setOnEditorActionListener(this); 58 | mEditText.addTextChangedListener(this); 59 | mEditText.setLayoutParams(new FrameLayout.LayoutParams(1, 1)); 60 | lay.addView(mEditText); 61 | mView = new View(this); 62 | mView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 63 | lay.addView(mView); 64 | 65 | //install observer to monitor if keyboard is shown 66 | View mRootView = getWindow().getDecorView().findViewById(android.R.id.content); 67 | mRootView.getViewTreeObserver().addOnGlobalLayoutListener( 68 | new ViewTreeObserver.OnGlobalLayoutListener() { 69 | public void onGlobalLayout() { 70 | View view = getWindow().getDecorView(); 71 | int screenHeight = view.getRootView().getHeight(); 72 | Rect r = new Rect(); 73 | view.getWindowVisibleDisplayFrame(r); 74 | //double hkbd = 2.54 * (double)(screenHeight - r.bottom) / getDpi(); 75 | //boolean kbdShown = hkbd > 3; //kbd height more than 3cm 76 | OnKeyboardShown(screenHeight - r.bottom); 77 | } 78 | }); 79 | } 80 | 81 | //@param type - see ImRad::IMEType 82 | public void showSoftInput(int _type) { 83 | final int[] actionMap = { 84 | EditorInfo.IME_ACTION_NONE, 85 | EditorInfo.IME_ACTION_DONE, EditorInfo.IME_ACTION_GO, 86 | EditorInfo.IME_ACTION_NEXT, EditorInfo.IME_ACTION_PREVIOUS, 87 | EditorInfo.IME_ACTION_SEARCH, EditorInfo.IME_ACTION_SEND 88 | }; 89 | final int type = _type; 90 | runOnUiThread(new Runnable() { 91 | @Override 92 | public void run() { 93 | InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 94 | //int sel1 = mEditText.getSelectionStart(); 95 | //int sel2 = mEditText.getSelectionEnd(); 96 | mEditText.setText(""); 97 | mEditText.setImeOptions(actionMap[type >> 8]); 98 | mEditText.requestFocus(); 99 | switch (type & 0xff) { 100 | case 0: 101 | mgr.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); 102 | break; 103 | default: //ImeText 104 | //to enforce NO_SUGGESTIONS we can use TYPE_TEXT_VARIATION_VISIBLE_PASSWORD but then Passwords button may be shown which is weird 105 | //mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; //this resets selection 106 | //this seems to work better: 107 | mEditText.setInputType(InputType.TYPE_NULL); 108 | //mEditText.setSelection(sel1, sel2); 109 | mgr.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 110 | break; 111 | case 2: //ImeNumber 112 | mEditText.setInputType(InputType.TYPE_CLASS_NUMBER); //this resets selection 113 | //mEditText.setSelection(sel1, sel2); 114 | mgr.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 115 | break; 116 | case 3: //ImeDecimal 117 | mEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); //this resets selection 118 | mEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); //this resets selection 119 | //mEditText.setSelection(sel1, sel2); 120 | mgr.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 121 | break; 122 | case 4: //ImePhone 123 | mEditText.setInputType(InputType.TYPE_CLASS_PHONE); //this resets selection 124 | //mEditText.setSelection(sel1, sel2); 125 | mgr.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 126 | break; 127 | case 5: //ImeEmail 128 | mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); //this resets selection 129 | //mEditText.setSelection(sel1, sel2); 130 | mgr.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 131 | break; 132 | } 133 | } 134 | }); 135 | } 136 | 137 | @Override 138 | public void beforeTextChanged(CharSequence var1, int var2, int var3, int var4) { 139 | } 140 | 141 | @Override 142 | public void afterTextChanged(Editable var1) { 143 | } 144 | 145 | @Override 146 | public void onTextChanged(CharSequence text, int start, int before, int count) { 147 | if (count < before) 148 | OnInputCharacter(0x8); //send backspace 149 | else if (count == before) 150 | ; //ignore, f.e. pressing @ in ImeEmail fires this 2x 151 | else if (count >= 1) 152 | OnInputCharacter((int)text.charAt(start + count - 1)); 153 | } 154 | 155 | @Override 156 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 157 | OnSpecialKey(actionId + 1024); 158 | return true; 159 | } 160 | 161 | //Use this only for special keys, otherwise not called with IME_TEXT, not called on hw keyboard 162 | @Override 163 | public boolean dispatchKeyEvent(KeyEvent ev) { 164 | //intercept Back button 165 | if (ev.getAction() == KeyEvent.ACTION_DOWN && ev.getKeyCode() == KeyEvent.KEYCODE_BACK) { 166 | OnSpecialKey(ev.getKeyCode()); 167 | return false; 168 | } 169 | return super.dispatchKeyEvent(ev); 170 | } 171 | /*@Override 172 | public boolean dispatchKeyEvent(KeyEvent ev) { 173 | if (ev.getAction() == KeyEvent.ACTION_DOWN) { 174 | int ch = ev.getUnicodeChar(ev.getMetaState()); 175 | if (ch >= 0x20) //control characters handled elsewhere 176 | unicodeQueue.offer(ch); 177 | } 178 | else if (ev.getAction() == KeyEvent.ACTION_MULTIPLE) { 179 | for (int i = 0; i < ev.getCharacters().length(); ++i) { 180 | int ch = ev.getCharacters().codePointAt(i); 181 | if (ch >= 0x20) //control characters handled elsewhere 182 | unicodeQueue.offer(ch); 183 | } 184 | } 185 | return super.dispatchKeyEvent(ev); 186 | }*/ 187 | 188 | @Override 189 | public void onConfigurationChanged(Configuration cfg) { 190 | super.onConfigurationChanged(cfg); 191 | int angle = 90 * getWindowManager().getDefaultDisplay().getRotation(); 192 | OnScreenRotation(angle); 193 | } 194 | 195 | // JNI calls -------------------------------------------------- 196 | 197 | public int getDpi() { 198 | Configuration cfg = getResources().getConfiguration(); 199 | return cfg.densityDpi; 200 | } 201 | public int getRotation() { 202 | int angle = 90 * getWindowManager().getDefaultDisplay().getRotation(); 203 | return angle; 204 | } 205 | public void showMessage(String _msg) { 206 | final String msg = _msg; 207 | final Activity act = this; 208 | runOnUiThread(new Runnable() { 209 | @Override 210 | public void run() { 211 | Toast.makeText(act, msg, Toast.LENGTH_SHORT); 212 | } 213 | }); 214 | } 215 | public void performHapticFeedback(int kind) { 216 | mView.performHapticFeedback(kind); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /template/glfw/main.cpp: -------------------------------------------------------------------------------- 1 | #include "imrad.h" 2 | #include 3 | //#include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef IMGUI_IMPL_OPENGL_ES2 9 | #include 10 | #endif 11 | #ifndef IMRAD_WITH_GLFW 12 | #error Please recompile with IMRAD_WITH_GLFW to unlock all features such as GLFW MainWindow 13 | #endif 14 | #include // Will drag system OpenGL headers 15 | 16 | //TODO: add your includes here 17 | 18 | // must come last 19 | #ifdef IMRAD_WITH_STB 20 | #define STB_IMAGE_IMPLEMENTATION 21 | #include 22 | #endif 23 | 24 | GLFWwindow* window; 25 | ImRad::IOUserData ioUserData; 26 | 27 | void Draw() 28 | { 29 | // TODO: Add your drawing code here 30 | ImGui::ShowDemoWindow(); 31 | } 32 | 33 | static void glfw_error_callback(int error, const char* description) 34 | { 35 | std::cerr << "Glfw Error: " << description; 36 | } 37 | 38 | // On Windows if you want to avoid console window to be shown 39 | // Use /SUBSYSTEM:WINDOWS and implement wWinMain instead 40 | // int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) 41 | int main(int argc, const char* argv[]) 42 | { 43 | // Setup window 44 | glfwSetErrorCallback(glfw_error_callback); 45 | if (!glfwInit()) 46 | return 1; 47 | 48 | // Decide GL+GLSL versions 49 | #if defined(IMGUI_IMPL_OPENGL_ES2) 50 | // GL ES 2.0 + GLSL 100 51 | const char* glsl_version = "#version 100"; 52 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 53 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 54 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 55 | #elif defined(__APPLE__) 56 | // GL 3.2 + GLSL 150 57 | const char* glsl_version = "#version 150"; 58 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 59 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 60 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only 61 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac 62 | #else 63 | // GL 3.0 + GLSL 130 64 | const char* glsl_version = "#version 130"; 65 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 66 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 67 | //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only 68 | //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only 69 | #endif 70 | 71 | // Create window with graphics context 72 | window = glfwCreateWindow(1280, 720, "Test", NULL, NULL); 73 | if (window == NULL) 74 | return 1; 75 | glfwMakeContextCurrent(window); 76 | glfwSwapInterval(1); // Enable vsync 77 | //glfwMaximizeWindow(window); 78 | 79 | // Setup Dear ImGui context 80 | IMGUI_CHECKVERSION(); 81 | ImGui::CreateContext(); 82 | ImGuiIO& io = ImGui::GetIO(); 83 | io.UserData = &ioUserData; 84 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; 85 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 86 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 87 | 88 | // Setup Platform/Renderer backends 89 | ImGui_ImplGlfw_InitForOpenGL(window, true); 90 | ImGui_ImplOpenGL3_Init(glsl_version); 91 | 92 | // TODO: Load custom style and fonts from the ImRAD INI file 93 | //ImRad::LoadStyle("my-style.ini"); 94 | 95 | // Alternatively set ImGui style and fonts manually 96 | // Read 'docs/FONTS.md' for more instructions and details. 97 | ImGui::StyleColorsDark(); 98 | ImGui::GetStyle().ScaleAllSizes(ioUserData.dpiScale); 99 | 100 | /* 101 | io.Fonts->AddFontFromFileTTF("Roboto-Medium.ttf", 20.0f * ioUserData.dpiScale); 102 | 103 | ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 }; 104 | ImFontConfig icons_config; 105 | icons_config.MergeMode = true; 106 | //icons_config.PixelSnapH = true; 107 | io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_FAR, 18.0f * ioUserData.dpiScale, &icons_config, icons_ranges); 108 | io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_FAS, 18.0f * ioUserData.dpiScale, &icons_config, icons_ranges); 109 | */ 110 | 111 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 112 | 113 | while (true) 114 | { 115 | if (glfwWindowShouldClose(window)) 116 | break; 117 | 118 | // Poll and handle events (inputs, window resize, etc.) 119 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 120 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. 121 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. 122 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 123 | glfwPollEvents(); 124 | 125 | // Start the Dear ImGui frame 126 | ImGui_ImplOpenGL3_NewFrame(); 127 | ImGui_ImplGlfw_NewFrame(); 128 | ImGui::NewFrame(); 129 | 130 | Draw(); 131 | 132 | // Rendering 133 | ImGui::Render(); 134 | int display_w, display_h; 135 | glfwGetFramebufferSize(window, &display_w, &display_h); 136 | glViewport(0, 0, display_w, display_h); 137 | glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); 138 | glClear(GL_COLOR_BUFFER_BIT); 139 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 140 | 141 | glfwSwapBuffers(window); 142 | } 143 | 144 | // Cleanup 145 | ImGui_ImplOpenGL3_Shutdown(); 146 | ImGui_ImplGlfw_Shutdown(); 147 | ImGui::DestroyContext(); 148 | 149 | glfwDestroyWindow(window); 150 | glfwTerminate(); 151 | 152 | return 0; 153 | } 154 | --------------------------------------------------------------------------------