Compare commits
2 Commits
9c3654e727
...
6f0ecab671
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 6f0ecab671 | |
Rekai Nyangadzayi Musuka | 58b5f87a95 |
84
README.md
84
README.md
|
@ -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.
|
||||
|
||||
|
@ -7,14 +7,18 @@ Easy to use, hand-crafted API with default arguments, named parameters and Zig s
|
|||
* Most public dear imgui API exposed
|
||||
* All memory allocations go through user provided Zig allocator
|
||||
* [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
|
||||
* [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
|
||||
|
||||
* [ImGui](https://github.com/ocornut/imgui/tree/v1.90.4-docking) `1.90.4-docking`
|
||||
* [ImGui test engine](https://github.com/ocornut/imgui_test_engine/tree/v1.90.4) `1.90.4`
|
||||
* [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.91.0) `1.91.0`
|
||||
* [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
|
||||
|
||||
|
@ -139,21 +143,6 @@ draw_list.addPolyline(
|
|||
.{ .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
|
||||
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 ->");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
104
build.zig
104
build.zig
|
@ -26,6 +26,16 @@ pub fn build(b: *std.Build) void {
|
|||
"with_implot",
|
||||
"Build with bundled implot source",
|
||||
) 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(
|
||||
bool,
|
||||
"with_te",
|
||||
|
@ -52,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 lib = b.addSharedLibrary(.{
|
||||
|
@ -103,26 +113,61 @@ pub fn build(b: *std.Build) void {
|
|||
.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) {
|
||||
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) {
|
||||
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_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL", "1");
|
||||
|
@ -180,8 +225,6 @@ pub fn build(b: *std.Build) void {
|
|||
imgui.linkLibrary(winpthreads);
|
||||
imgui.addSystemIncludePath(b.path("libs/winpthreads/include"));
|
||||
}
|
||||
} else {
|
||||
imgui.defineCMacro("ZGUI_TE", "0");
|
||||
}
|
||||
|
||||
switch (options.backend) {
|
||||
|
@ -221,16 +264,6 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
imgui.linkSystemLibrary("d3dcompiler_47");
|
||||
},
|
||||
.sdl2_opengl3 => {
|
||||
imgui.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"libs/imgui/backends/imgui_impl_sdl.cpp",
|
||||
"libs/imgui/backends/imgui_impl_opengl3.cpp",
|
||||
"libs/imgui/backends/imgui_impl_opengl3_loader.h",
|
||||
},
|
||||
.flags = cflags,
|
||||
});
|
||||
},
|
||||
.win32_dx12 => {
|
||||
imgui.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
|
@ -257,9 +290,24 @@ pub fn build(b: *std.Build) void {
|
|||
.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 => {},
|
||||
}
|
||||
|
||||
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 tests = b.addTest(.{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.{
|
||||
.name = "zgui",
|
||||
.version = "0.2.0",
|
||||
.version = "0.5.0",
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
|
@ -8,5 +8,9 @@
|
|||
"src",
|
||||
"README.md",
|
||||
},
|
||||
.dependencies = .{},
|
||||
.dependencies = .{
|
||||
.zglfw = .{ .path = "../zglfw" },
|
||||
.zgpu = .{ .path = "../zgpu" },
|
||||
.system_sdk = .{ .path = "../system-sdk" },
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
|
@ -847,7 +848,7 @@ void ImGui_ImplDX12_Shutdown()
|
|||
void ImGui_ImplDX12_NewFrame()
|
||||
{
|
||||
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)
|
||||
ImGui_ImplDX12_CreateDeviceObjects();
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
// [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: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
// (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-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-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)
|
||||
|
@ -93,17 +95,26 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#undef APIENTRY
|
||||
#ifndef GLFW_EXPOSE_NATIVE_WIN32
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#endif
|
||||
#include <GLFW/glfw3native.h> // for glfwGetWin32Window()
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#ifndef GLFW_EXPOSE_NATIVE_COCOA
|
||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||
#endif
|
||||
#include <GLFW/glfw3native.h> // for glfwGetCocoaWindow()
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3
|
||||
#include <GLFW/emscripten_glfw3.h>
|
||||
#else
|
||||
#define EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 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))
|
||||
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
// Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback().
|
||||
return;
|
||||
#endif
|
||||
|
@ -406,7 +417,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
|
|||
|
||||
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.
|
||||
// (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.
|
||||
|
@ -417,7 +428,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
|
|||
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr);
|
||||
const char* key_name = glfwGetKeyName(key, scancode);
|
||||
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);
|
||||
#endif
|
||||
if (key_name && key_name[0] != 0 && key_name[1] == 0)
|
||||
|
@ -525,7 +536,7 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
|
|||
bd->WantUpdateMonitors = true;
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*)
|
||||
{
|
||||
// Mimic Emscripten_HandleWheel() in SDL.
|
||||
|
@ -599,9 +610,14 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool 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)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
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);
|
||||
|
||||
|
@ -627,6 +643,10 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
|
|||
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
|
||||
io.ClipboardUserData = bd->Window;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
io.PlatformOpenInShellFn = [](ImGuiContext *, const char *url)
|
||||
{ ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };
|
||||
#endif
|
||||
|
||||
// Create mouse cursors
|
||||
// (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.
|
||||
if (install_callbacks)
|
||||
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)
|
||||
ImGui_ImplGlfw_UpdateMonitors();
|
||||
|
@ -717,8 +731,9 @@ void ImGui_ImplGlfw_Shutdown()
|
|||
|
||||
if (bd->InstalledCallbacks)
|
||||
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr);
|
||||
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
if (bd->CanvasSelector)
|
||||
emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr);
|
||||
#endif
|
||||
|
||||
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];
|
||||
GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
const bool is_window_focused = true;
|
||||
#else
|
||||
const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0;
|
||||
|
@ -845,7 +860,7 @@ static void ImGui_ImplGlfw_UpdateGamepads()
|
|||
return;
|
||||
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__)
|
||||
#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)
|
||||
GLFWgamepadstate gamepad;
|
||||
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))
|
||||
return;
|
||||
|
@ -935,7 +950,7 @@ void ImGui_ImplGlfw_NewFrame()
|
|||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
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)
|
||||
int w, h;
|
||||
|
@ -968,7 +983,7 @@ void ImGui_ImplGlfw_NewFrame()
|
|||
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)
|
||||
{
|
||||
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.
|
||||
// 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);
|
||||
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;
|
||||
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
|
||||
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
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
// [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: 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'.
|
||||
|
||||
// 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.
|
||||
// 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 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_InitForVulkan(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_NewFrame();
|
||||
|
||||
// Emscripten related initialization phase methods
|
||||
// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL)
|
||||
#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
|
||||
|
||||
// GLFW callbacks install
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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:
|
||||
// - 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
|
||||
// (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.
|
||||
// 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)
|
||||
|
@ -121,6 +126,7 @@
|
|||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#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 "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used
|
||||
|
@ -181,9 +187,10 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
// 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)
|
||||
#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
|
||||
|
||||
// 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;
|
||||
GLsizeiptr VertexBufferSize;
|
||||
GLsizeiptr IndexBufferSize;
|
||||
bool HasPolygonMode;
|
||||
bool HasClipOrigin;
|
||||
bool UseBufferSubData;
|
||||
|
||||
|
@ -247,6 +255,10 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
|
|||
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)
|
||||
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
struct ImGui_ImplOpenGL3_VtxAttribState
|
||||
|
@ -275,6 +287,7 @@ struct ImGui_ImplOpenGL3_VtxAttribState
|
|||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
|
||||
// Initialize our loader
|
||||
|
@ -298,16 +311,13 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|||
bd->GlProfileIsES2 = true;
|
||||
#else
|
||||
// Desktop or GLES 3
|
||||
const char *gl_version_str = (const char *)glGetString(GL_VERSION);
|
||||
GLint major = 0;
|
||||
GLint minor = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
if (major == 0 && minor == 0)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
|
||||
#if defined(GL_CONTEXT_PROFILE_MASK)
|
||||
if (bd->GlVersion >= 320)
|
||||
|
@ -317,6 +327,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|||
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
bd->GlProfileIsES3 = true;
|
||||
#else
|
||||
if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0)
|
||||
bd->GlProfileIsES3 = true;
|
||||
#endif
|
||||
|
||||
bd->UseBufferSubData = false;
|
||||
|
@ -331,13 +344,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (bd->GlVersion >= 320)
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
#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.
|
||||
// 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, ¤t_texture);
|
||||
|
||||
// Detect extensions we support
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
|
||||
bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3);
|
||||
#endif
|
||||
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
||||
#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
|
||||
GLint num_extensions = 0;
|
||||
|
@ -375,6 +392,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
ImGui_ImplOpenGL3_InitPlatformInterface();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -384,20 +404,23 @@ void ImGui_ImplOpenGL3_Shutdown()
|
|||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplOpenGL3_ShutdownPlatformInterface();
|
||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_NewFrame()
|
||||
{
|
||||
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)
|
||||
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)
|
||||
|
@ -416,7 +439,8 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
|||
if (bd->GlVersion >= 310)
|
||||
glDisable(GL_PRIMITIVE_RESTART);
|
||||
#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);
|
||||
#endif
|
||||
|
||||
|
@ -505,8 +529,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
|||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
||||
#endif
|
||||
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
|
||||
GLint last_polygon_mode[2];
|
||||
if (bd->HasPolygonMode)
|
||||
{
|
||||
glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||
}
|
||||
#endif
|
||||
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
||||
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); }
|
||||
#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
|
||||
if (bd->HasPolygonMode)
|
||||
{
|
||||
if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
#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]);
|
||||
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_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#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); }
|
||||
#endif
|
||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
|
@ -948,6 +979,34 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
|||
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__)
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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:
|
||||
// - 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();
|
||||
}
|
||||
|
||||
// Specific OpenGL ES versions
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
|
||||
// Configuration flags to add in your imconfig file:
|
||||
// #define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
|
||||
// #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.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// 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 INCUDING 'imgui_impl_opengl3_loader.h'
|
||||
// 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.
|
||||
|
@ -18,7 +18,7 @@
|
|||
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
|
||||
//
|
||||
// Regenerate with:
|
||||
// python gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
|
||||
// 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
|
||||
|
@ -346,6 +346,10 @@ GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean
|
|||
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
|
||||
|
@ -661,31 +665,123 @@ static GL3WglProc get_proc(const char *proc)
|
|||
#else
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void *libgl;
|
||||
static GL3WglProc (*glx_get_proc_address)(const GLubyte *);
|
||||
static void* libgl; // OpenGL library
|
||||
static void* libglx; // GLX library
|
||||
static void* libegl; // EGL library
|
||||
static GL3WGetProcAddressProc gl_get_proc_address;
|
||||
|
||||
static int open_libgl(void)
|
||||
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)
|
||||
|
||||
if (libgl)
|
||||
return GL3W_OK;
|
||||
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
*(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
|
||||
}
|
||||
|
||||
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 void close_libgl(void) { dlclose(libgl); }
|
||||
|
||||
static GL3WglProc get_proc(const char* proc)
|
||||
{
|
||||
GL3WglProc res;
|
||||
res = glx_get_proc_address((const GLubyte *)proc);
|
||||
if (!res)
|
||||
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
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// 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: 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'.
|
||||
// Missing features:
|
||||
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
|
||||
|
||||
// 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.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
extern "C" {
|
||||
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 void ImGui_ImplSDL2_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
static inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter
|
||||
#endif
|
|
@ -5,19 +5,35 @@
|
|||
|
||||
// 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'.
|
||||
// Missing features:
|
||||
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
|
||||
// [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.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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).
|
||||
|
@ -64,15 +80,24 @@
|
|||
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.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>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#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
|
||||
|
@ -87,12 +112,21 @@ 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];
|
||||
int PendingMouseLeaveFrame;
|
||||
char* ClipboardTextData;
|
||||
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)); }
|
||||
};
|
||||
|
||||
|
@ -120,8 +154,23 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
|
|||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
|
||||
// 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;
|
||||
|
@ -229,6 +278,21 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
|
|||
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;
|
||||
}
|
||||
|
@ -249,20 +313,33 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
|
|||
// 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)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
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:
|
||||
{
|
||||
io.AddMousePosEvent((float)event->motion.x, (float)event->motion.y);
|
||||
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:
|
||||
{
|
||||
float wheel_x = (event->wheel.x > 0) ? 1.0f : (event->wheel.x < 0) ? -1.0f : 0.0f;
|
||||
float wheel_y = (event->wheel.y > 0) ? 1.0f : (event->wheel.y < 0) ? -1.0f : 0.0f;
|
||||
//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;
|
||||
}
|
||||
|
@ -277,6 +354,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||
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;
|
||||
|
@ -290,7 +368,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||
case SDL_KEYUP:
|
||||
{
|
||||
ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
|
||||
ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym);
|
||||
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;
|
||||
|
@ -304,22 +382,36 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||
// 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->PendingMouseLeaveFrame = 0;
|
||||
{
|
||||
bd->MouseWindowID = event->window.windowID;
|
||||
bd->MouseLastLeaveFrame = 0;
|
||||
}
|
||||
if (window_event == SDL_WINDOWEVENT_LEAVE)
|
||||
bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;
|
||||
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;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
||||
#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
|
||||
|
@ -336,7 +428,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
|||
// Setup backend capabilities flags
|
||||
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = "imgui_impl_sdl";
|
||||
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)
|
||||
|
||||
|
@ -347,6 +439,14 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
|||
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);
|
||||
|
@ -360,14 +460,20 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
|||
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||
|
||||
// Set platform dependent data in viewport
|
||||
#ifdef _WIN32
|
||||
// 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))
|
||||
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)info.info.win.window;
|
||||
#else
|
||||
(void)window;
|
||||
{
|
||||
#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.
|
||||
|
@ -378,18 +484,25 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
|||
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)
|
||||
{
|
||||
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
|
||||
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||
return ImGui_ImplSDL2_Init(window, nullptr, sdl_gl_context);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
|
||||
|
@ -397,7 +510,7 @@ bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
|
|||
#if !SDL_HAS_VULKAN
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
|
||||
|
@ -405,19 +518,26 @@ bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
|
|||
#if !defined(_WIN32)
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||
return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
|
||||
{
|
||||
return ImGui_ImplSDL2_Init(window, 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();
|
||||
|
@ -428,9 +548,11 @@ void ImGui_ImplSDL2_Shutdown()
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -442,7 +564,7 @@ static void ImGui_ImplSDL2_UpdateMouseData()
|
|||
// 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 && ImGui::GetDragDropPayload() == nullptr) ? SDL_TRUE : SDL_FALSE);
|
||||
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
|
||||
|
@ -481,61 +603,125 @@ static void ImGui_ImplSDL2_UpdateMouseCursor()
|
|||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
SDL_SetCursor(bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
|
||||
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();
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||
return;
|
||||
|
||||
// Get gamepad
|
||||
// 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;
|
||||
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
|
||||
if (!game_controller)
|
||||
if (bd->Gamepads.Size == 0)
|
||||
return;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
// Update gamepad inputs
|
||||
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||
#define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0); }
|
||||
#define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }
|
||||
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
|
||||
MAP_BUTTON(ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START);
|
||||
MAP_BUTTON(ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK);
|
||||
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, SDL_CONTROLLER_BUTTON_X); // Xbox X, PS Square
|
||||
MAP_BUTTON(ImGuiKey_GamepadFaceRight, SDL_CONTROLLER_BUTTON_B); // Xbox B, PS Circle
|
||||
MAP_BUTTON(ImGuiKey_GamepadFaceUp, SDL_CONTROLLER_BUTTON_Y); // Xbox Y, PS Triangle
|
||||
MAP_BUTTON(ImGuiKey_GamepadFaceDown, SDL_CONTROLLER_BUTTON_A); // Xbox A, PS Cross
|
||||
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
|
||||
MAP_BUTTON(ImGuiKey_GamepadDpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
|
||||
MAP_BUTTON(ImGuiKey_GamepadDpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP);
|
||||
MAP_BUTTON(ImGuiKey_GamepadDpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
|
||||
MAP_BUTTON(ImGuiKey_GamepadL1, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
|
||||
MAP_BUTTON(ImGuiKey_GamepadR1, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
|
||||
MAP_ANALOG(ImGuiKey_GamepadL2, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0.0f, 32767);
|
||||
MAP_ANALOG(ImGuiKey_GamepadR2, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);
|
||||
MAP_BUTTON(ImGuiKey_GamepadL3, SDL_CONTROLLER_BUTTON_LEFTSTICK);
|
||||
MAP_BUTTON(ImGuiKey_GamepadR3, SDL_CONTROLLER_BUTTON_RIGHTSTICK);
|
||||
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||
MAP_ANALOG(ImGuiKey_GamepadLStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||
MAP_ANALOG(ImGuiKey_GamepadLStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32768);
|
||||
MAP_ANALOG(ImGuiKey_GamepadLStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);
|
||||
MAP_ANALOG(ImGuiKey_GamepadRStickRight, SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);
|
||||
MAP_ANALOG(ImGuiKey_GamepadRStickUp, SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);
|
||||
MAP_ANALOG(ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
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 && "Did you call ImGui_ImplSDL2_Init()?");
|
||||
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)
|
||||
|
@ -553,15 +739,19 @@ void ImGui_ImplSDL2_NewFrame()
|
|||
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->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||
if (bd->MouseLastLeaveFrame && bd->MouseLastLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||
{
|
||||
bd->MouseWindowID = 0;
|
||||
bd->MouseLastLeaveFrame = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
bd->PendingMouseLeaveFrame = 0;
|
||||
}
|
||||
|
||||
ImGui_ImplSDL2_UpdateMouseData();
|
||||
|
@ -570,3 +760,11 @@ void ImGui_ImplSDL2_NewFrame()
|
|||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplSDL2_UpdateGamepads();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
|
@ -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
|
|
@ -760,6 +760,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
|
|||
bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
|
||||
// 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)
|
||||
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];
|
||||
fr->IndexBuffer = nullptr;
|
||||
|
|
|
@ -34,11 +34,12 @@ struct ImGui_ImplWGPU_InitInfo
|
|||
ImGui_ImplWGPU_InitInfo()
|
||||
{
|
||||
PipelineMultisampleState.count = 1;
|
||||
PipelineMultisampleState.mask = -1u;
|
||||
PipelineMultisampleState.mask = UINT32_MAX;
|
||||
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 void ImGui_ImplWGPU_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
|
||||
|
|
|
@ -17,63 +17,10 @@
|
|||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - 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
|
||||
// (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-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-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.
|
||||
|
@ -126,8 +73,71 @@ extern "C" {
|
|||
// 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.
|
||||
|
||||
#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
|
||||
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_UpdateMonitors();
|
||||
|
||||
|
@ -177,6 +187,7 @@ static void ImGui_ImplWin32_UpdateKeyboardCodePage()
|
|||
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||
|
||||
INT64 perf_frequency, perf_counter;
|
||||
|
@ -329,7 +340,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
|
|||
io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
|
||||
io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
|
||||
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)
|
||||
|
@ -475,9 +486,9 @@ static void ImGui_ImplWin32_UpdateMonitors()
|
|||
|
||||
void ImGui_ImplWin32_NewFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
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)
|
||||
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)
|
||||
{
|
||||
if (ImGui::GetCurrentContext() == nullptr)
|
||||
return 0;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
// Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
|
||||
// We silently allow both context or just only backend data to be nullptr.
|
||||
ImGui_ImplWin32_Data *bd = ImGui_ImplWin32_GetBackendData();
|
||||
if (bd == nullptr)
|
||||
return 0;
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
|
@ -707,7 +719,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
::ScreenToClient(hwnd, &mouse_pos);
|
||||
io.AddMouseSourceEvent(mouse_source);
|
||||
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSELEAVE:
|
||||
case WM_NCMOUSELEAVE:
|
||||
|
@ -720,7 +732,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
bd->MouseTrackedArea = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
}
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||
|
@ -1075,6 +1087,9 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
|
|||
vd->HwndOwned = true;
|
||||
viewport->PlatformRequestResize = false;
|
||||
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)
|
||||
|
@ -1100,10 +1115,20 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
|
|||
{
|
||||
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||
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)
|
||||
::ShowWindow(vd->Hwnd, SW_SHOWNA);
|
||||
else
|
||||
::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)
|
||||
|
@ -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)
|
||||
{
|
||||
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
||||
return true;
|
||||
// Allow secondary viewport WndProc to be called regardless of current context
|
||||
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)
|
||||
{
|
||||
case WM_CLOSE:
|
||||
viewport->PlatformRequestClose = true;
|
||||
return 0;
|
||||
break;
|
||||
case WM_MOVE:
|
||||
viewport->PlatformRequestMove = true;
|
||||
break;
|
||||
|
@ -1285,7 +1316,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
|
|||
break;
|
||||
case WM_MOUSEACTIVATE:
|
||||
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
|
||||
return MA_NOACTIVATE;
|
||||
result = MA_NOACTIVATE;
|
||||
break;
|
||||
case WM_NCHITTEST:
|
||||
// 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
|
||||
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
|
||||
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
|
||||
return HTTRANSPARENT;
|
||||
result = HTTRANSPARENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
if (result == 0)
|
||||
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)
|
||||
|
@ -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
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#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_InitForOpenGL(void* hwnd);
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||
|
|
|
@ -21,10 +21,11 @@
|
|||
|
||||
//---- 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.
|
||||
// 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.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#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.
|
||||
//#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_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_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_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)
|
||||
|
@ -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_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
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
|
1663
libs/imgui/imgui.cpp
1663
libs/imgui/imgui.cpp
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
|
@ -1,4 +1,4 @@
|
|||
// dear imgui, v1.90.4
|
||||
// dear imgui, v1.91.0
|
||||
// (drawing and font code)
|
||||
|
||||
/*
|
||||
|
@ -8,6 +8,7 @@ Index of this file:
|
|||
// [SECTION] STB libraries implementation
|
||||
// [SECTION] Style functions
|
||||
// [SECTION] ImDrawList
|
||||
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
|
||||
// [SECTION] ImDrawListSplitter
|
||||
// [SECTION] ImDrawData
|
||||
// [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 "-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 "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#elif defined(__GNUC__)
|
||||
#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
|
||||
|
@ -209,11 +211,13 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
|||
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_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_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
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_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 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_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
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_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
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_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
|
||||
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_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
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_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 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_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
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_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
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_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||||
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_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
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_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 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_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
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_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
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.
|
||||
// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
|
||||
void ImDrawList::_ResetForNewFrame()
|
||||
{
|
||||
// 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)
|
||||
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));
|
||||
|
||||
|
@ -1235,11 +1247,10 @@ void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float
|
|||
for (int i = 0; i <= num_segments; i++)
|
||||
{
|
||||
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
|
||||
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);
|
||||
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);
|
||||
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);
|
||||
point.x = rel_x + center.x;
|
||||
point.y = rel_y + center.y;
|
||||
ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
|
||||
const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
|
||||
point.x = rel.x + center.x;
|
||||
point.y = rel.y + center.y;
|
||||
_Path.push_back(point);
|
||||
}
|
||||
}
|
||||
|
@ -1564,31 +1575,31 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
|
|||
}
|
||||
|
||||
// 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)
|
||||
return;
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1619,10 +1630,11 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
|
|||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
// Accept null ranges
|
||||
if (text_begin == text_end || text_begin[0] == 0)
|
||||
return;
|
||||
if (text_end == NULL)
|
||||
text_end = text_begin + strlen(text_begin);
|
||||
if (text_begin == text_end)
|
||||
return;
|
||||
|
||||
// Pull default font/size from the shared ImDrawListSharedData instance
|
||||
if (font == NULL)
|
||||
|
@ -1706,6 +1718,316 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
|
|||
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
|
||||
|
@ -2678,8 +3000,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
|||
int 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 descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
||||
const float ascent = ImCeil(unscaled_ascent * font_scale);
|
||||
const float descent = ImFloor(unscaled_descent * font_scale);
|
||||
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||
const float font_off_x = cfg.GlyphOffset.x;
|
||||
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;
|
||||
y += line_height;
|
||||
if (y > clip_rect.w)
|
||||
break; // break out of main loop
|
||||
word_wrap_eol = NULL;
|
||||
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
|
||||
continue;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
// dear imgui, v1.90.4
|
||||
// dear imgui, v1.91.0
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
|
@ -24,8 +24,9 @@ Index of this file:
|
|||
*/
|
||||
|
||||
// Navigating this file:
|
||||
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
||||
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - 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
|
||||
|
@ -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 "-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 "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#elif defined(__GNUC__)
|
||||
#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
|
||||
|
@ -318,6 +320,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||
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.
|
||||
// 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 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);
|
||||
|
@ -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)
|
||||
{
|
||||
ItemSize(outer_rect);
|
||||
ItemAdd(outer_rect, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -335,7 +344,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||
|
||||
// Acquire storage for the table
|
||||
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
|
||||
const ImGuiTableFlags table_last_flags = table->Flags;
|
||||
|
||||
// Acquire temporary buffers
|
||||
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
|
||||
const int previous_frame_active = table->LastFrameActive;
|
||||
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||||
const ImGuiTableFlags previous_flags = table->Flags;
|
||||
table->ID = id;
|
||||
table->Flags = flags;
|
||||
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));
|
||||
|
||||
// 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));
|
||||
|
||||
// Create scrolling region (without border and zero window padding)
|
||||
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
|
||||
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
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.
|
||||
// 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->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (previous_frame_active + 1 < g.FrameCount)
|
||||
table->IsActiveIdInTable = false;
|
||||
table->AngledHeadersHeight = 0.0f;
|
||||
temp_data->AngledHeadersExtraWidth = 0.0f;
|
||||
|
||||
// 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.
|
||||
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;
|
||||
|
||||
// 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 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.
|
||||
const float previous_instance_work_min_x = column->WorkMinX;
|
||||
column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
|
||||
column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
|
||||
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);
|
||||
|
||||
// Reset content width variables
|
||||
column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX;
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX;
|
||||
if (table->InstanceCurrent == 0)
|
||||
{
|
||||
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
|
||||
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).
|
||||
// Actual columns highlight/render will be performed in EndTable() and not be affected.
|
||||
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_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;
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
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();
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1462,9 +1491,13 @@ void ImGui::EndTable()
|
|||
}
|
||||
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);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
|
||||
// Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
|
||||
// - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
|
||||
// - 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
|
||||
{
|
||||
|
@ -1472,9 +1505,9 @@ void ImGui::EndTable()
|
|||
}
|
||||
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.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
|
||||
{
|
||||
|
@ -1890,7 +1923,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
|||
if (is_visible)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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.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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
// get the new cursor position.
|
||||
if (unfreeze_rows_request)
|
||||
{
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
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)
|
||||
{
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||
table->IsUnfrozenRows = true;
|
||||
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||
|
||||
// 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);
|
||||
|
@ -1991,6 +2027,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
|||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
table->RowBgColorCounter++;
|
||||
|
@ -2766,7 +2803,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
|
|||
static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)
|
||||
{
|
||||
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)
|
||||
|
@ -2907,6 +2944,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
|||
}
|
||||
|
||||
// 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;
|
||||
if (dirty && sort_specs != NULL)
|
||||
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->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = column->SortDirection;
|
||||
sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
|
||||
}
|
||||
|
||||
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().
|
||||
// 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.
|
||||
// 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.
|
||||
|
@ -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.
|
||||
// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn
|
||||
// FIXME: No hit-testing/button on the angled header.
|
||||
void ImGui::TableAngledHeadersRow()
|
||||
{
|
||||
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;
|
||||
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()
|
||||
// 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 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->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
|
||||
|
@ -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.
|
||||
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);
|
||||
KeepAliveID(row_id);
|
||||
|
||||
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;
|
||||
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
|
||||
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
|
||||
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
|
||||
|
||||
// Draw background and labels in first pass, then all borders.
|
||||
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 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))
|
||||
continue;
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
const ImGuiTableHeaderData* request = &data[order_n];
|
||||
const int column_n = request->Index;
|
||||
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];
|
||||
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)
|
||||
{
|
||||
// Draw shape
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg));
|
||||
if (column_n == highlight_column_n)
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
|
||||
max_x = ImMax(max_x, bg_shape[3].x);
|
||||
|
||||
// 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.
|
||||
const char* label_name = TableGetColumnName(table, column_n);
|
||||
const char* label_name_end = FindRenderedTextEnd(label_name);
|
||||
const float line_off_step_x = g.FontSize / -sin_a;
|
||||
float line_off_curr_x = 0.0f;
|
||||
const float line_off_step_x = (g.FontSize / -sin_a);
|
||||
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)
|
||||
{
|
||||
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.
|
||||
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);
|
||||
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
|
||||
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);
|
||||
PopStyleColor();
|
||||
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
|
||||
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);
|
||||
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;
|
||||
if (flip_label)
|
||||
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
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
@ -3402,7 +3474,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
|||
Separator();
|
||||
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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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(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);
|
||||
if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
|
||||
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
|
||||
|
@ -4314,7 +4386,7 @@ void ImGui::EndColumns()
|
|||
ImGuiOldColumnData* column = &columns->Columns[n];
|
||||
float x = window->Pos.x + GetColumnOffset(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));
|
||||
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
|
||||
continue;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -41,7 +41,7 @@
|
|||
// 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.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.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
|
||||
|
|
|
@ -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);
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Dear ImGui Test Engine License (v1.03)
|
||||
Copyright (c) 2018-2023 Omar Cornut
|
||||
Dear ImGui Test Engine License (v1.04)
|
||||
Copyright (c) 2018-2024 Omar Cornut
|
||||
|
||||
This document is a legal agreement ("License") between you ("Licensee") and
|
||||
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
|
||||
|
||||
A limited and non-exclusive license is hereby granted, to the Licensee, to reproduce,
|
||||
execute, publicly perform, and display, use, copy, modify, merge, distribute, or create
|
||||
derivative works based on and/or derived from the Software ("Derivative Software").
|
||||
A limited and non-exclusive worldwide license is hereby granted, to the Licensee,
|
||||
to reproduce, execute, publicly perform, and display, use, copy, modify, merge,
|
||||
distribute, or create derivative works based on and/or derived from the Software
|
||||
("Derivative Software").
|
||||
|
||||
2.2. Right of distribution
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ Index of this file:
|
|||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_te_engine.h"
|
||||
#include "imgui_capture_tool.h"
|
||||
#include "imgui_te_utils.h" // ImPathFindFilename, ImPathFindExtension, ImPathFixSeparatorsForCurrentOS, ImFileCreateDirectoryChain, ImOsOpenInShell
|
||||
#include "thirdparty/Str/Str.h"
|
||||
|
@ -50,7 +51,10 @@ Index of this file:
|
|||
#pragma warning (push)
|
||||
#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration
|
||||
#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 ignored "-Wsign-conversion"
|
||||
#endif
|
||||
|
@ -76,7 +80,9 @@ using namespace IMGUI_STB_NAMESPACE;
|
|||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#else
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#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.
|
||||
// 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.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);
|
||||
_HoveredWindow = g.HoveredWindow;
|
||||
}
|
||||
|
@ -732,11 +738,10 @@ void ImGuiCaptureToolUI::_CaptureWindowsSelector(ImGuiCaptureContext* context, I
|
|||
if (!allow_capture)
|
||||
ImGui::BeginDisabled();
|
||||
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)
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Alternatively press Alt+C to capture selection.");
|
||||
ImGui::SetItemTooltip("Alternatively press Alt+C to capture selection.");
|
||||
if (do_capture && _InitializeOutputFile())
|
||||
_StateIsCapturing = true;
|
||||
}
|
||||
|
@ -889,8 +894,8 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
|
|||
ImOsOpenInShell(OutputLastFilename);
|
||||
if (!has_last_file_name)
|
||||
ImGui::EndDisabled();
|
||||
if (has_last_file_name && ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Open %s", OutputLastFilename);
|
||||
if (has_last_file_name)
|
||||
ImGui::SetItemTooltip("Open %s", OutputLastFilename);
|
||||
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||
}
|
||||
|
||||
|
@ -908,8 +913,7 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
|
|||
ImPathFixSeparatorsForCurrentOS(output_dir);
|
||||
ImOsOpenInShell(output_dir);
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Open %s/", output_dir);
|
||||
ImGui::SetItemTooltip("Open %s/", output_dir);
|
||||
}
|
||||
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
|
@ -917,18 +921,16 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
|
|||
|
||||
ImGui::PushItemWidth(BUTTON_WIDTH);
|
||||
ImGui::InputText("Output template", _OutputFileTemplate, IM_ARRAYSIZE(_OutputFileTemplate));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Output template should contain one %%d (or variation of it) format variable. "
|
||||
ImGui::SetItemTooltip(
|
||||
"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.");
|
||||
|
||||
_ShowEncoderConfigFields(context);
|
||||
|
||||
ImGui::DragFloat("Padding", &_CaptureArgs.InPadding, 0.1f, 0, 32, "%.0f");
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Extra padding around captured area.");
|
||||
ImGui::SetItemTooltip("Extra padding around captured area.");
|
||||
ImGui::DragInt("Video FPS", &_CaptureArgs.InRecordFPSTarget, 0.1f, 10, 100, "%d fps");
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Target FPS for video captures.");
|
||||
ImGui::SetItemTooltip("Target FPS for video captures.");
|
||||
|
||||
if (ImGui::Button("Snap Windows To Grid", ImVec2(BUTTON_WIDTH, 0)))
|
||||
_SnapWindowsToGrid(SnapGridSize);
|
||||
|
@ -945,13 +947,12 @@ void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, boo
|
|||
ImGui::BeginDisabled(!content_stitching_available);
|
||||
ImGui::CheckboxFlags("Stitch full contents height", &_CaptureArgs.InFlags, ImGuiCaptureFlags_StitchAll);
|
||||
ImGui::EndDisabled();
|
||||
if (!content_stitching_available && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
ImGui::SetTooltip("Content stitching is not possible when using viewports.");
|
||||
if (!content_stitching_available)
|
||||
ImGui::SetItemTooltip("Content stitching is not possible when using viewports.");
|
||||
|
||||
ImGui::CheckboxFlags("Include other windows", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeOtherWindows);
|
||||
ImGui::CheckboxFlags("Include tooltips & popups", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeTooltipsAndPopups);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Capture area will be expanded to include visible tooltips.");
|
||||
ImGui::SetItemTooltip("Capture area will be expanded to include visible tooltips.");
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::TreePop();
|
||||
|
@ -1024,8 +1025,7 @@ bool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context)
|
|||
const bool encoder_exe_missing = !ImFileExist(context->VideoCaptureEncoderPath);
|
||||
if (encoder_exe_missing)
|
||||
ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255));
|
||||
if (ImGui::IsItemHovered())
|
||||
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!" : "");
|
||||
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!" : "");
|
||||
}
|
||||
|
||||
struct CmdLineParamsInfo
|
||||
|
@ -1105,8 +1105,7 @@ bool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context)
|
|||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("File extension for captured video file.");
|
||||
ImGui::SetItemTooltip("File extension for captured video file.");
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "imgui_te_utils.h" // ImFuncPtr
|
||||
// Need "imgui_te_engine.h" included for ImFuncPtr
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -90,7 +90,8 @@ struct IMGUI_API ImGuiTestRefDesc
|
|||
char Buf[80];
|
||||
|
||||
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
|
||||
// Some flags are only supported by a handful of functions. Check function headers for list of supported flags.
|
||||
enum ImGuiTestOpFlags_
|
||||
{
|
||||
ImGuiTestOpFlags_None = 0,
|
||||
|
@ -122,11 +124,12 @@ enum ImGuiTestOpFlags_
|
|||
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_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_MoveToEdgeL = 1 << 7, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
|
||||
ImGuiTestOpFlags_MoveToEdgeR = 1 << 8,
|
||||
ImGuiTestOpFlags_MoveToEdgeU = 1 << 9,
|
||||
ImGuiTestOpFlags_MoveToEdgeD = 1 << 10,
|
||||
ImGuiTestOpFlags_NoYield = 1 << 6, // Don't yield (only supported by a few functions), in case you need to manage rigorous per-frame timing.
|
||||
ImGuiTestOpFlags_IsSecondAttempt = 1 << 7, // Used by recursing functions to indicate a second attempt
|
||||
ImGuiTestOpFlags_MoveToEdgeL = 1 << 8, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
|
||||
ImGuiTestOpFlags_MoveToEdgeR = 1 << 9,
|
||||
ImGuiTestOpFlags_MoveToEdgeU = 1 << 10,
|
||||
ImGuiTestOpFlags_MoveToEdgeD = 1 << 11,
|
||||
};
|
||||
|
||||
// Advanced filtering for ItemActionAll()
|
||||
|
@ -176,6 +179,7 @@ struct IMGUI_API ImGuiTestGenericVars
|
|||
int Count;
|
||||
ImGuiID DockId;
|
||||
ImGuiID OwnerId;
|
||||
ImVec2 WindowSize;
|
||||
ImGuiWindowFlags WindowFlags;
|
||||
ImGuiTableFlags TableFlags;
|
||||
ImGuiPopupFlags PopupFlags;
|
||||
|
@ -185,8 +189,8 @@ struct IMGUI_API ImGuiTestGenericVars
|
|||
bool UseViewports;
|
||||
float Width;
|
||||
ImVec2 Pos;
|
||||
ImVec2 Size;
|
||||
ImVec2 Pivot;
|
||||
ImVec2 ItemSize;
|
||||
ImVec4 Color1, Color2;
|
||||
|
||||
// Generic unnamed storage
|
||||
|
@ -283,7 +287,6 @@ struct IMGUI_API ImGuiTestContext
|
|||
|
||||
// Yield, Timing
|
||||
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 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.
|
||||
|
@ -297,12 +300,15 @@ struct IMGUI_API ImGuiTestContext
|
|||
// - 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.
|
||||
// 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(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code)
|
||||
ImGuiTestRef GetRef();
|
||||
|
||||
// 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 WindowCollapse(ImGuiTestRef window_ref, bool collapsed);
|
||||
void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
|
@ -340,7 +346,7 @@ struct IMGUI_API ImGuiTestContext
|
|||
// Mouse inputs
|
||||
void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void MouseMoveToPos(ImVec2 pos);
|
||||
void MouseTeleportToPos(ImVec2 pos);
|
||||
void MouseTeleportToPos(ImVec2 pos, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void MouseClick(ImGuiMouseButton button = 0);
|
||||
void MouseClickMulti(ImGuiMouseButton button, int count);
|
||||
void MouseDoubleClick(ImGuiMouseButton button = 0);
|
||||
|
@ -400,10 +406,10 @@ struct IMGUI_API ImGuiTestContext
|
|||
// 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.
|
||||
// - 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* ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
ImGuiTestItemInfo ItemInfo(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);
|
||||
ImGuiTestItemInfo* ItemInfoNull();
|
||||
ImGuiTestItemInfo ItemInfoNull() { return ImGuiTestItemInfo(); }
|
||||
void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1);
|
||||
|
||||
// Item/Widgets manipulation
|
||||
|
@ -428,12 +434,12 @@ struct IMGUI_API ImGuiTestContext
|
|||
void ItemInputValue(ImGuiTestRef ref, float f);
|
||||
void ItemInputValue(ImGuiTestRef ref, const char* str);
|
||||
|
||||
// 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);
|
||||
// Item/Widgets: Helpers to easily read a value by selecting Slider/Drag/Input text, copying into clipboard and parsing it.
|
||||
// - This requires the item to be selectable (we will later provide helpers that works in more general manner)
|
||||
// - Original clipboard value is restored afterward.
|
||||
bool ItemSelectAndReadValue(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
|
||||
void ItemSelectAndReadValue(ImGuiTestRef ref, int* out_v);
|
||||
void ItemSelectAndReadValue(ImGuiTestRef ref, float* out_v);
|
||||
|
||||
// Item/Widgets: Status query
|
||||
bool ItemExists(ImGuiTestRef ref);
|
||||
|
@ -441,6 +447,13 @@ struct IMGUI_API ImGuiTestContext
|
|||
bool ItemIsOpened(ImGuiTestRef ref);
|
||||
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
|
||||
void TabClose(ImGuiTestRef ref);
|
||||
bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order);
|
||||
|
@ -493,15 +506,17 @@ struct IMGUI_API ImGuiTestContext
|
|||
|
||||
// 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
|
||||
ImGuiID GetIDByInt(int n); // Prefer using "$$123"
|
||||
ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref);
|
||||
ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF"
|
||||
ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref);
|
||||
// Obsoleted 2022/09/26
|
||||
void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); }
|
||||
void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); }
|
||||
void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }
|
||||
//void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); }
|
||||
//void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); }
|
||||
//void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }
|
||||
#endif
|
||||
|
||||
// [Internal]
|
||||
|
@ -604,6 +619,7 @@ template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, Im
|
|||
|
||||
// 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_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_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E)
|
||||
|
||||
|
|
|
@ -592,17 +592,34 @@ void ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine)
|
|||
case ImGuiTestInputType_Key:
|
||||
{
|
||||
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
|
||||
#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
|
||||
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
|
||||
ImGuiKeyChord mods = (key_chord & ImGuiMod_Mask_);
|
||||
if (mods != 0x00)
|
||||
{
|
||||
// OSX conversion
|
||||
#if IMGUI_VERSION_NUM >= 18912
|
||||
#if IMGUI_VERSION_NUM >= 18912 && IMGUI_VERSION_NUM < 19063
|
||||
if (mods & ImGuiMod_Shortcut)
|
||||
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
|
||||
// Submitting a ImGuiMod_XXX without associated key needs to add at least one of the key.
|
||||
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++)
|
||||
{
|
||||
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);
|
||||
engine->InfoTasks.erase(engine->InfoTasks.Data + task_n);
|
||||
|
@ -950,7 +967,7 @@ int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine)
|
|||
|
||||
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);
|
||||
if (v >= 0 && v < IM_ARRAYSIZE(names))
|
||||
return names[v];
|
||||
|
@ -1172,6 +1189,32 @@ ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* cat
|
|||
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)
|
||||
{
|
||||
return engine->PerfTool;
|
||||
|
@ -1297,6 +1340,7 @@ void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, c
|
|||
if (group != ImGuiTestGroup_Unknown && test->Group != group)
|
||||
continue;
|
||||
|
||||
if (filter_str != NULL)
|
||||
if (!ImGuiTestEngine_PassFilter(test, filter_str))
|
||||
continue;
|
||||
|
||||
|
@ -1386,8 +1430,10 @@ struct ImGuiTestContextUiContextBackup
|
|||
IO = g.IO;
|
||||
Style = g.Style;
|
||||
DebugLogFlags = g.DebugLogFlags;
|
||||
#if IMGUI_VERSION_NUM >= 18837
|
||||
ConfigNavWindowingKeyNext = g.ConfigNavWindowingKeyNext;
|
||||
ConfigNavWindowingKeyPrev = g.ConfigNavWindowingKeyPrev;
|
||||
#endif
|
||||
memset(IO.MouseDown, 0, sizeof(IO.MouseDown));
|
||||
for (int n = 0; n < IM_ARRAYSIZE(IO.KeysData); n++)
|
||||
IO.KeysData[n].Down = false;
|
||||
|
@ -1400,8 +1446,10 @@ struct ImGuiTestContextUiContextBackup
|
|||
g.IO = IO;
|
||||
g.Style = Style;
|
||||
g.DebugLogFlags = DebugLogFlags;
|
||||
#if IMGUI_VERSION_NUM >= 18837
|
||||
g.ConfigNavWindowingKeyNext = ConfigNavWindowingKeyNext;
|
||||
g.ConfigNavWindowingKeyPrev = ConfigNavWindowingKeyPrev;
|
||||
#endif
|
||||
}
|
||||
void RestoreClipboardFuncs(ImGuiContext& g)
|
||||
{
|
||||
|
@ -1517,19 +1565,12 @@ void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_c
|
|||
else
|
||||
{
|
||||
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
|
||||
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.
|
||||
ImGuiTestContextUiContextBackup backup_ui_context;
|
||||
backup_ui_context.Backup(*ctx->UiContext);
|
||||
|
@ -1570,6 +1611,12 @@ void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_c
|
|||
ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc;
|
||||
ctx->ActiveFunc = ImGuiTestActiveFunc_TestFunc;
|
||||
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
|
||||
// - 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.
|
||||
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)
|
||||
{
|
||||
ctx->LogError("Assert: '%s'", expr);
|
||||
|
|
|
@ -7,7 +7,28 @@
|
|||
|
||||
#include "imgui.h"
|
||||
#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
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -70,12 +91,12 @@ enum ImGuiTestVerboseLevel : int
|
|||
// Test status (stored in ImGuiTest)
|
||||
enum ImGuiTestStatus : int
|
||||
{
|
||||
ImGuiTestStatus_Unknown = -1,
|
||||
ImGuiTestStatus_Success = 0,
|
||||
ImGuiTestStatus_Queued = 1,
|
||||
ImGuiTestStatus_Running = 2,
|
||||
ImGuiTestStatus_Error = 3,
|
||||
ImGuiTestStatus_Suspended = 4,
|
||||
ImGuiTestStatus_Unknown = 0,
|
||||
ImGuiTestStatus_Success = 1,
|
||||
ImGuiTestStatus_Queued = 2,
|
||||
ImGuiTestStatus_Running = 3,
|
||||
ImGuiTestStatus_Error = 4,
|
||||
ImGuiTestStatus_Suspended = 5,
|
||||
ImGuiTestStatus_COUNT
|
||||
};
|
||||
|
||||
|
@ -168,6 +189,7 @@ IMGUI_API ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine);
|
|||
// Macros: Register Test
|
||||
#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 void ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test);
|
||||
|
||||
// Functions: Main
|
||||
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
|
||||
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)
|
||||
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 TimestampStatus = -1; // Timestamp of StatusFlags
|
||||
ImGuiID ID = 0; // Item ID
|
||||
int TimestampMain; // Timestamp of main result (all fields)
|
||||
int TimestampStatus; // Timestamp of StatusFlags
|
||||
ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack)
|
||||
ImGuiWindow* Window = NULL; // Item Window
|
||||
ImRect RectFull = ImRect(); // Item Rectangle
|
||||
ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission)
|
||||
ImGuiItemFlags InFlags = 0; // Item flags
|
||||
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; }
|
||||
bool IsEmpty() const { return ID == 0; }
|
||||
ImGuiTestItemInfo() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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.
|
||||
ImVector<ImGuiTest*> TestsAll;
|
||||
ImVector<ImGuiTestRunTask> TestsQueue;
|
||||
ImGuiTestContext* TestContext = NULL;
|
||||
ImGuiTestContext* TestContext = NULL; // Running test context
|
||||
ImVector<ImGuiTestInfoTask*>InfoTasks;
|
||||
ImGuiTestGatherTask GatherTask;
|
||||
ImGuiTestFindByLabelTask FindByLabelTask;
|
||||
|
|
|
@ -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.");
|
||||
|
||||
// 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)
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
|
||||
#endif
|
||||
|
||||
if (ImGui::MenuItem("Show All"))
|
||||
{
|
||||
|
@ -1050,8 +1055,6 @@ void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
|
|||
if (ImGui::IsWindowAppearing() && Empty())
|
||||
LoadCSV();
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// 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.");
|
||||
|
||||
// Align help button to the right.
|
||||
float help_pos = ImGui::GetWindowContentRegionMax().x - style.FramePadding.x * 2 - ImGui::CalcTextSize("(?)").x;
|
||||
if (help_pos > ImGui::GetCursorPosX())
|
||||
ImGui::SetCursorPosX(help_pos);
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImMax(0.0f, ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("(?)").x));
|
||||
ImGui::TextDisabled("(?)");
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
|
@ -1263,6 +1263,7 @@ void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
|
|||
{
|
||||
#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
|
||||
// Splitter between two following child windows is rendered first.
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
float plot_height = 0.0f;
|
||||
float& table_height = _InfoTableHeight;
|
||||
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->WindowResize("", ImVec2(1400, 900));
|
||||
#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);
|
||||
|
||||
// Move legend to right side.
|
||||
|
@ -1910,7 +1911,7 @@ void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
|
|||
#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
|
||||
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);
|
||||
|
||||
// Move legend to right side.
|
||||
|
@ -1928,7 +1929,7 @@ void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
|
|||
// Take a screenshot.
|
||||
ImGuiCaptureArgs* args = ctx->CaptureArgs;
|
||||
args->InCaptureRect = plot_child->Rect();
|
||||
ctx->CaptureAddWindow(window->Name);
|
||||
ctx->CaptureAddWindow(window->ID);
|
||||
ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor);
|
||||
ctx->ItemDragWithDelta("splitter", ImVec2(0, -180)); // Show info table
|
||||
perf_report_image = args->InOutputFile;
|
||||
|
|
|
@ -177,7 +177,11 @@ static void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test)
|
|||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE);
|
||||
break;
|
||||
}
|
||||
#if IMGUI_VERSION_NUM >= 19072
|
||||
ImGui::DebugTextUnformattedWithLocateItem(line_start, line_end);
|
||||
#else
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
#endif
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
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::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);
|
||||
#else
|
||||
bool = ImGui::Button("Run");
|
||||
#endif
|
||||
#if IMGUI_VERSION_NUM > 18963
|
||||
ImGui::SetItemTooltip("Ctrl+R");
|
||||
#endif
|
||||
#else
|
||||
bool run = ImGui::Button("Run");
|
||||
#endif
|
||||
if (run)
|
||||
{
|
||||
|
@ -320,7 +327,7 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
|
|||
|
||||
ImGui::SameLine();
|
||||
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);
|
||||
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;
|
||||
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::SameLine();
|
||||
ImGui::TextDisabled("(?)");
|
||||
|
@ -354,6 +364,7 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
|
|||
int tests_completed = 0;
|
||||
int tests_succeeded = 0;
|
||||
int tests_failed = 0;
|
||||
ImVector<ImGuiTest*> tests_to_remove;
|
||||
if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit))
|
||||
{
|
||||
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()))
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -571,6 +587,10 @@ static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
|
|||
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)
|
||||
{
|
||||
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)."
|
||||
);
|
||||
ImGui::SameLine();
|
||||
//ImGui::Checkbox("Fast", &engine->IO.ConfigRunFast);
|
||||
//ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
|
||||
// (Would be good if we exposed horizontal layout mode..)
|
||||
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::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::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc);
|
||||
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::SameLine();
|
||||
ImGui::Checkbox("Refocus", &engine->IO.ConfigRestoreFocusAfterTests);
|
||||
ImGui::SetItemTooltip("Restore focus back after running tests.");
|
||||
ImGui::Checkbox("Capture", &engine->IO.ConfigCaptureOnError);
|
||||
ImGui::SetItemTooltip("When hitting an error:\n- Capture screen to PNG. Right-click filename in Test Log to open.");
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
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);
|
||||
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::SetItemTooltip("Verbose level.");
|
||||
|
||||
//ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h> // stat()
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) || defined(__MINGW32__)
|
||||
#include <pthread.h> // pthread_setname_np()
|
||||
|
@ -280,7 +283,7 @@ bool ImFileCreateDirectoryChain(const char* path, const char* path_end)
|
|||
path_local[path_len] = 0;
|
||||
|
||||
#if defined(_WIN32)
|
||||
ImVector<ImWchar> buf;
|
||||
ImVector<wchar_t> buf;
|
||||
#endif
|
||||
// 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.
|
||||
|
@ -291,10 +294,11 @@ bool ImFileCreateDirectoryChain(const char* path, const char* path_end)
|
|||
*(token - 1) = IM_DIR_SEPARATOR;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8.
|
||||
const int filename_wsize = ImTextCountCharsFromUtf8(path_local, NULL) + 1;
|
||||
// Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8
|
||||
// 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);
|
||||
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)
|
||||
#else
|
||||
if (mkdir(path_local, S_IRWXU) != 0 && errno != EEXIST)
|
||||
|
@ -785,10 +789,11 @@ const ImBuildInfo* ImBuildGetCompilationInfo()
|
|||
// CPU
|
||||
#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";
|
||||
#elif defined(__aarch64__)
|
||||
#elif defined(__aarch64__) || (defined(_M_ARM64) && defined(_WIN64))
|
||||
build_info.Cpu = "ARM64";
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
build_info.Cpu = "WebAsm";
|
||||
#else
|
||||
#error
|
||||
build_info.Cpu = (sizeof(size_t) == 4) ? "Unknown32" : "Unknown64";
|
||||
#endif
|
||||
|
||||
|
@ -1000,6 +1005,27 @@ bool ImOsIsDebuggerPresent()
|
|||
debugger_pid = atoi(tracer_pid);
|
||||
}
|
||||
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
|
||||
// FIXME
|
||||
return false;
|
||||
|
@ -1212,6 +1238,7 @@ void TableDiscardInstanceAndSettings(ImGuiID table_id)
|
|||
// Helper to verify ImDrawData integrity of buffer count (broke before e.g. #6716)
|
||||
void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data)
|
||||
{
|
||||
#if IMGUI_VERSION_NUM >= 18973
|
||||
int total_vtx_count = 0;
|
||||
int total_idx_count = 0;
|
||||
for (ImDrawList* draw_list : draw_data->CmdLists)
|
||||
|
@ -1223,6 +1250,9 @@ void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data)
|
|||
IM_UNUSED(total_idx_count);
|
||||
IM_ASSERT(total_vtx_count == draw_data->TotalVtxCount);
|
||||
IM_ASSERT(total_idx_count == draw_data->TotalIdxCount);
|
||||
#else
|
||||
IM_UNUSED(draw_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -13,17 +13,6 @@
|
|||
#include "imgui.h" // ImGuiID, ImGuiKey
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
|
@ -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.
|
|
@ -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.
|
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
|
@ -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
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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();
|
||||
}
|
|
@ -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__
|
|
@ -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__
|
|
@ -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
|
@ -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__
|
|
@ -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
|
@ -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__
|
|
@ -1,8 +1,10 @@
|
|||
const zgui = @import("gui.zig");
|
||||
|
||||
const SDL_GL_Context = *anyopaque;
|
||||
const SDL_Window = *anyopaque;
|
||||
const SDL_Event = *anyopaque;
|
||||
|
||||
pub fn init(window: *anyopaque, context: SDL_GL_Context, glsl_version: []const u8) void {
|
||||
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;
|
||||
}
|
||||
|
@ -27,14 +29,14 @@ pub fn draw() void {
|
|||
ImGui_ImplOpenGL3_RenderDrawData(zgui.getDrawData());
|
||||
}
|
||||
|
||||
pub fn processEvent(event: *anyopaque) bool {
|
||||
pub fn processEvent(event: SDL_Event) bool {
|
||||
return ImGui_ImplSDL2_ProcessEvent(event);
|
||||
}
|
||||
|
||||
extern fn ImGui_ImplSDL2_InitForOpenGL(window: *anyopaque, sdl_gl_context: SDL_GL_Context) bool;
|
||||
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: *anyopaque) bool;
|
||||
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;
|
||||
|
|
|
@ -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;
|
144
src/gui.zig
144
src/gui.zig
|
@ -5,14 +5,17 @@
|
|||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
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 backend = switch (@import("zgui_options").backend) {
|
||||
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
|
||||
.glfw_opengl3 => @import("backend_glfw_opengl.zig"),
|
||||
.glfw_dx12 => @import("backend_glfw_dx12.zig"),
|
||||
.sdl2_opengl3 => @import("backend_sdl2_opengl.zig"),
|
||||
.glfw => @import("backend_glfw.zig"),
|
||||
.win32_dx12 => @import("backend_win32_dx12.zig"),
|
||||
.sdl2_opengl3 => @import("backend_sdl2_opengl.zig"),
|
||||
.no_backend => .{},
|
||||
};
|
||||
const te_enabled = @import("zgui_options").with_te;
|
||||
|
@ -139,8 +142,9 @@ pub const ConfigFlags = packed struct(c_int) {
|
|||
nav_no_capture_keyboard: bool = false,
|
||||
no_mouse: bool = false,
|
||||
no_mouse_cursor_change: bool = false,
|
||||
no_keyboard: bool = false,
|
||||
dock_enable: bool = false,
|
||||
_pading0: u3 = 0,
|
||||
_pading0: u2 = 0,
|
||||
viewport_enable: bool = false,
|
||||
_pading1: u3 = 0,
|
||||
dpi_enable_scale_viewport: bool = false,
|
||||
|
@ -275,6 +279,9 @@ pub const io = struct {
|
|||
pub const getWantTextInput = zguiIoGetWantTextInput;
|
||||
extern fn zguiIoGetWantTextInput() bool;
|
||||
|
||||
pub const getFramerate = zguiIoFramerate;
|
||||
extern fn zguiIoFramerate() f32;
|
||||
|
||||
pub fn setIniFilename(filename: ?[*:0]const u8) void {
|
||||
zguiIoSetIniFilename(filename);
|
||||
}
|
||||
|
@ -561,7 +568,6 @@ pub const WindowFlags = packed struct(c_int) {
|
|||
|
||||
pub const ChildFlags = packed struct(c_int) {
|
||||
border: bool = false,
|
||||
no_move: bool = false,
|
||||
always_use_window_padding: bool = false,
|
||||
resize_x: bool = false,
|
||||
resize_y: bool = false,
|
||||
|
@ -569,6 +575,7 @@ pub const ChildFlags = packed struct(c_int) {
|
|||
auto_resize_y: bool = false,
|
||||
always_auto_resize: bool = false,
|
||||
frame_style: bool = false,
|
||||
nav_flattened: bool = false,
|
||||
_padding: u23 = 0,
|
||||
};
|
||||
|
||||
|
@ -582,7 +589,8 @@ pub const SliderFlags = packed struct(c_int) {
|
|||
logarithmic: bool = false,
|
||||
no_round_to_format: bool = false,
|
||||
no_input: bool = false,
|
||||
_padding: u24 = 0,
|
||||
wrap_around: bool = false,
|
||||
_padding: u23 = 0,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const ButtonFlags = packed struct(c_int) {
|
||||
|
@ -600,7 +608,7 @@ pub const Direction = enum(c_int) {
|
|||
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) {
|
||||
none = 0,
|
||||
|
@ -633,6 +641,9 @@ extern fn zguiGetDrawData() DrawData;
|
|||
/// `pub fn showDemoWindow(popen: ?*bool) void`
|
||||
pub const showDemoWindow = zguiShowDemoWindow;
|
||||
extern fn zguiShowDemoWindow(popen: ?*bool) void;
|
||||
|
||||
pub const showMetricsWindow = zguiShowMetricsWindow;
|
||||
extern fn zguiShowMetricsWindow(popen: ?*bool) void;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Windows
|
||||
|
@ -911,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 {
|
||||
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;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -947,6 +959,38 @@ extern fn zguiDockBuilderSplitNode(
|
|||
) Ident;
|
||||
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
|
||||
|
@ -983,7 +1027,9 @@ pub const Style = extern struct {
|
|||
tab_border_size: f32,
|
||||
tab_min_width_for_close_button: f32,
|
||||
tab_bar_border_size: f32,
|
||||
tab_bar_overline_size: f32,
|
||||
table_angled_header_angle: f32,
|
||||
table_angled_headers_text_align: [2]f32,
|
||||
color_button_position: Direction,
|
||||
button_text_align: [2]f32,
|
||||
selectable_text_align: [2]f32,
|
||||
|
@ -1062,11 +1108,13 @@ pub const StyleCol = enum(c_int) {
|
|||
resize_grip,
|
||||
resize_grip_hovered,
|
||||
resize_grip_active,
|
||||
tab,
|
||||
tab_hovered,
|
||||
tab_active,
|
||||
tab_unfocused,
|
||||
tab_unfocused_active,
|
||||
tab,
|
||||
tab_selected,
|
||||
tab_selected_overline,
|
||||
tab_dimmed,
|
||||
tab_dimmed_selected,
|
||||
tab_dimmed_selected_overline,
|
||||
docking_preview,
|
||||
docking_empty_bg,
|
||||
plot_lines,
|
||||
|
@ -1078,6 +1126,7 @@ pub const StyleCol = enum(c_int) {
|
|||
table_border_light,
|
||||
table_row_bg,
|
||||
table_row_bg_alt,
|
||||
text_link,
|
||||
text_selected_bg,
|
||||
drag_drop_target,
|
||||
nav_highlight,
|
||||
|
@ -1143,7 +1192,11 @@ pub const StyleVar = enum(c_int) {
|
|||
grab_min_size, // 1f
|
||||
grab_rounding, // 1f
|
||||
tab_rounding, // 1f
|
||||
tab_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
|
||||
selectable_text_align, // 2f
|
||||
separator_text_border_size, // 1f
|
||||
|
@ -2318,26 +2371,28 @@ extern fn zguiSliderAngle(
|
|||
pub const InputTextFlags = packed struct(c_int) {
|
||||
chars_decimal: bool = false,
|
||||
chars_hexadecimal: bool = false,
|
||||
chars_scientific: bool = false,
|
||||
chars_uppercase: bool = false,
|
||||
chars_no_blank: bool = false,
|
||||
auto_select_all: bool = false,
|
||||
allow_tab_input: 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_history: bool = false,
|
||||
callback_always: 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_edit: bool = false,
|
||||
escape_clears_all: bool = false,
|
||||
_padding: u11 = 0,
|
||||
_padding: u9 = 0,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const InputTextCallbackData = extern struct {
|
||||
|
@ -2791,9 +2846,10 @@ pub const TreeNodeFlags = packed struct(c_int) {
|
|||
frame_padding: bool = false,
|
||||
span_avail_width: bool = false,
|
||||
span_full_width: bool = false,
|
||||
span_text_width: bool = false,
|
||||
span_all_columns: bool = false,
|
||||
nav_left_jumps_back_here: bool = false,
|
||||
_padding: u17 = 0,
|
||||
_padding: u16 = 0,
|
||||
|
||||
pub const collapsing_header = TreeNodeFlags{
|
||||
.framed = true,
|
||||
|
@ -2889,12 +2945,13 @@ extern fn zguiSetNextItemOpen(is_open: bool, cond: Condition) void;
|
|||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const SelectableFlags = packed struct(c_int) {
|
||||
dont_close_popups: bool = false,
|
||||
no_auto_close_popups: bool = false,
|
||||
span_all_columns: bool = false,
|
||||
allow_double_click: bool = false,
|
||||
disabled: bool = false,
|
||||
allow_overlap: bool = false,
|
||||
_padding: u27 = 0,
|
||||
highlight: bool = false,
|
||||
_padding: u26 = 0,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const Selectable = struct {
|
||||
|
@ -3023,7 +3080,10 @@ pub const TableFlags = packed struct(c_int) {
|
|||
sort_multi: 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) {
|
||||
|
@ -3207,8 +3267,14 @@ pub const MouseButton = enum(u32) {
|
|||
pub const isMouseDown = zguiIsMouseDown;
|
||||
/// `pub fn isMouseClicked(mouse_button: MouseButton) bool`
|
||||
pub const isMouseClicked = zguiIsMouseClicked;
|
||||
/// `pub fn isMouseReleased(mouse_button: MouseButton) bool`
|
||||
pub const isMouseReleased = zguiIsMouseReleased;
|
||||
/// `pub fn isMouseDoubleClicked(mouse_button: MouseButton) bool`
|
||||
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 const isItemClicked = zguiIsItemClicked;
|
||||
/// `pub fn isItemVisible() bool`
|
||||
|
@ -3231,7 +3297,10 @@ pub const isAnyItemActive = zguiIsAnyItemActive;
|
|||
pub const isAnyItemFocused = zguiIsAnyItemFocused;
|
||||
extern fn zguiIsMouseDown(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 zguiGetMouseClickedCount(mouse_button: MouseButton) u32;
|
||||
extern fn zguiIsMouseDragging(mouse_button: MouseButton, lock_threshold: f32) bool;
|
||||
extern fn zguiIsItemHovered(flags: HoveredFlags) bool;
|
||||
extern fn zguiIsItemActive() bool;
|
||||
extern fn zguiIsItemFocused() bool;
|
||||
|
@ -3245,6 +3314,9 @@ extern fn zguiIsItemToggledOpen() bool;
|
|||
extern fn zguiIsAnyItemHovered() bool;
|
||||
extern fn zguiIsAnyItemActive() bool;
|
||||
extern fn zguiIsAnyItemFocused() bool;
|
||||
|
||||
pub const isRectVisible = zguiIsRectVisible;
|
||||
extern fn zguiIsRectVisible(pos: *[2]f32) bool;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Color Utilities
|
||||
|
@ -3428,6 +3500,8 @@ pub const beginPopup = zguiBeginPopup;
|
|||
pub const endPopup = zguiEndPopup;
|
||||
/// `pub fn closeCurrentPopup() void`
|
||||
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 zguiBeginPopupContextItem() bool;
|
||||
extern fn zguiBeginPopupModal(name: [*:0]const u8, popen: ?*bool, flags: WindowFlags) bool;
|
||||
|
@ -3435,6 +3509,8 @@ extern fn zguiBeginPopup(str_id: [*:0]const u8, flags: WindowFlags) bool;
|
|||
extern fn zguiEndPopup() void;
|
||||
extern fn zguiOpenPopup(str_id: [*:0]const u8, flags: PopupFlags) void;
|
||||
extern fn zguiCloseCurrentPopup() void;
|
||||
extern fn zguiIsPopupOpen(str_id: [*:0]const u8, flags: PopupFlags) bool;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Tabs
|
||||
|
@ -3447,9 +3523,10 @@ pub const TabBarFlags = packed struct(c_int) {
|
|||
no_close_with_middle_mouse_button: bool = false,
|
||||
no_tab_list_scrolling_buttons: bool = false,
|
||||
no_tooltip: bool = false,
|
||||
draw_selected_overline: bool = false,
|
||||
fitting_policy_resize_down: bool = false,
|
||||
fitting_policy_scroll: bool = false,
|
||||
_padding: u24 = 0,
|
||||
_padding: u23 = 0,
|
||||
};
|
||||
pub const TabItemFlags = packed struct(c_int) {
|
||||
unsaved_document: bool = false,
|
||||
|
@ -3567,9 +3644,11 @@ pub const DragDropFlags = packed struct(c_int) {
|
|||
source_no_hold_open_to_others: bool = false,
|
||||
source_allow_null_id: 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_no_draw_default_rect: bool = false,
|
||||
|
@ -4071,7 +4150,7 @@ pub const DrawList = *opaque {
|
|||
&args.p,
|
||||
args.r,
|
||||
args.col,
|
||||
args.num_segments,
|
||||
@intCast(args.num_segments),
|
||||
args.thickness,
|
||||
);
|
||||
}
|
||||
|
@ -4090,7 +4169,7 @@ pub const DrawList = *opaque {
|
|||
col: u32,
|
||||
num_segments: u32,
|
||||
}) 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(
|
||||
draw_list: DrawList,
|
||||
|
@ -4174,7 +4253,7 @@ pub const DrawList = *opaque {
|
|||
&args.p4,
|
||||
args.col,
|
||||
args.thickness,
|
||||
args.num_segments,
|
||||
@intCast(args.num_segments),
|
||||
);
|
||||
}
|
||||
extern fn zguiDrawList_AddBezierCubic(
|
||||
|
@ -4203,7 +4282,7 @@ pub const DrawList = *opaque {
|
|||
&args.p3,
|
||||
args.col,
|
||||
args.thickness,
|
||||
args.num_segments,
|
||||
@intCast(args.num_segments),
|
||||
);
|
||||
}
|
||||
extern fn zguiDrawList_AddBezierQuadratic(
|
||||
|
@ -4556,6 +4635,7 @@ test {
|
|||
const testing = std.testing;
|
||||
|
||||
testing.refAllDeclsRecursive(@This());
|
||||
if (@import("zgui_options").with_gizmo) testing.refAllDeclsRecursive(gizmo);
|
||||
|
||||
init(testing.allocator);
|
||||
defer deinit();
|
||||
|
|
|
@ -10,7 +10,6 @@ pub usingnamespace @import("gui.zig");
|
|||
pub const plot = @import("plot.zig");
|
||||
pub const backend = switch (@import("zgui_options").backend) {
|
||||
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
|
||||
.sdl2_opengl3 => @import("backend_sdl2_opengl.zig"),
|
||||
.win32_dx12 => .{}, // TODO:
|
||||
.no_backend => .{},
|
||||
};
|
||||
|
|
|
@ -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;
|
101
src/plot.zig
101
src/plot.zig
|
@ -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_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) {
|
||||
north: bool = false,
|
||||
south: bool = false,
|
||||
|
@ -306,11 +313,10 @@ pub const Flags = packed struct(u32) {
|
|||
no_inputs: bool = false,
|
||||
no_menus: bool = false,
|
||||
no_box_select: bool = false,
|
||||
no_child: bool = false,
|
||||
no_frame: bool = false,
|
||||
equal: bool = false,
|
||||
crosshairs: bool = false,
|
||||
_padding: u22 = 0,
|
||||
_padding: u23 = 0,
|
||||
|
||||
pub const canvas_only = Flags{
|
||||
.no_title = true,
|
||||
|
@ -537,6 +543,92 @@ extern fn zguiPlot_PlotShaded(
|
|||
stride: i32,
|
||||
) 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) {
|
||||
no_cursors: bool = false,
|
||||
no_fit: bool = false,
|
||||
|
@ -585,6 +677,11 @@ extern fn zguiPlot_PlotText(
|
|||
flags: PlotTextFlags,
|
||||
) void;
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
pub fn isPlotHovered() bool {
|
||||
return zguiPlot_IsPlotHovered();
|
||||
}
|
||||
extern fn zguiPlot_IsPlotHovered() bool;
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/// `pub fn showDemoWindow(popen: ?*bool) void`
|
||||
pub const showDemoWindow = zguiPlot_ShowDemoWindow;
|
||||
|
|
|
@ -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" */
|
1728
src/zgui.cpp
1728
src/zgui.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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" */
|
|
@ -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" */
|
|
@ -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" */
|
Loading…
Reference in New Issue