Skip to content
18 changes: 0 additions & 18 deletions backends/imgui_impl_glfw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
bool has_viewports = false;
#ifndef __EMSCRIPTEN__
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
Expand Down Expand Up @@ -1068,11 +1064,6 @@ static void ImGui_ImplGlfw_UpdateMonitors()
// - 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_WAYLAND
if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window))
if (bd->IsWayland)
return 1.0f;
#endif
#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__))
float x_scale, y_scale;
glfwGetWindowContentScale(window, &x_scale, &y_scale);
Expand All @@ -1085,10 +1076,6 @@ float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window)

float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor)
{
#if GLFW_HAS_WAYLAND
if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor.
return 1.0f;
#endif
#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__))
float x_scale, y_scale;
glfwGetMonitorContentScale(monitor, &x_scale, &y_scale);
Expand All @@ -1107,11 +1094,6 @@ static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window,
glfwGetFramebufferSize(window, &display_w, &display_h);
float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f;
float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f;
#if GLFW_HAS_WAYLAND
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);
if (!bd->IsWayland)
fb_scale_x = fb_scale_y = 1.0f;
#endif
if (out_size != nullptr)
*out_size = ImVec2((float)w, (float)h);
if (out_framebuffer_scale != nullptr)
Expand Down
41 changes: 38 additions & 3 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,7 @@ ImGuiIO::ImGuiIO()
ConfigViewportsNoTaskBarIcon = false;
ConfigViewportsNoDecoration = true;
ConfigViewportsNoDefaultParent = true;
ConfigViewportsNoFloatingWindows = false;
ConfigViewportsPlatformFocusSetsImGuiFocus = true;

// Miscellaneous options
Expand Down Expand Up @@ -5418,10 +5419,28 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
{
SetWindowPos(moving_window, pos, ImGuiCond_Always);
// Use the actual window location returned by the window system if possible,
// since windows can snap to borders and things like that which result in a final
// window location that is not exactly the same as what was requested.
ImVec2 actual = pos;
if (moving_window->Viewport && moving_window->ViewportOwned
&& g.PlatformIO.Platform_SetWindowPos && g.PlatformIO.Platform_GetWindowPos)
{
g.PlatformIO.Platform_SetWindowPos(moving_window->Viewport, pos);
actual = g.PlatformIO.Platform_GetWindowPos(moving_window->Viewport);
}
SetWindowPos(moving_window, actual, ImGuiCond_Always);
if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
{
moving_window->Viewport->Pos = pos;
// Use moving_window->Pos (which SetWindowPos truncated via
// ImTrunc) rather than raw `actual`. If viewport->Pos kept
// the untruncated value, a later sync site would overwrite it
// with window->Pos (truncated), and UpdatePlatformWindows
// would see the mismatch against LastPlatformPos and push the
// truncated position to the OS — which maps to a different
// OS-pixel than what we just set, causing oscillation.
moving_window->Viewport->Pos = moving_window->Pos;
moving_window->Viewport->LastPlatformPos = moving_window->Pos;
moving_window->Viewport->UpdateWorkRect();
}
}
Expand Down Expand Up @@ -17838,6 +17857,18 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window)
SetWindowViewport(window, main_viewport);
return;
}

// When ConfigViewportsNoFloatingWindows is set, only popups/tooltips/menus
// may create their own viewport. All other windows stay in the main viewport.
if (g.IO.ConfigViewportsNoFloatingWindows)
{
if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu)) == 0)
{
SetWindowViewport(window, main_viewport);
return;
}
}

window->ViewportOwned = false;

// Appearing popups reset their viewport so they can inherit again
Expand Down Expand Up @@ -21962,7 +21993,11 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
g.LastItemData.ID = window->MoveId;
window = window->RootWindowDockTree;
IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit
// LuckyEngine: mirrors the custom tab_padding_y in TabItemCalcSize - without this,
// clicks in the lower half of a tall tab fall outside GetFrameHeight() and tear-away
// drags never turn into a drag-drop source (no docking overlay until re-grab).
const float tab_drag_height = ImMax(GetFrameHeight(), g.FontSize + 8.0f * 2.0f);
bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, tab_drag_height).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit
ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess;
if (is_drag_docking && BeginDragDropSource(drag_drop_flags))
{
Expand Down
1 change: 1 addition & 0 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -2521,6 +2521,7 @@ struct ImGuiIO
bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it.
bool ConfigViewportsNoDecoration; // = true // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size).
bool ConfigViewportsNoDefaultParent; // = true // When false: set secondary viewports' ParentViewportId to main viewport ID by default. Expects the platform backend to setup a parent/child relationship between the OS windows based on this value. Some backend may ignore this. Set to true if you want viewports to automatically be parent of main viewport, otherwise all viewports will be top-level OS windows.
bool ConfigViewportsNoFloatingWindows;// = false // When set, only popups, tooltips and menus may create their own viewport. Undocked/floating windows stay inside the main viewport. Useful on platforms (e.g. Wayland) where secondary OS windows for docked panels are problematic.
bool ConfigViewportsPlatformFocusSetsImGuiFocus;//= true // When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).

// DPI/Scaling options
Expand Down
65 changes: 60 additions & 5 deletions misc/freetype/imgui_freetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,9 @@ struct LunasvgPortState
FT_Error err = FT_Err_Ok;
lunasvg::Matrix matrix;
std::unique_ptr<lunasvg::Document> svg = nullptr;
#if LUNASVG_VERSION_MAJOR >= 3
lunasvg::Element glyphElement;
#endif
};

static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
Expand All @@ -642,11 +645,20 @@ static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
if (state->err != FT_Err_Ok)
return state->err;

// rows is height, pitch (or stride) equals to width * sizeof(int32)
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
#if LUNASVG_VERSION_MAJOR >= 3
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
if (state->glyphElement.isElement())
{
lunasvg::Bitmap rendered = state->glyphElement.renderToBitmap(slot->bitmap.width, slot->bitmap.rows);
if (rendered.data())
memcpy(slot->bitmap.buffer, rendered.data(), (size_t)slot->bitmap.rows * slot->bitmap.pitch);
}
else
{
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
state->svg->render(bitmap, state->matrix);
}
#else
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
#endif
Expand All @@ -673,10 +685,26 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_
}

#if LUNASVG_VERSION_MAJOR >= 3
lunasvg::Box box = state->svg->boundingBox();
// Multi-glyph SVG documents (e.g. NotoColorEmoji) store all glyphs in one SVG
// with elements identified by "glyph<ID>". Find the specific glyph element.
char glyph_id_str[64];
ImFormatString(glyph_id_str, sizeof(glyph_id_str), "glyph%u", slot->glyph_index);
state->glyphElement = state->svg->getElementById(glyph_id_str);

lunasvg::Box box;
if (state->glyphElement.isElement())
box = state->glyphElement.getBoundingBox();
else
box = state->svg->boundingBox();
#else
lunasvg::Box box = state->svg->box();
#endif
if (box.w == 0 || box.h == 0)
{
state->err = FT_Err_Invalid_SVG_Document;
return state->err;
}

double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
double xx = (double)document->transform.xx / (1 << 16);
double xy = -(double)document->transform.xy / (1 << 16);
Expand All @@ -686,7 +714,34 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_
double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;

#if LUNASVG_VERSION_MAJOR >= 3
// Scale, transform and pre-translate the matrix for the rendering step
if (state->glyphElement.isElement())
{
// renderToBitmap handles positioning internally, so we just need pixel dimensions
double bitmapW = box.w * scale;
double bitmapH = box.h * scale;

slot->bitmap.width = (unsigned int)(ImCeil((float)bitmapW));
slot->bitmap.rows = (unsigned int)(ImCeil((float)bitmapH));
slot->bitmap.pitch = slot->bitmap.width * 4;
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
slot->bitmap_left = 0;
slot->bitmap_top = (FT_Int)slot->bitmap.rows;

slot->metrics.width = FT_Pos(IM_ROUND(bitmapW * 64.0));
slot->metrics.height = FT_Pos(IM_ROUND(bitmapH * 64.0));
slot->metrics.horiBearingX = 0;
slot->metrics.horiBearingY = FT_Pos(IM_ROUND(bitmapH * 64.0));
slot->metrics.vertBearingX = slot->metrics.horiBearingX / 2 - slot->metrics.horiAdvance / 2;
slot->metrics.vertBearingY = (slot->metrics.vertAdvance - slot->metrics.height) / 2;

if (slot->metrics.vertAdvance == 0)
slot->metrics.vertAdvance = FT_Pos(bitmapH * 1.2 * 64.0);

state->err = FT_Err_Ok;
return state->err;
}

// Fallback: single-glyph SVG documents — use standard matrix-based rendering
state->matrix = lunasvg::Matrix::translated(-box.x, -box.y);
state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0));
state->matrix.scale(scale, scale);
Expand Down