├── .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 |
--------------------------------------------------------------------------------