build: Update ImGui to v1.92.1 (#2325)

Update ImGui to the latest version and rewrite most of the font handling
code
This commit is contained in:
Nik 2025-07-10 10:00:39 +02:00 committed by GitHub
parent fa62a675dc
commit d1840a2184
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 9932 additions and 5654 deletions

View File

@ -7,6 +7,7 @@ skip -rfu ^ImGui::
# Trigger breakpoint when execution reaches triggerSafeShutdown()
break triggerSafeShutdown
break __glibcxx_assert_fail
# Print backtrace after execution jumped to an invalid address
define fixbt

View File

@ -748,7 +748,7 @@ EXPORT_MODULE namespace hex {
struct GlyphRange { u16 begin, end; };
struct Offset { float x, y; };
struct Font {
struct MergeFont {
std::string name;
std::vector<u8> fontData;
std::vector<GlyphRange> glyphRanges;
@ -758,10 +758,21 @@ EXPORT_MODULE namespace hex {
std::optional<u32> defaultSize;
};
class Font {
public:
explicit Font(UnlocalizedString fontName);
void push(float size = 0.0F) const;
void pop() const;
[[nodiscard]] operator ImFont*() const;
private:
UnlocalizedString m_fontName;
};
namespace impl {
const std::vector<Font>& getFonts();
const std::vector<MergeFont>& getMergeFonts();
std::map<UnlocalizedString, ImFont*>& getFontDefinitions();
}
@ -779,6 +790,9 @@ EXPORT_MODULE namespace hex {
void registerFont(const UnlocalizedString &fontName);
ImFont* getFont(const UnlocalizedString &fontName);
void setDefaultFont(const Font& font);
const Font& getDefaultFont();
float getDpi();
float pixelsToPoints(float pixels);
float pointsToPixels(float points);

View File

@ -99,6 +99,10 @@ namespace ImGuiExt {
return m_textureId != 0;
}
[[nodiscard]] operator ImTextureRef() const noexcept {
return m_textureId;
}
[[nodiscard]] operator ImTextureID() const noexcept {
return m_textureId;
}

View File

@ -1006,35 +1006,48 @@ namespace hex {
namespace impl {
static AutoReset<std::vector<Font>> s_fonts;
const std::vector<Font>& getFonts() {
static AutoReset<std::vector<MergeFont>> s_fonts;
const std::vector<MergeFont>& getMergeFonts() {
return *s_fonts;
}
static float s_fontSize = DefaultFontSize;
void setFontSize(float size) {
s_fontSize = size;
}
static AutoReset<ImFontAtlas*> s_fontAtlas;
void setFontAtlas(ImFontAtlas* fontAtlas) {
s_fontAtlas = fontAtlas;
}
static ImFont *s_boldFont = nullptr;
static ImFont *s_italicFont = nullptr;
void setFonts(ImFont *bold, ImFont *italic) {
s_boldFont = bold;
s_italicFont = italic;
}
static AutoReset<std::map<UnlocalizedString, ImFont*>> s_fontDefinitions;
std::map<UnlocalizedString, ImFont*>& getFontDefinitions() {
return *s_fontDefinitions;
}
static AutoReset<const Font*> s_defaultFont;
}
Font::Font(UnlocalizedString fontName) : m_fontName(std::move(fontName)) {
Fonts::registerFont(m_fontName);
if (impl::s_defaultFont == nullptr)
impl::s_defaultFont = this;
}
void Font::push(float size) const {
auto font = getFont(m_fontName);
if (size <= 0.0F) {
size = font->LegacySize;
}
size *= System::getGlobalScale();
ImGui::PushFont(font, size);
}
void Font::pop() const {
ImGui::PopFont();
}
Font::operator ImFont*() const {
return getFont(m_fontName);
}
GlyphRange glyph(const char *glyph) {
u32 codepoint;
ImTextCharFromUtf8(&codepoint, glyph, nullptr);
@ -1075,7 +1088,7 @@ namespace hex {
return;
}
impl::s_fonts->emplace_back(Font {
impl::s_fonts->emplace_back(MergeFont {
wolv::util::toUTF8String(path.filename()),
fontFile.readVector(),
glyphRanges,
@ -1087,7 +1100,7 @@ namespace hex {
}
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<bool> scalable, std::optional<u32> defaultSize) {
impl::s_fonts->emplace_back(Font {
impl::s_fonts->emplace_back(MergeFont {
name,
{ data.begin(), data.end() },
glyphRanges,
@ -1098,14 +1111,6 @@ namespace hex {
});
}
float getFontSize() {
return impl::s_fontSize;
}
ImFontAtlas* getFontAtlas() {
return impl::s_fontAtlas;
}
void registerFont(const UnlocalizedString &fontName) {
(*impl::s_fontDefinitions)[fontName] = nullptr;
}
@ -1114,12 +1119,12 @@ namespace hex {
return (*impl::s_fontDefinitions)[fontName];
}
ImFont* Bold() {
return impl::s_boldFont;
void setDefaultFont(const Font& font) {
impl::s_defaultFont = &font;
}
ImFont* Italic() {
return impl::s_italicFont;
const Font& getDefaultFont() {
return **impl::s_defaultFont;
}
float getDpi() {

View File

@ -32,6 +32,7 @@ static bool s_constructingMenu = false;
static bool s_resetNeeded = true;
void macosMenuBarInit(void) {
s_menuStackSize = 0;
s_menuStack[0] = NSApp.mainMenu;
s_menuStackSize += 1;

View File

@ -1019,7 +1019,7 @@ namespace ImGuiExt {
std::string drawString;
auto textEnd = text + strlen(text);
for (auto wrapPos = text; wrapPos != textEnd;) {
wrapPos = ImGui::GetFont()->CalcWordWrapPositionA(1, wrapPos, textEnd, availableSpace.x * 0.8F);
wrapPos = ImGui::GetFont()->CalcWordWrapPosition(1, wrapPos, textEnd, availableSpace.x * 0.8F);
drawString += std::string(text, wrapPos) + "\n";
text = wrapPos;
}

View File

@ -8,10 +8,13 @@
// [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 are obsolete since 1.87 and not supported since 1.91.5]
// [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 (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues:
// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// [X] Multiple Dear ImGui contexts support.
// Missing features or Issues:
// [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround.
// [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors.
// [ ] 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.
@ -63,5 +66,8 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int
// GLFW helpers
IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds);
IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window);
IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor);
#endif // #ifndef IMGUI_DISABLE

View File

@ -4,8 +4,9 @@
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// 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: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// About WebGL/ES:
@ -30,18 +31,19 @@
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
// Backend API
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);
// 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)

View File

@ -8,10 +8,13 @@
// [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 are obsolete since 1.87 and not supported since 1.91.5]
// [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 (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues:
// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// [X] Multiple Dear ImGui contexts support.
// Missing features or Issues:
// [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround.
// [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors.
// [ ] 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.
@ -28,9 +31,16 @@
// 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.
// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2025-06-18: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069)
// 2025-06-11: Added ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) and ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) helper to facilitate making DPI-aware apps.
// 2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.
// 2025-04-26: [Docking] Disable multi-viewports under Wayland. (#8587)
// 2025-03-10: Map GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 into ImGuiKey_Oem102.
// 2025-03-03: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled with asserts enabled.
// 2025-02-21: [Docking] Update monitors and work areas information every frame, as the later may change regardless of monitor changes. (#8415)
// 2024-11-05: [Docking] Added Linux workaround for spurious mouse up events emitted while dragging and creating new viewport. (#3158, #7733, #7922)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// 2024-08-22: Moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn
@ -97,6 +107,8 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
#endif
// GLFW
@ -120,12 +132,12 @@
// IMHEX PATCH END
#ifndef _WIN32
#include <unistd.h> // for usleep()
// IMHEX PATCH BEGIN
#include <string_view> // for std::string_view
#include <cstdlib> // for std::getenv()
// IMHEX PATCH END
#endif
#ifdef __linux__
#include <string_view>
#include <cstdlib>
#endif
#include <stdio.h> // for snprintf()
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
@ -172,6 +184,17 @@ static std::string clipboardContent;
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
#define GLFW_HAS_GETPLATFORM (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform()
// Map GLFWWindow* to ImGuiContext*.
// - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource.
// - Would be simpler if we could use e.g. std::map<> as well. But we don't.
// - This is not particularly optimized as we expect size to be small and queries to be rare.
struct ImGui_ImplGlfw_WindowToContext { GLFWwindow* Window; ImGuiContext* Context; };
static ImVector<ImGui_ImplGlfw_WindowToContext> g_ContextMap;
static void ImGui_ImplGlfw_ContextMap_Add(GLFWwindow* window, ImGuiContext* ctx) { g_ContextMap.push_back(ImGui_ImplGlfw_WindowToContext{ window, ctx }); }
static void ImGui_ImplGlfw_ContextMap_Remove(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) { g_ContextMap.erase_unsorted(&entry); return; } }
static ImGuiContext* ImGui_ImplGlfw_ContextMap_Get(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) return entry.Context; return nullptr; }
// GLFW data
enum GlfwClientApi
@ -183,6 +206,7 @@ enum GlfwClientApi
struct ImGui_ImplGlfw_Data
{
ImGuiContext* Context;
GLFWwindow* Window;
GlfwClientApi ClientApi;
double Time;
@ -194,7 +218,7 @@ struct ImGui_ImplGlfw_Data
GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST];
bool InstalledCallbacks;
bool CallbacksChainForAllWindows;
bool WantUpdateMonitors;
char BackendPlatformName[32];
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
const char* CanvasSelector;
#endif
@ -222,10 +246,18 @@ struct ImGui_ImplGlfw_Data
// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks.
// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it.
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); }
static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
{
// Get data for current context
return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
}
static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window)
{
// Get data for a given GLFW window, regardless of current context (since GLFW events are sent together)
ImGuiContext* ctx = ImGui_ImplGlfw_ContextMap_Get(window);
return (ImGui_ImplGlfw_Data*)ImGui::GetIO(ctx).BackendPlatformUserData;
}
// Forward Declarations
static void ImGui_ImplGlfw_UpdateMonitors();
@ -233,6 +265,7 @@ static void ImGui_ImplGlfw_InitMultiViewportSupport();
static void ImGui_ImplGlfw_ShutdownMultiViewportSupport();
// Functions
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{
// IMHEX PATCH BEGIN
@ -286,6 +319,8 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)
case GLFW_KEY_EQUAL: return ImGuiKey_Equal;
case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket;
case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash;
case GLFW_KEY_WORLD_1: return ImGuiKey_Oem102;
case GLFW_KEY_WORLD_2: return ImGuiKey_Oem102;
case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket;
case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent;
case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock;
@ -385,11 +420,9 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)
// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW
// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630
static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window, int mods)
static void ImGui_ImplGlfw_UpdateKeyModifiers(ImGuiIO& io, GLFWwindow* window, int mods)
{
// IMHEX PATCH BEGIN
ImGuiIO& io = ImGui::GetIO();
#ifdef __linux__
static bool isX11 = [] {
const auto sessionType = std::getenv("XDG_SESSION_TYPE");
@ -415,33 +448,31 @@ static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window, int mods)
// IMHEX PATCH END
}
static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window)
static bool ImGui_ImplGlfw_ShouldChainCallback(ImGui_ImplGlfw_Data* bd, GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
return bd->CallbacksChainForAllWindows ? true : (window == bd->Window);
}
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackMousebutton(window, button, action, mods);
// Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation
if (bd->MouseIgnoreButtonUp && action == GLFW_RELEASE)
return;
ImGui_ImplGlfw_UpdateKeyModifiers(window, mods);
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods);
if (button >= 0 && button < ImGuiMouseButton_COUNT)
io.AddMouseButtonEvent(button, action == GLFW_PRESS);
}
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
@ -449,7 +480,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
return;
#endif
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
}
@ -489,21 +520,21 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackKey(window, keycode, scancode, action, mods);
if (action != GLFW_PRESS && action != GLFW_RELEASE)
return;
ImGui_ImplGlfw_UpdateKeyModifiers(window, mods);
ImGuiIO& io = ImGui::GetIO(bd->Context);
ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods);
if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows))
bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr;
keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
ImGuiIO& io = ImGui::GetIO();
ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode);
io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));
io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)
@ -511,25 +542,25 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackWindowFocus(window, focused);
// Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events
bd->MouseIgnoreButtonUp = (bd->MouseIgnoreButtonUpWaitForFocusLoss && focused == 0);
bd->MouseIgnoreButtonUpWaitForFocusLoss = false;
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
io.AddFocusEvent(focused != 0);
}
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackCursorPos(window, x, y);
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
int window_x, window_y;
@ -545,11 +576,11 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984)
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackCursorEnter(window, entered);
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
if (entered)
{
bd->MouseWindow = window;
@ -565,32 +596,32 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))
bd->PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
io.AddInputCharacter(c);
}
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
bd->WantUpdateMonitors = true;
// This function is technically part of the API even if we stopped using the callback, so leaving it around.
}
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*)
static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void* user_data)
{
// Mimic Emscripten_HandleWheel() in SDL.
// Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096
ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;
float multiplier = 0.0f;
if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step.
else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step.
else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps.
float wheel_x = ev->deltaX * -multiplier;
float wheel_y = ev->deltaY * -multiplier;
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO(bd->Context);
io.AddMouseWheelEvent(wheel_x, wheel_y);
//IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y);
return EM_TRUE;
@ -603,7 +634,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!");
IM_ASSERT(bd->Window == window);
@ -620,7 +651,7 @@ void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!");
IM_ASSERT(bd->Window == window);
@ -670,24 +701,40 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
// Setup backend capabilities flags
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)", GLFW_VERSION_COMBINED);
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_glfw";
io.BackendPlatformName = bd->BackendPlatformName;
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bool has_viewports = false;
#ifndef __EMSCRIPTEN__
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
has_viewports = true;
#if GLFW_HAS_GETPLATFORM
if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)
has_viewports = false;
#endif
if (has_viewports)
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
#endif
#if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
#endif
bd->Context = ImGui::GetCurrentContext();
bd->Window = window;
bd->Time = 0.0;
bd->WantUpdateMonitors = true;
ImGui_ImplGlfw_ContextMap_Add(window, bd->Context);
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); };
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); };
#if GLFW_VERSION_COMBINED < 3300
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window, text); };
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window); };
#else
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); };
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); };
#endif
#ifdef __EMSCRIPTEN__
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };
#endif
@ -749,11 +796,14 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
#else
IM_UNUSED(main_viewport);
#endif
ImGui_ImplGlfw_InitMultiViewportSupport();
if (has_viewports)
ImGui_ImplGlfw_InitMultiViewportSupport();
// Windows: register a WndProc hook so we can intercept some messages.
#ifdef _WIN32
bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC);
HWND hwnd = (HWND)main_viewport->PlatformHandleRaw;
::SetPropA(hwnd, "IMGUI_BACKEND_DATA", bd);
bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
IM_ASSERT(bd->PrevWndProc != nullptr);
::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif
@ -764,7 +814,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817
if (emscripten::glfw3::IsRuntimePlatformApple())
{
ImGui::GetIO().ConfigMacOSXBehaviors = true;
io.ConfigMacOSXBehaviors = true;
// Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used.
// This means that Meta + V only registers a single key-press, even if the keys are held.
@ -815,6 +865,7 @@ void ImGui_ImplGlfw_Shutdown()
// Windows: restore our WndProc hook
#ifdef _WIN32
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", nullptr);
::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc);
bd->PrevWndProc = nullptr;
#endif
@ -822,6 +873,7 @@ void ImGui_ImplGlfw_Shutdown()
io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
ImGui_ImplGlfw_ContextMap_Remove(bd->Window);
IM_DELETE(bd);
}
@ -933,7 +985,7 @@ static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0
static void ImGui_ImplGlfw_UpdateGamepads()
{
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.
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs, but see #8075
return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
@ -983,9 +1035,7 @@ static void ImGui_ImplGlfw_UpdateGamepads()
static void ImGui_ImplGlfw_UpdateMonitors()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
bd->WantUpdateMonitors = false;
int monitors_count = 0;
GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count);
@ -1012,35 +1062,63 @@ static void ImGui_ImplGlfw_UpdateMonitors()
monitor.WorkSize = ImVec2((float)w, (float)h);
}
#endif
#if GLFW_HAS_PER_MONITOR_DPI
// Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime.
float x_scale, y_scale;
glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale);
if (x_scale == 0.0f)
float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfw_monitors[n]);
if (scale == 0.0f)
continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902.
monitor.DpiScale = x_scale;
#endif
monitor.DpiScale = scale;
monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes"
platform_io.Monitors.push_back(monitor);
}
}
// - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend.
// - Apple platforms use FramebufferScale so we always return 1.0f.
// - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle.
float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window)
{
#if GLFW_HAS_PER_MONITOR_DPI && !defined(__APPLE__)
float x_scale, y_scale;
glfwGetWindowContentScale(window, &x_scale, &y_scale);
return x_scale;
#else
IM_UNUSED(window);
return 1.0f;
#endif
}
float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor)
{
#if GLFW_HAS_PER_MONITOR_DPI && !defined(__APPLE__)
float x_scale, y_scale;
glfwGetMonitorContentScale(monitor, &x_scale, &y_scale);
return x_scale;
#else
IM_UNUSED(monitor);
return 1.0f;
#endif
}
static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)
{
int w, h;
int display_w, display_h;
glfwGetWindowSize(window, &w, &h);
glfwGetFramebufferSize(window, &display_w, &display_h);
if (out_size != nullptr)
*out_size = ImVec2((float)w, (float)h);
if (out_framebuffer_scale != nullptr)
*out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / (float)w, (float)display_h / (float)h) : ImVec2(1.0f, 1.0f);
}
void ImGui_ImplGlfw_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
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;
int display_w, display_h;
glfwGetWindowSize(bd->Window, &w, &h);
glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
if (bd->WantUpdateMonitors)
ImGui_ImplGlfw_UpdateMonitors();
// Setup main viewport size (every frame to accommodate for window resizing)
ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);
ImGui_ImplGlfw_UpdateMonitors();
// Setup time step
// (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644)
@ -1105,7 +1183,7 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_s
// 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);
emscripten_set_wheel_callback(bd->CanvasSelector, bd, 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
@ -1127,10 +1205,10 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* c
// 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..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
// Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplGlfw_ViewportData
{
GLFWwindow* Window;
GLFWwindow* Window; // Stored in ImGuiViewport::PlatformHandle
bool WindowOwned;
int IgnoreWindowPosEventFrame;
int IgnoreWindowSizeEventFrame;
@ -1209,9 +1287,11 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport)
GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : nullptr;
vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", nullptr, share_window);
vd->WindowOwned = true;
ImGui_ImplGlfw_ContextMap_Add(vd->Window, bd->Context);
viewport->PlatformHandle = (void*)vd->Window;
#ifdef _WIN32
viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window);
::SetPropA((HWND)viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", bd);
// IMHEX PATCH BEGIN
// REASON: The patch with #include <GLFW/glfw3native.h>
// #elif defined(__APPLE__)
@ -1252,11 +1332,11 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)
// Release any keys that were pressed in the window being destroyed and are still held down,
// because we will not receive any release events after window is destroyed.
for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++) {
for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++)
if (bd->KeyOwnerWindows[i] == vd->Window)
ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called.
}
ImGui_ImplGlfw_ContextMap_Remove(vd->Window);
glfwDestroyWindow(vd->Window);
}
vd->Window = nullptr;
@ -1280,12 +1360,10 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport)
::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
}
// GLFW hack: install hook for WM_NCHITTEST message handler
#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)
// GLFW hack: install WndProc for mouse source event and WM_NCHITTEST message handler.
::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport);
vd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif
#if !GLFW_HAS_FOCUS_ON_SHOW
// GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window.
@ -1329,7 +1407,7 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport)
static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
#if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX
#if defined(__APPLE__) && !GLFW_HAS_OSX_WINDOW_POS_FIX
// Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are
// positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it
// doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based
@ -1343,6 +1421,14 @@ static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y);
}
static ImVec2 ImGui_ImplGlfw_GetWindowFramebufferScale(ImGuiViewport* viewport)
{
ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
ImVec2 framebuffer_scale;
ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(vd->Window, nullptr, &framebuffer_scale);
return framebuffer_scale;
}
static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title)
{
ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
@ -1441,6 +1527,7 @@ static void ImGui_ImplGlfw_InitMultiViewportSupport()
platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos;
platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize;
platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize;
platform_io.Platform_GetWindowFramebufferScale = ImGui_ImplGlfw_GetWindowFramebufferScale;
platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus;
platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus;
platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized;
@ -1484,7 +1571,9 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
}
static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)::GetPropA(hWnd, "IMGUI_BACKEND_DATA");
ImGuiIO& io = ImGui::GetIO(bd->Context);
WNDPROC prev_wndproc = bd->PrevWndProc;
ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT");
if (viewport != NULL)
@ -1500,7 +1589,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP:
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP:
ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
break;
// We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs".
@ -1517,6 +1606,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara
break;
}
#endif
default: break;
}
return ::CallWindowProcW(prev_wndproc, hWnd, msg, wParam, lParam);
}

View File

@ -4,8 +4,9 @@
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// 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: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// About WebGL/ES:
@ -23,7 +24,12 @@
// 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.
// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture().
// 2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664)
// 2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406)
// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
// 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.
@ -53,7 +59,7 @@
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre-3.3 context which have the defines set by a loader.
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
@ -113,10 +119,12 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
// IMHEX PATCH BEGIN
#if !defined(WINGDIAPI)
#define WINGDIAPI extern "C"
#define APIENTRY
#endif
// IMHEX PATCH END
#include "imgui.h"
#ifndef IMGUI_DISABLE
@ -130,6 +138,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
@ -141,6 +150,7 @@
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx'
#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
#endif
// GL includes
@ -168,9 +178,11 @@
// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).
// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):
// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped
// Typically you would run: python3 ./gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases
// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.
#define IMGL3W_IMPL
#define IMGUI_IMPL_OPENGL_LOADER_IMGL3W
#include "imgui_impl_opengl3_loader.h"
#endif
@ -230,7 +242,7 @@ struct ImGui_ImplOpenGL3_Data
bool GlProfileIsES3;
bool GlProfileIsCompat;
GLint GlProfileMask;
GLuint FontTexture;
GLint MaxTextureSize;
GLuint ShaderHandle;
GLint AttribLocationTex; // Uniforms location
GLint AttribLocationProjMtx;
@ -243,6 +255,7 @@ struct ImGui_ImplOpenGL3_Data
bool HasPolygonMode;
bool HasClipOrigin;
bool UseBufferSubData;
ImVector<char> TempBuffer;
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
};
@ -255,8 +268,8 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
}
// Forward Declarations
static void ImGui_ImplOpenGL3_InitPlatformInterface();
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
static void ImGui_ImplOpenGL3_InitMultiViewportSupport();
static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport();
// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
@ -282,20 +295,31 @@ struct ImGui_ImplOpenGL3_VtxAttribState
};
#endif
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
// Not static to allow third-party code to use that if they want to (but undocumented)
bool ImGui_ImplOpenGL3_InitLoader();
bool ImGui_ImplOpenGL3_InitLoader()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Initialize our loader
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
if (imgl3wInit() != 0)
#ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W
if (glGetIntegerv == nullptr && imgl3wInit() != 0)
{
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return false;
}
#endif
return true;
}
// Functions
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 loader
if (!ImGui_ImplOpenGL3_InitLoader())
return false;
// Setup backend capabilities flags
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
@ -303,13 +327,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
io.BackendRendererName = "imgui_impl_opengl3";
// Query for GL version (e.g. 320 for GL 3.2)
const char* gl_version_str = (const char*)glGetString(GL_VERSION);
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GLES 2
bd->GlVersion = 200;
bd->GlProfileIsES2 = true;
IM_UNUSED(gl_version_str);
#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);
@ -317,11 +342,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
if (major == 0 && minor == 0)
sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
#if defined(GL_CONTEXT_PROFILE_MASK)
if (bd->GlVersion >= 320)
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask);
bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0;
#endif
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize);
#if defined(IMGUI_IMPL_OPENGL_ES3)
bd->GlProfileIsES3 = true;
@ -330,6 +351,12 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
bd->GlProfileIsES3 = true;
#endif
#if defined(GL_CONTEXT_PROFILE_MASK)
if (!bd->GlProfileIsES3 && bd->GlVersion >= 320)
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask);
bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0;
#endif
bd->UseBufferSubData = false;
/*
// Query vendor to enable glBufferSubData kludge
@ -342,14 +369,18 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
#endif
#ifdef IMGUI_IMPL_OPENGL_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]
printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%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)
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize;
// 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.
@ -390,8 +421,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
}
#endif
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplOpenGL3_InitPlatformInterface();
ImGui_ImplOpenGL3_InitMultiViewportSupport();
return true;
}
@ -402,11 +432,11 @@ 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_ShutdownMultiViewportSupport();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
IM_DELETE(bd);
}
@ -415,8 +445,11 @@ void ImGui_ImplOpenGL3_NewFrame()
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?");
ImGui_ImplOpenGL3_InitLoader(); // Lazily init loader if not already done for e.g. DLL boundaries.
if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
if (!ImGui_ImplOpenGL3_CreateDeviceObjects())
IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!");
}
// IMHEX PATCH BEGIN
@ -454,7 +487,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310)
if (!bd->GlProfileIsES3 && bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
#endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
@ -535,8 +568,17 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (fb_width <= 0 || fb_height <= 0)
return;
ImGui_ImplOpenGL3_InitLoader(); // Lazily init loader if not already done for e.g. DLL boundaries.
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
if (draw_data->Textures != nullptr)
for (ImTextureData* tex : *draw_data->Textures)
if (tex->Status != ImTextureStatus_OK)
ImGui_ImplOpenGL3_UpdateTexture(tex);
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
@ -573,7 +615,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
GLboolean last_enable_primitive_restart = (!bd->GlProfileIsES3 && bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
#endif
// Setup desired GL state
@ -592,7 +634,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawList* draw_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
// - OpenGL drivers are in a very sorry state nowadays....
@ -602,8 +644,8 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code.
// We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path.
// - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues.
const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
const GLsizeiptr vtx_buffer_size = (GLsizeiptr)draw_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
const GLsizeiptr idx_buffer_size = (GLsizeiptr)draw_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
if (bd->UseBufferSubData)
{
if (bd->VertexBufferSize < vtx_buffer_size)
@ -616,18 +658,18 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
bd->IndexBufferSize = idx_buffer_size;
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW));
}
GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data));
GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data));
GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data));
GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data));
}
else
{
GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW));
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW));
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW));
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != nullptr)
{
// User callback, registered via ImDrawList::AddCallback()
@ -635,7 +677,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
pcmd->UserCallback(draw_list, pcmd);
}
else
{
@ -692,7 +734,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
@ -705,48 +747,82 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
(void)bd; // Not all compilation paths use this
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex)
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
glDeleteTextures(1, &gl_tex_id);
// Build texture atlas
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
GLint last_texture;
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
GL_CALL(glGenTextures(1, &bd->FontTexture));
GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
#endif
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
return true;
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
tex->SetTexID(ImTextureID_Invalid);
tex->SetStatus(ImTextureStatus_Destroyed);
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex)
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->FontTexture)
if (tex->Status == ImTextureStatus_WantCreate)
{
glDeleteTextures(1, &bd->FontTexture);
io.Fonts->SetTexID(0);
bd->FontTexture = 0;
// Create and upload new texture to graphics system
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
const void* pixels = tex->GetPixels();
GLuint gl_texture_id = 0;
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
GLint last_texture;
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
GL_CALL(glGenTextures(1, &gl_texture_id));
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
#endif
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
// Store identifiers
tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id);
tex->SetStatus(ImTextureStatus_OK);
// Restore state
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
}
else if (tex->Status == ImTextureStatus_WantUpdates)
{
// Update selected blocks. We only ever write to textures regions which have never been used before!
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
GLint last_texture;
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id));
#if 0// GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width));
for (ImTextureRect& r : tex->Updates)
GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y)));
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
#else
// GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line.
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
for (ImTextureRect& r : tex->Updates)
{
const int src_pitch = r.w * tex->BytesPerPixel;
bd->TempBuffer.resize(r.h * src_pitch);
char* out_p = bd->TempBuffer.Data;
for (int y = 0; y < r.h; y++, out_p += src_pitch)
memcpy(out_p, tex->GetPixelsAt(r.x, r.y + y), src_pitch);
IM_ASSERT(out_p == bd->TempBuffer.end());
GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data));
}
#endif
tex->SetStatus(ImTextureStatus_OK);
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state
}
else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
ImGui_ImplOpenGL3_DestroyTexture(tex);
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
@ -796,7 +872,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
@ -931,7 +1007,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
" Out_Color = Frag_Color;\n"
" SRC1_Color = vec4(texture(Texture, Frag_UV.st).rgb * Frag_Color.aaa,1.0);\n"
" }\n"
// IMHEX PATCH END
// IMHEX PATCH END
"}\n";
// Select shaders matching our GLSL versions
@ -960,23 +1036,28 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
// Create shaders
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
GLuint vert_handle;
GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER));
glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr);
glCompileShader(vert_handle);
CheckShader(vert_handle, "vertex shader");
if (!CheckShader(vert_handle, "vertex shader"))
return false;
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
GLuint frag_handle;
GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER));
glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr);
glCompileShader(frag_handle);
CheckShader(frag_handle, "fragment shader");
if (!CheckShader(frag_handle, "fragment shader"))
return false;
// Link
bd->ShaderHandle = glCreateProgram();
glAttachShader(bd->ShaderHandle, vert_handle);
glAttachShader(bd->ShaderHandle, frag_handle);
glLinkProgram(bd->ShaderHandle);
CheckProgram(bd->ShaderHandle, "shader program");
if (!CheckProgram(bd->ShaderHandle, "shader program"))
return false;
glDetachShader(bd->ShaderHandle, vert_handle);
glDetachShader(bd->ShaderHandle, frag_handle);
@ -993,8 +1074,6 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
glGenBuffers(1, &bd->VboHandle);
glGenBuffers(1, &bd->ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
@ -1014,7 +1093,11 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
// Destroy all textures
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
if (tex->RefCount == 1)
ImGui_ImplOpenGL3_DestroyTexture(tex);
}
//--------------------------------------------------------------------------------------------------------
@ -1034,13 +1117,13 @@ static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*)
ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplOpenGL3_InitPlatformInterface()
static void ImGui_ImplOpenGL3_InitMultiViewportSupport()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
}
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport()
{
ImGui::DestroyPlatformWindows();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.9b
// dear imgui, v1.92.1
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@ -37,6 +37,7 @@ Index of this file:
// [SECTION] Tab bar, Tab item support
// [SECTION] Table support
// [SECTION] ImGui internal API
// [SECTION] ImFontLoader
// [SECTION] ImFontAtlas internal API
// [SECTION] Test Engine specific hooks (imgui_test_engine)
@ -132,7 +133,7 @@ Index of this file:
//-----------------------------------------------------------------------------
// Utilities
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImPool<>, ImChunkStream<>)
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>)
struct ImBitVector; // Store 1-bit per value
struct ImRect; // An axis-aligned rectangle (2 points)
struct ImGuiTextIndex; // Maintain a line index for a text buffer.
@ -140,6 +141,9 @@ struct ImGuiTextIndex; // Maintain a line index for a text buffer.
// ImDrawList/ImFontAtlas
struct ImDrawDataBuilder; // Helper to build a ImDrawData instance
struct ImDrawListSharedData; // Data shared between all ImDrawList instances
struct ImFontAtlasBuilder; // Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasPostProcessData; // Data available to potential texture post-processing functions
struct ImFontAtlasRectEntry; // Packed rectangle lookup entry
// ImGui
struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others)
@ -213,6 +217,10 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
// Table column indexing
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
@ -248,7 +256,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_DOCKING(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventDocking) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_VIEWPORT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventViewport) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@ -360,6 +368,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
// - Helper: ImBitArray
// - Helper: ImBitVector
// - Helper: ImSpan<>, ImSpanAllocator<>
// - Helper: ImStableVector<>
// - Helper: ImPool<>
// - Helper: ImChunkStream<>
// - Helper: ImGuiTextIndex
@ -391,6 +400,7 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count.
IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't).
IMGUI_API char* ImStrdup(const char* str); // Duplicate a string.
IMGUI_API void* ImMemdup(const void* src, size_t size); // Duplicate a chunk of memory.
IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed.
IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range.
IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line
@ -505,6 +515,8 @@ static inline float ImTrunc(float f)
static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
static inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
@ -537,6 +549,14 @@ struct ImVec1
constexpr ImVec1(float _x) : x(_x) { }
};
// Helper: ImVec2i (2D vector, integer)
struct ImVec2i
{
int x, y;
constexpr ImVec2i() : x(0), y(0) {}
constexpr ImVec2i(int _x, int _y) : x(_x), y(_y) {}
};
// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
struct ImVec2ih
{
@ -688,6 +708,39 @@ struct ImSpanAllocator
inline void GetSpan(int n, ImSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
};
// Helper: ImStableVector<>
// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear().
// Important: does not destruct anything!
// Implemented only the minimum set of functions we need for it.
template<typename T, int BLOCK_SIZE>
struct ImStableVector
{
int Size = 0;
int Capacity = 0;
ImVector<T*> Blocks;
// Functions
inline ~ImStableVector() { for (T* block : Blocks) IM_FREE(block); }
inline void clear() { Size = Capacity = 0; Blocks.clear_delete(); }
inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; }
inline void reserve(int new_cap)
{
new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE);
int old_count = Capacity / BLOCK_SIZE;
int new_count = new_cap / BLOCK_SIZE;
if (new_count <= old_count)
return;
Blocks.resize(new_count);
for (int n = old_count; n < new_count; n++)
Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE);
Capacity = new_cap;
}
inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; }
};
// Helper: ImPool<>
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
@ -796,17 +849,20 @@ IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStorag
// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
struct IMGUI_API ImDrawListSharedData
{
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImFont* Font; // Current/default font (optional, for simplified AddText overload)
float FontSize; // Current/default font size (optional, for simplified AddText overload)
float FontScale; // Current/default font scale (== FontSize / Font->FontSize)
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines)
ImFontAtlas* FontAtlas; // Current font atlas
ImFont* Font; // Current font (used for simplified AddText overload)
float FontSize; // Current font size (used for for simplified AddText overload)
float FontScale; // Current font scale (== FontSize / Font->FontSize)
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
float InitialFringeScale; // Initial scale to apply to AA fringe
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
ImVector<ImVec2> TempBuffer; // Temporary write buffer
ImVector<ImDrawList*> DrawLists; // All draw lists associated to this ImDrawListSharedData
ImGuiContext* Context; // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
// Lookup tables
ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
@ -817,6 +873,7 @@ struct IMGUI_API ImDrawListSharedData
const ImVec4* ShadowRectUvs; // UV coordinates for shadow texture (10 entries)
ImDrawListSharedData();
~ImDrawListSharedData();
void SetCircleTessellationMaxError(float max_error);
};
@ -828,6 +885,13 @@ struct ImDrawDataBuilder
ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};
struct ImFontStackData
{
ImFont* Font;
float FontSizeBeforeScaling; // ~~ style.FontSizeBase
float FontSizeAfterScaling; // ~~ g.FontSize
};
//-----------------------------------------------------------------------------
// [SECTION] Style support
//-----------------------------------------------------------------------------
@ -899,6 +963,7 @@ enum ImGuiItemFlagsPrivate_
ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
ImGuiItemFlags_NoMarkEdited = 1 << 16, // false // Skip calling MarkItemEdited()
ImGuiItemFlags_NoFocus = 1 << 17, // false // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior().
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
@ -927,6 +992,7 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid.
ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
//ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
// Additional status + semantic for ImGuiTestEngine
#ifdef IMGUI_ENABLE_TEST_ENGINE
@ -976,8 +1042,10 @@ enum ImGuiButtonFlagsPrivate_
ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item
ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoFocus = 1 << 22, // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking.
ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease,
//ImGuiButtonFlags_NoKeyModifiers = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4
};
// Extend ImGuiComboFlags_
@ -1010,9 +1078,11 @@ enum ImGuiSelectableFlagsPrivate_
// Extend ImGuiTreeNodeFlags_
enum ImGuiTreeNodeFlagsPrivate_
{
ImGuiTreeNodeFlags_NoNavFocus = 1 << 27,// Don't claim nav focus when interacting with this item (#8551)
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,
};
enum ImGuiSeparatorFlags_
@ -1307,15 +1377,18 @@ struct ImGuiLastItemData
};
// Store data emitted by TreeNode() for usage by TreePop()
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data
// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
// Only stored when the node is a potential candidate for landing on a Left arrow jump.
struct ImGuiTreeNodeStackData
{
ImGuiID ID;
ImGuiTreeNodeFlags TreeFlags;
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
float DrawLinesX1;
float DrawLinesToNodesY2;
ImGuiTableColumnIdx DrawLinesTableColumn;
};
// sizeof() = 20
@ -1634,6 +1707,7 @@ enum ImGuiNavRenderCursorFlags_
ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4
ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4
ImGuiNavHighlightFlags_NoRounding = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4
//ImGuiNavHighlightFlags_TypeThin = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.90.2
#endif
};
@ -2204,11 +2278,13 @@ struct ImGuiMetricsConfig
bool ShowDrawCmdMesh = true;
bool ShowDrawCmdBoundingBoxes = true;
bool ShowTextEncodingViewer = false;
bool ShowTextureUsedRect = false;
bool ShowDockingNodes = false;
int ShowWindowsRectsType = -1;
int ShowTablesRectsType = -1;
int HighlightMonitorIdx = -1;
ImGuiID HighlightViewportID = 0;
bool ShowFontPreview = true;
};
struct ImGuiStackLevelInfo
@ -2261,16 +2337,18 @@ struct ImGuiContextHook
struct ImGuiContext
{
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style;
ImGuiConfigFlags ConfigFlagsCurrFrame; // = g.IO.ConfigFlags at the time of NewFrame()
ImGuiConfigFlags ConfigFlagsLastFrame;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
float FontScale; // == FontSize / Font->FontSize
ImVector<ImFontAtlas*> FontAtlases; // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
ImFont* Font; // Currently bound font. (== FontStack.back().Font)
ImFontBaked* FontBaked; // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize))
float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).
float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified.
float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f.
float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked().
float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
@ -2315,7 +2393,7 @@ struct ImGuiContext
ImVec2 WheelingAxisAvg;
// Item/widgets state and tracking information
ImGuiID DebugDrawIdConflicts; // Set when we detect multiple items with the same identifier
ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
@ -2373,7 +2451,7 @@ struct ImGuiContext
ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line)
ImVector<ImGuiColorMod> ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
ImVector<ImGuiStyleMod> StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImFontStackData> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImGuiFocusScopeData> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
ImVector<ImGuiItemFlags> ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
ImVector<ImGuiGroupData> GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
@ -2404,18 +2482,19 @@ struct ImGuiContext
ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow'
ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer)
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID()
ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
ImGuiActivateFlags NavActivateFlags;
ImVector<ImGuiFocusScopeData> NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.
ImGuiID NavHighlightActivatedId;
float NavHighlightActivatedTimer;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
ImS8 NavCursorHideFrames;
//ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606.
// Navigation: Init & Move Requests
bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
@ -2450,6 +2529,7 @@ struct ImGuiContext
bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
bool ConfigNavWindowingWithGamepad; // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
@ -2457,6 +2537,7 @@ struct ImGuiContext
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
ImGuiInputSource NavWindowingInputSource;
bool NavWindowingToggleLayer;
ImGuiKey NavWindowingToggleKey;
ImVec2 NavWindowingAccumDeltaPos;
@ -2527,7 +2608,8 @@ struct ImGuiContext
// Widget state
ImGuiInputTextState InputTextState;
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFont InputTextPasswordFont;
ImFontBaked InputTextPasswordFontBackupBaked;
ImFontFlags InputTextPasswordFontBackupFlags;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
@ -2559,12 +2641,12 @@ struct ImGuiContext
ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest()
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
ImGuiID PlatformImeViewport;
// Extensions
// FIXME: We could provide an API to register one slot in an array held in ImGuiContext?
ImVector<ImTextureData*> UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
ImGuiDockContext DockContext;
void (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar);
@ -2626,6 +2708,10 @@ struct ImGuiContext
ImGuiIDStackTool DebugIDStackTool;
ImGuiDebugAllocInfo DebugAllocInfo;
ImGuiDockNode* DebugHoveredDockNode; // Hovered dock node.
#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)
ImGuiStorage DebugDrawIdConflictsAliveCount;
ImGuiStorage DebugDrawIdConflictsHighlightSet;
#endif
// Misc
float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames..
@ -2634,7 +2720,7 @@ struct ImGuiContext
float FramerateSecPerFrameAccum;
int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
int WantCaptureKeyboardNextFrame; // "
int WantTextInputNextFrame;
int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs.
ImVector<char> TempBuffer; // Temporary text buffer
char TempKeychordName[64];
@ -2680,7 +2766,8 @@ struct IMGUI_API ImGuiWindowTempData
ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement
int TreeDepth; // Current tree depth.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeRecordsClippedNodesY2Mask; // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.
ImVector<ImGuiWindow*> ChildWindows;
ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state)
ImGuiOldColumns* CurrentColumns; // Current columns set
@ -2800,7 +2887,6 @@ struct IMGUI_API ImGuiWindow
ImVector<ImGuiOldColumns> ColumnsStorage;
float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale()
float FontWindowScaleParents;
float FontDpiScale;
float FontRefSize; // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window.
int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)
@ -2848,9 +2934,11 @@ public:
// We don't use g.FontSize because the window may be != g.CurrentWindow.
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontBaseSize * FontWindowScale * FontDpiScale * FontWindowScaleParents; }
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }
// [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
//float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontDpiScale * FontWindowScaleParents;
};
//-----------------------------------------------------------------------------
@ -2941,11 +3029,7 @@ struct IMGUI_API ImGuiTabBar
//-----------------------------------------------------------------------------
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 512 // May be further lifted
// Our current column maximum is 64 but we may raise that in the future.
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
#define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
// [Internal] sizeof() ~ 112
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
@ -3144,7 +3228,7 @@ struct IMGUI_API ImGuiTable
bool IsSortSpecsDirty;
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool IsSettingsRequestLoad;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
@ -3278,9 +3362,18 @@ namespace ImGui
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
// Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture
IMGUI_API void UnregisterUserTexture(ImTextureData* tex);
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling);
IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling);
IMGUI_API void SetFontRasterizerDensity(float rasterizer_density);
inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }
inline float GetRoundedFontSize(float size) { return IM_ROUND(size); }
IMGUI_API ImFont* GetDefaultFont();
IMGUI_API void PushPasswordFont();
IMGUI_API void PopPasswordFont();
inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); }
IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
@ -3290,7 +3383,7 @@ namespace ImGui
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);
IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock);
@ -3422,7 +3515,7 @@ namespace ImGui
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
@ -3438,7 +3531,7 @@ namespace ImGui
// This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are
// much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.
IMGUI_API void FocusItem(); // Focus last item (no selection/activation).
IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again.
IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7.
// Inputs
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
@ -3640,6 +3733,8 @@ namespace ImGui
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TablePushColumnChannel(int column_n);
IMGUI_API void TablePopColumnChannel();
IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
@ -3718,7 +3813,7 @@ namespace ImGui
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
@ -3739,11 +3834,15 @@ namespace ImGui
IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold);
// Widgets
// Widgets: Text
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
IMGUI_API void TextAligned(float align_x, float size_x, const char* fmt, ...); // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
IMGUI_API void TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
// Widgets
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
@ -3767,6 +3866,8 @@ namespace ImGui
// Widgets: Tree Nodes
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
IMGUI_API void TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
IMGUI_API void TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id);
IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open);
@ -3848,7 +3949,9 @@ namespace ImGui
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
IMGUI_API void DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask);
IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
IMGUI_API void DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the "current" texture.
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table);
@ -3871,6 +3974,7 @@ namespace ImGui
//inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89
//inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
// (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'
@ -3882,31 +3986,192 @@ namespace ImGui
} // namespace ImGui
//-----------------------------------------------------------------------------
// [SECTION] ImFontLoader
//-----------------------------------------------------------------------------
// Hooks and storage for a given font backend.
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be public, but API is still going to be evolve.
struct ImFontLoader
{
const char* Name;
bool (*LoaderInit)(ImFontAtlas* atlas);
void (*LoaderShutdown)(ImFontAtlas* atlas);
bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);
void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);
bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x);
// Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
// FIXME: At this point the two other types of buffers may be managed by core to be consistent?
size_t FontBakedSrcLoaderDataSize;
ImFontLoader() { memset(this, 0, sizeof(*this)); }
};
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype();
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper.
#endif
//-----------------------------------------------------------------------------
// [SECTION] ImFontAtlas internal API
//-----------------------------------------------------------------------------
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be in ImGuiPlatformIO, but we are far from ready to make this public.
struct ImFontBuilderIO
#define IMGUI_FONT_SIZE_MAX (512.0f)
#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE (128.0f)
// Helpers: ImTextureRef ==/!= operators provided as convenience
// (note that _TexID and _TexData are never set simultaneously)
inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; }
inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
#define ImFontAtlasRectId_GenerationShift_ (20)
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; }
inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
// User are returned ImFontAtlasRectId values which are meant to be persistent.
// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.
// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.
// Having this also makes it easier to e.g. sort rectangles during repack.
struct ImFontAtlasRectEntry
{
bool (*FontBuilder_Build)(ImFontAtlas* atlas);
int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int IsUsed : 1;
};
// Helper for font builder
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
// Data available to potential texture post-processing functions
struct ImFontAtlasPostProcessData
{
ImFontAtlas* FontAtlas;
ImFont* Font;
ImFontConfig* FontSrc;
ImFontBaked* FontBaked;
ImFontGlyph* Glyph;
// Pixel data
void* Pixels;
ImTextureFormat Format;
int Pitch;
int Width;
int Height;
};
// We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it)
#ifdef IMGUI_STB_NAMESPACE
namespace IMGUI_STB_NAMESPACE { struct stbrp_node; }
typedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im;
#else
struct stbrp_node;
typedef stbrp_node stbrp_node_im;
#endif
struct stbrp_context_opaque { char data[80]; };
// Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasBuilder
{
stbrp_context_opaque PackContext; // Actually 'stbrp_context' but we don't want to define this in the header file.
ImVector<stbrp_node_im> PackNodes;
ImVector<ImTextureRect> Rects;
ImVector<ImFontAtlasRectEntry> RectsIndex; // ImFontAtlasRectId -> index into Rects[]
ImVector<unsigned char> TempBuffer; // Misc scratch buffer
int RectsIndexFreeListStart;// First unused entry
int RectsPackedCount; // Number of packed rectangles.
int RectsPackedSurface; // Number of packed pixels. Used when compacting to heuristically find the ideal texture size.
int RectsDiscardedCount;
int RectsDiscardedSurface;
int FrameCount; // Current frame count
ImVec2i MaxRectSize; // Largest rectangle to pack (de-facto used as a "minimum texture size")
ImVec2i MaxRectBounds; // Bottom-right most used pixels
bool LockDisableResize; // Disable resizing texture
bool PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything.
// Cache of all ImFontBaked
ImStableVector<ImFontBaked,32> BakedPool;
ImGuiStorage BakedMap; // BakedId --> ImFontBaked*
int BakedDiscardedCount;
// Custom rectangle identifiers
ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure.
ImFontAtlasRectId PackIdLinesTexData;
ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; }
};
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);
IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);
IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects
IMGUI_API ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1);
IMGUI_API void ImFontAtlasTextureCompact(ImFontAtlas* atlas);
IMGUI_API ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);
IMGUI_API void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);
IMGUI_API bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed
IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);
IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames);
IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);
IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
IMGUI_API void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x);
IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
IMGUI_API void ImFontAtlasPackInit(ImFontAtlas* atlas);
IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures);
IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);
IMGUI_API void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);
IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);
IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status);
IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
IMGUI_API void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);
#endif
IMGUI_API void ImFontAtlasUpdateSourcesPointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* src, int* out_oversample_h, int* out_oversample_v);
IMGUI_API bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);

View File

@ -141,6 +141,7 @@
// with previous char)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (return type is int, -1 means not valid to insert)
// (not supported if you want to use UTF-8, see below)
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
// as manually wordwrapping for end-of-line positioning
@ -178,6 +179,13 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
//
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
@ -250,8 +258,10 @@
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
//
//
// When rendering, you can read the cursor position and selection state from
@ -400,6 +410,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove
#endif
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
#endif
/////////////////////////////////////////////////////////////////////////////
//
@ -648,17 +668,6 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
}
}
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
@ -668,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
--c;
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while (c >= 0 && !is_word_boundary(str, c))
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( c < 0 )
c = 0;
@ -684,9 +693,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
while( c < len && !is_word_boundary( str, c ) )
++c;
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( c > len )
c = len;
@ -739,6 +748,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int
#endif
// API key: process text input
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
@ -753,8 +763,7 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
} else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
@ -771,6 +780,7 @@ retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
@ -918,8 +928,9 @@ retry:
state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -927,7 +938,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -980,8 +992,9 @@ retry:
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -989,7 +1002,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -1002,8 +1016,13 @@ retry:
// go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
while (prev_scan > 0)
{
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
prev_scan = prev;
}
find.first_char = find.prev_first;
find.prev_first = prev_scan;
}
@ -1082,7 +1101,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@ -1094,9 +1113,9 @@ retry:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
}
@ -1110,7 +1129,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@ -1125,7 +1144,7 @@ retry:
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;

View File

@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
q2[0] = (float)x2;
q2[1] = (float)y2;
if (equal(q0,q1) || equal(q1,q2)) {
x0 = (int)verts[i-1].x;
y0 = (int)verts[i-1].y;
x0 = (int)verts[i-1].x; //-V1048
y0 = (int)verts[i-1].y; //-V1048
x1 = (int)verts[i ].x;
y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {

View File

@ -6,7 +6,9 @@
#ifndef IMGUI_DISABLE
// Usage:
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to enable support for imgui_freetype in imgui.
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support
// for imgui_freetype in imgui. It is equivalent to selecting the default loader with:
// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())
// Optional support for OpenType SVG fonts:
// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.
@ -14,46 +16,70 @@
// Forward declarations
struct ImFontAtlas;
struct ImFontBuilderIO;
struct ImFontLoader;
// Hinting greatly impacts visuals (and glyph sizes).
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
enum ImGuiFreeTypeBuilderFlags
// You can set those flags globally in ImFontAtlas::FontLoaderFlags
// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags
typedef unsigned int ImGuiFreeTypeLoaderFlags;
enum ImGuiFreeTypeLoaderFlags_
{
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs
ImGuiFreeTypeBuilderFlags_SubPixel = 1 << 10 // Atlas was generated with sub-pixel rendering enabled
ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs
// IMHEX PATCH BEGIN
ImGuiFreeTypeLoaderFlags_SubPixel = 1 << 10 // Atlas was generated with sub-pixel rendering enabled
// IMHEX PATCH END
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting,
ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint,
ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint,
ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting,
ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting,
ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold,
ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique,
ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome,
ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor,
ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap,
#endif
};
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_;
#endif
namespace ImGuiFreeType
{
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
// If you need to dynamically select between multiple builders:
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
// - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())'
// - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontLoader* GetFontLoader();
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
// Obsolete names (will be removed soon)
// Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source)
IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
//IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection.
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
#endif
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.1
// (tables and columns code)
/*
@ -451,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// 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;
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
}
// Push a standardized ID for both child-using and not-child-using tables
@ -1250,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 11] Default context menu
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
@ -1510,6 +1511,7 @@ void ImGui::EndTable()
}
else
{
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0);
}
@ -1951,7 +1953,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1)
{
TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging
if (g.LogEnabled)
@ -2191,6 +2196,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0;
}
// Also see TablePushColumnChannel()
if (table->Flags & ImGuiTableFlags_NoClip)
{
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
@ -2464,10 +2470,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
}
// Also see TableBeginCell()
void ImGui::TablePushColumnChannel(int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid SetCurrentChannel() + PushClipRect()
if (table->Flags & ImGuiTableFlags_NoClip)
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[column_n];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
void ImGui::TablePopColumnChannel()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid PopClipRect() + SetCurrentChannel()
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
@ -3244,7 +3278,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0)
@ -3341,7 +3375,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // 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;
@ -3396,7 +3430,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
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);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx;

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.1
// (widgets code)
/*
@ -339,6 +339,46 @@ void ImGui::TextWrappedV(const char* fmt, va_list args)
PopTextWrapPos();
}
void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextAlignedV(align_x, size_x, fmt, args);
va_end(args);
}
// align_x: 0.0f = left, 0.5f = center, 1.0f = right.
// size_x : 0.0f = shortcut for GetContentRegionAvail().x
// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
const char* text, *text_end;
ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
const ImVec2 text_size = CalcTextSize(text, text_end);
size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x;
ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y);
ImVec2 size(ImMin(size_x, text_size.x), text_size.y);
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x);
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x);
if (align_x > 0.0f && text_size.x < size_x)
pos.x += ImTrunc((size_x - text_size.x) * align_x);
RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size);
const ImVec2 backup_max_pos = window->DC.CursorMaxPos;
ItemSize(size);
ItemAdd(ImRect(pos, pos + size), 0);
window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up.
if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip))
SetTooltip("%.*s", (int)(text_end - text), text);
}
void ImGui::LabelText(const char* label, const char* fmt, ...)
{
va_list args;
@ -494,7 +534,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
// One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior()
// One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()
// with same ID and different MouseButton (see #8030). You can fix it by:
// (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
// or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
@ -508,6 +548,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
if (flags & ImGuiButtonFlags_AllowOverlap)
item_flags |= ImGuiItemFlags_AllowOverlap;
if (item_flags & ImGuiItemFlags_NoFocus)
flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus;
// Default only reacts to left mouse button
if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
@ -531,7 +573,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
bool pressed = false;
bool hovered = ItemHoverable(bb, id, item_flags);
// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
// Special mode for Drag and Drop used by openables (tree nodes, tabs etc.)
// where holding the button pressed for a long time while drag a payload item triggers the button.
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
@ -583,7 +626,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
SetFocusID(id, window);
FocusWindow(window);
}
else
else if (!(flags & ImGuiButtonFlags_NoFocus))
{
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
}
@ -601,7 +644,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
SetFocusID(id, window);
FocusWindow(window);
}
else
else if (!(flags & ImGuiButtonFlags_NoFocus))
{
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
}
@ -867,11 +910,12 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
if (hovered)
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
ImU32 cross_col = GetColorU32(ImGuiCol_Text);
ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
const ImU32 cross_col = GetColorU32(ImGuiCol_Text);
const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
const float cross_thickness = 1.0f; // FIXME-DPI
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness);
return pressed;
}
@ -1068,9 +1112,9 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@ -1088,28 +1132,28 @@ void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, c
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize);
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
{
ImageWithBg(user_texture_id, image_size, uv0, uv1);
ImageWithBg(tex_ref, image_size, uv0, uv1);
}
// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiContext& g = *GImGui;
PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f
PushStyleColor(ImGuiCol_Border, border_col);
ImageWithBg(user_texture_id, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
PopStyleColor();
PopStyleVar();
}
#endif
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@ -1131,21 +1175,21 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2&
RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?
bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
bool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -1483,8 +1527,8 @@ bool ImGui::TextLink(const char* label)
ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);
}
float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode.
float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode // FIXME-DPI
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
RenderText(bb.Min, label, label_end);
@ -1494,14 +1538,14 @@ bool ImGui::TextLink(const char* label)
return pressed;
}
void ImGui::TextLinkOpenURL(const char* label, const char* url)
bool ImGui::TextLinkOpenURL(const char* label, const char* url)
{
ImGuiContext& g = *GImGui;
if (url == NULL)
url = label;
if (TextLink(label))
if (g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
bool pressed = TextLink(label);
if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label
if (BeginPopupContextItem())
{
@ -1509,6 +1553,7 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url)
SetClipboardText(url);
EndPopup();
}
return pressed;
}
//-------------------------------------------------------------------------
@ -1695,7 +1740,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
if (g.LogEnabled)
LogSetNextTextDecoration("---", NULL);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
}
else
{
@ -3896,7 +3941,7 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
}
// This is only used in the path where the multiline widget is inactivate.
// This is only used in the path where the multiline widget is inactive.
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
{
int line_count = 0;
@ -3920,9 +3965,10 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
{
ImGuiContext& g = *ctx;
ImFont* font = g.Font;
//ImFont* font = g.Font;
ImFontBaked* baked = g.FontBaked;
const float line_height = g.FontSize;
const float scale = line_height / font->FontSize;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@ -3948,8 +3994,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
if (c == '\r')
continue;
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
line_width += char_width;
line_width += baked->GetCharAdvance((ImWchar)c) * scale;
}
if (text_size.x < line_width)
@ -3976,7 +4021,7 @@ namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }
static char STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
@ -4244,23 +4289,24 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
if (new_text == new_text_end)
return;
ImGuiContext& g = *Ctx;
ImGuiInputTextState* obj = &g.InputTextState;
IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID);
// Grow internal buffer if needed
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
if (new_text_len + BufTextLen >= BufSize)
if (new_text_len + BufTextLen + 1 > obj->TextA.Size && (Flags & ImGuiInputTextFlags_ReadOnly) == 0)
{
if (!is_resizable)
return;
ImGuiContext& g = *Ctx;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
IM_ASSERT(Buf == edit_state->TextA.Data);
IM_ASSERT(Buf == obj->TextA.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
edit_state->TextA.resize(new_buf_size + 1);
edit_state->TextSrc = edit_state->TextA.Data;
Buf = edit_state->TextA.Data;
BufSize = edit_state->BufCapacity = new_buf_size;
obj->TextA.resize(new_buf_size + 1);
obj->TextSrc = obj->TextA.Data;
Buf = obj->TextA.Data;
BufSize = obj->BufCapacity = new_buf_size;
}
if (BufTextLen != pos)
@ -4278,18 +4324,29 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
void ImGui::PushPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFont* in_font = g.Font;
ImFont* out_font = &g.InputTextPasswordFont;
ImFontGlyph* glyph = in_font->FindGlyph('*');
out_font->FontSize = in_font->FontSize;
out_font->Scale = in_font->Scale;
out_font->Ascent = in_font->Ascent;
out_font->Descent = in_font->Descent;
out_font->ContainerAtlas = in_font->ContainerAtlas;
out_font->FallbackGlyph = glyph;
out_font->FallbackAdvanceX = glyph->AdvanceX;
IM_ASSERT(out_font->Glyphs.Size == 0 && out_font->IndexAdvanceX.Size == 0 && out_font->IndexLookup.Size == 0);
PushFont(out_font);
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
ImFontGlyph* glyph = g.FontBaked->FindGlyph('*');
g.InputTextPasswordFontBackupFlags = g.Font->Flags;
backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex;
backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX;
backup->IndexLookup.swap(g.FontBaked->IndexLookup);
backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX);
g.Font->Flags |= ImFontFlags_NoLoadGlyphs;
g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph);
g.FontBaked->FallbackAdvanceX = glyph->AdvanceX;
}
void ImGui::PopPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
g.Font->Flags = g.InputTextPasswordFontBackupFlags;
g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex;
g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX;
g.FontBaked->IndexLookup.swap(backup->IndexLookup);
g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX);
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
}
// Return false to discard a character.
@ -4653,7 +4710,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id)
{
// Declare some inputs, the other are registered and polled via Shortcut() routing system.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.
const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
for (ImGuiKey key : always_owned_keys)
SetKeyOwner(key, id);
@ -5166,8 +5223,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Otherwise request text input ahead for next frame.
if (g.ActiveId == id && clear_active_id)
ClearActiveID();
else if (g.ActiveId == id)
g.WantTextInputNextFrame = 1;
// Render frame
if (!is_multiline)
@ -5193,7 +5248,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (new_is_displaying_hint != is_displaying_hint)
{
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
is_displaying_hint = new_is_displaying_hint;
if (is_password && !is_displaying_hint)
PushPasswordFont();
@ -5318,7 +5373,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else
{
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
@ -5345,15 +5400,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
if (!is_readonly)
// This is required for some backends (SDL3) to start emitting character/text inputs.
// As per #6341, make sure we don't set that on the deactivating frame.
if (!is_readonly && g.ActiveId == id)
{
g.PlatformImeData.WantVisible = true;
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
g.PlatformImeData.InputLineHeight = g.FontSize;
g.PlatformImeViewport = window->Viewport->ID;
ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data->WantVisible = true;
ime_data->WantTextInput = true;
ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
ime_data->InputLineHeight = g.FontSize;
ime_data->ViewportId = window->Viewport->ID;
}
}
}
@ -5380,7 +5439,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
if (is_multiline)
{
@ -5432,7 +5491,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID);
Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("BufCapacityA: %d", state->BufCapacity);
Text("BufCapacity: %d", state->BufCapacity);
Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
@ -6214,7 +6273,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if (g.Style.FrameBorderSize > 0.0f)
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border // FIXME-DPI
}
// Drag and Drop Source
@ -6396,6 +6455,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
// - TreeNodeV()
// - TreeNodeEx()
// - TreeNodeExV()
// - TreeNodeStoreStackData() [Internal]
// - TreeNodeBehavior() [Internal]
// - TreePush()
// - TreePop()
@ -6554,18 +6614,26 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
// Store ImGuiTreeNodeStackData for just submitted node.
// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1);
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back();
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
tree_node_data->ID = g.LastItemData.ID;
tree_node_data->TreeFlags = flags;
tree_node_data->ItemFlags = g.LastItemData.ItemFlags;
tree_node_data->NavRect = g.LastItemData.NavRect;
// Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees.
const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0;
tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX;
tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1;
tree_node_data->DrawLinesToNodesY2 = -FLT_MAX;
window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes)
window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth);
}
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop.
@ -6635,14 +6703,18 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled:
// Store data for the current depth to allow returning to this node from any child item.
// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle.
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle.
bool store_tree_node_stack_data = false;
if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0)
flags |= g.Style.TreeLinesFlags;
const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f);
if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
{
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive)
store_tree_node_stack_data = draw_tree_lines;
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive)
if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
store_tree_node_stack_data = true;
}
@ -6650,8 +6722,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (!is_visible)
{
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1))))
{
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway.
if (frame_bb.Min.y >= window->ClipRect.Max.y)
window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done
}
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
@ -6697,6 +6776,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
else
button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
if (flags & ImGuiTreeNodeFlags_NoNavFocus)
button_flags |= ImGuiButtonFlags_NoNavFocus;
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
const bool was_selected = selected;
@ -6783,6 +6864,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@ -6803,6 +6886,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
}
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@ -6811,8 +6896,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
LogSetNextTextDecoration(">", NULL);
}
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (draw_tree_lines)
TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f));
// Label
if (display_frame)
@ -6824,8 +6909,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
TablePopBackgroundChannel();
}
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice
@ -6833,6 +6918,64 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
return is_open;
}
// Draw horizontal line from our parent node
// This is only called for visible child nodes so we are not too fussy anymore about performances
void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0)
return;
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
float x1 = ImTrunc(parent_data->DrawLinesX1);
float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x);
float y = ImTrunc(target_pos.y);
float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f;
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding);
if (x1 >= x2)
return;
if (rounding > 0.0f)
{
x1 += 0.5f + rounding;
window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);
if (x1 < x2)
window->DrawList->PathLineTo(ImVec2(x2, y));
window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);
}
else
{
window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
}
}
// Draw vertical line of the hierarchy
void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y);
float y2 = data->DrawLinesToNodesY2;
if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull)
{
float y2_full = window->DC.CursorPos.y;
if (g.CurrentTable)
y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full);
y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f);
if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y
y2 = y2_full;
}
y2 = ImMin(y2, window->ClipRect.Max.y);
if (y2 <= y1)
return;
float x = ImTrunc(data->DrawLinesX1);
if (data->DrawLinesTableColumn != -1)
TablePushColumnChannel(data->DrawLinesTableColumn);
window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
if (data->DrawLinesTableColumn != -1)
TablePopColumnChannel();
}
void ImGui::TreePush(const char* str_id)
{
ImGuiWindow* window = GetCurrentWindow();
@ -6867,18 +7010,23 @@ void ImGui::TreePop()
window->DC.TreeDepth--;
ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)
{
ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back();
const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
IM_ASSERT(data->ID == window->IDStack.back());
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere)
{
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled)
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled)
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent)
if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data);
}
// Draw hierarchy lines
if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y)
TreeNodeDrawLineToTreePop(data);
g.TreeNodeStack.pop_back();
window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;
window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask;
}
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
@ -7493,7 +7641,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
ImRect box_select_r = bs->BoxSelectRectCurr;
box_select_r.ClipWith(scope_rect);
window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
// Scroll
const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
@ -7697,7 +7845,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
if (ms->IsFocused)
{
// We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure)
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)
{
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId.
storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;
@ -8783,7 +8931,6 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
}
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
// Create window
SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set.
PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0));
@ -10505,13 +10652,12 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
#endif
// Render text label (with clipping + alpha gradient) + unsaved marker
ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
// Return clipped state ignoring the close button
if (out_text_clipped)
{
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x;
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;
//draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
}
@ -10557,15 +10703,22 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
// This is all rather complicated
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
// FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;
float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;
if (close_button_visible || unsaved_marker_visible)
{
text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f);
text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;
ellipsis_max_x = text_pixel_clip_bb.Max.x;
const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;
if (visible_without_hover)
{
text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;
ellipsis_max_x -= button_sz * 0.90f;
}
else
{
text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;
}
}
LogSetNextTextDecoration("/", "\\");
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
#if 0
if (!is_contents_visible)

File diff suppressed because it is too large Load Diff

View File

@ -354,7 +354,7 @@ void ImDrawListGrowChannels(ImDrawList* draw_list, const int num_channels)
{
ImDrawCmd draw_cmd;
draw_cmd.ClipRect = draw_list->_ClipRectStack.back();
draw_cmd.TextureId = draw_list->_TextureIdStack.back();
draw_cmd.TexRef = draw_list->_TextureStack.back();
channel._CmdBuffer.push_back(draw_cmd);
}
}

View File

@ -912,7 +912,11 @@ IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int
IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0);
// Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down).
IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0);
#ifdef IMGUI_HAS_TEXTURES
IMPLOT_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), ImPlotImageFlags flags = 0);
#else
IMPLOT_API void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0);
#endif
// Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...).
IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0);

View File

@ -339,11 +339,18 @@ void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *te
if (!text_end)
text_end = text_begin + strlen(text_begin);
ImGuiContext& g = *GImGui;
#ifdef IMGUI_HAS_TEXTURES
ImFontBaked* font = g.Font->GetFontBaked(g.FontSize);
const float scale = g.FontSize / font->Size;
#else
ImFont* font = g.Font;
const float scale = g.FontSize / font->FontSize;
#endif
// Align to be pixel perfect
pos.x = ImFloor(pos.x);
pos.y = ImFloor(pos.y);
const float scale = g.FontSize / font->FontSize;
const char* s = text_begin;
int chars_exp = (int)(text_end - s);
int chars_rnd = 0;
@ -3064,7 +3071,11 @@ void EndPlot() {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID);
if (IO.MouseWheel != 0.0f) {
ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
#if IMGUI_VERSION_NUM < 19172
float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
#else
float font_size = ImGui::GetCurrentWindow()->FontRefSize;
#endif
float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
legend.Scroll.x += scroll_step * IO.MouseWheel;
legend.Scroll.y += scroll_step * IO.MouseWheel;
@ -3583,7 +3594,11 @@ void EndSubplots() {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID);
if (IO.MouseWheel != 0.0f) {
ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
#if IMGUI_VERSION_NUM < 19172
float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
#else
float font_size = ImGui::GetCurrentWindow()->FontRefSize;
#endif
float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
legend.Scroll.x += scroll_step * IO.MouseWheel;
legend.Scroll.y += scroll_step * IO.MouseWheel;

View File

@ -867,7 +867,14 @@ void Demo_Images() {
ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f");
ImGui::ColorEdit4("Tint",&tint.x);
if (ImPlot::BeginPlot("##image")) {
ImPlot::PlotImage("my image",ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint);
#ifdef IMGUI_HAS_TEXTURES
// We use the font atlas ImTextureRef for this demo, but in your real code when you submit
// an image that you have loaded yourself, you would normally have a ImTextureID which works
// just as well (as ImTextureRef can be constructed from ImTextureID).
ImPlot::PlotImage("my image", ImGui::GetIO().Fonts->TexRef, bmin, bmax, uv0, uv1, tint);
#else
ImPlot::PlotImage("my image", ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint);
#endif
ImPlot::EndPlot();
}
}

View File

@ -2750,15 +2750,19 @@ void PlotDigitalG(const char* label_id, ImPlotGetter getter_func, void* data, in
// [SECTION] PlotImage
//-----------------------------------------------------------------------------
void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bmin, const ImPlotPoint& bmax, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlotImageFlags) {
if (BeginItemEx(label_id, FitterRect(bmin,bmax))) {
#ifdef IMGUI_HAS_TEXTURES
void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlotPoint& bmin, const ImPlotPoint& bmax, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlotImageFlags) {
#else
void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlotPoint& bmin, const ImPlotPoint& bmax, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlotImageFlags) {
#endif
if (BeginItemEx(label_id, FitterRect(bmin,bmax))) {
ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col);
GetCurrentItem()->Color = tint_col32;
ImDrawList& draw_list = *GetPlotDrawList();
ImVec2 p1 = PlotToPixels(bmin.x, bmax.y,IMPLOT_AUTO,IMPLOT_AUTO);
ImVec2 p2 = PlotToPixels(bmax.x, bmin.y,IMPLOT_AUTO,IMPLOT_AUTO);
PushPlotClipRect();
draw_list.AddImage(user_texture_id, p1, p2, uv0, uv1, tint_col32);
draw_list.AddImage(tex_ref, p1, p2, uv0, uv1, tint_col32);
PopPlotClipRect();
EndItem();
}

View File

@ -1,5 +1,5 @@
//--------------------------------------------------
// ImPlot3D v0.1
// ImPlot3D v0.3 WIP
// implot3d.h
// Date: 2024-11-16
// Author: Breno Cunha Queiroz (brenocq.com)
@ -45,7 +45,8 @@
#define IMPLOT3D_API
#endif
#define IMPLOT3D_VERSION "0.1" // ImPlot3D version
#define IMPLOT3D_VERSION "0.3 WIP" // ImPlot3D version
#define IMPLOT3D_VERSION_NUM 300 // Integer encoded version
#define IMPLOT3D_AUTO -1 // Deduce variable automatically
#define IMPLOT3D_AUTO_COL ImVec4(0, 0, 0, -1) // Deduce color automatically
#define IMPLOT3D_TMP template <typename T> IMPLOT3D_API
@ -83,9 +84,16 @@ typedef int ImPlot3DTriangleFlags; // -> ImPlot3DTriangleFlags_ // Flags: Triang
typedef int ImPlot3DQuadFlags; // -> ImPlot3DQuadFlags_ // Flags: QuadFplot flags
typedef int ImPlot3DSurfaceFlags; // -> ImPlot3DSurfaceFlags_ // Flags: Surface plot flags
typedef int ImPlot3DMeshFlags; // -> ImPlot3DMeshFlags_ // Flags: Mesh plot flags
typedef int ImPlot3DImageFlags; // -> ImPlot3DImageFlags_ // Flags: Image plot flags
typedef int ImPlot3DLegendFlags; // -> ImPlot3DLegendFlags_ // Flags: Legend flags
typedef int ImPlot3DAxisFlags; // -> ImPlot3DAxisFlags_ // Flags: Axis flags
// Fallback for ImGui versions before v1.92: define ImTextureRef as ImTextureID
// You can `#define IMPLOT3D_NO_IMTEXTUREREF` to avoid this fallback
#if !defined(IMGUI_HAS_TEXTURES) && !defined(IMPLOT3D_NO_IMTEXTUREREF)
typedef ImTextureID ImTextureRef;
#endif
//-----------------------------------------------------------------------------
// [SECTION] Flags & Enumerations
//-----------------------------------------------------------------------------
@ -195,6 +203,9 @@ enum ImPlot3DTriangleFlags_ {
ImPlot3DTriangleFlags_None = 0, // Default
ImPlot3DTriangleFlags_NoLegend = ImPlot3DItemFlags_NoLegend,
ImPlot3DTriangleFlags_NoFit = ImPlot3DItemFlags_NoFit,
ImPlot3DTriangleFlags_NoLines = 1 << 10, // No lines will be rendered
ImPlot3DTriangleFlags_NoFill = 1 << 11, // No fill will be rendered
ImPlot3DTriangleFlags_NoMarkers = 1 << 12, // No markers will be rendered
};
// Flags for PlotQuad
@ -202,6 +213,9 @@ enum ImPlot3DQuadFlags_ {
ImPlot3DQuadFlags_None = 0, // Default
ImPlot3DQuadFlags_NoLegend = ImPlot3DItemFlags_NoLegend,
ImPlot3DQuadFlags_NoFit = ImPlot3DItemFlags_NoFit,
ImPlot3DQuadFlags_NoLines = 1 << 10, // No lines will be rendered
ImPlot3DQuadFlags_NoFill = 1 << 11, // No fill will be rendered
ImPlot3DQuadFlags_NoMarkers = 1 << 12, // No markers will be rendered
};
// Flags for PlotSurface
@ -209,6 +223,9 @@ enum ImPlot3DSurfaceFlags_ {
ImPlot3DSurfaceFlags_None = 0, // Default
ImPlot3DSurfaceFlags_NoLegend = ImPlot3DItemFlags_NoLegend,
ImPlot3DSurfaceFlags_NoFit = ImPlot3DItemFlags_NoFit,
ImPlot3DSurfaceFlags_NoLines = 1 << 10, // No lines will be rendered
ImPlot3DSurfaceFlags_NoFill = 1 << 11, // No fill will be rendered
ImPlot3DSurfaceFlags_NoMarkers = 1 << 12, // No markers will be rendered
};
// Flags for PlotMesh
@ -216,6 +233,16 @@ enum ImPlot3DMeshFlags_ {
ImPlot3DMeshFlags_None = 0, // Default
ImPlot3DMeshFlags_NoLegend = ImPlot3DItemFlags_NoLegend,
ImPlot3DMeshFlags_NoFit = ImPlot3DItemFlags_NoFit,
ImPlot3DMeshFlags_NoLines = 1 << 10, // No lines will be rendered
ImPlot3DMeshFlags_NoFill = 1 << 11, // No fill will be rendered
ImPlot3DMeshFlags_NoMarkers = 1 << 12, // No markers will be rendered
};
// Flags for PlotImage
enum ImPlot3DImageFlags_ {
ImPlot3DImageFlags_None = 0, // Default
ImPlot3DImageFlags_NoLegend = ImPlot3DItemFlags_NoLegend,
ImPlot3DImageFlags_NoFit = ImPlot3DItemFlags_NoFit,
};
// Flags for legends
@ -250,6 +277,7 @@ enum ImPlot3DAxisFlags_ {
ImPlot3DAxisFlags_LockMax = 1 << 5, // The axis maximum value will be locked when panning/zooming
ImPlot3DAxisFlags_AutoFit = 1 << 6, // Axis will be auto-fitting to data extents
ImPlot3DAxisFlags_Invert = 1 << 7, // The axis will be inverted
ImPlot3DAxisFlags_PanStretch = 1 << 8, // Panning in a locked or constrained state will cause the axis to stretch if possible
ImPlot3DAxisFlags_Lock = ImPlot3DAxisFlags_LockMin | ImPlot3DAxisFlags_LockMax,
ImPlot3DAxisFlags_NoDecorations = ImPlot3DAxisFlags_NoLabel | ImPlot3DAxisFlags_NoGridLines | ImPlot3DAxisFlags_NoTickLabels,
};
@ -362,21 +390,44 @@ IMPLOT3D_API void SetupAxis(ImAxis3D axis, const char* label = nullptr, ImPlot3D
IMPLOT3D_API void SetupAxisLimits(ImAxis3D axis, double v_min, double v_max, ImPlot3DCond cond = ImPlot3DCond_Once);
IMPLOT3D_API void SetupAxisFormat(ImAxis3D idx, ImPlot3DFormatter formatter, void* data = nullptr);
IMPLOT3D_API void SetupAxisFormat(ImAxis3D axis, ImPlot3DFormatter formatter, void* data = nullptr);
// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true
IMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, const double* values, int n_ticks, const char* const labels[] = nullptr, bool keep_default = false);
// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true
IMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, double v_min, double v_max, int n_ticks, const char* const labels[] = nullptr, bool keep_default = false);
IMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, double v_min, double v_max, int n_ticks, const char* const labels[] = nullptr,
bool keep_default = false);
// Sets an axis' limits constraints
IMPLOT3D_API void SetupAxisLimitsConstraints(ImAxis3D axis, double v_min, double v_max);
// Sets an axis' zoom constraints
IMPLOT3D_API void SetupAxisZoomConstraints(ImAxis3D axis, double z_min, double z_max);
// Sets the label and/or flags for primary X/Y/Z axes (shorthand for three calls to SetupAxis)
IMPLOT3D_API void SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags = 0, ImPlot3DAxisFlags y_flags = 0, ImPlot3DAxisFlags z_flags = 0);
IMPLOT3D_API void SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags = 0,
ImPlot3DAxisFlags y_flags = 0, ImPlot3DAxisFlags z_flags = 0);
// Sets the X/Y/Z axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits)
IMPLOT3D_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max, ImPlot3DCond cond = ImPlot3DCond_Once);
// Sets the X/Y/Z axes range limits. If ImPlot3DCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits)
IMPLOT3D_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max,
ImPlot3DCond cond = ImPlot3DCond_Once);
// Sets the plot box X/Y/Z scale. A scale of 1.0 is the default. Values greater than 1.0 enlarge the plot, while values between 0.0 and 1.0 shrink it.
// Sets the plot box rotation given the elevation and azimuth angles in degrees. If ImPlot3DCond_Always is used, the rotation will be locked
IMPLOT3D_API void SetupBoxRotation(float elevation, float azimuth, bool animate = false, ImPlot3DCond cond = ImPlot3DCond_Once);
// Sets the plot box rotation given a quaternion. If ImPlot3DCond_Always is used, the rotation will be locked
IMPLOT3D_API void SetupBoxRotation(ImPlot3DQuat rotation, bool animate = false, ImPlot3DCond cond = ImPlot3DCond_Once);
// Sets the plot box initial rotation given the elevation and azimuth angles in degrees. The initial rotation is the rotation the plot goes back to
// when a left mouse button double click happens
IMPLOT3D_API void SetupBoxInitialRotation(float elevation, float azimuth);
// Sets the plot box initial rotation given a quaternion. The initial rotation is the rotation the plot goes back to when a left mouse button double
// click happens
IMPLOT3D_API void SetupBoxInitialRotation(ImPlot3DQuat rotation);
// Sets the plot box X/Y/Z scale. A scale of 1.0 is the default. Values greater than 1.0 enlarge the plot, while values between 0.0 and 1.0 shrink it
IMPLOT3D_API void SetupBoxScale(float x, float y, float z);
IMPLOT3D_API void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags flags = 0);
@ -385,18 +436,46 @@ IMPLOT3D_API void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags fla
// [SECTION] Plot Items
//-----------------------------------------------------------------------------
IMPLOT3D_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags = 0, int offset = 0, int stride = sizeof(T));
IMPLOT3D_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags = 0, int offset = 0,
int stride = sizeof(T));
IMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags = 0, int offset = 0, int stride = sizeof(T));
IMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags = 0, int offset = 0,
int stride = sizeof(T));
IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags = 0, int offset = 0, int stride = sizeof(T));
IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags = 0,
int offset = 0, int stride = sizeof(T));
IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags = 0, int offset = 0, int stride = sizeof(T));
IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags = 0, int offset = 0,
int stride = sizeof(T));
// Plot the surface defined by a grid of vertices. The grid is defined by the x and y arrays, and the z array contains the height of each vertex. A total of x_count * y_count vertices are expected for each array. Leave #scale_min and #scale_max both at 0 for automatic color scaling, or set them to a predefined range.
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0, double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T));
// Plot the surface defined by a grid of vertices. The grid is defined by the x and y arrays, and the z array contains the height of each vertex. A
// total of x_count * y_count vertices are expected for each array. Leave #scale_min and #scale_max both at 0 for automatic color scaling, or set them
// to a predefined range
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0,
double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T));
IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags = 0);
IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count,
ImPlot3DMeshFlags flags = 0);
// Plots a rectangular image in 3D defined by its center and two direction vectors (axes).
// #center is the center of the rectangle in plot coordinates.
// #axis_u and #axis_v define the local axes and half-extents of the rectangle in 3D space.
// The rectangle is formed by moving from the center along ±axis_u and ±axis_v.
// #uv0 and #uv1 define the texture mapping.
// #tint_col can be used to tint the image.
IMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& center, const ImPlot3DPoint& axis_u,
const ImPlot3DPoint& axis_v, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1),
const ImVec4& tint_col = ImVec4(1, 1, 1, 1), ImPlot3DImageFlags flags = 0);
// Plots an image using four arbitrary 3D points that define a quad in space.
// Each corner (p0 to p3) corresponds to a corner in the image, and #uv0 to #uv3 are the texture coordinates for each.
// This overload allows full control over orientation, shape, and distortion.
// Note: The quad is internally split into two triangles, so non-rectangular quads may produce rendering artifacts
// since distortion is interpolated per triangle rather than over the full quad.
IMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, const ImPlot3DPoint& p2,
const ImPlot3DPoint& p3, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 0),
const ImVec2& uv2 = ImVec2(1, 1), const ImVec2& uv3 = ImVec2(0, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1),
ImPlot3DImageFlags flags = 0);
// Plots a centered text label at point x,y,z. It is possible to set the text angle in radians and offset in pixels
IMPLOT3D_API void PlotText(const char* text, float x, float y, float z, float angle = 0.0f, const ImVec2& pix_offset = ImVec2(0, 0));
@ -430,6 +509,7 @@ IMPLOT3D_API ImDrawList* GetPlotDrawList();
// Get current style
IMPLOT3D_API ImPlot3DStyle& GetStyle();
IMPLOT3D_API void SetStyle(const ImPlot3DStyle& style);
// Set color styles
IMPLOT3D_API void StyleColorsAuto(ImPlot3DStyle* dst = nullptr); // Set colors with ImGui style
@ -457,7 +537,8 @@ IMPLOT3D_API void SetNextLineStyle(const ImVec4& col = IMPLOT3D_AUTO_COL, float
// Set the fill color for the next item only
IMPLOT3D_API void SetNextFillStyle(const ImVec4& col = IMPLOT3D_AUTO_COL, float alpha_mod = IMPLOT3D_AUTO);
// Set the marker style for the next item only
IMPLOT3D_API void SetNextMarkerStyle(ImPlot3DMarker marker = IMPLOT3D_AUTO, float size = IMPLOT3D_AUTO, const ImVec4& fill = IMPLOT3D_AUTO_COL, float weight = IMPLOT3D_AUTO, const ImVec4& outline = IMPLOT3D_AUTO_COL);
IMPLOT3D_API void SetNextMarkerStyle(ImPlot3DMarker marker = IMPLOT3D_AUTO, float size = IMPLOT3D_AUTO, const ImVec4& fill = IMPLOT3D_AUTO_COL,
float weight = IMPLOT3D_AUTO, const ImVec4& outline = IMPLOT3D_AUTO_COL);
// Get color
IMPLOT3D_API ImVec4 GetStyleColorVec4(ImPlot3DCol idx);
@ -492,9 +573,11 @@ IMPLOT3D_API const char* GetColormapName(ImPlot3DColormap cmap);
// Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid
IMPLOT3D_API ImPlot3DColormap GetColormapIndex(const char* name);
// Temporarily switch to one of the built-in (i.e. ImPlot3DColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap!
// Temporarily switch to one of the built-in (i.e. ImPlot3DColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to
// call PopColormap!
IMPLOT3D_API void PushColormap(ImPlot3DColormap cmap);
// Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc. or a string you provided to AddColormap. Don't forget to call PopColormap!
// Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc. or a string you provided to AddColormap. Don't forget to
// call PopColormap!
IMPLOT3D_API void PushColormap(const char* name);
// Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count
IMPLOT3D_API void PopColormap(int count = 1);
@ -517,10 +600,15 @@ IMPLOT3D_API ImVec4 SampleColormap(float t, ImPlot3DColormap cmap = IMPLOT3D_AUT
// Shows the ImPlot3D demo window
IMPLOT3D_API void ShowDemoWindow(bool* p_open = nullptr);
// Shows all ImPlot3D demos, without enclosing window
IMPLOT3D_API void ShowAllDemos();
// Shows ImPlot3D style editor block (not a window)
IMPLOT3D_API void ShowStyleEditor(ImPlot3DStyle* ref = nullptr);
// Shows ImPlot3D metrics/debug information window.
IMPLOT3D_API void ShowMetricsWindow(bool* p_popen = nullptr);
} // namespace ImPlot3D
//-----------------------------------------------------------------------------
@ -591,7 +679,8 @@ struct ImPlot3DPoint {
IMPLOT3D_API bool IsNaN() const;
#ifdef IMPLOT3D_POINT_CLASS_EXTRA
IMPLOT3D_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImPlot3DPoint
IMPLOT3D_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math
// types and ImPlot3DPoint
#endif
};
@ -669,6 +758,9 @@ struct ImPlot3DQuat {
// Set quaternion from two vectors
IMPLOT3D_API static ImPlot3DQuat FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1);
// Set quaternion given elevation and azimuth angles in radians
IMPLOT3D_API static ImPlot3DQuat FromElAz(float elevation, float azimuth);
// Get quaternion length
IMPLOT3D_API float Length() const;
@ -701,7 +793,8 @@ struct ImPlot3DQuat {
IMPLOT3D_API float Dot(const ImPlot3DQuat& rhs) const;
#ifdef IMPLOT3D_QUAT_CLASS_EXTRA
IMPLOT3D_QUAT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImPlot3DQuat
IMPLOT3D_QUAT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math
// types and ImPlot3DQuat
#endif
};
@ -727,10 +820,13 @@ struct ImPlot3DStyle {
ImVec2 LegendSpacing; // Spacing between legend entries
// Colors
ImVec4 Colors[ImPlot3DCol_COUNT];
inline ImVec4 GetColor(ImPlot3DCol idx) const { return Colors[idx]; }
inline void SetColor(ImPlot3DCol idx, const ImVec4& col) { Colors[idx] = col; }
// Colormap
ImPlot3DColormap Colormap; // The current colormap. Set this to either an ImPlot3DColormap_ enum or an index returned by AddColormap
// Constructor
IMPLOT3D_API ImPlot3DStyle();
ImPlot3DStyle(const ImPlot3DStyle& other) = default;
};
//-----------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
//--------------------------------------------------
// ImPlot3D v0.1
// ImPlot3D v0.3 WIP
// implot3d_internal.h
// Date: 2024-11-17
// Author: Breno Cunha Queiroz (brenocq.com)
@ -53,13 +53,10 @@ namespace ImPlot3D {
// Computes the common (base-10) logarithm
static inline float ImLog10(float x) { return log10f(x); }
// Returns true if flag is set
template <typename TSet, typename TFlag>
static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; }
template <typename TSet, typename TFlag> static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; }
// Flips a flag in a flagset
template <typename TSet, typename TFlag>
static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; }
template <typename T>
static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); }
template <typename TSet, typename TFlag> static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; }
template <typename T> static inline T ImRemap01(T x, T x0, T x1) { return (x1 - x0) ? ((x - x0) / (x1 - x0)) : 0; }
// Returns true if val is NAN
static inline bool ImNan(float val) { return isnan(val); }
// Returns true if val is NAN or INFINITY
@ -67,14 +64,13 @@ static inline bool ImNanOrInf(float val) { return !(val >= -FLT_MAX && val <= FL
// Turns NANs to 0s
static inline double ImConstrainNan(float val) { return ImNan(val) ? 0 : val; }
// Turns infinity to floating point maximums
static inline double ImConstrainInf(double val) { return val >= FLT_MAX ? FLT_MAX : val <= -FLT_MAX ? -FLT_MAX
: val; }
static inline double ImConstrainInf(double val) { return val >= FLT_MAX ? FLT_MAX : val <= -FLT_MAX ? -FLT_MAX : val; }
// True if two numbers are approximately equal using units in the last place.
static inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1 - v2) < FLT_EPSILON * ImAbs(v1 + v2) * ulp || ImAbs(v1 - v2) < FLT_MIN; }
// Set alpha channel of 32-bit color from float in range [0.0 1.0]
static inline ImU32 ImAlphaU32(ImU32 col, float alpha) {
return col & ~((ImU32)((1.0f - alpha) * 255) << IM_COL32_A_SHIFT);
static inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) {
return ImAbs(v1 - v2) < FLT_EPSILON * ImAbs(v1 + v2) * ulp || ImAbs(v1 - v2) < FLT_MIN;
}
// Set alpha channel of 32-bit color from float in range [0.0 1.0]
static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f - alpha) * 255) << IM_COL32_A_SHIFT); }
// Mix color a and b by factor s in [0 256]
static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) {
#ifdef IMPLOT3D_MIX64
@ -98,8 +94,7 @@ static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) {
}
// Fills a buffer with n samples linear interpolated from vmin to vmax
template <typename T>
void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {
template <typename T> void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {
buffer.resize(n);
T step = (vmax - vmin) / (n - 1);
for (int i = 0; i < n; ++i) {
@ -126,30 +121,49 @@ typedef void (*ImPlot3DLocator)(ImPlot3DTicker& ticker, const ImPlot3DRange& ran
//-----------------------------------------------------------------------------
struct ImDrawList3D {
ImVector<ImDrawIdx> IdxBuffer; // Index buffer
ImVector<ImDrawVert> VtxBuffer; // Vertex buffer
ImVector<float> ZBuffer; // Z buffer. Depth value for each triangle
unsigned int _VtxCurrentIdx; // [Internal] current vertex index
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
float* _ZWritePtr; // [Internal] point within ZBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawListFlags _Flags; // [Internal] draw list flags
ImDrawListSharedData* _SharedData; // [Internal] shared draw list data
// [Internal] Define which texture should be used when rendering triangles.
struct ImTextureBufferItem {
ImTextureRef TexRef;
unsigned int VtxIdx;
};
ImVector<ImDrawIdx> IdxBuffer; // Index buffer
ImVector<ImDrawVert> VtxBuffer; // Vertex buffer
ImVector<float> ZBuffer; // Z buffer. Depth value for each triangle
unsigned int _VtxCurrentIdx; // [Internal] current vertex index
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
float* _ZWritePtr; // [Internal] point within ZBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawListFlags _Flags; // [Internal] draw list flags
ImVector<ImTextureBufferItem> _TextureBuffer; // [Internal] buffer for SetTexture/ResetTexture
ImDrawListSharedData* _SharedData; // [Internal] shared draw list data
ImDrawList3D() {
_VtxCurrentIdx = 0;
_VtxWritePtr = nullptr;
_IdxWritePtr = nullptr;
_ZWritePtr = nullptr;
_Flags = ImDrawListFlags_None;
_SharedData = nullptr;
ResetBuffers();
}
void PrimReserve(int idx_count, int vtx_count);
void PrimUnreserve(int idx_count, int vtx_count);
void SetTexture(ImTextureRef tex_ref);
void ResetTexture();
void SortedMoveToImGuiDrawList();
void ResetBuffers() {
IdxBuffer.clear();
VtxBuffer.clear();
ZBuffer.clear();
_VtxCurrentIdx = 0;
_VtxWritePtr = VtxBuffer.Data;
_IdxWritePtr = IdxBuffer.Data;
_ZWritePtr = ZBuffer.Data;
_TextureBuffer.clear();
ResetTexture();
}
constexpr static unsigned int MaxIdx() { return sizeof(ImDrawIdx) == 2 ? 65535 : 4294967295; }
};
@ -345,9 +359,7 @@ struct ImPlot3DItemGroup {
ImPlot3DLegend Legend;
int ColormapIdx;
ImPlot3DItemGroup() {
ColormapIdx = 0;
}
ImPlot3DItemGroup() { ColormapIdx = 0; }
int GetItemCount() const { return ItemPool.GetBufSize(); }
ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); }
@ -388,9 +400,7 @@ struct ImPlot3DTicker {
ImVector<ImPlot3DTick> Ticks;
ImGuiTextBuffer TextBuffer;
ImPlot3DTicker() {
Reset();
}
ImPlot3DTicker() { Reset(); }
ImPlot3DTick& AddTick(double value, bool major, bool show_label, const char* label) {
ImPlot3DTick tick(value, major, show_label);
@ -420,22 +430,16 @@ struct ImPlot3DTicker {
return Ticks.back();
}
const char* GetText(int idx) const {
return TextBuffer.Buf.Data + Ticks[idx].TextOffset;
}
const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; }
const char* GetText(const ImPlot3DTick& tick) const {
return GetText(tick.Idx);
}
const char* GetText(const ImPlot3DTick& tick) const { return GetText(tick.Idx); }
void Reset() {
Ticks.shrink(0);
TextBuffer.Buf.shrink(0);
}
int TickCount() const {
return Ticks.Size;
}
int TickCount() const { return Ticks.Size; }
};
// Holds axis information
@ -454,6 +458,9 @@ struct ImPlot3DAxis {
// Fit data
bool FitThisFrame;
ImPlot3DRange FitExtents;
// Constraints
ImPlot3DRange ConstraintRange;
ImPlot3DRange ConstraintZoom;
// User input
bool Hovered;
bool Held;
@ -472,34 +479,54 @@ struct ImPlot3DAxis {
ShowDefaultTicks = true;
// Fit data
FitThisFrame = true;
FitExtents.Min = HUGE_VAL;
FitExtents.Max = -HUGE_VAL;
FitExtents = ImPlot3DRange(HUGE_VAL, -HUGE_VAL);
// Constraints
ConstraintRange = ImPlot3DRange(-INFINITY, INFINITY);
ConstraintZoom = ImPlot3DRange(FLT_MIN, INFINITY);
// User input
Hovered = false;
Held = false;
}
inline void Reset() {
RangeCond = ImPlot3DCond_None;
// Ticks
Ticker.Reset();
Formatter = nullptr;
FormatterData = nullptr;
Locator = nullptr;
ShowDefaultTicks = true;
FitExtents.Min = HUGE_VAL;
FitExtents.Max = -HUGE_VAL;
Ticker.Reset();
// Fit data
FitExtents = ImPlot3DRange(HUGE_VAL, -HUGE_VAL);
// Constraints
ConstraintRange = ImPlot3DRange(-INFINITY, INFINITY);
ConstraintZoom = ImPlot3DRange(FLT_MIN, INFINITY);
}
inline void SetRange(double v1, double v2) {
Range.Min = (float)ImMin(v1, v2);
Range.Max = (float)ImMax(v1, v2);
Constrain();
}
inline bool SetMin(double _min, bool force = false) {
if (!force && IsLockedMin())
return false;
_min = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_min));
// Constraints
if (_min < ConstraintRange.Min)
_min = ConstraintRange.Min;
double zoom = Range.Max - _min;
if (zoom < ConstraintZoom.Min)
_min = Range.Max - ConstraintZoom.Min;
if (zoom > ConstraintZoom.Max)
_min = Range.Max - ConstraintZoom.Max;
// Ensure min is less than max
if (_min >= Range.Max)
return false;
Range.Min = (float)_min;
return true;
}
@ -508,12 +535,45 @@ struct ImPlot3DAxis {
if (!force && IsLockedMax())
return false;
_max = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_max));
// Constraints
if (_max > ConstraintRange.Max)
_max = ConstraintRange.Max;
double zoom = _max - Range.Min;
if (zoom < ConstraintZoom.Min)
_max = Range.Min + ConstraintZoom.Min;
if (zoom > ConstraintZoom.Max)
_max = Range.Min + ConstraintZoom.Max;
// Ensure max is greater than min
if (_max <= Range.Min)
return false;
Range.Max = (float)_max;
return true;
}
inline void Constrain() {
Range.Min = (float)ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf((double)Range.Min));
Range.Max = (float)ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf((double)Range.Max));
if (Range.Min < ConstraintRange.Min)
Range.Min = ConstraintRange.Min;
if (Range.Max > ConstraintRange.Max)
Range.Max = ConstraintRange.Max;
float zoom = Range.Size();
if (zoom < ConstraintZoom.Min) {
float delta = (ConstraintZoom.Min - zoom) * 0.5f;
Range.Min -= delta;
Range.Max += delta;
}
if (zoom > ConstraintZoom.Max) {
float delta = (zoom - ConstraintZoom.Max) * 0.5f;
Range.Min += delta;
Range.Max -= delta;
}
if (Range.Max <= Range.Min)
Range.Max = Range.Min + FLT_EPSILON;
}
inline bool IsRangeLocked() const { return RangeCond == ImPlot3DCond_Always; }
inline bool IsLockedMin() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMin); }
inline bool IsLockedMax() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMax); }
@ -522,6 +582,19 @@ struct ImPlot3DAxis {
inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); }
inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); }
inline bool IsPanLocked(bool increasing) {
if (ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_PanStretch)) {
return IsInputLocked();
} else {
if (IsLockedMin() || IsLockedMax() || IsAutoFitting())
return false;
if (increasing)
return Range.Max == ConstraintRange.Max;
else
return Range.Min == ConstraintRange.Min;
}
}
inline void SetLabel(const char* label) {
Label.Buf.shrink(0);
if (label && ImGui::FindRenderedTextEnd(label, nullptr) != label)
@ -552,7 +625,9 @@ struct ImPlot3DPlot {
ImRect CanvasRect; // Frame rectangle reduced by padding
ImRect PlotRect; // Bounding rectangle for the actual plot area
// Rotation & axes & box
ImPlot3DQuat Rotation; // Current rotation quaternion
ImPlot3DQuat InitialRotation; // Initial rotation quaternion
ImPlot3DQuat Rotation; // Current rotation quaternion
ImPlot3DCond RotationCond;
ImPlot3DAxis Axes[3]; // X, Y, Z axes
ImPlot3DPoint BoxScale; // Scale factor for plot box X, Y, Z axes
// Animation
@ -568,7 +643,6 @@ struct ImPlot3DPlot {
bool FitThisFrame;
// Items
ImPlot3DItemGroup Items;
ImPlot3DItem* CurrentItem;
// 3D draw list
ImDrawList3D DrawList;
// Misc
@ -579,7 +653,9 @@ struct ImPlot3DPlot {
PreviousFlags = Flags = ImPlot3DFlags_None;
JustCreated = true;
Initialized = false;
InitialRotation = ImPlot3DQuat(-0.513269f, -0.212596f, -0.318184f, 0.76819f);
Rotation = ImPlot3DQuat(0.0f, 0.0f, 0.0f, 1.0f);
RotationCond = ImPlot3DCond_None;
for (int i = 0; i < 3; i++)
Axes[i] = ImPlot3DAxis();
BoxScale = ImPlot3DPoint(1.0f, 1.0f, 1.0f);
@ -590,7 +666,6 @@ struct ImPlot3DPlot {
HeldEdgeIdx = -1;
HeldPlaneIdx = -1;
FitThisFrame = true;
CurrentItem = nullptr;
ContextClick = false;
OpenContextThisFrame = false;
}
@ -602,6 +677,7 @@ struct ImPlot3DPlot {
}
inline bool HasTitle() const { return !Title.empty() && !ImPlot3D::ImHasFlag(Flags, ImPlot3DFlags_NoTitle); }
inline const char* GetTitle() const { return Title.Buf.Data; }
inline bool IsRotationLocked() const { return RotationCond == ImPlot3DCond_Always; }
void ExtendFit(const ImPlot3DPoint& point);
ImPlot3DPoint RangeMin() const;
@ -615,6 +691,7 @@ struct ImPlot3DContext {
ImPool<ImPlot3DPlot> Plots;
ImPlot3DPlot* CurrentPlot;
ImPlot3DItemGroup* CurrentItems;
ImPlot3DItem* CurrentItem;
ImPlot3DNextItemData NextItemData;
ImPlot3DStyle Style;
ImVector<ImGuiColorMod> ColorModifiers;
@ -649,6 +726,12 @@ IMPLOT3D_API bool IsColorAuto(ImPlot3DCol idx);
IMPLOT3D_API ImVec4 GetAutoColor(ImPlot3DCol idx);
IMPLOT3D_API const char* GetStyleColorName(ImPlot3DCol idx);
// Returns white or black text given background color
static inline ImU32 CalcTextColor(const ImVec4& bg) {
return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE;
}
static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); }
// Get styling data for next item (call between BeginItem/EndItem)
IMPLOT3D_API const ImPlot3DNextItemData& GetItemData();
@ -658,6 +741,10 @@ IMPLOT3D_API ImU32 GetColormapColorU32(int idx, ImPlot3DColormap cmap);
// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired
IMPLOT3D_API ImU32 NextColormapColorU32();
// Render a colormap bar
IMPLOT3D_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed,
bool continuous);
//-----------------------------------------------------------------------------
// [SECTION] Item Utils
//-----------------------------------------------------------------------------
@ -668,6 +755,9 @@ IMPLOT3D_API void EndItem();
// Register or get an existing item from the current plot
IMPLOT3D_API ImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, bool* just_created = nullptr);
// Gets the current item from ImPlot3DContext
IMPLOT3D_API ImPlot3DItem* GetCurrentItem();
// Busts the cache for every item for every plot in the current context
IMPLOT3D_API void BustItemCache();
@ -678,7 +768,7 @@ IMPLOT3D_API void AddTextRotated(ImDrawList* draw_list, ImVec2 pos, float angle,
// [SECTION] Plot Utils
//-----------------------------------------------------------------------------
// Gets the current plot from the current ImPlot3DContext
// Gets the current plot from ImPlot3DContext
IMPLOT3D_API ImPlot3DPlot* GetCurrentPlot();
// Busts the cache for every plot in the current context

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
//--------------------------------------------------
// ImPlot3D v0.1
// ImPlot3D v0.3 WIP
// implot3d_demo.cpp
// Date: 2024-11-17
// Author: Breno Cunha Queiroz (brenocq.com)
@ -47,6 +47,8 @@ namespace ImPlot3D {
// [SECTION] Helpers
//-----------------------------------------------------------------------------
#define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag)
static void HelpMarker(const char* desc) {
ImGui::TextDisabled("(?)");
if (ImGui::BeginItemTooltip()) {
@ -196,6 +198,12 @@ void DemoTrianglePlots() {
// Now we have 18 vertices in xs, ys, zs forming the pyramid
// Triangle flags
static ImPlot3DTriangleFlags flags = ImPlot3DTriangleFlags_None;
CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoLines);
CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoFill);
CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoMarkers);
if (ImPlot3D::BeginPlot("Triangle Plots")) {
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -0.5, 1.5);
@ -205,7 +213,7 @@ void DemoTrianglePlots() {
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, ImPlot3D::GetColormapColor(2), IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2));
// Plot pyramid
ImPlot3D::PlotTriangle("Pyramid", xs, ys, zs, 6 * 3); // 6 triangles, 3 vertices each = 18
ImPlot3D::PlotTriangle("Pyramid", xs, ys, zs, 6 * 3, flags); // 6 triangles, 3 vertices each = 18
ImPlot3D::EndPlot();
}
}
@ -254,6 +262,12 @@ void DemoQuadPlots() {
xs[23] = -1; ys[23] = 1; zs[23] = -1;
// clang-format on
// Quad flags
static ImPlot3DQuadFlags flags = ImPlot3DQuadFlags_None;
CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoLines);
CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoFill);
CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoMarkers);
if (ImPlot3D::BeginPlot("Quad Plots")) {
ImPlot3D::SetupAxesLimits(-1.5f, 1.5f, -1.5f, 1.5f, -1.5f, 1.5f);
@ -262,21 +276,21 @@ void DemoQuadPlots() {
ImPlot3D::SetNextFillStyle(colorX);
ImPlot3D::SetNextLineStyle(colorX, 2);
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorX, IMPLOT3D_AUTO, colorX);
ImPlot3D::PlotQuad("X", &xs[0], &ys[0], &zs[0], 8);
ImPlot3D::PlotQuad("X", &xs[0], &ys[0], &zs[0], 8, flags);
// Render +y and -y faces
static ImVec4 colorY(0.2f, 0.8f, 0.2f, 0.8f); // Green
ImPlot3D::SetNextFillStyle(colorY);
ImPlot3D::SetNextLineStyle(colorY, 2);
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorY, IMPLOT3D_AUTO, colorY);
ImPlot3D::PlotQuad("Y", &xs[8], &ys[8], &zs[8], 8);
ImPlot3D::PlotQuad("Y", &xs[8], &ys[8], &zs[8], 8, flags);
// Render +z and -z faces
static ImVec4 colorZ(0.2f, 0.2f, 0.8f, 0.8f); // Blue
ImPlot3D::SetNextFillStyle(colorZ);
ImPlot3D::SetNextLineStyle(colorZ, 2);
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorZ, IMPLOT3D_AUTO, colorZ);
ImPlot3D::PlotQuad("Z", &xs[16], &ys[16], &zs[16], 8);
ImPlot3D::PlotQuad("Z", &xs[16], &ys[16], &zs[16], 8, flags);
ImPlot3D::EndPlot();
}
@ -307,8 +321,7 @@ void DemoSurfacePlots() {
ImGui::Text("Fill color");
static int selected_fill = 1; // Colormap by default
static ImVec4 solid_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);
const char* colormaps[] = {"Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet",
"Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys"};
const char* colormaps[] = {"Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys"};
static int sel_colormap = 5; // Jet by default
{
ImGui::Indent();
@ -347,22 +360,34 @@ void DemoSurfacePlots() {
ImGui::Unindent();
}
// Select flags
static ImPlot3DSurfaceFlags flags = ImPlot3DSurfaceFlags_NoMarkers;
CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoLines);
CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoFill);
CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoMarkers);
// Begin the plot
if (selected_fill == 1)
ImPlot3D::PushColormap(colormaps[sel_colormap]);
if (ImPlot3D::BeginPlot("Surface Plots", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) {
// Set styles
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1.5, 1.5);
// Set fill style
ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f);
if (selected_fill == 0)
ImPlot3D::SetNextFillStyle(solid_color);
// Set line style
ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1));
// Set marker style
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2));
// Plot the surface
if (custom_range)
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N, (double)range_min, (double)range_max);
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N, (double)range_min, (double)range_max, flags);
else
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N);
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N, 0.0, 0.0, flags);
// End the plot
ImPlot3D::PopStyleVar();
@ -376,55 +401,112 @@ void DemoMeshPlots() {
static int mesh_id = 0;
ImGui::Combo("Mesh", &mesh_id, "Duck\0Sphere\0Cube\0\0");
// Choose fill color
static bool set_fill_color = true;
static ImVec4 fill_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);
ImGui::Checkbox("Fill Color", &set_fill_color);
if (set_fill_color) {
ImGui::SameLine();
ImGui::ColorEdit4("##MeshFillColor", (float*)&fill_color);
}
// Choose line color
static bool set_line_color = true;
static ImVec4 line_color = ImVec4(0.2f, 0.2f, 0.2f, 0.8f);
ImGui::Checkbox("Line Color", &set_line_color);
if (set_line_color) {
ImGui::SameLine();
ImGui::ColorEdit4("##MeshLineColor", (float*)&line_color);
}
static ImVec4 line_color = ImVec4(0.5f, 0.5f, 0.2f, 0.6f);
ImGui::ColorEdit4("Line Color##Mesh", (float*)&line_color);
// Choose fill color
static ImVec4 fill_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);
ImGui::ColorEdit4("Fill Color##Mesh", (float*)&fill_color);
// Choose marker color
static bool set_marker_color = false;
static ImVec4 marker_color = ImVec4(0.2f, 0.2f, 0.2f, 0.8f);
ImGui::Checkbox("Marker Color", &set_marker_color);
if (set_marker_color) {
ImGui::SameLine();
ImGui::ColorEdit4("##MeshMarkerColor", (float*)&marker_color);
}
static ImVec4 marker_color = ImVec4(0.5f, 0.5f, 0.2f, 0.6f);
ImGui::ColorEdit4("Marker Color##Mesh", (float*)&marker_color);
// Mesh flags
static ImPlot3DMeshFlags flags = ImPlot3DMeshFlags_NoMarkers;
CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoLines);
CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoFill);
CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoMarkers);
if (ImPlot3D::BeginPlot("Mesh Plots")) {
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1);
// Set colors
if (set_fill_color)
ImPlot3D::SetNextFillStyle(fill_color);
else {
// If not set as transparent, the fill color will be determined by the colormap
ImPlot3D::SetNextFillStyle(ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
}
if (set_line_color)
ImPlot3D::SetNextLineStyle(line_color);
if (set_marker_color)
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, marker_color, IMPLOT3D_AUTO, marker_color);
// Set fill style
ImPlot3D::SetNextFillStyle(fill_color);
// Set line style
ImPlot3D::SetNextLineStyle(line_color);
// Set marker style
ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, marker_color, IMPLOT3D_AUTO, marker_color);
// Plot mesh
if (mesh_id == 0)
ImPlot3D::PlotMesh("Duck", duck_vtx, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT);
ImPlot3D::PlotMesh("Duck", duck_vtx, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT, flags);
else if (mesh_id == 1)
ImPlot3D::PlotMesh("Sphere", sphere_vtx, sphere_idx, SPHERE_VTX_COUNT, SPHERE_IDX_COUNT);
ImPlot3D::PlotMesh("Sphere", sphere_vtx, sphere_idx, SPHERE_VTX_COUNT, SPHERE_IDX_COUNT, flags);
else if (mesh_id == 2)
ImPlot3D::PlotMesh("Cube", cube_vtx, cube_idx, CUBE_VTX_COUNT, CUBE_IDX_COUNT);
ImPlot3D::PlotMesh("Cube", cube_vtx, cube_idx, CUBE_VTX_COUNT, CUBE_IDX_COUNT, flags);
ImPlot3D::EndPlot();
}
}
void DemoImagePlots() {
ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo.");
ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data.");
ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'.");
static ImVec4 tint1(1, 1, 1, 1);
static ImVec4 tint2(1, 1, 1, 1);
static ImPlot3DPoint center1(0, 0, 1);
static ImPlot3DPoint axis_u1(1, 0, 0);
static ImPlot3DPoint axis_v1(0, 1, 0);
static ImVec2 uv0_1(0, 0), uv1_1(1, 1);
static ImPlot3DPoint p0(-1, -1, 0);
static ImPlot3DPoint p1(1, -1, 0);
static ImPlot3DPoint p2(1, 1, 0);
static ImPlot3DPoint p3(-1, 1, 0);
static ImVec2 uv0(0, 0), uv1(1, 0), uv2(1, 1), uv3(0, 1);
// Spacing
ImGui::Dummy(ImVec2(0, 10));
// Image 1 Controls
if (ImGui::TreeNodeEx("Image 1 Controls: Center + Axes")) {
ImGui::SliderFloat3("Center", &center1.x, -2, 2, "%.1f");
ImGui::SliderFloat3("Axis U", &axis_u1.x, -2, 2, "%.1f");
ImGui::SliderFloat3("Axis V", &axis_v1.x, -2, 2, "%.1f");
ImGui::SliderFloat2("UV0", &uv0_1.x, 0, 1, "%.2f");
ImGui::SliderFloat2("UV1", &uv1_1.x, 0, 1, "%.2f");
ImGui::ColorEdit4("Tint", &tint1.x);
ImGui::TreePop();
}
// Image 2 Controls
if (ImGui::TreeNodeEx("Image 2 Controls: Full Quad")) {
ImGui::SliderFloat3("P0", &p0.x, -2, 2, "%.1f");
ImGui::SliderFloat3("P1", &p1.x, -2, 2, "%.1f");
ImGui::SliderFloat3("P2", &p2.x, -2, 2, "%.1f");
ImGui::SliderFloat3("P3", &p3.x, -2, 2, "%.1f");
ImGui::SliderFloat2("UV0", &uv0.x, 0, 1, "%.2f");
ImGui::SliderFloat2("UV1", &uv1.x, 0, 1, "%.2f");
ImGui::SliderFloat2("UV2", &uv2.x, 0, 1, "%.2f");
ImGui::SliderFloat2("UV3", &uv3.x, 0, 1, "%.2f");
ImGui::ColorEdit4("Tint##2", &tint2.x);
ImGui::TreePop();
}
// Plot
if (ImPlot3D::BeginPlot("Image Plot", ImVec2(-1, 0), ImPlot3DFlags_NoClip)) {
#ifdef IMGUI_HAS_TEXTURES
// We use the font atlas ImTextureRef for this demo, but in your real code when you submit
// an image that you have loaded yourself, you would normally have a ImTextureID which works
// just as well (as ImTextureRef can be constructed from ImTextureID).
ImTextureRef tex = ImGui::GetIO().Fonts->TexRef;
#else
ImTextureID tex = ImGui::GetIO().Fonts->TexID;
#endif
ImPlot3D::PlotImage("Image 1", tex, center1, axis_u1, axis_v1, uv0_1, uv1_1, tint1);
ImPlot3D::PlotImage("Image 2", tex, p0, p1, p2, p3, uv0, uv1, uv2, uv3, tint2);
ImPlot3D::EndPlot();
}
@ -470,7 +552,8 @@ void DemoMarkersAndText() {
if (ImPlot3D::BeginPlot("##MarkerStyles", ImVec2(-1, 0), ImPlot3DFlags_CanvasOnly)) {
ImPlot3D::SetupAxes(nullptr, nullptr, nullptr, ImPlot3DAxisFlags_NoDecorations, ImPlot3DAxisFlags_NoDecorations, ImPlot3DAxisFlags_NoDecorations);
ImPlot3D::SetupAxes(nullptr, nullptr, nullptr, ImPlot3DAxisFlags_NoDecorations, ImPlot3DAxisFlags_NoDecorations,
ImPlot3DAxisFlags_NoDecorations);
ImPlot3D::SetupAxesLimits(-0.5, 1.5, -0.5, 1.5, 0, ImPlot3DMarker_COUNT + 1);
float xs[2] = {0, 0};
@ -565,8 +648,54 @@ void DemoBoxScale() {
}
}
void DemoBoxRotation() {
float origin[2] = {0.0f, 0.0f};
float axis[2] = {0.0f, 1.0f};
// Sliders for rotation angles
static float elevation = 45.0f;
static float azimuth = -135.0f;
static bool animate = false;
ImGui::Text("Rotation");
bool changed = false;
if (ImGui::SliderFloat("Elevation", &elevation, -90.0f, 90.0f, "%.1f degrees"))
changed = true;
if (ImGui::SliderFloat("Azimuth", &azimuth, -180.0f, 180.0f, "%.1f degrees"))
changed = true;
ImGui::Checkbox("Animate", &animate);
ImGui::Text("Initial Rotation");
ImGui::SameLine();
HelpMarker("The rotation will be reset to the initial rotation when you double right-click");
static float init_elevation = 45.0f;
static float init_azimuth = -135.0f;
ImGui::SliderFloat("Initial Elevation", &init_elevation, -90.0f, 90.0f, "%.1f degrees");
ImGui::SliderFloat("Initial Azimuth", &init_azimuth, -180.0f, 180.0f, "%.1f degrees");
if (ImPlot3D::BeginPlot("##BoxRotation")) {
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1, ImPlot3DCond_Always);
// Set initial rotation
ImPlot3D::SetupBoxInitialRotation(init_elevation, init_azimuth);
// Set the rotation using the specified elevation and azimuth
if (changed)
ImPlot3D::SetupBoxRotation(elevation, azimuth, animate, ImPlot3DCond_Always);
// Plot axis lines
ImPlot3D::SetNextLineStyle(ImVec4(0.8f, 0.2f, 0.2f, 1));
ImPlot3D::PlotLine("X-Axis", axis, origin, origin, 2);
ImPlot3D::SetNextLineStyle(ImVec4(0.2f, 0.8f, 0.2f, 1));
ImPlot3D::PlotLine("Y-Axis", origin, axis, origin, 2);
ImPlot3D::SetNextLineStyle(ImVec4(0.2f, 0.2f, 0.8f, 1));
ImPlot3D::PlotLine("Z-Axis", origin, origin, axis, 2);
ImPlot3D::EndPlot();
}
}
void DemoTickLabels() {
static bool custom_ticks = false;
static bool custom_ticks = true;
static bool custom_labels = true;
ImGui::Checkbox("Show Custom Ticks", &custom_ticks);
if (custom_ticks) {
@ -588,6 +717,26 @@ void DemoTickLabels() {
}
}
void DemoAxisConstraints() {
static float limit_constraints[2] = {-10, 10};
static float zoom_constraints[2] = {1, 20};
static ImPlot3DAxisFlags flags;
ImGui::DragFloat2("Limits Constraints", limit_constraints, 0.01f);
ImGui::DragFloat2("Zoom Constraints", zoom_constraints, 0.01f);
CHECKBOX_FLAG(flags, ImPlot3DAxisFlags_PanStretch);
if (ImPlot3D::BeginPlot("##AxisConstraints", ImVec2(-1, 0))) {
ImPlot3D::SetupAxes("X", "Y", "Z", flags, flags, flags);
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1);
ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_X, limit_constraints[0], limit_constraints[1]);
ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_Y, limit_constraints[0], limit_constraints[1]);
ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_Z, limit_constraints[0], limit_constraints[1]);
ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_X, zoom_constraints[0], zoom_constraints[1]);
ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_Y, zoom_constraints[0], zoom_constraints[1]);
ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_Z, zoom_constraints[0], zoom_constraints[1]);
ImPlot3D::EndPlot();
}
}
//-----------------------------------------------------------------------------
// [SECTION] Custom
//-----------------------------------------------------------------------------
@ -624,14 +773,8 @@ void DemoCustomRendering() {
// Draw box
ImPlot3DPoint corners[8] = {
ImPlot3DPoint(0, 0, 0),
ImPlot3DPoint(1, 0, 0),
ImPlot3DPoint(1, 1, 0),
ImPlot3DPoint(0, 1, 0),
ImPlot3DPoint(0, 0, 1),
ImPlot3DPoint(1, 0, 1),
ImPlot3DPoint(1, 1, 1),
ImPlot3DPoint(0, 1, 1),
ImPlot3DPoint(0, 0, 0), ImPlot3DPoint(1, 0, 0), ImPlot3DPoint(1, 1, 0), ImPlot3DPoint(0, 1, 0),
ImPlot3DPoint(0, 0, 1), ImPlot3DPoint(1, 0, 1), ImPlot3DPoint(1, 1, 1), ImPlot3DPoint(0, 1, 1),
};
ImVec2 corners_px[8];
for (int i = 0; i < 8; i++)
@ -720,18 +863,57 @@ void DemoHeader(const char* label, void (*demo)()) {
}
}
void ShowAllDemos() {
ImGui::Text("ImPlot3D says olá! (%s)", IMPLOT3D_VERSION);
ImGui::Spacing();
if (ImGui::BeginTabBar("ImPlot3DDemoTabs")) {
if (ImGui::BeginTabItem("Plots")) {
DemoHeader("Line Plots", DemoLinePlots);
DemoHeader("Scatter Plots", DemoScatterPlots);
DemoHeader("Triangle Plots", DemoTrianglePlots);
DemoHeader("Quad Plots", DemoQuadPlots);
DemoHeader("Surface Plots", DemoSurfacePlots);
DemoHeader("Mesh Plots", DemoMeshPlots);
DemoHeader("Realtime Plots", DemoRealtimePlots);
DemoHeader("Image Plots", DemoImagePlots);
DemoHeader("Markers and Text", DemoMarkersAndText);
DemoHeader("NaN Values", DemoNaNValues);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Axes")) {
DemoHeader("Box Scale", DemoBoxScale);
DemoHeader("Box Rotation", DemoBoxRotation);
DemoHeader("Tick Labels", DemoTickLabels);
DemoHeader("Axis Constraints", DemoAxisConstraints);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Custom")) {
DemoHeader("Custom Styles", DemoCustomStyles);
DemoHeader("Custom Rendering", DemoCustomRendering);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Help")) {
DemoHelp();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
void ShowDemoWindow(bool* p_open) {
static bool show_implot3d_metrics = false;
static bool show_implot3d_style_editor = false;
static bool show_imgui_metrics = false;
static bool show_imgui_style_editor = false;
static bool show_imgui_demo = false;
if (show_implot3d_metrics)
ImPlot3D::ShowMetricsWindow(&show_implot3d_metrics);
if (show_implot3d_style_editor) {
ImGui::Begin("Style Editor (ImPlot3D)", &show_implot3d_style_editor);
ImPlot3D::ShowStyleEditor();
ImGui::End();
}
if (show_imgui_style_editor) {
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
ImGui::ShowStyleEditor();
@ -747,6 +929,7 @@ void ShowDemoWindow(bool* p_open) {
ImGui::Begin("ImPlot3D Demo", p_open, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("Tools")) {
ImGui::MenuItem("Metrics", nullptr, &show_implot3d_metrics);
ImGui::MenuItem("Style Editor", nullptr, &show_implot3d_style_editor);
ImGui::Separator();
ImGui::MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
@ -756,40 +939,7 @@ void ShowDemoWindow(bool* p_open) {
}
ImGui::EndMenuBar();
}
ImGui::Text("ImPlot3D says olá! (%s)", IMPLOT3D_VERSION);
ImGui::Spacing();
if (ImGui::BeginTabBar("ImPlot3DDemoTabs")) {
if (ImGui::BeginTabItem("Plots")) {
DemoHeader("Line Plots", DemoLinePlots);
DemoHeader("Scatter Plots", DemoScatterPlots);
DemoHeader("Triangle Plots", DemoTrianglePlots);
DemoHeader("Quad Plots", DemoQuadPlots);
DemoHeader("Surface Plots", DemoSurfacePlots);
DemoHeader("Mesh Plots", DemoMeshPlots);
DemoHeader("Realtime Plots", DemoRealtimePlots);
DemoHeader("Markers and Text", DemoMarkersAndText);
DemoHeader("NaN Values", DemoNaNValues);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Axes")) {
DemoHeader("Box Scale", DemoBoxScale);
DemoHeader("Tick Labels", DemoTickLabels);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Custom")) {
DemoHeader("Custom Styles", DemoCustomStyles);
DemoHeader("Custom Rendering", DemoCustomRendering);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Help")) {
DemoHelp();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ShowAllDemos();
ImGui::End();
}
@ -811,43 +961,6 @@ bool ShowStyleSelector(const char* label) {
return false;
}
void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) {
const int n = continuous ? size - 1 : size;
ImU32 col1, col2;
if (vert) {
const float step = bounds.GetHeight() / n;
ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step);
for (int i = 0; i < n; ++i) {
if (reversed) {
col1 = colors[size - i - 1];
col2 = continuous ? colors[size - i - 2] : col1;
} else {
col1 = colors[i];
col2 = continuous ? colors[i + 1] : col1;
}
DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2);
rect.TranslateY(step);
}
} else {
const float step = bounds.GetWidth() / n;
ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y);
for (int i = 0; i < n; ++i) {
if (reversed) {
col1 = colors[size - i - 1];
col2 = continuous ? colors[size - i - 2] : col1;
} else {
col1 = colors[i];
col2 = continuous ? colors[i + 1] : col1;
}
DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1);
rect.TranslateX(step);
}
}
}
static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; }
static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); }
bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlot3DColormap cmap) {
ImGuiContext& G = *GImGui;
const ImGuiStyle& style = G.Style;
@ -920,9 +1033,8 @@ void ShowStyleEditor(ImPlot3DStyle* ref) {
if (ImGui::Button("Revert Ref"))
style = *ref;
ImGui::SameLine();
HelpMarker(
"Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
"Use \"Export\" below to save them somewhere.");
HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
"Use \"Export\" below to save them somewhere.");
ImGui::Separator();
@ -958,8 +1070,8 @@ void ShowStyleEditor(ImPlot3DStyle* ref) {
const ImVec4& col = style.Colors[i];
const char* name = ImPlot3D::GetStyleColorName(i);
if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
ImGui::LogText("colors[ImPlot3DCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n",
name, 15 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
ImGui::LogText("colors[ImPlot3DCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n", name, 15 - (int)strlen(name), "", col.x,
col.y, col.z, col.w);
}
ImGui::LogFinish();
}
@ -973,6 +1085,17 @@ void ShowStyleEditor(ImPlot3DStyle* ref) {
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
#if IMGUI_VERSION_NUM < 19173
if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None))
alpha_flags = ImGuiColorEditFlags_None;
ImGui::SameLine();
if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview))
alpha_flags = ImGuiColorEditFlags_AlphaPreview;
ImGui::SameLine();
if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))
alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
ImGui::SameLine();
#else
if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque))
alpha_flags = ImGuiColorEditFlags_AlphaOpaque;
ImGui::SameLine();
@ -982,10 +1105,10 @@ void ShowStyleEditor(ImPlot3DStyle* ref) {
if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))
alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
ImGui::SameLine();
HelpMarker(
"In the color list:\n"
"Left-click on color square to open color picker,\n"
"Right-click to open edit options menu.");
#endif
HelpMarker("In the color list:\n"
"Left-click on color square to open color picker,\n"
"Right-click to open edit options menu.");
ImGui::Separator();

View File

@ -1,5 +1,5 @@
//--------------------------------------------------
// ImPlot3D v0.1
// ImPlot3D v0.3 WIP
// implot3d_items.cpp
// Date: 2024-11-26
// Author: Breno Cunha Queiroz (brenocq.com)
@ -25,6 +25,10 @@
// [SECTION] PlotScatter
// [SECTION] PlotLine
// [SECTION] PlotTriangle
// [SECTION] PlotQuad
// [SECTION] PlotSurface
// [SECTION] PlotMesh
// [SECTION] PlotImage
// [SECTION] PlotText
//-----------------------------------------------------------------------------
@ -67,14 +71,14 @@
#endif
// clang-format on
#define IMPLOT3D_NORMALIZE2F(VX, VY) \
do { \
float d2 = VX * VX + VY * VY; \
if (d2 > 0.0f) { \
float inv_len = ImRsqrt(d2); \
VX *= inv_len; \
VY *= inv_len; \
} \
#define IMPLOT3D_NORMALIZE2F(VX, VY) \
do { \
float d2 = VX * VX + VY * VY; \
if (d2 > 0.0f) { \
float inv_len = ImRsqrt(d2); \
VX *= inv_len; \
VY *= inv_len; \
} \
} while (0)
IMPLOT3D_INLINE void GetLineRenderProps(const ImDrawList3D& draw_list_3d, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) {
@ -109,7 +113,8 @@ IMPLOT3D_INLINE void GetLineRenderProps(const ImDrawList3D& draw_list_3d, float&
// As an example, you could use the compile time define given by the line below in order to support only float and double.
// -DIMPLOT3D_CUSTOM_NUMERIC_TYPES="(float)(double)"
// In order to support all known C++ types, use:
// -DIMPLOT3D_CUSTOM_NUMERIC_TYPES="(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned long)(signed long long)(unsigned long long)(float)(double)(long double)"
// -DIMPLOT3D_CUSTOM_NUMERIC_TYPES="(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned
// long)(signed long long)(unsigned long long)(float)(double)(long double)"
#ifdef IMPLOT3D_CUSTOM_NUMERIC_TYPES
#define IMPLOT3D_NUMERIC_TYPES IMPLOT3D_CUSTOM_NUMERIC_TYPES
@ -148,6 +153,8 @@ bool BeginItem(const char* label_id, ImPlot3DItemFlags flags, ImPlot3DCol recolo
// Register item
bool just_created;
ImPlot3DItem* item = RegisterOrGetItem(label_id, flags, &just_created);
// Set current item
gp.CurrentItem = item;
// Set/override item color
if (recolor_from != -1) {
@ -165,10 +172,20 @@ bool BeginItem(const char* label_id, ImPlot3DItemFlags flags, ImPlot3DCol recolo
ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color);
n.IsAutoLine = IsColorAuto(n.Colors[ImPlot3DCol_Line]) && IsColorAuto(ImPlot3DCol_Line);
n.IsAutoFill = IsColorAuto(n.Colors[ImPlot3DCol_Fill]) && IsColorAuto(ImPlot3DCol_Fill);
n.Colors[ImPlot3DCol_Line] = IsColorAuto(n.Colors[ImPlot3DCol_Line]) ? (IsColorAuto(ImPlot3DCol_Line) ? item_color : gp.Style.Colors[ImPlot3DCol_Line]) : n.Colors[ImPlot3DCol_Line];
n.Colors[ImPlot3DCol_Fill] = IsColorAuto(n.Colors[ImPlot3DCol_Fill]) ? (IsColorAuto(ImPlot3DCol_Fill) ? item_color : gp.Style.Colors[ImPlot3DCol_Fill]) : n.Colors[ImPlot3DCol_Fill];
n.Colors[ImPlot3DCol_MarkerOutline] = IsColorAuto(n.Colors[ImPlot3DCol_MarkerOutline]) ? (IsColorAuto(ImPlot3DCol_MarkerOutline) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerOutline]) : n.Colors[ImPlot3DCol_MarkerOutline];
n.Colors[ImPlot3DCol_MarkerFill] = IsColorAuto(n.Colors[ImPlot3DCol_MarkerFill]) ? (IsColorAuto(ImPlot3DCol_MarkerFill) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerFill]) : n.Colors[ImPlot3DCol_MarkerFill];
n.Colors[ImPlot3DCol_Line] = IsColorAuto(n.Colors[ImPlot3DCol_Line])
? (IsColorAuto(ImPlot3DCol_Line) ? item_color : gp.Style.Colors[ImPlot3DCol_Line])
: n.Colors[ImPlot3DCol_Line];
n.Colors[ImPlot3DCol_Fill] = IsColorAuto(n.Colors[ImPlot3DCol_Fill])
? (IsColorAuto(ImPlot3DCol_Fill) ? item_color : gp.Style.Colors[ImPlot3DCol_Fill])
: n.Colors[ImPlot3DCol_Fill];
n.Colors[ImPlot3DCol_MarkerOutline] =
IsColorAuto(n.Colors[ImPlot3DCol_MarkerOutline])
? (IsColorAuto(ImPlot3DCol_MarkerOutline) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerOutline])
: n.Colors[ImPlot3DCol_MarkerOutline];
n.Colors[ImPlot3DCol_MarkerFill] =
IsColorAuto(n.Colors[ImPlot3DCol_MarkerFill])
? (IsColorAuto(ImPlot3DCol_MarkerFill) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerFill])
: n.Colors[ImPlot3DCol_MarkerFill];
// Set size & weight
n.LineWeight = n.LineWeight < 0.0f ? style.LineWeight : n.LineWeight;
@ -222,6 +239,7 @@ bool BeginItemEx(const char* label_id, const _Getter& getter, ImPlot3DItemFlags
void EndItem() {
ImPlot3DContext& gp = *GImPlot3D;
gp.NextItemData.Reset();
gp.CurrentItem = nullptr;
}
ImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, bool* just_created) {
@ -248,6 +266,11 @@ ImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, b
return item;
}
ImPlot3DItem* GetCurrentItem() {
ImPlot3DContext& gp = *GImPlot3D;
return gp.CurrentItem;
}
void BustItemCache() {
ImPlot3DContext& gp = *GImPlot3D;
for (int p = 0; p < gp.Plots.GetBufSize(); ++p) {
@ -284,7 +307,8 @@ void SetNextMarkerStyle(ImPlot3DMarker marker, float size, const ImVec4& fill, f
// [SECTION] Draw Utils
//-----------------------------------------------------------------------------
IMPLOT3D_INLINE void PrimLine(ImDrawList3D& draw_list_3d, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1, float z) {
IMPLOT3D_INLINE void PrimLine(ImDrawList3D& draw_list_3d, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0,
const ImVec2& tex_uv1, float z) {
float dx = P2.x - P1.x;
float dy = P2.y - P1.y;
IMPLOT3D_NORMALIZE2F(dx, dy);
@ -327,30 +351,31 @@ IMPLOT3D_INLINE void PrimLine(ImDrawList3D& draw_list_3d, const ImVec2& P1, cons
float GetPointDepth(ImPlot3DPoint p) {
ImPlot3DContext& gp = *GImPlot3D;
ImPlot3DPlot& plot = *gp.CurrentPlot;
// Adjust for inverted axes before rotation
if (ImHasFlag(plot.Axes[0].Flags, ImPlot3DAxisFlags_Invert))
p.x = -p.x;
if (ImHasFlag(plot.Axes[1].Flags, ImPlot3DAxisFlags_Invert))
p.y = -p.y;
if (ImHasFlag(plot.Axes[2].Flags, ImPlot3DAxisFlags_Invert))
p.z = -p.z;
ImPlot3DPoint p_rot = plot.Rotation * p;
return p_rot.z;
}
struct RendererBase {
RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims),
IdxConsumed(idx_consumed),
VtxConsumed(vtx_consumed) {}
RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) {}
const unsigned int Prims; // Number of primitives to render
const unsigned int IdxConsumed; // Number of indices consumed per primitive
const unsigned int VtxConsumed; // Number of vertices consumed per primitive
};
template <class _Getter>
struct RendererMarkersFill : RendererBase {
RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col) : RendererBase(getter.Count, (count - 2) * 3, count),
Getter(getter),
Marker(marker),
Count(count),
Size(size),
Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const {
UV = draw_list_3d._SharedData->TexUvWhitePixel;
}
template <class _Getter> struct RendererMarkersFill : RendererBase {
RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col)
: RendererBase(getter.Count, (count - 2) * 3, count), Getter(getter), Marker(marker), Count(count), Size(size), Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint p_plot = Getter(prim);
@ -388,13 +413,12 @@ struct RendererMarkersFill : RendererBase {
mutable ImVec2 UV;
};
template <class _Getter>
struct RendererMarkersLine : RendererBase {
RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col) : RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), Marker(marker), Count(count), HalfWeight(ImMax(1.0f, weight) * 0.5f), Size(size), Col(col) {}
template <class _Getter> struct RendererMarkersLine : RendererBase {
RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col)
: RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), Marker(marker), Count(count),
HalfWeight(ImMax(1.0f, weight) * 0.5f), Size(size), Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const {
GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1);
}
void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint p_plot = Getter(prim);
@ -419,20 +443,14 @@ struct RendererMarkersLine : RendererBase {
mutable ImVec2 UV1;
};
template <class _Getter>
struct RendererLineStrip : RendererBase {
template <class _Getter> struct RendererLineStrip : RendererBase {
RendererLineStrip(const _Getter& getter, ImU32 col, float weight)
: RendererBase(getter.Count - 1, 6, 4),
Getter(getter),
Col(col),
HalfWeight(ImMax(1.0f, weight) * 0.5f) {
: RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {
// Initialize the first point in plot coordinates
P1_plot = Getter(0);
}
void Init(ImDrawList3D& draw_list_3d) const {
GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1);
}
void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint P2_plot = Getter(prim + 1);
@ -463,20 +481,14 @@ struct RendererLineStrip : RendererBase {
mutable ImVec2 UV1;
};
template <class _Getter>
struct RendererLineStripSkip : RendererBase {
template <class _Getter> struct RendererLineStripSkip : RendererBase {
RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight)
: RendererBase(getter.Count - 1, 6, 4),
Getter(getter),
Col(col),
HalfWeight(ImMax(1.0f, weight) * 0.5f) {
: RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {
// Initialize the first point in plot coordinates
P1_plot = Getter(0);
}
void Init(ImDrawList3D& draw_list_3d) const {
GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1);
}
void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
// Get the next point in plot coordinates
@ -484,8 +496,7 @@ struct RendererLineStripSkip : RendererBase {
bool visible = false;
// Check for NaNs in P1_plot and P2_plot
if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) &&
!ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {
if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {
// Clip the line segment to the culling box
ImPlot3DPoint P1_clipped, P2_clipped;
@ -515,17 +526,11 @@ struct RendererLineStripSkip : RendererBase {
mutable ImVec2 UV1;
};
template <class _Getter>
struct RendererLineSegments : RendererBase {
template <class _Getter> struct RendererLineSegments : RendererBase {
RendererLineSegments(const _Getter& getter, ImU32 col, float weight)
: RendererBase(getter.Count / 2, 6, 4),
Getter(getter),
Col(col),
HalfWeight(ImMax(1.0f, weight) * 0.5f) {}
: RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {}
void Init(ImDrawList3D& draw_list_3d) const {
GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1);
}
void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
// Get the segment's endpoints in plot coordinates
@ -533,8 +538,7 @@ struct RendererLineSegments : RendererBase {
ImPlot3DPoint P2_plot = Getter(prim * 2 + 1);
// Check for NaNs in P1_plot and P2_plot
if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) &&
!ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {
if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {
// Clip the line segment to the culling box
ImPlot3DPoint P1_clipped, P2_clipped;
@ -560,14 +564,10 @@ struct RendererLineSegments : RendererBase {
mutable ImVec2 UV1;
};
template <class _Getter>
struct RendererTriangleFill : RendererBase {
RendererTriangleFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 3, 3, 3),
Getter(getter),
Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const {
UV = draw_list_3d._SharedData->TexUvWhitePixel;
}
template <class _Getter> struct RendererTriangleFill : RendererBase {
RendererTriangleFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 3, 3, 3), Getter(getter), Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint p_plot[3];
@ -576,7 +576,7 @@ struct RendererTriangleFill : RendererBase {
p_plot[2] = Getter(3 * prim + 2);
// Check if the triangle is outside the culling box
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]))
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]))
return false;
// Project the triangle vertices to screen space
@ -620,14 +620,10 @@ struct RendererTriangleFill : RendererBase {
const ImU32 Col;
};
template <class _Getter>
struct RendererQuadFill : RendererBase {
RendererQuadFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 4, 6, 4),
Getter(getter),
Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const {
UV = draw_list_3d._SharedData->TexUvWhitePixel;
}
template <class _Getter> struct RendererQuadFill : RendererBase {
RendererQuadFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 4, 6, 4), Getter(getter), Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint p_plot[4];
@ -637,8 +633,7 @@ struct RendererQuadFill : RendererBase {
p_plot[3] = Getter(4 * prim + 3);
// Check if the quad is outside the culling box
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) &&
!cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))
return false;
// Project the quad vertices to screen space
@ -682,9 +677,10 @@ struct RendererQuadFill : RendererBase {
draw_list_3d._IdxWritePtr += 6;
// Add depth values for the two triangles
draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3.0f);
draw_list_3d._ZWritePtr[1] = GetPointDepth((p_plot[0] + p_plot[2] + p_plot[3]) / 3.0f);
// Add depth value for the quad
float z = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2] + p_plot[3]) / 4.0f);
draw_list_3d._ZWritePtr[0] = z;
draw_list_3d._ZWritePtr[1] = z;
draw_list_3d._ZWritePtr += 2;
// Update vertex count
@ -698,15 +694,93 @@ struct RendererQuadFill : RendererBase {
const ImU32 Col;
};
template <class _Getter>
struct RendererSurfaceFill : RendererBase {
RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col, double scale_min, double scale_max) : RendererBase((x_count - 1) * (y_count - 1), 6, 4),
Getter(getter),
XCount(x_count),
YCount(y_count),
Col(col),
ScaleMin(scale_min),
ScaleMax(scale_max) {}
template <class _Getter> struct RendererQuadImage : RendererBase {
RendererQuadImage(const _Getter& getter, ImTextureRef tex_ref, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3,
ImU32 col)
: RendererBase(getter.Count / 4, 6, 4), Getter(getter), TexRef(tex_ref), UV0(uv0), UV1(uv1), UV2(uv2), UV3(uv3), Col(col) {}
void Init(ImDrawList3D& draw_list_3d) const {}
IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {
ImPlot3DPoint p_plot[4];
p_plot[0] = Getter(4 * prim);
p_plot[1] = Getter(4 * prim + 1);
p_plot[2] = Getter(4 * prim + 2);
p_plot[3] = Getter(4 * prim + 3);
// Check if the quad is outside the culling box
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))
return false;
// Set texture ID to be used when rendering this quad
draw_list_3d.SetTexture(TexRef);
// Project the quad vertices to screen space
ImVec2 p[4];
p[0] = PlotToPixels(p_plot[0]);
p[1] = PlotToPixels(p_plot[1]);
p[2] = PlotToPixels(p_plot[2]);
p[3] = PlotToPixels(p_plot[3]);
// Add vertices for two triangles
draw_list_3d._VtxWritePtr[0].pos.x = p[0].x;
draw_list_3d._VtxWritePtr[0].pos.y = p[0].y;
draw_list_3d._VtxWritePtr[0].uv = UV0;
draw_list_3d._VtxWritePtr[0].col = Col;
draw_list_3d._VtxWritePtr[1].pos.x = p[1].x;
draw_list_3d._VtxWritePtr[1].pos.y = p[1].y;
draw_list_3d._VtxWritePtr[1].uv = UV1;
draw_list_3d._VtxWritePtr[1].col = Col;
draw_list_3d._VtxWritePtr[2].pos.x = p[2].x;
draw_list_3d._VtxWritePtr[2].pos.y = p[2].y;
draw_list_3d._VtxWritePtr[2].uv = UV2;
draw_list_3d._VtxWritePtr[2].col = Col;
draw_list_3d._VtxWritePtr[3].pos.x = p[3].x;
draw_list_3d._VtxWritePtr[3].pos.y = p[3].y;
draw_list_3d._VtxWritePtr[3].uv = UV3;
draw_list_3d._VtxWritePtr[3].col = Col;
draw_list_3d._VtxWritePtr += 4;
// Add indices for two triangles
draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);
draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);
draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);
draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);
draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);
draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3);
draw_list_3d._IdxWritePtr += 6;
// Add depth value for the quad
float z = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2] + p_plot[3]) / 4.0f);
draw_list_3d._ZWritePtr[0] = z;
draw_list_3d._ZWritePtr[1] = z;
draw_list_3d._ZWritePtr += 2;
// Update vertex count
draw_list_3d._VtxCurrentIdx += 4;
// Reset texture ID
draw_list_3d.ResetTexture();
return true;
}
const _Getter& Getter;
const ImTextureRef TexRef;
const ImVec2 UV0, UV1, UV2, UV3;
const ImU32 Col;
};
template <class _Getter> struct RendererSurfaceFill : RendererBase {
RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col, double scale_min, double scale_max)
: RendererBase((x_count - 1) * (y_count - 1), 6, 4), Getter(getter), XCount(x_count), YCount(y_count), Col(col), ScaleMin(scale_min),
ScaleMax(scale_max) {}
void Init(ImDrawList3D& draw_list_3d) const {
UV = draw_list_3d._SharedData->TexUvWhitePixel;
@ -735,8 +809,7 @@ struct RendererSurfaceFill : RendererBase {
p_plot[3] = Getter(x + (y + 1) * XCount);
// Check if the quad is outside the culling box
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) &&
!cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))
if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))
return false;
// Compute colors
@ -824,27 +897,20 @@ struct RendererSurfaceFill : RendererBase {
// [SECTION] Indexers
//-----------------------------------------------------------------------------
template <typename T>
IMPLOT3D_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) {
template <typename T> IMPLOT3D_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) {
const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1);
switch (s) {
case 3: return data[idx];
case 2: return data[(offset + idx) % count];
case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx)) * stride);
case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx))*stride);
case 0: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride);
default: return T(0);
}
}
template <typename T>
struct IndexerIdx {
IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data),
Count(count),
Offset(offset),
Stride(stride) {}
template <typename I> IMPLOT3D_INLINE double operator()(I idx) const {
return (double)IndexData(Data, idx, Count, Offset, Stride);
}
template <typename T> struct IndexerIdx {
IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(offset), Stride(stride) {}
template <typename I> IMPLOT3D_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); }
const T* Data;
int Count;
int Offset;
@ -855,8 +921,7 @@ struct IndexerIdx {
// [SECTION] Getters
//-----------------------------------------------------------------------------
template <typename _IndexerX, typename _IndexerY, typename _IndexerZ>
struct GetterXYZ {
template <typename _IndexerX, typename _IndexerY, typename _IndexerZ> struct GetterXYZ {
GetterXYZ(_IndexerX x, _IndexerY y, _IndexerZ z, int count) : IndexerX(x), IndexerY(y), IndexerZ(z), Count(count) {}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {
return ImPlot3DPoint((float)IndexerX(idx), (float)IndexerY(idx), (float)IndexerZ(idx));
@ -867,8 +932,7 @@ struct GetterXYZ {
const int Count;
};
template <typename _Getter>
struct GetterLoop {
template <typename _Getter> struct GetterLoop {
GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) {}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {
idx = idx % (Count - 1);
@ -878,8 +942,7 @@ struct GetterLoop {
const int Count;
};
template <typename _Getter>
struct GetterTriangleLines {
template <typename _Getter> struct GetterTriangleLines {
GetterTriangleLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {
idx = ((idx % 6 + 1) / 2) % 3 + idx / 6 * 3;
@ -889,8 +952,7 @@ struct GetterTriangleLines {
const int Count;
};
template <typename _Getter>
struct GetterQuadLines {
template <typename _Getter> struct GetterQuadLines {
GetterQuadLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {
idx = ((idx % 8 + 1) / 2) % 4 + idx / 8 * 4;
@ -900,10 +962,8 @@ struct GetterQuadLines {
const int Count;
};
template <typename _Getter>
struct GetterSurfaceLines {
GetterSurfaceLines(_Getter getter, int x_count, int y_count)
: Getter(getter), XCount(x_count), YCount(y_count) {
template <typename _Getter> struct GetterSurfaceLines {
GetterSurfaceLines(_Getter getter, int x_count, int y_count) : Getter(getter), XCount(x_count), YCount(y_count) {
int horizontal_segments = (XCount - 1) * YCount;
int vertical_segments = (YCount - 1) * XCount;
int segments = horizontal_segments + vertical_segments;
@ -946,9 +1006,7 @@ struct GetterSurfaceLines {
struct Getter3DPoints {
Getter3DPoints(const ImPlot3DPoint* points, int count) : Points(points), Count(count) {}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {
return Points[idx];
}
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return Points[idx]; }
const ImPlot3DPoint* Points;
const int Count;
};
@ -957,8 +1015,7 @@ struct GetterMeshTriangles {
GetterMeshTriangles(const ImPlot3DPoint* vtx, const unsigned int* idx, int idx_count)
: Vtx(vtx), Idx(idx), IdxCount(idx_count), TriCount(idx_count / 3), Count(idx_count) {}
template <typename I>
IMPLOT3D_INLINE ImPlot3DPoint operator()(I i) const {
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I i) const {
unsigned int vi = Idx[i];
return Vtx[vi];
}
@ -975,8 +1032,7 @@ struct GetterMeshTriangles {
//-----------------------------------------------------------------------------
/// Renders primitive shapes
template <template <class> class _Renderer, class _Getter, typename... Args>
void RenderPrimitives(const _Getter& getter, Args... args) {
template <template <class> class _Renderer, class _Getter, typename... Args> void RenderPrimitives(const _Getter& getter, Args... args) {
_Renderer<_Getter> renderer(getter, args...);
ImPlot3DPlot& plot = *GetCurrentPlot();
ImDrawList3D& draw_list_3d = plot.DrawList;
@ -1011,46 +1067,64 @@ void RenderPrimitives(const _Getter& getter, Args... args) {
// [SECTION] Markers
//-----------------------------------------------------------------------------
static const ImVec2 MARKER_FILL_CIRCLE[10] = {ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f), ImVec2(0.30901697f, 0.95105654f), ImVec2(-0.30901703f, 0.9510565f), ImVec2(-0.80901706f, 0.5877852f), ImVec2(-1.0f, 0.0f), ImVec2(-0.80901694f, -0.58778536f), ImVec2(-0.3090171f, -0.9510565f), ImVec2(0.30901712f, -0.9510565f), ImVec2(0.80901694f, -0.5877853f)};
static const ImVec2 MARKER_FILL_SQUARE[4] = {ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2)};
static const ImVec2 MARKER_FILL_CIRCLE[10] = {ImVec2(1.0f, 0.0f),
ImVec2(0.809017f, 0.58778524f),
ImVec2(0.30901697f, 0.95105654f),
ImVec2(-0.30901703f, 0.9510565f),
ImVec2(-0.80901706f, 0.5877852f),
ImVec2(-1.0f, 0.0f),
ImVec2(-0.80901694f, -0.58778536f),
ImVec2(-0.3090171f, -0.9510565f),
ImVec2(0.30901712f, -0.9510565f),
ImVec2(0.80901694f, -0.5877853f)};
static const ImVec2 MARKER_FILL_SQUARE[4] = {ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2),
ImVec2(-SQRT_1_2, SQRT_1_2)};
static const ImVec2 MARKER_FILL_DIAMOND[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};
static const ImVec2 MARKER_FILL_UP[3] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(-SQRT_3_2, 0.5f)};
static const ImVec2 MARKER_FILL_DOWN[3] = {ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f)};
static const ImVec2 MARKER_FILL_LEFT[3] = {ImVec2(-1, 0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)};
static const ImVec2 MARKER_FILL_RIGHT[3] = {ImVec2(1, 0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)};
static const ImVec2 MARKER_LINE_CIRCLE[20] = {
ImVec2(1.0f, 0.0f),
ImVec2(0.809017f, 0.58778524f),
ImVec2(0.809017f, 0.58778524f),
ImVec2(0.30901697f, 0.95105654f),
ImVec2(0.30901697f, 0.95105654f),
ImVec2(-0.30901703f, 0.9510565f),
ImVec2(-0.30901703f, 0.9510565f),
ImVec2(-0.80901706f, 0.5877852f),
ImVec2(-0.80901706f, 0.5877852f),
ImVec2(-1.0f, 0.0f),
ImVec2(-1.0f, 0.0f),
ImVec2(-0.80901694f, -0.58778536f),
ImVec2(-0.80901694f, -0.58778536f),
ImVec2(-0.3090171f, -0.9510565f),
ImVec2(-0.3090171f, -0.9510565f),
ImVec2(0.30901712f, -0.9510565f),
ImVec2(0.30901712f, -0.9510565f),
ImVec2(0.80901694f, -0.5877853f),
ImVec2(0.80901694f, -0.5877853f),
ImVec2(1.0f, 0.0f)};
static const ImVec2 MARKER_LINE_SQUARE[8] = {ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, SQRT_1_2)};
static const ImVec2 MARKER_LINE_DIAMOND[8] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(-1, 0), ImVec2(0, 1), ImVec2(0, 1), ImVec2(1, 0)};
static const ImVec2 MARKER_LINE_UP[6] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(0, -1), ImVec2(-SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, 0.5f)};
static const ImVec2 MARKER_LINE_DOWN[6] = {ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f), ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, -0.5f)};
static const ImVec2 MARKER_LINE_LEFT[6] = {ImVec2(-1, 0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2), ImVec2(0.5, -SQRT_3_2), ImVec2(-1, 0)};
static const ImVec2 MARKER_LINE_RIGHT[6] = {ImVec2(1, 0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(1, 0)};
static const ImVec2 MARKER_LINE_ASTERISK[6] = {ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, -0.5f), ImVec2(0, -1), ImVec2(0, 1)};
static const ImVec2 MARKER_LINE_CIRCLE[20] = {ImVec2(1.0f, 0.0f),
ImVec2(0.809017f, 0.58778524f),
ImVec2(0.809017f, 0.58778524f),
ImVec2(0.30901697f, 0.95105654f),
ImVec2(0.30901697f, 0.95105654f),
ImVec2(-0.30901703f, 0.9510565f),
ImVec2(-0.30901703f, 0.9510565f),
ImVec2(-0.80901706f, 0.5877852f),
ImVec2(-0.80901706f, 0.5877852f),
ImVec2(-1.0f, 0.0f),
ImVec2(-1.0f, 0.0f),
ImVec2(-0.80901694f, -0.58778536f),
ImVec2(-0.80901694f, -0.58778536f),
ImVec2(-0.3090171f, -0.9510565f),
ImVec2(-0.3090171f, -0.9510565f),
ImVec2(0.30901712f, -0.9510565f),
ImVec2(0.30901712f, -0.9510565f),
ImVec2(0.80901694f, -0.5877853f),
ImVec2(0.80901694f, -0.5877853f),
ImVec2(1.0f, 0.0f)};
static const ImVec2 MARKER_LINE_SQUARE[8] = {ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2),
ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2),
ImVec2(-SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, SQRT_1_2)};
static const ImVec2 MARKER_LINE_DIAMOND[8] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, -1), ImVec2(-1, 0),
ImVec2(-1, 0), ImVec2(0, 1), ImVec2(0, 1), ImVec2(1, 0)};
static const ImVec2 MARKER_LINE_UP[6] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(0, -1),
ImVec2(-SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, 0.5f)};
static const ImVec2 MARKER_LINE_DOWN[6] = {ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f),
ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, -0.5f)};
static const ImVec2 MARKER_LINE_LEFT[6] = {ImVec2(-1, 0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, SQRT_3_2),
ImVec2(0.5, -SQRT_3_2), ImVec2(0.5, -SQRT_3_2), ImVec2(-1, 0)};
static const ImVec2 MARKER_LINE_RIGHT[6] = {
ImVec2(1, 0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(1, 0)};
static const ImVec2 MARKER_LINE_ASTERISK[6] = {ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f),
ImVec2(SQRT_3_2, -0.5f), ImVec2(0, -1), ImVec2(0, 1)};
static const ImVec2 MARKER_LINE_PLUS[4] = {ImVec2(-1, 0), ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, 1)};
static const ImVec2 MARKER_LINE_CROSS[4] = {ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2)};
static const ImVec2 MARKER_LINE_CROSS[4] = {ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2),
ImVec2(-SQRT_1_2, SQRT_1_2)};
template <typename _Getter>
void RenderMarkers(const _Getter& getter, ImPlot3DMarker marker, float size, bool rend_fill, ImU32 col_fill, bool rend_line, ImU32 col_line, float weight) {
template <typename _Getter> void RenderMarkers(const _Getter& getter, ImPlot3DMarker marker, float size, bool rend_fill, ImU32 col_fill,
bool rend_line, ImU32 col_line, float weight) {
if (rend_fill) {
switch (marker) {
case ImPlot3DMarker_Circle: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_CIRCLE, 10, size, col_fill); break;
@ -1082,8 +1156,7 @@ void RenderMarkers(const _Getter& getter, ImPlot3DMarker marker, float size, boo
// [SECTION] PlotScatter
//-----------------------------------------------------------------------------
template <typename Getter>
void PlotScatterEx(const char* label_id, const Getter& getter, ImPlot3DScatterFlags flags) {
template <typename Getter> void PlotScatterEx(const char* label_id, const Getter& getter, ImPlot3DScatterFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_MarkerOutline)) {
const ImPlot3DNextItemData& n = GetItemData();
ImPlot3DMarker marker = n.Marker == ImPlot3DMarker_None ? ImPlot3DMarker_Circle : n.Marker;
@ -1099,12 +1172,14 @@ template <typename T>
void PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags, int offset, int stride) {
if (count < 1)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),
IndexerIdx<T>(zs, count, offset, stride), count);
return PlotScatterEx(label_id, getter, flags);
}
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotScatter<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags, int offset, int stride);
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotScatter<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags, \
int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
@ -1112,8 +1187,7 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
// [SECTION] PlotLine
//-----------------------------------------------------------------------------
template <typename _Getter>
void PlotLineEx(const char* label_id, const _Getter& getter, ImPlot3DLineFlags flags) {
template <typename _Getter> void PlotLineEx(const char* label_id, const _Getter& getter, ImPlot3DLineFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Line)) {
const ImPlot3DNextItemData& n = GetItemData();
if (getter.Count >= 2 && n.RenderLine) {
@ -1146,12 +1220,14 @@ void PlotLineEx(const char* label_id, const _Getter& getter, ImPlot3DLineFlags f
IMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags, int offset, int stride) {
if (count < 2)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),
IndexerIdx<T>(zs, count, offset, stride), count);
return PlotLineEx(label_id, getter, flags);
}
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotLine<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags, int offset, int stride);
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotLine<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags, \
int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
@ -1159,25 +1235,24 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
// [SECTION] PlotTriangle
//-----------------------------------------------------------------------------
template <typename _Getter>
void PlotTriangleEx(const char* label_id, const _Getter& getter, ImPlot3DTriangleFlags flags) {
template <typename _Getter> void PlotTriangleEx(const char* label_id, const _Getter& getter, ImPlot3DTriangleFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {
const ImPlot3DNextItemData& n = GetItemData();
// Render fill
if (getter.Count >= 3 && n.RenderFill) {
if (getter.Count >= 3 && n.RenderFill && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoFill)) {
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);
RenderPrimitives<RendererTriangleFill>(getter, col_fill);
}
// Render lines
if (getter.Count >= 2 && n.RenderLine) {
if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoLines)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);
RenderPrimitives<RendererLineSegments>(GetterTriangleLines<_Getter>(getter), col_line, n.LineWeight);
}
// Render markers
if (n.Marker != ImPlot3DMarker_None) {
if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoMarkers)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);
RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);
@ -1187,15 +1262,18 @@ void PlotTriangleEx(const char* label_id, const _Getter& getter, ImPlot3DTriangl
}
}
IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags, int offset, int stride) {
IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags, int offset,
int stride) {
if (count < 3)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),
IndexerIdx<T>(zs, count, offset, stride), count);
return PlotTriangleEx(label_id, getter, flags);
}
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotTriangle<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags, int offset, int stride);
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotTriangle<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags, \
int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
@ -1203,25 +1281,24 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
// [SECTION] PlotQuad
//-----------------------------------------------------------------------------
template <typename _Getter>
void PlotQuadEx(const char* label_id, const _Getter& getter, ImPlot3DQuadFlags flags) {
template <typename _Getter> void PlotQuadEx(const char* label_id, const _Getter& getter, ImPlot3DQuadFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {
const ImPlot3DNextItemData& n = GetItemData();
// Render fill
if (getter.Count >= 4 && n.RenderFill) {
if (getter.Count >= 4 && n.RenderFill && !ImHasFlag(flags, ImPlot3DQuadFlags_NoFill)) {
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);
RenderPrimitives<RendererQuadFill>(getter, col_fill);
}
// Render lines
if (getter.Count >= 2 && n.RenderLine) {
if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DQuadFlags_NoLines)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);
RenderPrimitives<RendererLineSegments>(GetterQuadLines<_Getter>(getter), col_line, n.LineWeight);
}
// Render markers
if (n.Marker != ImPlot3DMarker_None) {
if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DQuadFlags_NoMarkers)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);
RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);
@ -1234,12 +1311,14 @@ void PlotQuadEx(const char* label_id, const _Getter& getter, ImPlot3DQuadFlags f
IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags, int offset, int stride) {
if (count < 3)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),
IndexerIdx<T>(zs, count, offset, stride), count);
return PlotQuadEx(label_id, getter, flags);
}
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotQuad<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags, int offset, int stride);
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotQuad<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags, \
int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
@ -1247,25 +1326,25 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
// [SECTION] PlotSurface
//-----------------------------------------------------------------------------
template <typename _Getter>
void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags) {
template <typename _Getter> void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min,
double scale_max, ImPlot3DSurfaceFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {
const ImPlot3DNextItemData& n = GetItemData();
// Render fill
if (getter.Count >= 4 && n.RenderFill) {
if (getter.Count >= 4 && n.RenderFill && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoFill)) {
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);
RenderPrimitives<RendererSurfaceFill>(getter, x_count, y_count, col_fill, scale_min, scale_max);
}
// Render lines
if (getter.Count >= 2 && n.RenderLine) {
if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoLines)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);
RenderPrimitives<RendererLineSegments>(GetterSurfaceLines<_Getter>(getter, x_count, y_count), col_line, n.LineWeight);
}
// Render markers
if (n.Marker != ImPlot3DMarker_None) {
if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoMarkers)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);
RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);
@ -1275,19 +1354,26 @@ void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int
}
}
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) {
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min,
double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) {
int count = x_count * y_count;
if (count < 4)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),
IndexerIdx<T>(zs, count, offset, stride), count);
return PlotSurfaceEx(label_id, getter, x_count, y_count, scale_min, scale_max, flags);
}
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride);
#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, \
double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
//-----------------------------------------------------------------------------
// [SECTION] PlotMesh
//-----------------------------------------------------------------------------
void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags) {
Getter3DPoints getter(vtx, vtx_count); // Get vertices
GetterMeshTriangles getter_triangles(vtx, idx, idx_count); // Get triangle vertices
@ -1295,19 +1381,19 @@ void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int
const ImPlot3DNextItemData& n = GetItemData();
// Render fill
if (getter.Count >= 3 && n.RenderFill) {
if (getter.Count >= 3 && n.RenderFill && !ImHasFlag(flags, ImPlot3DMeshFlags_NoFill)) {
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);
RenderPrimitives<RendererTriangleFill>(getter_triangles, col_fill);
}
// Render lines
if (getter.Count >= 2 && n.RenderLine && !n.IsAutoLine) {
if (getter.Count >= 2 && n.RenderLine && !n.IsAutoLine && !ImHasFlag(flags, ImPlot3DMeshFlags_NoLines)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);
RenderPrimitives<RendererLineSegments>(GetterTriangleLines<GetterMeshTriangles>(getter_triangles), col_line, n.LineWeight);
}
// Render markers
if (n.Marker != ImPlot3DMarker_None) {
if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DMeshFlags_NoMarkers)) {
const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);
RenderMarkers(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);
@ -1317,6 +1403,57 @@ void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int
}
}
//-----------------------------------------------------------------------------
// [SECTION] PlotImage
//-----------------------------------------------------------------------------
IMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& center, const ImPlot3DPoint& axis_u,
const ImPlot3DPoint& axis_v, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlot3DImageFlags flags) {
// Compute corners from center and axes
ImPlot3DPoint p0 = center - axis_u - axis_v; // Bottom-left
ImPlot3DPoint p1 = center + axis_u - axis_v; // Bottom-right
ImPlot3DPoint p2 = center + axis_u + axis_v; // Top-right
ImPlot3DPoint p3 = center - axis_u + axis_v; // Top-left
// Map ImPlot-style 2-point UVs into full 4-corner UVs
ImVec2 uv_0 = uv0;
ImVec2 uv_1 = ImVec2(uv1.x, uv0.y);
ImVec2 uv_2 = uv1;
ImVec2 uv_3 = ImVec2(uv0.x, uv1.y);
// Delegate to full quad version
PlotImage(label_id, tex_ref, p0, p1, p2, p3, uv_0, uv_1, uv_2, uv_3, tint_col, flags);
}
IMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, const ImPlot3DPoint& p2,
const ImPlot3DPoint& p3, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3,
const ImVec4& tint_col, ImPlot3DImageFlags flags) {
ImPlot3DContext& gp = *GImPlot3D;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotImage() needs to be called between BeginPlot() and EndPlot()!");
SetupLock();
ImPlot3DPoint corners[4] = {p0, p1, p2, p3};
Getter3DPoints getter(corners, 4);
// Invert Y from UVs
ImVec2 uv_0 = ImVec2(uv0.x, 1 - uv0.y);
ImVec2 uv_1 = ImVec2(uv1.x, 1 - uv1.y);
ImVec2 uv_2 = ImVec2(uv2.x, 1 - uv2.y);
ImVec2 uv_3 = ImVec2(uv3.x, 1 - uv3.y);
if (BeginItemEx(label_id, getter, flags)) {
ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col);
GetCurrentItem()->Color = tint_col32;
// Render image
bool is_transparent = (tint_col32 & IM_COL32_A_MASK) == 0;
if (!is_transparent)
RenderPrimitives<RendererQuadImage>(getter, tex_ref, uv_0, uv_1, uv_2, uv_3, tint_col32);
EndItem();
}
}
//-----------------------------------------------------------------------------
// [SECTION] PlotText
//-----------------------------------------------------------------------------

File diff suppressed because one or more lines are too long

View File

@ -530,7 +530,6 @@ namespace hex::init {
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize;
io.Fonts->AddFontDefault(&cfg);
ImGui_ImplOpenGL3_CreateFontsTexture();
}
// Don't save window settings for the splash screen

View File

@ -32,6 +32,7 @@ namespace hex {
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);
}
void Window::initNative() {

View File

@ -39,6 +39,7 @@
#include <implot3d_internal.h>
#include <imnodes.h>
#include <imnodes_internal.h>
#if defined(IMGUI_TEST_ENGINE)
#include <imgui_te_engine.h>
#include <imgui_te_ui.h>
@ -352,61 +353,12 @@ namespace hex {
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
// Create font textures if necessary
{
const auto &fontDefinitions = ImHexApi::Fonts::impl::getFontDefinitions();
auto &currentFont = ImGui::GetIO().Fonts;
for (const auto &[name, font] : fontDefinitions) {
// If the texture for this atlas has been built already, don't do it again
if (font == nullptr || font->ContainerAtlas == nullptr || font->ContainerAtlas->TexID != 0)
continue;
currentFont = font->ContainerAtlas;
ImGui_ImplOpenGL3_CreateFontsTexture();
for (ImFontConfig& fontCfg : font->ContainerAtlas->Sources) {
if (fontCfg.FontData && fontCfg.FontDataOwnedByAtlas) {
IM_FREE(fontCfg.FontData);
fontCfg.FontData = NULL;
}
}
currentFont->ClearTexData();
}
{
auto font = ImHexApi::Fonts::getFont("hex.fonts.font.default");
if (font == nullptr) {
const auto &io = ImGui::GetIO();
io.Fonts->Clear();
ImFontConfig cfg;
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize;
font = io.Fonts->AddFontDefault(&cfg);
ImGui_ImplOpenGL3_CreateFontsTexture();
for (ImFontConfig& fontCfg : font->ContainerAtlas->Sources) {
if (fontCfg.FontData && fontCfg.FontDataOwnedByAtlas) {
IM_FREE(fontCfg.FontData);
fontCfg.FontData = NULL;
}
}
io.Fonts->ClearTexData();
} else {
currentFont = font->ContainerAtlas;
}
ImGui::SetCurrentFont(font);
}
}
// Start new ImGui Frame
ImGui::NewFrame();
ImHexApi::Fonts::getDefaultFont().push();
#if defined(IMGUI_TEST_ENGINE)
if (ImGuiExt::ImGuiTestEngine::isEnabled())
ImGuiTestEngine_ShowTestEngineWindows(m_testEngine, nullptr);
@ -802,6 +754,8 @@ namespace hex {
this->endNativeWindowFrame();
ImHexApi::Fonts::getDefaultFont().pop();
// Finalize ImGui frame
ImGui::Render();
@ -968,7 +922,7 @@ namespace hex {
void Window::unlockFrameRate() {
{
std::scoped_lock lock(m_wakeupMutex);
m_remainingUnlockedTime = std::chrono::seconds(2);
m_remainingUnlockedTime = std::chrono::seconds(2LL);
}
this->forceNewFrame();
@ -1255,7 +1209,7 @@ namespace hex {
{
std::scoped_lock lock(m_sleepMutex);
if (m_remainingUnlockedTime > std::chrono::nanoseconds(0)) {
if (m_remainingUnlockedTime > std::chrono::nanoseconds(0LL)) {
m_remainingUnlockedTime -= iterationTime;
} else {
targetFps = 5;
@ -1314,9 +1268,6 @@ namespace hex {
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigWindowsMoveFromTitleBarOnly = true;
io.FontGlobalScale = 1.0F;
ImGui::GetCurrentContext()->FontAtlasOwnedByContext = false;
if (glfwGetPrimaryMonitor() != nullptr) {
if (ImHexApi::System::isMutliWindowModeEnabled())

View File

@ -145,6 +145,5 @@ namespace hex::plugin::builtin {
ImHexApi::System::addStartupTask("Load Window Settings", false, loadWindowSettings);
ImHexApi::System::addStartupTask("Configuring UI scale", false, configureUIScale);
ImHexApi::System::addStartupTask("Checking for updates", true, checkForUpdates);
ImHexApi::System::addStartupTask("Loading fonts", true, fonts::setupFonts);
}
}

View File

@ -341,9 +341,9 @@ namespace hex::plugin::builtin {
m_focusedSubWindowName = name;
}
ImGui::PushFont(fonts::CodeEditor());
fonts::CodeEditor().push();
m_textEditor.get(provider).Render("##pattern_editor", textEditorSize, false);
ImGui::PopFont();
fonts::CodeEditor().pop();
m_textEditorHoverBox = ImRect(windowPosition,windowPosition+textEditorSize);
m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize);
@ -1087,9 +1087,9 @@ namespace hex::plugin::builtin {
m_consoleNeedsUpdate = false;
}
ImGui::PushFont(fonts::CodeEditor());
fonts::CodeEditor().push();
m_consoleEditor.get(provider).Render("##console", size, true);
ImGui::PopFont();
fonts::CodeEditor().pop();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y + 1_scaled);
}

View File

@ -426,7 +426,7 @@ namespace hex::plugin::builtin {
ImGui::OpenPopup("WindowingMenu");
#elif defined(OS_MACOS)
if (!isMacosFullScreenModeEnabled(window))
ImGui::SetCursorPosX(68_scaled);
ImGui::SetCursorPosX(75_scaled);
#endif
}

View File

@ -7,7 +7,6 @@ add_imhex_plugin(
fonts
SOURCES
source/library_fonts.cpp
source/font_loader.cpp
source/fonts.cpp
source/font_settings.cpp
INCLUDES

View File

@ -1,298 +0,0 @@
#pragma once
#include <imgui.h>
#include <imgui_internal.h>
#include <imgui_freetype.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_LCD_FILTER_H
#include FT_BITMAP_H
#include <memory>
#include <list>
#include <hex/api/task_manager.hpp>
#include <romfs/romfs.hpp>
#include <wolv/io/file.hpp>
namespace hex::fonts {
class Font {
public:
Font() = default;
float getDescent() const {
return m_font->Descent;
}
float calculateFontDescend(FT_Library ft, float fontSize) const {
if (ft == nullptr) {
log::fatal("FreeType not initialized");
return 0.0f;
}
FT_Face face;
if (FT_New_Memory_Face(ft, reinterpret_cast<const FT_Byte *>(m_font->Sources->FontData), m_font->Sources->FontDataSize, 0, &face) != 0) {
log::fatal("Failed to load face");
return 0.0f;
}
// Calculate the expected font size
auto size = fontSize;
if (m_font->FontSize > 0.0F)
size = m_font->FontSize * std::max(1.0F, std::floor(ImHexApi::System::getGlobalScale()));
else
size = std::max(1.0F, std::floor(size / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize * std::floor(ImHexApi::System::getGlobalScale());
FT_Size_RequestRec req;
req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)(IM_ROUND(size) * 64.0F);
req.horiResolution = 0;
req.vertResolution = 0;
FT_Request_Size(face, &req);
return face->size->metrics.ascender / 64.0F;
}
ImFont* getFont() { return m_font; }
private:
explicit Font(ImFont *font) : m_font(font) { }
private:
friend class FontAtlas;
ImFont *m_font;
};
class FontAtlas {
public:
FontAtlas() : m_fontAtlas(IM_NEW(ImFontAtlas)) {
enableUnicodeCharacters(false);
// Set the default configuration for the font atlas
m_defaultConfig.OversampleH = m_defaultConfig.OversampleV = 1;
m_defaultConfig.PixelSnapH = true;
m_defaultConfig.MergeMode = false;
// Make sure the font atlas doesn't get too large, otherwise weaker GPUs might reject it
m_fontAtlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
m_fontAtlas->TexDesiredWidth = 4096;
}
FontAtlas(const FontAtlas &) = delete;
FontAtlas &operator=(const FontAtlas &) = delete;
FontAtlas(FontAtlas &&other) noexcept {
m_fontAtlas = other.m_fontAtlas;
other.m_fontAtlas = nullptr;
m_defaultConfig = other.m_defaultConfig;
m_fontSizes = std::move(other.m_fontSizes);
m_fontConfigs = std::move(other.m_fontConfigs);
m_glyphRange = std::move(other.m_glyphRange);
m_fontData = std::move(other.m_fontData);
}
FontAtlas& operator=(FontAtlas &&other) noexcept {
if (this != &other) {
if (m_fontAtlas != nullptr) {
IM_DELETE(m_fontAtlas);
}
m_fontAtlas = other.m_fontAtlas;
other.m_fontAtlas = nullptr;
m_defaultConfig = other.m_defaultConfig;
m_fontSizes = std::move(other.m_fontSizes);
m_fontConfigs = std::move(other.m_fontConfigs);
m_glyphRange = std::move(other.m_glyphRange);
m_fontData = std::move(other.m_fontData);
}
return *this;
}
~FontAtlas() {
if (m_fontAtlas != nullptr) {
m_fontAtlas->Locked = false;
if (ImGui::GetIO().Fonts != m_fontAtlas) {
IM_DELETE(m_fontAtlas);
}
m_fontAtlas = nullptr;
}
}
Font addDefaultFont() {
auto &config = m_fontConfigs.emplace_back(m_defaultConfig);
config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
config.SizePixels = std::max(1.0F, std::floor(ImHexApi::System::getGlobalScale() * ImHexApi::System::getBackingScaleFactor() * 13.0F));
auto font = m_fontAtlas->AddFontDefault(&config);
font->Scale = 1.0 / std::floor(ImHexApi::System::getBackingScaleFactor());
m_fontSizes.emplace_back(false, config.SizePixels);
m_defaultConfig.MergeMode = true;
return Font(font);
}
Font addFontFromMemory(const std::vector<u8> &fontData, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
auto &storedFontData = m_fontData.emplace_back(fontData);
if (storedFontData.empty()) {
log::fatal("Failed to load font data");
return Font();
}
auto &config = m_fontConfigs.emplace_back(m_defaultConfig);
config.FontDataOwnedByAtlas = false;
config.GlyphOffset = { offset.x, offset.y };
auto font = m_fontAtlas->AddFontFromMemoryTTF(storedFontData.data(), int(storedFontData.size()), fontSize, &config, !glyphRange.empty() ? glyphRange.Data : m_glyphRange.Data);
font->Scale = 1.0 / ImHexApi::System::getBackingScaleFactor();
m_fontSizes.emplace_back(scalable, fontSize);
m_defaultConfig.MergeMode = true;
return Font(font);
}
Font addFontFromRomFs(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
auto data = romfs::get(path).span<u8>();
return addFontFromMemory({ data.begin(), data.end() }, fontSize, scalable, offset, glyphRange);
}
Font addFontFromFile(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
auto data = file.readVector();
return addFontFromMemory(data, fontSize, scalable, offset, glyphRange);
}
void setBold(bool enabled) {
if (enabled)
m_defaultConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold;
else
m_defaultConfig.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Bold;
}
void setItalic(bool enabled) {
if (enabled)
m_defaultConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique;
else
m_defaultConfig.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Oblique;
}
void setAntiAliasing(bool enabled) {
if (enabled)
m_defaultConfig.FontBuilderFlags &= ~(ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting);
else
m_defaultConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
}
void enableUnicodeCharacters(bool enabled) {
ImFontGlyphRangesBuilder glyphRangesBuilder;
{
constexpr static std::array<ImWchar, 3> controlCodeRange = { 0x0001, 0x001F, 0 };
constexpr static std::array<ImWchar, 3> extendedAsciiRange = { 0x007F, 0x00FF, 0 };
constexpr static std::array<ImWchar, 3> latinExtendedARange = { 0x0100, 0x017F, 0 };
glyphRangesBuilder.AddRanges(controlCodeRange.data());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesDefault());
glyphRangesBuilder.AddRanges(extendedAsciiRange.data());
glyphRangesBuilder.AddRanges(latinExtendedARange.data());
}
if (enabled) {
constexpr static ImWchar fullUnicodeRanges[] = {
0x0080, 0x00FF, // Latin-1 Supplement
0x0100, 0xFFEF, // Remaining BMP (excluding specials)
0x10000, 0x1FFFF, // Plane 1
0x20000, 0x2FFFF, // Plane 2
0 // null-terminated
};
glyphRangesBuilder.AddRanges(fullUnicodeRanges);
} else {
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesJapanese());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesChineseFull());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesCyrillic());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesKorean());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesThai());
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesVietnamese());
glyphRangesBuilder.AddText("⌘⌥⌃⇧⏎⇥⌫⇪");
}
m_glyphRange.clear();
glyphRangesBuilder.BuildRanges(&m_glyphRange);
}
bool build() const {
return m_fontAtlas->Build();
}
[[nodiscard]] ImFontAtlas* getAtlas() {
return m_fontAtlas;
}
float calculateFontDescend( FT_Library ft, const ImHexApi::Fonts::Font &font, float fontSize) const {
if (ft == nullptr) {
log::fatal("FreeType not initialized");
return 0.0f;
}
FT_Face face;
if (FT_New_Memory_Face(ft, reinterpret_cast<const FT_Byte *>(font.fontData.data()), font.fontData.size(), 0, &face) != 0) {
log::fatal("Failed to load face");
return 0.0f;
}
FT_Size_RequestRec req;
req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)(IM_ROUND(fontSize) * 64.0F);
req.horiResolution = 0;
req.vertResolution = 0;
FT_Request_Size(face, &req);
return face->size->metrics.descender / 64.0F;
}
void reset() {
m_fontData.clear();
m_glyphRange.clear();
m_fontSizes.clear();
m_fontConfigs.clear();
m_fontAtlas->Clear();
m_defaultConfig.MergeMode = false;
}
void updateFontScaling(float newScaling) {
for (int i = 0; i < m_fontAtlas->Sources.size(); i += 1) {
const auto &[scalable, fontSize] = m_fontSizes[i];
auto &configData = m_fontAtlas->Sources[i];
if (!scalable) {
configData.SizePixels = fontSize * std::floor(newScaling);
} else {
configData.SizePixels = fontSize * newScaling;
}
}
}
private:
ImFontAtlas* m_fontAtlas = nullptr;
std::vector<std::pair<bool, float>> m_fontSizes;
ImFontConfig m_defaultConfig;
std::list<ImFontConfig> m_fontConfigs;
ImVector<ImWchar> m_glyphRange;
std::list<std::vector<u8>> m_fontData;
};
}

View File

@ -4,6 +4,13 @@
#include <hex/api/content_registry.hpp>
namespace hex::fonts {
enum class AntialiasingType {
None,
Grayscale,
Lcd
};
class AntialiasPicker : public ContentRegistry::Settings::Widgets::DropDown {
public:
AntialiasPicker() : DropDown(
@ -59,7 +66,7 @@ namespace hex::fonts {
[[nodiscard]] float getFontSize() const;
[[nodiscard]] bool isBold() const;
[[nodiscard]] bool isItalic() const;
[[nodiscard]] const std::string antiAliasingType() const;
[[nodiscard]] AntialiasingType getAntialiasingType() const;
private:
bool drawPopup();

View File

@ -4,8 +4,8 @@
namespace hex::fonts {
const static auto Default = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.default"); };
const static auto HexEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.hex_editor"); };
const static auto CodeEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.code_editor"); };
[[nodiscard]] const ImHexApi::Fonts::Font& Default();
[[nodiscard]] const ImHexApi::Fonts::Font& HexEditor();
[[nodiscard]] const ImHexApi::Fonts::Font& CodeEditor();
}

View File

@ -1,263 +0,0 @@
#if defined(_MSC_VER)
#include <windows.h>
#endif
#include <imgui.h>
#include <imgui_internal.h>
#include <list>
#include <hex/api/imhex_api.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/freetype.hpp>
#include <wolv/utils/string.hpp>
#include <freetype/freetype.h>
#include "imgui_impl_opengl3_loader.h"
#include <font_atlas.hpp>
namespace hex::fonts {
bool BuildSubPixelAtlas(FontAtlas *fontAtlas) {
FT_Library ft = nullptr;
if (FT_Init_FreeType(&ft) != 0) {
log::fatal("Failed to initialize FreeType");
return false;
}
FT_Face face;
auto io = ImGui::GetIO();
io.Fonts = fontAtlas->getAtlas();
if (io.Fonts->Sources.Size <= 0) {
log::fatal("No font data found");
return false;
} else {
ImVector<ImS32> customRectIds;
std::map<ImS32, ft::Bitmap> customRectBitmaps;
for (const auto &config : io.Fonts->Sources) {
const auto &fontName = config.Name;
if (hex::equalsIgnoreCase(fontName, "nonscalable")) {
continue;
}
if (FT_New_Memory_Face(ft, static_cast<const FT_Byte *>(config.FontData), config.FontDataSize, 0, &face) != 0) {
log::fatal("Failed to load face");
return false;
}
FT_Size_RequestRec request;
request.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
request.width = 0;
request.height = (uint32_t)(IM_ROUND(config.SizePixels) * 64.0F);
request.horiResolution = 0;
request.vertResolution = 0;
FT_Request_Size(face, &request);
FT_UInt glyphIndex;
FT_ULong charCode = FT_Get_First_Char(face, &glyphIndex);
while (glyphIndex != 0) {
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_TARGET_LCD | FT_LOAD_TARGET_LIGHT | FT_LOAD_RENDER) != 0) {
IM_ASSERT(true && "Failed to load glyph");
return false;
}
const ft::Bitmap bitmap = ft::Bitmap(face->glyph->bitmap.width, face->glyph->bitmap.rows, face->glyph->bitmap.pitch, face->glyph->bitmap.buffer);
if (face->glyph->bitmap.width * face->glyph->bitmap.rows == 0) {
charCode = FT_Get_Next_Char(face, charCode, &glyphIndex);
continue;
}
const auto width = bitmap.getWidth() / 3.0F;
const auto height = bitmap.getHeight();
const auto slot = face->glyph;
const auto size = face->size;
auto offset = ImVec2(face->glyph->bitmap_left, -face->glyph->bitmap_top);
offset.x += config.GlyphOffset.x;
offset.y += size->metrics.ascender / 64.0F;
const ImS32 advance = slot->advance.x / 64.0F;
ImS32 rectId = io.Fonts->AddCustomRectFontGlyph(io.Fonts->Fonts[0], charCode, width, height, advance, offset);
customRectIds.push_back(rectId);
customRectBitmaps.insert(std::make_pair(rectId, bitmap));
charCode = FT_Get_Next_Char(face, charCode, &glyphIndex);
}
FT_Done_Face(face);
}
fontAtlas->getAtlas()->FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_SubPixel;
if (!fontAtlas->build())
return false;
ImU8 *textureColors = nullptr;
ImS32 textureWidth, textureHeight;
fontAtlas->getAtlas()->GetTexDataAsRGBA32(&textureColors, &textureWidth, &textureHeight);
auto *texturePixels = reinterpret_cast<ImU32 *>(textureColors);
for (auto rect_id: customRectIds) {
if (const ImFontAtlasCustomRect *rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
if (rect->X == 0xFFFF || rect->Y == 0xFFFF || !customRectBitmaps.contains(rect_id))
continue;
const auto &bitmap = customRectBitmaps.at(rect_id);
ImU32 imageWidth = bitmap.getWidth() / 3;
ImU32 imageHeight = bitmap.getHeight();
const auto &bitmapBuffer = bitmap.getData();
for (ImU32 y = 0; y < imageHeight; y++) {
ImU32 *pixel = texturePixels + (rect->Y + y) * textureWidth + (rect->X);
for (ImU32 x = 0; x < imageWidth; x++) {
const ImU8 *bitmapPixel = &bitmapBuffer[y * bitmap.getPitch() + 3 * x];
*pixel++ = ft::RGBA::addAlpha(*bitmapPixel, *(bitmapPixel + 1), *(bitmapPixel + 2));
}
}
}
}
for (i64 i = 0; i < textureWidth * textureHeight; i++) {
if (texturePixels[i] == 0x00FFFFFF) {
texturePixels[i] = 0x00000000;
}
}
if (ft != nullptr) {
FT_Done_FreeType(ft);
ft = nullptr;
}
}
return true;
}
bool buildFontAtlas(FontAtlas *fontAtlas, std::fs::path fontPath, bool pixelPerfectFont, float fontSize, bool loadUnicodeCharacters, bool bold, bool italic,const std::string &antiAliasType) {
if (fontAtlas == nullptr) {
return false;
}
bool antialias = antiAliasType == "grayscale";
bool monochrome = antiAliasType == "none";
FT_Library ft = nullptr;
if (FT_Init_FreeType(&ft) != 0) {
log::fatal("Failed to initialize FreeType");
return false;
}
fontAtlas->reset();
u32 fontIndex = 0;
auto io = ImGui::GetIO();
io.Fonts = fontAtlas->getAtlas();
// Check if Unicode support is enabled in the settings and that the user doesn't use the No GPU version on Windows
// The Mesa3D software renderer on Windows identifies itself as "VMware, Inc."
bool shouldLoadUnicode = ContentRegistry::Settings::read<bool>("hex.fonts.setting.font", "hex.fonts.setting.font.load_all_unicode_chars", false) && ImHexApi::System::getGPUVendor() != "VMware, Inc.";
if (!loadUnicodeCharacters)
shouldLoadUnicode = false;
fontAtlas->enableUnicodeCharacters(shouldLoadUnicode);
// If a custom font is set in the settings, load the rest of the settings as well
if (!pixelPerfectFont) {
fontAtlas->setBold(bold);
fontAtlas->setItalic(italic);
if (antialias || monochrome)
fontAtlas->setAntiAliasing(antialias);
} else {
fontPath.clear();
}
// Try to load the custom font if one was set
std::optional<Font> defaultFont;
if (!fontPath.empty()) {
defaultFont = fontAtlas->addFontFromFile(fontPath, fontSize, true, ImVec2());
std::string defaultFontName = defaultFont.has_value() ? fontPath.filename().string() : "Custom Font";
memcpy(fontAtlas->getAtlas()->Sources[fontIndex].Name, defaultFontName.c_str(), defaultFontName.size());
fontIndex += 1;
if ((antialias || monochrome) && !fontAtlas->build()) {
log::error("Failed to load custom font '{}'! Falling back to default font", wolv::util::toUTF8String(fontPath));
defaultFont.reset();
}
}
// If there's no custom font set, or it failed to load, fall back to the default font
if (!defaultFont.has_value()) {
if (pixelPerfectFont) {
defaultFont = fontAtlas->addDefaultFont();
std::string defaultFontName = "Proggy Clean";
memcpy(fontAtlas->getAtlas()->Sources[fontIndex].Name, defaultFontName.c_str(), defaultFontName.size());
fontIndex += 1;
} else {
defaultFont = fontAtlas->addFontFromRomFs("fonts/JetBrainsMono.ttf", fontSize, true, ImVec2());
std::string defaultFontName = "JetBrains Mono";
memcpy(fontAtlas->getAtlas()->Sources[fontIndex].Name, defaultFontName.c_str(), defaultFontName.size());
fontIndex += 1;
}
}
// Add all the built-in fonts
{
static std::list<ImVector<ImWchar>> glyphRanges;
glyphRanges.clear();
for (auto &font : ImHexApi::Fonts::impl::getFonts()) {
// Construct the glyph range for the font
ImVector<ImWchar> glyphRange;
if (!font.glyphRanges.empty()) {
for (const auto &range : font.glyphRanges) {
glyphRange.push_back(range.begin);
glyphRange.push_back(range.end);
}
glyphRange.push_back(0x00);
}
glyphRanges.push_back(glyphRange);
// Calculate the glyph offset for the font
// Load the font
if (font.defaultSize.has_value())
fontSize = font.defaultSize.value() * ImHexApi::System::getBackingScaleFactor();
ImVec2 offset = { font.offset.x, font.offset.y };
bool scalable = font.scalable.value_or(true);
if (scalable) {
offset.y += ImCeil(3_scaled);
}
fontAtlas->addFontFromMemory(font.fontData, fontSize, !font.defaultSize.has_value(), offset, glyphRanges.back());
if (!scalable) {
std::string fontName = "NonScalable";
auto nameSize = fontName.size();
memcpy(fontAtlas->getAtlas()->Sources[fontIndex].Name, fontName.c_str(), nameSize);
} else {
auto nameSize = font.name.size();
memcpy(fontAtlas->getAtlas()->Sources[fontIndex].Name, font.name.c_str(), nameSize);
}
fontIndex += 1;
}
}
if (ft != nullptr) {
FT_Done_FreeType(ft);
ft = nullptr;
}
if (antialias || monochrome) {
if (!fontAtlas->build()) {
log::fatal("Failed to load font!");
return false;
}
return true;
} else
return BuildSubPixelAtlas(fontAtlas);
}
}

View File

@ -198,10 +198,20 @@ namespace hex::fonts {
return m_italic.isChecked();
}
[[nodiscard]] const std::string FontSelector::antiAliasingType() const {
[[nodiscard]] AntialiasingType FontSelector::getAntialiasingType() const {
if (isPixelPerfectFont())
return "none";
return m_antiAliased.getValue();
return AntialiasingType::None;
auto value = m_antiAliased.getValue();
if (value == "none")
return AntialiasingType::None;
else if (value == "grayscale")
return AntialiasingType::Grayscale;
else if (value == "subpixel")
return AntialiasingType::Lcd;
else
return AntialiasingType::Grayscale;
}

View File

@ -1,5 +1,7 @@
#include <hex/api/imhex_api.hpp>
#include <fonts/fonts.hpp>
#include <romfs/romfs.hpp>
#include <hex/helpers/utils.hpp>
@ -9,6 +11,13 @@
namespace hex::fonts {
static auto s_defaultFont = ImHexApi::Fonts::Font("hex.fonts.font.default");
const ImHexApi::Fonts::Font& Default() { return s_defaultFont; }
static auto s_hexEditorFont = ImHexApi::Fonts::Font("hex.fonts.font.hex_editor");
const ImHexApi::Fonts::Font& HexEditor() { return s_hexEditorFont; }
static auto s_codeEditorFont = ImHexApi::Fonts::Font("hex.fonts.font.code_editor");
const ImHexApi::Fonts::Font& CodeEditor() { return s_codeEditorFont; }
void registerFonts() {
using namespace ImHexApi::Fonts;
@ -24,7 +33,7 @@ namespace hex::fonts {
{
{ ICON_MIN_VS, ICON_MAX_VS }
},
{ -1_scaled, -1_scaled });
{ -1, -2 });
ImHexApi::Fonts::loadFont("Unifont", romfs::get("fonts/unifont.otf").span<u8>(), { }, {}, 0, false, 16);
}

View File

@ -5,55 +5,81 @@
#include <hex/helpers/logger.hpp>
#include <romfs/romfs.hpp>
#include <font_atlas.hpp>
#include <font_settings.hpp>
#include <imgui_freetype.h>
#include <fonts/fonts.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/api/events/events_lifecycle.hpp>
namespace hex::fonts {
void registerFonts();
bool buildFontAtlas(FontAtlas *fontAtlas, std::fs::path fontPath, bool pixelPerfectFont, float fontSize, bool loadUnicodeCharacters, bool bold, bool italic,const std::string &antialias);
static AutoReset<std::map<UnlocalizedString, std::unique_ptr<FontAtlas>>> s_fontAtlases;
void loadFont(const ContentRegistry::Settings::Widgets::Widget &widget, const UnlocalizedString &name, ImFont **font, float scale) {
void loadFont(const ContentRegistry::Settings::Widgets::Widget &widget, const UnlocalizedString &name, ImFont **imguiFont) {
const auto &settings = static_cast<const FontSelector&>(widget);
auto atlas = std::make_unique<FontAtlas>();
const bool atlasBuilt = buildFontAtlas(
atlas.get(),
settings.getFontPath(),
settings.isPixelPerfectFont(),
settings.getFontSize() * scale,
true,
settings.isBold(),
settings.isItalic(),
settings.antiAliasingType()
);
auto atlas = ImGui::GetIO().Fonts;
if (!atlasBuilt) {
buildFontAtlas(
atlas.get(),
"",
false,
settings.getFontSize() * scale,
false,
settings.isBold(),
settings.isItalic(),
settings.antiAliasingType()
);
{
auto &font = *imguiFont;
log::error("Failed to load font {}! Reverting back to default font!", name.get());
if (font != nullptr) {
atlas->RemoveFont(font);
font = nullptr;
}
}
*font = atlas->getAtlas()->Fonts[0];
ImFontConfig config;
config.MergeMode = false;
config.FontDataOwnedByAtlas = false;
config.SizePixels = settings.getFontSize();
(*s_fontAtlases)[name] = std::move(atlas);
std::memcpy(config.Name, name.get().c_str(), std::min(name.get().size(), sizeof(config.Name) - 1));
if (settings.isBold())
config.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_Bold;
if (settings.isItalic())
config.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_Oblique;
switch (settings.getAntialiasingType()) {
case AntialiasingType::None:
config.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_Monochrome | ImGuiFreeTypeLoaderFlags_MonoHinting;
break;
case AntialiasingType::Grayscale:
break;
case AntialiasingType::Lcd:
config.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_SubPixel;
break;
}
{
const auto fontPath = settings.getFontPath();
if (!fontPath.empty())
*imguiFont = atlas->AddFontFromFileTTF(fontPath.string().c_str(), 0.0F, &config);
if (*imguiFont == nullptr) {
if (settings.isPixelPerfectFont())
*imguiFont = atlas->AddFontDefault();
else {
static auto jetbrainsFont = romfs::get("fonts/JetBrainsMono.ttf");
*imguiFont = atlas->AddFontFromMemoryTTF(const_cast<u8 *>(jetbrainsFont.data<u8>()), jetbrainsFont.size(), 0.0F, &config);
if (*imguiFont == nullptr) {
log::error("Failed to load font '{}', using default font instead", name.get());
*imguiFont = atlas->AddFontDefault();
}
}
}
}
config.MergeMode = true;
for (auto &extraFont : ImHexApi::Fonts::impl::getMergeFonts()) {
config.GlyphOffset = { extraFont.offset.x, -extraFont.offset.y };
config.GlyphOffset *= ImHexApi::System::getGlobalScale();
atlas->AddFontFromMemoryTTF(const_cast<u8 *>(extraFont.fontData.data()), extraFont.fontData.size(), 0.0F, &config);
}
}
bool setupFonts() {
ContentRegistry::Settings::add<ContentRegistry::Settings::Widgets::Checkbox>("hex.fonts.setting.font", "hex.fonts.setting.font.glyphs", "hex.fonts.setting.font.load_all_unicode_chars", false).requiresRestart();
for (auto &[name, font] : ImHexApi::Fonts::impl::getFontDefinitions()) {
auto &widget = addFontSettingsWidget(name)
.setChangedCallback([name, &font, firstLoad = true](auto &widget) mutable {
@ -62,15 +88,12 @@ namespace hex::fonts {
return;
}
TaskManager::doLater([&name, &font, &widget] {
loadFont(widget, name, &font, ImHexApi::System::getGlobalScale() * ImHexApi::System::getBackingScaleFactor());
TaskManager::doLater([name, &font, &widget] {
loadFont(widget, name, &font);
});
});
loadFont(widget.getWidget(), name, &font, ImHexApi::System::getGlobalScale() * ImHexApi::System::getBackingScaleFactor());
EventDPIChanged::subscribe(font, [&widget, name, &font](float, float newScaling) {
loadFont(widget.getWidget(), name, &font, ImHexApi::System::getGlobalScale() * newScaling);
});
loadFont(widget.getWidget(), name, &font);
}
return true;
@ -87,4 +110,9 @@ IMHEX_LIBRARY_SETUP("Fonts") {
hex::ImHexApi::Fonts::registerFont("hex.fonts.font.code_editor");
hex::fonts::registerFonts();
hex::EventImHexStartupFinished::subscribe([] {
hex::fonts::setupFonts();
hex::ImHexApi::Fonts::setDefaultFont(hex::fonts::Default());
});
}

View File

@ -1291,9 +1291,9 @@ namespace hex::ui {
if (tableSize.y <= 0)
tableSize.y = height;
ImGui::PushFont(fonts::HexEditor());
fonts::HexEditor().push();
this->drawEditor(tableSize);
ImGui::PopFont();
fonts::HexEditor().pop();
if (tableSize.y > 0)
this->drawFooter(footerSize);

View File

@ -462,13 +462,13 @@ namespace hex::ui {
using enum TreeStyle;
default:
case Default:
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_OpenOnArrow);
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_OpenOnArrow);
break;
case AutoExpanded:
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnArrow);
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnArrow);
break;
case Flattened:
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
retVal = ImGui::TreeNodeEx("##TreeNode", ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
break;
}
ImGui::PopStyleVar();
@ -1086,7 +1086,7 @@ namespace hex::ui {
ImGui::TableNextColumn();
chunkOpen = highlightWhenSelected(startOffset, ((endOffset + endSize) - startOffset) - 1, [&]{
return ImGui::TreeNodeEx(hex::format("{0}[{1} ... {2}]", m_treeStyle == TreeStyle::Flattened ? this->getDisplayName(pattern).c_str() : "", i, endIndex - 1).c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
return ImGui::TreeNodeEx(hex::format("{0}[{1} ... {2}]", m_treeStyle == TreeStyle::Flattened ? this->getDisplayName(pattern).c_str() : "", i, endIndex - 1).c_str(), ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanFullWidth);
});
ImGui::TableNextColumn();
@ -1342,7 +1342,7 @@ namespace hex::ui {
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::PushID(id);
if (ImGui::TreeNodeEx("hex.ui.pattern_drawer.favorites"_lang, ImGuiTreeNodeFlags_SpanFullWidth)) {
if (ImGui::TreeNodeEx("hex.ui.pattern_drawer.favorites"_lang, ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanFullWidth)) {
for (auto &[path, pattern] : m_favorites) {
if (pattern == nullptr)
continue;
@ -1369,7 +1369,7 @@ namespace hex::ui {
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::PushID(id);
if (ImGui::TreeNodeEx(groupName.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) {
if (ImGui::TreeNodeEx(groupName.c_str(), ImGuiTreeNodeFlags_DrawLinesToNodes | ImGuiTreeNodeFlags_SpanFullWidth)) {
for (auto &groupPattern: groupPatterns) {
if (groupPattern == nullptr)
continue;