Compare commits

...

2 Commits

70 changed files with 43118 additions and 5176 deletions

View File

@ -1,4 +1,4 @@
# zgui v0.2.0 - dear imgui bindings # zgui v0.5.0 - dear imgui bindings
Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. [Here](https://github.com/michal-z/zig-gamedev/tree/main/samples/minimal_zgpu_zgui) is a simple sample application, and [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) is a full one. Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. [Here](https://github.com/michal-z/zig-gamedev/tree/main/samples/minimal_zgpu_zgui) is a simple sample application, and [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) is a full one.
@ -7,14 +7,18 @@ Easy to use, hand-crafted API with default arguments, named parameters and Zig s
* Most public dear imgui API exposed * Most public dear imgui API exposed
* All memory allocations go through user provided Zig allocator * All memory allocations go through user provided Zig allocator
* [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets * [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets
* [Plot API](#plot-api) for advanced data visualizations
* [Test engine API](#test-engine-api) for automatic testing * [Test engine API](#test-engine-api) for automatic testing
* [Plot API](#plot-api) for advanced data visualizations
* [Gizmo API](#gizmo-api) for gizmo
* [Node editor API](#node-editor-api) for node based stuff
## Versions ## Versions
* [ImGui](https://github.com/ocornut/imgui/tree/v1.90.4-docking) `1.90.4-docking` * [ImGui](https://github.com/ocornut/imgui/tree/v1.91.0-docking) `1.91.0-docking`
* [ImGui test engine](https://github.com/ocornut/imgui_test_engine/tree/v1.90.4) `1.90.4` * [ImGui test engine](https://github.com/ocornut/imgui_test_engine/tree/v1.91.0) `1.91.0`
* [ImPlot](https://github.com/epezent/implot) `O.17` * [ImPlot](https://github.com/epezent/implot) `O.17`
* [ImGuizmo](https://github.com/CedricGuillemet/ImGuizmo) `1.89 WIP`
* [ImGuiNodeEditor](https://github.com/thedmd/imgui-node-editor/tree/v0.9.3) `O.9.3`
## Getting started ## Getting started
@ -139,21 +143,6 @@ draw_list.addPolyline(
.{ .col = 0xff_00_aa_11, .thickness = 7 }, .{ .col = 0xff_00_aa_11, .thickness = 7 },
); );
``` ```
### Plot API
```zig
if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
zgui.plot.setupAxis(.x1, .{ .label = "xaxis" });
zgui.plot.setupAxisLimits(.x1, .{ .min = 0, .max = 5 });
zgui.plot.setupLegend(.{ .south = true, .west = true }, .{});
zgui.plot.setupFinish();
zgui.plot.plotLineValues("y data", i32, .{ .v = &.{ 0, 1, 0, 1, 0, 1 } });
zgui.plot.plotLine("xy data", f32, .{
.xv = &.{ 0.1, 0.2, 0.5, 2.5 },
.yv = &.{ 0.1, 0.3, 0.5, 0.9 },
});
zgui.plot.endPlot();
}
```
### Test Engine API ### Test Engine API
Zig wraper for [ImGUI test engine](https://github.com/ocornut/imgui_test_engine). Zig wraper for [ImGUI test engine](https://github.com/ocornut/imgui_test_engine).
@ -199,3 +188,60 @@ fn registerTests() void {
); );
} }
``` ```
### Plot API
```zig
if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
zgui.plot.setupAxis(.x1, .{ .label = "xaxis" });
zgui.plot.setupAxisLimits(.x1, .{ .min = 0, .max = 5 });
zgui.plot.setupLegend(.{ .south = true, .west = true }, .{});
zgui.plot.setupFinish();
zgui.plot.plotLineValues("y data", i32, .{ .v = &.{ 0, 1, 0, 1, 0, 1 } });
zgui.plot.plotLine("xy data", f32, .{
.xv = &.{ 0.1, 0.2, 0.5, 2.5 },
.yv = &.{ 0.1, 0.3, 0.5, 0.9 },
});
zgui.plot.endPlot();
}
```
### Gizmo API
Zig wraper for [ImGuizmo](https://github.com/CedricGuillemet/ImGuizmo).
### Node editor API
Zig wraper for [ImGuiNodeEditor](https://github.com/thedmd/imgui-node-editor).
```zig
var node_editor = zgui.node_editor.EditorContext.create(.{ .enable_smooth_zoom = true }),
zgui.node_editor.setCurrentEditor(node_editor);
defer zgui.node_editor.setCurrentEditor(null);
{
zgui.node_editor.begin("NodeEditor", .{ 0, 0 });
defer zgui.node_editor.end();
zgui.node_editor.beginNode(1);
{
defer zgui.node_editor.endNode();
zgui.textUnformatted("Node A");
zgui.node_editor.beginPin(1, .input);
{
defer zgui.node_editor.endPin();
zgui.textUnformatted("-> In");
}
zgui.sameLine(.{});
zgui.node_editor.beginPin(2, .output);
{
defer zgui.node_editor.endPin();
zgui.textUnformatted("Out ->");
}
}
}
```

View File

@ -7,6 +7,7 @@ pub const Backend = enum {
glfw_dx12, glfw_dx12,
win32_dx12, win32_dx12,
glfw, glfw,
sdl2_opengl3,
}; };
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
@ -25,6 +26,16 @@ pub fn build(b: *std.Build) void {
"with_implot", "with_implot",
"Build with bundled implot source", "Build with bundled implot source",
) orelse true, ) orelse true,
.with_gizmo = b.option(
bool,
"with_gizmo",
"Build with bundled ImGuizmo tool",
) orelse true,
.with_node_editor = b.option(
bool,
"with_node_editor",
"Build with bundled ImGui node editor",
) orelse true,
.with_te = b.option( .with_te = b.option(
bool, bool,
"with_te", "with_te",
@ -51,7 +62,7 @@ pub fn build(b: *std.Build) void {
}, },
}); });
const cflags = &.{"-fno-sanitize=undefined"}; const cflags = &.{ "-fno-sanitize=undefined", "-Wno-elaborated-enum-base" };
const imgui = if (options.shared) blk: { const imgui = if (options.shared) blk: {
const lib = b.addSharedLibrary(.{ const lib = b.addSharedLibrary(.{
@ -102,26 +113,61 @@ pub fn build(b: *std.Build) void {
.flags = cflags, .flags = cflags,
}); });
if (options.with_implot) {
imgui.defineCMacro("ZGUI_IMPLOT", "1");
imgui.addCSourceFiles(.{
.files = &.{
"libs/imgui/implot_demo.cpp",
"libs/imgui/implot.cpp",
"libs/imgui/implot_items.cpp",
},
.flags = cflags,
});
} else {
imgui.defineCMacro("ZGUI_IMPLOT", "0");
}
if (options.use_wchar32) { if (options.use_wchar32) {
imgui.defineCMacro("IMGUI_USE_WCHAR32", "1"); imgui.defineCMacro("IMGUI_USE_WCHAR32", "1");
} }
if (options.with_implot) {
imgui.addIncludePath(b.path("libs/implot"));
imgui.addCSourceFile(.{
.file = b.path("src/zplot.cpp"),
.flags = cflags,
});
imgui.addCSourceFiles(.{
.files = &.{
"libs/implot/implot_demo.cpp",
"libs/implot/implot.cpp",
"libs/implot/implot_items.cpp",
},
.flags = cflags,
});
}
if (options.with_gizmo) {
imgui.addIncludePath(b.path("libs/imguizmo/"));
imgui.addCSourceFile(.{
.file = b.path("src/zgizmo.cpp"),
.flags = cflags,
});
imgui.addCSourceFiles(.{
.files = &.{
"libs/imguizmo/ImGuizmo.cpp",
},
.flags = cflags,
});
}
if (options.with_node_editor) {
imgui.addCSourceFile(.{
.file = b.path("src/znode_editor.cpp"),
.flags = cflags,
});
imgui.addCSourceFile(.{ .file = b.path("libs/node_editor/crude_json.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/node_editor/imgui_canvas.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/node_editor/imgui_node_editor_api.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/node_editor/imgui_node_editor.cpp"), .flags = cflags });
}
if (options.with_te) { if (options.with_te) {
imgui.defineCMacro("ZGUI_TE", "1"); imgui.addCSourceFile(.{
.file = b.path("src/zte.cpp"),
.flags = cflags,
});
imgui.defineCMacro("IMGUI_ENABLE_TEST_ENGINE", null); imgui.defineCMacro("IMGUI_ENABLE_TEST_ENGINE", null);
imgui.defineCMacro("IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL", "1"); imgui.defineCMacro("IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL", "1");
@ -179,8 +225,6 @@ pub fn build(b: *std.Build) void {
imgui.linkLibrary(winpthreads); imgui.linkLibrary(winpthreads);
imgui.addSystemIncludePath(b.path("libs/winpthreads/include")); imgui.addSystemIncludePath(b.path("libs/winpthreads/include"));
} }
} else {
imgui.defineCMacro("ZGUI_TE", "0");
} }
switch (options.backend) { switch (options.backend) {
@ -246,9 +290,24 @@ pub fn build(b: *std.Build) void {
.flags = cflags, .flags = cflags,
}); });
}, },
.sdl2_opengl3 => {
imgui.addCSourceFiles(.{
.files = &.{
"libs/imgui/backends/imgui_impl_opengl3.cpp",
"libs/imgui/backends/imgui_impl_sdl2.cpp",
},
.flags = cflags,
});
},
.no_backend => {}, .no_backend => {},
} }
if (target.result.os.tag == .macos) {
const system_sdk = b.dependency("system_sdk", .{});
imgui.addSystemIncludePath(system_sdk.path("macos12/usr/include"));
imgui.addFrameworkPath(system_sdk.path("macos12/System/Library/Frameworks"));
}
const test_step = b.step("test", "Run zgui tests"); const test_step = b.step("test", "Run zgui tests");
const tests = b.addTest(.{ const tests = b.addTest(.{

View File

@ -1,6 +1,6 @@
.{ .{
.name = "zgui", .name = "zgui",
.version = "0.2.0", .version = "0.5.0",
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
@ -11,5 +11,6 @@
.dependencies = .{ .dependencies = .{
.zglfw = .{ .path = "../zglfw" }, .zglfw = .{ .path = "../zglfw" },
.zgpu = .{ .path = "../zgpu" }, .zgpu = .{ .path = "../zgpu" },
.system_sdk = .{ .path = "../system-sdk" },
}, },
} }

View File

@ -791,6 +791,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Setup backend capabilities flags // Setup backend capabilities flags
@ -847,7 +848,7 @@ void ImGui_ImplDX12_Shutdown()
void ImGui_ImplDX12_NewFrame() void ImGui_ImplDX12_NewFrame()
{ {
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplDX12_Init()?"); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX12_Init()?");
if (!bd->pPipelineState) if (!bd->pPipelineState)
ImGui_ImplDX12_CreateDeviceObjects(); ImGui_ImplDX12_CreateDeviceObjects();

View File

@ -10,9 +10,8 @@
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues: // Issues:
// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@ -25,6 +24,9 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter.
// 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw)
// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.
// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window.
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys.
// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609)
@ -93,17 +95,26 @@
#ifdef _WIN32 #ifdef _WIN32
#undef APIENTRY #undef APIENTRY
#ifndef GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WIN32 #define GLFW_EXPOSE_NATIVE_WIN32
#endif
#include <GLFW/glfw3native.h> // for glfwGetWin32Window() #include <GLFW/glfw3native.h> // for glfwGetWin32Window()
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#ifndef GLFW_EXPOSE_NATIVE_COCOA
#define GLFW_EXPOSE_NATIVE_COCOA #define GLFW_EXPOSE_NATIVE_COCOA
#endif
#include <GLFW/glfw3native.h> // for glfwGetCocoaWindow() #include <GLFW/glfw3native.h> // for glfwGetCocoaWindow()
#endif #endif
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
#include <emscripten/html5.h> #include <emscripten/html5.h>
#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3
#include <GLFW/emscripten_glfw3.h>
#else
#define EMSCRIPTEN_USE_EMBEDDED_GLFW3
#endif
#endif #endif
// We gather version tests as define in order to easily see which features are version-dependent. // We gather version tests as define in order to easily see which features are version-dependent.
@ -395,7 +406,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackScroll(window, xoffset, yoffset); bd->PrevUserCallbackScroll(window, xoffset, yoffset);
#ifdef __EMSCRIPTEN__ #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
// Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback().
return; return;
#endif #endif
@ -406,7 +417,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
{ {
#if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__) #if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)
// GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult.
// (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently)
// See https://github.com/glfw/glfw/issues/1502 for details. // See https://github.com/glfw/glfw/issues/1502 for details.
@ -417,7 +428,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr);
const char* key_name = glfwGetKeyName(key, scancode); const char* key_name = glfwGetKeyName(key, scancode);
glfwSetErrorCallback(prev_error_callback); glfwSetErrorCallback(prev_error_callback);
#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) #if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908)
(void)glfwGetError(nullptr); (void)glfwGetError(nullptr);
#endif #endif
if (key_name && key_name[0] != 0 && key_name[1] == 0) if (key_name && key_name[0] != 0 && key_name[1] == 0)
@ -525,7 +536,7 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
bd->WantUpdateMonitors = true; bd->WantUpdateMonitors = true;
} }
#ifdef __EMSCRIPTEN__ #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*)
{ {
// Mimic Emscripten_HandleWheel() in SDL. // Mimic Emscripten_HandleWheel() in SDL.
@ -599,9 +610,14 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)
bd->CallbacksChainForAllWindows = chain_for_all_windows; bd->CallbacksChainForAllWindows = chain_for_all_windows;
} }
#ifdef __EMSCRIPTEN__
EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (char const *url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
#endif
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
//printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED);
@ -627,6 +643,10 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = bd->Window; io.ClipboardUserData = bd->Window;
#ifdef __EMSCRIPTEN__
io.PlatformOpenInShellFn = [](ImGuiContext *, const char *url)
{ ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };
#endif
// Create mouse cursors // Create mouse cursors
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
@ -657,12 +677,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
if (install_callbacks) if (install_callbacks)
ImGui_ImplGlfw_InstallCallbacks(window); ImGui_ImplGlfw_InstallCallbacks(window);
// Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096)
// We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves.
// FIXME: May break chaining in case user registered their own Emscripten callback?
#ifdef __EMSCRIPTEN__
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, ImGui_ImplEmscripten_WheelCallback);
#endif
// Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784)
ImGui_ImplGlfw_UpdateMonitors(); ImGui_ImplGlfw_UpdateMonitors();
@ -717,8 +731,9 @@ void ImGui_ImplGlfw_Shutdown()
if (bd->InstalledCallbacks) if (bd->InstalledCallbacks)
ImGui_ImplGlfw_RestoreCallbacks(bd->Window); ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
#ifdef __EMSCRIPTEN__ #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr); if (bd->CanvasSelector)
emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr);
#endif #endif
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
@ -750,7 +765,7 @@ static void ImGui_ImplGlfw_UpdateMouseData()
ImGuiViewport* viewport = platform_io.Viewports[n]; ImGuiViewport* viewport = platform_io.Viewports[n];
GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle;
#ifdef __EMSCRIPTEN__ #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
const bool is_window_focused = true; const bool is_window_focused = true;
#else #else
const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0;
@ -845,7 +860,7 @@ static void ImGui_ImplGlfw_UpdateGamepads()
return; return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__) #if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)
GLFWgamepadstate gamepad; GLFWgamepadstate gamepad;
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))
return; return;
@ -935,7 +950,7 @@ void ImGui_ImplGlfw_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing) // Setup display size (every frame to accommodate for window resizing)
int w, h; int w, h;
@ -968,7 +983,7 @@ void ImGui_ImplGlfw_NewFrame()
ImGui_ImplGlfw_UpdateGamepads(); ImGui_ImplGlfw_UpdateGamepads();
} }
#ifdef __EMSCRIPTEN__ #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data)
{ {
ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;
@ -989,11 +1004,11 @@ static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, con
// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. // 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query.
// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. // STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID.
void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow *, const char *canvas_selector)
{ {
IM_ASSERT(canvas_selector != nullptr); IM_ASSERT(canvas_selector != nullptr);
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?");
bd->CanvasSelector = canvas_selector; bd->CanvasSelector = canvas_selector;
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange);
@ -1001,9 +1016,24 @@ void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_sel
// Change the size of the GLFW window according to the size of the canvas // Change the size of the GLFW window according to the size of the canvas
ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd);
}
#endif
// Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096)
// We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves.
// FIXME: May break chaining in case user registered their own Emscripten callback?
emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback);
}
#elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3)
// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call
// by invoking emscripten_glfw_make_canvas_resizable afterward.
// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation
void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow *window, const char *canvas_selector)
{
GLFWwindow *w = (GLFWwindow *)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector));
IM_ASSERT(window == w); // Sanity check
IM_UNUSED(w);
emscripten_glfw_make_canvas_resizable(window, "window", nullptr);
}
#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3
//-------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT

View File

@ -8,11 +8,10 @@
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues: // Issues:
// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@ -29,15 +28,17 @@
struct GLFWwindow; struct GLFWwindow;
struct GLFWmonitor; struct GLFWmonitor;
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// Emscripten related initialization phase methods // Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL)
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector); IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector);
//static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0
#endif #endif
// GLFW callbacks install // GLFW callbacks install

View File

@ -5,7 +5,8 @@
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// About WebGL/ES: // About WebGL/ES:
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
@ -22,6 +23,10 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748)
// 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562)
// 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447)
// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. // 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink.
// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983) // 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983)
// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445) // 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445)
@ -121,6 +126,7 @@
// Clang/GCC warnings with -Weverything // Clang/GCC warnings with -Weverything
#if defined(__clang__) #if defined(__clang__)
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: ignore unknown flags
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used #pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used
@ -181,9 +187,10 @@ extern "C" {
#endif #endif
// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have.. // Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..
// A desktop ES context can technically compile fine with our loader, so we also perform a runtime checks
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS) #define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS)
#define IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE // has glPolygonMode() #define IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // may have glPolygonMode()
#endif #endif
// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target. // Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target.
@ -234,6 +241,7 @@ struct ImGui_ImplOpenGL3_Data
unsigned int VboHandle, ElementsHandle; unsigned int VboHandle, ElementsHandle;
GLsizeiptr VertexBufferSize; GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize; GLsizeiptr IndexBufferSize;
bool HasPolygonMode;
bool HasClipOrigin; bool HasClipOrigin;
bool UseBufferSubData; bool UseBufferSubData;
@ -247,6 +255,10 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
} }
// Forward Declarations
static void ImGui_ImplOpenGL3_InitPlatformInterface();
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
struct ImGui_ImplOpenGL3_VtxAttribState struct ImGui_ImplOpenGL3_VtxAttribState
@ -275,6 +287,7 @@ struct ImGui_ImplOpenGL3_VtxAttribState
bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Initialize our loader // Initialize our loader
@ -298,16 +311,13 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
bd->GlProfileIsES2 = true; bd->GlProfileIsES2 = true;
#else #else
// Desktop or GLES 3 // Desktop or GLES 3
const char *gl_version_str = (const char *)glGetString(GL_VERSION);
GLint major = 0; GLint major = 0;
GLint minor = 0; GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor); glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0) if (major == 0 && minor == 0)
{ sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
bd->GlVersion = (GLuint)(major * 100 + minor * 10); bd->GlVersion = (GLuint)(major * 100 + minor * 10);
#if defined(GL_CONTEXT_PROFILE_MASK) #if defined(GL_CONTEXT_PROFILE_MASK)
if (bd->GlVersion >= 320) if (bd->GlVersion >= 320)
@ -317,6 +327,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
#if defined(IMGUI_IMPL_OPENGL_ES3) #if defined(IMGUI_IMPL_OPENGL_ES3)
bd->GlProfileIsES3 = true; bd->GlProfileIsES3 = true;
#else
if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0)
bd->GlProfileIsES3 = true;
#endif #endif
bd->UseBufferSubData = false; bd->UseBufferSubData = false;
@ -331,13 +344,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_DEBUG #ifdef IMGUI_IMPL_OPENGL_DEBUG
printf("GlVersion = %d\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char *)glGetString(GL_VENDOR), (const char *)glGetString(GL_RENDERER)); // [DEBUG]
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (bd->GlVersion >= 320) if (bd->GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif #endif
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// Store GLSL version string so we can refer to it later in case we recreate shaders. // Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure.
@ -363,6 +377,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
// Detect extensions we support // Detect extensions we support
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3);
#endif
bd->HasClipOrigin = (bd->GlVersion >= 450); bd->HasClipOrigin = (bd->GlVersion >= 450);
#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS #ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
GLint num_extensions = 0; GLint num_extensions = 0;
@ -375,6 +392,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
} }
#endif #endif
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplOpenGL3_InitPlatformInterface();
return true; return true;
} }
@ -384,20 +404,23 @@ void ImGui_ImplOpenGL3_Shutdown()
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_ShutdownPlatformInterface();
ImGui_ImplOpenGL3_DestroyDeviceObjects(); ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = nullptr; io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr; io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
IM_DELETE(bd); IM_DELETE(bd);
} }
void ImGui_ImplOpenGL3_NewFrame() void ImGui_ImplOpenGL3_NewFrame()
{ {
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplOpenGL3_Init()?"); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?");
if (!bd->ShaderHandle) if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects(); ImGui_ImplOpenGL3_CreateDeviceObjects();
if (!bd->FontTexture)
ImGui_ImplOpenGL3_CreateFontsTexture();
} }
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
@ -416,7 +439,8 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
if (bd->GlVersion >= 310) if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART); glDisable(GL_PRIMITIVE_RESTART);
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
if (bd->HasPolygonMode)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif #endif
@ -505,8 +529,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); GLint last_polygon_mode[2];
if (bd->HasPolygonMode)
{
glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
}
#endif #endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
@ -644,8 +672,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
// Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
if (bd->HasPolygonMode)
{
if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
{ {
glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]);
@ -655,7 +685,8 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{ {
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
} }
#endif // IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE }
#endif // IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
@ -753,7 +784,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
GLint last_pixel_unpack_buffer; GLint last_pixel_unpack_buffer = 0;
if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); }
#endif #endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
@ -948,6 +979,34 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
ImGui_ImplOpenGL3_DestroyFontsTexture(); ImGui_ImplOpenGL3_DestroyFontsTexture();
} }
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport *viewport, void *)
{
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
}
ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplOpenGL3_InitPlatformInterface()
{
ImGuiPlatformIO &platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
}
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(__GNUC__) #if defined(__GNUC__)

View File

@ -5,7 +5,8 @@
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// About WebGL/ES: // About WebGL/ES:
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
@ -44,9 +45,9 @@ extern "C" {
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
} }
// Specific OpenGL ES versions // Configuration flags to add in your imconfig file:
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten // #define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android // #define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android)
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \ #if !defined(IMGUI_IMPL_OPENGL_ES2) \

View File

@ -0,0 +1,910 @@
//-----------------------------------------------------------------------------
// About imgui_impl_opengl3_loader.h:
//
// We embed our own OpenGL loader to not require user to provide their own or to have to use ours,
// which proved to be endless problems for users.
// Our loader is custom-generated, based on gl3w but automatically filtered to only include
// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small.
//
// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY.
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
//
// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions):
// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCLUDING 'imgui_impl_opengl3_loader.h'
// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER.
// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS)
// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT.
// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
//
// Regenerate with:
// python3 gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
//
// More info:
// https://github.com/dearimgui/gl3w_stripped
// https://github.com/ocornut/imgui/issues/4445
//-----------------------------------------------------------------------------
/*
* This file was generated with gl3w_gen.py, part of imgl3w
* (hosted at https://github.com/dearimgui/gl3w_stripped)
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __gl3w_h_
#define __gl3w_h_
// Adapted from KHR/khrplatform.h to avoid including entire file.
#ifndef __khrplatform_h_
typedef float khronos_float_t;
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef signed long long int khronos_ssize_t;
#else
typedef signed long int khronos_intptr_t;
typedef signed long int khronos_ssize_t;
#endif
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
#include <stdint.h>
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#else
typedef signed long long khronos_int64_t;
typedef unsigned long long khronos_uint64_t;
#endif
#endif // __khrplatform_h_
#ifndef __gl_glcorearb_h_
#define __gl_glcorearb_h_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
** Copyright 2013-2020 The Khronos Group Inc.
** SPDX-License-Identifier: MIT
**
** This header is generated from the Khronos OpenGL / OpenGL ES XML
** API Registry. The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** https://github.com/KhronosGroup/OpenGL-Registry
*/
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#endif
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif
#ifndef GLAPI
#define GLAPI extern
#endif
/* glcorearb.h is for use with OpenGL core profile implementations.
** It should should be placed in the same directory as gl.h and
** included as <GL/glcorearb.h>.
**
** glcorearb.h includes only APIs in the latest OpenGL core profile
** implementation together with APIs in newer ARB extensions which
** can be supported by the core profile. It does not, and never will
** include functionality removed from the core profile, such as
** fixed-function vertex and fragment processing.
**
** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or
** <GL/glext.h> in the same source file.
*/
/* Generated C header for:
* API: gl
* Profile: core
* Versions considered: .*
* Versions emitted: .*
* Default extensions included: glcore
* Additional extensions included: _nomatch_^
* Extensions removed: _nomatch_^
*/
#ifndef GL_VERSION_1_0
typedef void GLvoid;
typedef unsigned int GLenum;
typedef khronos_float_t GLfloat;
typedef int GLint;
typedef int GLsizei;
typedef unsigned int GLbitfield;
typedef double GLdouble;
typedef unsigned int GLuint;
typedef unsigned char GLboolean;
typedef khronos_uint8_t GLubyte;
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_TRIANGLES 0x0004
#define GL_ONE 1
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_FRONT 0x0404
#define GL_BACK 0x0405
#define GL_FRONT_AND_BACK 0x0408
#define GL_POLYGON_MODE 0x0B40
#define GL_CULL_FACE 0x0B44
#define GL_DEPTH_TEST 0x0B71
#define GL_STENCIL_TEST 0x0B90
#define GL_VIEWPORT 0x0BA2
#define GL_BLEND 0x0BE2
#define GL_SCISSOR_BOX 0x0C10
#define GL_SCISSOR_TEST 0x0C11
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define GL_PACK_ALIGNMENT 0x0D05
#define GL_TEXTURE_2D 0x0DE1
#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_SHORT 0x1403
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_RGBA 0x1908
#define GL_FILL 0x1B02
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_LINEAR 0x2601
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);
typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLFLUSHPROC) (void);
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);
typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
GLAPI void APIENTRY glClear (GLbitfield mask);
GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
GLAPI void APIENTRY glDisable (GLenum cap);
GLAPI void APIENTRY glEnable (GLenum cap);
GLAPI void APIENTRY glFlush (void);
GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
GLAPI GLenum APIENTRY glGetError (void);
GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);
GLAPI const GLubyte *APIENTRY glGetString (GLenum name);
GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
#endif
#endif /* GL_VERSION_1_0 */
#ifndef GL_VERSION_1_1
typedef khronos_float_t GLclampf;
typedef double GLclampd;
#define GL_TEXTURE_BINDING_2D 0x8069
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
#endif
#endif /* GL_VERSION_1_1 */
#ifndef GL_VERSION_1_3
#define GL_TEXTURE0 0x84C0
#define GL_ACTIVE_TEXTURE 0x84E0
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glActiveTexture (GLenum texture);
#endif
#endif /* GL_VERSION_1_3 */
#ifndef GL_VERSION_1_4
#define GL_BLEND_DST_RGB 0x80C8
#define GL_BLEND_SRC_RGB 0x80C9
#define GL_BLEND_DST_ALPHA 0x80CA
#define GL_BLEND_SRC_ALPHA 0x80CB
#define GL_FUNC_ADD 0x8006
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
GLAPI void APIENTRY glBlendEquation (GLenum mode);
#endif
#endif /* GL_VERSION_1_4 */
#ifndef GL_VERSION_1_5
typedef khronos_ssize_t GLsizeiptr;
typedef khronos_intptr_t GLintptr;
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
#define GL_STREAM_DRAW 0x88E0
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#endif
#endif /* GL_VERSION_1_5 */
#ifndef GL_VERSION_2_0
typedef char GLchar;
typedef khronos_int16_t GLshort;
typedef khronos_int8_t GLbyte;
typedef khronos_uint16_t GLushort;
#define GL_BLEND_EQUATION_RGB 0x8009
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
#define GL_BLEND_EQUATION_ALPHA 0x883D
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_CURRENT_PROGRAM 0x8B8D
#define GL_UPPER_LEFT 0x8CA2
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);
typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glCompileShader (GLuint shader);
GLAPI GLuint APIENTRY glCreateProgram (void);
GLAPI GLuint APIENTRY glCreateShader (GLenum type);
GLAPI void APIENTRY glDeleteProgram (GLuint program);
GLAPI void APIENTRY glDeleteShader (GLuint shader);
GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);
GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);
GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);
GLAPI GLboolean APIENTRY glIsProgram (GLuint program);
GLAPI void APIENTRY glLinkProgram (GLuint program);
GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
GLAPI void APIENTRY glUseProgram (GLuint program);
GLAPI void APIENTRY glUniform1i (GLint location, GLint v0);
GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#endif
#endif /* GL_VERSION_2_0 */
#ifndef GL_VERSION_2_1
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
#endif /* GL_VERSION_2_1 */
#ifndef GL_VERSION_3_0
typedef khronos_uint16_t GLhalf;
#define GL_MAJOR_VERSION 0x821B
#define GL_MINOR_VERSION 0x821C
#define GL_NUM_EXTENSIONS 0x821D
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#define GL_VERTEX_ARRAY_BINDING 0x85B5
typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
GLAPI void APIENTRY glBindVertexArray (GLuint array);
GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);
#endif
#endif /* GL_VERSION_3_0 */
#ifndef GL_VERSION_3_1
#define GL_VERSION_3_1 1
#define GL_PRIMITIVE_RESTART 0x8F9D
#endif /* GL_VERSION_3_1 */
#ifndef GL_VERSION_3_2
#define GL_VERSION_3_2 1
typedef struct __GLsync *GLsync;
typedef khronos_uint64_t GLuint64;
typedef khronos_int64_t GLint64;
#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
#define GL_CONTEXT_PROFILE_MASK 0x9126
typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
#endif
#endif /* GL_VERSION_3_2 */
#ifndef GL_VERSION_3_3
#define GL_VERSION_3_3 1
#define GL_SAMPLER_BINDING 0x8919
typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);
#endif
#endif /* GL_VERSION_3_3 */
#ifndef GL_VERSION_4_1
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);
#endif /* GL_VERSION_4_1 */
#ifndef GL_VERSION_4_3
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_VERSION_4_3 */
#ifndef GL_VERSION_4_5
#define GL_CLIP_ORIGIN 0x935C
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
#endif /* GL_VERSION_4_5 */
#ifndef GL_ARB_bindless_texture
typedef khronos_uint64_t GLuint64EXT;
#endif /* GL_ARB_bindless_texture */
#ifndef GL_ARB_cl_event
struct _cl_context;
struct _cl_event;
#endif /* GL_ARB_cl_event */
#ifndef GL_ARB_clip_control
#define GL_ARB_clip_control 1
#endif /* GL_ARB_clip_control */
#ifndef GL_ARB_debug_output
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_ARB_debug_output */
#ifndef GL_EXT_EGL_image_storage
typedef void *GLeglImageOES;
#endif /* GL_EXT_EGL_image_storage */
#ifndef GL_EXT_direct_state_access
typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);
typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);
#endif /* GL_EXT_direct_state_access */
#ifndef GL_NV_draw_vulkan_image
typedef void (APIENTRY *GLVULKANPROCNV)(void);
#endif /* GL_NV_draw_vulkan_image */
#ifndef GL_NV_gpu_shader5
typedef khronos_int64_t GLint64EXT;
#endif /* GL_NV_gpu_shader5 */
#ifndef GL_NV_vertex_buffer_unified_memory
typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);
#endif /* GL_NV_vertex_buffer_unified_memory */
#ifdef __cplusplus
}
#endif
#endif
#ifndef GL3W_API
#define GL3W_API
#endif
#ifndef __gl_h_
#define __gl_h_
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define GL3W_OK 0
#define GL3W_ERROR_INIT -1
#define GL3W_ERROR_LIBRARY_OPEN -2
#define GL3W_ERROR_OPENGL_VERSION -3
typedef void (*GL3WglProc)(void);
typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);
/* gl3w api */
GL3W_API int imgl3wInit(void);
GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);
GL3W_API int imgl3wIsSupported(int major, int minor);
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
union ImGL3WProcs {
GL3WglProc ptr[59];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
PFNGLATTACHSHADERPROC AttachShader;
PFNGLBINDBUFFERPROC BindBuffer;
PFNGLBINDSAMPLERPROC BindSampler;
PFNGLBINDTEXTUREPROC BindTexture;
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
PFNGLBLENDEQUATIONPROC BlendEquation;
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
PFNGLBUFFERDATAPROC BufferData;
PFNGLBUFFERSUBDATAPROC BufferSubData;
PFNGLCLEARPROC Clear;
PFNGLCLEARCOLORPROC ClearColor;
PFNGLCOMPILESHADERPROC CompileShader;
PFNGLCREATEPROGRAMPROC CreateProgram;
PFNGLCREATESHADERPROC CreateShader;
PFNGLDELETEBUFFERSPROC DeleteBuffers;
PFNGLDELETEPROGRAMPROC DeleteProgram;
PFNGLDELETESHADERPROC DeleteShader;
PFNGLDELETETEXTURESPROC DeleteTextures;
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
PFNGLDETACHSHADERPROC DetachShader;
PFNGLDISABLEPROC Disable;
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
PFNGLDRAWELEMENTSPROC DrawElements;
PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex;
PFNGLENABLEPROC Enable;
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
PFNGLFLUSHPROC Flush;
PFNGLGENBUFFERSPROC GenBuffers;
PFNGLGENTEXTURESPROC GenTextures;
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
PFNGLGETERRORPROC GetError;
PFNGLGETINTEGERVPROC GetIntegerv;
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
PFNGLGETPROGRAMIVPROC GetProgramiv;
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
PFNGLGETSHADERIVPROC GetShaderiv;
PFNGLGETSTRINGPROC GetString;
PFNGLGETSTRINGIPROC GetStringi;
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv;
PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv;
PFNGLISENABLEDPROC IsEnabled;
PFNGLISPROGRAMPROC IsProgram;
PFNGLLINKPROGRAMPROC LinkProgram;
PFNGLPIXELSTOREIPROC PixelStorei;
PFNGLPOLYGONMODEPROC PolygonMode;
PFNGLREADPIXELSPROC ReadPixels;
PFNGLSCISSORPROC Scissor;
PFNGLSHADERSOURCEPROC ShaderSource;
PFNGLTEXIMAGE2DPROC TexImage2D;
PFNGLTEXPARAMETERIPROC TexParameteri;
PFNGLUNIFORM1IPROC Uniform1i;
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
PFNGLUSEPROGRAMPROC UseProgram;
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
PFNGLVIEWPORTPROC Viewport;
} gl;
};
GL3W_API extern union ImGL3WProcs imgl3wProcs;
/* OpenGL functions */
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
#define glAttachShader imgl3wProcs.gl.AttachShader
#define glBindBuffer imgl3wProcs.gl.BindBuffer
#define glBindSampler imgl3wProcs.gl.BindSampler
#define glBindTexture imgl3wProcs.gl.BindTexture
#define glBindVertexArray imgl3wProcs.gl.BindVertexArray
#define glBlendEquation imgl3wProcs.gl.BlendEquation
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
#define glBufferData imgl3wProcs.gl.BufferData
#define glBufferSubData imgl3wProcs.gl.BufferSubData
#define glClear imgl3wProcs.gl.Clear
#define glClearColor imgl3wProcs.gl.ClearColor
#define glCompileShader imgl3wProcs.gl.CompileShader
#define glCreateProgram imgl3wProcs.gl.CreateProgram
#define glCreateShader imgl3wProcs.gl.CreateShader
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
#define glDeleteProgram imgl3wProcs.gl.DeleteProgram
#define glDeleteShader imgl3wProcs.gl.DeleteShader
#define glDeleteTextures imgl3wProcs.gl.DeleteTextures
#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays
#define glDetachShader imgl3wProcs.gl.DetachShader
#define glDisable imgl3wProcs.gl.Disable
#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray
#define glDrawElements imgl3wProcs.gl.DrawElements
#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex
#define glEnable imgl3wProcs.gl.Enable
#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray
#define glFlush imgl3wProcs.gl.Flush
#define glGenBuffers imgl3wProcs.gl.GenBuffers
#define glGenTextures imgl3wProcs.gl.GenTextures
#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays
#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation
#define glGetError imgl3wProcs.gl.GetError
#define glGetIntegerv imgl3wProcs.gl.GetIntegerv
#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog
#define glGetProgramiv imgl3wProcs.gl.GetProgramiv
#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog
#define glGetShaderiv imgl3wProcs.gl.GetShaderiv
#define glGetString imgl3wProcs.gl.GetString
#define glGetStringi imgl3wProcs.gl.GetStringi
#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation
#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv
#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv
#define glIsEnabled imgl3wProcs.gl.IsEnabled
#define glIsProgram imgl3wProcs.gl.IsProgram
#define glLinkProgram imgl3wProcs.gl.LinkProgram
#define glPixelStorei imgl3wProcs.gl.PixelStorei
#define glPolygonMode imgl3wProcs.gl.PolygonMode
#define glReadPixels imgl3wProcs.gl.ReadPixels
#define glScissor imgl3wProcs.gl.Scissor
#define glShaderSource imgl3wProcs.gl.ShaderSource
#define glTexImage2D imgl3wProcs.gl.TexImage2D
#define glTexParameteri imgl3wProcs.gl.TexParameteri
#define glUniform1i imgl3wProcs.gl.Uniform1i
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
#define glUseProgram imgl3wProcs.gl.UseProgram
#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer
#define glViewport imgl3wProcs.gl.Viewport
#ifdef __cplusplus
}
#endif
#endif
#ifdef IMGL3W_IMPL
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
static HMODULE libgl;
typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);
static GL3WglGetProcAddr wgl_get_proc_address;
static int open_libgl(void)
{
libgl = LoadLibraryA("opengl32.dll");
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress");
return GL3W_OK;
}
static void close_libgl(void) { FreeLibrary(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
res = (GL3WglProc)wgl_get_proc_address(proc);
if (!res)
res = (GL3WglProc)GetProcAddress(libgl, proc);
return res;
}
#elif defined(__APPLE__)
#include <dlfcn.h>
static void *libgl;
static int open_libgl(void)
{
libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
return GL3W_OK;
}
static void close_libgl(void) { dlclose(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
*(void **)(&res) = dlsym(libgl, proc);
return res;
}
#else
#include <dlfcn.h>
static void* libgl; // OpenGL library
static void* libglx; // GLX library
static void* libegl; // EGL library
static GL3WGetProcAddressProc gl_get_proc_address;
static void close_libgl(void)
{
if (libgl) {
dlclose(libgl);
libgl = NULL;
}
if (libegl) {
dlclose(libegl);
libegl = NULL;
}
if (libglx) {
dlclose(libglx);
libglx = NULL;
}
}
static int is_library_loaded(const char* name, void** lib)
{
*lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
return *lib != NULL;
}
static int open_libs(void)
{
// On Linux we have two APIs to get process addresses: EGL and GLX.
// EGL is supported under both X11 and Wayland, whereas GLX is X11-specific.
libgl = NULL;
libegl = NULL;
libglx = NULL;
// First check what's already loaded, the windowing library might have
// already loaded either EGL or GLX and we want to use the same one.
if (is_library_loaded("libEGL.so.1", &libegl) ||
is_library_loaded("libGLX.so.0", &libglx)) {
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
if (libgl)
return GL3W_OK;
else
close_libgl();
}
if (is_library_loaded("libGL.so", &libgl))
return GL3W_OK;
if (is_library_loaded("libGL.so.1", &libgl))
return GL3W_OK;
if (is_library_loaded("libGL.so.3", &libgl))
return GL3W_OK;
// Neither is already loaded, so we have to load one. Try EGL first
// because it is supported under both X11 and Wayland.
// Load OpenGL + EGL
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
libegl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL);
if (libgl && libegl)
return GL3W_OK;
else
close_libgl();
// Fall back to legacy libGL, which includes GLX
// While most systems use libGL.so.1, NetBSD seems to use that libGL.so.3. See https://github.com/ocornut/imgui/issues/6983
libgl = dlopen("libGL.so", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
libgl = dlopen("libGL.so.3", RTLD_LAZY | RTLD_LOCAL);
if (libgl)
return GL3W_OK;
return GL3W_ERROR_LIBRARY_OPEN;
}
static int open_libgl(void)
{
int res = open_libs();
if (res)
return res;
if (libegl)
*(void**)(&gl_get_proc_address) = dlsym(libegl, "eglGetProcAddress");
else if (libglx)
*(void**)(&gl_get_proc_address) = dlsym(libglx, "glXGetProcAddressARB");
else
*(void**)(&gl_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
if (!gl_get_proc_address) {
close_libgl();
return GL3W_ERROR_LIBRARY_OPEN;
}
return GL3W_OK;
}
static GL3WglProc get_proc(const char* proc)
{
GL3WglProc res = NULL;
// Before EGL version 1.5, eglGetProcAddress doesn't support querying core
// functions and may return a dummy function if we try, so try to load the
// function from the GL library directly first.
if (libegl)
*(void**)(&res) = dlsym(libgl, proc);
if (!res)
res = gl_get_proc_address(proc);
if (!libegl && !res)
*(void**)(&res) = dlsym(libgl, proc);
return res;
}
#endif
static struct { int major, minor; } version;
static int parse_version(void)
{
if (!glGetIntegerv)
return GL3W_ERROR_INIT;
glGetIntegerv(GL_MAJOR_VERSION, &version.major);
glGetIntegerv(GL_MINOR_VERSION, &version.minor);
if (version.major == 0 && version.minor == 0)
{
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
if (const char* gl_version = (const char*)glGetString(GL_VERSION))
sscanf(gl_version, "%d.%d", &version.major, &version.minor);
}
if (version.major < 2)
return GL3W_ERROR_OPENGL_VERSION;
return GL3W_OK;
}
static void load_procs(GL3WGetProcAddressProc proc);
int imgl3wInit(void)
{
int res = open_libgl();
if (res)
return res;
atexit(close_libgl);
return imgl3wInit2(get_proc);
}
int imgl3wInit2(GL3WGetProcAddressProc proc)
{
load_procs(proc);
return parse_version();
}
int imgl3wIsSupported(int major, int minor)
{
if (major < 2)
return 0;
if (version.major == major)
return version.minor >= minor;
return version.major >= major;
}
GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }
static const char *proc_names[] = {
"glActiveTexture",
"glAttachShader",
"glBindBuffer",
"glBindSampler",
"glBindTexture",
"glBindVertexArray",
"glBlendEquation",
"glBlendEquationSeparate",
"glBlendFuncSeparate",
"glBufferData",
"glBufferSubData",
"glClear",
"glClearColor",
"glCompileShader",
"glCreateProgram",
"glCreateShader",
"glDeleteBuffers",
"glDeleteProgram",
"glDeleteShader",
"glDeleteTextures",
"glDeleteVertexArrays",
"glDetachShader",
"glDisable",
"glDisableVertexAttribArray",
"glDrawElements",
"glDrawElementsBaseVertex",
"glEnable",
"glEnableVertexAttribArray",
"glFlush",
"glGenBuffers",
"glGenTextures",
"glGenVertexArrays",
"glGetAttribLocation",
"glGetError",
"glGetIntegerv",
"glGetProgramInfoLog",
"glGetProgramiv",
"glGetShaderInfoLog",
"glGetShaderiv",
"glGetString",
"glGetStringi",
"glGetUniformLocation",
"glGetVertexAttribPointerv",
"glGetVertexAttribiv",
"glIsEnabled",
"glIsProgram",
"glLinkProgram",
"glPixelStorei",
"glPolygonMode",
"glReadPixels",
"glScissor",
"glShaderSource",
"glTexImage2D",
"glTexParameteri",
"glUniform1i",
"glUniformMatrix4fv",
"glUseProgram",
"glVertexAttribPointer",
"glViewport",
};
GL3W_API union ImGL3WProcs imgl3wProcs;
static void load_procs(GL3WGetProcAddressProc proc)
{
size_t i;
for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++)
imgl3wProcs.ptr[i] = proc(proc_names[i]);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,770 @@
// dear imgui: Platform Backend for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Prefer SDL 2.0.5+ for full feature support.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.
// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library.
// 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode().
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
// 2023-02-07: Implement IME handler (io.SetPlatformImeDataFn will call SDL_SetTextInputRect()/SDL_StartTextInput()).
// 2023-02-07: *BREAKING CHANGE* Renamed this backend file from imgui_impl_sdl.cpp/.h to imgui_impl_sdl2.cpp/.h in prevision for the future release of SDL3.
// 2023-02-02: Avoid calling SDL_SetCursor() when cursor has not changed, as the function is surprisingly costly on Mac with latest SDL (may be fixed in next SDL version).
// 2023-02-02: Added support for SDL 2.0.18+ preciseX/preciseY mouse wheel data for smooth scrolling + Scaling X value on Emscripten (bug?). (#4019, #6096)
// 2023-02-02: Removed SDL_MOUSEWHEEL value clamping, as values seem correct in latest Emscripten. (#4019)
// 2023-02-01: Flipping SDL_MOUSEWHEEL 'wheel.x' value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-26: Inputs: Disable SDL 2.0.22 new "auto capture" (SDL_HINT_MOUSE_AUTO_CAPTURE) which prevents drag and drop across windows for multi-viewport support + don't capture when drag and dropping. (#5710)
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
// 2022-03-22: Inputs: Fix mouse position issues when dragging outside of boundaries. SDL_CaptureMouse() erroneously still gives out LEAVE events when hovering OS decorations.
// 2022-03-22: Inputs: Added support for extra mouse buttons (SDL_BUTTON_X1/SDL_BUTTON_X2).
// 2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer.
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
// 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)
// 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_sdl2.h"
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#endif
// SDL
#include <SDL.h>
#include <SDL_syswm.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten/em_js.h>
#endif
#if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
#else
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
#endif
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// SDL Data
struct ImGui_ImplSDL2_Data
{
SDL_Window* Window;
SDL_Renderer* Renderer;
Uint64 Time;
char* ClipboardTextData;
// Mouse handling
Uint32 MouseWindowID;
int MouseButtonsDown;
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
SDL_Cursor* MouseLastCursor;
int MouseLastLeaveFrame;
bool MouseCanUseGlobalState;
// Gamepad handling
ImVector<SDL_GameController*> Gamepads;
ImGui_ImplSDL2_GamepadMode GamepadMode;
bool WantUpdateGamepadsList;
ImGui_ImplSDL2_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
}
// Functions
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
if (bd->ClipboardTextData)
SDL_free(bd->ClipboardTextData);
bd->ClipboardTextData = SDL_GetClipboardText();
return bd->ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow().
static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data)
{
if (data->WantVisible)
{
SDL_Rect r;
r.x = (int)data->InputPos.x;
r.y = (int)data->InputPos.y;
r.w = 1;
r.h = (int)data->InputLineHeight;
SDL_SetTextInputRect(&r);
}
}
static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
{
IM_UNUSED(scancode);
switch (keycode)
{
case SDLK_TAB: return ImGuiKey_Tab;
case SDLK_LEFT: return ImGuiKey_LeftArrow;
case SDLK_RIGHT: return ImGuiKey_RightArrow;
case SDLK_UP: return ImGuiKey_UpArrow;
case SDLK_DOWN: return ImGuiKey_DownArrow;
case SDLK_PAGEUP: return ImGuiKey_PageUp;
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
case SDLK_HOME: return ImGuiKey_Home;
case SDLK_END: return ImGuiKey_End;
case SDLK_INSERT: return ImGuiKey_Insert;
case SDLK_DELETE: return ImGuiKey_Delete;
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
case SDLK_SPACE: return ImGuiKey_Space;
case SDLK_RETURN: return ImGuiKey_Enter;
case SDLK_ESCAPE: return ImGuiKey_Escape;
case SDLK_QUOTE: return ImGuiKey_Apostrophe;
case SDLK_COMMA: return ImGuiKey_Comma;
case SDLK_MINUS: return ImGuiKey_Minus;
case SDLK_PERIOD: return ImGuiKey_Period;
case SDLK_SLASH: return ImGuiKey_Slash;
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
case SDLK_EQUALS: return ImGuiKey_Equal;
case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
case SDLK_BACKSLASH: return ImGuiKey_Backslash;
case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
case SDLK_PAUSE: return ImGuiKey_Pause;
case SDLK_KP_0: return ImGuiKey_Keypad0;
case SDLK_KP_1: return ImGuiKey_Keypad1;
case SDLK_KP_2: return ImGuiKey_Keypad2;
case SDLK_KP_3: return ImGuiKey_Keypad3;
case SDLK_KP_4: return ImGuiKey_Keypad4;
case SDLK_KP_5: return ImGuiKey_Keypad5;
case SDLK_KP_6: return ImGuiKey_Keypad6;
case SDLK_KP_7: return ImGuiKey_Keypad7;
case SDLK_KP_8: return ImGuiKey_Keypad8;
case SDLK_KP_9: return ImGuiKey_Keypad9;
case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
case SDLK_LALT: return ImGuiKey_LeftAlt;
case SDLK_LGUI: return ImGuiKey_LeftSuper;
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
case SDLK_RSHIFT: return ImGuiKey_RightShift;
case SDLK_RALT: return ImGuiKey_RightAlt;
case SDLK_RGUI: return ImGuiKey_RightSuper;
case SDLK_APPLICATION: return ImGuiKey_Menu;
case SDLK_0: return ImGuiKey_0;
case SDLK_1: return ImGuiKey_1;
case SDLK_2: return ImGuiKey_2;
case SDLK_3: return ImGuiKey_3;
case SDLK_4: return ImGuiKey_4;
case SDLK_5: return ImGuiKey_5;
case SDLK_6: return ImGuiKey_6;
case SDLK_7: return ImGuiKey_7;
case SDLK_8: return ImGuiKey_8;
case SDLK_9: return ImGuiKey_9;
case SDLK_a: return ImGuiKey_A;
case SDLK_b: return ImGuiKey_B;
case SDLK_c: return ImGuiKey_C;
case SDLK_d: return ImGuiKey_D;
case SDLK_e: return ImGuiKey_E;
case SDLK_f: return ImGuiKey_F;
case SDLK_g: return ImGuiKey_G;
case SDLK_h: return ImGuiKey_H;
case SDLK_i: return ImGuiKey_I;
case SDLK_j: return ImGuiKey_J;
case SDLK_k: return ImGuiKey_K;
case SDLK_l: return ImGuiKey_L;
case SDLK_m: return ImGuiKey_M;
case SDLK_n: return ImGuiKey_N;
case SDLK_o: return ImGuiKey_O;
case SDLK_p: return ImGuiKey_P;
case SDLK_q: return ImGuiKey_Q;
case SDLK_r: return ImGuiKey_R;
case SDLK_s: return ImGuiKey_S;
case SDLK_t: return ImGuiKey_T;
case SDLK_u: return ImGuiKey_U;
case SDLK_v: return ImGuiKey_V;
case SDLK_w: return ImGuiKey_W;
case SDLK_x: return ImGuiKey_X;
case SDLK_y: return ImGuiKey_Y;
case SDLK_z: return ImGuiKey_Z;
case SDLK_F1: return ImGuiKey_F1;
case SDLK_F2: return ImGuiKey_F2;
case SDLK_F3: return ImGuiKey_F3;
case SDLK_F4: return ImGuiKey_F4;
case SDLK_F5: return ImGuiKey_F5;
case SDLK_F6: return ImGuiKey_F6;
case SDLK_F7: return ImGuiKey_F7;
case SDLK_F8: return ImGuiKey_F8;
case SDLK_F9: return ImGuiKey_F9;
case SDLK_F10: return ImGuiKey_F10;
case SDLK_F11: return ImGuiKey_F11;
case SDLK_F12: return ImGuiKey_F12;
case SDLK_F13: return ImGuiKey_F13;
case SDLK_F14: return ImGuiKey_F14;
case SDLK_F15: return ImGuiKey_F15;
case SDLK_F16: return ImGuiKey_F16;
case SDLK_F17: return ImGuiKey_F17;
case SDLK_F18: return ImGuiKey_F18;
case SDLK_F19: return ImGuiKey_F19;
case SDLK_F20: return ImGuiKey_F20;
case SDLK_F21: return ImGuiKey_F21;
case SDLK_F22: return ImGuiKey_F22;
case SDLK_F23: return ImGuiKey_F23;
case SDLK_F24: return ImGuiKey_F24;
case SDLK_AC_BACK: return ImGuiKey_AppBack;
case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
default: break;
}
return ImGuiKey_None;
}
static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
{
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?");
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEMOTION:
{
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
return true;
}
case SDL_MOUSEWHEEL:
{
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
#if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
float wheel_x = -event->wheel.preciseX;
float wheel_y = event->wheel.preciseY;
#else
float wheel_x = -(float)event->wheel.x;
float wheel_y = (float)event->wheel.y;
#endif
#ifdef __EMSCRIPTEN__
wheel_x /= 100.0f;
#endif
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
io.AddMouseWheelEvent(wheel_x, wheel_y);
return true;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
int mouse_button = -1;
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
if (mouse_button == -1)
break;
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));
bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
return true;
}
case SDL_WINDOWEVENT:
{
// - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
// - However we won't get a correct LEAVE event for a captured window.
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
Uint8 window_event = event->window.event;
if (window_event == SDL_WINDOWEVENT_ENTER)
{
bd->MouseWindowID = event->window.windowID;
bd->MouseLastLeaveFrame = 0;
}
if (window_event == SDL_WINDOWEVENT_LEAVE)
bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1;
if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
io.AddFocusEvent(true);
else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
io.AddFocusEvent(false);
return true;
}
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
{
bd->WantUpdateGamepadsList = true;
return true;
}
}
return false;
}
#ifdef __EMSCRIPTEN__
EM_JS(void, ImGui_ImplSDL2_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
#endif
static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
{
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
// Check and store if we are on a SDL backend that supports global mouse position
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
bool mouse_can_use_global_state = false;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
const char* sdl_backend = SDL_GetCurrentVideoDriver();
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
mouse_can_use_global_state = true;
#endif
// Setup backend capabilities flags
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_sdl2";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window;
bd->Renderer = renderer;
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = nullptr;
io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData;
#ifdef __EMSCRIPTEN__
io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; };
#endif
// Gamepad handling
bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst;
bd->WantUpdateGamepadsList = true;
// Load mouse cursors
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
// Set platform dependent data in viewport
// Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = (void*)window;
main_viewport->PlatformHandleRaw = nullptr;
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (SDL_GetWindowWMInfo(window, &info))
{
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
main_viewport->PlatformHandleRaw = (void*)info.info.win.window;
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
#endif
}
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
// you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
#endif
// From 2.0.18: Enable native IME.
// IMPORTANT: This is used at the time of SDL_CreateWindow() so this will only affects secondary windows, if any.
// For the main window to be affected, your application needs to call this manually before calling SDL_CreateWindow().
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
#endif
(void)sdl_gl_context; // Unused in 'master' branch.
return true;
}
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
{
return ImGui_ImplSDL2_Init(window, nullptr, sdl_gl_context);
}
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
{
#if !SDL_HAS_VULKAN
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
}
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
{
#if !defined(_WIN32)
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
}
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
}
bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
{
return ImGui_ImplSDL2_Init(window, renderer, nullptr);
}
bool ImGui_ImplSDL2_InitForOther(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
}
static void ImGui_ImplSDL2_CloseGamepads();
void ImGui_ImplSDL2_Shutdown()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
if (bd->ClipboardTextData)
SDL_free(bd->ClipboardTextData);
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(bd->MouseCursors[cursor_n]);
ImGui_ImplSDL2_CloseGamepads();
io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
IM_DELETE(bd);
}
static void ImGui_ImplSDL2_UpdateMouseData()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
// We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
SDL_Window* focused_window = SDL_GetKeyboardFocus();
const bool is_app_focused = (bd->Window == focused_window);
#else
const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
#endif
if (is_app_focused)
{
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
// (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)
if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0)
{
int window_x, window_y, mouse_x_global, mouse_y_global;
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
SDL_GetWindowPosition(bd->Window, &window_x, &window_y);
io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y));
}
}
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
if (bd->MouseLastCursor != expected_cursor)
{
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
bd->MouseLastCursor = expected_cursor;
}
SDL_ShowCursor(SDL_TRUE);
}
}
static void ImGui_ImplSDL2_CloseGamepads()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
if (bd->GamepadMode != ImGui_ImplSDL2_GamepadMode_Manual)
for (SDL_GameController* gamepad : bd->Gamepads)
SDL_GameControllerClose(gamepad);
bd->Gamepads.resize(0);
}
void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array, int manual_gamepads_count)
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGui_ImplSDL2_CloseGamepads();
if (mode == ImGui_ImplSDL2_GamepadMode_Manual)
{
IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0);
for (int n = 0; n < manual_gamepads_count; n++)
bd->Gamepads.push_back(manual_gamepads_array[n]);
}
else
{
IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
bd->WantUpdateGamepadsList = true;
}
bd->GamepadMode = mode;
}
static void ImGui_ImplSDL2_UpdateGamepadButton(ImGui_ImplSDL2_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GameControllerButton button_no)
{
bool merged_value = false;
for (SDL_GameController* gamepad : bd->Gamepads)
merged_value |= SDL_GameControllerGetButton(gamepad, button_no) != 0;
io.AddKeyEvent(key, merged_value);
}
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
static void ImGui_ImplSDL2_UpdateGamepadAnalog(ImGui_ImplSDL2_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GameControllerAxis axis_no, float v0, float v1)
{
float merged_value = 0.0f;
for (SDL_GameController* gamepad : bd->Gamepads)
{
float vn = Saturate((float)(SDL_GameControllerGetAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
if (merged_value < vn)
merged_value = vn;
}
io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
}
static void ImGui_ImplSDL2_UpdateGamepads()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
// Update list of controller(s) to use
if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL2_GamepadMode_Manual)
{
ImGui_ImplSDL2_CloseGamepads();
int joystick_count = SDL_NumJoysticks();
for (int n = 0; n < joystick_count; n++)
if (SDL_IsGameController(n))
if (SDL_GameController* gamepad = SDL_GameControllerOpen(n))
{
bd->Gamepads.push_back(gamepad);
if (bd->GamepadMode == ImGui_ImplSDL2_GamepadMode_AutoFirst)
break;
}
bd->WantUpdateGamepadsList = false;
}
// FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (bd->Gamepads.Size == 0)
return;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
// Update gamepad inputs
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_CONTROLLER_BUTTON_X); // Xbox X, PS Square
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_CONTROLLER_BUTTON_B); // Xbox B, PS Circle
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_CONTROLLER_BUTTON_Y); // Xbox Y, PS Triangle
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_CONTROLLER_BUTTON_A); // Xbox A, PS Cross
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0.0f, 32767);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_CONTROLLER_BUTTON_LEFTSTICK);
ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_CONTROLLER_BUTTON_RIGHTSTICK);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32768);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);
ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);
}
void ImGui_ImplSDL2_NewFrame()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?");
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(bd->Window, &w, &h);
if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)
w = h = 0;
if (bd->Renderer != nullptr)
SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h);
else
SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
if (current_time <= bd->Time)
current_time = bd->Time + 1;
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
bd->Time = current_time;
if (bd->MouseLastLeaveFrame && bd->MouseLastLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
{
bd->MouseWindowID = 0;
bd->MouseLastLeaveFrame = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
ImGui_ImplSDL2_UpdateMouseData();
ImGui_ImplSDL2_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL2_UpdateGamepads();
}
//-----------------------------------------------------------------------------
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE

View File

@ -0,0 +1,46 @@
// dear imgui: Platform Backend for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
struct SDL_Window;
struct SDL_Renderer;
struct _SDL_GameController;
typedef union SDL_Event SDL_Event;
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOther(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
// When using manual mode, caller is responsible for opening/closing gamepad.
enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual };
IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = NULL, int manual_gamepads_count = -1);
#endif // #ifndef IMGUI_DISABLE

View File

@ -760,6 +760,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Setup backend capabilities flags // Setup backend capabilities flags
@ -787,7 +788,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
// Create buffers with a default size (they will later be grown as needed) // Create buffers with a default size (they will later be grown as needed)
bd->pFrameResources = new FrameResources[bd->numFramesInFlight]; bd->pFrameResources = new FrameResources[bd->numFramesInFlight];
for (int i = 0; i < bd->numFramesInFlight; i++) for (unsigned int i = 0; i < bd->numFramesInFlight; i++)
{ {
FrameResources* fr = &bd->pFrameResources[i]; FrameResources* fr = &bd->pFrameResources[i];
fr->IndexBuffer = nullptr; fr->IndexBuffer = nullptr;

View File

@ -34,11 +34,12 @@ struct ImGui_ImplWGPU_InitInfo
ImGui_ImplWGPU_InitInfo() ImGui_ImplWGPU_InitInfo()
{ {
PipelineMultisampleState.count = 1; PipelineMultisampleState.count = 1;
PipelineMultisampleState.mask = -1u; PipelineMultisampleState.mask = UINT32_MAX;
PipelineMultisampleState.alphaToCoverageEnabled = false; PipelineMultisampleState.alphaToCoverageEnabled = false;
} }
}; };
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info); IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info);
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(); IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();

View File

@ -17,63 +17,10 @@
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp // - Introduction, links and more at the top of imgui.cpp
#include "imgui.h"
#ifndef IMGUI_DISABLE
// FIX(zig-gamedev):
// #include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <windowsx.h> // GET_X_LPARAM(), GET_Y_LPARAM()
#include <tchar.h>
#include <dwmapi.h>
// Configuration flags to add in your imconfig.h file:
//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant.
// Using XInput for gamepad (will load DLL dynamically)
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
#include <xinput.h>
typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
#endif
// FIX(zig-gamedev):
extern "C" {
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
// Win32 message handler your application need to call.
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
#if 0
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif
// DPI-related helpers (optional)
// - Use to enable DPI awareness without having to create an application manifest.
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
// Transparency related helpers (optional) [experimental]
// - Use to enable alpha compositing transparency with the desktop.
// - Use together with e.g. clearing your framebuffer with zero-alpha.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
}; // extern "C"
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768)
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. // 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
@ -126,8 +73,71 @@ extern "C" {
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
#include "imgui.h"
#ifndef IMGUI_DISABLE
// FIX(zig-gamedev):
// #include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <windowsx.h> // GET_X_LPARAM(), GET_Y_LPARAM()
#include <tchar.h>
#include <dwmapi.h>
// Using XInput for gamepad (will load DLL dynamically)
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
#include <xinput.h>
typedef DWORD(WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
typedef DWORD(WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE *);
#endif
// Clang/GCC warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
#endif
// FIX(zig-gamedev):
extern "C"
{
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void *hwnd);
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void *hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
// Win32 message handler your application need to call.
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
#if 0
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif
// DPI-related helpers (optional)
// - Use to enable DPI awareness without having to create an application manifest.
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void *hwnd); // HWND hwnd
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void *monitor); // HMONITOR monitor
// Transparency related helpers (optional) [experimental]
// - Use to enable alpha compositing transparency with the desktop.
// - Use together with e.g. clearing your framebuffer with zero-alpha.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void *hwnd); // HWND hwnd
}; // extern "C"
// Forward Declarations // Forward Declarations
static void ImGui_ImplWin32_InitPlatformInterface(bool platformHasOwnDC); static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc);
static void ImGui_ImplWin32_ShutdownPlatformInterface(); static void ImGui_ImplWin32_ShutdownPlatformInterface();
static void ImGui_ImplWin32_UpdateMonitors(); static void ImGui_ImplWin32_UpdateMonitors();
@ -177,6 +187,7 @@ static void ImGui_ImplWin32_UpdateKeyboardCodePage()
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
INT64 perf_frequency, perf_counter; INT64 perf_frequency, perf_counter;
@ -329,7 +340,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL)); io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT)); io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU)); io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU));
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS)); io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
} }
// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports) // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
@ -475,9 +486,9 @@ static void ImGui_ImplWin32_UpdateMonitors()
void ImGui_ImplWin32_NewFrame() void ImGui_ImplWin32_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplWin32_Init()?"); IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplWin32_Init()?");
ImGuiIO &io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing) // Setup display size (every frame to accommodate for window resizing)
RECT rect = { 0, 0, 0, 0 }; RECT rect = { 0, 0, 0, 0 };
@ -675,11 +686,12 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
if (ImGui::GetCurrentContext() == nullptr) // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
return 0; // We silently allow both context or just only backend data to be nullptr.
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data *bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_Data *bd = ImGui_ImplWin32_GetBackendData();
if (bd == nullptr)
return 0;
ImGuiIO &io = ImGui::GetIO();
switch (msg) switch (msg)
{ {
@ -707,7 +719,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
::ScreenToClient(hwnd, &mouse_pos); ::ScreenToClient(hwnd, &mouse_pos);
io.AddMouseSourceEvent(mouse_source); io.AddMouseSourceEvent(mouse_source);
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
break; return 0;
} }
case WM_MOUSELEAVE: case WM_MOUSELEAVE:
case WM_NCMOUSELEAVE: case WM_NCMOUSELEAVE:
@ -720,7 +732,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
bd->MouseTrackedArea = 0; bd->MouseTrackedArea = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
} }
break; return 0;
} }
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
@ -1075,6 +1087,9 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
vd->HwndOwned = true; vd->HwndOwned = true;
viewport->PlatformRequestResize = false; viewport->PlatformRequestResize = false;
viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd;
// Secondary viewports store their imgui context
::SetPropA(vd->Hwnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
} }
static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
@ -1100,10 +1115,20 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
{ {
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0); IM_ASSERT(vd->Hwnd != 0);
// ShowParent() also brings parent to front, which is not always desirable,
// so we temporarily disable parenting. (#7354)
if (vd->HwndParent != NULL)
::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
::ShowWindow(vd->Hwnd, SW_SHOWNA); ::ShowWindow(vd->Hwnd, SW_SHOWNA);
else else
::ShowWindow(vd->Hwnd, SW_SHOW); ::ShowWindow(vd->Hwnd, SW_SHOW);
// Restore
if (vd->HwndParent != NULL)
::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent);
} }
static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport)
@ -1267,16 +1292,22 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) // Allow secondary viewport WndProc to be called regardless of current context
return true; ImGuiContext *hwnd_ctx = (ImGuiContext *)::GetPropA(hWnd, "IMGUI_CONTEXT");
ImGuiContext *prev_ctx = ImGui::GetCurrentContext();
if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
ImGui::SetCurrentContext(hwnd_ctx);
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd)) LRESULT result = 0;
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
result = true;
else if (ImGuiViewport *viewport = ImGui::FindViewportByPlatformHandle((void *)hWnd))
{ {
switch (msg) switch (msg)
{ {
case WM_CLOSE: case WM_CLOSE:
viewport->PlatformRequestClose = true; viewport->PlatformRequestClose = true;
return 0; break;
case WM_MOVE: case WM_MOVE:
viewport->PlatformRequestMove = true; viewport->PlatformRequestMove = true;
break; break;
@ -1285,7 +1316,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
break; break;
case WM_MOUSEACTIVATE: case WM_MOUSEACTIVATE:
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick) if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
return MA_NOACTIVATE; result = MA_NOACTIVATE;
break; break;
case WM_NCHITTEST: case WM_NCHITTEST:
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional). // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional).
@ -1293,12 +1324,15 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
if (viewport->Flags & ImGuiViewportFlags_NoInputs) if (viewport->Flags & ImGuiViewportFlags_NoInputs)
return HTTRANSPARENT; result = HTTRANSPARENT;
break; break;
} }
} }
if (result == 0)
return DefWindowProc(hWnd, msg, wParam, lParam); result = DefWindowProc(hWnd, msg, wParam, lParam);
if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
ImGui::SetCurrentContext(prev_ctx);
return result;
} }
static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc) static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
@ -1357,4 +1391,11 @@ static void ImGui_ImplWin32_ShutdownPlatformInterface()
//--------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@ -21,6 +21,7 @@
#include "imgui.h" // IMGUI_IMPL_API #include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE #ifndef IMGUI_DISABLE
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();

View File

@ -21,10 +21,11 @@
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// 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. // 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.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() // - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport ) //#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
//#define IMGUI_API __declspec( dllimport ) //#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -42,6 +43,7 @@
//#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) //#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)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#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). //#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).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#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) //#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)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
@ -49,6 +51,9 @@
//#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(). //#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().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Enable Test Engine / Automation features.
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
//---- Include imgui_user.h at the end of imgui.h as a convenience //---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H //#define IMGUI_INCLUDE_IMGUI_USER_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.90.4 // dear imgui, v1.91.0
// (drawing and font code) // (drawing and font code)
/* /*
@ -8,6 +8,7 @@ Index of this file:
// [SECTION] STB libraries implementation // [SECTION] STB libraries implementation
// [SECTION] Style functions // [SECTION] Style functions
// [SECTION] ImDrawList // [SECTION] ImDrawList
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
// [SECTION] ImDrawListSplitter // [SECTION] ImDrawListSplitter
// [SECTION] ImDrawData // [SECTION] ImDrawData
// [SECTION] Helpers ShadeVertsXXX functions // [SECTION] Helpers ShadeVertsXXX functions
@ -64,6 +65,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__) #elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@ -209,11 +211,13 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
@ -225,6 +229,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
@ -271,11 +276,13 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
@ -287,6 +294,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
@ -334,11 +342,13 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 1.00f);
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
@ -350,6 +360,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
@ -389,6 +400,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
} }
// Initialize before use in a new frame. We always have a command ready in the buffer. // Initialize before use in a new frame. We always have a command ready in the buffer.
// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
void ImDrawList::_ResetForNewFrame() void ImDrawList::_ResetForNewFrame()
{ {
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
@ -1223,10 +1235,10 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
} }
} }
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments) void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments)
{ {
if (num_segments <= 0) if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
_Path.reserve(_Path.Size + (num_segments + 1)); _Path.reserve(_Path.Size + (num_segments + 1));
@ -1235,11 +1247,10 @@ void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float
for (int i = 0; i <= num_segments; i++) for (int i = 0; i <= num_segments; i++)
{ {
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y); ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot); const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot); point.x = rel.x + center.x;
point.x = rel_x + center.x; point.y = rel.y + center.y;
point.y = rel_y + center.y;
_Path.push_back(point); _Path.push_back(point);
} }
} }
@ -1564,31 +1575,31 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
} }
// Ellipse // Ellipse
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness) void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness)
{ {
if ((col & IM_COL32_A_MASK) == 0) if ((col & IM_COL32_A_MASK) == 0)
return; return;
if (num_segments <= 0) if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points // Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
PathStroke(col, true, thickness); PathStroke(col, true, thickness);
} }
void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments) void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)
{ {
if ((col & IM_COL32_A_MASK) == 0) if ((col & IM_COL32_A_MASK) == 0)
return; return;
if (num_segments <= 0) if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points // Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
PathFillConvex(col); PathFillConvex(col);
} }
@ -1619,10 +1630,11 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
if ((col & IM_COL32_A_MASK) == 0) if ((col & IM_COL32_A_MASK) == 0)
return; return;
// Accept null ranges
if (text_begin == text_end || text_begin[0] == 0)
return;
if (text_end == NULL) if (text_end == NULL)
text_end = text_begin + strlen(text_begin); text_end = text_begin + strlen(text_begin);
if (text_begin == text_end)
return;
// Pull default font/size from the shared ImDrawListSharedData instance // Pull default font/size from the shared ImDrawListSharedData instance
if (font == NULL) if (font == NULL)
@ -1706,6 +1718,316 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
PopTextureID(); PopTextureID();
} }
//-----------------------------------------------------------------------------
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
//-----------------------------------------------------------------------------
// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
// Provided as a convenience for user but not used by main library.
//-----------------------------------------------------------------------------
// - ImTriangulator [Internal]
// - AddConcavePolyFilled()
//-----------------------------------------------------------------------------
enum ImTriangulatorNodeType
{
ImTriangulatorNodeType_Convex,
ImTriangulatorNodeType_Ear,
ImTriangulatorNodeType_Reflex
};
struct ImTriangulatorNode
{
ImTriangulatorNodeType Type;
int Index;
ImVec2 Pos;
ImTriangulatorNode* Next;
ImTriangulatorNode* Prev;
void Unlink() { Next->Prev = Prev; Prev->Next = Next; }
};
struct ImTriangulatorNodeSpan
{
ImTriangulatorNode** Data = NULL;
int Size = 0;
void push_back(ImTriangulatorNode* node) { Data[Size++] = node; }
void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }
};
struct ImTriangulator
{
static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; }
static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }
void Init(const ImVec2* points, int points_count, void* scratch_buffer);
void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle
// Internal functions
void BuildNodes(const ImVec2* points, int points_count);
void BuildReflexes();
void BuildEars();
void FlipNodeList();
bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
void ReclassifyNode(ImTriangulatorNode* node);
// Internal members
int _TrianglesLeft = 0;
ImTriangulatorNode* _Nodes = NULL;
ImTriangulatorNodeSpan _Ears;
ImTriangulatorNodeSpan _Reflexes;
};
// Distribute storage for nodes, ears and reflexes.
// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
// (this would require first building reflexes to bail to convex if empty, without even building nodes)
void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)
{
IM_ASSERT(scratch_buffer != NULL && points_count >= 3);
_TrianglesLeft = EstimateTriangleCount(points_count);
_Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node
_Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node*
_Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*
BuildNodes(points, points_count);
BuildReflexes();
BuildEars();
}
void ImTriangulator::BuildNodes(const ImVec2* points, int points_count)
{
for (int i = 0; i < points_count; i++)
{
_Nodes[i].Type = ImTriangulatorNodeType_Convex;
_Nodes[i].Index = i;
_Nodes[i].Pos = points[i];
_Nodes[i].Next = _Nodes + i + 1;
_Nodes[i].Prev = _Nodes + i - 1;
}
_Nodes[0].Prev = _Nodes + points_count - 1;
_Nodes[points_count - 1].Next = _Nodes;
}
void ImTriangulator::BuildReflexes()
{
ImTriangulatorNode* n1 = _Nodes;
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
{
if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))
continue;
n1->Type = ImTriangulatorNodeType_Reflex;
_Reflexes.push_back(n1);
}
}
void ImTriangulator::BuildEars()
{
ImTriangulatorNode* n1 = _Nodes;
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
{
if (n1->Type != ImTriangulatorNodeType_Convex)
continue;
if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))
continue;
n1->Type = ImTriangulatorNodeType_Ear;
_Ears.push_back(n1);
}
}
void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])
{
if (_Ears.Size == 0)
{
FlipNodeList();
ImTriangulatorNode* node = _Nodes;
for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)
node->Type = ImTriangulatorNodeType_Convex;
_Reflexes.Size = 0;
BuildReflexes();
BuildEars();
// If we still don't have ears, it means geometry is degenerated.
if (_Ears.Size == 0)
{
// Return first triangle available, mimicking the behavior of convex fill.
IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated
_Ears.Data[0] = _Nodes;
_Ears.Size = 1;
}
}
ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];
out_triangle[0] = ear->Prev->Index;
out_triangle[1] = ear->Index;
out_triangle[2] = ear->Next->Index;
ear->Unlink();
if (ear == _Nodes)
_Nodes = ear->Next;
ReclassifyNode(ear->Prev);
ReclassifyNode(ear->Next);
_TrianglesLeft--;
}
void ImTriangulator::FlipNodeList()
{
ImTriangulatorNode* prev = _Nodes;
ImTriangulatorNode* temp = _Nodes;
ImTriangulatorNode* current = _Nodes->Next;
prev->Next = prev;
prev->Prev = prev;
while (current != _Nodes)
{
temp = current->Next;
current->Next = prev;
prev->Prev = current;
_Nodes->Next = current;
current->Prev = _Nodes;
prev = current;
current = temp;
}
_Nodes = prev;
}
// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
{
ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;
for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)
{
ImTriangulatorNode* reflex = *p;
if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)
if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))
return false;
}
return true;
}
void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)
{
// Classify node
ImTriangulatorNodeType type;
const ImTriangulatorNode* n0 = n1->Prev;
const ImTriangulatorNode* n2 = n1->Next;
if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))
type = ImTriangulatorNodeType_Reflex;
else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))
type = ImTriangulatorNodeType_Ear;
else
type = ImTriangulatorNodeType_Convex;
// Update lists when a type changes
if (type == n1->Type)
return;
if (n1->Type == ImTriangulatorNodeType_Reflex)
_Reflexes.find_erase_unsorted(n1->Index);
else if (n1->Type == ImTriangulatorNodeType_Ear)
_Ears.find_erase_unsorted(n1->Index);
if (type == ImTriangulatorNodeType_Reflex)
_Reflexes.push_back(n1);
else if (type == ImTriangulatorNodeType_Ear)
_Ears.push_back(n1);
n1->Type = type;
}
// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
// It is up to caller to ensure not making costly calls that will be outside of visible area.
// As concave fill is noticeably more expensive than other primitives, be mindful of this...
// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
{
if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
return;
const ImVec2 uv = _Data->TexUvWhitePixel;
ImTriangulator triangulator;
unsigned int triangle[3];
if (Flags & ImDrawListFlags_AntiAliasedFill)
{
// Anti-aliased Fill
const float AA_SIZE = _FringeScale;
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
const int idx_count = (points_count - 2) * 3 + points_count * 6;
const int vtx_count = (points_count * 2);
PrimReserve(idx_count, vtx_count);
// Add indexes for fill
unsigned int vtx_inner_idx = _VtxCurrentIdx;
unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
while (triangulator._TrianglesLeft > 0)
{
triangulator.GetNextTriangle(triangle);
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));
_IdxWritePtr += 3;
}
// Compute normals
_Data->TempBuffer.reserve_discard(points_count);
ImVec2* temp_normals = _Data->TempBuffer.Data;
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
{
const ImVec2& p0 = points[i0];
const ImVec2& p1 = points[i1];
float dx = p1.x - p0.x;
float dy = p1.y - p0.y;
IM_NORMALIZE2F_OVER_ZERO(dx, dy);
temp_normals[i0].x = dy;
temp_normals[i0].y = -dx;
}
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
{
// Average normals
const ImVec2& n0 = temp_normals[i0];
const ImVec2& n1 = temp_normals[i1];
float dm_x = (n0.x + n1.x) * 0.5f;
float dm_y = (n0.y + n1.y) * 0.5f;
IM_FIXNORMAL2F(dm_x, dm_y);
dm_x *= AA_SIZE * 0.5f;
dm_y *= AA_SIZE * 0.5f;
// Add vertices
_VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
_VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
_VtxWritePtr += 2;
// Add indexes for fringes
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
_IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
_IdxWritePtr += 6;
}
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
}
else
{
// Non Anti-aliased Fill
const int idx_count = (points_count - 2) * 3;
const int vtx_count = points_count;
PrimReserve(idx_count, vtx_count);
for (int i = 0; i < vtx_count; i++)
{
_VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
_VtxWritePtr++;
}
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
while (triangulator._TrianglesLeft > 0)
{
triangulator.GetNextTriangle(triangle);
_IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);
_IdxWritePtr += 3;
}
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] ImDrawListSplitter // [SECTION] ImDrawListSplitter
@ -2678,8 +3000,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int unscaled_ascent, unscaled_descent, unscaled_line_gap; int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); const float ascent = ImCeil(unscaled_ascent * font_scale);
const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); const float descent = ImFloor(unscaled_descent * font_scale);
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x; const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
@ -3774,6 +4096,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{ {
x = start_x; x = start_x;
y += line_height; y += line_height;
if (y > clip_rect.w)
break; // break out of main loop
word_wrap_eol = NULL; word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue; continue;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.90.4 // dear imgui, v1.91.0
// (tables and columns code) // (tables and columns code)
/* /*
@ -24,8 +24,9 @@ Index of this file:
*/ */
// Navigating this file: // Navigating this file:
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. // - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Commentary // [SECTION] Commentary
@ -227,6 +228,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__) #elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
@ -318,6 +320,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
IM_ASSERT(inner_width >= 0.0f); IM_ASSERT(inner_width >= 0.0f);
// If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve. // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.
// FIXME: coarse clipping because access to table data causes two issues:
// - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
// - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
// The side-effects of accessing table data on coarse clip would be:
// - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
const ImVec2 avail_size = GetContentRegionAvail(); const ImVec2 avail_size = GetContentRegionAvail();
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
@ -326,6 +334,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
{ {
ItemSize(outer_rect); ItemSize(outer_rect);
ItemAdd(outer_rect, id);
return false; return false;
} }
@ -335,7 +344,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Acquire storage for the table // Acquire storage for the table
ImGuiTable* table = g.Tables.GetOrAddByKey(id); ImGuiTable* table = g.Tables.GetOrAddByKey(id);
const ImGuiTableFlags table_last_flags = table->Flags;
// Acquire temporary buffers // Acquire temporary buffers
const int table_idx = g.Tables.GetIndex(table); const int table_idx = g.Tables.GetIndex(table);
@ -353,6 +361,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Initialize // Initialize
const int previous_frame_active = table->LastFrameActive; const int previous_frame_active = table->LastFrameActive;
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
const ImGuiTableFlags previous_flags = table->Flags;
table->ID = id; table->ID = id;
table->Flags = flags; table->Flags = flags;
table->LastFrameActive = g.FrameCount; table->LastFrameActive = g.FrameCount;
@ -399,12 +408,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f)); SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));
// Reset scroll if we are reactivating it // Reset scroll if we are reactivating it
if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f)); SetNextWindowScroll(ImVec2(0.0f, 0.0f));
// Create scrolling region (without border and zero window padding) // Create scrolling region (without border and zero window padding)
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
table->InnerWindow = g.CurrentWindow; table->InnerWindow = g.CurrentWindow;
table->WorkRect = table->InnerWindow->WorkRect; table->WorkRect = table->InnerWindow->WorkRect;
table->OuterRect = table->InnerWindow->Rect(); table->OuterRect = table->InnerWindow->Rect();
@ -428,6 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// For non-scrolling tables, WorkRect == OuterRect == InnerRect. // For non-scrolling tables, WorkRect == OuterRect == InnerRect.
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
} }
// Push a standardized ID for both child-using and not-child-using tables // Push a standardized ID for both child-using and not-child-using tables
@ -498,6 +508,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->DeclColumnsCount = table->AngledHeadersCount = 0; table->DeclColumnsCount = table->AngledHeadersCount = 0;
if (previous_frame_active + 1 < g.FrameCount) if (previous_frame_active + 1 < g.FrameCount)
table->IsActiveIdInTable = false; table->IsActiveIdInTable = false;
table->AngledHeadersHeight = 0.0f;
temp_data->AngledHeadersExtraWidth = 0.0f; temp_data->AngledHeadersExtraWidth = 0.0f;
// Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
@ -511,7 +522,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
inner_window->DC.CurrentTableIdx = table_idx; inner_window->DC.CurrentTableIdx = table_idx;
if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
table->IsResetDisplayOrderRequest = true; table->IsResetDisplayOrderRequest = true;
// Mark as used to avoid GC // Mark as used to avoid GC
@ -1066,6 +1077,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
// - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
// - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
const float previous_instance_work_min_x = column->WorkMinX;
column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f); column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);
@ -1118,8 +1130,22 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f); // column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
// Reset content width variables // Reset content width variables
column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX; if (table->InstanceCurrent == 0)
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX; {
column->ContentMaxXFrozen = column->WorkMinX;
column->ContentMaxXUnfrozen = column->WorkMinX;
column->ContentMaxXHeadersUsed = column->WorkMinX;
column->ContentMaxXHeadersIdeal = column->WorkMinX;
}
else
{
// As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;
column->ContentMaxXFrozen += offset_from_previous_instance;
column->ContentMaxXUnfrozen += offset_from_previous_instance;
column->ContentMaxXHeadersUsed += offset_from_previous_instance;
column->ContentMaxXHeadersIdeal += offset_from_previous_instance;
}
// Don't decrement auto-fit counters until container window got a chance to submit its items // Don't decrement auto-fit counters until container window got a chance to submit its items
if (table->HostSkipItems == false) if (table->HostSkipItems == false)
@ -1238,9 +1264,9 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
// really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height). // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
// Actual columns highlight/render will be performed in EndTable() and not be affected. // Actual columns highlight/render will be performed in EndTable() and not be affected.
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);
const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight; const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);
const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
@ -1415,7 +1441,7 @@ void ImGui::EndTable()
if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted) if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)
{ {
ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS); const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));
const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
table->ResizedColumnNextWidth = new_width; table->ResizedColumnNextWidth = new_width;
} }
@ -1444,7 +1470,10 @@ void ImGui::EndTable()
// CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414) // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
if (inner_window != outer_window) if (inner_window != outer_window)
{ {
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
EndChild(); EndChild();
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
} }
else else
{ {
@ -1462,9 +1491,13 @@ void ImGui::EndTable()
} }
else if (temp_data->UserOuterSize.x <= 0.0f) else if (temp_data->UserOuterSize.x <= 0.0f)
{ {
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f); // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.
const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));
} }
else else
{ {
@ -1472,9 +1505,9 @@ void ImGui::EndTable()
} }
if (temp_data->UserOuterSize.y <= 0.0f) if (temp_data->UserOuterSize.y <= 0.0f)
{ {
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));
} }
else else
{ {
@ -1890,7 +1923,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
if (is_visible) if (is_visible)
{ {
// Update data for TableGetHoveredRow() // Update data for TableGetHoveredRow()
if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2) if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)
table_instance->HoveredRowNext = table->CurrentRow; table_instance->HoveredRowNext = table->CurrentRow;
// Decide of background color for the row // Decide of background color for the row
@ -1945,6 +1978,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
cell_bg_rect.ClipWith(table->BgClipRect); cell_bg_rect.ClipWith(table->BgClipRect);
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
} }
} }
@ -1962,14 +1996,16 @@ void ImGui::TableEndRow(ImGuiTable* table)
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
// get the new cursor position. // get the new cursor position.
if (unfreeze_rows_request) if (unfreeze_rows_request)
{
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
if (unfreeze_rows_actual) if (unfreeze_rows_actual)
{ {
IM_ASSERT(table->IsUnfrozenRows == false); IM_ASSERT(table->IsUnfrozenRows == false);
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->IsUnfrozenRows = true; table->IsUnfrozenRows = true;
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
@ -1991,6 +2027,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
} }
}
if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
table->RowBgColorCounter++; table->RowBgColorCounter++;
@ -2766,7 +2803,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n) static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)
{ {
IM_ASSERT(n < column->SortDirectionsAvailCount); IM_ASSERT(n < column->SortDirectionsAvailCount);
return (column->SortDirectionsAvailList >> (n << 1)) & 0x03; return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03);
} }
// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending) // Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)
@ -2907,6 +2944,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
} }
// Write output // Write output
// May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
if (dirty && sort_specs != NULL) if (dirty && sort_specs != NULL)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
@ -2919,7 +2957,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
sort_spec->ColumnUserID = column->UserID; sort_spec->ColumnUserID = column->UserID;
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
sort_spec->SortDirection = column->SortDirection; sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
} }
table->SortSpecs.Specs = sort_specs; table->SortSpecs.Specs = sort_specs;
@ -2967,7 +3005,7 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). // [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
// The intent is that advanced users willing to create customized headers would not need to use this helper // The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets. // and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy. // This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
@ -3153,15 +3191,43 @@ void ImGui::TableHeader(const char* label)
} }
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. // Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn
// FIXME: No hit-testing/button on the angled header. // FIXME: No hit-testing/button on the angled header.
void ImGui::TableAngledHeadersRow() void ImGui::TableAngledHeadersRow()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f); ImGuiTable* table = g.CurrentTable;
ImGuiTableTempData* temp_data = table->TempData;
temp_data->AngledHeadersRequests.resize(0);
temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);
// Which column needs highlight?
const ImGuiID row_id = GetID("##AngledHeaders");
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
int highlight_column_n = table->HighlightColumnHeader;
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
highlight_column_n = table->HoveredColumnBody;
// Build up request
ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);
ImU32 col_text = GetColorU32(ImGuiCol_Text);
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
{
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
continue;
ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };
temp_data->AngledHeadersRequests.push_back(request);
} }
void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) // Render row
TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);
}
// Important: data must be fed left to right
void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
@ -3185,7 +3251,7 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
// Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow() // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
// FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other. // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f; const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;
const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y); const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));
table->AngledHeadersHeight = row_height; table->AngledHeadersHeight = row_height;
table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right
@ -3203,28 +3269,22 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns
const ImGuiID row_id = GetID("##AngledHeaders");
ButtonBehavior(row_r, row_id, NULL, NULL); ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id); KeepAliveID(row_id);
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
int highlight_column_n = table->HighlightColumnHeader; const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
if (highlight_column_n == -1 && table->HoveredColumnBody != -1) const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
highlight_column_n = table->HoveredColumnBody;
// Draw background and labels in first pass, then all borders. // Draw background and labels in first pass, then all borders.
float max_x = 0.0f; float max_x = 0.0f;
ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
for (int pass = 0; pass < 2; pass++) for (int pass = 0; pass < 2; pass++)
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < data_count; order_n++)
{ {
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) const ImGuiTableHeaderData* request = &data[order_n];
continue; const int column_n = request->Index;
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
continue;
ImVec2 bg_shape[4]; ImVec2 bg_shape[4];
bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
@ -3234,9 +3294,8 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
if (pass == 0) if (pass == 0)
{ {
// Draw shape // Draw shape
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg)); draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
if (column_n == highlight_column_n) draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover
max_x = ImMax(max_x, bg_shape[3].x); max_x = ImMax(max_x, bg_shape[3].x);
// Draw label // Draw label
@ -3244,8 +3303,17 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
// - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated. // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
const char* label_name = TableGetColumnName(table, column_n); const char* label_name = TableGetColumnName(table, column_n);
const char* label_name_end = FindRenderedTextEnd(label_name); const char* label_name_end = FindRenderedTextEnd(label_name);
const float line_off_step_x = g.FontSize / -sin_a; const float line_off_step_x = (g.FontSize / -sin_a);
float line_off_curr_x = 0.0f; const int label_lines = ImTextCountLines(label_name, label_name_end);
// Left<>Right alignment
float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
// Register header width
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
while (label_name < label_name_end) while (label_name < label_name_end)
{ {
const char* label_name_eol = strchr(label_name, '\n'); const char* label_name_eol = strchr(label_name, '\n');
@ -3254,26 +3322,30 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
// FIXME: Individual line clipping for right-most column is broken for negative angles. // FIXME: Individual line clipping for right-most column is broken for negative angles.
ImVec2 label_size = CalcTextSize(label_name, label_name_eol); ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symetrical but hide more text. float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x); float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx; int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx; int vtx_idx_end = draw_list->_VtxCurrentIdx;
// Up<>Down alignment
const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);
const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);
// Rotate and offset label // Rotate and offset label
ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x, window->ClipRect.Min.y + label_size.y); ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);
ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y); ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);
line_off_curr_x += line_off_step_x; line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;
pivot_out += unit_right * padding.y; pivot_out += unit_right * padding.y;
if (flip_label) if (flip_label)
pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x)); pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));
pivot_out.x += flip_label ? line_off_curr_x - line_off_step_x : line_off_curr_x; pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;
ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
//if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
// Register header width
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(line_off_curr_x);
label_name = label_name_eol + 1; label_name = label_name_eol + 1;
} }
} }
@ -3402,7 +3474,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
Separator(); Separator();
want_separator = true; want_separator = true;
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
{ {
ImGuiTableColumn* other_column = &table->Columns[other_column_n]; ImGuiTableColumn* other_column = &table->Columns[other_column_n];
@ -3988,7 +4060,7 @@ float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offse
return offset / (columns->OffMaxX - columns->OffMinX); return offset / (columns->OffMaxX - columns->OffMinX);
} }
static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f; static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;
static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
{ {
@ -3999,7 +4071,7 @@ static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x; float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;
x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths)) if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
@ -4314,7 +4386,7 @@ void ImGui::EndColumns()
ImGuiOldColumnData* column = &columns->Columns[n]; ImGuiOldColumnData* column = &columns->Columns[n];
float x = window->Pos.x + GetColumnOffset(n); float x = window->Pos.x + GetColumnOffset(n);
const ImGuiID column_id = columns->ID + ImGuiID(n); const ImGuiID column_id = columns->ID + ImGuiID(n);
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav)) if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
continue; continue;

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@
// 1.13 (2019-02-07) fix bug in undo size management // 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield // 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual // 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
// 1.9 (2016-08-27) customizable move-by-word // 1.9 (2016-08-27) customizable move-by-word
// 1.8 (2016-04-02) better keyboard handling when mouse button is down // 1.8 (2016-04-02) better keyboard handling when mouse button is down
// 1.7 (2015-09-13) change y range handling in case baseline is non-0 // 1.7 (2015-09-13) change y range handling in case baseline is non-0

View File

@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
// If skip != 0, this tells stb_truetype to skip any codepoints for which // If skip != 0, this tells stb_truetype to skip any codepoints for which
// there is no corresponding glyph. If skip=0, which is the default, then // there is no corresponding glyph. If skip=0, which is the default, then
// codepoints without a glyph recived the font's "missing character" glyph, // codepoints without a glyph received the font's "missing character" glyph,
// typically an empty box by convention. // typically an empty box by convention.
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above

View File

@ -1,5 +1,5 @@
Dear ImGui Test Engine License (v1.03) Dear ImGui Test Engine License (v1.04)
Copyright (c) 2018-2023 Omar Cornut Copyright (c) 2018-2024 Omar Cornut
This document is a legal agreement ("License") between you ("Licensee") and This document is a legal agreement ("License") between you ("Licensee") and
DISCO HELLO ("Licensor") that governs your use of Dear ImGui Test Engine ("Software"). DISCO HELLO ("Licensor") that governs your use of Dear ImGui Test Engine ("Software").
@ -31,9 +31,10 @@ information are available at the following URL: http://www.dearimgui.com/license
2.1. License scope 2.1. License scope
A limited and non-exclusive license is hereby granted, to the Licensee, to reproduce, A limited and non-exclusive worldwide license is hereby granted, to the Licensee,
execute, publicly perform, and display, use, copy, modify, merge, distribute, or create to reproduce, execute, publicly perform, and display, use, copy, modify, merge,
derivative works based on and/or derived from the Software ("Derivative Software"). distribute, or create derivative works based on and/or derived from the Software
("Derivative Software").
2.2. Right of distribution 2.2. Right of distribution

View File

@ -30,6 +30,7 @@ Index of this file:
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "imgui_te_engine.h"
#include "imgui_capture_tool.h" #include "imgui_capture_tool.h"
#include "imgui_te_utils.h" // ImPathFindFilename, ImPathFindExtension, ImPathFixSeparatorsForCurrentOS, ImFileCreateDirectoryChain, ImOsOpenInShell #include "imgui_te_utils.h" // ImPathFindFilename, ImPathFindExtension, ImPathFixSeparatorsForCurrentOS, ImFileCreateDirectoryChain, ImOsOpenInShell
#include "thirdparty/Str/Str.h" #include "thirdparty/Str/Str.h"
@ -50,7 +51,10 @@ Index of this file:
#pragma warning (push) #pragma warning (push)
#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration
#pragma warning (disable: 4457) // declaration of 'xx' hides function parameter #pragma warning (disable: 4457) // declaration of 'xx' hides function parameter
#else #elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"// warning: 'sprintf' has been explicitly marked deprecated here
#elif defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wsign-conversion"
#endif #endif
@ -76,7 +80,9 @@ using namespace IMGUI_STB_NAMESPACE;
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (pop) #pragma warning (pop)
#else #elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
@ -365,7 +371,7 @@ ImGuiCaptureStatus ImGuiCaptureContext::CaptureUpdate(ImGuiCaptureArgs* args)
// FIXME-CAPTURE: Window width change may affect vertical content size if window contains text that wraps. To accurately position mouse cursor for capture we avoid horizontal resize. // FIXME-CAPTURE: Window width change may affect vertical content size if window contains text that wraps. To accurately position mouse cursor for capture we avoid horizontal resize.
// Instead window width should be set manually before capture, as it is simple to do and most of the time we already have a window of desired width. // Instead window width should be set manually before capture, as it is simple to do and most of the time we already have a window of desired width.
//full_size.x = ImMax(window->SizeFull.x, window->ContentSize.x + (window->WindowPadding.x + window->WindowBorderSize) * 2); //full_size.x = ImMax(window->SizeFull.x, window->ContentSize.x + (window->WindowPadding.x + window->WindowBorderSize) * 2);
full_size.y = ImMax(window->SizeFull.y, window->ContentSize.y + (window->WindowPadding.y + window->WindowBorderSize) * 2 + window->TitleBarHeight() + window->MenuBarHeight()); full_size.y = ImMax(window->SizeFull.y, window->ContentSize.y + (window->WindowPadding.y + window->WindowBorderSize) * 2 + window->DecoOuterSizeY1);
ImGui::SetWindowSize(window, full_size); ImGui::SetWindowSize(window, full_size);
_HoveredWindow = g.HoveredWindow; _HoveredWindow = g.HoveredWindow;
} }
@ -732,11 +738,10 @@ void ImGuiCaptureToolUI::_CaptureWindowsSelector(ImGuiCaptureContext* context, I
if (!allow_capture) if (!allow_capture)
ImGui::BeginDisabled(); ImGui::BeginDisabled();
bool do_capture = ImGui::Button(label, button_sz); bool do_capture = ImGui::Button(label, button_sz);
do_capture |= io.KeyAlt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)); do_capture |= io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_C);
if (!allow_capture) if (!allow_capture)
ImGui::EndDisabled(); ImGui::EndDisabled();
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Alternatively press Alt+C to capture selection.");
ImGui::SetTooltip("Alternatively press Alt+C to capture selection.");
if (do_capture && _InitializeOutputFile()) if (do_capture && _InitializeOutputFile())
_StateIsCapturing = true; _StateIsCapturing = true;
} }
@ -889,8 +894,8 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
ImOsOpenInShell(OutputLastFilename); ImOsOpenInShell(OutputLastFilename);
if (!has_last_file_name) if (!has_last_file_name)
ImGui::EndDisabled(); ImGui::EndDisabled();
if (has_last_file_name && ImGui::IsItemHovered()) if (has_last_file_name)
ImGui::SetTooltip("Open %s", OutputLastFilename); ImGui::SetItemTooltip("Open %s", OutputLastFilename);
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
} }
@ -908,8 +913,7 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
ImPathFixSeparatorsForCurrentOS(output_dir); ImPathFixSeparatorsForCurrentOS(output_dir);
ImOsOpenInShell(output_dir); ImOsOpenInShell(output_dir);
} }
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Open %s/", output_dir);
ImGui::SetTooltip("Open %s/", output_dir);
} }
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
@ -917,18 +921,16 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
ImGui::PushItemWidth(BUTTON_WIDTH); ImGui::PushItemWidth(BUTTON_WIDTH);
ImGui::InputText("Output template", _OutputFileTemplate, IM_ARRAYSIZE(_OutputFileTemplate)); ImGui::InputText("Output template", _OutputFileTemplate, IM_ARRAYSIZE(_OutputFileTemplate));
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip(
ImGui::SetTooltip("Output template should contain one %%d (or variation of it) format variable. " "Output template should contain one %%d (or variation of it) format variable. "
"Multiple captures will be saved with an increasing number to avoid overwriting same file."); "Multiple captures will be saved with an increasing number to avoid overwriting same file.");
_ShowEncoderConfigFields(context); _ShowEncoderConfigFields(context);
ImGui::DragFloat("Padding", &_CaptureArgs.InPadding, 0.1f, 0, 32, "%.0f"); ImGui::DragFloat("Padding", &_CaptureArgs.InPadding, 0.1f, 0, 32, "%.0f");
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Extra padding around captured area.");
ImGui::SetTooltip("Extra padding around captured area.");
ImGui::DragInt("Video FPS", &_CaptureArgs.InRecordFPSTarget, 0.1f, 10, 100, "%d fps"); ImGui::DragInt("Video FPS", &_CaptureArgs.InRecordFPSTarget, 0.1f, 10, 100, "%d fps");
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Target FPS for video captures.");
ImGui::SetTooltip("Target FPS for video captures.");
if (ImGui::Button("Snap Windows To Grid", ImVec2(BUTTON_WIDTH, 0))) if (ImGui::Button("Snap Windows To Grid", ImVec2(BUTTON_WIDTH, 0)))
_SnapWindowsToGrid(SnapGridSize); _SnapWindowsToGrid(SnapGridSize);
@ -945,13 +947,12 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
ImGui::BeginDisabled(!content_stitching_available); ImGui::BeginDisabled(!content_stitching_available);
ImGui::CheckboxFlags("Stitch full contents height", &_CaptureArgs.InFlags, ImGuiCaptureFlags_StitchAll); ImGui::CheckboxFlags("Stitch full contents height", &_CaptureArgs.InFlags, ImGuiCaptureFlags_StitchAll);
ImGui::EndDisabled(); ImGui::EndDisabled();
if (!content_stitching_available && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) if (!content_stitching_available)
ImGui::SetTooltip("Content stitching is not possible when using viewports."); ImGui::SetItemTooltip("Content stitching is not possible when using viewports.");
ImGui::CheckboxFlags("Include other windows", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeOtherWindows); ImGui::CheckboxFlags("Include other windows", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeOtherWindows);
ImGui::CheckboxFlags("Include tooltips & popups", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeTooltipsAndPopups); ImGui::CheckboxFlags("Include tooltips & popups", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeTooltipsAndPopups);
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Capture area will be expanded to include visible tooltips.");
ImGui::SetTooltip("Capture area will be expanded to include visible tooltips.");
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::TreePop(); ImGui::TreePop();
@ -1024,8 +1025,7 @@ bool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context)
const bool encoder_exe_missing = !ImFileExist(context->VideoCaptureEncoderPath); const bool encoder_exe_missing = !ImFileExist(context->VideoCaptureEncoderPath);
if (encoder_exe_missing) if (encoder_exe_missing)
ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255)); ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255));
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("Absolute or relative path to video encoder executable (e.g. \"path/to/ffmpeg.exe\"). Required for video recording.%s", encoder_exe_missing ? "\nFile does not exist!" : "");
ImGui::SetTooltip("Absolute or relative path to video encoder executable (e.g. \"path/to/ffmpeg.exe\"). Required for video recording.%s", encoder_exe_missing ? "\nFile does not exist!" : "");
} }
struct CmdLineParamsInfo struct CmdLineParamsInfo
@ -1105,8 +1105,7 @@ bool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context)
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
if (ImGui::IsItemHovered()) ImGui::SetItemTooltip("File extension for captured video file.");
ImGui::SetTooltip("File extension for captured video file.");
} }
return modified; return modified;
} }

View File

@ -4,7 +4,7 @@
#pragma once #pragma once
#include "imgui_te_utils.h" // ImFuncPtr // Need "imgui_te_engine.h" included for ImFuncPtr
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Forward declarations // Forward declarations

File diff suppressed because it is too large Load Diff

View File

@ -90,7 +90,8 @@ struct IMGUI_API ImGuiTestRefDesc
char Buf[80]; char Buf[80];
const char* c_str() { return Buf; } const char* c_str() { return Buf; }
ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo* item); ImGuiTestRefDesc(const ImGuiTestRef& ref);
ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo& item);
}; };
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -114,6 +115,7 @@ enum ImGuiTestAction
}; };
// Generic flags for many ImGuiTestContext functions // Generic flags for many ImGuiTestContext functions
// Some flags are only supported by a handful of functions. Check function headers for list of supported flags.
enum ImGuiTestOpFlags_ enum ImGuiTestOpFlags_
{ {
ImGuiTestOpFlags_None = 0, ImGuiTestOpFlags_None = 0,
@ -122,11 +124,12 @@ enum ImGuiTestOpFlags_
ImGuiTestOpFlags_NoFocusWindow = 1 << 3, // Don't focus window when aiming at an item ImGuiTestOpFlags_NoFocusWindow = 1 << 3, // Don't focus window when aiming at an item
ImGuiTestOpFlags_NoAutoUncollapse = 1 << 4, // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors) ImGuiTestOpFlags_NoAutoUncollapse = 1 << 4, // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors)
ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5, // Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path. ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5, // Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path.
ImGuiTestOpFlags_IsSecondAttempt = 1 << 6, // Used by recursing functions to indicate a second attempt ImGuiTestOpFlags_NoYield = 1 << 6, // Don't yield (only supported by a few functions), in case you need to manage rigorous per-frame timing.
ImGuiTestOpFlags_MoveToEdgeL = 1 << 7, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities. ImGuiTestOpFlags_IsSecondAttempt = 1 << 7, // Used by recursing functions to indicate a second attempt
ImGuiTestOpFlags_MoveToEdgeR = 1 << 8, ImGuiTestOpFlags_MoveToEdgeL = 1 << 8, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
ImGuiTestOpFlags_MoveToEdgeU = 1 << 9, ImGuiTestOpFlags_MoveToEdgeR = 1 << 9,
ImGuiTestOpFlags_MoveToEdgeD = 1 << 10, ImGuiTestOpFlags_MoveToEdgeU = 1 << 10,
ImGuiTestOpFlags_MoveToEdgeD = 1 << 11,
}; };
// Advanced filtering for ItemActionAll() // Advanced filtering for ItemActionAll()
@ -176,6 +179,7 @@ struct IMGUI_API ImGuiTestGenericVars
int Count; int Count;
ImGuiID DockId; ImGuiID DockId;
ImGuiID OwnerId; ImGuiID OwnerId;
ImVec2 WindowSize;
ImGuiWindowFlags WindowFlags; ImGuiWindowFlags WindowFlags;
ImGuiTableFlags TableFlags; ImGuiTableFlags TableFlags;
ImGuiPopupFlags PopupFlags; ImGuiPopupFlags PopupFlags;
@ -185,8 +189,8 @@ struct IMGUI_API ImGuiTestGenericVars
bool UseViewports; bool UseViewports;
float Width; float Width;
ImVec2 Pos; ImVec2 Pos;
ImVec2 Size;
ImVec2 Pivot; ImVec2 Pivot;
ImVec2 ItemSize;
ImVec4 Color1, Color2; ImVec4 Color1, Color2;
// Generic unnamed storage // Generic unnamed storage
@ -283,7 +287,6 @@ struct IMGUI_API ImGuiTestContext
// Yield, Timing // Yield, Timing
void Yield(int count = 1); void Yield(int count = 1);
void YieldUntil(int frame_count);
void Sleep(float time_in_second); // Sleep for a given simulation time, unless in Fast mode void Sleep(float time_in_second); // Sleep for a given simulation time, unless in Fast mode
void SleepShort(); // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode. void SleepShort(); // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode.
void SleepStandard(); // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode. void SleepStandard(); // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode.
@ -297,12 +300,15 @@ struct IMGUI_API ImGuiTestContext
// - SetRef("//$FOCUSED"), ItemClick("Button") --> click "Button" in focused window. // - SetRef("//$FOCUSED"), ItemClick("Button") --> click "Button" in focused window.
// See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions. // See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions.
// Note: SetRef() may take multiple frames to complete if specified ref is an item id. // Note: SetRef() may take multiple frames to complete if specified ref is an item id.
// Note: SetRef() ignores current reference, so they are always absolute path.
void SetRef(ImGuiTestRef ref); void SetRef(ImGuiTestRef ref);
void SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code) void SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code)
ImGuiTestRef GetRef(); ImGuiTestRef GetRef();
// Windows // Windows
ImGuiTestItemInfo* WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); // - Use WindowInfo() to access path to child windows, since the paths are internally mangled.
// - SetRef(WindowInfo("Parent/Child")->Window) --> set ref to child window.
ImGuiTestItemInfo WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void WindowClose(ImGuiTestRef window_ref); void WindowClose(ImGuiTestRef window_ref);
void WindowCollapse(ImGuiTestRef window_ref, bool collapsed); void WindowCollapse(ImGuiTestRef window_ref, bool collapsed);
void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
@ -340,7 +346,7 @@ struct IMGUI_API ImGuiTestContext
// Mouse inputs // Mouse inputs
void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void MouseMoveToPos(ImVec2 pos); void MouseMoveToPos(ImVec2 pos);
void MouseTeleportToPos(ImVec2 pos); void MouseTeleportToPos(ImVec2 pos, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void MouseClick(ImGuiMouseButton button = 0); void MouseClick(ImGuiMouseButton button = 0);
void MouseClickMulti(ImGuiMouseButton button, int count); void MouseClickMulti(ImGuiMouseButton button, int count);
void MouseDoubleClick(ImGuiMouseButton button = 0); void MouseDoubleClick(ImGuiMouseButton button = 0);
@ -400,10 +406,10 @@ struct IMGUI_API ImGuiTestContext
// Low-level queries // Low-level queries
// - ItemInfo queries never returns a NULL pointer, instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored. // - ItemInfo queries never returns a NULL pointer, instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored.
// - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does. // - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does.
ImGuiTestItemInfo* ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); ImGuiTestItemInfo ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
ImGuiTestItemInfo* ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); ImGuiTestItemInfo ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
ImGuiID ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start); ImGuiID ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start);
ImGuiTestItemInfo* ItemInfoNull(); ImGuiTestItemInfo ItemInfoNull() { return ImGuiTestItemInfo(); }
void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1); void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1);
// Item/Widgets manipulation // Item/Widgets manipulation
@ -428,12 +434,12 @@ struct IMGUI_API ImGuiTestContext
void ItemInputValue(ImGuiTestRef ref, float f); void ItemInputValue(ImGuiTestRef ref, float f);
void ItemInputValue(ImGuiTestRef ref, const char* str); void ItemInputValue(ImGuiTestRef ref, const char* str);
// Item/Widgets: Drag and Mouse operations // Item/Widgets: Helpers to easily read a value by selecting Slider/Drag/Input text, copying into clipboard and parsing it.
void ItemHold(ImGuiTestRef ref, float time); // - This requires the item to be selectable (we will later provide helpers that works in more general manner)
void ItemHoldForFrames(ImGuiTestRef ref, int frames); // - Original clipboard value is restored afterward.
void ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst); bool ItemSelectAndReadValue(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0); void ItemSelectAndReadValue(ImGuiTestRef ref, int* out_v);
void ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta); void ItemSelectAndReadValue(ImGuiTestRef ref, float* out_v);
// Item/Widgets: Status query // Item/Widgets: Status query
bool ItemExists(ImGuiTestRef ref); bool ItemExists(ImGuiTestRef ref);
@ -441,6 +447,13 @@ struct IMGUI_API ImGuiTestContext
bool ItemIsOpened(ImGuiTestRef ref); bool ItemIsOpened(ImGuiTestRef ref);
void ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked); void ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked);
// Item/Widgets: Drag and Mouse operations
void ItemHold(ImGuiTestRef ref, float time);
void ItemHoldForFrames(ImGuiTestRef ref, int frames);
void ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst);
void ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0);
void ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta);
// Helpers for Tab Bars widgets // Helpers for Tab Bars widgets
void TabClose(ImGuiTestRef ref); void TabClose(ImGuiTestRef ref);
bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order); bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order);
@ -493,15 +506,17 @@ struct IMGUI_API ImGuiTestContext
// Obsolete functions // Obsolete functions
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Obsoleted 2024/05/21
void YieldUntil(int frame_count) { while (FrameCount < frame_count) { Yield(); } }
// Obsoleted 2022/10/11 // Obsoleted 2022/10/11
ImGuiID GetIDByInt(int n); // Prefer using "$$123" ImGuiID GetIDByInt(int n); // Prefer using "$$123"
ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref); ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref);
ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF" ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF"
ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref); ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref);
// Obsoleted 2022/09/26 // Obsoleted 2022/09/26
void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); } //void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); }
void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); } //void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); }
void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); } //void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }
#endif #endif
// [Internal] // [Internal]
@ -604,6 +619,7 @@ template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, Im
// Floating point compares // Floating point compares
#define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Equal #define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Equal
#define IM_CHECK_FLOAT_NE_EPS(_LHS, _RHS) IM_CHECK_GT(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Not Equal
#define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS) #define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS)
#define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E) #define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E)

View File

@ -592,17 +592,34 @@ void ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine)
case ImGuiTestInputType_Key: case ImGuiTestInputType_Key:
{ {
ImGuiKeyChord key_chord = input.KeyChord; ImGuiKeyChord key_chord = input.KeyChord;
#if IMGUI_VERSION_NUM >= 19016 #if IMGUI_VERSION_NUM >= 19016 && IMGUI_VERSION_NUM < 19063
key_chord = ImGui::FixupKeyChord(&g, key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight key_chord = ImGui::FixupKeyChord(&g, key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight
#endif
#if IMGUI_VERSION_NUM >= 19063
key_chord = ImGui::FixupKeyChord(key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight
#endif #endif
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
ImGuiKeyChord mods = (key_chord & ImGuiMod_Mask_); ImGuiKeyChord mods = (key_chord & ImGuiMod_Mask_);
if (mods != 0x00) if (mods != 0x00)
{ {
// OSX conversion // OSX conversion
#if IMGUI_VERSION_NUM >= 18912 #if IMGUI_VERSION_NUM >= 18912 && IMGUI_VERSION_NUM < 19063
if (mods & ImGuiMod_Shortcut) if (mods & ImGuiMod_Shortcut)
mods = (mods & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); mods = (mods & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl);
#endif
#if IMGUI_VERSION_NUM >= 19063
// MacOS: swap Cmd(Super) and Ctrl WILL BE SWAPPED BACK BY io.AddKeyEvent()
if (g.IO.ConfigMacOSXBehaviors)
{
if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Super)
mods = (mods & ~ImGuiMod_Super) | ImGuiMod_Ctrl;
else if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Ctrl)
mods = (mods & ~ImGuiMod_Ctrl) | ImGuiMod_Super;
if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_RightCtrl; }
else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_RightSuper; }
}
#endif #endif
// Submitting a ImGuiMod_XXX without associated key needs to add at least one of the key. // Submitting a ImGuiMod_XXX without associated key needs to add at least one of the key.
if (mods & ImGuiMod_Ctrl) if (mods & ImGuiMod_Ctrl)
@ -805,7 +822,7 @@ static void ImGuiTestEngine_PostNewFrame(ImGuiTestEngine* engine, ImGuiContext*
for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++) for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++)
{ {
ImGuiTestInfoTask* task = engine->InfoTasks[task_n]; ImGuiTestInfoTask* task = engine->InfoTasks[task_n];
if (task->FrameCount < engine->FrameCount - LOCATION_TASK_ELAPSE_FRAMES && task->Result.RefCount == 0) if (task->FrameCount < engine->FrameCount - LOCATION_TASK_ELAPSE_FRAMES)
{ {
IM_DELETE(task); IM_DELETE(task);
engine->InfoTasks.erase(engine->InfoTasks.Data + task_n); engine->InfoTasks.erase(engine->InfoTasks.Data + task_n);
@ -950,7 +967,7 @@ int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine)
const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v) const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v)
{ {
static const char* names[ImGuiTestStatus_COUNT] = { "Success", "Queued", "Running", "Error", "Suspended" }; static const char* names[ImGuiTestStatus_COUNT] = { "Unknown", "Success", "Queued", "Running", "Error", "Suspended" };
IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestStatus_COUNT); IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestStatus_COUNT);
if (v >= 0 && v < IM_ARRAYSIZE(names)) if (v >= 0 && v < IM_ARRAYSIZE(names))
return names[v]; return names[v];
@ -1172,6 +1189,32 @@ ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* cat
return t; return t;
} }
void ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test)
{
// Cannot unregister a running test. Please contact us if you need this.
if (engine->TestContext != NULL)
IM_ASSERT(engine->TestContext->Test != test);
// Remove from lists
bool found = engine->TestsAll.find_erase(test);
IM_ASSERT(found); // Calling ImGuiTestEngine_UnregisterTest() on an unknown test.
for (int n = 0; n < engine->TestsQueue.Size; n++)
{
ImGuiTestRunTask& task = engine->TestsQueue[n];
if (task.Test == test)
{
engine->TestsQueue.erase(&task);
n--;
}
}
if (engine->UiSelectAndScrollToTest == test)
engine->UiSelectAndScrollToTest = NULL;
if (engine->UiSelectedTest == test)
engine->UiSelectedTest = NULL;
IM_DELETE(test);
}
ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine) ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine)
{ {
return engine->PerfTool; return engine->PerfTool;
@ -1297,6 +1340,7 @@ void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, c
if (group != ImGuiTestGroup_Unknown && test->Group != group) if (group != ImGuiTestGroup_Unknown && test->Group != group)
continue; continue;
if (filter_str != NULL)
if (!ImGuiTestEngine_PassFilter(test, filter_str)) if (!ImGuiTestEngine_PassFilter(test, filter_str))
continue; continue;
@ -1386,8 +1430,10 @@ struct ImGuiTestContextUiContextBackup
IO = g.IO; IO = g.IO;
Style = g.Style; Style = g.Style;
DebugLogFlags = g.DebugLogFlags; DebugLogFlags = g.DebugLogFlags;
#if IMGUI_VERSION_NUM >= 18837
ConfigNavWindowingKeyNext = g.ConfigNavWindowingKeyNext; ConfigNavWindowingKeyNext = g.ConfigNavWindowingKeyNext;
ConfigNavWindowingKeyPrev = g.ConfigNavWindowingKeyPrev; ConfigNavWindowingKeyPrev = g.ConfigNavWindowingKeyPrev;
#endif
memset(IO.MouseDown, 0, sizeof(IO.MouseDown)); memset(IO.MouseDown, 0, sizeof(IO.MouseDown));
for (int n = 0; n < IM_ARRAYSIZE(IO.KeysData); n++) for (int n = 0; n < IM_ARRAYSIZE(IO.KeysData); n++)
IO.KeysData[n].Down = false; IO.KeysData[n].Down = false;
@ -1400,8 +1446,10 @@ struct ImGuiTestContextUiContextBackup
g.IO = IO; g.IO = IO;
g.Style = Style; g.Style = Style;
g.DebugLogFlags = DebugLogFlags; g.DebugLogFlags = DebugLogFlags;
#if IMGUI_VERSION_NUM >= 18837
g.ConfigNavWindowingKeyNext = ConfigNavWindowingKeyNext; g.ConfigNavWindowingKeyNext = ConfigNavWindowingKeyNext;
g.ConfigNavWindowingKeyPrev = ConfigNavWindowingKeyPrev; g.ConfigNavWindowingKeyPrev = ConfigNavWindowingKeyPrev;
#endif
} }
void RestoreClipboardFuncs(ImGuiContext& g) void RestoreClipboardFuncs(ImGuiContext& g)
{ {
@ -1517,19 +1565,12 @@ void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_c
else else
{ {
ctx->LogWarning("Child Test: '%s' '%s'..", test->Category, test->Name); ctx->LogWarning("Child Test: '%s' '%s'..", test->Category, test->Name);
ctx->LogWarning("(ShareVars=%d ShareTestContext=%d)", (run_flags & ImGuiTestRunFlags_ShareVars) ? 1 : 0, (run_flags & ImGuiTestRunFlags_ShareTestContext) ? 1 : 0); ctx->LogDebug("(ShareVars=%d ShareTestContext=%d)", (run_flags & ImGuiTestRunFlags_ShareVars) ? 1 : 0, (run_flags & ImGuiTestRunFlags_ShareTestContext) ? 1 : 0);
} }
// Clear ImGui inputs to avoid key/mouse leaks from one test to another // Clear ImGui inputs to avoid key/mouse leaks from one test to another
ImGuiTestEngine_ClearInput(engine); ImGuiTestEngine_ClearInput(engine);
ctx->FrameCount = parent_ctx ? parent_ctx->FrameCount : 0;
ctx->ErrorCounter = 0;
ctx->SetRef("");
ctx->SetInputMode(ImGuiInputSource_Mouse);
ctx->UiContext->NavInputSource = ImGuiInputSource_Keyboard;
ctx->Clipboard.clear();
// Backup entire IO and style. Allows tests modifying them and not caring about restoring state. // Backup entire IO and style. Allows tests modifying them and not caring about restoring state.
ImGuiTestContextUiContextBackup backup_ui_context; ImGuiTestContextUiContextBackup backup_ui_context;
backup_ui_context.Backup(*ctx->UiContext); backup_ui_context.Backup(*ctx->UiContext);
@ -1570,6 +1611,12 @@ void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_c
ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc; ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc;
ctx->ActiveFunc = ImGuiTestActiveFunc_TestFunc; ctx->ActiveFunc = ImGuiTestActiveFunc_TestFunc;
ctx->FirstGuiFrame = (test->GuiFunc != NULL) ? true : false; ctx->FirstGuiFrame = (test->GuiFunc != NULL) ? true : false;
ctx->FrameCount = parent_ctx ? parent_ctx->FrameCount : 0;
ctx->ErrorCounter = 0;
ctx->SetRef("");
ctx->SetInputMode(ImGuiInputSource_Mouse);
ctx->UiContext->NavInputSource = ImGuiInputSource_Keyboard;
ctx->Clipboard.clear();
// Warm up GUI // Warm up GUI
// - We need one mandatory frame running GuiFunc before running TestFunc // - We need one mandatory frame running GuiFunc before running TestFunc
@ -2055,7 +2102,7 @@ void ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...)
// Your custom assert code may optionally want to call this. // Your custom assert code may optionally want to call this.
void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line) void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line)
{ {
ImGuiTestEngine* engine = GImGuiTestEngine; if (ImGuiTestEngine* engine = GImGuiTestEngine)
if (ImGuiTestContext* ctx = engine->TestContext) if (ImGuiTestContext* ctx = engine->TestContext)
{ {
ctx->LogError("Assert: '%s'", expr); ctx->LogError("Assert: '%s'", expr);

View File

@ -7,7 +7,28 @@
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" // ImPool<>, ImRect, ImGuiItemStatusFlags, ImFormatString #include "imgui_internal.h" // ImPool<>, ImRect, ImGuiItemStatusFlags, ImFormatString
#include "imgui_te_utils.h" // ImFuncPtr
#if defined(__clang__)
#pragma clang diagnostic push
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif
#ifdef Status // X11 headers
#undef Status
#endif
//-----------------------------------------------------------------------------
// Function Pointers
//-----------------------------------------------------------------------------
#if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
#include <functional>
#define ImFuncPtr(FUNC_TYPE) std::function<FUNC_TYPE>
#else
#define ImFuncPtr(FUNC_TYPE) FUNC_TYPE*
#endif
#include "imgui_capture_tool.h" // ImGuiScreenCaptureFunc #include "imgui_capture_tool.h" // ImGuiScreenCaptureFunc
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -70,12 +91,12 @@ enum ImGuiTestVerboseLevel : int
// Test status (stored in ImGuiTest) // Test status (stored in ImGuiTest)
enum ImGuiTestStatus : int enum ImGuiTestStatus : int
{ {
ImGuiTestStatus_Unknown = -1, ImGuiTestStatus_Unknown = 0,
ImGuiTestStatus_Success = 0, ImGuiTestStatus_Success = 1,
ImGuiTestStatus_Queued = 1, ImGuiTestStatus_Queued = 2,
ImGuiTestStatus_Running = 2, ImGuiTestStatus_Running = 3,
ImGuiTestStatus_Error = 3, ImGuiTestStatus_Error = 4,
ImGuiTestStatus_Suspended = 4, ImGuiTestStatus_Suspended = 5,
ImGuiTestStatus_COUNT ImGuiTestStatus_COUNT
}; };
@ -168,6 +189,7 @@ IMGUI_API ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine);
// Macros: Register Test // Macros: Register Test
#define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME) ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__) #define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME) ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__)
IMGUI_API ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = NULL, int src_line = 0); // Prefer calling IM_REGISTER_TEST() IMGUI_API ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = NULL, int src_line = 0); // Prefer calling IM_REGISTER_TEST()
IMGUI_API void ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test);
// Functions: Main // Functions: Main
IMGUI_API void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0); IMGUI_API void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0);
@ -275,22 +297,20 @@ struct IMGUI_API ImGuiTestEngineIO
// Information about a given item or window, result of an ItemInfo() or WindowInfo() query // Information about a given item or window, result of an ItemInfo() or WindowInfo() query
struct ImGuiTestItemInfo struct ImGuiTestItemInfo
{ {
int RefCount : 8; // User can increment this if they want to hold on the result pointer across frames, otherwise the task will be GC-ed. ImGuiID ID = 0; // Item ID
char DebugLabel[32] = {}; // Shortened/truncated label for debugging and convenience purpose
ImGuiWindow* Window = NULL; // Item Window
unsigned int NavLayer : 1; // Nav layer of the item (ImGuiNavLayer) unsigned int NavLayer : 1; // Nav layer of the item (ImGuiNavLayer)
int Depth : 16; // Depth from requested parent id. 0 == ID is immediate child of requested parent id. int Depth : 16; // Depth from requested parent id. 0 == ID is immediate child of requested parent id.
int TimestampMain = -1; // Timestamp of main result (all fields) int TimestampMain; // Timestamp of main result (all fields)
int TimestampStatus = -1; // Timestamp of StatusFlags int TimestampStatus; // Timestamp of StatusFlags
ImGuiID ID = 0; // Item ID
ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack) ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack)
ImGuiWindow* Window = NULL; // Item Window
ImRect RectFull = ImRect(); // Item Rectangle ImRect RectFull = ImRect(); // Item Rectangle
ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission) ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission)
ImGuiItemFlags InFlags = 0; // Item flags ImGuiItemFlags InFlags = 0; // Item flags
ImGuiItemStatusFlags StatusFlags = 0; // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount) ImGuiItemStatusFlags StatusFlags = 0; // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount)
char DebugLabel[32] = {}; // Shortened label for debugging purpose
ImGuiTestItemInfo() { RefCount = 0; NavLayer = 0; Depth = 0; } ImGuiTestItemInfo() { memset(this, 0, sizeof(*this)); }
bool IsEmpty() const { return ID == 0; }
}; };
// Result of an GatherItems() query // Result of an GatherItems() query
@ -427,3 +447,9 @@ struct IMGUI_API ImGuiTestRunTask
}; };
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

View File

@ -138,7 +138,7 @@ struct ImGuiTestEngine
float OverrideDeltaTime = -1.0f; // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time. float OverrideDeltaTime = -1.0f; // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time.
ImVector<ImGuiTest*> TestsAll; ImVector<ImGuiTest*> TestsAll;
ImVector<ImGuiTestRunTask> TestsQueue; ImVector<ImGuiTestRunTask> TestsQueue;
ImGuiTestContext* TestContext = NULL; ImGuiTestContext* TestContext = NULL; // Running test context
ImVector<ImGuiTestInfoTask*>InfoTasks; ImVector<ImGuiTestInfoTask*>InfoTasks;
ImGuiTestGatherTask GatherTask; ImGuiTestGatherTask GatherTask;
ImGuiTestFindByLabelTask FindByLabelTask; ImGuiTestFindByLabelTask FindByLabelTask;

View File

@ -435,8 +435,13 @@ static bool RenderMultiSelectFilter(ImGuiPerfTool* perf, const char* filter_hint
ImGui::SetTooltip("Hold CTRL to invert other items.\nHold SHIFT to close popup instantly."); ImGui::SetTooltip("Hold CTRL to invert other items.\nHold SHIFT to close popup instantly.");
// Keep popup open for multiple actions if SHIFT is pressed. // Keep popup open for multiple actions if SHIFT is pressed.
#if IMGUI_VERSION_NUM >= 19094
if (!io.KeyShift)
ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
#else
if (!io.KeyShift) if (!io.KeyShift)
ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
#endif
if (ImGui::MenuItem("Show All")) if (ImGui::MenuItem("Show All"))
{ {
@ -1050,8 +1055,6 @@ void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
if (ImGui::IsWindowAppearing() && Empty()) if (ImGui::IsWindowAppearing() && Empty())
LoadCSV(); LoadCSV();
ImGuiStyle& style = ImGui::GetStyle();
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
// Render utility buttons // Render utility buttons
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
@ -1157,10 +1160,7 @@ void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
ImGui::SetTooltip("Generate a report and open it in the browser."); ImGui::SetTooltip("Generate a report and open it in the browser.");
// Align help button to the right. // Align help button to the right.
float help_pos = ImGui::GetWindowContentRegionMax().x - style.FramePadding.x * 2 - ImGui::CalcTextSize("(?)").x; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImMax(0.0f, ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("(?)").x));
if (help_pos > ImGui::GetCursorPosX())
ImGui::SetCursorPosX(help_pos);
ImGui::TextDisabled("(?)"); ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
{ {
@ -1263,6 +1263,7 @@ void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
{ {
#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
// Splitter between two following child windows is rendered first. // Splitter between two following child windows is rendered first.
ImGuiStyle& style = ImGui::GetStyle();
float plot_height = 0.0f; float plot_height = 0.0f;
float& table_height = _InfoTableHeight; float& table_height = _InfoTableHeight;
ImGui::Splitter("splitter", &plot_height, &table_height, ImGuiAxis_Y, +1); ImGui::Splitter("splitter", &plot_height, &table_height, ImGuiAxis_Y, +1);
@ -1843,7 +1844,7 @@ void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
ctx->WindowMove("", ImVec2(50, 50)); ctx->WindowMove("", ImVec2(50, 50));
ctx->WindowResize("", ImVec2(1400, 900)); ctx->WindowResize("", ImVec2(1400, 900));
#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
ImGuiWindow* plot_child = ctx->WindowInfo("plot")->Window; // "plot/PerfTool" prior to implot 2023/08/21 ImGuiWindow* plot_child = ctx->WindowInfo("plot").Window; // "plot/PerfTool" prior to implot 2023/08/21
IM_CHECK(plot_child != NULL); IM_CHECK(plot_child != NULL);
// Move legend to right side. // Move legend to right side.
@ -1910,7 +1911,7 @@ void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
ctx->ItemDoubleClick("splitter"); // Hide info table ctx->ItemDoubleClick("splitter"); // Hide info table
ImGuiWindow* plot_child = ctx->WindowInfo("plot")->Window; // "plot/PerfTool" prior to implot 2023/08/21 ImGuiWindow* plot_child = ctx->WindowInfo("plot").Window; // "plot/PerfTool" prior to implot 2023/08/21
IM_CHECK(plot_child != NULL); IM_CHECK(plot_child != NULL);
// Move legend to right side. // Move legend to right side.
@ -1928,7 +1929,7 @@ void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
// Take a screenshot. // Take a screenshot.
ImGuiCaptureArgs* args = ctx->CaptureArgs; ImGuiCaptureArgs* args = ctx->CaptureArgs;
args->InCaptureRect = plot_child->Rect(); args->InCaptureRect = plot_child->Rect();
ctx->CaptureAddWindow(window->Name); ctx->CaptureAddWindow(window->ID);
ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor); ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor);
ctx->ItemDragWithDelta("splitter", ImVec2(0, -180)); // Show info table ctx->ItemDragWithDelta("splitter", ImVec2(0, -180)); // Show info table
perf_report_image = args->InOutputFile; perf_report_image = args->InOutputFile;

View File

@ -177,7 +177,11 @@ static void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test)
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE); ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE);
break; break;
} }
#if IMGUI_VERSION_NUM >= 19072
ImGui::DebugTextUnformattedWithLocateItem(line_start, line_end);
#else
ImGui::TextUnformatted(line_start, line_end); ImGui::TextUnformatted(line_start, line_end);
#endif
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::PushID(line_no); ImGui::PushID(line_no);
@ -277,13 +281,16 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x);
//ImGui::Text("TESTS (%d)", engine->TestsAll.Size); //ImGui::Text("TESTS (%d)", engine->TestsAll.Size);
#if IMGUI_VERSION_NUM >= 18837 #if IMGUI_VERSION_NUM >= 19066
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow);
bool run = ImGui::Button("Run");
#elif IMGUI_VERSION_NUM >= 18837
bool run = ImGui::Button("Run") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R); bool run = ImGui::Button("Run") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R);
#else
bool = ImGui::Button("Run");
#endif
#if IMGUI_VERSION_NUM > 18963 #if IMGUI_VERSION_NUM > 18963
ImGui::SetItemTooltip("Ctrl+R"); ImGui::SetItemTooltip("Ctrl+R");
#endif
#else
bool run = ImGui::Button("Run");
#endif #endif
if (run) if (run)
{ {
@ -320,7 +327,7 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
ImGui::SameLine(); ImGui::SameLine();
const char* perflog_label = "Perf Tool"; const char* perflog_label = "Perf Tool";
float filter_width = ImGui::GetWindowContentRegionMax().x - ImGui::GetCursorPos().x; float filter_width = ImGui::GetContentRegionAvail().x;
float perf_stress_factor_width = (30 * dpi_scale); float perf_stress_factor_width = (30 * dpi_scale);
if (group == ImGuiTestGroup_Perfs) if (group == ImGuiTestGroup_Perfs)
{ {
@ -329,6 +336,9 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
} }
filter_width -= ImGui::CalcTextSize("(?)").x + style.ItemSpacing.x; filter_width -= ImGui::CalcTextSize("(?)").x + style.ItemSpacing.x;
ImGui::SetNextItemWidth(ImMax(20.0f, filter_width)); ImGui::SetNextItemWidth(ImMax(20.0f, filter_width));
#if IMGUI_VERSION_NUM >= 19066
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow);
#endif
ImGui::InputText("##filter", filter); ImGui::InputText("##filter", filter);
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextDisabled("(?)"); ImGui::TextDisabled("(?)");
@ -354,6 +364,7 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
int tests_completed = 0; int tests_completed = 0;
int tests_succeeded = 0; int tests_succeeded = 0;
int tests_failed = 0; int tests_failed = 0;
ImVector<ImGuiTest*> tests_to_remove;
if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit)) if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit))
{ {
ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupScrollFreeze(0, 1);
@ -515,6 +526,11 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
if (ImGui::MenuItem("Clear log", NULL, false, !test_log->IsEmpty())) if (ImGui::MenuItem("Clear log", NULL, false, !test_log->IsEmpty()))
test_log->Clear(); test_log->Clear();
// [DEBUG] Simple way to exercise ImGuiTestEngine_UnregisterTest()
//ImGui::Separator();
//if (ImGui::MenuItem("Remove test"))
// tests_to_remove.push_back(test);
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -571,6 +587,10 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
ImGui::EndTable(); ImGui::EndTable();
} }
// Process removal
for (ImGuiTest* test : tests_to_remove)
ImGuiTestEngine_UnregisterTest(e, test);
// Display test status recap (colors match per-test run button colors defined above) // Display test status recap (colors match per-test run button colors defined above)
{ {
ImVec4 status_color; ImVec4 status_color;
@ -750,22 +770,34 @@ static void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open)
"- Cinematic: Run tests with pauses between actions (for e.g. tutorials)." "- Cinematic: Run tests with pauses between actions (for e.g. tutorials)."
); );
ImGui::SameLine(); ImGui::SameLine();
//ImGui::Checkbox("Fast", &engine->IO.ConfigRunFast); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
//ImGui::SameLine(); ImGui::SameLine();
// (Would be good if we exposed horizontal layout mode..)
ImGui::Checkbox("Stop", &engine->IO.ConfigStopOnError); ImGui::Checkbox("Stop", &engine->IO.ConfigStopOnError);
ImGui::SetItemTooltip("Stop running tests when hitting an error."); ImGui::SetItemTooltip("When hitting an error:\n- Stop running other tests.");
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox("DbgBrk", &engine->IO.ConfigBreakOnError); ImGui::Checkbox("DbgBrk", &engine->IO.ConfigBreakOnError);
ImGui::SetItemTooltip("Break in debugger when hitting an error."); ImGui::SetItemTooltip("When hitting an error:\n- Break in debugger.");
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc); ImGui::Checkbox("Capture", &engine->IO.ConfigCaptureOnError);
ImGui::SetItemTooltip("Keep GUI function running after a test fails, or when a single queued test is finished.\nHold ESC to abort a running GUI function."); ImGui::SetItemTooltip("When hitting an error:\n- Capture screen to PNG. Right-click filename in Test Log to open.");
ImGui::SameLine();
ImGui::Checkbox("Refocus", &engine->IO.ConfigRestoreFocusAfterTests);
ImGui::SetItemTooltip("Restore focus back after running tests.");
ImGui::SameLine(); ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc);
ImGui::SetItemTooltip("After running single test or hitting an error:\n- Keep GUI function visible and interactive.\n- Hold ESC to abort a running GUI function.");
ImGui::SameLine();
bool keep_focus = !engine->IO.ConfigRestoreFocusAfterTests;
if (ImGui::Checkbox("KeepFocus", &keep_focus))
engine->IO.ConfigRestoreFocusAfterTests = !keep_focus;
ImGui::SetItemTooltip("After running tests:\n- Keep GUI current focus, instead of restoring focus to this window.");
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
ImGui::SetNextItemWidth(70 * dpi_scale); ImGui::SetNextItemWidth(70 * dpi_scale);
if (ImGui::BeginCombo("##Verbose", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None)) if (ImGui::BeginCombo("##Verbose", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None))
{ {
@ -775,6 +807,7 @@ static void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open)
ImGui::EndCombo(); ImGui::EndCombo();
} }
ImGui::SetItemTooltip("Verbose level."); ImGui::SetItemTooltip("Verbose level.");
//ImGui::PopStyleVar(); //ImGui::PopStyleVar();
ImGui::Separator(); ImGui::Separator();

View File

@ -26,6 +26,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> // stat() #include <sys/stat.h> // stat()
#endif #endif
#ifdef __APPLE__
#include <sys/sysctl.h>
#endif
#if defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) || defined(__MINGW32__) #if defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) || defined(__MINGW32__)
#include <pthread.h> // pthread_setname_np() #include <pthread.h> // pthread_setname_np()
@ -280,7 +283,7 @@ bool ImFileCreateDirectoryChain(const char* path, const char* path_end)
path_local[path_len] = 0; path_local[path_len] = 0;
#if defined(_WIN32) #if defined(_WIN32)
ImVector<ImWchar> buf; ImVector<wchar_t> buf;
#endif #endif
// Modification of passed file_name allows us to avoid extra temporary memory allocation. // Modification of passed file_name allows us to avoid extra temporary memory allocation.
// strtok() pokes \0 into places where slashes are, we create a directory using directory_name and restore slash. // strtok() pokes \0 into places where slashes are, we create a directory using directory_name and restore slash.
@ -291,10 +294,11 @@ bool ImFileCreateDirectoryChain(const char* path, const char* path_end)
*(token - 1) = IM_DIR_SEPARATOR; *(token - 1) = IM_DIR_SEPARATOR;
#if defined(_WIN32) #if defined(_WIN32)
// Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8. // Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8
const int filename_wsize = ImTextCountCharsFromUtf8(path_local, NULL) + 1; // We cannot use ImWchar, which can be 32bits if IMGUI_USE_WCHAR32 (and CreateDirectoryW require 16bits wchar)
int filename_wsize = MultiByteToWideChar(CP_UTF8, 0, path_local, -1, NULL, 0);
buf.resize(filename_wsize); buf.resize(filename_wsize);
ImTextStrFromUtf8(&buf[0], filename_wsize, path_local, NULL); MultiByteToWideChar(CP_UTF8, 0, path_local, -1, &buf[0], filename_wsize);
if (!::CreateDirectoryW((wchar_t*)&buf[0], NULL) && GetLastError() != ERROR_ALREADY_EXISTS) if (!::CreateDirectoryW((wchar_t*)&buf[0], NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
#else #else
if (mkdir(path_local, S_IRWXU) != 0 && errno != EEXIST) if (mkdir(path_local, S_IRWXU) != 0 && errno != EEXIST)
@ -785,10 +789,11 @@ const ImBuildInfo* ImBuildGetCompilationInfo()
// CPU // CPU
#if defined(_M_X86) || defined(_M_IX86) || defined(__i386) || defined(__i386__) || defined(_X86_) || defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__) #if defined(_M_X86) || defined(_M_IX86) || defined(__i386) || defined(__i386__) || defined(_X86_) || defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__)
build_info.Cpu = (sizeof(size_t) == 4) ? "X86" : "X64"; build_info.Cpu = (sizeof(size_t) == 4) ? "X86" : "X64";
#elif defined(__aarch64__) #elif defined(__aarch64__) || (defined(_M_ARM64) && defined(_WIN64))
build_info.Cpu = "ARM64"; build_info.Cpu = "ARM64";
#elif defined(__EMSCRIPTEN__)
build_info.Cpu = "WebAsm";
#else #else
#error
build_info.Cpu = (sizeof(size_t) == 4) ? "Unknown32" : "Unknown64"; build_info.Cpu = (sizeof(size_t) == 4) ? "Unknown32" : "Unknown64";
#endif #endif
@ -1000,6 +1005,27 @@ bool ImOsIsDebuggerPresent()
debugger_pid = atoi(tracer_pid); debugger_pid = atoi(tracer_pid);
} }
return debugger_pid != 0; return debugger_pid != 0;
#elif defined(__APPLE__)
// https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
info.kp_proc.p_flag = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
IM_ASSERT(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return (info.kp_proc.p_flag & P_TRACED) != 0;
#else #else
// FIXME // FIXME
return false; return false;
@ -1212,6 +1238,7 @@ void TableDiscardInstanceAndSettings(ImGuiID table_id)
// Helper to verify ImDrawData integrity of buffer count (broke before e.g. #6716) // Helper to verify ImDrawData integrity of buffer count (broke before e.g. #6716)
void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data) void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data)
{ {
#if IMGUI_VERSION_NUM >= 18973
int total_vtx_count = 0; int total_vtx_count = 0;
int total_idx_count = 0; int total_idx_count = 0;
for (ImDrawList* draw_list : draw_data->CmdLists) for (ImDrawList* draw_list : draw_data->CmdLists)
@ -1223,6 +1250,9 @@ void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data)
IM_UNUSED(total_idx_count); IM_UNUSED(total_idx_count);
IM_ASSERT(total_vtx_count == draw_data->TotalVtxCount); IM_ASSERT(total_vtx_count == draw_data->TotalVtxCount);
IM_ASSERT(total_idx_count == draw_data->TotalIdxCount); IM_ASSERT(total_idx_count == draw_data->TotalIdxCount);
#else
IM_UNUSED(draw_data);
#endif
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -13,17 +13,6 @@
#include "imgui.h" // ImGuiID, ImGuiKey #include "imgui.h" // ImGuiID, ImGuiKey
class Str; // Str<> from thirdparty/Str/Str.h class Str; // Str<> from thirdparty/Str/Str.h
//-----------------------------------------------------------------------------
// Function Pointers
//-----------------------------------------------------------------------------
#if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
#include <functional>
#define ImFuncPtr(FUNC_TYPE) std::function<FUNC_TYPE>
#else
#define ImFuncPtr(FUNC_TYPE) FUNC_TYPE*
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Hashing Helpers // Hashing Helpers
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

2996
libs/imguizmo/ImGuizmo.cpp Normal file

File diff suppressed because it is too large Load Diff

272
libs/imguizmo/ImGuizmo.h Normal file
View File

@ -0,0 +1,272 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -------------------------------------------------------------------------------------------
// History :
// 2019/11/03 View gizmo
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
// 2016/08/31 First version
//
// -------------------------------------------------------------------------------------------
// Future (no order):
//
// - Multi view
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
// -------------------------------------------------------------------------------------------
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
#endif
#pragma once
#ifdef USE_IMGUI_API
#include "imconfig.h"
#endif
#ifndef IMGUI_API
#define IMGUI_API
#endif
#ifndef IMGUIZMO_NAMESPACE
#define IMGUIZMO_NAMESPACE ImGuizmo
#endif
namespace IMGUIZMO_NAMESPACE
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
// call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame();
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
// globals are not shared between them.
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
// expose method to set imgui context
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing();
// return true if any gizmo is in moving state
IMGUI_API bool IsUsingAny();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
// Angles are in degrees (more suitable for human editing)
// example:
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
// ImGui::InputFloat3("Rt", matrixRotation, 3);
// ImGui::InputFloat3("Sc", matrixScale, 3);
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space
enum OPERATION
{
TRANSLATE_X = (1u << 0),
TRANSLATE_Y = (1u << 1),
TRANSLATE_Z = (1u << 2),
ROTATE_X = (1u << 3),
ROTATE_Y = (1u << 4),
ROTATE_Z = (1u << 5),
ROTATE_SCREEN = (1u << 6),
SCALE_X = (1u << 7),
SCALE_Y = (1u << 8),
SCALE_Z = (1u << 9),
BOUNDS = (1u << 10),
SCALE_XU = (1u << 11),
SCALE_YU = (1u << 12),
SCALE_ZU = (1u << 13),
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
};
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
enum MODE
{
LOCAL,
WORLD
};
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
// use this version if you did not call Manipulate before and you are just using ViewManipulate
IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetID(int id);
// return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
// Allow axis to flip
// When true (default), the guizmo axis flip for better visibility
// When false, they always stay along the positive world/local axis
IMGUI_API void AllowAxisFlip(bool value);
// Configure the limit where axis are hidden
IMGUI_API void SetAxisLimit(float value);
// Configure the limit where planes are hiden
IMGUI_API void SetPlaneLimit(float value);
enum COLOR
{
DIRECTION_X, // directionColor[0]
DIRECTION_Y, // directionColor[1]
DIRECTION_Z, // directionColor[2]
PLANE_X, // planeColor[0]
PLANE_Y, // planeColor[1]
PLANE_Z, // planeColor[2]
SELECTION, // selectionColor
INACTIVE, // inactiveColor
TRANSLATION_LINE, // translationLineColor
SCALE_LINE,
ROTATION_USING_BORDER,
ROTATION_USING_FILL,
HATCHED_AXIS_LINES,
TEXT,
TEXT_SHADOW,
COUNT
};
struct Style
{
IMGUI_API Style();
float TranslationLineThickness; // Thickness of lines for translation gizmo
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
float RotationLineThickness; // Thickness of lines for rotation gizmo
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
float ScaleLineThickness; // Thickness of lines for scale gizmo
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
float HatchedAxisLineThickness; // Thickness of hatched axis lines
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
ImVec4 Colors[COLOR::COUNT];
};
IMGUI_API Style& GetStyle();
}

21
libs/imguizmo/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Cedric Guillemet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

21
libs/implot/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Evan Pezent
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5885
libs/implot/implot.cpp Normal file

File diff suppressed because it is too large Load Diff

1297
libs/implot/implot.h Normal file

File diff suppressed because it is too large Load Diff

2479
libs/implot/implot_demo.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2808
libs/implot/implot_items.cpp Normal file

File diff suppressed because it is too large Load Diff

21
libs/node_editor/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Michał Cichoń
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,890 @@
// Crude implementation of JSON value object and parser.
//
// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
# include "crude_json.h"
# include <iomanip>
# include <limits>
# include <cstdlib>
# include <clocale>
# include <cmath>
# include <cstring>
# if CRUDE_JSON_IO
# include <stdio.h>
# include <memory>
# endif
namespace crude_json {
value::value(value&& other)
: m_Type(other.m_Type)
{
switch (m_Type)
{
case type_t::object: construct(m_Storage, std::move( *object_ptr(other.m_Storage))); break;
case type_t::array: construct(m_Storage, std::move( *array_ptr(other.m_Storage))); break;
case type_t::string: construct(m_Storage, std::move( *string_ptr(other.m_Storage))); break;
case type_t::boolean: construct(m_Storage, std::move(*boolean_ptr(other.m_Storage))); break;
case type_t::number: construct(m_Storage, std::move( *number_ptr(other.m_Storage))); break;
default: break;
}
destruct(other.m_Storage, other.m_Type);
other.m_Type = type_t::null;
}
value::value(const value& other)
: m_Type(other.m_Type)
{
switch (m_Type)
{
case type_t::object: construct(m_Storage, *object_ptr(other.m_Storage)); break;
case type_t::array: construct(m_Storage, *array_ptr(other.m_Storage)); break;
case type_t::string: construct(m_Storage, *string_ptr(other.m_Storage)); break;
case type_t::boolean: construct(m_Storage, *boolean_ptr(other.m_Storage)); break;
case type_t::number: construct(m_Storage, *number_ptr(other.m_Storage)); break;
default: break;
}
}
value& value::operator[](size_t index)
{
if (is_null())
m_Type = construct(m_Storage, type_t::array);
if (is_array())
{
auto& v = *array_ptr(m_Storage);
if (index >= v.size())
v.insert(v.end(), index - v.size() + 1, value());
return v[index];
}
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
const value& value::operator[](size_t index) const
{
if (is_array())
return (*array_ptr(m_Storage))[index];
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
value& value::operator[](const string& key)
{
if (is_null())
m_Type = construct(m_Storage, type_t::object);
if (is_object())
return (*object_ptr(m_Storage))[key];
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
const value& value::operator[](const string& key) const
{
if (is_object())
{
auto& o = *object_ptr(m_Storage);
auto it = o.find(key);
CRUDE_ASSERT(it != o.end());
return it->second;
}
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
bool value::contains(const string& key) const
{
if (is_object())
{
auto& o = *object_ptr(m_Storage);
auto it = o.find(key);
return it != o.end();
}
return false;
}
void value::push_back(const value& value)
{
if (is_null())
m_Type = construct(m_Storage, type_t::array);
if (is_array())
{
auto& v = *array_ptr(m_Storage);
v.push_back(value);
}
else
{
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
}
void value::push_back(value&& value)
{
if (is_null())
m_Type = construct(m_Storage, type_t::array);
if (is_array())
{
auto& v = *array_ptr(m_Storage);
v.push_back(std::move(value));
}
else
{
CRUDE_ASSERT(false && "operator[] on unsupported type");
std::terminate();
}
}
size_t value::erase(const string& key)
{
if (!is_object())
return 0;
auto& o = *object_ptr(m_Storage);
auto it = o.find(key);
if (it == o.end())
return 0;
o.erase(it);
return 1;
}
void value::swap(value& other)
{
using std::swap;
if (m_Type == other.m_Type)
{
switch (m_Type)
{
case type_t::object: swap(*object_ptr(m_Storage), *object_ptr(other.m_Storage)); break;
case type_t::array: swap(*array_ptr(m_Storage), *array_ptr(other.m_Storage)); break;
case type_t::string: swap(*string_ptr(m_Storage), *string_ptr(other.m_Storage)); break;
case type_t::boolean: swap(*boolean_ptr(m_Storage), *boolean_ptr(other.m_Storage)); break;
case type_t::number: swap(*number_ptr(m_Storage), *number_ptr(other.m_Storage)); break;
default: break;
}
}
else
{
value tmp(std::move(other));
other.~value();
new (&other) value(std::move(*this));
this->~value();
new (this) value(std::move(tmp));
}
}
string value::dump(const int indent, const char indent_char) const
{
dump_context_t context(indent, indent_char);
context.out.precision(std::numeric_limits<double>::max_digits10 + 1);
context.out << std::defaultfloat;
dump(context, 0);
return context.out.str();
}
void value::dump_context_t::write_indent(int level)
{
if (indent <= 0 || level == 0)
return;
out.fill(indent_char);
out.width(indent * level);
out << indent_char;
out.width(0);
}
void value::dump_context_t::write_separator()
{
if (indent < 0)
return;
out.put(' ');
}
void value::dump_context_t::write_newline()
{
if (indent < 0)
return;
out.put('\n');
}
void value::dump(dump_context_t& context, int level) const
{
context.write_indent(level);
switch (m_Type)
{
case type_t::null:
context.out << "null";
break;
case type_t::object:
context.out << '{';
{
context.write_newline();
bool first = true;
for (auto& entry : *object_ptr(m_Storage))
{
if (!first) { context.out << ','; context.write_newline(); } else first = false;
context.write_indent(level + 1);
context.out << '\"' << entry.first << "\":";
if (!entry.second.is_structured())
{
context.write_separator();
entry.second.dump(context, 0);
}
else
{
context.write_newline();
entry.second.dump(context, level + 1);
}
}
if (!first)
context.write_newline();
}
context.write_indent(level);
context.out << '}';
break;
case type_t::array:
context.out << '[';
{
context.write_newline();
bool first = true;
for (auto& entry : *array_ptr(m_Storage))
{
if (!first) { context.out << ','; context.write_newline(); } else first = false;
if (!entry.is_structured())
{
context.write_indent(level + 1);
entry.dump(context, 0);
}
else
{
entry.dump(context, level + 1);
}
}
if (!first)
context.write_newline();
}
context.write_indent(level);
context.out << ']';
break;
case type_t::string:
context.out << '\"';
if (string_ptr(m_Storage)->find_first_of("\"\\/\b\f\n\r") != string::npos || string_ptr(m_Storage)->find('\0') != string::npos)
{
for (auto c : *string_ptr(m_Storage))
{
if (c == '\"') context.out << "\\\"";
else if (c == '\\') context.out << "\\\\";
else if (c == '/') context.out << "\\/";
else if (c == '\b') context.out << "\\b";
else if (c == '\f') context.out << "\\f";
else if (c == '\n') context.out << "\\n";
else if (c == '\r') context.out << "\\r";
else if (c == '\t') context.out << "\\t";
else if (c == 0) context.out << "\\u0000";
else context.out << c;
}
}
else
context.out << *string_ptr(m_Storage);
context.out << '\"';
break;
case type_t::boolean:
if (*boolean_ptr(m_Storage))
context.out << "true";
else
context.out << "false";
break;
case type_t::number:
context.out << *number_ptr(m_Storage);
break;
default:
break;
}
}
struct value::parser
{
parser(const char* begin, const char* end)
: m_Cursor(begin)
, m_End(end)
{
}
value parse()
{
value v;
// Switch to C locale to make strtod and strtol work as expected
auto previous_locale = std::setlocale(LC_NUMERIC, "C");
// Accept single value only when end of the stream is reached.
if (!accept_element(v) || !eof())
v = value(type_t::discarded);
if (previous_locale && strcmp(previous_locale, "C") != 0)
std::setlocale(LC_NUMERIC, previous_locale);
return v;
}
private:
struct cursor_state
{
cursor_state(parser* p)
: m_Owner(p)
, m_LastCursor(p->m_Cursor)
{
}
void reset()
{
m_Owner->m_Cursor = m_LastCursor;
}
bool operator()(bool accept)
{
if (!accept)
reset();
else
m_LastCursor = m_Owner->m_Cursor;
return accept;
}
private:
parser* m_Owner;
const char* m_LastCursor;
};
cursor_state state()
{
return cursor_state(this);
}
bool accept_value(value& result)
{
return accept_object(result)
|| accept_array(result)
|| accept_string(result)
|| accept_number(result)
|| accept_boolean(result)
|| accept_null(result);
}
bool accept_object(value& result)
{
auto s = state();
object o;
if (s(accept('{') && accept_ws() && accept('}')))
{
result = o;
return true;
}
else if (s(accept('{') && accept_members(o) && accept('}')))
{
result = std::move(o);
return true;
}
return false;
}
bool accept_members(object& o)
{
if (!accept_member(o))
return false;
while (true)
{
auto s = state();
if (!s(accept(',') && accept_member(o)))
break;
}
return true;
}
bool accept_member(object& o)
{
auto s = state();
value key;
value v;
if (s(accept_ws() && accept_string(key) && accept_ws() && accept(':') && accept_element(v)))
{
o.emplace(std::move(key.get<string>()), std::move(v));
return true;
}
return false;
}
bool accept_array(value& result)
{
auto s = state();
if (s(accept('[') && accept_ws() && accept(']')))
{
result = array();
return true;
}
array a;
if (s(accept('[') && accept_elements(a) && accept(']')))
{
result = std::move(a);
return true;
}
return false;
}
bool accept_elements(array& a)
{
value v;
if (!accept_element(v))
return false;
a.emplace_back(std::move(v));
while (true)
{
auto s = state();
v = nullptr;
if (!s(accept(',') && accept_element(v)))
break;
a.emplace_back(std::move(v));
}
return true;
}
bool accept_element(value& result)
{
auto s = state();
return s(accept_ws() && accept_value(result) && accept_ws());
}
bool accept_string(value& result)
{
auto s = state();
string v;
if (s(accept('\"') && accept_characters(v) && accept('\"')))
{
result = std::move(v);
return true;
}
else
return false;
}
bool accept_characters(string& result)
{
int c;
while (accept_character(c))
{
CRUDE_ASSERT(c < 128); // #todo: convert characters > 127 to UTF-8
result.push_back(static_cast<char>(c));
}
return true;
}
bool accept_character(int& c)
{
auto s = state();
if (accept('\\'))
{
return accept_escape(c);
}
else if (expect('\"'))
return false;
// #todo: Handle UTF-8 sequences.
return s((c = peek()) >= 0) && advance();
}
bool accept_escape(int& c)
{
if (accept('\"')) { c = '\"'; return true; }
if (accept('\\')) { c = '\\'; return true; }
if (accept('/')) { c = '/'; return true; }
if (accept('b')) { c = '\b'; return true; }
if (accept('f')) { c = '\f'; return true; }
if (accept('n')) { c = '\n'; return true; }
if (accept('r')) { c = '\r'; return true; }
if (accept('t')) { c = '\t'; return true; }
auto s = state();
string hex;
hex.reserve(4);
if (s(accept('u') && accept_hex(hex) && accept_hex(hex) && accept_hex(hex) && accept_hex(hex)))
{
char* end = nullptr;
auto v = std::strtol(hex.c_str(), &end, 16);
if (end != hex.c_str() + hex.size())
return false;
c = static_cast<int>(v);
return true;
}
return false;
}
bool accept_hex(string& result)
{
if (accept_digit(result))
return true;
auto c = peek();
if ((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
{
advance();
result.push_back(static_cast<char>(c));
return true;
}
return false;
}
bool accept_number(value& result)
{
auto s = state();
string n;
if (s(accept_int(n) && accept_frac(n) && accept_exp(n)))
{
char* end = nullptr;
auto v = std::strtod(n.c_str(), &end);
if (end != n.c_str() + n.size())
return false;
if (v != 0 && !std::isnormal(v))
return false;
result = v;
return true;
}
return false;
}
bool accept_int(string& result)
{
auto s = state();
string part;
if (s(accept_onenine(part) && accept_digits(part)))
{
result += std::move(part);
return true;
}
part.resize(0);
if (accept_digit(part))
{
result += std::move(part);
return true;
}
part.resize(0);
if (s(accept('-') && accept_onenine(part) && accept_digits(part)))
{
result += '-';
result += std::move(part);
return true;
}
part.resize(0);
if (s(accept('-') && accept_digit(part)))
{
result += '-';
result += std::move(part);
return true;
}
return false;
}
bool accept_digits(string& result)
{
string part;
if (!accept_digit(part))
return false;
while (accept_digit(part))
;
result += std::move(part);
return true;
}
bool accept_digit(string& result)
{
if (accept('0'))
{
result.push_back('0');
return true;
}
else if (accept_onenine(result))
return true;
return false;
}
bool accept_onenine(string& result)
{
auto c = peek();
if (c >= '1' && c <= '9')
{
result.push_back(static_cast<char>(c));
return advance();
}
return false;
}
bool accept_frac(string& result)
{
auto s = state();
string part;
if (s(accept('.') && accept_digits(part)))
{
result += '.';
result += std::move(part);
}
return true;
}
bool accept_exp(string& result)
{
auto s = state();
string part;
if (s(accept('e') && accept_sign(part) && accept_digits(part)))
{
result += 'e';
result += std::move(part);
return true;
}
part.resize(0);
if (s(accept('E') && accept_sign(part) && accept_digits(part)))
{
result += 'E';
result += std::move(part);
}
return true;
}
bool accept_sign(string& result)
{
if (accept('+'))
result.push_back('+');
else if (accept('-'))
result.push_back('-');
return true;
}
bool accept_ws()
{
while (expect('\x09') || expect('\x0A') || expect('\x0D') || expect('\x20'))
advance();
return true;
}
bool accept_boolean(value& result)
{
if (accept("true"))
{
result = true;
return true;
}
else if (accept("false"))
{
result = false;
return true;
}
return false;
}
bool accept_null(value& result)
{
if (accept("null"))
{
result = nullptr;
return true;
}
return false;
}
bool accept(char c)
{
if (expect(c))
return advance();
else
return false;
}
bool accept(const char* str)
{
auto last = m_Cursor;
while (*str)
{
if (eof() || *str != *m_Cursor)
{
m_Cursor = last;
return false;
}
advance();
++str;
}
return true;
}
int peek() const
{
if (!eof())
return *m_Cursor;
else
return -1;
}
bool expect(char c)
{
return peek() == c;
}
bool advance(int count = 1)
{
if (m_Cursor + count > m_End)
{
m_Cursor = m_End;
return false;
}
m_Cursor += count;
return true;
}
bool eof() const
{
return m_Cursor == m_End;
}
const char* m_Cursor;
const char* m_End;
};
value value::parse(const string& data)
{
auto p = parser(data.c_str(), data.c_str() + data.size());
auto v = p.parse();
return v;
}
# if CRUDE_JSON_IO
std::pair<value, bool> value::load(const string& path)
{
// Modern C++, so beautiful...
std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
FILE* handle = nullptr;
if (fopen_s(&handle, path.c_str(), "rb") != 0)
return {value{}, false};
file.reset(handle);
# else
file.reset(fopen(path.c_str(), "rb"));
# endif
if (!file)
return {value{}, false};
fseek(file.get(), 0, SEEK_END);
auto size = static_cast<size_t>(ftell(file.get()));
fseek(file.get(), 0, SEEK_SET);
string data;
data.resize(size);
if (fread(const_cast<char*>(data.data()), size, 1, file.get()) != 1)
return {value{}, false};
return {parse(data), true};
}
bool value::save(const string& path, const int indent, const char indent_char) const
{
// Modern C++, so beautiful...
std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
FILE* handle = nullptr;
if (fopen_s(&handle, path.c_str(), "wb") != 0)
return false;
file.reset(handle);
# else
file.reset(fopen(path.c_str(), "wb"));
# endif
if (!file)
return false;
auto data = dump(indent, indent_char);
if (fwrite(data.data(), data.size(), 1, file.get()) != 1)
return false;
return true;
}
# endif
} // namespace crude_json

View File

@ -0,0 +1,250 @@
// Crude implementation of JSON value object and parser.
//
// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
# ifndef __CRUDE_JSON_H__
# define __CRUDE_JSON_H__
# pragma once
# include <type_traits>
# include <string>
# include <vector>
# include <map>
# include <cstddef>
# include <algorithm>
# include <sstream>
# ifndef CRUDE_ASSERT
# include <cassert>
# define CRUDE_ASSERT(expr) assert(expr)
# endif
# ifndef CRUDE_JSON_IO
# define CRUDE_JSON_IO 1
# endif
namespace crude_json {
struct value;
using string = std::string;
using object = std::map<string, value>;
using array = std::vector<value>;
using number = double;
using boolean = bool;
using null = std::nullptr_t;
enum class type_t
{
null,
object,
array,
string,
boolean,
number,
discarded
};
struct value
{
value(type_t type = type_t::null): m_Type(construct(m_Storage, type)) {}
value(value&& other);
value(const value& other);
value( null) : m_Type(construct(m_Storage, null())) {}
value( object&& v): m_Type(construct(m_Storage, std::move(v))) {}
value(const object& v): m_Type(construct(m_Storage, v)) {}
value( array&& v): m_Type(construct(m_Storage, std::move(v))) {}
value(const array& v): m_Type(construct(m_Storage, v)) {}
value( string&& v): m_Type(construct(m_Storage, std::move(v))) {}
value(const string& v): m_Type(construct(m_Storage, v)) {}
value(const char* v): m_Type(construct(m_Storage, v)) {}
value( boolean v): m_Type(construct(m_Storage, v)) {}
value( number v): m_Type(construct(m_Storage, v)) {}
~value() { destruct(m_Storage, m_Type); }
value& operator=(value&& other) { if (this != &other) { value(std::move(other)).swap(*this); } return *this; }
value& operator=(const value& other) { if (this != &other) { value( other).swap(*this); } return *this; }
value& operator=( null) { auto other = value( ); swap(other); return *this; }
value& operator=( object&& v) { auto other = value(std::move(v)); swap(other); return *this; }
value& operator=(const object& v) { auto other = value( v); swap(other); return *this; }
value& operator=( array&& v) { auto other = value(std::move(v)); swap(other); return *this; }
value& operator=(const array& v) { auto other = value( v); swap(other); return *this; }
value& operator=( string&& v) { auto other = value(std::move(v)); swap(other); return *this; }
value& operator=(const string& v) { auto other = value( v); swap(other); return *this; }
value& operator=(const char* v) { auto other = value( v); swap(other); return *this; }
value& operator=( boolean v) { auto other = value( v); swap(other); return *this; }
value& operator=( number v) { auto other = value( v); swap(other); return *this; }
type_t type() const { return m_Type; }
operator type_t() const { return m_Type; }
value& operator[](size_t index);
const value& operator[](size_t index) const;
value& operator[](const string& key);
const value& operator[](const string& key) const;
bool contains(const string& key) const;
void push_back(const value& value);
void push_back(value&& value);
size_t erase(const string& key);
bool is_primitive() const { return is_string() || is_number() || is_boolean() || is_null(); }
bool is_structured() const { return is_object() || is_array(); }
bool is_null() const { return m_Type == type_t::null; }
bool is_object() const { return m_Type == type_t::object; }
bool is_array() const { return m_Type == type_t::array; }
bool is_string() const { return m_Type == type_t::string; }
bool is_boolean() const { return m_Type == type_t::boolean; }
bool is_number() const { return m_Type == type_t::number; }
bool is_discarded() const { return m_Type == type_t::discarded; }
template <typename T> const T& get() const;
template <typename T> T& get();
template <typename T> const T* get_ptr() const;
template <typename T> T* get_ptr();
string dump(const int indent = -1, const char indent_char = ' ') const;
void swap(value& other);
inline friend void swap(value& lhs, value& rhs) { lhs.swap(rhs); }
// Returns discarded value for invalid inputs.
static value parse(const string& data);
# if CRUDE_JSON_IO
static std::pair<value, bool> load(const string& path);
bool save(const string& path, const int indent = -1, const char indent_char = ' ') const;
# endif
private:
struct parser;
// VS2015: std::max() is not constexpr yet.
# define CRUDE_MAX2(a, b) ((a) < (b) ? (b) : (a))
# define CRUDE_MAX3(a, b, c) CRUDE_MAX2(CRUDE_MAX2(a, b), c)
# define CRUDE_MAX4(a, b, c, d) CRUDE_MAX2(CRUDE_MAX3(a, b, c), d)
# define CRUDE_MAX5(a, b, c, d, e) CRUDE_MAX2(CRUDE_MAX4(a, b, c, d), e)
enum
{
max_size = CRUDE_MAX5( sizeof(string), sizeof(object), sizeof(array), sizeof(number), sizeof(boolean)),
max_align = CRUDE_MAX5(alignof(string), alignof(object), alignof(array), alignof(number), alignof(boolean))
};
# undef CRUDE_MAX5
# undef CRUDE_MAX4
# undef CRUDE_MAX3
# undef CRUDE_MAX2
using storage_t = std::aligned_storage<max_size, max_align>::type;
static object* object_ptr( storage_t& storage) { return reinterpret_cast< object*>(&storage); }
static const object* object_ptr(const storage_t& storage) { return reinterpret_cast<const object*>(&storage); }
static array* array_ptr( storage_t& storage) { return reinterpret_cast< array*>(&storage); }
static const array* array_ptr(const storage_t& storage) { return reinterpret_cast<const array*>(&storage); }
static string* string_ptr( storage_t& storage) { return reinterpret_cast< string*>(&storage); }
static const string* string_ptr(const storage_t& storage) { return reinterpret_cast<const string*>(&storage); }
static boolean* boolean_ptr( storage_t& storage) { return reinterpret_cast< boolean*>(&storage); }
static const boolean* boolean_ptr(const storage_t& storage) { return reinterpret_cast<const boolean*>(&storage); }
static number* number_ptr( storage_t& storage) { return reinterpret_cast< number*>(&storage); }
static const number* number_ptr(const storage_t& storage) { return reinterpret_cast<const number*>(&storage); }
static type_t construct(storage_t& storage, type_t type)
{
switch (type)
{
case type_t::object: new (&storage) object(); break;
case type_t::array: new (&storage) array(); break;
case type_t::string: new (&storage) string(); break;
case type_t::boolean: new (&storage) boolean(); break;
case type_t::number: new (&storage) number(); break;
default: break;
}
return type;
}
static type_t construct(storage_t& storage, null) { (void)storage; return type_t::null; }
static type_t construct(storage_t& storage, object&& value) { new (&storage) object(std::forward<object>(value)); return type_t::object; }
static type_t construct(storage_t& storage, const object& value) { new (&storage) object(value); return type_t::object; }
static type_t construct(storage_t& storage, array&& value) { new (&storage) array(std::forward<array>(value)); return type_t::array; }
static type_t construct(storage_t& storage, const array& value) { new (&storage) array(value); return type_t::array; }
static type_t construct(storage_t& storage, string&& value) { new (&storage) string(std::forward<string>(value)); return type_t::string; }
static type_t construct(storage_t& storage, const string& value) { new (&storage) string(value); return type_t::string; }
static type_t construct(storage_t& storage, const char* value) { new (&storage) string(value); return type_t::string; }
static type_t construct(storage_t& storage, boolean value) { new (&storage) boolean(value); return type_t::boolean; }
static type_t construct(storage_t& storage, number value) { new (&storage) number(value); return type_t::number; }
static void destruct(storage_t& storage, type_t type)
{
switch (type)
{
case type_t::object: object_ptr(storage)->~object(); break;
case type_t::array: array_ptr(storage)->~array(); break;
case type_t::string: string_ptr(storage)->~string(); break;
default: break;
}
}
struct dump_context_t
{
std::ostringstream out;
const int indent = -1;
const char indent_char = ' ';
// VS2015: Aggregate initialization isn't a thing yet.
dump_context_t(const int indent, const char indent_char)
: indent(indent)
, indent_char(indent_char)
{
}
void write_indent(int level);
void write_separator();
void write_newline();
};
void dump(dump_context_t& context, int level) const;
storage_t m_Storage;
type_t m_Type;
};
template <> inline const object& value::get<object>() const { CRUDE_ASSERT(m_Type == type_t::object); return *object_ptr(m_Storage); }
template <> inline const array& value::get<array>() const { CRUDE_ASSERT(m_Type == type_t::array); return *array_ptr(m_Storage); }
template <> inline const string& value::get<string>() const { CRUDE_ASSERT(m_Type == type_t::string); return *string_ptr(m_Storage); }
template <> inline const boolean& value::get<boolean>() const { CRUDE_ASSERT(m_Type == type_t::boolean); return *boolean_ptr(m_Storage); }
template <> inline const number& value::get<number>() const { CRUDE_ASSERT(m_Type == type_t::number); return *number_ptr(m_Storage); }
template <> inline object& value::get<object>() { CRUDE_ASSERT(m_Type == type_t::object); return *object_ptr(m_Storage); }
template <> inline array& value::get<array>() { CRUDE_ASSERT(m_Type == type_t::array); return *array_ptr(m_Storage); }
template <> inline string& value::get<string>() { CRUDE_ASSERT(m_Type == type_t::string); return *string_ptr(m_Storage); }
template <> inline boolean& value::get<boolean>() { CRUDE_ASSERT(m_Type == type_t::boolean); return *boolean_ptr(m_Storage); }
template <> inline number& value::get<number>() { CRUDE_ASSERT(m_Type == type_t::number); return *number_ptr(m_Storage); }
template <> inline const object* value::get_ptr<object>() const { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
template <> inline const array* value::get_ptr<array>() const { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
template <> inline const string* value::get_ptr<string>() const { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
template <> inline const boolean* value::get_ptr<boolean>() const { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
template <> inline const number* value::get_ptr<number>() const { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
template <> inline object* value::get_ptr<object>() { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
template <> inline array* value::get_ptr<array>() { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
template <> inline string* value::get_ptr<string>() { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
template <> inline boolean* value::get_ptr<boolean>() { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
template <> inline number* value::get_ptr<number>() { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
} // namespace crude_json
# endif // __CRUDE_JSON_H__

View File

@ -0,0 +1,144 @@
//------------------------------------------------------------------------------
// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_BEZIER_MATH_H__
# define __IMGUI_BEZIER_MATH_H__
# pragma once
//------------------------------------------------------------------------------
# include "imgui_extra_math.h"
//------------------------------------------------------------------------------
template <typename T>
struct ImCubicBezierPointsT
{
T P0;
T P1;
T P2;
T P3;
};
using ImCubicBezierPoints = ImCubicBezierPointsT<ImVec2>;
//------------------------------------------------------------------------------
// Low-level Bezier curve sampling.
template <typename T> inline T ImLinearBezier(const T& p0, const T& p1, float t);
template <typename T> inline T ImLinearBezierDt(const T& p0, const T& p1, float t);
template <typename T> inline T ImQuadraticBezier(const T& p0, const T& p1, const T& p2, float t);
template <typename T> inline T ImQuadraticBezierDt(const T& p0, const T& p1, const T& p2, float t);
template <typename T> inline T ImCubicBezier(const T& p0, const T& p1, const T& p2, const T& p3, float t);
template <typename T> inline T ImCubicBezierDt(const T& p0, const T& p1, const T& p2, const T& p3, float t);
// High-level Bezier sampling, automatically collapse to lower level Bezier curves if control points overlap.
template <typename T> inline T ImCubicBezierSample(const T& p0, const T& p1, const T& p2, const T& p3, float t);
template <typename T> inline T ImCubicBezierSample(const ImCubicBezierPointsT<T>& curve, float t);
template <typename T> inline T ImCubicBezierTangent(const T& p0, const T& p1, const T& p2, const T& p3, float t);
template <typename T> inline T ImCubicBezierTangent(const ImCubicBezierPointsT<T>& curve, float t);
// Calculate approximate length of Cubic Bezier curve.
template <typename T> inline float ImCubicBezierLength(const T& p0, const T& p1, const T& p2, const T& p3);
template <typename T> inline float ImCubicBezierLength(const ImCubicBezierPointsT<T>& curve);
// Splits Cubic Bezier curve into two curves.
template <typename T>
struct ImCubicBezierSplitResultT
{
ImCubicBezierPointsT<T> Left;
ImCubicBezierPointsT<T> Right;
};
using ImCubicBezierSplitResult = ImCubicBezierSplitResultT<ImVec2>;
template <typename T> inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const T& p0, const T& p1, const T& p2, const T& p3, float t);
template <typename T> inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const ImCubicBezierPointsT<T>& curve, float t);
// Returns bounding rectangle of Cubic Bezier curve.
inline ImRect ImCubicBezierBoundingRect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3);
inline ImRect ImCubicBezierBoundingRect(const ImCubicBezierPoints& curve);
// Project point on Cubic Bezier curve.
struct ImProjectResult
{
ImVec2 Point; // Point on curve
float Time; // [0 - 1]
float Distance; // Distance to curve
};
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const int subdivisions = 100);
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImCubicBezierPoints& curve, const int subdivisions = 100);
// Calculate intersection between line and a Cubic Bezier curve.
struct ImCubicBezierIntersectResult
{
int Count;
ImVec2 Points[3];
};
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& a0, const ImVec2& a1);
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImCubicBezierPoints& curve, const ImLine& line);
// Adaptive Cubic Bezier subdivision.
enum ImCubicBezierSubdivideFlags
{
ImCubicBezierSubdivide_None = 0,
ImCubicBezierSubdivide_SkipFirst = 1
};
struct ImCubicBezierSubdivideSample
{
ImVec2 Point;
ImVec2 Tangent;
};
using ImCubicBezierSubdivideCallback = void (*)(const ImCubicBezierSubdivideSample& p, void* user_pointer);
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
// F has signature void(const ImCubicBezierSubdivideSample& p)
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImCubicBezierPoints& curve, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
// Fixed step Cubic Bezier subdivision.
struct ImCubicBezierFixedStepSample
{
float T;
float Length;
ImVec2 Point;
bool BreakSearch;
};
using ImCubicBezierFixedStepCallback = void (*)(ImCubicBezierFixedStepSample& sample, void* user_pointer);
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
// F has signature void(const ImCubicBezierFixedStepSample& p)
template <typename F> inline void ImCubicBezierFixedStep(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
template <typename F> inline void ImCubicBezierFixedStep(F& callback, const ImCubicBezierPoints& curve, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
//------------------------------------------------------------------------------
# include "imgui_bezier_math.inl"
//------------------------------------------------------------------------------
# endif // __IMGUI_BEZIER_MATH_H__

View File

@ -0,0 +1,675 @@
//------------------------------------------------------------------------------
// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_BEZIER_MATH_INL__
# define __IMGUI_BEZIER_MATH_INL__
# pragma once
//------------------------------------------------------------------------------
# include "imgui_bezier_math.h"
# include <map> // used in ImCubicBezierFixedStep
//------------------------------------------------------------------------------
template <typename T>
inline T ImLinearBezier(const T& p0, const T& p1, float t)
{
return p0 + t * (p1 - p0);
}
template <typename T>
inline T ImLinearBezierDt(const T& p0, const T& p1, float t)
{
IM_UNUSED(t);
return p1 - p0;
}
template <typename T>
inline T ImQuadraticBezier(const T& p0, const T& p1, const T& p2, float t)
{
const auto a = 1 - t;
return a * a * p0 + 2 * t * a * p1 + t * t * p2;
}
template <typename T>
inline T ImQuadraticBezierDt(const T& p0, const T& p1, const T& p2, float t)
{
return 2 * (1 - t) * (p1 - p0) + 2 * t * (p2 - p1);
}
template <typename T>
inline T ImCubicBezier(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
const auto a = 1 - t;
const auto b = a * a * a;
const auto c = t * t * t;
return b * p0 + 3 * t * a * a * p1 + 3 * t * t * a * p2 + c * p3;
}
template <typename T>
inline T ImCubicBezierDt(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
const auto a = 1 - t;
const auto b = a * a;
const auto c = t * t;
const auto d = 2 * t * a;
return -3 * p0 * b + 3 * p1 * (b - d) + 3 * p2 * (d - c) + 3 * p3 * c;
}
template <typename T>
inline T ImCubicBezierSample(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
const auto cp0_zero = ImLengthSqr(p1 - p0) < 1e-5f;
const auto cp1_zero = ImLengthSqr(p3 - p2) < 1e-5f;
if (cp0_zero && cp1_zero)
return ImLinearBezier(p0, p3, t);
else if (cp0_zero)
return ImQuadraticBezier(p0, p2, p3, t);
else if (cp1_zero)
return ImQuadraticBezier(p0, p1, p3, t);
else
return ImCubicBezier(p0, p1, p2, p3, t);
}
template <typename T>
inline T ImCubicBezierSample(const ImCubicBezierPointsT<T>& curve, float t)
{
return ImCubicBezierSample(curve.P0, curve.P1, curve.P2, curve.P3, t);
}
template <typename T>
inline T ImCubicBezierTangent(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
const auto cp0_zero = ImLengthSqr(p1 - p0) < 1e-5f;
const auto cp1_zero = ImLengthSqr(p3 - p2) < 1e-5f;
if (cp0_zero && cp1_zero)
return ImLinearBezierDt(p0, p3, t);
else if (cp0_zero)
return ImQuadraticBezierDt(p0, p2, p3, t);
else if (cp1_zero)
return ImQuadraticBezierDt(p0, p1, p3, t);
else
return ImCubicBezierDt(p0, p1, p2, p3, t);
}
template <typename T>
inline T ImCubicBezierTangent(const ImCubicBezierPointsT<T>& curve, float t)
{
return ImCubicBezierTangent(curve.P0, curve.P1, curve.P2, curve.P3, t);
}
template <typename T>
inline float ImCubicBezierLength(const T& p0, const T& p1, const T& p2, const T& p3)
{
// Legendre-Gauss abscissae with n=24 (x_i values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x))
static const float t_values[] =
{
-0.0640568928626056260850430826247450385909f,
0.0640568928626056260850430826247450385909f,
-0.1911188674736163091586398207570696318404f,
0.1911188674736163091586398207570696318404f,
-0.3150426796961633743867932913198102407864f,
0.3150426796961633743867932913198102407864f,
-0.4337935076260451384870842319133497124524f,
0.4337935076260451384870842319133497124524f,
-0.5454214713888395356583756172183723700107f,
0.5454214713888395356583756172183723700107f,
-0.6480936519369755692524957869107476266696f,
0.6480936519369755692524957869107476266696f,
-0.7401241915785543642438281030999784255232f,
0.7401241915785543642438281030999784255232f,
-0.8200019859739029219539498726697452080761f,
0.8200019859739029219539498726697452080761f,
-0.8864155270044010342131543419821967550873f,
0.8864155270044010342131543419821967550873f,
-0.9382745520027327585236490017087214496548f,
0.9382745520027327585236490017087214496548f,
-0.9747285559713094981983919930081690617411f,
0.9747285559713094981983919930081690617411f,
-0.9951872199970213601799974097007368118745f,
0.9951872199970213601799974097007368118745f
};
// Legendre-Gauss weights with n=24 (w_i values, defined by a function linked to in the Bezier primer article)
static const float c_values[] =
{
0.1279381953467521569740561652246953718517f,
0.1279381953467521569740561652246953718517f,
0.1258374563468282961213753825111836887264f,
0.1258374563468282961213753825111836887264f,
0.1216704729278033912044631534762624256070f,
0.1216704729278033912044631534762624256070f,
0.1155056680537256013533444839067835598622f,
0.1155056680537256013533444839067835598622f,
0.1074442701159656347825773424466062227946f,
0.1074442701159656347825773424466062227946f,
0.0976186521041138882698806644642471544279f,
0.0976186521041138882698806644642471544279f,
0.0861901615319532759171852029837426671850f,
0.0861901615319532759171852029837426671850f,
0.0733464814110803057340336152531165181193f,
0.0733464814110803057340336152531165181193f,
0.0592985849154367807463677585001085845412f,
0.0592985849154367807463677585001085845412f,
0.0442774388174198061686027482113382288593f,
0.0442774388174198061686027482113382288593f,
0.0285313886289336631813078159518782864491f,
0.0285313886289336631813078159518782864491f,
0.0123412297999871995468056670700372915759f,
0.0123412297999871995468056670700372915759f
};
static_assert(sizeof(t_values) / sizeof(*t_values) == sizeof(c_values) / sizeof(*c_values), "");
auto arc = [p0, p1, p2, p3](float t)
{
const auto p = ImCubicBezierDt(p0, p1, p2, p3, t);
const auto l = ImLength(p);
return l;
};
const auto z = 0.5f;
const auto n = sizeof(t_values) / sizeof(*t_values);
auto accumulator = 0.0f;
for (size_t i = 0; i < n; ++i)
{
const auto t = z * t_values[i] + z;
accumulator += c_values[i] * arc(t);
}
return z * accumulator;
}
template <typename T>
inline float ImCubicBezierLength(const ImCubicBezierPointsT<T>& curve)
{
return ImCubicBezierLength(curve.P0, curve.P1, curve.P2, curve.P3);
}
template <typename T>
inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
const auto z1 = t;
const auto z2 = z1 * z1;
const auto z3 = z1 * z1 * z1;
const auto s1 = z1 - 1;
const auto s2 = s1 * s1;
const auto s3 = s1 * s1 * s1;
return ImCubicBezierSplitResultT<T>
{
ImCubicBezierPointsT<T>
{
p0,
z1 * p1 - s1 * p0,
z2 * p2 - 2 * z1 * s1 * p1 + s2 * p0,
z3 * p3 - 3 * z2 * s1 * p2 + 3 * z1 * s2 * p1 - s3 * p0
},
ImCubicBezierPointsT<T>
{
z3 * p0 - 3 * z2 * s1 * p1 + 3 * z1 * s2 * p2 - s3 * p3,
z2 * p1 - 2 * z1 * s1 * p2 + s2 * p3,
z1 * p2 - s1 * p3,
p3,
}
};
}
template <typename T>
inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const ImCubicBezierPointsT<T>& curve, float t)
{
return ImCubicBezierSplit(curve.P0, curve.P1, curve.P2, curve.P3, t);
}
inline ImRect ImCubicBezierBoundingRect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3)
{
auto a = 3 * p3 - 9 * p2 + 9 * p1 - 3 * p0;
auto b = 6 * p0 - 12 * p1 + 6 * p2;
auto c = 3 * p1 - 3 * p0;
auto delta_squared = ImMul(b, b) - 4 * ImMul(a, c);
auto tl = ImMin(p0, p3);
auto rb = ImMax(p0, p3);
# define IM_VEC2_INDEX(v, i) *(&v.x + i)
for (int i = 0; i < 2; ++i)
{
if (IM_VEC2_INDEX(a, i) == 0.0f)
continue;
if (IM_VEC2_INDEX(delta_squared, i) >= 0)
{
auto delta = ImSqrt(IM_VEC2_INDEX(delta_squared, i));
auto t0 = (-IM_VEC2_INDEX(b, i) + delta) / (2 * IM_VEC2_INDEX(a, i));
if (t0 > 0 && t0 < 1)
{
auto p = ImCubicBezier(IM_VEC2_INDEX(p0, i), IM_VEC2_INDEX(p1, i), IM_VEC2_INDEX(p2, i), IM_VEC2_INDEX(p3, i), t0);
IM_VEC2_INDEX(tl, i) = ImMin(IM_VEC2_INDEX(tl, i), p);
IM_VEC2_INDEX(rb, i) = ImMax(IM_VEC2_INDEX(rb, i), p);
}
auto t1 = (-IM_VEC2_INDEX(b, i) - delta) / (2 * IM_VEC2_INDEX(a, i));
if (t1 > 0 && t1 < 1)
{
auto p = ImCubicBezier(IM_VEC2_INDEX(p0, i), IM_VEC2_INDEX(p1, i), IM_VEC2_INDEX(p2, i), IM_VEC2_INDEX(p3, i), t1);
IM_VEC2_INDEX(tl, i) = ImMin(IM_VEC2_INDEX(tl, i), p);
IM_VEC2_INDEX(rb, i) = ImMax(IM_VEC2_INDEX(rb, i), p);
}
}
}
# undef IM_VEC2_INDEX
return ImRect(tl, rb);
}
inline ImRect ImCubicBezierBoundingRect(const ImCubicBezierPoints& curve)
{
return ImCubicBezierBoundingRect(curve.P0, curve.P1, curve.P2, curve.P3);
}
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& point, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const int subdivisions)
{
// http://pomax.github.io/bezierinfo/#projections
const float epsilon = 1e-5f;
const float fixed_step = 1.0f / static_cast<float>(subdivisions - 1);
ImProjectResult result;
result.Point = point;
result.Time = 0.0f;
result.Distance = FLT_MAX;
// Step 1: Coarse check
for (int i = 0; i < subdivisions; ++i)
{
auto t = i * fixed_step;
auto p = ImCubicBezier(p0, p1, p2, p3, t);
auto s = point - p;
auto d = ImDot(s, s);
if (d < result.Distance)
{
result.Point = p;
result.Time = t;
result.Distance = d;
}
}
if (result.Time == 0.0f || ImFabs(result.Time - 1.0f) <= epsilon)
{
result.Distance = ImSqrt(result.Distance);
return result;
}
// Step 2: Fine check
auto left = result.Time - fixed_step;
auto right = result.Time + fixed_step;
auto step = fixed_step * 0.1f;
for (auto t = left; t < right + step; t += step)
{
auto p = ImCubicBezier(p0, p1, p2, p3, t);
auto s = point - p;
auto d = ImDot(s, s);
if (d < result.Distance)
{
result.Point = p;
result.Time = t;
result.Distance = d;
}
}
result.Distance = ImSqrt(result.Distance);
return result;
}
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImCubicBezierPoints& curve, const int subdivisions)
{
return ImProjectOnCubicBezier(p, curve.P0, curve.P1, curve.P2, curve.P3, subdivisions);
}
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& a0, const ImVec2& a1)
{
auto cubic_roots = [](float a, float b, float c, float d, float* roots) -> int
{
int count = 0;
auto sign = [](float x) -> float { return x < 0 ? -1.0f : 1.0f; };
auto A = b / a;
auto B = c / a;
auto C = d / a;
auto Q = (3 * B - ImPow(A, 2)) / 9;
auto R = (9 * A * B - 27 * C - 2 * ImPow(A, 3)) / 54;
auto D = ImPow(Q, 3) + ImPow(R, 2); // polynomial discriminant
if (D >= 0) // complex or duplicate roots
{
auto S = sign(R + ImSqrt(D)) * ImPow(ImFabs(R + ImSqrt(D)), (1.0f / 3.0f));
auto T = sign(R - ImSqrt(D)) * ImPow(ImFabs(R - ImSqrt(D)), (1.0f / 3.0f));
roots[0] = -A / 3 + (S + T); // real root
roots[1] = -A / 3 - (S + T) / 2; // real part of complex root
roots[2] = -A / 3 - (S + T) / 2; // real part of complex root
auto Im = ImFabs(ImSqrt(3) * (S - T) / 2); // complex part of root pair
// discard complex roots
if (Im != 0)
count = 1;
else
count = 3;
}
else // distinct real roots
{
auto th = ImAcos(R / ImSqrt(-ImPow(Q, 3)));
roots[0] = 2 * ImSqrt(-Q) * ImCos(th / 3) - A / 3;
roots[1] = 2 * ImSqrt(-Q) * ImCos((th + 2 * IM_PI) / 3) - A / 3;
roots[2] = 2 * ImSqrt(-Q) * ImCos((th + 4 * IM_PI) / 3) - A / 3;
count = 3;
}
return count;
};
// https://github.com/kaishiqi/Geometric-Bezier/blob/master/GeometricBezier/src/kaishiqi/geometric/intersection/Intersection.as
//
// Start with Bezier using Bernstein polynomials for weighting functions:
// (1-t^3)P0 + 3t(1-t)^2P1 + 3t^2(1-t)P2 + t^3P3
//
// Expand and collect terms to form linear combinations of original Bezier
// controls. This ends up with a vector cubic in t:
// (-P0+3P1-3P2+P3)t^3 + (3P0-6P1+3P2)t^2 + (-3P0+3P1)t + P0
// /\ /\ /\ /\
// || || || ||
// c3 c2 c1 c0
// Calculate the coefficients
auto c3 = -p0 + 3 * p1 - 3 * p2 + p3;
auto c2 = 3 * p0 - 6 * p1 + 3 * p2;
auto c1 = -3 * p0 + 3 * p1;
auto c0 = p0;
// Convert line to normal form: ax + by + c = 0
auto a = a1.y - a0.y;
auto b = a0.x - a1.x;
auto c = a0.x * (a0.y - a1.y) + a0.y * (a1.x - a0.x);
// Rotate each cubic coefficient using line for new coordinate system?
// Find roots of rotated cubic
float roots[3];
auto rootCount = cubic_roots(
a * c3.x + b * c3.y,
a * c2.x + b * c2.y,
a * c1.x + b * c1.y,
a * c0.x + b * c0.y + c,
roots);
// Any roots in closed interval [0,1] are intersections on Bezier, but
// might not be on the line segment.
// Find intersections and calculate point coordinates
auto min = ImMin(a0, a1);
auto max = ImMax(a0, a1);
ImCubicBezierIntersectResult result;
auto points = result.Points;
for (int i = 0; i < rootCount; ++i)
{
auto root = roots[i];
if (0 <= root && root <= 1)
{
// We're within the Bezier curve
// Find point on Bezier
auto p = ImCubicBezier(p0, p1, p2, p3, root);
// See if point is on line segment
// Had to make special cases for vertical and horizontal lines due
// to slight errors in calculation of p00
if (a0.x == a1.x)
{
if (min.y <= p.y && p.y <= max.y)
*points++ = p;
}
else if (a0.y == a1.y)
{
if (min.x <= p.x && p.x <= max.x)
*points++ = p;
}
else if (p.x >= min.x && p.y >= min.y && p.x <= max.x && p.y <= max.y)
{
*points++ = p;
}
}
}
result.Count = static_cast<int>(points - result.Points);
return result;
}
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImCubicBezierPoints& curve, const ImLine& line)
{
return ImCubicBezierLineIntersect(curve.P0, curve.P1, curve.P2, curve.P3, line.A, line.B);
}
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol, ImCubicBezierSubdivideFlags flags)
{
return ImCubicBezierSubdivide(callback, user_pointer, ImCubicBezierPoints{ p0, p1, p2, p3 }, tess_tol, flags);
}
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float tess_tol, ImCubicBezierSubdivideFlags flags)
{
struct Tesselator
{
ImCubicBezierSubdivideCallback Callback;
void* UserPointer;
float TesselationTollerance;
ImCubicBezierSubdivideFlags Flags;
void Commit(const ImVec2& p, const ImVec2& t)
{
ImCubicBezierSubdivideSample sample;
sample.Point = p;
sample.Tangent = t;
Callback(sample, UserPointer);
}
void Subdivide(const ImCubicBezierPoints& curve, int level = 0)
{
float dx = curve.P3.x - curve.P0.x;
float dy = curve.P3.y - curve.P0.y;
float d2 = ((curve.P1.x - curve.P3.x) * dy - (curve.P1.y - curve.P3.y) * dx);
float d3 = ((curve.P2.x - curve.P3.x) * dy - (curve.P2.y - curve.P3.y) * dx);
d2 = (d2 >= 0) ? d2 : -d2;
d3 = (d3 >= 0) ? d3 : -d3;
if ((d2 + d3) * (d2 + d3) < TesselationTollerance * (dx * dx + dy * dy))
{
Commit(curve.P3, ImCubicBezierTangent(curve, 1.0f));
}
else if (level < 10)
{
const auto p12 = (curve.P0 + curve.P1) * 0.5f;
const auto p23 = (curve.P1 + curve.P2) * 0.5f;
const auto p34 = (curve.P2 + curve.P3) * 0.5f;
const auto p123 = (p12 + p23) * 0.5f;
const auto p234 = (p23 + p34) * 0.5f;
const auto p1234 = (p123 + p234) * 0.5f;
Subdivide(ImCubicBezierPoints { curve.P0, p12, p123, p1234 }, level + 1);
Subdivide(ImCubicBezierPoints { p1234, p234, p34, curve.P3 }, level + 1);
}
}
};
if (tess_tol < 0)
tess_tol = 1.118f; // sqrtf(1.25f)
Tesselator tesselator;
tesselator.Callback = callback;
tesselator.UserPointer = user_pointer;
tesselator.TesselationTollerance = tess_tol * tess_tol;
tesselator.Flags = flags;
if (!(tesselator.Flags & ImCubicBezierSubdivide_SkipFirst))
tesselator.Commit(curve.P0, ImCubicBezierTangent(curve, 0.0f));
tesselator.Subdivide(curve, 0);
}
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol, ImCubicBezierSubdivideFlags flags)
{
auto handler = [](const ImCubicBezierSubdivideSample& p, void* user_pointer)
{
auto& callback = *reinterpret_cast<F*>(user_pointer);
callback(p);
};
ImCubicBezierSubdivide(handler, &callback, ImCubicBezierPoints{ p0, p1, p2, p3 }, tess_tol, flags);
}
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImCubicBezierPoints& curve, float tess_tol, ImCubicBezierSubdivideFlags flags)
{
auto handler = [](const ImCubicBezierSubdivideSample& p, void* user_pointer)
{
auto& callback = *reinterpret_cast<F*>(user_pointer);
callback(p);
};
ImCubicBezierSubdivide(handler, &callback, curve, tess_tol, flags);
}
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot, float max_value_error, float max_t_error)
{
if (step <= 0.0f || !callback || max_value_error <= 0 || max_t_error <= 0)
return;
ImCubicBezierFixedStepSample sample;
sample.T = 0.0f;
sample.Length = 0.0f;
sample.Point = p0;
sample.BreakSearch = false;
callback(sample, user_pointer);
if (sample.BreakSearch)
return;
const auto total_length = ImCubicBezierLength(p0, p1, p2, p3);
const auto point_count = static_cast<int>(total_length / step) + (overshoot ? 2 : 1);
const auto t_min = 0.0f;
const auto t_max = step * point_count / total_length;
const auto t_0 = (t_min + t_max) * 0.5f;
// #todo: replace map with ImVector + binary search
std::map<float, float> cache;
for (int point_index = 1; point_index < point_count; ++point_index)
{
const auto targetLength = point_index * step;
float t_start = t_min;
float t_end = t_max;
float t = t_0;
float t_best = t;
float error_best = total_length;
while (true)
{
auto cacheIt = cache.find(t);
if (cacheIt == cache.end())
{
const auto front = ImCubicBezierSplit(p0, p1, p2, p3, t).Left;
const auto split_length = ImCubicBezierLength(front);
cacheIt = cache.emplace(t, split_length).first;
}
const auto length = cacheIt->second;
const auto error = targetLength - length;
if (error < error_best)
{
error_best = error;
t_best = t;
}
if (ImFabs(error) <= max_value_error || ImFabs(t_start - t_end) <= max_t_error)
{
sample.T = t;
sample.Length = length;
sample.Point = ImCubicBezier(p0, p1, p2, p3, t);
callback(sample, user_pointer);
if (sample.BreakSearch)
return;
break;
}
else if (error < 0.0f)
t_end = t;
else // if (error > 0.0f)
t_start = t;
t = (t_start + t_end) * 0.5f;
}
}
}
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float step, bool overshoot, float max_value_error, float max_t_error)
{
ImCubicBezierFixedStep(callback, user_pointer, curve.P0, curve.P1, curve.P2, curve.P3, step, overshoot, max_value_error, max_t_error);
}
// F has signature void(const ImCubicBezierFixedStepSample& p)
template <typename F>
inline void ImCubicBezierFixedStep(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot, float max_value_error, float max_t_error)
{
auto handler = [](ImCubicBezierFixedStepSample& sample, void* user_pointer)
{
auto& callback = *reinterpret_cast<F*>(user_pointer);
callback(sample);
};
ImCubicBezierFixedStep(handler, &callback, p0, p1, p2, p3, step, overshoot, max_value_error, max_t_error);
}
template <typename F>
inline void ImCubicBezierFixedStep(F& callback, const ImCubicBezierPoints& curve, float step, bool overshoot, float max_value_error, float max_t_error)
{
auto handler = [](ImCubicBezierFixedStepSample& sample, void* user_pointer)
{
auto& callback = *reinterpret_cast<F*>(user_pointer);
callback(sample);
};
ImCubicBezierFixedStep(handler, &callback, curve.P0, curve.P1, curve.P2, curve.P3, step, overshoot, max_value_error, max_t_error);
}
//------------------------------------------------------------------------------
# endif // __IMGUI_BEZIER_MATH_INL__

View File

@ -0,0 +1,574 @@
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "imgui_canvas.h"
# include <type_traits>
// https://stackoverflow.com/a/36079786
# define DECLARE_HAS_MEMBER(__trait_name__, __member_name__) \
\
template <typename __boost_has_member_T__> \
class __trait_name__ \
{ \
using check_type = ::std::remove_const_t<__boost_has_member_T__>; \
struct no_type {char x[2];}; \
using yes_type = char; \
\
struct base { void __member_name__() {}}; \
struct mixin : public base, public check_type {}; \
\
template <void (base::*)()> struct aux {}; \
\
template <typename U> static no_type test(aux<&U::__member_name__>*); \
template <typename U> static yes_type test(...); \
\
public: \
\
static constexpr bool value = (sizeof(yes_type) == sizeof(test<mixin>(0))); \
}
// Special sentinel value. This needs to be unique, so allow it to be overridden in the user's ImGui config
# ifndef ImDrawCallback_ImCanvas
# define ImDrawCallback_ImCanvas (ImDrawCallback)(-2)
# endif
namespace ImCanvasDetails {
DECLARE_HAS_MEMBER(HasFringeScale, _FringeScale);
struct FringeScaleRef
{
// Overload is present when ImDrawList does have _FringeScale member variable.
template <typename T>
static float& Get(typename std::enable_if<HasFringeScale<T>::value, T>::type* drawList)
{
return drawList->_FringeScale;
}
// Overload is present when ImDrawList does not have _FringeScale member variable.
template <typename T>
static float& Get(typename std::enable_if<!HasFringeScale<T>::value, T>::type*)
{
static float placeholder = 1.0f;
return placeholder;
}
};
DECLARE_HAS_MEMBER(HasVtxCurrentOffset, _VtxCurrentOffset);
struct VtxCurrentOffsetRef
{
// Overload is present when ImDrawList does have _FringeScale member variable.
template <typename T>
static unsigned int& Get(typename std::enable_if<HasVtxCurrentOffset<T>::value, T>::type* drawList)
{
return drawList->_VtxCurrentOffset;
}
// Overload is present when ImDrawList does not have _FringeScale member variable.
template <typename T>
static unsigned int& Get(typename std::enable_if<!HasVtxCurrentOffset<T>::value, T>::type* drawList)
{
return drawList->_CmdHeader.VtxOffset;
}
};
} // namespace ImCanvasDetails
// Returns a reference to _FringeScale extension to ImDrawList
//
// If ImDrawList does not have _FringeScale a placeholder is returned.
static inline float& ImFringeScaleRef(ImDrawList* drawList)
{
using namespace ImCanvasDetails;
return FringeScaleRef::Get<ImDrawList>(drawList);
}
static inline unsigned int& ImVtxOffsetRef(ImDrawList* drawList)
{
using namespace ImCanvasDetails;
return VtxCurrentOffsetRef::Get<ImDrawList>(drawList);
}
static inline ImVec2 ImSelectPositive(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x > 0.0f ? lhs.x : rhs.x, lhs.y > 0.0f ? lhs.y : rhs.y); }
bool ImGuiEx::Canvas::Begin(const char* id, const ImVec2& size)
{
return Begin(ImGui::GetID(id), size);
}
bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
{
IM_ASSERT(m_InBeginEnd == false);
m_WidgetPosition = ImGui::GetCursorScreenPos();
m_WidgetSize = ImSelectPositive(size, ImGui::GetContentRegionAvail());
m_WidgetRect = ImRect(m_WidgetPosition, m_WidgetPosition + m_WidgetSize);
m_DrawList = ImGui::GetWindowDrawList();
UpdateViewTransformPosition();
# if IMGUI_VERSION_NUM > 18415
if (ImGui::IsClippedEx(m_WidgetRect, id))
return false;
# else
if (ImGui::IsClippedEx(m_WidgetRect, id, false))
return false;
# endif
// Save current channel, so we can assert when user
// call canvas API with different one.
m_ExpectedChannel = m_DrawList->_Splitter._Current;
// #debug: Canvas content.
//m_DrawList->AddRectFilled(m_StartPos, m_StartPos + m_CurrentSize, IM_COL32(0, 0, 0, 64));
//m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
# if IMGUI_EX_CANVAS_DEFERED()
m_Ranges.resize(0);
# endif
SaveInputState();
SaveViewportState();
// Record cursor max to prevent scrollbars from appearing.
m_WindowCursorMaxBackup = ImGui::GetCurrentWindow()->DC.CursorMaxPos;
EnterLocalSpace();
# if IMGUI_VERSION_NUM >= 18967
ImGui::SetNextItemAllowOverlap();
# endif
// Emit dummy widget matching bounds of the canvas.
ImGui::SetCursorScreenPos(m_ViewRect.Min);
ImGui::Dummy(m_ViewRect.GetSize());
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
m_InBeginEnd = true;
return true;
}
void ImGuiEx::Canvas::End()
{
// If you're here your call to Begin() returned false,
// or Begin() wasn't called at all.
IM_ASSERT(m_InBeginEnd == true);
// If you're here, please make sure you do not interleave
// channel splitter with canvas.
// Always call canvas function with using same channel.
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
//auto& io = ImGui::GetIO();
// Check: Unmatched calls to Suspend() / Resume(). Please check your code.
IM_ASSERT(m_SuspendCounter == 0);
LeaveLocalSpace();
ImGui::GetCurrentWindow()->DC.CursorMaxPos = m_WindowCursorMaxBackup;
# if IMGUI_VERSION_NUM < 18967
ImGui::SetItemAllowOverlap();
# endif
// Emit dummy widget matching bounds of the canvas.
ImGui::SetCursorScreenPos(m_WidgetPosition);
ImGui::Dummy(m_WidgetSize);
// #debug: Rect around canvas. Content should be inside these bounds.
//m_DrawList->AddRect(m_WidgetPosition - ImVec2(1.0f, 1.0f), m_WidgetPosition + m_WidgetSize + ImVec2(1.0f, 1.0f), IM_COL32(196, 0, 0, 255));
m_InBeginEnd = false;
}
void ImGuiEx::Canvas::SetView(const ImVec2& origin, float scale)
{
SetView(CanvasView(origin, scale));
}
void ImGuiEx::Canvas::SetView(const CanvasView& view)
{
if (m_InBeginEnd)
LeaveLocalSpace();
if (m_View.Origin.x != view.Origin.x || m_View.Origin.y != view.Origin.y)
{
m_View.Origin = view.Origin;
UpdateViewTransformPosition();
}
if (m_View.Scale != view.Scale)
{
m_View.Scale = view.Scale;
m_View.InvScale = view.InvScale;
}
if (m_InBeginEnd)
EnterLocalSpace();
}
void ImGuiEx::Canvas::CenterView(const ImVec2& canvasPoint)
{
auto view = CalcCenterView(canvasPoint);
SetView(view);
}
ImGuiEx::CanvasView ImGuiEx::Canvas::CalcCenterView(const ImVec2& canvasPoint) const
{
auto localCenter = ToLocal(m_WidgetPosition + m_WidgetSize * 0.5f);
auto localOffset = canvasPoint - localCenter;
auto offset = FromLocalV(localOffset);
return CanvasView{ m_View.Origin - offset, m_View.Scale };
}
void ImGuiEx::Canvas::CenterView(const ImRect& canvasRect)
{
auto view = CalcCenterView(canvasRect);
SetView(view);
}
ImGuiEx::CanvasView ImGuiEx::Canvas::CalcCenterView(const ImRect& canvasRect) const
{
auto canvasRectSize = canvasRect.GetSize();
if (canvasRectSize.x <= 0.0f || canvasRectSize.y <= 0.0f)
return View();
auto widgetAspectRatio = m_WidgetSize.y > 0.0f ? m_WidgetSize.x / m_WidgetSize.y : 0.0f;
auto canvasRectAspectRatio = canvasRectSize.y > 0.0f ? canvasRectSize.x / canvasRectSize.y : 0.0f;
if (widgetAspectRatio <= 0.0f || canvasRectAspectRatio <= 0.0f)
return View();
auto newOrigin = m_View.Origin;
auto newScale = m_View.Scale;
if (canvasRectAspectRatio > widgetAspectRatio)
{
// width span across view
newScale = m_WidgetSize.x / canvasRectSize.x;
newOrigin = canvasRect.Min * -newScale;
newOrigin.y += (m_WidgetSize.y - canvasRectSize.y * newScale) * 0.5f;
}
else
{
// height span across view
newScale = m_WidgetSize.y / canvasRectSize.y;
newOrigin = canvasRect.Min * -newScale;
newOrigin.x += (m_WidgetSize.x - canvasRectSize.x * newScale) * 0.5f;
}
return CanvasView{ newOrigin, newScale };
}
void ImGuiEx::Canvas::Suspend()
{
// If you're here, please make sure you do not interleave
// channel splitter with canvas.
// Always call canvas function with using same channel.
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
if (m_SuspendCounter == 0)
LeaveLocalSpace();
++m_SuspendCounter;
}
void ImGuiEx::Canvas::Resume()
{
// If you're here, please make sure you do not interleave
// channel splitter with canvas.
// Always call canvas function with using same channel.
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
// Check: Number of calls to Resume() do not match calls to Suspend(). Please check your code.
IM_ASSERT(m_SuspendCounter > 0);
if (--m_SuspendCounter == 0)
EnterLocalSpace();
}
ImVec2 ImGuiEx::Canvas::FromLocal(const ImVec2& point) const
{
return point * m_View.Scale + m_ViewTransformPosition;
}
ImVec2 ImGuiEx::Canvas::FromLocal(const ImVec2& point, const CanvasView& view) const
{
return point * view.Scale + view.Origin + m_WidgetPosition;
}
ImVec2 ImGuiEx::Canvas::FromLocalV(const ImVec2& vector) const
{
return vector * m_View.Scale;
}
ImVec2 ImGuiEx::Canvas::FromLocalV(const ImVec2& vector, const CanvasView& view) const
{
return vector * view.Scale;
}
ImVec2 ImGuiEx::Canvas::ToLocal(const ImVec2& point) const
{
return (point - m_ViewTransformPosition) * m_View.InvScale;
}
ImVec2 ImGuiEx::Canvas::ToLocal(const ImVec2& point, const CanvasView& view) const
{
return (point - view.Origin - m_WidgetPosition) * view.InvScale;
}
ImVec2 ImGuiEx::Canvas::ToLocalV(const ImVec2& vector) const
{
return vector * m_View.InvScale;
}
ImVec2 ImGuiEx::Canvas::ToLocalV(const ImVec2& vector, const CanvasView& view) const
{
return vector * view.InvScale;
}
ImRect ImGuiEx::Canvas::CalcViewRect(const CanvasView& view) const
{
ImRect result;
result.Min = ImVec2(-view.Origin.x, -view.Origin.y) * view.InvScale;
result.Max = (m_WidgetSize - view.Origin) * view.InvScale;
return result;
}
void ImGuiEx::Canvas::UpdateViewTransformPosition()
{
m_ViewTransformPosition = m_View.Origin + m_WidgetPosition;
}
void ImGuiEx::Canvas::SaveInputState()
{
auto& io = ImGui::GetIO();
m_MousePosBackup = io.MousePos;
m_MousePosPrevBackup = io.MousePosPrev;
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
m_MouseClickedPosBackup[i] = io.MouseClickedPos[i];
}
void ImGuiEx::Canvas::RestoreInputState()
{
auto& io = ImGui::GetIO();
io.MousePos = m_MousePosBackup;
io.MousePosPrev = m_MousePosPrevBackup;
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
io.MouseClickedPos[i] = m_MouseClickedPosBackup[i];
}
void ImGuiEx::Canvas::SaveViewportState()
{
# if defined(IMGUI_HAS_VIEWPORT)
auto window = ImGui::GetCurrentWindow();
auto viewport = ImGui::GetWindowViewport();
m_WindowPosBackup = window->Pos;
m_ViewportPosBackup = viewport->Pos;
m_ViewportSizeBackup = viewport->Size;
# if IMGUI_VERSION_NUM > 18002
m_ViewportWorkPosBackup = viewport->WorkPos;
m_ViewportWorkSizeBackup = viewport->WorkSize;
# else
m_ViewportWorkOffsetMinBackup = viewport->WorkOffsetMin;
m_ViewportWorkOffsetMaxBackup = viewport->WorkOffsetMax;
# endif
# endif
}
void ImGuiEx::Canvas::RestoreViewportState()
{
# if defined(IMGUI_HAS_VIEWPORT)
auto window = ImGui::GetCurrentWindow();
auto viewport = ImGui::GetWindowViewport();
window->Pos = m_WindowPosBackup;
viewport->Pos = m_ViewportPosBackup;
viewport->Size = m_ViewportSizeBackup;
# if IMGUI_VERSION_NUM > 18002
viewport->WorkPos = m_ViewportWorkPosBackup;
viewport->WorkSize = m_ViewportWorkSizeBackup;
# else
viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup;
viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup;
# endif
# endif
}
void ImGuiEx::Canvas::EnterLocalSpace()
{
// Prepare ImDrawList for drawing in local coordinate system:
// - determine visible part of the canvas
// - start unique draw command
// - add clip rect matching canvas size
// - record current command index
// - record current vertex write index
// Determine visible part of the canvas. Make it before
// adding new command, to avoid round rip where command
// is removed in PopClipRect() and added again next PushClipRect().
ImGui::PushClipRect(m_WidgetPosition, m_WidgetPosition + m_WidgetSize, true);
auto clipped_clip_rect = m_DrawList->_ClipRectStack.back();
ImGui::PopClipRect();
# if IMGUI_EX_CANVAS_DEFERED()
m_Ranges.resize(m_Ranges.Size + 1);
m_CurrentRange = &m_Ranges.back();
m_CurrentRange->BeginComandIndex = ImMax(m_DrawList->CmdBuffer.Size, 0);
m_CurrentRange->BeginVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
# endif
m_DrawListCommadBufferSize = ImMax(m_DrawList->CmdBuffer.Size, 0);
m_DrawListStartVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
// Make sure we do not share draw command with anyone. We don't want to mess
// with someones clip rectangle.
// #FIXME:
// This condition is not enough to avoid when user choose
// to use channel splitter.
//
// To deal with Suspend()/Resume() calls empty draw command
// is always added then splitter is active. Otherwise
// channel merger will collapse our draw command one with
// different clip rectangle.
//
// More investigation is needed. To get to the bottom of this.
if ((!m_DrawList->CmdBuffer.empty() && m_DrawList->CmdBuffer.back().ElemCount > 0) || m_DrawList->_Splitter._Count > 1)
m_DrawList->AddCallback(ImDrawCallback_ImCanvas, nullptr);
m_DrawListFirstCommandIndex = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
# if defined(IMGUI_HAS_VIEWPORT)
auto window = ImGui::GetCurrentWindow();
window->Pos = ImVec2(0.0f, 0.0f);
auto viewport_min = m_ViewportPosBackup;
auto viewport_max = m_ViewportPosBackup + m_ViewportSizeBackup;
viewport_min.x = (viewport_min.x - m_ViewTransformPosition.x) * m_View.InvScale;
viewport_min.y = (viewport_min.y - m_ViewTransformPosition.y) * m_View.InvScale;
viewport_max.x = (viewport_max.x - m_ViewTransformPosition.x) * m_View.InvScale;
viewport_max.y = (viewport_max.y - m_ViewTransformPosition.y) * m_View.InvScale;
auto viewport = ImGui::GetWindowViewport();
viewport->Pos = viewport_min;
viewport->Size = viewport_max - viewport_min;
# if IMGUI_VERSION_NUM > 18002
viewport->WorkPos = m_ViewportWorkPosBackup * m_View.InvScale;
viewport->WorkSize = m_ViewportWorkSizeBackup * m_View.InvScale;
# else
viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup * m_View.InvScale;
viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup * m_View.InvScale;
# endif
# endif
// Clip rectangle in parent canvas space and move it to local space.
clipped_clip_rect.x = (clipped_clip_rect.x - m_ViewTransformPosition.x) * m_View.InvScale;
clipped_clip_rect.y = (clipped_clip_rect.y - m_ViewTransformPosition.y) * m_View.InvScale;
clipped_clip_rect.z = (clipped_clip_rect.z - m_ViewTransformPosition.x) * m_View.InvScale;
clipped_clip_rect.w = (clipped_clip_rect.w - m_ViewTransformPosition.y) * m_View.InvScale;
ImGui::PushClipRect(ImVec2(clipped_clip_rect.x, clipped_clip_rect.y), ImVec2(clipped_clip_rect.z, clipped_clip_rect.w), false);
// Transform mouse position to local space.
auto& io = ImGui::GetIO();
io.MousePos = (m_MousePosBackup - m_ViewTransformPosition) * m_View.InvScale;
io.MousePosPrev = (m_MousePosPrevBackup - m_ViewTransformPosition) * m_View.InvScale;
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
io.MouseClickedPos[i] = (m_MouseClickedPosBackup[i] - m_ViewTransformPosition) * m_View.InvScale;
m_ViewRect = CalcViewRect(m_View);;
auto& fringeScale = ImFringeScaleRef(m_DrawList);
m_LastFringeScale = fringeScale;
fringeScale *= m_View.InvScale;
}
void ImGuiEx::Canvas::LeaveLocalSpace()
{
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
# if IMGUI_EX_CANVAS_DEFERED()
IM_ASSERT(m_CurrentRange != nullptr);
m_CurrentRange->EndVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
m_CurrentRange->EndCommandIndex = m_DrawList->CmdBuffer.size();
if (m_CurrentRange->BeginVertexIndex == m_CurrentRange->EndVertexIndex)
{
// Drop empty range
m_Ranges.resize(m_Ranges.Size - 1);
}
m_CurrentRange = nullptr;
# endif
// Move vertices to screen space.
auto vertex = m_DrawList->VtxBuffer.Data + m_DrawListStartVertexIndex;
auto vertexEnd = m_DrawList->VtxBuffer.Data + m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
// If canvas view is not scaled take a faster path.
if (m_View.Scale != 1.0f)
{
while (vertex < vertexEnd)
{
vertex->pos.x = vertex->pos.x * m_View.Scale + m_ViewTransformPosition.x;
vertex->pos.y = vertex->pos.y * m_View.Scale + m_ViewTransformPosition.y;
++vertex;
}
// Move clip rectangles to screen space.
for (int i = m_DrawListFirstCommandIndex; i < m_DrawList->CmdBuffer.size(); ++i)
{
auto& command = m_DrawList->CmdBuffer[i];
command.ClipRect.x = command.ClipRect.x * m_View.Scale + m_ViewTransformPosition.x;
command.ClipRect.y = command.ClipRect.y * m_View.Scale + m_ViewTransformPosition.y;
command.ClipRect.z = command.ClipRect.z * m_View.Scale + m_ViewTransformPosition.x;
command.ClipRect.w = command.ClipRect.w * m_View.Scale + m_ViewTransformPosition.y;
}
}
else
{
while (vertex < vertexEnd)
{
vertex->pos.x = vertex->pos.x + m_ViewTransformPosition.x;
vertex->pos.y = vertex->pos.y + m_ViewTransformPosition.y;
++vertex;
}
// Move clip rectangles to screen space.
for (int i = m_DrawListFirstCommandIndex; i < m_DrawList->CmdBuffer.size(); ++i)
{
auto& command = m_DrawList->CmdBuffer[i];
command.ClipRect.x = command.ClipRect.x + m_ViewTransformPosition.x;
command.ClipRect.y = command.ClipRect.y + m_ViewTransformPosition.y;
command.ClipRect.z = command.ClipRect.z + m_ViewTransformPosition.x;
command.ClipRect.w = command.ClipRect.w + m_ViewTransformPosition.y;
}
}
// Remove sentinel draw command if present
if (m_DrawListCommadBufferSize > 0)
{
if (m_DrawList->CmdBuffer.size() > m_DrawListCommadBufferSize && m_DrawList->CmdBuffer[m_DrawListCommadBufferSize].UserCallback == ImDrawCallback_ImCanvas)
m_DrawList->CmdBuffer.erase(m_DrawList->CmdBuffer.Data + m_DrawListCommadBufferSize);
else if (m_DrawList->CmdBuffer.size() >= m_DrawListCommadBufferSize && m_DrawList->CmdBuffer[m_DrawListCommadBufferSize - 1].UserCallback == ImDrawCallback_ImCanvas)
m_DrawList->CmdBuffer.erase(m_DrawList->CmdBuffer.Data + m_DrawListCommadBufferSize - 1);
}
auto& fringeScale = ImFringeScaleRef(m_DrawList);
fringeScale = m_LastFringeScale;
// And pop \o/
ImGui::PopClipRect();
RestoreInputState();
RestoreViewportState();
}

View File

@ -0,0 +1,273 @@
// Canvas widget - view over infinite virtual space.
//
// Canvas allows you to draw your widgets anywhere over infinite space and provide
// view over it with support for panning and scaling.
//
// When you enter a canvas ImGui is moved to virtual space which mean:
// - ImGui::GetCursorScreenPos() return (0, 0) and which correspond to top left corner
// of the canvas on the screen (this can be changed using CanvasView()).
// - Mouse input is brought to canvas space, so widgets works as usual.
// - Everything you draw with ImDrawList will be in virtual space.
//
// By default origin point is on top left corner of canvas widget. It can be
// changed with call to CanvasView() where you can specify what part of space
// should be viewed by setting viewport origin point and scale. Current state
// can be queried with CanvasViewOrigin() and CanvasViewScale().
//
// Viewport size is controlled by 'size' parameter in BeginCanvas(). You can query
// it using CanvasContentMin/Max/Size functions. They are useful if you to not specify
// canvas size in which case all free space is used.
//
// Bounds of visible region of infinite space can be queried using CanvasViewMin/Max/Size
// functions. Everything that is drawn outside of this region will be clipped
// as usual in ImGui.
//
// While drawing inside canvas you can translate position from world (usual ImGui space)
// to virtual space and back using CanvasFromWorld()/CanvasToWorld().
//
// Canvas can be nested in each other (they are regular widgets after all). There
// is a way to transform position between current and parent canvas with
// CanvasFromParent()/CanvasToParent().
//
// Sometimes in more elaborate scenarios you want to move out canvas virtual space,
// do something and came back. You can do that with SuspendCanvas() and ResumeCanvas().
//
// Note:
// It is not valid to call canvas API outside of BeginCanvas() / EndCanvas() scope.
//
// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
# ifndef __IMGUI_EX_CANVAS_H__
# define __IMGUI_EX_CANVAS_H__
# pragma once
# include <imgui.h>
# include <imgui_internal.h> // ImRect, ImFloor
#ifndef IMGUIEX_CANVAS_API
#define IMGUIEX_CANVAS_API
#endif
namespace ImGuiEx {
struct CanvasView
{
ImVec2 Origin;
float Scale = 1.0f;
float InvScale = 1.0f;
CanvasView() = default;
CanvasView(const ImVec2& origin, float scale)
: Origin(origin)
, Scale(scale)
, InvScale(scale ? 1.0f / scale : 0.0f)
{
}
void Set(const ImVec2& origin, float scale)
{
*this = CanvasView(origin, scale);
}
};
// Canvas widget represent view over infinite plane.
//
// It acts like a child window without scroll bars with
// ability to zoom to specific part of canvas plane.
//
// Widgets are clipped according to current view exactly
// same way ImGui do. To avoid `missing widgets` artifacts first
// setup visible region with SetView() then draw content.
//
// Everything drawn with ImDrawList betwen calls to Begin()/End()
// will be drawn on canvas plane. This behavior can be suspended
// by calling Suspend() and resumed by calling Resume().
//
// Warning:
// Please do not interleave canvas with use of channel splitter.
// Keep channel splitter contained inside canvas or always
// call canvas functions from same channel.
struct Canvas
{
// Begins drawing content of canvas plane.
//
// When false is returned that mean canvas is not visible to the
// user can drawing should be skipped and End() not called.
// When true is returned drawing must be ended with call to End().
//
// If any size component is equal to zero or less canvas will
// automatically expand to all available area on that axis.
// So (0, 300) will take horizontal space and have height
// of 300 points. (0, 0) will take all remaining space of
// the window.
//
// You can query size of the canvas while it is being drawn
// by calling Rect().
IMGUIEX_CANVAS_API bool Begin(const char* id, const ImVec2& size);
IMGUIEX_CANVAS_API bool Begin(ImGuiID id, const ImVec2& size);
// Ends interaction with canvas plane.
//
// Must be called only when Begin() retuned true.
IMGUIEX_CANVAS_API void End();
// Sets visible region of canvas plane.
//
// Origin is an offset of infinite plane origin from top left
// corner of the canvas.
//
// Scale greater than 1 make canvas content be bigger, less than 1 smaller.
IMGUIEX_CANVAS_API void SetView(const ImVec2& origin, float scale);
IMGUIEX_CANVAS_API void SetView(const CanvasView& view);
// Centers view over specific point on canvas plane.
//
// View will be centered on specific point by changing origin
// but not scale.
IMGUIEX_CANVAS_API void CenterView(const ImVec2& canvasPoint);
// Calculates view over specific point on canvas plane.
IMGUIEX_CANVAS_API CanvasView CalcCenterView(const ImVec2& canvasPoint) const;
// Centers view over specific rectangle on canvas plane.
//
// Whole rectangle will fit in canvas view. This will affect both
// origin and scale.
IMGUIEX_CANVAS_API void CenterView(const ImRect& canvasRect);
// Calculates view over specific rectangle on canvas plane.
IMGUIEX_CANVAS_API CanvasView CalcCenterView(const ImRect& canvasRect) const;
// Suspends canvas by returning to normal ImGui transformation space.
// While suspended UI will not be drawn on canvas plane.
//
// Calls to Suspend()/Resume() are symetrical. Each call to Suspend()
// must be matched with call to Resume().
IMGUIEX_CANVAS_API void Suspend();
IMGUIEX_CANVAS_API void Resume();
// Transforms point from canvas plane to ImGui.
IMGUIEX_CANVAS_API ImVec2 FromLocal(const ImVec2& point) const;
IMGUIEX_CANVAS_API ImVec2 FromLocal(const ImVec2& point, const CanvasView& view) const;
// Transforms vector from canvas plant to ImGui.
IMGUIEX_CANVAS_API ImVec2 FromLocalV(const ImVec2& vector) const;
IMGUIEX_CANVAS_API ImVec2 FromLocalV(const ImVec2& vector, const CanvasView& view) const;
// Transforms point from ImGui to canvas plane.
IMGUIEX_CANVAS_API ImVec2 ToLocal(const ImVec2& point) const;
IMGUIEX_CANVAS_API ImVec2 ToLocal(const ImVec2& point, const CanvasView& view) const;
// Transforms vector from ImGui to canvas plane.
IMGUIEX_CANVAS_API ImVec2 ToLocalV(const ImVec2& vector) const;
IMGUIEX_CANVAS_API ImVec2 ToLocalV(const ImVec2& vector, const CanvasView& view) const;
// Returns widget bounds.
//
// Note:
// Rect is valid after call to Begin().
const ImRect& Rect() const { return m_WidgetRect; }
// Returns visible region on canvas plane (in canvas plane coordinates).
const ImRect& ViewRect() const { return m_ViewRect; }
// Calculates visible region for view.
IMGUIEX_CANVAS_API ImRect CalcViewRect(const CanvasView& view) const;
// Returns current view.
const CanvasView& View() const { return m_View; }
// Returns origin of the view.
//
// Origin is an offset of infinite plane origin from top left
// corner of the canvas.
const ImVec2& ViewOrigin() const { return m_View.Origin; }
// Returns scale of the view.
float ViewScale() const { return m_View.Scale; }
// Returns true if canvas is suspended.
//
// See: Suspend()/Resume()
bool IsSuspended() const { return m_SuspendCounter > 0; }
private:
# define IMGUI_EX_CANVAS_DEFERED() 0
# if IMGUI_EX_CANVAS_DEFERED()
struct Range
{
int BeginVertexIndex = 0;
int EndVertexIndex = 0;
int BeginComandIndex = 0;
int EndCommandIndex = 0;
};
# endif
void UpdateViewTransformPosition();
void SaveInputState();
void RestoreInputState();
void SaveViewportState();
void RestoreViewportState();
void EnterLocalSpace();
void LeaveLocalSpace();
bool m_InBeginEnd = false;
ImVec2 m_WidgetPosition;
ImVec2 m_WidgetSize;
ImRect m_WidgetRect;
ImDrawList* m_DrawList = nullptr;
int m_ExpectedChannel = 0;
# if IMGUI_EX_CANVAS_DEFERED()
ImVector<Range> m_Ranges;
Range* m_CurrentRange = nullptr;
# endif
int m_DrawListFirstCommandIndex = 0;
int m_DrawListCommadBufferSize = 0;
int m_DrawListStartVertexIndex = 0;
CanvasView m_View;
ImRect m_ViewRect;
ImVec2 m_ViewTransformPosition;
int m_SuspendCounter = 0;
float m_LastFringeScale = 1.0f;
ImVec2 m_MousePosBackup;
ImVec2 m_MousePosPrevBackup;
ImVec2 m_MouseClickedPosBackup[IM_ARRAYSIZE(ImGuiIO::MouseClickedPos)];
ImVec2 m_WindowCursorMaxBackup;
# if defined(IMGUI_HAS_VIEWPORT)
ImVec2 m_WindowPosBackup;
ImVec2 m_ViewportPosBackup;
ImVec2 m_ViewportSizeBackup;
# if IMGUI_VERSION_NUM > 18002
ImVec2 m_ViewportWorkPosBackup;
ImVec2 m_ViewportWorkSizeBackup;
# else
ImVec2 m_ViewportWorkOffsetMinBackup;
ImVec2 m_ViewportWorkOffsetMaxBackup;
# endif
# endif
};
} // namespace ImGuiEx
# endif // __IMGUI_EX_CANVAS_H__

View File

@ -0,0 +1,77 @@
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_EXTRA_MATH_H__
# define __IMGUI_EXTRA_MATH_H__
# pragma once
//------------------------------------------------------------------------------
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include <imgui.h>
# include <imgui_internal.h>
//------------------------------------------------------------------------------
struct ImLine
{
ImVec2 A, B;
};
//------------------------------------------------------------------------------
# if IMGUI_VERSION_NUM < 19002
inline bool operator==(const ImVec2& lhs, const ImVec2& rhs);
inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs);
# endif
inline ImVec2 operator*(const float lhs, const ImVec2& rhs);
# if IMGUI_VERSION_NUM < 18955
inline ImVec2 operator-(const ImVec2& lhs);
# endif
//------------------------------------------------------------------------------
inline float ImLength(float v);
inline float ImLength(const ImVec2& v);
inline float ImLengthSqr(float v);
inline ImVec2 ImNormalized(const ImVec2& v);
//------------------------------------------------------------------------------
inline bool ImRect_IsEmpty(const ImRect& rect);
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge);
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge, float radius);
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImRect& b);
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b);
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b, float radius_a, float radius_b);
//------------------------------------------------------------------------------
namespace ImEasing {
template <typename V, typename T>
inline V EaseOutQuad(V b, V c, T t)
{
return b - c * (t * (t - 2));
}
} // namespace ImEasing
//------------------------------------------------------------------------------
# include "imgui_extra_math.inl"
//------------------------------------------------------------------------------
# endif // __IMGUI_EXTRA_MATH_H__

View File

@ -0,0 +1,193 @@
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_EXTRA_MATH_INL__
# define __IMGUI_EXTRA_MATH_INL__
# pragma once
//------------------------------------------------------------------------------
# include "imgui_extra_math.h"
//------------------------------------------------------------------------------
# if IMGUI_VERSION_NUM < 19002
inline bool operator==(const ImVec2& lhs, const ImVec2& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs)
{
return lhs.x != rhs.x || lhs.y != rhs.y;
}
# endif
inline ImVec2 operator*(const float lhs, const ImVec2& rhs)
{
return ImVec2(lhs * rhs.x, lhs * rhs.y);
}
# if IMGUI_VERSION_NUM < 18955
inline ImVec2 operator-(const ImVec2& lhs)
{
return ImVec2(-lhs.x, -lhs.y);
}
# endif
//------------------------------------------------------------------------------
inline float ImLength(float v)
{
return v;
}
inline float ImLength(const ImVec2& v)
{
return ImSqrt(ImLengthSqr(v));
}
inline float ImLengthSqr(float v)
{
return v * v;
}
inline ImVec2 ImNormalized(const ImVec2& v)
{
return v * ImInvLength(v, 0.0f);
}
//------------------------------------------------------------------------------
inline bool ImRect_IsEmpty(const ImRect& rect)
{
return rect.Min.x >= rect.Max.x
|| rect.Min.y >= rect.Max.y;
}
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge)
{
if (!snap_to_edge && rect.Contains(p))
return p;
return ImVec2(
(p.x > rect.Max.x) ? rect.Max.x : (p.x < rect.Min.x ? rect.Min.x : p.x),
(p.y > rect.Max.y) ? rect.Max.y : (p.y < rect.Min.y ? rect.Min.y : p.y)
);
}
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge, float radius)
{
auto point = ImRect_ClosestPoint(rect, p, snap_to_edge);
const auto offset = p - point;
const auto distance_sq = offset.x * offset.x + offset.y * offset.y;
if (distance_sq <= 0)
return point;
const auto distance = ImSqrt(distance_sq);
return point + offset * (ImMin(distance, radius) * (1.0f / distance));
}
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImRect& other)
{
ImVec2 result;
if (other.Min.x >= rect.Max.x)
result.x = rect.Max.x;
else if (other.Max.x <= rect.Min.x)
result.x = rect.Min.x;
else
result.x = (ImMax(rect.Min.x, other.Min.x) + ImMin(rect.Max.x, other.Max.x)) / 2;
if (other.Min.y >= rect.Max.y)
result.y = rect.Max.y;
else if (other.Max.y <= rect.Min.y)
result.y = rect.Min.y;
else
result.y = (ImMax(rect.Min.y, other.Min.y) + ImMin(rect.Max.y, other.Max.y)) / 2;
return result;
}
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b)
{
ImLine result;
result.A = ImRect_ClosestPoint(rect_a, rect_b);
result.B = ImRect_ClosestPoint(rect_b, rect_a);
auto distribute = [](float& a, float& b, float a0, float a1, float b0, float b1)
{
if (a0 >= b1 || a1 <= b0)
return;
const auto aw = a1 - a0;
const auto bw = b1 - b0;
if (aw > bw)
{
b = b0 + bw - bw * (a - a0) / aw;
a = b;
}
else if (aw < bw)
{
a = a0 + aw - aw * (b - b0) / bw;
b = a;
}
};
distribute(result.A.x, result.B.x, rect_a.Min.x, rect_a.Max.x, rect_b.Min.x, rect_b.Max.x);
distribute(result.A.y, result.B.y, rect_a.Min.y, rect_a.Max.y, rect_b.Min.y, rect_b.Max.y);
return result;
}
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b, float radius_a, float radius_b)
{
auto line = ImRect_ClosestLine(rect_a, rect_b);
if (radius_a < 0)
radius_a = 0;
if (radius_b < 0)
radius_b = 0;
if (radius_a == 0 && radius_b == 0)
return line;
const auto offset = line.B - line.A;
const auto length_sq = offset.x * offset.x + offset.y * offset.y;
const auto radius_a_sq = radius_a * radius_a;
const auto radius_b_sq = radius_b * radius_b;
if (length_sq <= 0)
return line;
const auto length = ImSqrt(length_sq);
const auto direction = ImVec2(offset.x / length, offset.y / length);
const auto total_radius_sq = radius_a_sq + radius_b_sq;
if (total_radius_sq > length_sq)
{
const auto scale = length / (radius_a + radius_b);
radius_a *= scale;
radius_b *= scale;
}
line.A = line.A + (direction * radius_a);
line.B = line.B - (direction * radius_b);
return line;
}
//------------------------------------------------------------------------------
# endif // __IMGUI_EXTRA_MATH_INL__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,530 @@
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_NODE_EDITOR_H__
# define __IMGUI_NODE_EDITOR_H__
# pragma once
//------------------------------------------------------------------------------
# include <imgui.h>
# include <cstdint> // std::uintXX_t
# include <utility> // std::move
//------------------------------------------------------------------------------
# define IMGUI_NODE_EDITOR_VERSION "0.9.4"
# define IMGUI_NODE_EDITOR_VERSION_NUM 000904
//------------------------------------------------------------------------------
#ifndef IMGUI_NODE_EDITOR_API
#define IMGUI_NODE_EDITOR_API
#endif
//------------------------------------------------------------------------------
namespace ax {
namespace NodeEditor {
//------------------------------------------------------------------------------
struct NodeId;
struct LinkId;
struct PinId;
//------------------------------------------------------------------------------
enum class PinKind
{
Input,
Output
};
enum class FlowDirection
{
Forward,
Backward
};
enum class CanvasSizeMode
{
FitVerticalView, // Previous view will be scaled to fit new view on Y axis
FitHorizontalView, // Previous view will be scaled to fit new view on X axis
CenterOnly, // Previous view will be centered on new view
};
//------------------------------------------------------------------------------
enum class SaveReasonFlags: uint32_t
{
None = 0x00000000,
Navigation = 0x00000001,
Position = 0x00000002,
Size = 0x00000004,
Selection = 0x00000008,
AddNode = 0x00000010,
RemoveNode = 0x00000020,
User = 0x00000040
};
inline SaveReasonFlags operator |(SaveReasonFlags lhs, SaveReasonFlags rhs) { return static_cast<SaveReasonFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs)); }
inline SaveReasonFlags operator &(SaveReasonFlags lhs, SaveReasonFlags rhs) { return static_cast<SaveReasonFlags>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)); }
using ConfigSaveSettings = bool (*)(const char* data, size_t size, SaveReasonFlags reason, void* userPointer);
using ConfigLoadSettings = size_t (*)(char* data, void* userPointer);
using ConfigSaveNodeSettings = bool (*)(NodeId nodeId, const char* data, size_t size, SaveReasonFlags reason, void* userPointer);
using ConfigLoadNodeSettings = size_t (*)(NodeId nodeId, char* data, void* userPointer);
using ConfigSession = void (*)(void* userPointer);
struct Config
{
using CanvasSizeModeAlias = ax::NodeEditor::CanvasSizeMode;
const char* SettingsFile;
ConfigSession BeginSaveSession;
ConfigSession EndSaveSession;
ConfigSaveSettings SaveSettings;
ConfigLoadSettings LoadSettings;
ConfigSaveNodeSettings SaveNodeSettings;
ConfigLoadNodeSettings LoadNodeSettings;
void* UserPointer;
ImVector<float> CustomZoomLevels;
CanvasSizeModeAlias CanvasSizeMode;
int DragButtonIndex; // Mouse button index drag action will react to (0-left, 1-right, 2-middle)
int SelectButtonIndex; // Mouse button index select action will react to (0-left, 1-right, 2-middle)
int NavigateButtonIndex; // Mouse button index navigate action will react to (0-left, 1-right, 2-middle)
int ContextMenuButtonIndex; // Mouse button index context menu action will react to (0-left, 1-right, 2-middle)
bool EnableSmoothZoom;
float SmoothZoomPower;
Config()
: SettingsFile("NodeEditor.json")
, BeginSaveSession(nullptr)
, EndSaveSession(nullptr)
, SaveSettings(nullptr)
, LoadSettings(nullptr)
, SaveNodeSettings(nullptr)
, LoadNodeSettings(nullptr)
, UserPointer(nullptr)
, CustomZoomLevels()
, CanvasSizeMode(CanvasSizeModeAlias::FitVerticalView)
, DragButtonIndex(0)
, SelectButtonIndex(0)
, NavigateButtonIndex(1)
, ContextMenuButtonIndex(1)
, EnableSmoothZoom(false)
# ifdef __APPLE__
, SmoothZoomPower(1.1f)
# else
, SmoothZoomPower(1.3f)
# endif
{
}
};
//------------------------------------------------------------------------------
enum StyleColor
{
StyleColor_Bg,
StyleColor_Grid,
StyleColor_NodeBg,
StyleColor_NodeBorder,
StyleColor_HovNodeBorder,
StyleColor_SelNodeBorder,
StyleColor_NodeSelRect,
StyleColor_NodeSelRectBorder,
StyleColor_HovLinkBorder,
StyleColor_SelLinkBorder,
StyleColor_HighlightLinkBorder,
StyleColor_LinkSelRect,
StyleColor_LinkSelRectBorder,
StyleColor_PinRect,
StyleColor_PinRectBorder,
StyleColor_Flow,
StyleColor_FlowMarker,
StyleColor_GroupBg,
StyleColor_GroupBorder,
StyleColor_Count
};
enum StyleVar
{
StyleVar_NodePadding,
StyleVar_NodeRounding,
StyleVar_NodeBorderWidth,
StyleVar_HoveredNodeBorderWidth,
StyleVar_SelectedNodeBorderWidth,
StyleVar_PinRounding,
StyleVar_PinBorderWidth,
StyleVar_LinkStrength,
StyleVar_SourceDirection,
StyleVar_TargetDirection,
StyleVar_ScrollDuration,
StyleVar_FlowMarkerDistance,
StyleVar_FlowSpeed,
StyleVar_FlowDuration,
StyleVar_PivotAlignment,
StyleVar_PivotSize,
StyleVar_PivotScale,
StyleVar_PinCorners,
StyleVar_PinRadius,
StyleVar_PinArrowSize,
StyleVar_PinArrowWidth,
StyleVar_GroupRounding,
StyleVar_GroupBorderWidth,
StyleVar_HighlightConnectedLinks,
StyleVar_SnapLinkToPinDir,
StyleVar_HoveredNodeBorderOffset,
StyleVar_SelectedNodeBorderOffset,
StyleVar_Count
};
struct Style
{
ImVec4 NodePadding;
float NodeRounding;
float NodeBorderWidth;
float HoveredNodeBorderWidth;
float HoverNodeBorderOffset;
float SelectedNodeBorderWidth;
float SelectedNodeBorderOffset;
float PinRounding;
float PinBorderWidth;
float LinkStrength;
ImVec2 SourceDirection;
ImVec2 TargetDirection;
float ScrollDuration;
float FlowMarkerDistance;
float FlowSpeed;
float FlowDuration;
ImVec2 PivotAlignment;
ImVec2 PivotSize;
ImVec2 PivotScale;
float PinCorners;
float PinRadius;
float PinArrowSize;
float PinArrowWidth;
float GroupRounding;
float GroupBorderWidth;
float HighlightConnectedLinks;
float SnapLinkToPinDir; // when true link will start on the line defined by pin direction
ImVec4 Colors[StyleColor_Count];
Style()
{
NodePadding = ImVec4(8, 8, 8, 8);
NodeRounding = 12.0f;
NodeBorderWidth = 1.5f;
HoveredNodeBorderWidth = 3.5f;
HoverNodeBorderOffset = 0.0f;
SelectedNodeBorderWidth = 3.5f;
SelectedNodeBorderOffset = 0.0f;
PinRounding = 4.0f;
PinBorderWidth = 0.0f;
LinkStrength = 100.0f;
SourceDirection = ImVec2(1.0f, 0.0f);
TargetDirection = ImVec2(-1.0f, 0.0f);
ScrollDuration = 0.35f;
FlowMarkerDistance = 30.0f;
FlowSpeed = 150.0f;
FlowDuration = 2.0f;
PivotAlignment = ImVec2(0.5f, 0.5f);
PivotSize = ImVec2(0.0f, 0.0f);
PivotScale = ImVec2(1, 1);
#if IMGUI_VERSION_NUM > 18101
PinCorners = ImDrawFlags_RoundCornersAll;
#else
PinCorners = ImDrawCornerFlags_All;
#endif
PinRadius = 0.0f;
PinArrowSize = 0.0f;
PinArrowWidth = 0.0f;
GroupRounding = 6.0f;
GroupBorderWidth = 1.0f;
HighlightConnectedLinks = 0.0f;
SnapLinkToPinDir = 0.0f;
Colors[StyleColor_Bg] = ImColor( 60, 60, 70, 200);
Colors[StyleColor_Grid] = ImColor(120, 120, 120, 40);
Colors[StyleColor_NodeBg] = ImColor( 32, 32, 32, 200);
Colors[StyleColor_NodeBorder] = ImColor(255, 255, 255, 96);
Colors[StyleColor_HovNodeBorder] = ImColor( 50, 176, 255, 255);
Colors[StyleColor_SelNodeBorder] = ImColor(255, 176, 50, 255);
Colors[StyleColor_NodeSelRect] = ImColor( 5, 130, 255, 64);
Colors[StyleColor_NodeSelRectBorder] = ImColor( 5, 130, 255, 128);
Colors[StyleColor_HovLinkBorder] = ImColor( 50, 176, 255, 255);
Colors[StyleColor_SelLinkBorder] = ImColor(255, 176, 50, 255);
Colors[StyleColor_HighlightLinkBorder]= ImColor(204, 105, 0, 255);
Colors[StyleColor_LinkSelRect] = ImColor( 5, 130, 255, 64);
Colors[StyleColor_LinkSelRectBorder] = ImColor( 5, 130, 255, 128);
Colors[StyleColor_PinRect] = ImColor( 60, 180, 255, 100);
Colors[StyleColor_PinRectBorder] = ImColor( 60, 180, 255, 128);
Colors[StyleColor_Flow] = ImColor(255, 128, 64, 255);
Colors[StyleColor_FlowMarker] = ImColor(255, 128, 64, 255);
Colors[StyleColor_GroupBg] = ImColor( 0, 0, 0, 160);
Colors[StyleColor_GroupBorder] = ImColor(255, 255, 255, 32);
}
};
//------------------------------------------------------------------------------
struct EditorContext;
//------------------------------------------------------------------------------
IMGUI_NODE_EDITOR_API void SetCurrentEditor(EditorContext* ctx);
IMGUI_NODE_EDITOR_API EditorContext* GetCurrentEditor();
IMGUI_NODE_EDITOR_API EditorContext* CreateEditor(const Config* config = nullptr);
IMGUI_NODE_EDITOR_API void DestroyEditor(EditorContext* ctx);
IMGUI_NODE_EDITOR_API const Config& GetConfig(EditorContext* ctx = nullptr);
IMGUI_NODE_EDITOR_API Style& GetStyle();
IMGUI_NODE_EDITOR_API const char* GetStyleColorName(StyleColor colorIndex);
IMGUI_NODE_EDITOR_API void PushStyleColor(StyleColor colorIndex, const ImVec4& color);
IMGUI_NODE_EDITOR_API void PopStyleColor(int count = 1);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, float value);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, const ImVec2& value);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, const ImVec4& value);
IMGUI_NODE_EDITOR_API void PopStyleVar(int count = 1);
IMGUI_NODE_EDITOR_API void Begin(const char* id, const ImVec2& size = ImVec2(0, 0));
IMGUI_NODE_EDITOR_API void End();
IMGUI_NODE_EDITOR_API void BeginNode(NodeId id);
IMGUI_NODE_EDITOR_API void BeginPin(PinId id, PinKind kind);
IMGUI_NODE_EDITOR_API void PinRect(const ImVec2& a, const ImVec2& b);
IMGUI_NODE_EDITOR_API void PinPivotRect(const ImVec2& a, const ImVec2& b);
IMGUI_NODE_EDITOR_API void PinPivotSize(const ImVec2& size);
IMGUI_NODE_EDITOR_API void PinPivotScale(const ImVec2& scale);
IMGUI_NODE_EDITOR_API void PinPivotAlignment(const ImVec2& alignment);
IMGUI_NODE_EDITOR_API void EndPin();
IMGUI_NODE_EDITOR_API void Group(const ImVec2& size);
IMGUI_NODE_EDITOR_API void EndNode();
IMGUI_NODE_EDITOR_API bool BeginGroupHint(NodeId nodeId);
IMGUI_NODE_EDITOR_API ImVec2 GetGroupMin();
IMGUI_NODE_EDITOR_API ImVec2 GetGroupMax();
IMGUI_NODE_EDITOR_API ImDrawList* GetHintForegroundDrawList();
IMGUI_NODE_EDITOR_API ImDrawList* GetHintBackgroundDrawList();
IMGUI_NODE_EDITOR_API void EndGroupHint();
// TODO: Add a way to manage node background channels
IMGUI_NODE_EDITOR_API ImDrawList* GetNodeBackgroundDrawList(NodeId nodeId);
IMGUI_NODE_EDITOR_API bool Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
IMGUI_NODE_EDITOR_API void Flow(LinkId linkId, FlowDirection direction = FlowDirection::Forward);
IMGUI_NODE_EDITOR_API bool BeginCreate(const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool QueryNewLink(PinId* startId, PinId* endId);
IMGUI_NODE_EDITOR_API bool QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool QueryNewNode(PinId* pinId);
IMGUI_NODE_EDITOR_API bool QueryNewNode(PinId* pinId, const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool AcceptNewItem();
IMGUI_NODE_EDITOR_API bool AcceptNewItem(const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API void RejectNewItem();
IMGUI_NODE_EDITOR_API void RejectNewItem(const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API void EndCreate();
IMGUI_NODE_EDITOR_API bool BeginDelete();
IMGUI_NODE_EDITOR_API bool QueryDeletedLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
IMGUI_NODE_EDITOR_API bool QueryDeletedNode(NodeId* nodeId);
IMGUI_NODE_EDITOR_API bool AcceptDeletedItem(bool deleteDependencies = true);
IMGUI_NODE_EDITOR_API void RejectDeletedItem();
IMGUI_NODE_EDITOR_API void EndDelete();
IMGUI_NODE_EDITOR_API void SetNodePosition(NodeId nodeId, const ImVec2& editorPosition);
IMGUI_NODE_EDITOR_API void SetGroupSize(NodeId nodeId, const ImVec2& size);
IMGUI_NODE_EDITOR_API ImVec2 GetNodePosition(NodeId nodeId);
IMGUI_NODE_EDITOR_API ImVec2 GetNodeSize(NodeId nodeId);
IMGUI_NODE_EDITOR_API void CenterNodeOnScreen(NodeId nodeId);
IMGUI_NODE_EDITOR_API void SetNodeZPosition(NodeId nodeId, float z); // Sets node z position, nodes with higher value are drawn over nodes with lower value
IMGUI_NODE_EDITOR_API float GetNodeZPosition(NodeId nodeId); // Returns node z position, defaults is 0.0f
IMGUI_NODE_EDITOR_API void RestoreNodeState(NodeId nodeId);
IMGUI_NODE_EDITOR_API void Suspend();
IMGUI_NODE_EDITOR_API void Resume();
IMGUI_NODE_EDITOR_API bool IsSuspended();
IMGUI_NODE_EDITOR_API bool IsActive();
IMGUI_NODE_EDITOR_API bool HasSelectionChanged();
IMGUI_NODE_EDITOR_API int GetSelectedObjectCount();
IMGUI_NODE_EDITOR_API int GetSelectedNodes(NodeId* nodes, int size);
IMGUI_NODE_EDITOR_API int GetSelectedLinks(LinkId* links, int size);
IMGUI_NODE_EDITOR_API bool IsNodeSelected(NodeId nodeId);
IMGUI_NODE_EDITOR_API bool IsLinkSelected(LinkId linkId);
IMGUI_NODE_EDITOR_API void ClearSelection();
IMGUI_NODE_EDITOR_API void SelectNode(NodeId nodeId, bool append = false);
IMGUI_NODE_EDITOR_API void SelectLink(LinkId linkId, bool append = false);
IMGUI_NODE_EDITOR_API void DeselectNode(NodeId nodeId);
IMGUI_NODE_EDITOR_API void DeselectLink(LinkId linkId);
IMGUI_NODE_EDITOR_API bool DeleteNode(NodeId nodeId);
IMGUI_NODE_EDITOR_API bool DeleteLink(LinkId linkId);
IMGUI_NODE_EDITOR_API bool HasAnyLinks(NodeId nodeId); // Returns true if node has any link connected
IMGUI_NODE_EDITOR_API bool HasAnyLinks(PinId pinId); // Return true if pin has any link connected
IMGUI_NODE_EDITOR_API int BreakLinks(NodeId nodeId); // Break all links connected to this node
IMGUI_NODE_EDITOR_API int BreakLinks(PinId pinId); // Break all links connected to this pin
IMGUI_NODE_EDITOR_API void NavigateToContent(float duration = -1);
IMGUI_NODE_EDITOR_API void NavigateToSelection(bool zoomIn = false, float duration = -1);
IMGUI_NODE_EDITOR_API bool ShowNodeContextMenu(NodeId* nodeId);
IMGUI_NODE_EDITOR_API bool ShowPinContextMenu(PinId* pinId);
IMGUI_NODE_EDITOR_API bool ShowLinkContextMenu(LinkId* linkId);
IMGUI_NODE_EDITOR_API bool ShowBackgroundContextMenu();
IMGUI_NODE_EDITOR_API void EnableShortcuts(bool enable);
IMGUI_NODE_EDITOR_API bool AreShortcutsEnabled();
IMGUI_NODE_EDITOR_API bool BeginShortcut();
IMGUI_NODE_EDITOR_API bool AcceptCut();
IMGUI_NODE_EDITOR_API bool AcceptCopy();
IMGUI_NODE_EDITOR_API bool AcceptPaste();
IMGUI_NODE_EDITOR_API bool AcceptDuplicate();
IMGUI_NODE_EDITOR_API bool AcceptCreateNode();
IMGUI_NODE_EDITOR_API int GetActionContextSize();
IMGUI_NODE_EDITOR_API int GetActionContextNodes(NodeId* nodes, int size);
IMGUI_NODE_EDITOR_API int GetActionContextLinks(LinkId* links, int size);
IMGUI_NODE_EDITOR_API void EndShortcut();
IMGUI_NODE_EDITOR_API float GetCurrentZoom();
IMGUI_NODE_EDITOR_API NodeId GetHoveredNode();
IMGUI_NODE_EDITOR_API PinId GetHoveredPin();
IMGUI_NODE_EDITOR_API LinkId GetHoveredLink();
IMGUI_NODE_EDITOR_API NodeId GetDoubleClickedNode();
IMGUI_NODE_EDITOR_API PinId GetDoubleClickedPin();
IMGUI_NODE_EDITOR_API LinkId GetDoubleClickedLink();
IMGUI_NODE_EDITOR_API bool IsBackgroundClicked();
IMGUI_NODE_EDITOR_API bool IsBackgroundDoubleClicked();
IMGUI_NODE_EDITOR_API ImGuiMouseButton GetBackgroundClickButtonIndex(); // -1 if none
IMGUI_NODE_EDITOR_API ImGuiMouseButton GetBackgroundDoubleClickButtonIndex(); // -1 if none
IMGUI_NODE_EDITOR_API bool GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId); // pass nullptr if particular pin do not interest you
IMGUI_NODE_EDITOR_API bool PinHadAnyLinks(PinId pinId);
IMGUI_NODE_EDITOR_API ImVec2 GetScreenSize();
IMGUI_NODE_EDITOR_API ImVec2 ScreenToCanvas(const ImVec2& pos);
IMGUI_NODE_EDITOR_API ImVec2 CanvasToScreen(const ImVec2& pos);
IMGUI_NODE_EDITOR_API int GetNodeCount(); // Returns number of submitted nodes since Begin() call
IMGUI_NODE_EDITOR_API int GetOrderedNodeIds(NodeId* nodes, int size); // Fills an array with node id's in order they're drawn; up to 'size` elements are set. Returns actual size of filled id's.
//------------------------------------------------------------------------------
namespace Details {
template <typename T, typename Tag>
struct SafeType
{
SafeType(T t)
: m_Value(std::move(t))
{
}
SafeType(const SafeType&) = default;
template <typename T2, typename Tag2>
SafeType(
const SafeType
<
typename std::enable_if<!std::is_same<T, T2>::value, T2>::type,
typename std::enable_if<!std::is_same<Tag, Tag2>::value, Tag2>::type
>&) = delete;
SafeType& operator=(const SafeType&) = default;
explicit operator T() const { return Get(); }
T Get() const { return m_Value; }
private:
T m_Value;
};
template <typename Tag>
struct SafePointerType
: SafeType<uintptr_t, Tag>
{
static const Tag Invalid;
using SafeType<uintptr_t, Tag>::SafeType;
SafePointerType()
: SafePointerType(Invalid)
{
}
template <typename T = void> explicit SafePointerType(T* ptr): SafePointerType(reinterpret_cast<uintptr_t>(ptr)) {}
template <typename T = void> T* AsPointer() const { return reinterpret_cast<T*>(this->Get()); }
explicit operator bool() const { return *this != Invalid; }
};
template <typename Tag>
const Tag SafePointerType<Tag>::Invalid = { 0 };
template <typename Tag>
inline bool operator==(const SafePointerType<Tag>& lhs, const SafePointerType<Tag>& rhs)
{
return lhs.Get() == rhs.Get();
}
template <typename Tag>
inline bool operator!=(const SafePointerType<Tag>& lhs, const SafePointerType<Tag>& rhs)
{
return lhs.Get() != rhs.Get();
}
} // namespace Details
struct NodeId final: Details::SafePointerType<NodeId>
{
using SafePointerType::SafePointerType;
};
struct LinkId final: Details::SafePointerType<LinkId>
{
using SafePointerType::SafePointerType;
};
struct PinId final: Details::SafePointerType<PinId>
{
using SafePointerType::SafePointerType;
};
//------------------------------------------------------------------------------
} // namespace Editor
} // namespace ax
//------------------------------------------------------------------------------
# endif // __IMGUI_NODE_EDITOR_H__

View File

@ -0,0 +1,762 @@
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# include "imgui_node_editor_internal.h"
# include <algorithm>
//------------------------------------------------------------------------------
static ax::NodeEditor::Detail::EditorContext* s_Editor = nullptr;
//------------------------------------------------------------------------------
template <typename C, typename I, typename F>
static int BuildIdList(C& container, I* list, int listSize, F&& accept)
{
if (list != nullptr)
{
int count = 0;
for (auto object : container)
{
if (listSize <= 0)
break;
if (accept(object))
{
list[count] = I(object->ID().AsPointer());
++count;
--listSize;}
}
return count;
}
else
return static_cast<int>(std::count_if(container.begin(), container.end(), accept));
}
//------------------------------------------------------------------------------
ax::NodeEditor::EditorContext* ax::NodeEditor::CreateEditor(const Config* config)
{
return reinterpret_cast<ax::NodeEditor::EditorContext*>(new ax::NodeEditor::Detail::EditorContext(config));
}
void ax::NodeEditor::DestroyEditor(EditorContext* ctx)
{
auto lastContext = GetCurrentEditor();
// Set context we're about to destroy as current, to give callback valid context
if (lastContext != ctx)
SetCurrentEditor(ctx);
auto editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
delete editor;
if (lastContext != ctx)
SetCurrentEditor(lastContext);
}
const ax::NodeEditor::Config& ax::NodeEditor::GetConfig(EditorContext* ctx)
{
if (ctx == nullptr)
ctx = GetCurrentEditor();
if (ctx)
{
auto editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
return editor->GetConfig();
}
else
{
static Config s_EmptyConfig;
return s_EmptyConfig;
}
}
void ax::NodeEditor::SetCurrentEditor(EditorContext* ctx)
{
s_Editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
}
ax::NodeEditor::EditorContext* ax::NodeEditor::GetCurrentEditor()
{
return reinterpret_cast<ax::NodeEditor::EditorContext*>(s_Editor);
}
ax::NodeEditor::Style& ax::NodeEditor::GetStyle()
{
return s_Editor->GetStyle();
}
const char* ax::NodeEditor::GetStyleColorName(StyleColor colorIndex)
{
return s_Editor->GetStyle().GetColorName(colorIndex);
}
void ax::NodeEditor::PushStyleColor(StyleColor colorIndex, const ImVec4& color)
{
s_Editor->GetStyle().PushColor(colorIndex, color);
}
void ax::NodeEditor::PopStyleColor(int count)
{
s_Editor->GetStyle().PopColor(count);
}
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, float value)
{
s_Editor->GetStyle().PushVar(varIndex, value);
}
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, const ImVec2& value)
{
s_Editor->GetStyle().PushVar(varIndex, value);
}
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, const ImVec4& value)
{
s_Editor->GetStyle().PushVar(varIndex, value);
}
void ax::NodeEditor::PopStyleVar(int count)
{
s_Editor->GetStyle().PopVar(count);
}
void ax::NodeEditor::Begin(const char* id, const ImVec2& size)
{
s_Editor->Begin(id, size);
}
void ax::NodeEditor::End()
{
s_Editor->End();
}
void ax::NodeEditor::BeginNode(NodeId id)
{
s_Editor->GetNodeBuilder().Begin(id);
}
void ax::NodeEditor::BeginPin(PinId id, PinKind kind)
{
s_Editor->GetNodeBuilder().BeginPin(id, kind);
}
void ax::NodeEditor::PinRect(const ImVec2& a, const ImVec2& b)
{
s_Editor->GetNodeBuilder().PinRect(a, b);
}
void ax::NodeEditor::PinPivotRect(const ImVec2& a, const ImVec2& b)
{
s_Editor->GetNodeBuilder().PinPivotRect(a, b);
}
void ax::NodeEditor::PinPivotSize(const ImVec2& size)
{
s_Editor->GetNodeBuilder().PinPivotSize(size);
}
void ax::NodeEditor::PinPivotScale(const ImVec2& scale)
{
s_Editor->GetNodeBuilder().PinPivotScale(scale);
}
void ax::NodeEditor::PinPivotAlignment(const ImVec2& alignment)
{
s_Editor->GetNodeBuilder().PinPivotAlignment(alignment);
}
void ax::NodeEditor::EndPin()
{
s_Editor->GetNodeBuilder().EndPin();
}
void ax::NodeEditor::Group(const ImVec2& size)
{
s_Editor->GetNodeBuilder().Group(size);
}
void ax::NodeEditor::EndNode()
{
s_Editor->GetNodeBuilder().End();
}
bool ax::NodeEditor::BeginGroupHint(NodeId nodeId)
{
return s_Editor->GetHintBuilder().Begin(nodeId);
}
ImVec2 ax::NodeEditor::GetGroupMin()
{
return s_Editor->GetHintBuilder().GetGroupMin();
}
ImVec2 ax::NodeEditor::GetGroupMax()
{
return s_Editor->GetHintBuilder().GetGroupMax();
}
ImDrawList* ax::NodeEditor::GetHintForegroundDrawList()
{
return s_Editor->GetHintBuilder().GetForegroundDrawList();
}
ImDrawList* ax::NodeEditor::GetHintBackgroundDrawList()
{
return s_Editor->GetHintBuilder().GetBackgroundDrawList();
}
void ax::NodeEditor::EndGroupHint()
{
s_Editor->GetHintBuilder().End();
}
ImDrawList* ax::NodeEditor::GetNodeBackgroundDrawList(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
return s_Editor->GetNodeBuilder().GetUserBackgroundDrawList(node);
else
return nullptr;
}
bool ax::NodeEditor::Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color/* = ImVec4(1, 1, 1, 1)*/, float thickness/* = 1.0f*/)
{
return s_Editor->DoLink(id, startPinId, endPinId, ImColor(color), thickness);
}
void ax::NodeEditor::Flow(LinkId linkId, FlowDirection direction)
{
if (auto link = s_Editor->FindLink(linkId))
s_Editor->Flow(link, direction);
}
bool ax::NodeEditor::BeginCreate(const ImVec4& color, float thickness)
{
auto& context = s_Editor->GetItemCreator();
if (context.Begin())
{
context.SetStyle(ImColor(color), thickness);
return true;
}
else
return false;
}
bool ax::NodeEditor::QueryNewLink(PinId* startId, PinId* endId)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
return context.QueryLink(startId, endId) == Result::True;
}
bool ax::NodeEditor::QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
auto result = context.QueryLink(startId, endId);
if (result != Result::Indeterminate)
context.SetStyle(ImColor(color), thickness);
return result == Result::True;
}
bool ax::NodeEditor::QueryNewNode(PinId* pinId)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
return context.QueryNode(pinId) == Result::True;
}
bool ax::NodeEditor::QueryNewNode(PinId* pinId, const ImVec4& color, float thickness)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
auto result = context.QueryNode(pinId);
if (result != Result::Indeterminate)
context.SetStyle(ImColor(color), thickness);
return result == Result::True;
}
bool ax::NodeEditor::AcceptNewItem()
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
return context.AcceptItem() == Result::True;
}
bool ax::NodeEditor::AcceptNewItem(const ImVec4& color, float thickness)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
auto result = context.AcceptItem();
if (result != Result::Indeterminate)
context.SetStyle(ImColor(color), thickness);
return result == Result::True;
}
void ax::NodeEditor::RejectNewItem()
{
auto& context = s_Editor->GetItemCreator();
context.RejectItem();
}
void ax::NodeEditor::RejectNewItem(const ImVec4& color, float thickness)
{
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
auto& context = s_Editor->GetItemCreator();
if (context.RejectItem() != Result::Indeterminate)
context.SetStyle(ImColor(color), thickness);
}
void ax::NodeEditor::EndCreate()
{
auto& context = s_Editor->GetItemCreator();
context.End();
}
bool ax::NodeEditor::BeginDelete()
{
auto& context = s_Editor->GetItemDeleter();
return context.Begin();
}
bool ax::NodeEditor::QueryDeletedLink(LinkId* linkId, PinId* startId, PinId* endId)
{
auto& context = s_Editor->GetItemDeleter();
return context.QueryLink(linkId, startId, endId);
}
bool ax::NodeEditor::QueryDeletedNode(NodeId* nodeId)
{
auto& context = s_Editor->GetItemDeleter();
return context.QueryNode(nodeId);
}
bool ax::NodeEditor::AcceptDeletedItem(bool deleteDependencies)
{
auto& context = s_Editor->GetItemDeleter();
return context.AcceptItem(deleteDependencies);
}
void ax::NodeEditor::RejectDeletedItem()
{
auto& context = s_Editor->GetItemDeleter();
context.RejectItem();
}
void ax::NodeEditor::EndDelete()
{
auto& context = s_Editor->GetItemDeleter();
context.End();
}
void ax::NodeEditor::SetNodePosition(NodeId nodeId, const ImVec2& position)
{
s_Editor->SetNodePosition(nodeId, position);
}
void ax::NodeEditor::SetGroupSize(NodeId nodeId, const ImVec2& size)
{
s_Editor->SetGroupSize(nodeId, size);
}
ImVec2 ax::NodeEditor::GetNodePosition(NodeId nodeId)
{
return s_Editor->GetNodePosition(nodeId);
}
ImVec2 ax::NodeEditor::GetNodeSize(NodeId nodeId)
{
return s_Editor->GetNodeSize(nodeId);
}
void ax::NodeEditor::CenterNodeOnScreen(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
node->CenterOnScreenInNextFrame();
}
void ax::NodeEditor::SetNodeZPosition(NodeId nodeId, float z)
{
s_Editor->SetNodeZPosition(nodeId, z);
}
float ax::NodeEditor::GetNodeZPosition(NodeId nodeId)
{
return s_Editor->GetNodeZPosition(nodeId);
}
void ax::NodeEditor::RestoreNodeState(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
s_Editor->MarkNodeToRestoreState(node);
}
void ax::NodeEditor::Suspend()
{
s_Editor->Suspend();
}
void ax::NodeEditor::Resume()
{
s_Editor->Resume();
}
bool ax::NodeEditor::IsSuspended()
{
return s_Editor->IsSuspended();
}
bool ax::NodeEditor::IsActive()
{
return s_Editor->IsFocused();
}
bool ax::NodeEditor::HasSelectionChanged()
{
return s_Editor->HasSelectionChanged();
}
int ax::NodeEditor::GetSelectedObjectCount()
{
return (int)s_Editor->GetSelectedObjects().size();
}
int ax::NodeEditor::GetSelectedNodes(NodeId* nodes, int size)
{
return BuildIdList(s_Editor->GetSelectedObjects(), nodes, size, [](auto object)
{
return object->AsNode() != nullptr;
});
}
int ax::NodeEditor::GetSelectedLinks(LinkId* links, int size)
{
return BuildIdList(s_Editor->GetSelectedObjects(), links, size, [](auto object)
{
return object->AsLink() != nullptr;
});
}
bool ax::NodeEditor::IsNodeSelected(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
return s_Editor->IsSelected(node);
else
return false;
}
bool ax::NodeEditor::IsLinkSelected(LinkId linkId)
{
if (auto link = s_Editor->FindLink(linkId))
return s_Editor->IsSelected(link);
else
return false;
}
void ax::NodeEditor::ClearSelection()
{
s_Editor->ClearSelection();
}
void ax::NodeEditor::SelectNode(NodeId nodeId, bool append)
{
if (auto node = s_Editor->FindNode(nodeId))
{
if (append)
s_Editor->SelectObject(node);
else
s_Editor->SetSelectedObject(node);
}
}
void ax::NodeEditor::SelectLink(LinkId linkId, bool append)
{
if (auto link = s_Editor->FindLink(linkId))
{
if (append)
s_Editor->SelectObject(link);
else
s_Editor->SetSelectedObject(link);
}
}
void ax::NodeEditor::DeselectNode(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
s_Editor->DeselectObject(node);
}
void ax::NodeEditor::DeselectLink(LinkId linkId)
{
if (auto link = s_Editor->FindLink(linkId))
s_Editor->DeselectObject(link);
}
bool ax::NodeEditor::DeleteNode(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
return s_Editor->GetItemDeleter().Add(node);
else
return false;
}
bool ax::NodeEditor::DeleteLink(LinkId linkId)
{
if (auto link = s_Editor->FindLink(linkId))
return s_Editor->GetItemDeleter().Add(link);
else
return false;
}
bool ax::NodeEditor::HasAnyLinks(NodeId nodeId)
{
return s_Editor->HasAnyLinks(nodeId);
}
bool ax::NodeEditor::HasAnyLinks(PinId pinId)
{
return s_Editor->HasAnyLinks(pinId);
}
int ax::NodeEditor::BreakLinks(NodeId nodeId)
{
return s_Editor->BreakLinks(nodeId);
}
int ax::NodeEditor::BreakLinks(PinId pinId)
{
return s_Editor->BreakLinks(pinId);
}
void ax::NodeEditor::NavigateToContent(float duration)
{
s_Editor->NavigateTo(s_Editor->GetContentBounds(), true, duration);
}
void ax::NodeEditor::NavigateToSelection(bool zoomIn, float duration)
{
s_Editor->NavigateTo(s_Editor->GetSelectionBounds(), zoomIn, duration);
}
bool ax::NodeEditor::ShowNodeContextMenu(NodeId* nodeId)
{
return s_Editor->GetContextMenu().ShowNodeContextMenu(nodeId);
}
bool ax::NodeEditor::ShowPinContextMenu(PinId* pinId)
{
return s_Editor->GetContextMenu().ShowPinContextMenu(pinId);
}
bool ax::NodeEditor::ShowLinkContextMenu(LinkId* linkId)
{
return s_Editor->GetContextMenu().ShowLinkContextMenu(linkId);
}
bool ax::NodeEditor::ShowBackgroundContextMenu()
{
return s_Editor->GetContextMenu().ShowBackgroundContextMenu();
}
void ax::NodeEditor::EnableShortcuts(bool enable)
{
s_Editor->EnableShortcuts(enable);
}
bool ax::NodeEditor::AreShortcutsEnabled()
{
return s_Editor->AreShortcutsEnabled();
}
bool ax::NodeEditor::BeginShortcut()
{
return s_Editor->GetShortcut().Begin();
}
bool ax::NodeEditor::AcceptCut()
{
return s_Editor->GetShortcut().AcceptCut();
}
bool ax::NodeEditor::AcceptCopy()
{
return s_Editor->GetShortcut().AcceptCopy();
}
bool ax::NodeEditor::AcceptPaste()
{
return s_Editor->GetShortcut().AcceptPaste();
}
bool ax::NodeEditor::AcceptDuplicate()
{
return s_Editor->GetShortcut().AcceptDuplicate();
}
bool ax::NodeEditor::AcceptCreateNode()
{
return s_Editor->GetShortcut().AcceptCreateNode();
}
int ax::NodeEditor::GetActionContextSize()
{
return static_cast<int>(s_Editor->GetShortcut().m_Context.size());
}
int ax::NodeEditor::GetActionContextNodes(NodeId* nodes, int size)
{
return BuildIdList(s_Editor->GetSelectedObjects(), nodes, size, [](auto object)
{
return object->AsNode() != nullptr;
});
}
int ax::NodeEditor::GetActionContextLinks(LinkId* links, int size)
{
return BuildIdList(s_Editor->GetSelectedObjects(), links, size, [](auto object)
{
return object->AsLink() != nullptr;
});
}
void ax::NodeEditor::EndShortcut()
{
return s_Editor->GetShortcut().End();
}
float ax::NodeEditor::GetCurrentZoom()
{
return s_Editor->GetView().InvScale;
}
ax::NodeEditor::NodeId ax::NodeEditor::GetHoveredNode()
{
return s_Editor->GetHoveredNode();
}
ax::NodeEditor::PinId ax::NodeEditor::GetHoveredPin()
{
return s_Editor->GetHoveredPin();
}
ax::NodeEditor::LinkId ax::NodeEditor::GetHoveredLink()
{
return s_Editor->GetHoveredLink();
}
ax::NodeEditor::NodeId ax::NodeEditor::GetDoubleClickedNode()
{
return s_Editor->GetDoubleClickedNode();
}
ax::NodeEditor::PinId ax::NodeEditor::GetDoubleClickedPin()
{
return s_Editor->GetDoubleClickedPin();
}
ax::NodeEditor::LinkId ax::NodeEditor::GetDoubleClickedLink()
{
return s_Editor->GetDoubleClickedLink();
}
bool ax::NodeEditor::IsBackgroundClicked()
{
return s_Editor->IsBackgroundClicked();
}
bool ax::NodeEditor::IsBackgroundDoubleClicked()
{
return s_Editor->IsBackgroundDoubleClicked();
}
ImGuiMouseButton ax::NodeEditor::GetBackgroundClickButtonIndex()
{
return s_Editor->GetBackgroundClickButtonIndex();
}
ImGuiMouseButton ax::NodeEditor::GetBackgroundDoubleClickButtonIndex()
{
return s_Editor->GetBackgroundDoubleClickButtonIndex();
}
bool ax::NodeEditor::GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId)
{
auto link = s_Editor->FindLink(linkId);
if (!link)
return false;
if (startPinId)
*startPinId = link->m_StartPin->m_ID;
if (endPinId)
*endPinId = link->m_EndPin->m_ID;
return true;
}
bool ax::NodeEditor::PinHadAnyLinks(PinId pinId)
{
return s_Editor->PinHadAnyLinks(pinId);
}
ImVec2 ax::NodeEditor::GetScreenSize()
{
return s_Editor->GetRect().GetSize();
}
ImVec2 ax::NodeEditor::ScreenToCanvas(const ImVec2& pos)
{
return s_Editor->ToCanvas(pos);
}
ImVec2 ax::NodeEditor::CanvasToScreen(const ImVec2& pos)
{
return s_Editor->ToScreen(pos);
}
int ax::NodeEditor::GetNodeCount()
{
return s_Editor->CountLiveNodes();
}
int ax::NodeEditor::GetOrderedNodeIds(NodeId* nodes, int size)
{
return s_Editor->GetNodeIds(nodes, size);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# ifndef __IMGUI_NODE_EDITOR_INTERNAL_INL__
# define __IMGUI_NODE_EDITOR_INTERNAL_INL__
# pragma once
//------------------------------------------------------------------------------
# include "imgui_node_editor_internal.h"
//------------------------------------------------------------------------------
namespace ax {
namespace NodeEditor {
namespace Detail {
//------------------------------------------------------------------------------
//inline ImRect ToRect(const ax::rectf& rect)
//{
// return ImRect(
// to_imvec(rect.top_left()),
// to_imvec(rect.bottom_right())
// );
//}
//
//inline ImRect ToRect(const ax::rect& rect)
//{
// return ImRect(
// to_imvec(rect.top_left()),
// to_imvec(rect.bottom_right())
// );
//}
inline ImRect ImGui_GetItemRect()
{
return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
}
inline ImVec2 ImGui_GetMouseClickPos(ImGuiMouseButton buttonIndex)
{
if (ImGui::IsMouseDown(buttonIndex))
return ImGui::GetIO().MouseClickedPos[buttonIndex];
else
return ImGui::GetMousePos();
}
//------------------------------------------------------------------------------
} // namespace Detail
} // namespace Editor
} // namespace ax
//------------------------------------------------------------------------------
# endif // __IMGUI_NODE_EDITOR_INTERNAL_INL__

View File

@ -0,0 +1,44 @@
const zgui = @import("gui.zig");
const SDL_GL_Context = *anyopaque;
const SDL_Window = *anyopaque;
const SDL_Event = *anyopaque;
pub fn init(window: SDL_Window, context: SDL_GL_Context, glsl_version: []const u8) void {
if (!ImGui_ImplSDL2_InitForOpenGL(window, context)) unreachable;
if (!ImGui_ImplOpenGL3_Init(glsl_version.ptr)) unreachable;
}
pub fn deinit() void {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
}
pub fn newFrame(width: f32, height: f32) void {
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame();
zgui.io.setDisplaySize(width, height);
zgui.io.setDisplayFramebufferScale(1.0, 1.0);
zgui.newFrame();
}
pub fn draw() void {
zgui.render();
ImGui_ImplOpenGL3_RenderDrawData(zgui.getDrawData());
}
pub fn processEvent(event: SDL_Event) bool {
return ImGui_ImplSDL2_ProcessEvent(event);
}
extern fn ImGui_ImplSDL2_InitForOpenGL(window: SDL_Window, sdl_gl_context: SDL_GL_Context) bool;
extern fn ImGui_ImplSDL2_Shutdown() void;
extern fn ImGui_ImplSDL2_NewFrame() void;
extern fn ImGui_ImplSDL2_ProcessEvent(event: SDL_Event) bool;
extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*]const u8) bool;
extern fn ImGui_ImplOpenGL3_Shutdown() void;
extern fn ImGui_ImplOpenGL3_NewFrame() void;
extern fn ImGui_ImplOpenGL3_RenderDrawData(draw_data: *anyopaque) void;

300
src/gizmo.zig Normal file
View File

@ -0,0 +1,300 @@
const gui = @import("gui.zig");
const DrawList = gui.DrawList;
pub const Matrix = [16]f32;
pub const Vector = [3]f32;
/// [-x, -y, -z, x, y, z]
pub const Bounds = [6]f32;
pub const Operation = packed struct(u32) {
translate_x: bool = false,
translate_y: bool = false,
translate_z: bool = false,
rotate_x: bool = false,
rotate_y: bool = false,
rotate_z: bool = false,
rotate_screen: bool = false,
scale_x: bool = false,
scale_y: bool = false,
scale_z: bool = false,
bounds: bool = false,
scale_xu: bool = false,
scale_yu: bool = false,
scale_zu: bool = false,
_padding: u18 = 0,
pub fn translate() Operation {
return .{ .translate_x = true, .translate_y = true, .translate_z = true };
}
pub fn rotate() Operation {
return .{ .rotate_x = true, .rotate_y = true, .rotate_z = true };
}
pub fn scale() Operation {
return .{ .scale_x = true, .scale_y = true, .scale_z = true };
}
pub fn scaleU() Operation {
return .{ .scale_xu = true, .scale_yu = true, .scale_zu = true };
}
pub fn universal() Operation {
return .{
.translate_x = true,
.translate_y = true,
.translate_z = true,
.rotate_x = true,
.rotate_y = true,
.rotate_z = true,
.scale_xu = true,
.scale_yu = true,
.scale_zu = true,
};
}
};
pub const Mode = enum(u32) {
local,
world,
};
pub const Color = enum(u32) {
direction_x,
direction_y,
direction_z,
plane_x,
plane_y,
plane_z,
selection,
inactive,
translation_line,
scale_line,
rotation_using_border,
rotation_using_fill,
hatched_axis_lines,
text,
text_shadow,
};
pub const Style = extern struct {
translation_line_thickness: f32,
translation_line_arrow_size: f32,
rotation_line_thickness: f32,
rotation_outer_line_thickness: f32,
scale_line_thickness: f32,
scale_line_circle_size: f32,
hatched_axis_line_thickness: f32,
center_circle_size: f32,
colors: [@typeInfo(Color).Enum.fields.len][4]f32,
};
//---------------------------------------------------------------------------------------------------------------------|
pub fn setDrawList(draw_list: ?DrawList) void {
zguiGizmo_SetDrawlist(draw_list);
}
pub fn beginFrame() void {
zguiGizmo_BeginFrame();
}
pub fn setImGuiContext(ctx: *anyopaque) void {
zguiGizmo_SetImGuiContext(ctx);
}
pub fn isOver() bool {
return zguiGizmo_IsOver();
}
pub fn isUsing() bool {
return zguiGizmo_IsUsing();
}
pub fn isUsingAny() bool {
return zguiGizmo_IsUsingAny();
}
pub fn setEnabled(enable: bool) void {
zguiGizmo_Enable(enable);
}
pub fn decomposeMatrixToComponents(
matrix: *const Matrix,
translation: *Vector,
rotation: *Vector,
scale: *Vector,
) void {
zguiGizmo_DecomposeMatrixToComponents(&matrix[0], &translation[0], &rotation[0], &scale[0]);
}
pub fn recomposeMatrixFromComponents(
translation: *const Vector,
rotation: *const Vector,
scale: *const Vector,
matrix: *Matrix,
) void {
zguiGizmo_RecomposeMatrixFromComponents(&translation[0], &rotation[0], &scale[0], &matrix[0]);
}
pub fn setRect(x: f32, y: f32, width: f32, height: f32) void {
zguiGizmo_SetRect(x, y, width, height);
}
pub fn setOrthographic(is_orthographic: bool) void {
zguiGizmo_SetOrthographic(is_orthographic);
}
pub fn drawCubes(view: *const Matrix, projection: *const Matrix, matrices: []const Matrix) void {
zguiGizmo_DrawCubes(&view[0], &projection[0], &matrices[0][0], @as(i32, @intCast(matrices.len)));
}
pub fn drawGrid(view: *const Matrix, projection: *const Matrix, matrix: *const Matrix, grid_size: f32) void {
zguiGizmo_DrawGrid(&view[0], &projection[0], &matrix[0], grid_size);
}
pub fn manipulate(
view: *const Matrix,
projection: *const Matrix,
operation: Operation,
mode: Mode,
matrix: *Matrix,
opt: struct {
delta_matrix: ?*Matrix = null,
snap: ?*const Vector = null,
local_bounds: ?*const Bounds = null,
bounds_snap: ?*const Vector = null,
},
) bool {
return zguiGizmo_Manipulate(
&view[0],
&projection[0],
operation,
mode,
&matrix[0],
if (opt.delta_matrix) |arr| &arr[0] else null,
if (opt.snap) |arr| &arr[0] else null,
if (opt.local_bounds) |arr| &arr[0] else null,
if (opt.bounds_snap) |arr| &arr[0] else null,
);
}
/// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
/// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
/// other software are using the same mechanics. But just in case, you are now warned!
pub fn viewManipulate(
view: *Matrix,
length: f32,
position: *const [2]f32,
size: *const [2]f32,
background_color: u32,
) void {
zguiGizmo_ViewManipulate(&view[0], length, position, size, background_color);
}
/// Use this version if you did not call `manipulate` before and you are just using `viewManipulate`
pub fn viewManipulateIndependent(
view: *Matrix,
projection: *const Matrix,
operation: Operation,
mode: Mode,
matrix: *Matrix,
length: f32,
position: *const [2]f32,
size: *const [2]f32,
background_color: u32,
) void {
zguiGizmo_ViewManipulateIndependent(
&view[0],
&projection[0],
operation,
mode,
&matrix[0],
length,
position,
size,
background_color,
);
}
pub fn setID(id: i32) void {
zguiGizmo_SetID(id);
}
pub fn isOverOperation(op: Operation) bool {
return zguiGizmo_IsOverOperation(op);
}
pub fn allowAxisFlip(allowed: bool) void {
zguiGizmo_AllowAxisFlip(allowed);
}
pub fn setAxisLimit(limit: f32) void {
zguiGizmo_SetAxisLimit(limit);
}
pub fn setPlaneLimit(limit: f32) void {
zguiGizmo_SetPlaneLimit(limit);
}
pub fn getStyle() *Style {
return zguiGizmo_GetStyle();
}
//---------------------------------------------------------------------------------------------------------------------|
extern fn zguiGizmo_SetDrawlist(draw_list: ?DrawList) void;
extern fn zguiGizmo_BeginFrame() void;
extern fn zguiGizmo_SetImGuiContext(ctx: *anyopaque) void;
extern fn zguiGizmo_IsOver() bool;
extern fn zguiGizmo_IsUsing() bool;
extern fn zguiGizmo_IsUsingAny() bool;
extern fn zguiGizmo_Enable(enable: bool) void;
extern fn zguiGizmo_DecomposeMatrixToComponents(
matrix: *const f32,
translation: *f32,
rotation: *f32,
scale: *f32,
) void;
extern fn zguiGizmo_RecomposeMatrixFromComponents(
translation: *const f32,
rotation: *const f32,
scale: *const f32,
matrix: *f32,
) void;
extern fn zguiGizmo_SetRect(x: f32, y: f32, width: f32, height: f32) void;
extern fn zguiGizmo_SetOrthographic(is_orthographic: bool) void;
extern fn zguiGizmo_DrawCubes(view: *const f32, projection: *const f32, matrices: *const f32, matrix_count: i32) void;
extern fn zguiGizmo_DrawGrid(view: *const f32, projection: *const f32, matrix: *const f32, grid_size: f32) void;
extern fn zguiGizmo_Manipulate(
view: *const f32,
projection: *const f32,
operation: Operation,
mode: Mode,
matrix: *f32,
delta_matrix: ?*f32,
snap: ?*const f32,
local_bounds: ?*const f32,
bounds_snap: ?*const f32,
) bool;
extern fn zguiGizmo_ViewManipulate(
view: *f32,
length: f32,
position: *const [2]f32,
size: *const [2]f32,
background_color: u32,
) void;
extern fn zguiGizmo_ViewManipulateIndependent(
view: *f32,
projection: *const f32,
operation: Operation,
mode: Mode,
matrix: *f32,
length: f32,
position: *const [2]f32,
size: *const [2]f32,
background_color: u32,
) void;
extern fn zguiGizmo_SetID(id: i32) void;
extern fn zguiGizmo_IsOverOperation(op: Operation) bool;
extern fn zguiGizmo_AllowAxisFlip(value: bool) void;
extern fn zguiGizmo_SetAxisLimit(value: f32) void;
extern fn zguiGizmo_SetPlaneLimit(value: f32) void;
extern fn zguiGizmo_GetStyle() *Style;

View File

@ -5,13 +5,17 @@
// //
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const plot = @import("plot.zig"); pub const plot = @import("plot.zig");
pub const gizmo = @import("gizmo.zig");
pub const node_editor = @import("node_editor.zig");
pub const te = @import("te.zig"); pub const te = @import("te.zig");
pub const backend = switch (@import("zgui_options").backend) { pub const backend = switch (@import("zgui_options").backend) {
.glfw_wgpu => @import("backend_glfw_wgpu.zig"), .glfw_wgpu => @import("backend_glfw_wgpu.zig"),
.glfw_opengl3 => @import("backend_glfw_opengl.zig"), .glfw_opengl3 => @import("backend_glfw_opengl.zig"),
.glfw_dx12 => @import("backend_glfw_dx12.zig"), .glfw_dx12 => @import("backend_glfw_dx12.zig"),
.glfw => @import("backend_glfw.zig"), .glfw => @import("backend_glfw.zig"),
.win32_dx12 => @import("backend_win32_dx12.zig"), .win32_dx12 => @import("backend_win32_dx12.zig"),
.sdl2_opengl3 => @import("backend_sdl2_opengl.zig"),
.no_backend => .{}, .no_backend => .{},
}; };
const te_enabled = @import("zgui_options").with_te; const te_enabled = @import("zgui_options").with_te;
@ -138,8 +142,9 @@ pub const ConfigFlags = packed struct(c_int) {
nav_no_capture_keyboard: bool = false, nav_no_capture_keyboard: bool = false,
no_mouse: bool = false, no_mouse: bool = false,
no_mouse_cursor_change: bool = false, no_mouse_cursor_change: bool = false,
no_keyboard: bool = false,
dock_enable: bool = false, dock_enable: bool = false,
_pading0: u3 = 0, _pading0: u2 = 0,
viewport_enable: bool = false, viewport_enable: bool = false,
_pading1: u3 = 0, _pading1: u3 = 0,
dpi_enable_scale_viewport: bool = false, dpi_enable_scale_viewport: bool = false,
@ -274,6 +279,9 @@ pub const io = struct {
pub const getWantTextInput = zguiIoGetWantTextInput; pub const getWantTextInput = zguiIoGetWantTextInput;
extern fn zguiIoGetWantTextInput() bool; extern fn zguiIoGetWantTextInput() bool;
pub const getFramerate = zguiIoFramerate;
extern fn zguiIoFramerate() f32;
pub fn setIniFilename(filename: ?[*:0]const u8) void { pub fn setIniFilename(filename: ?[*:0]const u8) void {
zguiIoSetIniFilename(filename); zguiIoSetIniFilename(filename);
} }
@ -560,7 +568,6 @@ pub const WindowFlags = packed struct(c_int) {
pub const ChildFlags = packed struct(c_int) { pub const ChildFlags = packed struct(c_int) {
border: bool = false, border: bool = false,
no_move: bool = false,
always_use_window_padding: bool = false, always_use_window_padding: bool = false,
resize_x: bool = false, resize_x: bool = false,
resize_y: bool = false, resize_y: bool = false,
@ -568,6 +575,7 @@ pub const ChildFlags = packed struct(c_int) {
auto_resize_y: bool = false, auto_resize_y: bool = false,
always_auto_resize: bool = false, always_auto_resize: bool = false,
frame_style: bool = false, frame_style: bool = false,
nav_flattened: bool = false,
_padding: u23 = 0, _padding: u23 = 0,
}; };
@ -581,7 +589,8 @@ pub const SliderFlags = packed struct(c_int) {
logarithmic: bool = false, logarithmic: bool = false,
no_round_to_format: bool = false, no_round_to_format: bool = false,
no_input: bool = false, no_input: bool = false,
_padding: u24 = 0, wrap_around: bool = false,
_padding: u23 = 0,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const ButtonFlags = packed struct(c_int) { pub const ButtonFlags = packed struct(c_int) {
@ -599,7 +608,7 @@ pub const Direction = enum(c_int) {
down = 3, down = 3,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const DataType = enum(c_int) { I8, U8, I16, U16, I32, U32, I64, U64, F32, F64 }; pub const DataType = enum(c_int) { I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, BOOL };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const Condition = enum(c_int) { pub const Condition = enum(c_int) {
none = 0, none = 0,
@ -632,6 +641,9 @@ extern fn zguiGetDrawData() DrawData;
/// `pub fn showDemoWindow(popen: ?*bool) void` /// `pub fn showDemoWindow(popen: ?*bool) void`
pub const showDemoWindow = zguiShowDemoWindow; pub const showDemoWindow = zguiShowDemoWindow;
extern fn zguiShowDemoWindow(popen: ?*bool) void; extern fn zguiShowDemoWindow(popen: ?*bool) void;
pub const showMetricsWindow = zguiShowMetricsWindow;
extern fn zguiShowMetricsWindow(popen: ?*bool) void;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// Windows // Windows
@ -910,7 +922,8 @@ extern fn zguiDockSpace(str_id: [*:0]const u8, size: *const [2]f32, flags: DockN
pub fn DockSpace(str_id: [:0]const u8, size: [2]f32, flags: DockNodeFlags) Ident { pub fn DockSpace(str_id: [:0]const u8, size: [2]f32, flags: DockNodeFlags) Ident {
return zguiDockSpace(str_id.ptr, &size, flags); return zguiDockSpace(str_id.ptr, &size, flags);
} }
extern fn zguiDockSpaceOverViewport(viewport: Viewport, flags: DockNodeFlags) Ident;
extern fn zguiDockSpaceOverViewport(dockspace_id: Ident, viewport: Viewport, flags: DockNodeFlags) Ident;
pub const DockSpaceOverViewport = zguiDockSpaceOverViewport; pub const DockSpaceOverViewport = zguiDockSpaceOverViewport;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -946,6 +959,38 @@ extern fn zguiDockBuilderSplitNode(
) Ident; ) Ident;
extern fn zguiDockBuilderFinish(node_id: Ident) void; extern fn zguiDockBuilderFinish(node_id: Ident) void;
//--------------------------------------------------------------------------------------------------
//
// ListClipper
//
//--------------------------------------------------------------------------------------------------
pub const ListClipper = extern struct {
Ctx: *Context,
DisplayStart: c_int,
DisplayEnd: c_int,
ItemsCount: c_int,
ItemsHeight: f32,
StartPosY: f32,
TempData: *anyopaque,
pub const init = zguiListClipper_Init;
extern fn zguiListClipper_Init() ListClipper;
pub fn begin(self: *ListClipper, items_count: ?i32, items_height: ?f32) void {
zguiListClipper_Begin(self, items_count orelse std.math.maxInt(i32), items_height orelse -1.0);
}
extern fn zguiListClipper_Begin(self: *ListClipper, items_count: i32, items_height: f32) void;
pub const end = zguiListClipper_End;
extern fn zguiListClipper_End(self: *ListClipper) void;
pub const includeItemsByIndex = zguiListClipper_IncludeItemsByIndex;
extern fn zguiListClipper_IncludeItemsByIndex(self: *ListClipper, item_begin: i32, item_end: i32) void;
pub const step = zguiListClipper_Step;
extern fn zguiListClipper_Step(self: *ListClipper) bool;
};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// Style // Style
@ -982,7 +1027,9 @@ pub const Style = extern struct {
tab_border_size: f32, tab_border_size: f32,
tab_min_width_for_close_button: f32, tab_min_width_for_close_button: f32,
tab_bar_border_size: f32, tab_bar_border_size: f32,
tab_bar_overline_size: f32,
table_angled_header_angle: f32, table_angled_header_angle: f32,
table_angled_headers_text_align: [2]f32,
color_button_position: Direction, color_button_position: Direction,
button_text_align: [2]f32, button_text_align: [2]f32,
selectable_text_align: [2]f32, selectable_text_align: [2]f32,
@ -1061,11 +1108,13 @@ pub const StyleCol = enum(c_int) {
resize_grip, resize_grip,
resize_grip_hovered, resize_grip_hovered,
resize_grip_active, resize_grip_active,
tab,
tab_hovered, tab_hovered,
tab_active, tab,
tab_unfocused, tab_selected,
tab_unfocused_active, tab_selected_overline,
tab_dimmed,
tab_dimmed_selected,
tab_dimmed_selected_overline,
docking_preview, docking_preview,
docking_empty_bg, docking_empty_bg,
plot_lines, plot_lines,
@ -1077,6 +1126,7 @@ pub const StyleCol = enum(c_int) {
table_border_light, table_border_light,
table_row_bg, table_row_bg,
table_row_bg_alt, table_row_bg_alt,
text_link,
text_selected_bg, text_selected_bg,
drag_drop_target, drag_drop_target,
nav_highlight, nav_highlight,
@ -1142,7 +1192,11 @@ pub const StyleVar = enum(c_int) {
grab_min_size, // 1f grab_min_size, // 1f
grab_rounding, // 1f grab_rounding, // 1f
tab_rounding, // 1f tab_rounding, // 1f
tab_border_size, // 1f
tab_bar_border_size, // 1f tab_bar_border_size, // 1f
tab_bar_overline_size, // 1f
table_angled_headers_angle, // 1f
table_angled_headers_text_align, // 2f
button_text_align, // 2f button_text_align, // 2f
selectable_text_align, // 2f selectable_text_align, // 2f
separator_text_border_size, // 1f separator_text_border_size, // 1f
@ -2317,26 +2371,28 @@ extern fn zguiSliderAngle(
pub const InputTextFlags = packed struct(c_int) { pub const InputTextFlags = packed struct(c_int) {
chars_decimal: bool = false, chars_decimal: bool = false,
chars_hexadecimal: bool = false, chars_hexadecimal: bool = false,
chars_scientific: bool = false,
chars_uppercase: bool = false, chars_uppercase: bool = false,
chars_no_blank: bool = false, chars_no_blank: bool = false,
auto_select_all: bool = false, allow_tab_input: bool = false,
enter_returns_true: bool = false, enter_returns_true: bool = false,
escape_clears_all: bool = false,
ctrl_enter_for_new_line: bool = false,
read_only: bool = false,
password: bool = false,
always_overwrite: bool = false,
auto_select_all: bool = false,
parse_empty_ref_val: bool = false,
display_empty_ref_val: bool = false,
no_horizontal_scroll: bool = false,
no_undo_redo: bool = false,
callback_completion: bool = false, callback_completion: bool = false,
callback_history: bool = false, callback_history: bool = false,
callback_always: bool = false, callback_always: bool = false,
callback_char_filter: bool = false, callback_char_filter: bool = false,
allow_tab_input: bool = false,
ctrl_enter_for_new_line: bool = false,
no_horizontal_scroll: bool = false,
always_overwrite: bool = false,
read_only: bool = false,
password: bool = false,
no_undo_redo: bool = false,
chars_scientific: bool = false,
callback_resize: bool = false, callback_resize: bool = false,
callback_edit: bool = false, callback_edit: bool = false,
escape_clears_all: bool = false, _padding: u9 = 0,
_padding: u11 = 0,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const InputTextCallbackData = extern struct { pub const InputTextCallbackData = extern struct {
@ -2790,9 +2846,10 @@ pub const TreeNodeFlags = packed struct(c_int) {
frame_padding: bool = false, frame_padding: bool = false,
span_avail_width: bool = false, span_avail_width: bool = false,
span_full_width: bool = false, span_full_width: bool = false,
span_text_width: bool = false,
span_all_columns: bool = false, span_all_columns: bool = false,
nav_left_jumps_back_here: bool = false, nav_left_jumps_back_here: bool = false,
_padding: u17 = 0, _padding: u16 = 0,
pub const collapsing_header = TreeNodeFlags{ pub const collapsing_header = TreeNodeFlags{
.framed = true, .framed = true,
@ -2888,12 +2945,13 @@ extern fn zguiSetNextItemOpen(is_open: bool, cond: Condition) void;
// //
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const SelectableFlags = packed struct(c_int) { pub const SelectableFlags = packed struct(c_int) {
dont_close_popups: bool = false, no_auto_close_popups: bool = false,
span_all_columns: bool = false, span_all_columns: bool = false,
allow_double_click: bool = false, allow_double_click: bool = false,
disabled: bool = false, disabled: bool = false,
allow_overlap: bool = false, allow_overlap: bool = false,
_padding: u27 = 0, highlight: bool = false,
_padding: u26 = 0,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
const Selectable = struct { const Selectable = struct {
@ -3022,7 +3080,10 @@ pub const TableFlags = packed struct(c_int) {
sort_multi: bool = false, sort_multi: bool = false,
sort_tristate: bool = false, sort_tristate: bool = false,
_padding: u4 = 0, // Miscellaneous
highlight_hovered_column: bool = false,
_padding: u3 = 0,
}; };
pub const TableRowFlags = packed struct(c_int) { pub const TableRowFlags = packed struct(c_int) {
@ -3206,8 +3267,14 @@ pub const MouseButton = enum(u32) {
pub const isMouseDown = zguiIsMouseDown; pub const isMouseDown = zguiIsMouseDown;
/// `pub fn isMouseClicked(mouse_button: MouseButton) bool` /// `pub fn isMouseClicked(mouse_button: MouseButton) bool`
pub const isMouseClicked = zguiIsMouseClicked; pub const isMouseClicked = zguiIsMouseClicked;
/// `pub fn isMouseReleased(mouse_button: MouseButton) bool`
pub const isMouseReleased = zguiIsMouseReleased;
/// `pub fn isMouseDoubleClicked(mouse_button: MouseButton) bool` /// `pub fn isMouseDoubleClicked(mouse_button: MouseButton) bool`
pub const isMouseDoubleClicked = zguiIsMouseDoubleClicked; pub const isMouseDoubleClicked = zguiIsMouseDoubleClicked;
/// `pub fn getMouseClickedCount(mouse_button: MouseButton) bool`
pub const getMouseClickedCount = zguiGetMouseClickedCount;
/// `pub fn isMouseDragging(mouse_button: MouseButton, lock_threshold: f32) bool`
pub const isMouseDragging = zguiIsMouseDragging;
/// `pub fn isItemClicked(mouse_button: MouseButton) bool` /// `pub fn isItemClicked(mouse_button: MouseButton) bool`
pub const isItemClicked = zguiIsItemClicked; pub const isItemClicked = zguiIsItemClicked;
/// `pub fn isItemVisible() bool` /// `pub fn isItemVisible() bool`
@ -3230,7 +3297,10 @@ pub const isAnyItemActive = zguiIsAnyItemActive;
pub const isAnyItemFocused = zguiIsAnyItemFocused; pub const isAnyItemFocused = zguiIsAnyItemFocused;
extern fn zguiIsMouseDown(mouse_button: MouseButton) bool; extern fn zguiIsMouseDown(mouse_button: MouseButton) bool;
extern fn zguiIsMouseClicked(mouse_button: MouseButton) bool; extern fn zguiIsMouseClicked(mouse_button: MouseButton) bool;
extern fn zguiIsMouseReleased(mouse_button: MouseButton) bool;
extern fn zguiIsMouseDoubleClicked(mouse_button: MouseButton) bool; extern fn zguiIsMouseDoubleClicked(mouse_button: MouseButton) bool;
extern fn zguiGetMouseClickedCount(mouse_button: MouseButton) u32;
extern fn zguiIsMouseDragging(mouse_button: MouseButton, lock_threshold: f32) bool;
extern fn zguiIsItemHovered(flags: HoveredFlags) bool; extern fn zguiIsItemHovered(flags: HoveredFlags) bool;
extern fn zguiIsItemActive() bool; extern fn zguiIsItemActive() bool;
extern fn zguiIsItemFocused() bool; extern fn zguiIsItemFocused() bool;
@ -3244,6 +3314,9 @@ extern fn zguiIsItemToggledOpen() bool;
extern fn zguiIsAnyItemHovered() bool; extern fn zguiIsAnyItemHovered() bool;
extern fn zguiIsAnyItemActive() bool; extern fn zguiIsAnyItemActive() bool;
extern fn zguiIsAnyItemFocused() bool; extern fn zguiIsAnyItemFocused() bool;
pub const isRectVisible = zguiIsRectVisible;
extern fn zguiIsRectVisible(pos: *[2]f32) bool;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// Color Utilities // Color Utilities
@ -3427,6 +3500,8 @@ pub const beginPopup = zguiBeginPopup;
pub const endPopup = zguiEndPopup; pub const endPopup = zguiEndPopup;
/// `pub fn closeCurrentPopup() void` /// `pub fn closeCurrentPopup() void`
pub const closeCurrentPopup = zguiCloseCurrentPopup; pub const closeCurrentPopup = zguiCloseCurrentPopup;
/// `pub fn isPopupOpen(str_id: [:0]const u8, flags: PopupFlags) bool`
pub const isPopupOpen = zguiIsPopupOpen;
extern fn zguiBeginPopupContextWindow() bool; extern fn zguiBeginPopupContextWindow() bool;
extern fn zguiBeginPopupContextItem() bool; extern fn zguiBeginPopupContextItem() bool;
extern fn zguiBeginPopupModal(name: [*:0]const u8, popen: ?*bool, flags: WindowFlags) bool; extern fn zguiBeginPopupModal(name: [*:0]const u8, popen: ?*bool, flags: WindowFlags) bool;
@ -3434,6 +3509,8 @@ extern fn zguiBeginPopup(str_id: [*:0]const u8, flags: WindowFlags) bool;
extern fn zguiEndPopup() void; extern fn zguiEndPopup() void;
extern fn zguiOpenPopup(str_id: [*:0]const u8, flags: PopupFlags) void; extern fn zguiOpenPopup(str_id: [*:0]const u8, flags: PopupFlags) void;
extern fn zguiCloseCurrentPopup() void; extern fn zguiCloseCurrentPopup() void;
extern fn zguiIsPopupOpen(str_id: [*:0]const u8, flags: PopupFlags) bool;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// Tabs // Tabs
@ -3446,9 +3523,10 @@ pub const TabBarFlags = packed struct(c_int) {
no_close_with_middle_mouse_button: bool = false, no_close_with_middle_mouse_button: bool = false,
no_tab_list_scrolling_buttons: bool = false, no_tab_list_scrolling_buttons: bool = false,
no_tooltip: bool = false, no_tooltip: bool = false,
draw_selected_overline: bool = false,
fitting_policy_resize_down: bool = false, fitting_policy_resize_down: bool = false,
fitting_policy_scroll: bool = false, fitting_policy_scroll: bool = false,
_padding: u24 = 0, _padding: u23 = 0,
}; };
pub const TabItemFlags = packed struct(c_int) { pub const TabItemFlags = packed struct(c_int) {
unsaved_document: bool = false, unsaved_document: bool = false,
@ -3566,9 +3644,11 @@ pub const DragDropFlags = packed struct(c_int) {
source_no_hold_open_to_others: bool = false, source_no_hold_open_to_others: bool = false,
source_allow_null_id: bool = false, source_allow_null_id: bool = false,
source_extern: bool = false, source_extern: bool = false,
source_auto_expire_payload: bool = false, payload_auto_expire: bool = false,
payload_no_cross_context: bool = false,
payload_no_cross_process: bool = false,
_padding0: u4 = 0, _padding0: u2 = 0,
accept_before_delivery: bool = false, accept_before_delivery: bool = false,
accept_no_draw_default_rect: bool = false, accept_no_draw_default_rect: bool = false,
@ -4070,7 +4150,7 @@ pub const DrawList = *opaque {
&args.p, &args.p,
args.r, args.r,
args.col, args.col,
args.num_segments, @intCast(args.num_segments),
args.thickness, args.thickness,
); );
} }
@ -4089,7 +4169,7 @@ pub const DrawList = *opaque {
col: u32, col: u32,
num_segments: u32, num_segments: u32,
}) void { }) void {
zguiDrawList_AddNgonFilled(draw_list, &args.p, args.r, args.col, args.num_segments); zguiDrawList_AddNgonFilled(draw_list, &args.p, args.r, args.col, @intCast(args.num_segments));
} }
extern fn zguiDrawList_AddNgonFilled( extern fn zguiDrawList_AddNgonFilled(
draw_list: DrawList, draw_list: DrawList,
@ -4173,7 +4253,7 @@ pub const DrawList = *opaque {
&args.p4, &args.p4,
args.col, args.col,
args.thickness, args.thickness,
args.num_segments, @intCast(args.num_segments),
); );
} }
extern fn zguiDrawList_AddBezierCubic( extern fn zguiDrawList_AddBezierCubic(
@ -4202,7 +4282,7 @@ pub const DrawList = *opaque {
&args.p3, &args.p3,
args.col, args.col,
args.thickness, args.thickness,
args.num_segments, @intCast(args.num_segments),
); );
} }
extern fn zguiDrawList_AddBezierQuadratic( extern fn zguiDrawList_AddBezierQuadratic(
@ -4555,6 +4635,7 @@ test {
const testing = std.testing; const testing = std.testing;
testing.refAllDeclsRecursive(@This()); testing.refAllDeclsRecursive(@This());
if (@import("zgui_options").with_gizmo) testing.refAllDeclsRecursive(gizmo);
init(testing.allocator); init(testing.allocator);
defer deinit(); defer deinit();

501
src/node_editor.zig Normal file
View File

@ -0,0 +1,501 @@
const std = @import("std");
const Style = extern struct {
node_padding: [4]f32 = .{ 8, 8, 8, 8 },
node_rounding: f32 = 12,
node_border_width: f32 = 1.5,
hovered_node_border_width: f32 = 3.5,
hover_node_border_offset: f32 = 0,
selected_node_border_width: f32 = 3.5,
selected_node_border_offset: f32 = 0,
pin_rounding: f32 = 4,
pin_border_width: f32 = 0,
link_strength: f32 = 100,
source_direction: [2]f32 = .{ 1, 0 },
target_direction: [2]f32 = .{ -1, 0 },
scroll_duration: f32 = 0.35,
flow_marker_distance: f32 = 30,
flow_speed: f32 = 150.0,
flow_duration: f32 = 2.0,
pivot_alignment: [2]f32 = .{ 0.5, 0.5 },
pivot_size: [2]f32 = .{ 0, 0 },
pivot_scale: [2]f32 = .{ 1, 1 },
pin_corners: f32 = 240,
pin_radius: f32 = 0,
pin_arrow_size: f32 = 0,
pin_arrow_width: f32 = 0,
group_rounding: f32 = 6,
group_border_width: f32 = 1,
highlight_connected_links: f32 = 0,
snap_link_to_pin_dir: f32 = 0,
colors: [@typeInfo(StyleColor).Enum.fields.len][4]f32,
pub fn getColor(style: Style, idx: StyleColor) [4]f32 {
return style.colors[@intCast(@intFromEnum(idx))];
}
pub fn setColor(style: *Style, idx: StyleColor, color: [4]f32) void {
style.colors[@intCast(@intFromEnum(idx))] = color;
}
};
const StyleColor = enum(c_int) {
bg,
grid,
node_bg,
node_border,
hov_node_border,
sel_node_border,
node_sel_rect,
node_sel_rect_border,
hov_link_border,
sel_link_border,
highlight_link_border,
link_sel_rect,
link_sel_rect_border,
pin_rect,
pin_rect_border,
flow,
flow_marker,
group_bg,
group_border,
count,
};
const StyleVar = enum(c_int) {
node_padding,
node_rounding,
node_border_width,
hovered_node_border_width,
selected_node_border_width,
pin_rounding,
pin_border_width,
link_strength,
source_direction,
target_direction,
scroll_duration,
flow_marker_distance,
flow_speed,
flow_duration,
pivot_alignment,
pivot_size,
pivot_scale,
pin_corners,
pin_radius,
pin_arrow_size,
pin_arrow_width,
group_rounding,
group_border_width,
highlight_connected_links,
snap_link_to_pin_dir,
hovered_node_border_offset,
selected_node_border_offset,
count,
};
pub const EditorContext = opaque {
pub fn create(config: Config) *EditorContext {
return node_editor_CreateEditor(&config);
}
extern fn node_editor_CreateEditor(config: *const Config) *EditorContext;
pub fn destroy(self: *EditorContext) void {
return node_editor_DestroyEditor(self);
}
extern fn node_editor_DestroyEditor(editor: *EditorContext) void;
};
const CanvasSizeMode = enum(c_int) {
FitVerticalView, // Previous view will be scaled to fit new view on Y axis
FitHorizontalView, // Previous view will be scaled to fit new view on X axis
CenterOnly, // Previous view will be centered on new view
};
const SaveNodeSettings = fn (nodeId: NodeId, data: [*]const u8, size: usize, reason: SaveReasonFlags, userPointer: *anyopaque) callconv(.C) bool;
const LoadNodeSettings = fn (nodeId: NodeId, data: [*]u8, userPointer: *anyopaque) callconv(.C) usize;
const SaveSettings = fn (data: [*]const u8, size: usize, reason: SaveReasonFlags, userPointer: *anyopaque) callconv(.C) bool;
const LoadSettings = fn (data: [*]u8, userPointer: *anyopaque) callconv(.C) usize;
const ConfigSession = fn (userPointer: *anyopaque) callconv(.C) void;
const _ImVector = extern struct {
Size: c_int = 0,
Capacity: c_int = 0,
Data: ?*anyopaque = null,
};
pub const Config = extern struct {
settings_file: ?*const u8 = null,
begin_save_session: ?*const ConfigSession = null,
end_save_session: ?*const ConfigSession = null,
save_settings: ?*const SaveSettings = null,
load_settings: ?*const LoadSettings = null,
save_node_settings: ?*const SaveNodeSettings = null,
load_node_settings: ?*const LoadNodeSettings = null,
user_pointer: ?*anyopaque = null,
custom_zoom_levels: _ImVector = .{},
canvas_size_mode: CanvasSizeMode = .FitVerticalView,
drag_button_index: c_int = 0,
select_button_index: c_int = 0,
navigate_button_index: c_int = 1,
context_menu_button_index: c_int = 1,
enable_smooth_zoom: bool = false,
smooth_zoom_power: f32 = 1.1,
};
//
// Editor
//
const SaveReasonFlags = packed struct(u32) {
navigation: bool,
position: bool,
size: bool,
selection: bool,
add_node: bool,
remove_node: bool,
user: bool,
_pad: u25,
};
pub fn setCurrentEditor(editor: ?*EditorContext) void {
node_editor_SetCurrentEditor(editor);
}
extern fn node_editor_SetCurrentEditor(editor: ?*EditorContext) void;
pub fn begin(id: [:0]const u8, size: [2]f32) void {
node_editor_Begin(id, &size);
}
extern fn node_editor_Begin(id: [*c]const u8, size: [*]const f32) void;
pub fn end() void {
node_editor_End();
}
extern fn node_editor_End() void;
pub fn showBackgroundContextMenu() bool {
return node_editor_ShowBackgroundContextMenu();
}
extern fn node_editor_ShowBackgroundContextMenu() bool;
pub fn showNodeContextMenu(id: *NodeId) bool {
return node_editor_ShowNodeContextMenu(id);
}
extern fn node_editor_ShowNodeContextMenu(id: *NodeId) bool;
pub fn showLinkContextMenu(id: *LinkId) bool {
return node_editor_ShowLinkContextMenu(id);
}
extern fn node_editor_ShowLinkContextMenu(id: *LinkId) bool;
pub fn showPinContextMenu(id: *PinId) bool {
return node_editor_ShowPinContextMenu(id);
}
extern fn node_editor_ShowPinContextMenu(id: *PinId) bool;
pub fn suspend_() void {
return node_editor_Suspend();
}
extern fn node_editor_Suspend() void;
pub fn resume_() void {
return node_editor_Resume();
}
extern fn node_editor_Resume() void;
pub fn navigateToContent(duration: f32) void {
node_editor_NavigateToContent(duration);
}
extern fn node_editor_NavigateToContent(duration: f32) void;
pub fn navigateToSelection(zoomIn: bool, duration: f32) void {
node_editor_NavigateToSelection(zoomIn, duration);
}
extern fn node_editor_NavigateToSelection(zoomIn: bool, duration: f32) void;
pub fn selectNode(nodeId: NodeId, append: bool) void {
node_editor_SelectNode(nodeId, append);
}
extern fn node_editor_SelectNode(nodeId: NodeId, append: bool) void;
pub fn selectLink(linkId: LinkId, append: bool) void {
node_editor_SelectLink(linkId, append);
}
extern fn node_editor_SelectLink(linkId: LinkId, append: bool) void;
//
// Node
//
const NodeId = u64;
pub fn beginNode(id: NodeId) void {
node_editor_BeginNode(id);
}
extern fn node_editor_BeginNode(id: NodeId) void;
pub fn endNode() void {
node_editor_EndNode();
}
extern fn node_editor_EndNode() void;
pub fn setNodePosition(id: NodeId, pos: [2]f32) void {
node_editor_SetNodePosition(id, &pos);
}
extern fn node_editor_SetNodePosition(id: NodeId, pos: [*]const f32) void;
pub fn getNodePosition(id: NodeId) [2]f32 {
var pos: [2]f32 = .{ 0, 0 };
node_editor_getNodePosition(id, &pos);
return pos;
}
extern fn node_editor_getNodePosition(id: NodeId, pos: [*]f32) void;
pub fn getNodeSize(id: NodeId) [2]f32 {
var size: [2]f32 = .{ 0, 0 };
node_editor_getNodeSize(id, &size);
return size;
}
extern fn node_editor_getNodeSize(id: NodeId, size: [*]f32) void;
pub fn deleteNode(id: NodeId) bool {
return node_editor_DeleteNode(id);
}
extern fn node_editor_DeleteNode(id: NodeId) bool;
//
// Pin
//
const PinId = u64;
const PinKind = enum(u32) {
input = 0,
output,
};
pub fn beginPin(id: PinId, kind: PinKind) void {
node_editor_BeginPin(id, kind);
}
extern fn node_editor_BeginPin(id: PinId, kind: PinKind) void;
pub fn endPin() void {
node_editor_EndPin();
}
extern fn node_editor_EndPin() void;
pub fn pinHadAnyLinks(pinId: PinId) bool {
return node_editor_PinHadAnyLinks(pinId);
}
extern fn node_editor_PinHadAnyLinks(pinId: PinId) bool;
pub fn pinRect(a: [2]f32, b: [2]f32) void {
node_editor_PinRect(&a, &b);
}
extern fn node_editor_PinRect(a: [*]const f32, b: [*]const f32) void;
pub fn pinPivotRect(a: [2]f32, b: [2]f32) void {
node_editor_PinPivotRect(&a, &b);
}
extern fn node_editor_PinPivotRect(a: [*]const f32, b: [*]const f32) void;
pub fn pinPivotSize(size: [2]f32) void {
node_editor_PinPivotSize(&size);
}
extern fn node_editor_PinPivotSize(size: [*]const f32) void;
pub fn pinPivotScale(scale: [2]f32) void {
node_editor_PinPivotScale(&scale);
}
extern fn node_editor_PinPivotScale(scale: [*]const f32) void;
pub fn pinPivotAlignment(alignment: [2]f32) void {
node_editor_PinPivotAlignment(&alignment);
}
extern fn node_editor_PinPivotAlignment(alignment: [*]const f32) void;
//
// Link
//
const LinkId = u64;
pub fn link(id: LinkId, startPinId: PinId, endPinId: PinId, color: [4]f32, thickness: f32) bool {
return node_editor_Link(id, startPinId, endPinId, &color, thickness);
}
extern fn node_editor_Link(id: LinkId, startPinId: PinId, endPinId: PinId, color: [*]const f32, thickness: f32) bool;
pub fn deleteLink(id: LinkId) bool {
return node_editor_DeleteLink(id);
}
extern fn node_editor_DeleteLink(id: LinkId) bool;
pub fn breakPinLinks(id: PinId) i32 {
return node_editor_BreakPinLinks(id);
}
extern fn node_editor_BreakPinLinks(id: PinId) c_int;
//
// Created
//
pub fn beginCreate() bool {
return node_editor_BeginCreate();
}
extern fn node_editor_BeginCreate() bool;
pub fn endCreate() void {
node_editor_EndCreate();
}
extern fn node_editor_EndCreate() void;
pub fn queryNewLink(startId: *?PinId, endId: *?PinId) bool {
var sid: PinId = 0;
var eid: PinId = 0;
const result = node_editor_QueryNewLink(&sid, &eid);
startId.* = if (sid == 0) null else sid;
endId.* = if (eid == 0) null else eid;
return result;
}
extern fn node_editor_QueryNewLink(startId: *PinId, endId: *PinId) bool;
pub fn acceptNewItem(color: [4]f32, thickness: f32) bool {
return node_editor_AcceptNewItem(&color, thickness);
}
extern fn node_editor_AcceptNewItem(color: [*]const f32, thickness: f32) bool;
pub fn rejectNewItem(color: [4]f32, thickness: f32) void {
node_editor_RejectNewItem(&color, thickness);
}
extern fn node_editor_RejectNewItem(color: [*]const f32, thickness: f32) void;
//
// Deleted
//
pub fn beginDelete() bool {
return node_editor_BeginDelete();
}
extern fn node_editor_BeginDelete() bool;
pub fn endDelete() void {
node_editor_EndDelete();
}
extern fn node_editor_EndDelete() void;
pub fn queryDeletedLink(linkId: *LinkId, startId: ?*PinId, endId: ?*PinId) bool {
const result = node_editor_QueryDeletedLink(linkId, startId, endId);
return result;
}
extern fn node_editor_QueryDeletedLink(linkId: *LinkId, startId: ?*PinId, endId: ?*PinId) bool;
pub fn queryDeletedNode(nodeId: *NodeId) bool {
var nid: LinkId = 0;
const result = node_editor_QueryDeletedNode(&nid);
nodeId.* = nid;
return result;
}
extern fn node_editor_QueryDeletedNode(nodeId: *NodeId) bool;
pub fn acceptDeletedItem(deleteDependencies: bool) bool {
return node_editor_AcceptDeletedItem(deleteDependencies);
}
extern fn node_editor_AcceptDeletedItem(deleteDependencies: bool) bool;
pub fn rejectDeletedItem() void {
node_editor_RejectDeletedItem();
}
extern fn node_editor_RejectDeletedItem() void;
//
// Style
//
pub fn getStyle() Style {
return node_editor_GetStyle();
}
extern fn node_editor_GetStyle() Style;
pub fn getStyleColorName(colorIndex: StyleColor) [*c]const u8 {
return node_editor_GetStyleColorName(colorIndex);
}
extern fn node_editor_GetStyleColorName(colorIndex: StyleColor) [*c]const u8;
pub fn pushStyleColor(colorIndex: StyleColor, color: [4]f32) void {
node_editor_PushStyleColor(colorIndex, &color);
}
extern fn node_editor_PushStyleColor(colorIndex: StyleColor, color: [*]const f32) void;
pub fn popStyleColor(count: c_int) void {
node_editor_PopStyleColor(count);
}
extern fn node_editor_PopStyleColor(count: c_int) void;
pub fn pushStyleVar1f(varIndex: StyleVar, value: f32) void {
node_editor_PushStyleVarF(varIndex, value);
}
extern fn node_editor_PushStyleVarF(varIndex: StyleVar, value: f32) void;
pub fn pushStyleVar2f(varIndex: StyleVar, value: [2]f32) void {
node_editor_PushStyleVar2f(varIndex, &value);
}
extern fn node_editor_PushStyleVar2f(varIndex: StyleVar, value: [*]const f32) void;
pub fn pushStyleVar4f(varIndex: StyleVar, value: [4]f32) void {
node_editor_PushStyleVar4f(varIndex, &value);
}
extern fn node_editor_PushStyleVar4f(varIndex: StyleVar, value: [*]const f32) void;
pub fn popStyleVar(count: c_int) void {
node_editor_PopStyleVar(count);
}
extern fn node_editor_PopStyleVar(count: c_int) void;
//
// Selection
//
pub fn hasSelectionChanged() bool {
return node_editor_HasSelectionChanged();
}
extern fn node_editor_HasSelectionChanged() bool;
pub fn getSelectedObjectCount() c_int {
return node_editor_GetSelectedObjectCount();
}
extern fn node_editor_GetSelectedObjectCount() c_int;
pub fn clearSelection() void {
node_editor_ClearSelection();
}
extern fn node_editor_ClearSelection() void;
pub fn getSelectedNodes(nodes: []NodeId) c_int {
return node_editor_GetSelectedNodes(nodes.ptr, @intCast(nodes.len));
}
extern fn node_editor_GetSelectedNodes(nodes: [*]NodeId, size: c_int) c_int;
pub fn getSelectedLinks(links: []LinkId) c_int {
return node_editor_GetSelectedLinks(links.ptr, @intCast(links.len));
}
extern fn node_editor_GetSelectedLinks(links: [*]LinkId, size: c_int) c_int;
pub fn group(size: [2]f32) void {
node_editor_Group(&size);
}
extern fn node_editor_Group(size: [*]const f32) void;
//
// Drawlist
//
pub fn getHintForegroundDrawList() *anyopaque {
return node_editor_GetHintForegroundDrawList();
}
extern fn node_editor_GetHintForegroundDrawList() *anyopaque;
pub fn getHintBackgroundDrawLis() *anyopaque {
return node_editor_GetHintBackgroundDrawList();
}
extern fn node_editor_GetHintBackgroundDrawList() *anyopaque;
pub fn getNodeBackgroundDrawList(node_id: NodeId) *anyopaque {
return node_editor_GetNodeBackgroundDrawList(node_id);
}
extern fn node_editor_GetNodeBackgroundDrawList(node_id: NodeId) *anyopaque;

View File

@ -210,6 +210,13 @@ extern fn zguiPlot_PushStyleVar1f(idx: StyleVar, v: f32) void;
extern fn zguiPlot_PushStyleVar2f(idx: StyleVar, v: *const [2]f32) void; extern fn zguiPlot_PushStyleVar2f(idx: StyleVar, v: *const [2]f32) void;
extern fn zguiPlot_PopStyleVar(count: i32) void; extern fn zguiPlot_PopStyleVar(count: i32) void;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub fn getLastItemColor() [4]f32 {
var color: [4]f32 = undefined;
zguiPlot_GetLastItemColor(&color);
return color;
}
extern fn zguiPlot_GetLastItemColor(color: *[4]f32) void;
//----------------------------------------------------------------------------------------------
pub const PlotLocation = packed struct(u32) { pub const PlotLocation = packed struct(u32) {
north: bool = false, north: bool = false,
south: bool = false, south: bool = false,
@ -306,11 +313,10 @@ pub const Flags = packed struct(u32) {
no_inputs: bool = false, no_inputs: bool = false,
no_menus: bool = false, no_menus: bool = false,
no_box_select: bool = false, no_box_select: bool = false,
no_child: bool = false,
no_frame: bool = false, no_frame: bool = false,
equal: bool = false, equal: bool = false,
crosshairs: bool = false, crosshairs: bool = false,
_padding: u22 = 0, _padding: u23 = 0,
pub const canvas_only = Flags{ pub const canvas_only = Flags{
.no_title = true, .no_title = true,
@ -537,6 +543,92 @@ extern fn zguiPlot_PlotShaded(
stride: i32, stride: i32,
) void; ) void;
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
pub const BarsFlags = packed struct(u32) {
_reserved0: bool = false,
_reserved1: bool = false,
_reserved2: bool = false,
_reserved3: bool = false,
_reserved4: bool = false,
_reserved5: bool = false,
_reserved6: bool = false,
_reserved7: bool = false,
_reserved8: bool = false,
_reserved9: bool = false,
horizontal: bool = false,
_padding: u21 = 0,
};
fn PlotBarsGen(comptime T: type) type {
return struct {
xv: []const T,
yv: []const T,
bar_size: f64 = 0.67,
flags: BarsFlags = .{},
offset: i32 = 0,
stride: i32 = @sizeOf(T),
};
}
pub fn plotBars(label_id: [:0]const u8, comptime T: type, args: PlotBarsGen(T)) void {
assert(args.xv.len == args.yv.len);
zguiPlot_PlotBars(
label_id,
gui.typeToDataTypeEnum(T),
args.xv.ptr,
args.yv.ptr,
@as(i32, @intCast(args.xv.len)),
args.bar_size,
args.flags,
args.offset,
args.stride,
);
}
extern fn zguiPlot_PlotBars(
label_id: [*:0]const u8,
data_type: gui.DataType,
xv: *const anyopaque,
yv: *const anyopaque,
count: i32,
bar_size: f64,
flags: BarsFlags,
offset: i32,
stride: i32,
) void;
fn PlotBarsValuesGen(comptime T: type) type {
return struct {
v: []const T,
bar_size: f64 = 0.0,
shift: f64 = 0.0,
flags: BarsFlags = .{},
offset: i32 = 0,
stride: i32 = @sizeOf(T),
};
}
pub fn plotBarsValues(label_id: [:0]const u8, comptime T: type, args: PlotBarsValuesGen(T)) void {
assert(args.xv.len == args.yv.len);
zguiPlot_PlotBars(
label_id,
gui.typeToDataTypeEnum(T),
args.v.ptr,
@as(i32, @intCast(args.xv.len)),
args.bar_size,
args.shift,
args.flags,
args.offset,
args.stride,
);
}
extern fn zguiPlot_PlotBarsValues(
label_id: [*:0]const u8,
data_type: gui.DataType,
values: *const anyopaque,
count: i32,
bar_size: f64,
shift: f64,
flags: BarsFlags,
offset: i32,
stride: i32,
) void;
//----------------------------------------------------------------------------------------------
pub const DragToolFlags = packed struct(u32) { pub const DragToolFlags = packed struct(u32) {
no_cursors: bool = false, no_cursors: bool = false,
no_fit: bool = false, no_fit: bool = false,
@ -585,6 +677,11 @@ extern fn zguiPlot_PlotText(
flags: PlotTextFlags, flags: PlotTextFlags,
) void; ) void;
//----------------------------------------------------------------------------------------------
pub fn isPlotHovered() bool {
return zguiPlot_IsPlotHovered();
}
extern fn zguiPlot_IsPlotHovered() bool;
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
/// `pub fn showDemoWindow(popen: ?*bool) void` /// `pub fn showDemoWindow(popen: ?*bool) void`
pub const showDemoWindow = zguiPlot_ShowDemoWindow; pub const showDemoWindow = zguiPlot_ShowDemoWindow;

173
src/zgizmo.cpp Normal file
View File

@ -0,0 +1,173 @@
#include "imgui.h"
#include "ImGuizmo.h"
#include "imgui_internal.h"
#ifndef ZGUI_API
#define ZGUI_API
#endif
//--------------------------------------------------------------------------------------------------
//
// ImGuizmo
//
//--------------------------------------------------------------------------------------------------
extern "C"
{
ZGUI_API void zguiGizmo_SetDrawlist(ImDrawList *drawlist)
{
ImGuizmo::SetDrawlist(drawlist);
}
ZGUI_API void zguiGizmo_BeginFrame()
{
ImGuizmo::BeginFrame();
}
ZGUI_API void zguiGizmo_SetImGuiContext(ImGuiContext *ctx)
{
ImGuizmo::SetImGuiContext(ctx);
}
ZGUI_API bool zguiGizmo_IsOver()
{
return ImGuizmo::IsOver();
}
ZGUI_API bool zguiGizmo_IsUsing()
{
return ImGuizmo::IsUsing();
}
ZGUI_API bool zguiGizmo_IsUsingAny()
{
return ImGuizmo::IsUsingAny();
}
ZGUI_API void zguiGizmo_Enable(bool enable)
{
ImGuizmo::Enable(enable);
}
ZGUI_API void zguiGizmo_DecomposeMatrixToComponents(
const float *matrix,
float *translation,
float *rotation,
float *scale)
{
ImGuizmo::DecomposeMatrixToComponents(matrix, translation, rotation, scale);
}
ZGUI_API void zguiGizmo_RecomposeMatrixFromComponents(
const float *translation,
const float *rotation,
const float *scale,
float *matrix)
{
ImGuizmo::RecomposeMatrixFromComponents(translation, rotation, scale, matrix);
}
ZGUI_API void zguiGizmo_SetRect(float x, float y, float width, float height)
{
ImGuizmo::SetRect(x, y, width, height);
}
ZGUI_API void zguiGizmo_SetOrthographic(bool isOrthographic)
{
ImGuizmo::SetOrthographic(isOrthographic);
}
ZGUI_API void zguiGizmo_DrawCubes(const float *view, const float *projection, const float *matrices, int matrixCount)
{
ImGuizmo::DrawCubes(view, projection, matrices, matrixCount);
}
ZGUI_API void zguiGizmo_DrawGrid(
const float *view,
const float *projection,
const float *matrix,
const float gridSize)
{
ImGuizmo::DrawGrid(view, projection, matrix, gridSize);
}
ZGUI_API bool zguiGizmo_Manipulate(
const float *view,
const float *projection,
ImGuizmo::OPERATION operation,
ImGuizmo::MODE mode,
float *matrix,
float *deltaMatrix = NULL,
const float *snap = NULL,
const float *localBounds = NULL,
const float *boundsSnap = NULL)
{
return ImGuizmo::Manipulate(view, projection, operation, mode, matrix, deltaMatrix, snap, localBounds, boundsSnap);
}
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
ZGUI_API void zguiGizmo_ViewManipulate(
float *view,
float length,
const float position[2],
const float size[2],
ImU32 backgroundColor)
{
const ImVec2 p(position[0], position[1]);
const ImVec2 s(size[0], size[1]);
ImGuizmo::ViewManipulate(view, length, p, s, backgroundColor);
}
// use this version if you did not call Manipulate before and you are just using ViewManipulate
ZGUI_API void zguiGizmo_ViewManipulateIndependent(
float *view,
const float *projection,
ImGuizmo::OPERATION operation,
ImGuizmo::MODE mode,
float *matrix,
float length,
const float position[2],
const float size[2],
ImU32 backgroundColor)
{
const ImVec2 p(position[0], position[1]);
const ImVec2 s(size[0], size[1]);
ImGuizmo::ViewManipulate(view, projection, operation, mode, matrix, length, p, s, backgroundColor);
}
ZGUI_API void zguiGizmo_SetID(int id)
{
ImGuizmo::SetID(id);
}
ZGUI_API bool zguiGizmo_IsOverOperation(ImGuizmo::OPERATION op)
{
return ImGuizmo::IsOver(op);
}
ZGUI_API void zguiGizmo_AllowAxisFlip(bool value)
{
ImGuizmo::AllowAxisFlip(value);
}
ZGUI_API void zguiGizmo_SetAxisLimit(float value)
{
ImGuizmo::SetAxisLimit(value);
}
ZGUI_API void zguiGizmo_SetPlaneLimit(float value)
{
ImGuizmo::SetPlaneLimit(value);
}
ZGUI_API ImGuizmo::Style *zguiGizmo_GetStyle()
{
return &ImGuizmo::GetStyle();
}
} /* extern "C" */

File diff suppressed because it is too large Load Diff

339
src/znode_editor.cpp Normal file
View File

@ -0,0 +1,339 @@
#include "imgui.h"
#include "../node_editor/imgui_node_editor.h"
#ifndef ZGUI_API
#define ZGUI_API
#endif
//--------------------------------------------------------------------------------------------------
//
// ImGUI Node editor
//
//--------------------------------------------------------------------------------------------------
namespace ed = ax::NodeEditor;
extern "C"
{
//
// Editor
//
ZGUI_API ed::EditorContext *node_editor_CreateEditor(ed::Config *cfg)
{
return ed::CreateEditor(cfg);
}
ZGUI_API void node_editor_DestroyEditor(ed::EditorContext *editor)
{
return ed::DestroyEditor(editor);
}
ZGUI_API void node_editor_SetCurrentEditor(ed::EditorContext *editor)
{
return ed::SetCurrentEditor(editor);
}
ZGUI_API void node_editor_Begin(const char *id, const float size[2])
{
ed::Begin(id, ImVec2(size[0], size[1]));
}
ZGUI_API void node_editor_End()
{
ed::End();
}
ZGUI_API bool node_editor_ShowBackgroundContextMenu()
{
return ed::ShowBackgroundContextMenu();
}
ZGUI_API bool node_editor_ShowNodeContextMenu(ed::NodeId *id)
{
return ed::ShowNodeContextMenu(id);
}
ZGUI_API bool node_editor_ShowLinkContextMenu(ed::LinkId *id)
{
return ed::ShowLinkContextMenu(id);
}
ZGUI_API bool node_editor_ShowPinContextMenu(ed::PinId *id)
{
return ed::ShowPinContextMenu(id);
}
ZGUI_API void node_editor_Suspend()
{
ed::Suspend();
}
ZGUI_API void node_editor_Resume()
{
ed::Resume();
}
ZGUI_API void node_editor_NavigateToContent(float duration)
{
ed::NavigateToContent(duration);
}
ZGUI_API void node_editor_NavigateToSelection(bool zoomIn, float duration)
{
ed::NavigateToSelection(zoomIn, duration);
}
ZGUI_API void node_editor_SelectNode(ed::NodeId nodeId, bool append)
{
ed::SelectNode(nodeId, append);
}
ZGUI_API void node_editor_SelectLink(ed::LinkId linkId, bool append)
{
ed::SelectLink(linkId, append);
}
//
// Node
//
ZGUI_API void node_editor_BeginNode(ed::NodeId id)
{
ed::BeginNode(id);
}
ZGUI_API void node_editor_EndNode()
{
ed::EndNode();
}
ZGUI_API void node_editor_SetNodePosition(ed::NodeId id, const float pos[2])
{
ed::SetNodePosition(id, ImVec2(pos[0], pos[1]));
}
ZGUI_API void node_editor_getNodePosition(ed::NodeId id, float *pos)
{
auto node_pos = ed::GetNodePosition(id);
pos[0] = node_pos.x;
pos[1] = node_pos.y;
}
ZGUI_API void node_editor_getNodeSize(ed::NodeId id, float *size)
{
auto node_size = ed::GetNodeSize(id);
size[0] = node_size.x;
size[1] = node_size.y;
}
ZGUI_API bool node_editor_DeleteNode(ed::NodeId id)
{
return ed::DeleteNode(id);
}
//
// Pin
//
ZGUI_API void node_editor_BeginPin(ed::PinId id, ed::PinKind kind)
{
ed::BeginPin(id, kind);
}
ZGUI_API void node_editor_EndPin()
{
ed::EndPin();
}
ZGUI_API bool node_editor_PinHadAnyLinks(ed::PinId pinId)
{
return ed::PinHadAnyLinks(pinId);
}
ZGUI_API void node_editor_PinRect(const float a[2], const float b[2])
{
ed::PinRect(ImVec2(a[0], a[1]), ImVec2(b[0], b[1]));
}
ZGUI_API void node_editor_PinPivotRect(const float a[2], const float b[2])
{
ed::PinPivotRect(ImVec2(a[0], a[1]), ImVec2(b[0], b[1]));
}
ZGUI_API void node_editor_PinPivotSize(const float size[2])
{
ed::PinPivotSize(ImVec2(size[0], size[1]));
}
ZGUI_API void node_editor_PinPivotScale(const float scale[2])
{
ed::PinPivotScale(ImVec2(scale[0], scale[1]));
}
ZGUI_API void node_editor_PinPivotAlignment(const float alignment[2])
{
ed::PinPivotAlignment(ImVec2(alignment[0], alignment[1]));
}
//
// Link
//
ZGUI_API bool node_editor_Link(ed::LinkId id, ed::PinId startPinId, ed::PinId endPinId, const float color[4], float thickness)
{
return ed::Link(id, startPinId, endPinId, ImVec4(color[0], color[1], color[2], color[3]), thickness);
}
ZGUI_API bool node_editor_DeleteLink(ed::LinkId id)
{
return ed::DeleteLink(id);
}
ZGUI_API int node_editor_BreakPinLinks(ed::PinId id)
{
return ed::BreakLinks(id);
}
// Groups
ZGUI_API void node_editor_Group(const float size[2])
{
ed::Group(ImVec2(size[0], size[1]));
}
// Created
ZGUI_API bool node_editor_BeginCreate()
{
return ed::BeginCreate();
}
ZGUI_API void node_editor_EndCreate()
{
ed::EndCreate();
}
ZGUI_API bool node_editor_QueryNewLink(ed::PinId *startId, ed::PinId *endId)
{
return ed::QueryNewLink(startId, endId);
}
ZGUI_API bool node_editor_AcceptNewItem(const float color[4], float thickness)
{
return ed::AcceptNewItem(ImVec4(color[0], color[1], color[2], color[3]), thickness);
}
ZGUI_API void node_editor_RejectNewItem(const float color[4], float thickness)
{
return ed::RejectNewItem(ImVec4(color[0], color[1], color[2], color[3]), thickness);
}
// Delete
ZGUI_API bool node_editor_BeginDelete()
{
return ed::BeginDelete();
}
ZGUI_API void node_editor_EndDelete()
{
ed::EndDelete();
}
ZGUI_API bool node_editor_QueryDeletedLink(ed::LinkId *linkId, ed::PinId *startId, ed::PinId *endId)
{
return ed::QueryDeletedLink(linkId, startId, endId);
}
ZGUI_API bool node_editor_QueryDeletedNode(ed::NodeId *nodeId)
{
return ed::QueryDeletedNode(nodeId);
}
ZGUI_API bool node_editor_AcceptDeletedItem(bool deleteDependencies)
{
return ed::AcceptDeletedItem(deleteDependencies);
}
ZGUI_API void node_editor_RejectDeletedItem()
{
ed::RejectDeletedItem();
}
// Style
ZGUI_API ax::NodeEditor::Style node_editor_GetStyle()
{
return ed::GetStyle();
}
ZGUI_API const char *node_editor_GetStyleColorName(ed::StyleColor colorIndex)
{
return ed::GetStyleColorName(colorIndex);
}
ZGUI_API void node_editor_PushStyleColor(ed::StyleColor colorIndex, const ImVec4 *color)
{
ed::PushStyleColor(colorIndex, *color);
}
ZGUI_API void node_editor_PopStyleColor(int count)
{
ed::PopStyleColor(count);
}
ZGUI_API void node_editor_PushStyleVarF(ed::StyleVar varIndex, float value)
{
ed::PushStyleVar(varIndex, value);
}
ZGUI_API void node_editor_PushStyleVar2f(ed::StyleVar varIndex, const ImVec2 *value)
{
ed::PushStyleVar(varIndex, *value);
}
ZGUI_API void node_editor_PushStyleVar4f(ed::StyleVar varIndex, const ImVec4 *value)
{
ed::PushStyleVar(varIndex, *value);
}
ZGUI_API void node_editor_PopStyleVar(int count)
{
ed::PopStyleVar(count);
}
// Selection
ZGUI_API bool node_editor_HasSelectionChanged()
{
return ed::HasSelectionChanged();
}
ZGUI_API int node_editor_GetSelectedObjectCount()
{
return ed::GetSelectedObjectCount();
}
ZGUI_API void node_editor_ClearSelection()
{
return ed::ClearSelection();
}
ZGUI_API int node_editor_GetSelectedNodes(ed::NodeId *nodes, int size)
{
return ed::GetSelectedNodes(nodes, size);
}
ZGUI_API int node_editor_GetSelectedLinks(ed::LinkId *links, int size)
{
return ed::GetSelectedLinks(links, size);
}
ZGUI_API ImDrawList *node_editor_GetHintForegroundDrawList()
{
return ed::GetHintForegroundDrawList();
}
ZGUI_API ImDrawList *node_editor_GetHintBackgroundDrawList()
{
return ed::GetHintBackgroundDrawList();
}
ZGUI_API ImDrawList *node_editor_GetNodeBackgroundDrawList(ed::NodeId nodeId)
{
return ed::GetNodeBackgroundDrawList(nodeId);
}
} /* extern "C" */

365
src/zplot.cpp Normal file
View File

@ -0,0 +1,365 @@
#include "imgui.h"
#include "implot.h"
#include "imgui_internal.h"
#ifndef ZGUI_API
#define ZGUI_API
#endif
//--------------------------------------------------------------------------------------------------
//
// ImPlot
//
//--------------------------------------------------------------------------------------------------
extern "C"
{
ZGUI_API ImPlotContext *zguiPlot_CreateContext(void)
{
return ImPlot::CreateContext();
}
ZGUI_API void zguiPlot_DestroyContext(ImPlotContext *ctx)
{
ImPlot::DestroyContext(ctx);
}
ZGUI_API ImPlotContext *zguiPlot_GetCurrentContext(void)
{
return ImPlot::GetCurrentContext();
}
ZGUI_API ImPlotStyle zguiPlotStyle_Init(void)
{
return ImPlotStyle();
}
ZGUI_API ImPlotStyle *zguiPlot_GetStyle(void)
{
return &ImPlot::GetStyle();
}
ZGUI_API void zguiPlot_PushStyleColor4f(ImPlotCol idx, const float col[4])
{
ImPlot::PushStyleColor(idx, {col[0], col[1], col[2], col[3]});
}
ZGUI_API void zguiPlot_PushStyleColor1u(ImPlotCol idx, ImU32 col)
{
ImPlot::PushStyleColor(idx, col);
}
ZGUI_API void zguiPlot_PopStyleColor(int count)
{
ImPlot::PopStyleColor(count);
}
ZGUI_API void zguiPlot_PushStyleVar1i(ImPlotStyleVar idx, int var)
{
ImPlot::PushStyleVar(idx, var);
}
ZGUI_API void zguiPlot_PushStyleVar1f(ImPlotStyleVar idx, float var)
{
ImPlot::PushStyleVar(idx, var);
}
ZGUI_API void zguiPlot_PushStyleVar2f(ImPlotStyleVar idx, const float var[2])
{
ImPlot::PushStyleVar(idx, {var[0], var[1]});
}
ZGUI_API void zguiPlot_PopStyleVar(int count)
{
ImPlot::PopStyleVar(count);
}
ZGUI_API void zguiPlot_SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags)
{
ImPlot::SetupLegend(location, flags);
}
ZGUI_API void zguiPlot_SetupAxis(ImAxis axis, const char *label, ImPlotAxisFlags flags)
{
ImPlot::SetupAxis(axis, label, flags);
}
ZGUI_API void zguiPlot_SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond)
{
ImPlot::SetupAxisLimits(axis, v_min, v_max, cond);
}
ZGUI_API void zguiPlot_SetupFinish(void)
{
ImPlot::SetupFinish();
}
ZGUI_API bool zguiPlot_BeginPlot(const char *title_id, float width, float height, ImPlotFlags flags)
{
return ImPlot::BeginPlot(title_id, {width, height}, flags);
}
ZGUI_API void zguiPlot_PlotLineValues(
const char *label_id,
ImGuiDataType data_type,
const void *values,
int count,
double xscale,
double x0,
ImPlotLineFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotLine(label_id, (const ImS8 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotLine(label_id, (const ImU8 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotLine(label_id, (const ImS16 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotLine(label_id, (const ImU16 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotLine(label_id, (const ImS32 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotLine(label_id, (const ImU32 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotLine(label_id, (const float *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotLine(label_id, (const double *)values, count, xscale, x0, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotLine(
const char *label_id,
ImGuiDataType data_type,
const void *xv,
const void *yv,
int count,
ImPlotLineFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotLine(label_id, (const ImS8 *)xv, (const ImS8 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotLine(label_id, (const ImU8 *)xv, (const ImU8 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotLine(label_id, (const ImS16 *)xv, (const ImS16 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotLine(label_id, (const ImU16 *)xv, (const ImU16 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotLine(label_id, (const ImS32 *)xv, (const ImS32 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotLine(label_id, (const ImU32 *)xv, (const ImU32 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotLine(label_id, (const float *)xv, (const float *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotLine(label_id, (const double *)xv, (const double *)yv, count, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotScatter(
const char *label_id,
ImGuiDataType data_type,
const void *xv,
const void *yv,
int count,
ImPlotScatterFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotScatter(label_id, (const ImS8 *)xv, (const ImS8 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotScatter(label_id, (const ImU8 *)xv, (const ImU8 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotScatter(label_id, (const ImS16 *)xv, (const ImS16 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotScatter(label_id, (const ImU16 *)xv, (const ImU16 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotScatter(label_id, (const ImS32 *)xv, (const ImS32 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotScatter(label_id, (const ImU32 *)xv, (const ImU32 *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotScatter(label_id, (const float *)xv, (const float *)yv, count, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotScatter(label_id, (const double *)xv, (const double *)yv, count, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotScatterValues(
const char *label_id,
ImGuiDataType data_type,
const void *values,
int count,
double xscale,
double x0,
ImPlotScatterFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotScatter(label_id, (const ImS8 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotScatter(label_id, (const ImU8 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotScatter(label_id, (const ImS16 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotScatter(label_id, (const ImU16 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotScatter(label_id, (const ImS32 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotScatter(label_id, (const ImU32 *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotScatter(label_id, (const float *)values, count, xscale, x0, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotScatter(label_id, (const double *)values, count, xscale, x0, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotShaded(
const char *label_id,
ImGuiDataType data_type,
const void *xv,
const void *yv,
int count,
double yref,
ImPlotShadedFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotShaded(label_id, (const ImS8 *)xv, (const ImS8 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotShaded(label_id, (const ImU8 *)xv, (const ImU8 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotShaded(label_id, (const ImS16 *)xv, (const ImS16 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotShaded(label_id, (const ImU16 *)xv, (const ImU16 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotShaded(label_id, (const ImS32 *)xv, (const ImS32 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotShaded(label_id, (const ImU32 *)xv, (const ImU32 *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotShaded(label_id, (const float *)xv, (const float *)yv, count, yref, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotShaded(label_id, (const double *)xv, (const double *)yv, count, yref, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotBars(
const char *label_id,
ImGuiDataType data_type,
const void *xv,
const void *yv,
int count,
double bar_size,
ImPlotBarsFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotBars(label_id, (const ImS8 *)xv, (const ImS8 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotBars(label_id, (const ImU8 *)xv, (const ImU8 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotBars(label_id, (const ImS16 *)xv, (const ImS16 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotBars(label_id, (const ImU16 *)xv, (const ImU16 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotBars(label_id, (const ImS32 *)xv, (const ImS32 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotBars(label_id, (const ImU32 *)xv, (const ImU32 *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotBars(label_id, (const float *)xv, (const float *)yv, count, bar_size, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotBars(label_id, (const double *)xv, (const double *)yv, count, bar_size, flags, offset, stride);
else
assert(false);
}
ZGUI_API void zguiPlot_PlotBarsValues(
const char *label_id,
ImGuiDataType data_type,
const void *values,
int count,
double bar_size,
double shift,
ImPlotBarsFlags flags,
int offset,
int stride)
{
if (data_type == ImGuiDataType_S8)
ImPlot::PlotBars(label_id, (const ImS8 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_U8)
ImPlot::PlotBars(label_id, (const ImU8 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_S16)
ImPlot::PlotBars(label_id, (const ImS16 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_U16)
ImPlot::PlotBars(label_id, (const ImU16 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_S32)
ImPlot::PlotBars(label_id, (const ImS32 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_U32)
ImPlot::PlotBars(label_id, (const ImU32 *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_Float)
ImPlot::PlotBars(label_id, (const float *)values, count, bar_size, shift, flags, offset, stride);
else if (data_type == ImGuiDataType_Double)
ImPlot::PlotBars(label_id, (const double *)values, count, bar_size, shift, flags, offset, stride);
else
assert(false);
}
ZGUI_API bool zguiPlot_IsPlotHovered()
{
return ImPlot::IsPlotHovered();
}
ZGUI_API void zguiPlot_GetLastItemColor(float color[4])
{
const ImVec4 col = ImPlot::GetLastItemColor();
color[0] = col.x;
color[1] = col.y;
color[2] = col.z;
color[3] = col.w;
}
ZGUI_API void zguiPlot_ShowDemoWindow(bool *p_open)
{
ImPlot::ShowDemoWindow(p_open);
}
ZGUI_API void zguiPlot_EndPlot(void)
{
ImPlot::EndPlot();
}
ZGUI_API bool zguiPlot_DragPoint(
int id,
double *x,
double *y,
float col[4],
float size,
ImPlotDragToolFlags flags)
{
return ImPlot::DragPoint(
id,
x,
y,
(*(const ImVec4 *)&(col[0])),
size,
flags);
}
ZGUI_API void zguiPlot_PlotText(
const char *text,
double x, double y,
const float pix_offset[2],
ImPlotTextFlags flags = 0)
{
const ImVec2 p(pix_offset[0], pix_offset[1]);
ImPlot::PlotText(text, x, y, p, flags);
}
} /* extern "C" */

163
src/zte.cpp Normal file
View File

@ -0,0 +1,163 @@
#include "imgui.h"
#include "imgui_te_engine.h"
#include "imgui_te_context.h"
#include "imgui_te_ui.h"
#include "imgui_te_utils.h"
#include "imgui_te_exporters.h"
#include "imgui_internal.h"
#ifndef ZGUI_API
#define ZGUI_API
#endif
//--------------------------------------------------------------------------------------------------
//
// ImGUI Test Engine
//
//--------------------------------------------------------------------------------------------------
extern "C"
{
ZGUI_API void *zguiTe_CreateContext(void)
{
ImGuiTestEngine *e = ImGuiTestEngine_CreateContext();
ImGuiTestEngine_Start(e, ImGui::GetCurrentContext());
ImGuiTestEngine_InstallDefaultCrashHandler();
return e;
}
ZGUI_API void zguiTe_DestroyContext(ImGuiTestEngine *engine)
{
ImGuiTestEngine_DestroyContext(engine);
}
ZGUI_API void zguiTe_EngineSetRunSpeed(ImGuiTestEngine *engine, ImGuiTestRunSpeed speed)
{
ImGuiTestEngine_GetIO(engine).ConfigRunSpeed = speed;
}
ZGUI_API void zguiTe_EngineExportJunitResult(ImGuiTestEngine *engine, const char *filename)
{
ImGuiTestEngine_GetIO(engine).ExportResultsFilename = filename;
ImGuiTestEngine_GetIO(engine).ExportResultsFormat = ImGuiTestEngineExportFormat_JUnitXml;
}
ZGUI_API void zguiTe_TryAbortEngine(ImGuiTestEngine *engine)
{
ImGuiTestEngine_TryAbortEngine(engine);
}
ZGUI_API void zguiTe_Stop(ImGuiTestEngine *engine)
{
ImGuiTestEngine_Stop(engine);
}
ZGUI_API void zguiTe_PostSwap(ImGuiTestEngine *engine)
{
ImGuiTestEngine_PostSwap(engine);
}
ZGUI_API bool zguiTe_IsTestQueueEmpty(ImGuiTestEngine *engine)
{
return ImGuiTestEngine_IsTestQueueEmpty(engine);
}
ZGUI_API void zguiTe_GetResult(ImGuiTestEngine *engine, int *count_tested, int *count_success)
{
int ct = 0;
int cs = 0;
ImGuiTestEngine_GetResult(engine, ct, cs);
*count_tested = ct;
*count_success = cs;
}
ZGUI_API void zguiTe_PrintResultSummary(ImGuiTestEngine *engine)
{
ImGuiTestEngine_PrintResultSummary(engine);
}
ZGUI_API void zguiTe_QueueTests(ImGuiTestEngine *engine, ImGuiTestGroup group, const char *filter_str, ImGuiTestRunFlags run_flags)
{
ImGuiTestEngine_QueueTests(engine, group, filter_str, run_flags);
}
ZGUI_API void zguiTe_ShowTestEngineWindows(ImGuiTestEngine *engine, bool *p_open)
{
ImGuiTestEngine_ShowTestEngineWindows(engine, p_open);
}
ZGUI_API void *zguiTe_RegisterTest(ImGuiTestEngine *engine, const char *category, const char *name, const char *src_file, int src_line, ImGuiTestGuiFunc *gui_fce, ImGuiTestTestFunc *gui_test_fce)
{
auto t = ImGuiTestEngine_RegisterTest(engine, category, name, src_file, src_line);
t->GuiFunc = gui_fce;
t->TestFunc = gui_test_fce;
return t;
}
ZGUI_API bool zguiTe_Check(const char *file, const char *func, int line, ImGuiTestCheckFlags flags, bool result, const char *expr)
{
return ImGuiTestEngine_Check(file, func, line, flags, result, expr);
}
// CONTEXT
ZGUI_API void zguiTe_ContextSetRef(ImGuiTestContext *ctx, const char *ref)
{
ctx->SetRef(ref);
}
ZGUI_API void zguiTe_ContextWindowFocus(ImGuiTestContext *ctx, const char *ref)
{
ctx->WindowFocus(ref);
}
ZGUI_API void zguiTe_ContextItemAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref, ImGuiTestOpFlags flags = 0, void *action_arg = NULL)
{
ctx->ItemAction(action, ref, flags, action_arg);
}
ZGUI_API void zguiTe_ContextItemInputStrValue(ImGuiTestContext *ctx, const char *ref, const char *value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextItemInputIntValue(ImGuiTestContext *ctx, const char *ref, int value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextItemInputFloatValue(ImGuiTestContext *ctx, const char *ref, float value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextYield(ImGuiTestContext *ctx, int frame_count)
{
ctx->Yield(frame_count);
}
ZGUI_API void zguiTe_ContextMenuAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref)
{
ctx->MenuAction(action, ref);
}
ZGUI_API void zguiTe_ContextDragAndDrop(ImGuiTestContext *ctx, const char *ref_src, const char *ref_dst, ImGuiMouseButton button)
{
ctx->ItemDragAndDrop(ref_src, ref_dst, button);
}
ZGUI_API void zguiTe_ContextKeyDown(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
{
ctx->KeyDown(key_chord);
}
ZGUI_API void zguiTe_ContextKeyUp(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
{
ctx->KeyUp(key_chord);
}
} /* extern "C" */