├── .gitignore ├── cimgui ├── cimgui_all.h ├── LICENSE.txt ├── imconfig.h ├── imstb_rectpack.h └── imstb_textedit.h ├── update_deps.sh ├── sokol ├── sokol.c ├── shell.html ├── sokol_glue.h └── sokol_log.h ├── LICENSE ├── demo.c ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | -------------------------------------------------------------------------------- /cimgui/cimgui_all.h: -------------------------------------------------------------------------------- 1 | #include "cimgui.h" 2 | #include "cimgui_internal.h" 3 | -------------------------------------------------------------------------------- /update_deps.sh: -------------------------------------------------------------------------------- 1 | cp ../sokol/sokol_app.h sokol/ 2 | cp ../sokol/sokol_gfx.h sokol/ 3 | cp ../sokol/sokol_log.h sokol/ 4 | cp ../sokol/sokol_glue.h sokol/ 5 | cp ../sokol/util/sokol_imgui.h sokol/ 6 | cp ../dcimgui/src/* cimgui/ 7 | -------------------------------------------------------------------------------- /sokol/sokol.c: -------------------------------------------------------------------------------- 1 | // sokol implementation library on non-Apple platforms 2 | #define SOKOL_IMPL 3 | #if defined(__MINGW32__) 4 | #define SOKOL_GLCORE 5 | #elif defined(_WIN32) 6 | #define SOKOL_D3D11 7 | #elif defined(__EMSCRIPTEN__) 8 | #define SOKOL_GLES3 9 | #elif defined(__APPLE__) 10 | // NOTE: on macOS, sokol.c is compiled explicitely as ObjC 11 | #define SOKOL_METAL 12 | #else 13 | #define SOKOL_GLCORE 14 | #endif 15 | #include "sokol_app.h" 16 | #include "sokol_gfx.h" 17 | #include "sokol_log.h" 18 | #include "sokol_glue.h" 19 | #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS 20 | #include "cimgui.h" 21 | #define SOKOL_IMGUI_IMPL 22 | #include "sokol_imgui.h" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andre Weissflog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cimgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2025 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo.c: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Simple C99 cimgui+sokol starter project for Win32, Linux and macOS. 3 | //------------------------------------------------------------------------------ 4 | #include "sokol_app.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_log.h" 7 | #include "sokol_glue.h" 8 | #include "cimgui.h" 9 | #include "sokol_imgui.h" 10 | 11 | static struct { 12 | sg_pass_action pass_action; 13 | } state; 14 | 15 | static void init(void) { 16 | sg_setup(&(sg_desc){ 17 | .environment = sglue_environment(), 18 | .logger.func = slog_func, 19 | }); 20 | simgui_setup(&(simgui_desc_t){ 0 }); 21 | 22 | // initial clear color 23 | state.pass_action = (sg_pass_action) { 24 | .colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.0f, 0.5f, 1.0f, 1.0 } } 25 | }; 26 | } 27 | 28 | static void frame(void) { 29 | simgui_new_frame(&(simgui_frame_desc_t){ 30 | .width = sapp_width(), 31 | .height = sapp_height(), 32 | .delta_time = sapp_frame_duration(), 33 | .dpi_scale = sapp_dpi_scale(), 34 | }); 35 | 36 | /*=== UI CODE STARTS HERE ===*/ 37 | igSetNextWindowPos((ImVec2){10,10}, ImGuiCond_Once); 38 | igSetNextWindowSize((ImVec2){400, 100}, ImGuiCond_Once); 39 | igBegin("Hello Dear ImGui!", 0, ImGuiWindowFlags_None); 40 | igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ImGuiColorEditFlags_None); 41 | igEnd(); 42 | /*=== UI CODE ENDS HERE ===*/ 43 | 44 | sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = sglue_swapchain() }); 45 | simgui_render(); 46 | sg_end_pass(); 47 | sg_commit(); 48 | } 49 | 50 | static void cleanup(void) { 51 | simgui_shutdown(); 52 | sg_shutdown(); 53 | } 54 | 55 | static void event(const sapp_event* ev) { 56 | simgui_handle_event(ev); 57 | } 58 | 59 | sapp_desc sokol_main(int argc, char* argv[]) { 60 | (void)argc; 61 | (void)argv; 62 | return (sapp_desc){ 63 | .init_cb = init, 64 | .frame_cb = frame, 65 | .cleanup_cb = cleanup, 66 | .event_cb = event, 67 | .window_title = "Hello Sokol + Dear ImGui", 68 | .width = 800, 69 | .height = 600, 70 | .icon.sokol_default = true, 71 | .logger.func = slog_func, 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /sokol/shell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 29 | 30 | 31 | 32 | 62 | {{{ SCRIPT }}} 63 | 64 | 65 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(cimgui-sokol-starterkit) 3 | set(CMAKE_C_STANDARD 11) 4 | set(CMAKE_CXX_STANDARD 11) 5 | # Linux -pthread shenanigans 6 | if (CMAKE_SYSTEM_NAME STREQUAL Linux) 7 | set(THREADS_PREFER_PTHREAD_FLAG ON) 8 | find_package(Threads REQUIRED) 9 | endif() 10 | 11 | #=== LIBRARY: cimgui + Dear ImGui 12 | add_library(cimgui STATIC 13 | cimgui/cimgui.cpp 14 | cimgui/cimgui.h 15 | cimgui/imgui.cpp 16 | cimgui/imgui.h 17 | cimgui/imgui_widgets.cpp 18 | cimgui/imgui_draw.cpp 19 | cimgui/imgui_tables.cpp 20 | cimgui/imgui_demo.cpp) 21 | target_include_directories(cimgui INTERFACE cimgui) 22 | 23 | #=== LIBRARY: sokol 24 | # add headers to the the file list because they are useful to have in IDEs 25 | set(SOKOL_HEADERS 26 | sokol/sokol_gfx.h 27 | sokol/sokol_app.h 28 | sokol/sokol_imgui.h 29 | sokol/sokol_glue.h) 30 | if(CMAKE_SYSTEM_NAME STREQUAL Darwin) 31 | add_library(sokol STATIC sokol/sokol.c ${SOKOL_HEADERS}) 32 | target_compile_options(sokol PRIVATE -x objective-c) 33 | target_link_libraries(sokol PUBLIC 34 | "-framework QuartzCore" 35 | "-framework Cocoa" 36 | "-framework MetalKit" 37 | "-framework Metal") 38 | else() 39 | add_library(sokol STATIC sokol/sokol.c ${SOKOL_HEADERS}) 40 | if (CMAKE_SYSTEM_NAME STREQUAL Linux) 41 | target_link_libraries(sokol INTERFACE X11 Xi Xcursor GL dl m) 42 | target_link_libraries(sokol PUBLIC Threads::Threads) 43 | endif() 44 | endif() 45 | target_link_libraries(sokol PUBLIC cimgui) 46 | target_include_directories(sokol INTERFACE sokol) 47 | 48 | #=== EXECUTABLE: demo 49 | if(CMAKE_SYSTEM_NAME STREQUAL Windows) 50 | add_executable(demo WIN32 demo.c) 51 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT demo) 52 | else() 53 | add_executable(demo demo.c) 54 | endif() 55 | target_link_libraries(demo sokol) 56 | 57 | # Emscripten-specific linker options 58 | if (CMAKE_SYSTEM_NAME STREQUAL Emscripten) 59 | set(CMAKE_EXECUTABLE_SUFFIX ".html") 60 | # use our own minimal shell.html 61 | target_link_options(demo PRIVATE --shell-file ../sokol/shell.html) 62 | # link with WebGL2 63 | target_link_options(demo PRIVATE -sUSE_WEBGL2=1) 64 | # WASM+JS size optimizations 65 | target_link_options(demo PRIVATE -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc --closure=1) 66 | endif() 67 | 68 | # explicitly strip dead code 69 | if (CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_SYSTEM_NAME STREQUAL Emscripten) 70 | target_link_options(demo PRIVATE LINKER:-dead_strip) 71 | endif() 72 | 73 | # this hack removes the xxx-CMakeForceLinker.cxx dummy file 74 | set_target_properties(demo PROPERTIES LINKER_LANGUAGE C) 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cimgui-sokol-starterkit 2 | 3 | A minimal cross-platform starter-kit to write simple 4 | Dear ImGui tools for Windows, Linux and macOS in C. 5 | 6 | [WASM version](https://floooh.github.io/cimgui-sokol-starterkit/) (see below for build instructions) 7 | 8 | ## Clone: 9 | 10 | ```bash 11 | > git clone https://github.com/floooh/cimgui-sokol-starterkit 12 | > cd cimgui-sokol-starterkit 13 | ``` 14 | 15 | ## Build: 16 | 17 | ```bash 18 | > mkdir build 19 | > cd build 20 | 21 | > cmake .. 22 | > cmake --build . 23 | ``` 24 | 25 | To build a Release version on Linux and Mac: 26 | 27 | ```bash 28 | > cmake -DCMAKE_BUILD_TYPE=MinSizeRel .. 29 | > cmake --build . 30 | ``` 31 | 32 | To build a Release version on Windows with the VisualStudio toolchain: 33 | 34 | ```bash 35 | > cmake .. 36 | > cmake --build . --config MinSizeRel 37 | ``` 38 | 39 | To build in w64devkit console 40 | ``` 41 | > cmake -G "MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=make .. 42 | > cmake --build . 43 | ``` 44 | NOTE: on Linux you'll also need to install the 'usual' dev-packages needed for X11+GL development. 45 | 46 | ## Run: 47 | 48 | On Linux and macOS: 49 | ```bash 50 | > ./demo 51 | ``` 52 | 53 | On Windows with the Visual Studio toolchain the exe is in a subdirectory: 54 | ```bash 55 | > Debug\demo.exe 56 | > MinSizeRel\demo.exe 57 | ``` 58 | 59 | ## Build and Run WASM/HTML version via Emscripten (Linux, macOS) 60 | 61 | Setup the emscripten SDK as described here: 62 | 63 | https://emscripten.org/docs/getting_started/downloads.html#installation-instructions 64 | 65 | Don't forget to run ```source ./emsdk_env.sh``` after activating the SDK. 66 | 67 | And then in the ```cimgui-sokol-starterkit``` directory: 68 | 69 | ``` 70 | mkdir build 71 | cd build 72 | emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel .. 73 | cmake --build . 74 | ``` 75 | 76 | To run the compilation result in the system web browser: 77 | 78 | ``` 79 | > emrun demo.html 80 | ``` 81 | 82 | ...which should look like [this](https://floooh.github.io/cimgui-sokol-starterkit/). 83 | 84 | ## IDE Support 85 | 86 | ### Visual Studio (Windows) 87 | 88 | On Windows, cmake will automatically create a Visual Studio solution file in 89 | the build directory, which can be opened (inside the build directory) with: 90 | 91 | ```bash 92 | > cmake --open . 93 | ``` 94 | 95 | ### Xcode (macOS) 96 | 97 | Replace ```cmake ..``` with ```cmake -GXcode ..``` and open the generated 98 | Xcode project: 99 | 100 | ```bash 101 | > cmake -GXcode .. 102 | > cmake --open . 103 | ``` 104 | 105 | ### Visual Studio Code (Windows, Linux, macOS) 106 | 107 | Use the [MS C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) 108 | together with the [MS CMake Tools extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) 109 | and start Visual Studio code from the project's root directory. The CMake 110 | extension will detect the CMakeLists.txt file and take over from there. 111 | 112 | ## Notes: 113 | 114 | The repository contains snapshots of the following libraries: 115 | 116 | - [Dear ImGui](https://github.com/ocornut/imgui) 117 | - [Sokol Headers](https://github.com/floooh/sokol) (only what's needed) 118 | 119 | C bindings were generated via [dear_bindings](https://github.com/dearimgui/dear_bindings) 120 | and are located in [floooh/dcimgui](https://github.com/floooh/dcimgui) 121 | 122 | I'm not planning to do frequent updates to newer versions of those 123 | files, so the versions contained in here may be behind. Updating 124 | your own copies is trivial though, just copy the new files from 125 | the original repositories over the files contained here. 126 | 127 | I tried to keep the CMake file as simple as possible (unfortunately 128 | it's not quite as simple as I had liked, because of some cross-platform 129 | differences). 130 | 131 | All the important code is in ```demo.c```. 132 | 133 | Enjoy! 134 | -------------------------------------------------------------------------------- /sokol/sokol_glue.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #ifndef SOKOL_GLUE_INCLUDED 5 | /* 6 | sokol_glue.h -- glue helper functions for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_GLUE_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | ...optionally provide the following macros to override defaults: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 20 | SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 21 | SOKOL_API_IMPL - public function implementation prefix (default: -) 22 | 23 | If sokol_glue.h is compiled as a DLL, define the following before 24 | including the declaration or implementation: 25 | 26 | SOKOL_DLL 27 | 28 | On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 29 | or __declspec(dllimport) as needed. 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, 34 | so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be 35 | used with different window system glue libraries. 36 | 37 | PROVIDED FUNCTIONS 38 | ================== 39 | 40 | sg_environment sglue_environment(void) 41 | 42 | Returns an sg_environment struct initialized by calling sokol_app.h 43 | functions. Use this in the sg_setup() call like this: 44 | 45 | sg_setup(&(sg_desc){ 46 | .environment = sglue_environment(), 47 | ... 48 | }); 49 | 50 | sg_swapchain sglue_swapchain(void) 51 | 52 | Returns an sg_swapchain struct initialized by calling sokol_app.h 53 | functions. Use this in sg_begin_pass() for a 'swapchain pass' like 54 | this: 55 | 56 | sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); 57 | 58 | LICENSE 59 | ======= 60 | zlib/libpng license 61 | 62 | Copyright (c) 2018 Andre Weissflog 63 | 64 | This software is provided 'as-is', without any express or implied warranty. 65 | In no event will the authors be held liable for any damages arising from the 66 | use of this software. 67 | 68 | Permission is granted to anyone to use this software for any purpose, 69 | including commercial applications, and to alter it and redistribute it 70 | freely, subject to the following restrictions: 71 | 72 | 1. The origin of this software must not be misrepresented; you must not 73 | claim that you wrote the original software. If you use this software in a 74 | product, an acknowledgment in the product documentation would be 75 | appreciated but is not required. 76 | 77 | 2. Altered source versions must be plainly marked as such, and must not 78 | be misrepresented as being the original software. 79 | 80 | 3. This notice may not be removed or altered from any source 81 | distribution. 82 | */ 83 | #define SOKOL_GLUE_INCLUDED 84 | 85 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) 86 | #define SOKOL_GLUE_API_DECL SOKOL_API_DECL 87 | #endif 88 | #ifndef SOKOL_GLUE_API_DECL 89 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) 90 | #define SOKOL_GLUE_API_DECL __declspec(dllexport) 91 | #elif defined(_WIN32) && defined(SOKOL_DLL) 92 | #define SOKOL_GLUE_API_DECL __declspec(dllimport) 93 | #else 94 | #define SOKOL_GLUE_API_DECL extern 95 | #endif 96 | #endif 97 | 98 | #ifndef SOKOL_GFX_INCLUDED 99 | #error "Please include sokol_gfx.h before sokol_glue.h" 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | extern "C" { 104 | #endif 105 | 106 | SOKOL_GLUE_API_DECL sg_environment sglue_environment(void); 107 | SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void); 108 | 109 | #ifdef __cplusplus 110 | } /* extern "C" */ 111 | #endif 112 | #endif /* SOKOL_GLUE_INCLUDED */ 113 | 114 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 115 | #ifdef SOKOL_GLUE_IMPL 116 | #define SOKOL_GLUE_IMPL_INCLUDED (1) 117 | #include /* memset */ 118 | 119 | #ifndef SOKOL_APP_INCLUDED 120 | #error "Please include sokol_app.h before the sokol_glue.h implementation" 121 | #endif 122 | 123 | #ifndef SOKOL_API_IMPL 124 | #define SOKOL_API_IMPL 125 | #endif 126 | 127 | #ifndef _SOKOL_PRIVATE 128 | #if defined(__GNUC__) || defined(__clang__) 129 | #define _SOKOL_PRIVATE __attribute__((unused)) static 130 | #else 131 | #define _SOKOL_PRIVATE static 132 | #endif 133 | #endif 134 | 135 | #ifndef SOKOL_ASSERT 136 | #include 137 | #define SOKOL_ASSERT(c) assert(c) 138 | #endif 139 | #ifndef SOKOL_UNREACHABLE 140 | #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) 141 | #endif 142 | 143 | _SOKOL_PRIVATE sg_pixel_format _sglue_to_sgpixelformat(sapp_pixel_format fmt) { 144 | switch (fmt) { 145 | case SAPP_PIXELFORMAT_NONE: return SG_PIXELFORMAT_NONE; 146 | case SAPP_PIXELFORMAT_RGBA8: return SG_PIXELFORMAT_RGBA8; 147 | case SAPP_PIXELFORMAT_SRGB8A8: return SG_PIXELFORMAT_SRGB8A8; 148 | case SAPP_PIXELFORMAT_BGRA8: return SG_PIXELFORMAT_BGRA8; 149 | case SAPP_PIXELFORMAT_DEPTH_STENCIL: return SG_PIXELFORMAT_DEPTH_STENCIL; 150 | case SAPP_PIXELFORMAT_DEPTH: return SG_PIXELFORMAT_DEPTH; 151 | case SAPP_PIXELFORMAT_SBGRA8: // FIXME! 152 | default: 153 | SOKOL_UNREACHABLE; 154 | return SG_PIXELFORMAT_NONE; 155 | } 156 | } 157 | 158 | SOKOL_API_IMPL sg_environment sglue_environment(void) { 159 | sg_environment res; 160 | memset(&res, 0, sizeof(res)); 161 | const sapp_environment env = sapp_get_environment(); 162 | res.defaults.color_format = _sglue_to_sgpixelformat(env.defaults.color_format); 163 | res.defaults.depth_format = _sglue_to_sgpixelformat(env.defaults.depth_format); 164 | res.defaults.sample_count = env.defaults.sample_count; 165 | res.metal.device = env.metal.device; 166 | res.d3d11.device = env.d3d11.device; 167 | res.d3d11.device_context = env.d3d11.device_context; 168 | res.wgpu.device = env.wgpu.device; 169 | res.vulkan.physical_device = env.vulkan.physical_device; 170 | res.vulkan.device = env.vulkan.device; 171 | res.vulkan.queue = env.vulkan.queue; 172 | res.vulkan.queue_family_index = env.vulkan.queue_family_index; 173 | return res; 174 | } 175 | 176 | SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { 177 | sg_swapchain res; 178 | memset(&res, 0, sizeof(res)); 179 | const sapp_swapchain sc = sapp_get_swapchain(); 180 | res.width = sc.width; 181 | res.height = sc.height; 182 | res.sample_count = sc.sample_count; 183 | res.color_format = _sglue_to_sgpixelformat(sc.color_format); 184 | res.depth_format = _sglue_to_sgpixelformat(sc.depth_format); 185 | res.metal.current_drawable = sc.metal.current_drawable; 186 | res.metal.depth_stencil_texture = sc.metal.depth_stencil_texture; 187 | res.metal.msaa_color_texture = sc.metal.msaa_color_texture; 188 | res.d3d11.render_view = sc.d3d11.render_view; 189 | res.d3d11.resolve_view = sc.d3d11.resolve_view; 190 | res.d3d11.depth_stencil_view = sc.d3d11.depth_stencil_view; 191 | res.wgpu.render_view = sc.wgpu.render_view; 192 | res.wgpu.resolve_view = sc.wgpu.resolve_view; 193 | res.wgpu.depth_stencil_view = sc.wgpu.depth_stencil_view; 194 | res.vulkan.render_image = sc.vulkan.render_image; 195 | res.vulkan.render_view = sc.vulkan.render_view; 196 | res.vulkan.resolve_image = sc.vulkan.resolve_image; 197 | res.vulkan.resolve_view = sc.vulkan.resolve_view; 198 | res.vulkan.depth_stencil_image = sc.vulkan.depth_stencil_image; 199 | res.vulkan.depth_stencil_view = sc.vulkan.depth_stencil_view; 200 | res.vulkan.render_finished_semaphore = sc.vulkan.render_finished_semaphore; 201 | res.vulkan.present_complete_semaphore = sc.vulkan.present_complete_semaphore; 202 | res.gl.framebuffer = sc.gl.framebuffer; 203 | return res; 204 | } 205 | 206 | #endif /* SOKOL_GLUE_IMPL */ 207 | -------------------------------------------------------------------------------- /cimgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // DEAR IMGUI COMPILE-TIME OPTIONS 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export 27 | //#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import 28 | //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden 29 | 30 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. 31 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 32 | 33 | //---- Disable all of Dear ImGui or don't implement standard windows/tools. 34 | // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. 35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. 37 | //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty. 38 | 39 | //---- Don't implement some functions to reduce linkage requirements. 40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) 42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) 43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). 44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 45 | //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). 46 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 47 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 48 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 49 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 50 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 51 | //#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert. 52 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available 53 | 54 | //---- Enable Test Engine / Automation features. 55 | //#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. 56 | 57 | //---- Include imgui_user.h at the end of imgui.h as a convenience 58 | // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. 59 | //#define IMGUI_INCLUDE_IMGUI_USER_H 60 | //#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h" 61 | 62 | //---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support. 63 | //#define IMGUI_USE_BGRA_PACKED_COLOR 64 | 65 | //---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate. 66 | //#define IMGUI_USE_LEGACY_CRC32_ADLER 67 | 68 | //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 69 | //#define IMGUI_USE_WCHAR32 70 | 71 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 72 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 73 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 74 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 75 | //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined. 76 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 77 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 78 | //#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined. 79 | 80 | //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 81 | // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. 82 | //#define IMGUI_USE_STB_SPRINTF 83 | 84 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 85 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 86 | // Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience. 87 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. 88 | //#define IMGUI_ENABLE_FREETYPE 89 | 90 | //---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT) 91 | // Only works in combination with IMGUI_ENABLE_FREETYPE. 92 | // - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions. 93 | // - Both require headers to be available in the include path + program to be linked with the library code (not provided). 94 | // - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) 95 | //#define IMGUI_ENABLE_FREETYPE_PLUTOSVG 96 | //#define IMGUI_ENABLE_FREETYPE_LUNASVG 97 | 98 | //---- Use stb_truetype to build and rasterize the font atlas (default) 99 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 100 | //#define IMGUI_ENABLE_STB_TRUETYPE 101 | 102 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 103 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 104 | /* 105 | #define IM_VEC2_CLASS_EXTRA \ 106 | constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ 107 | operator MyVec2() const { return MyVec2(x,y); } 108 | 109 | #define IM_VEC4_CLASS_EXTRA \ 110 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ 111 | operator MyVec4() const { return MyVec4(x,y,z,w); } 112 | */ 113 | //---- ...Or use Dear ImGui's own very basic math operators. 114 | //#define IMGUI_DEFINE_MATH_OPERATORS 115 | 116 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 117 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 118 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 119 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 120 | //#define ImDrawIdx unsigned int 121 | 122 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 123 | //struct ImDrawList; 124 | //struct ImDrawCmd; 125 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 126 | //#define ImDrawCallback MyImDrawCallback 127 | 128 | //---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase) 129 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 130 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 131 | //#define IM_DEBUG_BREAK __debugbreak() 132 | 133 | //---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set. 134 | // (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use) 135 | //#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS 136 | 137 | //---- Debug Tools: Enable slower asserts 138 | //#define IMGUI_DEBUG_PARANOID 139 | 140 | //---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files) 141 | /* 142 | namespace ImGui 143 | { 144 | void MyFunction(const char* name, MyMatrix44* mtx); 145 | } 146 | */ 147 | -------------------------------------------------------------------------------- /sokol/sokol_log.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL) 2 | #define SOKOL_LOG_IMPL 3 | #endif 4 | #ifndef SOKOL_LOG_INCLUDED 5 | /* 6 | sokol_log.h -- common logging callback for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Example code: https://github.com/floooh/sokol-samples 11 | 12 | Do this: 13 | #define SOKOL_IMPL or 14 | #define SOKOL_LOG_IMPL 15 | before you include this file in *one* C or C++ file to create the 16 | implementation. 17 | 18 | Optionally provide the following defines when building the implementation: 19 | 20 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 21 | SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) 22 | SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) 23 | SOKOL_API_DECL - same as SOKOL_GFX_API_DECL 24 | SOKOL_API_IMPL - public function implementation prefix (default: -) 25 | 26 | Optionally define the following for verbose output: 27 | 28 | SOKOL_DEBUG - by default this is defined if NDEBUG is not defined 29 | 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_log.h provides a default logging callback for other sokol headers. 34 | 35 | To use the default log callback, just include sokol_log.h and provide 36 | a function pointer to the 'slog_func' function when setting up the 37 | sokol library: 38 | 39 | For instance with sokol_audio.h: 40 | 41 | #include "sokol_log.h" 42 | ... 43 | saudio_setup(&(saudio_desc){ .logger.func = slog_func }); 44 | 45 | Logging output goes to stderr and/or a platform specific logging subsystem 46 | (which means that in some scenarios you might see logging messages duplicated): 47 | 48 | - Windows: stderr + OutputDebugStringA() 49 | - macOS/iOS/Linux: stderr + syslog() 50 | - Emscripten: console.info()/warn()/error() 51 | - Android: __android_log_write() 52 | 53 | On Windows with sokol_app.h also note the runtime config items to make 54 | stdout/stderr output visible on the console for WinMain() applications 55 | via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, 56 | however when running in a debugger on Windows, the logging output should 57 | show up on the debug output UI panel. 58 | 59 | In debug mode, a log message might look like this: 60 | 61 | [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: 62 | SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas 63 | 64 | The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) 65 | such error messages are clickable. 66 | 67 | In release mode, logging is less verbose as to not bloat the executable with string data, but you still get 68 | enough information to identify the type and location of an error: 69 | 70 | [sspine][error][id:12][line:3472] 71 | 72 | RULES FOR WRITING YOUR OWN LOGGING FUNCTION 73 | =========================================== 74 | - must be re-entrant because it might be called from different threads 75 | - must treat **all** provided string pointers as optional (can be null) 76 | - don't store the string pointers, copy the string data instead 77 | - must not return for log level panic 78 | 79 | LICENSE 80 | ======= 81 | zlib/libpng license 82 | 83 | Copyright (c) 2023 Andre Weissflog 84 | 85 | This software is provided 'as-is', without any express or implied warranty. 86 | In no event will the authors be held liable for any damages arising from the 87 | use of this software. 88 | 89 | Permission is granted to anyone to use this software for any purpose, 90 | including commercial applications, and to alter it and redistribute it 91 | freely, subject to the following restrictions: 92 | 93 | 1. The origin of this software must not be misrepresented; you must not 94 | claim that you wrote the original software. If you use this software in a 95 | product, an acknowledgment in the product documentation would be 96 | appreciated but is not required. 97 | 98 | 2. Altered source versions must be plainly marked as such, and must not 99 | be misrepresented as being the original software. 100 | 101 | 3. This notice may not be removed or altered from any source 102 | distribution. 103 | */ 104 | #define SOKOL_LOG_INCLUDED (1) 105 | #include 106 | 107 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL) 108 | #define SOKOL_LOG_API_DECL SOKOL_API_DECL 109 | #endif 110 | #ifndef SOKOL_LOG_API_DECL 111 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL) 112 | #define SOKOL_LOG_API_DECL __declspec(dllexport) 113 | #elif defined(_WIN32) && defined(SOKOL_DLL) 114 | #define SOKOL_LOG_API_DECL __declspec(dllimport) 115 | #else 116 | #define SOKOL_LOG_API_DECL extern 117 | #endif 118 | #endif 119 | 120 | #ifdef __cplusplus 121 | extern "C" { 122 | #endif 123 | 124 | /* 125 | Plug this function into the 'logger.func' struct item when initializing any of the sokol 126 | headers. For instance for sokol_audio.h it would look like this: 127 | 128 | saudio_setup(&(saudio_desc){ 129 | .logger = { 130 | .func = slog_func 131 | } 132 | }); 133 | */ 134 | SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data); 135 | 136 | #ifdef __cplusplus 137 | } // extern "C" 138 | #endif 139 | #endif // SOKOL_LOG_INCLUDED 140 | 141 | // ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ 142 | // ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ 143 | // ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ 144 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 145 | // ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ 146 | // 147 | // >>implementation 148 | #ifdef SOKOL_LOG_IMPL 149 | #define SOKOL_LOG_IMPL_INCLUDED (1) 150 | 151 | #ifndef SOKOL_API_IMPL 152 | #define SOKOL_API_IMPL 153 | #endif 154 | #ifndef SOKOL_DEBUG 155 | #ifndef NDEBUG 156 | #define SOKOL_DEBUG 157 | #endif 158 | #endif 159 | #ifndef SOKOL_ASSERT 160 | #include 161 | #define SOKOL_ASSERT(c) assert(c) 162 | #endif 163 | 164 | #ifndef _SOKOL_PRIVATE 165 | #if defined(__GNUC__) || defined(__clang__) 166 | #define _SOKOL_PRIVATE __attribute__((unused)) static 167 | #else 168 | #define _SOKOL_PRIVATE static 169 | #endif 170 | #endif 171 | 172 | #ifndef _SOKOL_UNUSED 173 | #define _SOKOL_UNUSED(x) (void)(x) 174 | #endif 175 | 176 | // platform detection 177 | #if defined(__APPLE__) 178 | #define _SLOG_APPLE (1) 179 | #elif defined(__EMSCRIPTEN__) 180 | #define _SLOG_EMSCRIPTEN (1) 181 | #elif defined(_WIN32) 182 | #define _SLOG_WINDOWS (1) 183 | #elif defined(__ANDROID__) 184 | #define _SLOG_ANDROID (1) 185 | #elif defined(__linux__) || defined(__unix__) 186 | #define _SLOG_LINUX (1) 187 | #else 188 | #error "sokol_log.h: unknown platform" 189 | #endif 190 | 191 | #include // abort 192 | #include // fputs 193 | #include // size_t 194 | 195 | #if defined(_SLOG_EMSCRIPTEN) 196 | #include 197 | #elif defined(_SLOG_WINDOWS) 198 | #ifndef WIN32_LEAN_AND_MEAN 199 | #define WIN32_LEAN_AND_MEAN 200 | #endif 201 | #ifndef NOMINMAX 202 | #define NOMINMAX 203 | #endif 204 | #include 205 | #elif defined(_SLOG_ANDROID) 206 | #include 207 | #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) 208 | #include 209 | #endif 210 | 211 | // size of line buffer (on stack!) in bytes including terminating zero 212 | #define _SLOG_LINE_LENGTH (512) 213 | 214 | _SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { 215 | if (str) { 216 | char c; 217 | while (((c = *str++) != 0) && (dst < (end - 1))) { 218 | *dst++ = c; 219 | } 220 | } 221 | *dst = 0; 222 | return dst; 223 | } 224 | 225 | _SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { 226 | const size_t max_digits_and_null = 11; 227 | if (buf_size < max_digits_and_null) { 228 | return 0; 229 | } 230 | char* p = buf + max_digits_and_null; 231 | *--p = 0; 232 | do { 233 | *--p = '0' + (x % 10); 234 | x /= 10; 235 | } while (x != 0); 236 | return p; 237 | } 238 | 239 | #if defined(_SLOG_EMSCRIPTEN) 240 | EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { 241 | const str = UTF8ToString(c_str); 242 | switch (level) { 243 | case 0: console.error(str); break; 244 | case 1: console.error(str); break; 245 | case 2: console.warn(str); break; 246 | default: console.info(str); break; 247 | } 248 | }) 249 | #endif 250 | 251 | SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { 252 | _SOKOL_UNUSED(user_data); 253 | 254 | const char* log_level_str; 255 | switch (log_level) { 256 | case 0: log_level_str = "panic"; break; 257 | case 1: log_level_str = "error"; break; 258 | case 2: log_level_str = "warning"; break; 259 | default: log_level_str = "info"; break; 260 | } 261 | 262 | // build log output line 263 | char line_buf[_SLOG_LINE_LENGTH]; 264 | char* str = line_buf; 265 | char* end = line_buf + sizeof(line_buf); 266 | char num_buf[32]; 267 | if (tag) { 268 | str = _slog_append("[", str, end); 269 | str = _slog_append(tag, str, end); 270 | str = _slog_append("]", str, end); 271 | } 272 | str = _slog_append("[", str, end); 273 | str = _slog_append(log_level_str, str, end); 274 | str = _slog_append("]", str, end); 275 | str = _slog_append("[id:", str, end); 276 | str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); 277 | str = _slog_append("]", str, end); 278 | // if a filename is provided, build a clickable log message that's compatible with compiler error messages 279 | if (filename) { 280 | str = _slog_append(" ", str, end); 281 | #if defined(_MSC_VER) 282 | // MSVC compiler error format 283 | str = _slog_append(filename, str, end); 284 | str = _slog_append("(", str, end); 285 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 286 | str = _slog_append("): ", str, end); 287 | #else 288 | // gcc/clang compiler error format 289 | str = _slog_append(filename, str, end); 290 | str = _slog_append(":", str, end); 291 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 292 | str = _slog_append(":0: ", str, end); 293 | #endif 294 | } 295 | else { 296 | str = _slog_append("[line:", str, end); 297 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 298 | str = _slog_append("] ", str, end); 299 | } 300 | if (message) { 301 | str = _slog_append("\n\t", str, end); 302 | str = _slog_append(message, str, end); 303 | } 304 | str = _slog_append("\n\n", str, end); 305 | if (0 == log_level) { 306 | str = _slog_append("ABORTING because of [panic]\n", str, end); 307 | (void)str; 308 | } 309 | 310 | // print to stderr? 311 | #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) 312 | fputs(line_buf, stderr); 313 | #endif 314 | 315 | // platform specific logging calls 316 | #if defined(_SLOG_WINDOWS) 317 | OutputDebugStringA(line_buf); 318 | #elif defined(_SLOG_ANDROID) 319 | int prio; 320 | switch (log_level) { 321 | case 0: prio = ANDROID_LOG_FATAL; break; 322 | case 1: prio = ANDROID_LOG_ERROR; break; 323 | case 2: prio = ANDROID_LOG_WARN; break; 324 | default: prio = ANDROID_LOG_INFO; break; 325 | } 326 | __android_log_write(prio, "SOKOL", line_buf); 327 | #elif defined(_SLOG_EMSCRIPTEN) 328 | slog_js_log(log_level, line_buf); 329 | #endif 330 | if (0 == log_level) { 331 | abort(); 332 | } 333 | } 334 | #endif // SOKOL_LOG_IMPL 335 | -------------------------------------------------------------------------------- /cimgui/imstb_rectpack.h: -------------------------------------------------------------------------------- 1 | // [DEAR IMGUI] 2 | // This is a slightly modified version of stb_rect_pack.h 1.01. 3 | // Grep for [DEAR IMGUI] to find the changes. 4 | // 5 | // stb_rect_pack.h - v1.01 - public domain - rectangle packing 6 | // Sean Barrett 2014 7 | // 8 | // Useful for e.g. packing rectangular textures into an atlas. 9 | // Does not do rotation. 10 | // 11 | // Before #including, 12 | // 13 | // #define STB_RECT_PACK_IMPLEMENTATION 14 | // 15 | // in the file that you want to have the implementation. 16 | // 17 | // Not necessarily the awesomest packing method, but better than 18 | // the totally naive one in stb_truetype (which is primarily what 19 | // this is meant to replace). 20 | // 21 | // Has only had a few tests run, may have issues. 22 | // 23 | // More docs to come. 24 | // 25 | // No memory allocations; uses qsort() and assert() from stdlib. 26 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 27 | // 28 | // This library currently uses the Skyline Bottom-Left algorithm. 29 | // 30 | // Please note: better rectangle packers are welcome! Please 31 | // implement them to the same API, but with a different init 32 | // function. 33 | // 34 | // Credits 35 | // 36 | // Library 37 | // Sean Barrett 38 | // Minor features 39 | // Martins Mozeiko 40 | // github:IntellectualKitty 41 | // 42 | // Bugfixes / warning fixes 43 | // Jeremy Jaussaud 44 | // Fabian Giesen 45 | // 46 | // Version history: 47 | // 48 | // 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section 49 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles 50 | // 0.99 (2019-02-07) warning fixes 51 | // 0.11 (2017-03-03) return packing success/fail result 52 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 53 | // 0.09 (2016-08-27) fix compiler warnings 54 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 55 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 56 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 57 | // 0.05: added STBRP_ASSERT to allow replacing assert 58 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 59 | // 0.01: initial release 60 | // 61 | // LICENSE 62 | // 63 | // See end of file for license information. 64 | 65 | ////////////////////////////////////////////////////////////////////////////// 66 | // 67 | // INCLUDE SECTION 68 | // 69 | 70 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 71 | #define STB_INCLUDE_STB_RECT_PACK_H 72 | 73 | #define STB_RECT_PACK_VERSION 1 74 | 75 | #ifdef STBRP_STATIC 76 | #define STBRP_DEF static 77 | #else 78 | #define STBRP_DEF extern 79 | #endif 80 | 81 | #ifdef __cplusplus 82 | extern "C" { 83 | #endif 84 | 85 | typedef struct stbrp_context stbrp_context; 86 | typedef struct stbrp_node stbrp_node; 87 | typedef struct stbrp_rect stbrp_rect; 88 | 89 | typedef int stbrp_coord; 90 | 91 | #define STBRP__MAXVAL 0x7fffffff 92 | // Mostly for internal use, but this is the maximum supported coordinate value. 93 | 94 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 95 | // Assign packed locations to rectangles. The rectangles are of type 96 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 97 | // are 'num_rects' many of them. 98 | // 99 | // Rectangles which are successfully packed have the 'was_packed' flag 100 | // set to a non-zero value and 'x' and 'y' store the minimum location 101 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 102 | // if you imagine y increasing downwards). Rectangles which do not fit 103 | // have the 'was_packed' flag set to 0. 104 | // 105 | // You should not try to access the 'rects' array from another thread 106 | // while this function is running, as the function temporarily reorders 107 | // the array while it executes. 108 | // 109 | // To pack into another rectangle, you need to call stbrp_init_target 110 | // again. To continue packing into the same rectangle, you can call 111 | // this function again. Calling this multiple times with multiple rect 112 | // arrays will probably produce worse packing results than calling it 113 | // a single time with the full rectangle array, but the option is 114 | // available. 115 | // 116 | // The function returns 1 if all of the rectangles were successfully 117 | // packed and 0 otherwise. 118 | 119 | struct stbrp_rect 120 | { 121 | // reserved for your use: 122 | int id; 123 | 124 | // input: 125 | stbrp_coord w, h; 126 | 127 | // output: 128 | stbrp_coord x, y; 129 | int was_packed; // non-zero if valid packing 130 | 131 | }; // 16 bytes, nominally 132 | 133 | 134 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 135 | // Initialize a rectangle packer to: 136 | // pack a rectangle that is 'width' by 'height' in dimensions 137 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 138 | // 139 | // You must call this function every time you start packing into a new target. 140 | // 141 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 142 | // the following stbrp_pack_rects() call (or calls), but can be freed after 143 | // the call (or calls) finish. 144 | // 145 | // Note: to guarantee best results, either: 146 | // 1. make sure 'num_nodes' >= 'width' 147 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 148 | // 149 | // If you don't do either of the above things, widths will be quantized to multiples 150 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 151 | // 152 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 153 | // may run out of temporary storage and be unable to pack some rectangles. 154 | 155 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 156 | // Optionally call this function after init but before doing any packing to 157 | // change the handling of the out-of-temp-memory scenario, described above. 158 | // If you call init again, this will be reset to the default (false). 159 | 160 | 161 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 162 | // Optionally select which packing heuristic the library should use. Different 163 | // heuristics will produce better/worse results for different data sets. 164 | // If you call init again, this will be reset to the default. 165 | 166 | enum 167 | { 168 | STBRP_HEURISTIC_Skyline_default=0, 169 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 170 | STBRP_HEURISTIC_Skyline_BF_sortHeight 171 | }; 172 | 173 | 174 | ////////////////////////////////////////////////////////////////////////////// 175 | // 176 | // the details of the following structures don't matter to you, but they must 177 | // be visible so you can handle the memory allocations for them 178 | 179 | struct stbrp_node 180 | { 181 | stbrp_coord x,y; 182 | stbrp_node *next; 183 | }; 184 | 185 | struct stbrp_context 186 | { 187 | int width; 188 | int height; 189 | int align; 190 | int init_mode; 191 | int heuristic; 192 | int num_nodes; 193 | stbrp_node *active_head; 194 | stbrp_node *free_head; 195 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 196 | }; 197 | 198 | #ifdef __cplusplus 199 | } 200 | #endif 201 | 202 | #endif 203 | 204 | ////////////////////////////////////////////////////////////////////////////// 205 | // 206 | // IMPLEMENTATION SECTION 207 | // 208 | 209 | #ifdef STB_RECT_PACK_IMPLEMENTATION 210 | #ifndef STBRP_SORT 211 | #include 212 | #define STBRP_SORT qsort 213 | #endif 214 | 215 | #ifndef STBRP_ASSERT 216 | #include 217 | #define STBRP_ASSERT assert 218 | #endif 219 | 220 | #ifdef _MSC_VER 221 | #define STBRP__NOTUSED(v) (void)(v) 222 | #define STBRP__CDECL __cdecl 223 | #else 224 | #define STBRP__NOTUSED(v) (void)sizeof(v) 225 | #define STBRP__CDECL 226 | #endif 227 | 228 | enum 229 | { 230 | STBRP__INIT_skyline = 1 231 | }; 232 | 233 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 234 | { 235 | switch (context->init_mode) { 236 | case STBRP__INIT_skyline: 237 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 238 | context->heuristic = heuristic; 239 | break; 240 | default: 241 | STBRP_ASSERT(0); 242 | } 243 | } 244 | 245 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 246 | { 247 | if (allow_out_of_mem) 248 | // if it's ok to run out of memory, then don't bother aligning them; 249 | // this gives better packing, but may fail due to OOM (even though 250 | // the rectangles easily fit). @TODO a smarter approach would be to only 251 | // quantize once we've hit OOM, then we could get rid of this parameter. 252 | context->align = 1; 253 | else { 254 | // if it's not ok to run out of memory, then quantize the widths 255 | // so that num_nodes is always enough nodes. 256 | // 257 | // I.e. num_nodes * align >= width 258 | // align >= width / num_nodes 259 | // align = ceil(width/num_nodes) 260 | 261 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 262 | } 263 | } 264 | 265 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 266 | { 267 | int i; 268 | 269 | for (i=0; i < num_nodes-1; ++i) 270 | nodes[i].next = &nodes[i+1]; 271 | nodes[i].next = NULL; 272 | context->init_mode = STBRP__INIT_skyline; 273 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 274 | context->free_head = &nodes[0]; 275 | context->active_head = &context->extra[0]; 276 | context->width = width; 277 | context->height = height; 278 | context->num_nodes = num_nodes; 279 | stbrp_setup_allow_out_of_mem(context, 0); 280 | 281 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 282 | context->extra[0].x = 0; 283 | context->extra[0].y = 0; 284 | context->extra[0].next = &context->extra[1]; 285 | context->extra[1].x = (stbrp_coord) width; 286 | context->extra[1].y = (1<<30); 287 | context->extra[1].next = NULL; 288 | } 289 | 290 | // find minimum y position if it starts at x1 291 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 292 | { 293 | stbrp_node *node = first; 294 | int x1 = x0 + width; 295 | int min_y, visited_width, waste_area; 296 | 297 | STBRP__NOTUSED(c); 298 | 299 | STBRP_ASSERT(first->x <= x0); 300 | 301 | #if 0 302 | // skip in case we're past the node 303 | while (node->next->x <= x0) 304 | ++node; 305 | #else 306 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 307 | #endif 308 | 309 | STBRP_ASSERT(node->x <= x0); 310 | 311 | min_y = 0; 312 | waste_area = 0; 313 | visited_width = 0; 314 | while (node->x < x1) { 315 | if (node->y > min_y) { 316 | // raise min_y higher. 317 | // we've accounted for all waste up to min_y, 318 | // but we'll now add more waste for everything we've visted 319 | waste_area += visited_width * (node->y - min_y); 320 | min_y = node->y; 321 | // the first time through, visited_width might be reduced 322 | if (node->x < x0) 323 | visited_width += node->next->x - x0; 324 | else 325 | visited_width += node->next->x - node->x; 326 | } else { 327 | // add waste area 328 | int under_width = node->next->x - node->x; 329 | if (under_width + visited_width > width) 330 | under_width = width - visited_width; 331 | waste_area += under_width * (min_y - node->y); 332 | visited_width += under_width; 333 | } 334 | node = node->next; 335 | } 336 | 337 | *pwaste = waste_area; 338 | return min_y; 339 | } 340 | 341 | typedef struct 342 | { 343 | int x,y; 344 | stbrp_node **prev_link; 345 | } stbrp__findresult; 346 | 347 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 348 | { 349 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 350 | stbrp__findresult fr; 351 | stbrp_node **prev, *node, *tail, **best = NULL; 352 | 353 | // align to multiple of c->align 354 | width = (width + c->align - 1); 355 | width -= width % c->align; 356 | STBRP_ASSERT(width % c->align == 0); 357 | 358 | // if it can't possibly fit, bail immediately 359 | if (width > c->width || height > c->height) { 360 | fr.prev_link = NULL; 361 | fr.x = fr.y = 0; 362 | return fr; 363 | } 364 | 365 | node = c->active_head; 366 | prev = &c->active_head; 367 | while (node->x + width <= c->width) { 368 | int y,waste; 369 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 370 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 371 | // bottom left 372 | if (y < best_y) { 373 | best_y = y; 374 | best = prev; 375 | } 376 | } else { 377 | // best-fit 378 | if (y + height <= c->height) { 379 | // can only use it if it first vertically 380 | if (y < best_y || (y == best_y && waste < best_waste)) { 381 | best_y = y; 382 | best_waste = waste; 383 | best = prev; 384 | } 385 | } 386 | } 387 | prev = &node->next; 388 | node = node->next; 389 | } 390 | 391 | best_x = (best == NULL) ? 0 : (*best)->x; 392 | 393 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 394 | // 395 | // e.g, if fitting 396 | // 397 | // ____________________ 398 | // |____________________| 399 | // 400 | // into 401 | // 402 | // | | 403 | // | ____________| 404 | // |____________| 405 | // 406 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 407 | // 408 | // This makes BF take about 2x the time 409 | 410 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 411 | tail = c->active_head; 412 | node = c->active_head; 413 | prev = &c->active_head; 414 | // find first node that's admissible 415 | while (tail->x < width) 416 | tail = tail->next; 417 | while (tail) { 418 | int xpos = tail->x - width; 419 | int y,waste; 420 | STBRP_ASSERT(xpos >= 0); 421 | // find the left position that matches this 422 | while (node->next->x <= xpos) { 423 | prev = &node->next; 424 | node = node->next; 425 | } 426 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 427 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 428 | if (y + height <= c->height) { 429 | if (y <= best_y) { 430 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 431 | best_x = xpos; 432 | //STBRP_ASSERT(y <= best_y); [DEAR IMGUI] 433 | best_y = y; 434 | best_waste = waste; 435 | best = prev; 436 | } 437 | } 438 | } 439 | tail = tail->next; 440 | } 441 | } 442 | 443 | fr.prev_link = best; 444 | fr.x = best_x; 445 | fr.y = best_y; 446 | return fr; 447 | } 448 | 449 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 450 | { 451 | // find best position according to heuristic 452 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 453 | stbrp_node *node, *cur; 454 | 455 | // bail if: 456 | // 1. it failed 457 | // 2. the best node doesn't fit (we don't always check this) 458 | // 3. we're out of memory 459 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 460 | res.prev_link = NULL; 461 | return res; 462 | } 463 | 464 | // on success, create new node 465 | node = context->free_head; 466 | node->x = (stbrp_coord) res.x; 467 | node->y = (stbrp_coord) (res.y + height); 468 | 469 | context->free_head = node->next; 470 | 471 | // insert the new node into the right starting point, and 472 | // let 'cur' point to the remaining nodes needing to be 473 | // stiched back in 474 | 475 | cur = *res.prev_link; 476 | if (cur->x < res.x) { 477 | // preserve the existing one, so start testing with the next one 478 | stbrp_node *next = cur->next; 479 | cur->next = node; 480 | cur = next; 481 | } else { 482 | *res.prev_link = node; 483 | } 484 | 485 | // from here, traverse cur and free the nodes, until we get to one 486 | // that shouldn't be freed 487 | while (cur->next && cur->next->x <= res.x + width) { 488 | stbrp_node *next = cur->next; 489 | // move the current node to the free list 490 | cur->next = context->free_head; 491 | context->free_head = cur; 492 | cur = next; 493 | } 494 | 495 | // stitch the list back in 496 | node->next = cur; 497 | 498 | if (cur->x < res.x + width) 499 | cur->x = (stbrp_coord) (res.x + width); 500 | 501 | #ifdef _DEBUG 502 | cur = context->active_head; 503 | while (cur->x < context->width) { 504 | STBRP_ASSERT(cur->x < cur->next->x); 505 | cur = cur->next; 506 | } 507 | STBRP_ASSERT(cur->next == NULL); 508 | 509 | { 510 | int count=0; 511 | cur = context->active_head; 512 | while (cur) { 513 | cur = cur->next; 514 | ++count; 515 | } 516 | cur = context->free_head; 517 | while (cur) { 518 | cur = cur->next; 519 | ++count; 520 | } 521 | STBRP_ASSERT(count == context->num_nodes+2); 522 | } 523 | #endif 524 | 525 | return res; 526 | } 527 | 528 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b) 529 | { 530 | const stbrp_rect *p = (const stbrp_rect *) a; 531 | const stbrp_rect *q = (const stbrp_rect *) b; 532 | if (p->h > q->h) 533 | return -1; 534 | if (p->h < q->h) 535 | return 1; 536 | return (p->w > q->w) ? -1 : (p->w < q->w); 537 | } 538 | 539 | static int STBRP__CDECL rect_original_order(const void *a, const void *b) 540 | { 541 | const stbrp_rect *p = (const stbrp_rect *) a; 542 | const stbrp_rect *q = (const stbrp_rect *) b; 543 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 544 | } 545 | 546 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 547 | { 548 | int i, all_rects_packed = 1; 549 | 550 | // we use the 'was_packed' field internally to allow sorting/unsorting 551 | for (i=0; i < num_rects; ++i) { 552 | rects[i].was_packed = i; 553 | } 554 | 555 | // sort according to heuristic 556 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 557 | 558 | for (i=0; i < num_rects; ++i) { 559 | if (rects[i].w == 0 || rects[i].h == 0) { 560 | rects[i].x = rects[i].y = 0; // empty rect needs no space 561 | } else { 562 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 563 | if (fr.prev_link) { 564 | rects[i].x = (stbrp_coord) fr.x; 565 | rects[i].y = (stbrp_coord) fr.y; 566 | } else { 567 | rects[i].x = rects[i].y = STBRP__MAXVAL; 568 | } 569 | } 570 | } 571 | 572 | // unsort 573 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 574 | 575 | // set was_packed flags and all_rects_packed status 576 | for (i=0; i < num_rects; ++i) { 577 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 578 | if (!rects[i].was_packed) 579 | all_rects_packed = 0; 580 | } 581 | 582 | // return the all_rects_packed status 583 | return all_rects_packed; 584 | } 585 | #endif 586 | 587 | /* 588 | ------------------------------------------------------------------------------ 589 | This software is available under 2 licenses -- choose whichever you prefer. 590 | ------------------------------------------------------------------------------ 591 | ALTERNATIVE A - MIT License 592 | Copyright (c) 2017 Sean Barrett 593 | Permission is hereby granted, free of charge, to any person obtaining a copy of 594 | this software and associated documentation files (the "Software"), to deal in 595 | the Software without restriction, including without limitation the rights to 596 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 597 | of the Software, and to permit persons to whom the Software is furnished to do 598 | so, subject to the following conditions: 599 | The above copyright notice and this permission notice shall be included in all 600 | copies or substantial portions of the Software. 601 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 602 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 603 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 604 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 605 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 606 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 607 | SOFTWARE. 608 | ------------------------------------------------------------------------------ 609 | ALTERNATIVE B - Public Domain (www.unlicense.org) 610 | This is free and unencumbered software released into the public domain. 611 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 612 | software, either in source code form or as a compiled binary, for any purpose, 613 | commercial or non-commercial, and by any means. 614 | In jurisdictions that recognize copyright laws, the author or authors of this 615 | software dedicate any and all copyright interest in the software to the public 616 | domain. We make this dedication for the benefit of the public at large and to 617 | the detriment of our heirs and successors. We intend this dedication to be an 618 | overt act of relinquishment in perpetuity of all present and future rights to 619 | this software under copyright law. 620 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 621 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 622 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 623 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 624 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 625 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 626 | ------------------------------------------------------------------------------ 627 | */ 628 | -------------------------------------------------------------------------------- /cimgui/imstb_textedit.h: -------------------------------------------------------------------------------- 1 | // [DEAR IMGUI] 2 | // This is a slightly modified version of stb_textedit.h 1.14. 3 | // Those changes would need to be pushed into nothings/stb: 4 | // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) 5 | // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) 6 | // - Added name to struct or it may be forward declared in our code. 7 | // - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925) 8 | // Grep for [DEAR IMGUI] to find the changes. 9 | // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* 10 | 11 | // stb_textedit.h - v1.14 - public domain - Sean Barrett 12 | // Development of this library was sponsored by RAD Game Tools 13 | // 14 | // This C header file implements the guts of a multi-line text-editing 15 | // widget; you implement display, word-wrapping, and low-level string 16 | // insertion/deletion, and stb_textedit will map user inputs into 17 | // insertions & deletions, plus updates to the cursor position, 18 | // selection state, and undo state. 19 | // 20 | // It is intended for use in games and other systems that need to build 21 | // their own custom widgets and which do not have heavy text-editing 22 | // requirements (this library is not recommended for use for editing large 23 | // texts, as its performance does not scale and it has limited undo). 24 | // 25 | // Non-trivial behaviors are modelled after Windows text controls. 26 | // 27 | // 28 | // LICENSE 29 | // 30 | // See end of file for license information. 31 | // 32 | // 33 | // DEPENDENCIES 34 | // 35 | // Uses the C runtime function 'memmove', which you can override 36 | // by defining IMSTB_TEXTEDIT_memmove before the implementation. 37 | // Uses no other functions. Performs no runtime allocations. 38 | // 39 | // 40 | // VERSION HISTORY 41 | // 42 | // 1.14 (2021-07-11) page up/down, various fixes 43 | // 1.13 (2019-02-07) fix bug in undo size management 44 | // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash 45 | // 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield 46 | // 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual 47 | // 1.9 (2016-08-27) customizable move-by-word 48 | // 1.8 (2016-04-02) better keyboard handling when mouse button is down 49 | // 1.7 (2015-09-13) change y range handling in case baseline is non-0 50 | // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 51 | // 1.5 (2014-09-10) add support for secondary keys for OS X 52 | // 1.4 (2014-08-17) fix signed/unsigned warnings 53 | // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 54 | // 1.2 (2014-05-27) fix some RAD types that had crept into the new code 55 | // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 56 | // 1.0 (2012-07-26) improve documentation, initial public release 57 | // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 58 | // 0.2 (2011-11-28) fixes to undo/redo 59 | // 0.1 (2010-07-08) initial version 60 | // 61 | // ADDITIONAL CONTRIBUTORS 62 | // 63 | // Ulf Winklemann: move-by-word in 1.1 64 | // Fabian Giesen: secondary key inputs in 1.5 65 | // Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 66 | // Louis Schnellbach: page up/down in 1.14 67 | // 68 | // Bugfixes: 69 | // Scott Graham 70 | // Daniel Keller 71 | // Omar Cornut 72 | // Dan Thompson 73 | // 74 | // USAGE 75 | // 76 | // This file behaves differently depending on what symbols you define 77 | // before including it. 78 | // 79 | // 80 | // Header-file mode: 81 | // 82 | // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 83 | // it will operate in "header file" mode. In this mode, it declares a 84 | // single public symbol, STB_TexteditState, which encapsulates the current 85 | // state of a text widget (except for the string, which you will store 86 | // separately). 87 | // 88 | // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 89 | // primitive type that defines a single character (e.g. char, wchar_t, etc). 90 | // 91 | // To save space or increase undo-ability, you can optionally define the 92 | // following things that are used by the undo system: 93 | // 94 | // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 95 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 96 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 97 | // 98 | // If you don't define these, they are set to permissive types and 99 | // moderate sizes. The undo system does no memory allocations, so 100 | // it grows STB_TexteditState by the worst-case storage which is (in bytes): 101 | // 102 | // [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT 103 | // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHARCOUNT 104 | // 105 | // 106 | // Implementation mode: 107 | // 108 | // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 109 | // will compile the implementation of the text edit widget, depending 110 | // on a large number of symbols which must be defined before the include. 111 | // 112 | // The implementation is defined only as static functions. You will then 113 | // need to provide your own APIs in the same file which will access the 114 | // static functions. 115 | // 116 | // The basic concept is that you provide a "string" object which 117 | // behaves like an array of characters. stb_textedit uses indices to 118 | // refer to positions in the string, implicitly representing positions 119 | // in the displayed textedit. This is true for both plain text and 120 | // rich text; even with rich text stb_truetype interacts with your 121 | // code as if there was an array of all the displayed characters. 122 | // 123 | // Symbols that must be the same in header-file and implementation mode: 124 | // 125 | // STB_TEXTEDIT_CHARTYPE the character type 126 | // STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position 127 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 128 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 129 | // 130 | // Symbols you must define for implementation mode: 131 | // 132 | // STB_TEXTEDIT_STRING the type of object representing a string being edited, 133 | // typically this is a wrapper object with other data you need 134 | // 135 | // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 136 | // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 137 | // starting from character #n (see discussion below) 138 | // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 139 | // to the xpos of the i+1'th char for a line of characters 140 | // starting at character #n (i.e. accounts for kerning 141 | // with previous char) 142 | // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 143 | // (return type is int, -1 means not valid to insert) 144 | // (not supported if you want to use UTF-8, see below) 145 | // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 146 | // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 147 | // as manually wordwrapping for end-of-line positioning 148 | // 149 | // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 150 | // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 151 | // 152 | // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 153 | // 154 | // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 155 | // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 156 | // STB_TEXTEDIT_K_UP keyboard input to move cursor up 157 | // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 158 | // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 159 | // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 160 | // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 161 | // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 162 | // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 163 | // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 164 | // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 165 | // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 166 | // STB_TEXTEDIT_K_UNDO keyboard input to perform undo 167 | // STB_TEXTEDIT_K_REDO keyboard input to perform redo 168 | // 169 | // Optional: 170 | // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 171 | // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 172 | // required for default WORDLEFT/WORDRIGHT handlers 173 | // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 174 | // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 175 | // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 176 | // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 177 | // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 178 | // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 179 | // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 180 | // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 181 | // 182 | // To support UTF-8: 183 | // 184 | // STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character 185 | // STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character 186 | // Do NOT define STB_TEXTEDIT_KEYTOTEXT. 187 | // Instead, call stb_textedit_text() directly for text contents. 188 | // 189 | // Keyboard input must be encoded as a single integer value; e.g. a character code 190 | // and some bitflags that represent shift states. to simplify the interface, SHIFT must 191 | // be a bitflag, so we can test the shifted state of cursor movements to allow selection, 192 | // i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 193 | // 194 | // You can encode other things, such as CONTROL or ALT, in additional bits, and 195 | // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 196 | // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 197 | // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 198 | // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 199 | // API below. The control keys will only match WM_KEYDOWN events because of the 200 | // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 201 | // bit so it only decodes WM_CHAR events. 202 | // 203 | // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 204 | // row of characters assuming they start on the i'th character--the width and 205 | // the height and the number of characters consumed. This allows this library 206 | // to traverse the entire layout incrementally. You need to compute word-wrapping 207 | // here. 208 | // 209 | // Each textfield keeps its own insert mode state, which is not how normal 210 | // applications work. To keep an app-wide insert mode, update/copy the 211 | // "insert_mode" field of STB_TexteditState before/after calling API functions. 212 | // 213 | // API 214 | // 215 | // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 216 | // 217 | // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 218 | // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 219 | // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 220 | // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 221 | // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) 222 | // void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len) 223 | // 224 | // Each of these functions potentially updates the string and updates the 225 | // state. 226 | // 227 | // initialize_state: 228 | // set the textedit state to a known good default state when initially 229 | // constructing the textedit. 230 | // 231 | // click: 232 | // call this with the mouse x,y on a mouse down; it will update the cursor 233 | // and reset the selection start/end to the cursor point. the x,y must 234 | // be relative to the text widget, with (0,0) being the top left. 235 | // 236 | // drag: 237 | // call this with the mouse x,y on a mouse drag/up; it will update the 238 | // cursor and the selection end point 239 | // 240 | // cut: 241 | // call this to delete the current selection; returns true if there was 242 | // one. you should FIRST copy the current selection to the system paste buffer. 243 | // (To copy, just copy the current selection out of the string yourself.) 244 | // 245 | // paste: 246 | // call this to paste text at the current cursor point or over the current 247 | // selection if there is one. 248 | // 249 | // key: 250 | // call this for keyboard inputs sent to the textfield. you can use it 251 | // for "key down" events or for "translated" key events. if you need to 252 | // do both (as in Win32), or distinguish Unicode characters from control 253 | // inputs, set a high bit to distinguish the two; then you can define the 254 | // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 255 | // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 256 | // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to 257 | // anything other type you want before including. 258 | // if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are 259 | // transformed into text and stb_textedit_text() is automatically called. 260 | // 261 | // text: (added 2025) 262 | // call this to directly send text input the textfield, which is required 263 | // for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT() 264 | // cannot infer text length. 265 | // 266 | // 267 | // When rendering, you can read the cursor position and selection state from 268 | // the STB_TexteditState. 269 | // 270 | // 271 | // Notes: 272 | // 273 | // This is designed to be usable in IMGUI, so it allows for the possibility of 274 | // running in an IMGUI that has NOT cached the multi-line layout. For this 275 | // reason, it provides an interface that is compatible with computing the 276 | // layout incrementally--we try to make sure we make as few passes through 277 | // as possible. (For example, to locate the mouse pointer in the text, we 278 | // could define functions that return the X and Y positions of characters 279 | // and binary search Y and then X, but if we're doing dynamic layout this 280 | // will run the layout algorithm many times, so instead we manually search 281 | // forward in one pass. Similar logic applies to e.g. up-arrow and 282 | // down-arrow movement.) 283 | // 284 | // If it's run in a widget that *has* cached the layout, then this is less 285 | // efficient, but it's not horrible on modern computers. But you wouldn't 286 | // want to edit million-line files with it. 287 | 288 | 289 | //////////////////////////////////////////////////////////////////////////// 290 | //////////////////////////////////////////////////////////////////////////// 291 | //// 292 | //// Header-file mode 293 | //// 294 | //// 295 | 296 | #ifndef INCLUDE_IMSTB_TEXTEDIT_H 297 | #define INCLUDE_IMSTB_TEXTEDIT_H 298 | 299 | //////////////////////////////////////////////////////////////////////// 300 | // 301 | // STB_TexteditState 302 | // 303 | // Definition of STB_TexteditState which you should store 304 | // per-textfield; it includes cursor position, selection state, 305 | // and undo state. 306 | // 307 | 308 | #ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT 309 | #define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 310 | #endif 311 | #ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT 312 | #define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 313 | #endif 314 | #ifndef IMSTB_TEXTEDIT_CHARTYPE 315 | #define IMSTB_TEXTEDIT_CHARTYPE int 316 | #endif 317 | #ifndef IMSTB_TEXTEDIT_POSITIONTYPE 318 | #define IMSTB_TEXTEDIT_POSITIONTYPE int 319 | #endif 320 | 321 | typedef struct 322 | { 323 | // private data 324 | IMSTB_TEXTEDIT_POSITIONTYPE where; 325 | IMSTB_TEXTEDIT_POSITIONTYPE insert_length; 326 | IMSTB_TEXTEDIT_POSITIONTYPE delete_length; 327 | int char_storage; 328 | } StbUndoRecord; 329 | 330 | typedef struct 331 | { 332 | // private data 333 | StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT]; 334 | IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT]; 335 | short undo_point, redo_point; 336 | int undo_char_point, redo_char_point; 337 | } StbUndoState; 338 | 339 | typedef struct STB_TexteditState 340 | { 341 | ///////////////////// 342 | // 343 | // public data 344 | // 345 | 346 | int cursor; 347 | // position of the text cursor within the string 348 | 349 | int select_start; // selection start point 350 | int select_end; 351 | // selection start and end point in characters; if equal, no selection. 352 | // note that start may be less than or greater than end (e.g. when 353 | // dragging the mouse, start is where the initial click was, and you 354 | // can drag in either direction) 355 | 356 | unsigned char insert_mode; 357 | // each textfield keeps its own insert mode state. to keep an app-wide 358 | // insert mode, copy this value in/out of the app state 359 | 360 | int row_count_per_page; 361 | // page size in number of row. 362 | // this value MUST be set to >0 for pageup or pagedown in multilines documents. 363 | 364 | ///////////////////// 365 | // 366 | // private data 367 | // 368 | unsigned char cursor_at_end_of_line; // not implemented yet 369 | unsigned char initialized; 370 | unsigned char has_preferred_x; 371 | unsigned char single_line; 372 | unsigned char padding1, padding2, padding3; 373 | float preferred_x; // this determines where the cursor up/down tries to seek to along x 374 | StbUndoState undostate; 375 | } STB_TexteditState; 376 | 377 | 378 | //////////////////////////////////////////////////////////////////////// 379 | // 380 | // StbTexteditRow 381 | // 382 | // Result of layout query, used by stb_textedit to determine where 383 | // the text in each row is. 384 | 385 | // result of layout query 386 | typedef struct 387 | { 388 | float x0,x1; // starting x location, end x location (allows for align=right, etc) 389 | float baseline_y_delta; // position of baseline relative to previous row's baseline 390 | float ymin,ymax; // height of row above and below baseline 391 | int num_chars; 392 | } StbTexteditRow; 393 | #endif //INCLUDE_IMSTB_TEXTEDIT_H 394 | 395 | 396 | //////////////////////////////////////////////////////////////////////////// 397 | //////////////////////////////////////////////////////////////////////////// 398 | //// 399 | //// Implementation mode 400 | //// 401 | //// 402 | 403 | 404 | // implementation isn't include-guarded, since it might have indirectly 405 | // included just the "header" portion 406 | #ifdef IMSTB_TEXTEDIT_IMPLEMENTATION 407 | 408 | #ifndef IMSTB_TEXTEDIT_memmove 409 | #include 410 | #define IMSTB_TEXTEDIT_memmove memmove 411 | #endif 412 | 413 | // [DEAR IMGUI] 414 | // Functions must be implemented for UTF8 support 415 | // Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit. 416 | // There is not necessarily a '[DEAR IMGUI]' at the usage sites. 417 | #ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX 418 | #define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1) 419 | #endif 420 | #ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX 421 | #define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1) 422 | #endif 423 | 424 | ///////////////////////////////////////////////////////////////////////////// 425 | // 426 | // Mouse input handling 427 | // 428 | 429 | // traverse the layout to locate the nearest character to a display position 430 | static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line) 431 | { 432 | StbTexteditRow r; 433 | int n = STB_TEXTEDIT_STRINGLEN(str); 434 | float base_y = 0, prev_x; 435 | int i=0, k; 436 | 437 | r.x0 = r.x1 = 0; 438 | r.ymin = r.ymax = 0; 439 | r.num_chars = 0; 440 | *out_side_on_line = 0; 441 | 442 | // search rows to find one that straddles 'y' 443 | while (i < n) { 444 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 445 | if (r.num_chars <= 0) 446 | return n; 447 | 448 | if (i==0 && y < base_y + r.ymin) 449 | return 0; 450 | 451 | if (y < base_y + r.ymax) 452 | break; 453 | 454 | i += r.num_chars; 455 | base_y += r.baseline_y_delta; 456 | } 457 | 458 | // below all text, return 'after' last character 459 | if (i >= n) 460 | { 461 | *out_side_on_line = 1; 462 | return n; 463 | } 464 | 465 | // check if it's before the beginning of the line 466 | if (x < r.x0) 467 | return i; 468 | 469 | // check if it's before the end of the line 470 | if (x < r.x1) { 471 | // search characters in row for one that straddles 'x' 472 | prev_x = r.x0; 473 | for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { 474 | float w = STB_TEXTEDIT_GETWIDTH(str, i, k); 475 | if (x < prev_x+w) { 476 | *out_side_on_line = (k == 0) ? 0 : 1; 477 | if (x < prev_x+w/2) 478 | return k+i; 479 | else 480 | return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k); 481 | } 482 | prev_x += w; 483 | } 484 | // shouldn't happen, but if it does, fall through to end-of-line case 485 | } 486 | 487 | // if the last character is a newline, return that. otherwise return 'after' the last character 488 | *out_side_on_line = 1; 489 | if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) 490 | return i+r.num_chars-1; 491 | else 492 | return i+r.num_chars; 493 | } 494 | 495 | // API click: on mouse down, move the cursor to the clicked location, and reset the selection 496 | static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 497 | { 498 | // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse 499 | // goes off the top or bottom of the text 500 | int side_on_line; 501 | if( state->single_line ) 502 | { 503 | StbTexteditRow r; 504 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 505 | y = r.ymin; 506 | } 507 | 508 | state->cursor = stb_text_locate_coord(str, x, y, &side_on_line); 509 | state->select_start = state->cursor; 510 | state->select_end = state->cursor; 511 | state->has_preferred_x = 0; 512 | str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); 513 | } 514 | 515 | // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 516 | static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 517 | { 518 | int p = 0; 519 | int side_on_line; 520 | 521 | // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse 522 | // goes off the top or bottom of the text 523 | if( state->single_line ) 524 | { 525 | StbTexteditRow r; 526 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 527 | y = r.ymin; 528 | } 529 | 530 | if (state->select_start == state->select_end) 531 | state->select_start = state->cursor; 532 | 533 | p = stb_text_locate_coord(str, x, y, &side_on_line); 534 | state->cursor = state->select_end = p; 535 | str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); 536 | } 537 | 538 | ///////////////////////////////////////////////////////////////////////////// 539 | // 540 | // Keyboard input handling 541 | // 542 | 543 | // forward declarations 544 | static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); 545 | static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); 546 | static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 547 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 548 | static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 549 | 550 | typedef struct 551 | { 552 | float x,y; // position of n'th character 553 | float height; // height of line 554 | int first_char, length; // first char of row, and length 555 | int prev_first; // first char of previous row 556 | } StbFindState; 557 | 558 | // find the x/y location of a character, and remember info about the previous row in 559 | // case we get a move-up event (for page up, we'll have to rescan) 560 | static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line) 561 | { 562 | StbTexteditRow r; 563 | int prev_start = 0; 564 | int z = STB_TEXTEDIT_STRINGLEN(str); 565 | int i=0, first; 566 | 567 | if (n == z && single_line) { 568 | // special case if it's at the end (may not be needed?) 569 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 570 | find->y = 0; 571 | find->first_char = 0; 572 | find->length = z; 573 | find->height = r.ymax - r.ymin; 574 | find->x = r.x1; 575 | return; 576 | } 577 | 578 | // search rows to find the one that straddles character n 579 | find->y = 0; 580 | 581 | for(;;) { 582 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 583 | if (n < i + r.num_chars) 584 | break; 585 | if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling 586 | break; 587 | if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line 588 | break; // [DEAR IMGUI] 589 | prev_start = i; 590 | i += r.num_chars; 591 | find->y += r.baseline_y_delta; 592 | if (i == z) // [DEAR IMGUI] 593 | { 594 | r.num_chars = 0; // [DEAR IMGUI] 595 | break; // [DEAR IMGUI] 596 | } 597 | } 598 | 599 | find->first_char = first = i; 600 | find->length = r.num_chars; 601 | find->height = r.ymax - r.ymin; 602 | find->prev_first = prev_start; 603 | 604 | // now scan to find xpos 605 | find->x = r.x0; 606 | for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first) 607 | find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 608 | } 609 | 610 | #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 611 | 612 | // make the selection/cursor state valid if client altered the string 613 | static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 614 | { 615 | int n = STB_TEXTEDIT_STRINGLEN(str); 616 | if (STB_TEXT_HAS_SELECTION(state)) { 617 | if (state->select_start > n) state->select_start = n; 618 | if (state->select_end > n) state->select_end = n; 619 | // if clamping forced them to be equal, move the cursor to match 620 | if (state->select_start == state->select_end) 621 | state->cursor = state->select_start; 622 | } 623 | if (state->cursor > n) state->cursor = n; 624 | } 625 | 626 | // delete characters while updating undo 627 | static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 628 | { 629 | stb_text_makeundo_delete(str, state, where, len); 630 | STB_TEXTEDIT_DELETECHARS(str, where, len); 631 | state->has_preferred_x = 0; 632 | } 633 | 634 | // delete the section 635 | static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 636 | { 637 | stb_textedit_clamp(str, state); 638 | if (STB_TEXT_HAS_SELECTION(state)) { 639 | if (state->select_start < state->select_end) { 640 | stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 641 | state->select_end = state->cursor = state->select_start; 642 | } else { 643 | stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 644 | state->select_start = state->cursor = state->select_end; 645 | } 646 | state->has_preferred_x = 0; 647 | } 648 | } 649 | 650 | // canoncialize the selection so start <= end 651 | static void stb_textedit_sortselection(STB_TexteditState *state) 652 | { 653 | if (state->select_end < state->select_start) { 654 | int temp = state->select_end; 655 | state->select_end = state->select_start; 656 | state->select_start = temp; 657 | } 658 | } 659 | 660 | // move cursor to first character of selection 661 | static void stb_textedit_move_to_first(STB_TexteditState *state) 662 | { 663 | if (STB_TEXT_HAS_SELECTION(state)) { 664 | stb_textedit_sortselection(state); 665 | state->cursor = state->select_start; 666 | state->select_end = state->select_start; 667 | state->has_preferred_x = 0; 668 | } 669 | } 670 | 671 | // move cursor to last character of selection 672 | static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 673 | { 674 | if (STB_TEXT_HAS_SELECTION(state)) { 675 | stb_textedit_sortselection(state); 676 | stb_textedit_clamp(str, state); 677 | state->cursor = state->select_end; 678 | state->select_start = state->select_end; 679 | state->has_preferred_x = 0; 680 | } 681 | } 682 | 683 | // [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping. 684 | #ifndef STB_TEXTEDIT_MOVELINESTART 685 | static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) 686 | { 687 | if (state->single_line) 688 | return 0; 689 | while (cursor > 0) { 690 | int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor); 691 | if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE) 692 | break; 693 | cursor = prev; 694 | } 695 | return cursor; 696 | } 697 | #define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start 698 | #endif 699 | #ifndef STB_TEXTEDIT_MOVELINEEND 700 | static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) 701 | { 702 | int n = STB_TEXTEDIT_STRINGLEN(str); 703 | if (state->single_line) 704 | return n; 705 | while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE) 706 | cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor); 707 | return cursor; 708 | } 709 | #define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end 710 | #endif 711 | 712 | #ifdef STB_TEXTEDIT_IS_SPACE 713 | static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) 714 | { 715 | return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; 716 | } 717 | 718 | #ifndef STB_TEXTEDIT_MOVEWORDLEFT 719 | static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c ) 720 | { 721 | c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character 722 | while (c >= 0 && !is_word_boundary(str, c)) 723 | c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c); 724 | 725 | if( c < 0 ) 726 | c = 0; 727 | 728 | return c; 729 | } 730 | #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 731 | #endif 732 | 733 | #ifndef STB_TEXTEDIT_MOVEWORDRIGHT 734 | static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c ) 735 | { 736 | const int len = STB_TEXTEDIT_STRINGLEN(str); 737 | c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character 738 | while( c < len && !is_word_boundary( str, c ) ) 739 | c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); 740 | 741 | if( c > len ) 742 | c = len; 743 | 744 | return c; 745 | } 746 | #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 747 | #endif 748 | 749 | #endif 750 | 751 | // update selection and cursor to match each other 752 | static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 753 | { 754 | if (!STB_TEXT_HAS_SELECTION(state)) 755 | state->select_start = state->select_end = state->cursor; 756 | else 757 | state->cursor = state->select_end; 758 | } 759 | 760 | // API cut: delete selection 761 | static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 762 | { 763 | if (STB_TEXT_HAS_SELECTION(state)) { 764 | stb_textedit_delete_selection(str,state); // implicitly clamps 765 | state->has_preferred_x = 0; 766 | return 1; 767 | } 768 | return 0; 769 | } 770 | 771 | // API paste: replace existing selection with passed-in text 772 | static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len) 773 | { 774 | // if there's a selection, the paste should delete it 775 | stb_textedit_clamp(str, state); 776 | stb_textedit_delete_selection(str,state); 777 | // try to insert the characters 778 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 779 | stb_text_makeundo_insert(state, state->cursor, len); 780 | state->cursor += len; 781 | state->has_preferred_x = 0; 782 | return 1; 783 | } 784 | // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) 785 | return 0; 786 | } 787 | 788 | #ifndef STB_TEXTEDIT_KEYTYPE 789 | #define STB_TEXTEDIT_KEYTYPE int 790 | #endif 791 | 792 | // API key: process text input 793 | // [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility. 794 | static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) 795 | { 796 | // can't add newline in single-line mode 797 | if (text[0] == '\n' && state->single_line) 798 | return; 799 | 800 | if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 801 | stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 802 | STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 803 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { 804 | state->cursor += text_len; 805 | state->has_preferred_x = 0; 806 | } 807 | } else { 808 | stb_textedit_delete_selection(str, state); // implicitly clamps 809 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { 810 | stb_text_makeundo_insert(state, state->cursor, text_len); 811 | state->cursor += text_len; 812 | state->has_preferred_x = 0; 813 | } 814 | } 815 | } 816 | 817 | // API key: process a keyboard input 818 | static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) 819 | { 820 | retry: 821 | switch (key) { 822 | default: { 823 | #ifdef STB_TEXTEDIT_KEYTOTEXT 824 | // This is not suitable for UTF-8 support. 825 | int c = STB_TEXTEDIT_KEYTOTEXT(key); 826 | if (c > 0) { 827 | IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c; 828 | stb_textedit_text(str, state, &ch, 1); 829 | } 830 | #endif 831 | break; 832 | } 833 | 834 | #ifdef STB_TEXTEDIT_K_INSERT 835 | case STB_TEXTEDIT_K_INSERT: 836 | state->insert_mode = !state->insert_mode; 837 | break; 838 | #endif 839 | 840 | case STB_TEXTEDIT_K_UNDO: 841 | stb_text_undo(str, state); 842 | state->has_preferred_x = 0; 843 | break; 844 | 845 | case STB_TEXTEDIT_K_REDO: 846 | stb_text_redo(str, state); 847 | state->has_preferred_x = 0; 848 | break; 849 | 850 | case STB_TEXTEDIT_K_LEFT: 851 | // if currently there's a selection, move cursor to start of selection 852 | if (STB_TEXT_HAS_SELECTION(state)) 853 | stb_textedit_move_to_first(state); 854 | else 855 | if (state->cursor > 0) 856 | state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); 857 | state->has_preferred_x = 0; 858 | break; 859 | 860 | case STB_TEXTEDIT_K_RIGHT: 861 | // if currently there's a selection, move cursor to end of selection 862 | if (STB_TEXT_HAS_SELECTION(state)) 863 | stb_textedit_move_to_last(str, state); 864 | else 865 | state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); 866 | stb_textedit_clamp(str, state); 867 | state->has_preferred_x = 0; 868 | break; 869 | 870 | case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 871 | stb_textedit_clamp(str, state); 872 | stb_textedit_prep_selection_at_cursor(state); 873 | // move selection left 874 | if (state->select_end > 0) 875 | state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end); 876 | state->cursor = state->select_end; 877 | state->has_preferred_x = 0; 878 | break; 879 | 880 | #ifdef STB_TEXTEDIT_MOVEWORDLEFT 881 | case STB_TEXTEDIT_K_WORDLEFT: 882 | if (STB_TEXT_HAS_SELECTION(state)) 883 | stb_textedit_move_to_first(state); 884 | else { 885 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 886 | stb_textedit_clamp( str, state ); 887 | } 888 | break; 889 | 890 | case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 891 | if( !STB_TEXT_HAS_SELECTION( state ) ) 892 | stb_textedit_prep_selection_at_cursor(state); 893 | 894 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 895 | state->select_end = state->cursor; 896 | 897 | stb_textedit_clamp( str, state ); 898 | break; 899 | #endif 900 | 901 | #ifdef STB_TEXTEDIT_MOVEWORDRIGHT 902 | case STB_TEXTEDIT_K_WORDRIGHT: 903 | if (STB_TEXT_HAS_SELECTION(state)) 904 | stb_textedit_move_to_last(str, state); 905 | else { 906 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 907 | stb_textedit_clamp( str, state ); 908 | } 909 | break; 910 | 911 | case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 912 | if( !STB_TEXT_HAS_SELECTION( state ) ) 913 | stb_textedit_prep_selection_at_cursor(state); 914 | 915 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 916 | state->select_end = state->cursor; 917 | 918 | stb_textedit_clamp( str, state ); 919 | break; 920 | #endif 921 | 922 | case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 923 | stb_textedit_prep_selection_at_cursor(state); 924 | // move selection right 925 | state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end); 926 | stb_textedit_clamp(str, state); 927 | state->cursor = state->select_end; 928 | state->has_preferred_x = 0; 929 | break; 930 | 931 | case STB_TEXTEDIT_K_DOWN: 932 | case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: 933 | case STB_TEXTEDIT_K_PGDOWN: 934 | case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: { 935 | StbFindState find; 936 | StbTexteditRow row; 937 | int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 938 | int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN; 939 | int row_count = is_page ? state->row_count_per_page : 1; 940 | 941 | if (!is_page && state->single_line) { 942 | // on windows, up&down in single-line behave like left&right 943 | key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 944 | goto retry; 945 | } 946 | 947 | if (sel) 948 | stb_textedit_prep_selection_at_cursor(state); 949 | else if (STB_TEXT_HAS_SELECTION(state)) 950 | stb_textedit_move_to_last(str, state); 951 | 952 | // compute current position of cursor point 953 | stb_textedit_clamp(str, state); 954 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 955 | 956 | for (j = 0; j < row_count; ++j) { 957 | float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; 958 | int start = find.first_char + find.length; 959 | 960 | if (find.length == 0) 961 | break; 962 | 963 | // [DEAR IMGUI] 964 | // going down while being on the last line shouldn't bring us to that line end 965 | //if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) 966 | // break; 967 | 968 | // now find character position down a row 969 | state->cursor = start; 970 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 971 | x = row.x0; 972 | for (i=0; i < row.num_chars; ) { 973 | float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 974 | int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); 975 | #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE 976 | if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) 977 | break; 978 | #endif 979 | x += dx; 980 | if (x > goal_x) 981 | break; 982 | i += next - state->cursor; 983 | state->cursor = next; 984 | } 985 | stb_textedit_clamp(str, state); 986 | 987 | if (state->cursor == find.first_char + find.length) 988 | str->LastMoveDirectionLR = ImGuiDir_Left; 989 | state->has_preferred_x = 1; 990 | state->preferred_x = goal_x; 991 | 992 | if (sel) 993 | state->select_end = state->cursor; 994 | 995 | // go to next line 996 | find.first_char = find.first_char + find.length; 997 | find.length = row.num_chars; 998 | } 999 | break; 1000 | } 1001 | 1002 | case STB_TEXTEDIT_K_UP: 1003 | case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: 1004 | case STB_TEXTEDIT_K_PGUP: 1005 | case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: { 1006 | StbFindState find; 1007 | StbTexteditRow row; 1008 | int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 1009 | int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP; 1010 | int row_count = is_page ? state->row_count_per_page : 1; 1011 | 1012 | if (!is_page && state->single_line) { 1013 | // on windows, up&down become left&right 1014 | key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 1015 | goto retry; 1016 | } 1017 | 1018 | if (sel) 1019 | stb_textedit_prep_selection_at_cursor(state); 1020 | else if (STB_TEXT_HAS_SELECTION(state)) 1021 | stb_textedit_move_to_first(state); 1022 | 1023 | // compute current position of cursor point 1024 | stb_textedit_clamp(str, state); 1025 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 1026 | 1027 | for (j = 0; j < row_count; ++j) { 1028 | float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; 1029 | 1030 | // can only go up if there's a previous row 1031 | if (find.prev_first == find.first_char) 1032 | break; 1033 | 1034 | // now find character position up a row 1035 | state->cursor = find.prev_first; 1036 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 1037 | x = row.x0; 1038 | for (i=0; i < row.num_chars; ) { 1039 | float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 1040 | int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); 1041 | #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE 1042 | if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) 1043 | break; 1044 | #endif 1045 | x += dx; 1046 | if (x > goal_x) 1047 | break; 1048 | i += next - state->cursor; 1049 | state->cursor = next; 1050 | } 1051 | stb_textedit_clamp(str, state); 1052 | 1053 | if (state->cursor == find.first_char) 1054 | str->LastMoveDirectionLR = ImGuiDir_Right; 1055 | else if (state->cursor == find.prev_first) 1056 | str->LastMoveDirectionLR = ImGuiDir_Left; 1057 | state->has_preferred_x = 1; 1058 | state->preferred_x = goal_x; 1059 | 1060 | if (sel) 1061 | state->select_end = state->cursor; 1062 | 1063 | // go to previous line 1064 | // (we need to scan previous line the hard way. maybe we could expose this as a new API function?) 1065 | prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0; 1066 | while (prev_scan > 0) 1067 | { 1068 | int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan); 1069 | if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE) 1070 | break; 1071 | prev_scan = prev; 1072 | } 1073 | find.first_char = find.prev_first; 1074 | find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan); 1075 | } 1076 | break; 1077 | } 1078 | 1079 | case STB_TEXTEDIT_K_DELETE: 1080 | case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 1081 | if (STB_TEXT_HAS_SELECTION(state)) 1082 | stb_textedit_delete_selection(str, state); 1083 | else { 1084 | int n = STB_TEXTEDIT_STRINGLEN(str); 1085 | if (state->cursor < n) 1086 | stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor); 1087 | } 1088 | state->has_preferred_x = 0; 1089 | break; 1090 | 1091 | case STB_TEXTEDIT_K_BACKSPACE: 1092 | case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 1093 | if (STB_TEXT_HAS_SELECTION(state)) 1094 | stb_textedit_delete_selection(str, state); 1095 | else { 1096 | stb_textedit_clamp(str, state); 1097 | if (state->cursor > 0) { 1098 | int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); 1099 | stb_textedit_delete(str, state, prev, state->cursor - prev); 1100 | state->cursor = prev; 1101 | } 1102 | } 1103 | state->has_preferred_x = 0; 1104 | break; 1105 | 1106 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 1107 | case STB_TEXTEDIT_K_TEXTSTART2: 1108 | #endif 1109 | case STB_TEXTEDIT_K_TEXTSTART: 1110 | state->cursor = state->select_start = state->select_end = 0; 1111 | state->has_preferred_x = 0; 1112 | break; 1113 | 1114 | #ifdef STB_TEXTEDIT_K_TEXTEND2 1115 | case STB_TEXTEDIT_K_TEXTEND2: 1116 | #endif 1117 | case STB_TEXTEDIT_K_TEXTEND: 1118 | state->cursor = STB_TEXTEDIT_STRINGLEN(str); 1119 | state->select_start = state->select_end = 0; 1120 | state->has_preferred_x = 0; 1121 | break; 1122 | 1123 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 1124 | case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 1125 | #endif 1126 | case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 1127 | stb_textedit_prep_selection_at_cursor(state); 1128 | state->cursor = state->select_end = 0; 1129 | state->has_preferred_x = 0; 1130 | break; 1131 | 1132 | #ifdef STB_TEXTEDIT_K_TEXTEND2 1133 | case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 1134 | #endif 1135 | case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 1136 | stb_textedit_prep_selection_at_cursor(state); 1137 | state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 1138 | state->has_preferred_x = 0; 1139 | break; 1140 | 1141 | 1142 | #ifdef STB_TEXTEDIT_K_LINESTART2 1143 | case STB_TEXTEDIT_K_LINESTART2: 1144 | #endif 1145 | case STB_TEXTEDIT_K_LINESTART: 1146 | stb_textedit_clamp(str, state); 1147 | stb_textedit_move_to_first(state); 1148 | state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); 1149 | state->has_preferred_x = 0; 1150 | break; 1151 | 1152 | #ifdef STB_TEXTEDIT_K_LINEEND2 1153 | case STB_TEXTEDIT_K_LINEEND2: 1154 | #endif 1155 | case STB_TEXTEDIT_K_LINEEND: { 1156 | stb_textedit_clamp(str, state); 1157 | stb_textedit_move_to_last(str, state); 1158 | state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); 1159 | state->has_preferred_x = 0; 1160 | break; 1161 | } 1162 | 1163 | #ifdef STB_TEXTEDIT_K_LINESTART2 1164 | case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1165 | #endif 1166 | case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: 1167 | stb_textedit_clamp(str, state); 1168 | stb_textedit_prep_selection_at_cursor(state); 1169 | state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); 1170 | state->select_end = state->cursor; 1171 | state->has_preferred_x = 0; 1172 | break; 1173 | 1174 | #ifdef STB_TEXTEDIT_K_LINEEND2 1175 | case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1176 | #endif 1177 | case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1178 | stb_textedit_clamp(str, state); 1179 | stb_textedit_prep_selection_at_cursor(state); 1180 | state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); 1181 | state->select_end = state->cursor; 1182 | state->has_preferred_x = 0; 1183 | break; 1184 | } 1185 | } 1186 | } 1187 | 1188 | ///////////////////////////////////////////////////////////////////////////// 1189 | // 1190 | // Undo processing 1191 | // 1192 | // @OPTIMIZE: the undo/redo buffer should be circular 1193 | 1194 | static void stb_textedit_flush_redo(StbUndoState *state) 1195 | { 1196 | state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; 1197 | state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; 1198 | } 1199 | 1200 | // discard the oldest entry in the undo list 1201 | static void stb_textedit_discard_undo(StbUndoState *state) 1202 | { 1203 | if (state->undo_point > 0) { 1204 | // if the 0th undo state has characters, clean those up 1205 | if (state->undo_rec[0].char_storage >= 0) { 1206 | int n = state->undo_rec[0].insert_length, i; 1207 | // delete n characters from all other records 1208 | state->undo_char_point -= n; 1209 | IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); 1210 | for (i=0; i < state->undo_point; ++i) 1211 | if (state->undo_rec[i].char_storage >= 0) 1212 | state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it 1213 | } 1214 | --state->undo_point; 1215 | IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); 1216 | } 1217 | } 1218 | 1219 | // discard the oldest entry in the redo list--it's bad if this 1220 | // ever happens, but because undo & redo have to store the actual 1221 | // characters in different cases, the redo character buffer can 1222 | // fill up even though the undo buffer didn't 1223 | static void stb_textedit_discard_redo(StbUndoState *state) 1224 | { 1225 | int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1; 1226 | 1227 | if (state->redo_point <= k) { 1228 | // if the k'th undo state has characters, clean those up 1229 | if (state->undo_rec[k].char_storage >= 0) { 1230 | int n = state->undo_rec[k].insert_length, i; 1231 | // move the remaining redo character data to the end of the buffer 1232 | state->redo_char_point += n; 1233 | IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); 1234 | // adjust the position of all the other records to account for above memmove 1235 | for (i=state->redo_point; i < k; ++i) 1236 | if (state->undo_rec[i].char_storage >= 0) 1237 | state->undo_rec[i].char_storage += n; 1238 | } 1239 | // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' 1240 | // [DEAR IMGUI] 1241 | size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); 1242 | const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; 1243 | const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; 1244 | IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); 1245 | IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); 1246 | IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); 1247 | 1248 | // now move redo_point to point to the new one 1249 | ++state->redo_point; 1250 | } 1251 | } 1252 | 1253 | static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1254 | { 1255 | // any time we create a new undo record, we discard redo 1256 | stb_textedit_flush_redo(state); 1257 | 1258 | // if we have no free records, we have to make room, by sliding the 1259 | // existing records down 1260 | if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) 1261 | stb_textedit_discard_undo(state); 1262 | 1263 | // if the characters to store won't possibly fit in the buffer, we can't undo 1264 | if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) { 1265 | state->undo_point = 0; 1266 | state->undo_char_point = 0; 1267 | return NULL; 1268 | } 1269 | 1270 | // if we don't have enough free characters in the buffer, we have to make room 1271 | while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) 1272 | stb_textedit_discard_undo(state); 1273 | 1274 | return &state->undo_rec[state->undo_point++]; 1275 | } 1276 | 1277 | static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1278 | { 1279 | StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1280 | if (r == NULL) 1281 | return NULL; 1282 | 1283 | r->where = pos; 1284 | r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len; 1285 | r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len; 1286 | 1287 | if (insert_len == 0) { 1288 | r->char_storage = -1; 1289 | return NULL; 1290 | } else { 1291 | r->char_storage = state->undo_char_point; 1292 | state->undo_char_point += insert_len; 1293 | return &state->undo_char[r->char_storage]; 1294 | } 1295 | } 1296 | 1297 | static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1298 | { 1299 | StbUndoState *s = &state->undostate; 1300 | StbUndoRecord u, *r; 1301 | if (s->undo_point == 0) 1302 | return; 1303 | 1304 | // we need to do two things: apply the undo record, and create a redo record 1305 | u = s->undo_rec[s->undo_point-1]; 1306 | r = &s->undo_rec[s->redo_point-1]; 1307 | r->char_storage = -1; 1308 | 1309 | r->insert_length = u.delete_length; 1310 | r->delete_length = u.insert_length; 1311 | r->where = u.where; 1312 | 1313 | if (u.delete_length) { 1314 | // if the undo record says to delete characters, then the redo record will 1315 | // need to re-insert the characters that get deleted, so we need to store 1316 | // them. 1317 | 1318 | // there are three cases: 1319 | // there's enough room to store the characters 1320 | // characters stored for *redoing* don't leave room for redo 1321 | // characters stored for *undoing* don't leave room for redo 1322 | // if the last is true, we have to bail 1323 | 1324 | if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) { 1325 | // the undo records take up too much character space; there's no space to store the redo characters 1326 | r->insert_length = 0; 1327 | } else { 1328 | int i; 1329 | 1330 | // there's definitely room to store the characters eventually 1331 | while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1332 | // should never happen: 1333 | if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) 1334 | return; 1335 | // there's currently not enough room, so discard a redo record 1336 | stb_textedit_discard_redo(s); 1337 | } 1338 | r = &s->undo_rec[s->redo_point-1]; 1339 | 1340 | r->char_storage = s->redo_char_point - u.delete_length; 1341 | s->redo_char_point = s->redo_char_point - u.delete_length; 1342 | 1343 | // now save the characters 1344 | for (i=0; i < u.delete_length; ++i) 1345 | s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1346 | } 1347 | 1348 | // now we can carry out the deletion 1349 | STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1350 | } 1351 | 1352 | // check type of recorded action: 1353 | if (u.insert_length) { 1354 | // easy case: was a deletion, so we need to insert n characters 1355 | STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1356 | s->undo_char_point -= u.insert_length; 1357 | } 1358 | 1359 | state->cursor = u.where + u.insert_length; 1360 | 1361 | s->undo_point--; 1362 | s->redo_point--; 1363 | } 1364 | 1365 | static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1366 | { 1367 | StbUndoState *s = &state->undostate; 1368 | StbUndoRecord *u, r; 1369 | if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) 1370 | return; 1371 | 1372 | // we need to do two things: apply the redo record, and create an undo record 1373 | u = &s->undo_rec[s->undo_point]; 1374 | r = s->undo_rec[s->redo_point]; 1375 | 1376 | // we KNOW there must be room for the undo record, because the redo record 1377 | // was derived from an undo record 1378 | 1379 | u->delete_length = r.insert_length; 1380 | u->insert_length = r.delete_length; 1381 | u->where = r.where; 1382 | u->char_storage = -1; 1383 | 1384 | if (r.delete_length) { 1385 | // the redo record requires us to delete characters, so the undo record 1386 | // needs to store the characters 1387 | 1388 | if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1389 | u->insert_length = 0; 1390 | u->delete_length = 0; 1391 | } else { 1392 | int i; 1393 | u->char_storage = s->undo_char_point; 1394 | s->undo_char_point = s->undo_char_point + u->insert_length; 1395 | 1396 | // now save the characters 1397 | for (i=0; i < u->insert_length; ++i) 1398 | s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1399 | } 1400 | 1401 | STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1402 | } 1403 | 1404 | if (r.insert_length) { 1405 | // easy case: need to insert n characters 1406 | STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1407 | s->redo_char_point += r.insert_length; 1408 | } 1409 | 1410 | state->cursor = r.where + r.insert_length; 1411 | 1412 | s->undo_point++; 1413 | s->redo_point++; 1414 | } 1415 | 1416 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1417 | { 1418 | stb_text_createundo(&state->undostate, where, 0, length); 1419 | } 1420 | 1421 | static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1422 | { 1423 | int i; 1424 | IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1425 | if (p) { 1426 | for (i=0; i < length; ++i) 1427 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1428 | } 1429 | } 1430 | 1431 | static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1432 | { 1433 | int i; 1434 | IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1435 | if (p) { 1436 | for (i=0; i < old_length; ++i) 1437 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1438 | } 1439 | } 1440 | 1441 | // reset the state to default 1442 | static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1443 | { 1444 | state->undostate.undo_point = 0; 1445 | state->undostate.undo_char_point = 0; 1446 | state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; 1447 | state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; 1448 | state->select_end = state->select_start = 0; 1449 | state->cursor = 0; 1450 | state->has_preferred_x = 0; 1451 | state->preferred_x = 0; 1452 | state->cursor_at_end_of_line = 0; 1453 | state->initialized = 1; 1454 | state->single_line = (unsigned char) is_single_line; 1455 | state->insert_mode = 0; 1456 | state->row_count_per_page = 0; 1457 | } 1458 | 1459 | // API initialize 1460 | static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1461 | { 1462 | stb_textedit_clear_state(state, is_single_line); 1463 | } 1464 | 1465 | #if defined(__GNUC__) || defined(__clang__) 1466 | #pragma GCC diagnostic push 1467 | #pragma GCC diagnostic ignored "-Wcast-qual" 1468 | #endif 1469 | 1470 | static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len) 1471 | { 1472 | return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len); 1473 | } 1474 | 1475 | #if defined(__GNUC__) || defined(__clang__) 1476 | #pragma GCC diagnostic pop 1477 | #endif 1478 | 1479 | #endif//IMSTB_TEXTEDIT_IMPLEMENTATION 1480 | 1481 | /* 1482 | ------------------------------------------------------------------------------ 1483 | This software is available under 2 licenses -- choose whichever you prefer. 1484 | ------------------------------------------------------------------------------ 1485 | ALTERNATIVE A - MIT License 1486 | Copyright (c) 2017 Sean Barrett 1487 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1488 | this software and associated documentation files (the "Software"), to deal in 1489 | the Software without restriction, including without limitation the rights to 1490 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1491 | of the Software, and to permit persons to whom the Software is furnished to do 1492 | so, subject to the following conditions: 1493 | The above copyright notice and this permission notice shall be included in all 1494 | copies or substantial portions of the Software. 1495 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1496 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1497 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1498 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1499 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1500 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1501 | SOFTWARE. 1502 | ------------------------------------------------------------------------------ 1503 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1504 | This is free and unencumbered software released into the public domain. 1505 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1506 | software, either in source code form or as a compiled binary, for any purpose, 1507 | commercial or non-commercial, and by any means. 1508 | In jurisdictions that recognize copyright laws, the author or authors of this 1509 | software dedicate any and all copyright interest in the software to the public 1510 | domain. We make this dedication for the benefit of the public at large and to 1511 | the detriment of our heirs and successors. We intend this dedication to be an 1512 | overt act of relinquishment in perpetuity of all present and future rights to 1513 | this software under copyright law. 1514 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1515 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1516 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1517 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1518 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1519 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1520 | ------------------------------------------------------------------------------ 1521 | */ 1522 | --------------------------------------------------------------------------------