refactor(audit): de-duplicate the main.cpp backdrop draw + DPI transition
Two render-loop de-duplications, zero behavior change (verified byte-equivalent on both the GL and DX11 backends via the mingw Windows cross-build): - drawWindowBackdrop(app, bgDL, p0, p1, backdrop_active, lowSpec): the ~80-line gradient/texture/acrylic-capture/WindowBg-alpha block was copy-pasted in the main loop (with low-spec guards) and the resize-watcher lambda (without them). Extracted once; the main loop calls it with lowSpec=isLowSpecMode() and the resize watcher with lowSpec=false — each reproducing its CURRENT behavior exactly (pure dedup, no change). The intentional differences around it (resize's immediate present(0) vs the main loop's vsync, the main loop's try/catch, the BeginFrame low-spec guard) are left untouched. - handleDisplayScaleChange(window, newScale, savedSizeForScale, lastKnownW/H, dpiTargetW/H, dpiResizeRetries, dpiResizePending): the SDL display-scale-change handler was duplicated ~verbatim in the idle-wait and poll event paths (differing only in debug-log text). Moved the body into one helper called from both; the #ifdef DRAGONX_USE_DX11 g_borderlessDpi and #ifdef __APPLE__ scale normalization are preserved. Deliberately did NOT unify the full frame/present sequence — the resize path's immediate present and lack of try/catch are intentional. Full-node (GL) + Lite build clean; Windows/DX11 cross-build compiles main.cpp clean; ctest 1/1; hygiene clean. NEEDS RUNTIME VERIFICATION: a live window drag-resize (backdrop draws, no flicker) and a monitor DPI-scale change (window resizes + fonts reload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
467
src/main.cpp
467
src/main.cpp
@@ -382,6 +382,184 @@ static std::string findPaymentURI(int argc, char* argv[])
|
||||
return "";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Shared render-loop helpers (used by both the main loop and the
|
||||
// resize event-watcher). Extracted verbatim to de-duplicate the
|
||||
// per-frame code — same calls, same order, same #ifdef branches.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// Draw the window backdrop (base gradient / texture tint / acrylic
|
||||
// capture callback / WindowBg alpha). The main loop passes lowSpec =
|
||||
// isLowSpecMode() so low-spec skips texture sampling and the capture
|
||||
// callback; the resize watcher passes lowSpec = false to reproduce its
|
||||
// current always-sample-and-capture behavior.
|
||||
static void drawWindowBackdrop(dragonx::App& app, ImDrawList* bgDL, ImVec2 p0, ImVec2 p1,
|
||||
bool backdrop_active, bool lowSpec)
|
||||
{
|
||||
// Read the current gradient texture from the app each frame
|
||||
// so hot-reloaded / skin-changed images are picked up immediately.
|
||||
ImTextureID curGradTex = app.getGradientTexture();
|
||||
|
||||
// Window opacity: scale only background layer alpha so the
|
||||
// desktop shows through while UI stays fully opaque.
|
||||
float winOpacity = app.settings() ? app.settings()->getWindowOpacity() : 1.0f;
|
||||
|
||||
// Scale a single color's alpha channel by window opacity
|
||||
auto scaleAlpha = [&](ImU32 col) -> ImU32 {
|
||||
uint8_t a = (col >> IM_COL32_A_SHIFT) & 0xFF;
|
||||
a = (uint8_t)(a * winOpacity);
|
||||
return (col & ~(0xFFu << IM_COL32_A_SHIFT)) | ((ImU32)a << IM_COL32_A_SHIFT);
|
||||
};
|
||||
|
||||
if (curGradTex != 0) {
|
||||
// Base color gradient underneath the texture — read from ui.toml
|
||||
const auto& S2 = dragonx::ui::schema::UI();
|
||||
ImU32 baseTop = S2.resolveColor(S2.drawElement("backdrop", "base-color-top").color, IM_COL32(18,28,65,200));
|
||||
ImU32 baseBottom = S2.resolveColor(S2.drawElement("backdrop", "base-color-bottom").color, IM_COL32(8,12,35,200));
|
||||
|
||||
if (lowSpec) {
|
||||
// Low-spec: skip texture sampling — just draw the base gradient.
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, scaleAlpha(baseTop), scaleAlpha(baseTop),
|
||||
scaleAlpha(baseBottom), scaleAlpha(baseBottom));
|
||||
} else {
|
||||
int tintAlpha = (int)S2.drawElement("backdrop", "texture-tint-alpha").size;
|
||||
if (tintAlpha <= 0) tintAlpha = 140;
|
||||
|
||||
// Scale background gradient + texture tint alpha by
|
||||
// window opacity — only affects the backdrop, not UI.
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, scaleAlpha(baseTop), scaleAlpha(baseTop),
|
||||
scaleAlpha(baseBottom), scaleAlpha(baseBottom));
|
||||
int scaledTintAlpha = (int)(tintAlpha * winOpacity);
|
||||
ImU32 tintCol = IM_COL32(255, 255, 255, scaledTintAlpha);
|
||||
bgDL->AddImage(curGradTex, p0, p1, ImVec2(0, 0), ImVec2(1, 1), tintCol);
|
||||
}
|
||||
} else if (backdrop_active) {
|
||||
// Programmatic gradient tint (fallback when texture unavailable)
|
||||
const auto& S = dragonx::ui::schema::UI();
|
||||
auto bde = [&](const char* key, float fb) {
|
||||
float v = S.drawElement("backdrop", key).size;
|
||||
return v >= 0 ? v : fb;
|
||||
};
|
||||
ImU32 colTop = IM_COL32((int)bde("gradient-top-r",8), (int)bde("gradient-top-g",12),
|
||||
(int)bde("gradient-top-b",28), (int)(bde("gradient-top-a",80) * winOpacity));
|
||||
ImU32 colBottom = IM_COL32((int)bde("gradient-bottom-r",6), (int)bde("gradient-bottom-g",8),
|
||||
(int)bde("gradient-bottom-b",18), (int)(bde("gradient-bottom-a",60) * winOpacity));
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, colTop, colTop, colBottom, colBottom);
|
||||
}
|
||||
|
||||
// Insert acrylic capture callback BEFORE the noise overlay
|
||||
// so the noise grain is not captured and blurred into the
|
||||
// glass cards (the noise should only be a visual overlay).
|
||||
// Skip in low-spec mode — acrylic is disabled so the FBO
|
||||
// capture/blur callback is unnecessary GPU work.
|
||||
if (!lowSpec) {
|
||||
auto captureCb = dragonx::ui::effects::ImGuiAcrylic::GetBackgroundCaptureCallback();
|
||||
if (captureCb) {
|
||||
bgDL->AddCallback(captureCb, nullptr);
|
||||
bgDL->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// WindowBg alpha stays at its base theme value — NOT scaled
|
||||
// by window opacity. Only the background gradient/texture
|
||||
// layers fade so UI elements remain fully readable.
|
||||
if (backdrop_active || curGradTex != 0) {
|
||||
const auto& S3 = dragonx::ui::schema::UI();
|
||||
float baseBgAlpha = S3.drawElement("backdrop", "background-alpha").sizeOr(0.40f);
|
||||
ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = baseBgAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: save the old-scale size,
|
||||
// compute + clamp the new physical size, arm the deferred resize retry,
|
||||
// and rebuild fonts / reset ImGui style at the new DPI scale. newScale
|
||||
// is pre-normalized by the caller (<=0 -> 1.0f, and macOS -> 1.0f).
|
||||
static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
||||
std::map<int,std::pair<int,int>>& savedSizeForScale,
|
||||
int& lastKnownW, int& lastKnownH,
|
||||
int& dpiTargetW, int& dpiTargetH,
|
||||
int& dpiResizeRetries, bool& dpiResizePending)
|
||||
{
|
||||
auto& typo = dragonx::ui::material::Typography::instance();
|
||||
float oldScale = typo.getDpiScale();
|
||||
if (std::abs(newScale - oldScale) > 0.01f) {
|
||||
float ratio = newScale / oldScale;
|
||||
DEBUG_LOGF("Display scale changed: %.2f -> %.2f (ratio %.2f)\n",
|
||||
oldScale, newScale, ratio);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
#ifdef DRAGONX_USE_DX11
|
||||
g_borderlessDpi = newScale;
|
||||
#endif
|
||||
|
||||
// Save the last known size (before auto-resize) under
|
||||
// the OLD scale. We use lastKnownW/H instead of
|
||||
// SDL_GetWindowSize() because by now WM_DPICHANGED
|
||||
// may have already auto-resized the window.
|
||||
{
|
||||
int oldPct = (int)lroundf(oldScale * 100.0f);
|
||||
savedSizeForScale[oldPct] = {lastKnownW, lastKnownH};
|
||||
DEBUG_LOGF(" Saved %dx%d for scale %d%%\n",
|
||||
lastKnownW, lastKnownH, oldPct);
|
||||
}
|
||||
|
||||
// Compute new physical size: if we've been to this
|
||||
// scale before, restore that exact size; otherwise
|
||||
// proportionally scale from the current size.
|
||||
int newPct = (int)lroundf(newScale * 100.0f);
|
||||
int newW, newH;
|
||||
auto it = savedSizeForScale.find(newPct);
|
||||
if (it != savedSizeForScale.end()) {
|
||||
newW = it->second.first;
|
||||
newH = it->second.second;
|
||||
} else {
|
||||
// Use lastKnownW/H (pre-auto-resize) instead of
|
||||
// SDL_GetWindowSize() which already reflects the
|
||||
// WM_DPICHANGED auto-resize.
|
||||
newW = (int)lroundf((float)lastKnownW * newScale / oldScale);
|
||||
newH = (int)lroundf((float)lastKnownH * newScale / oldScale);
|
||||
}
|
||||
|
||||
// Clamp to the target display's work area so the
|
||||
// window doesn't overflow a smaller/higher-density monitor.
|
||||
SDL_DisplayID did = SDL_GetDisplayForWindow(window);
|
||||
if (did) {
|
||||
SDL_Rect usable;
|
||||
if (SDL_GetDisplayUsableBounds(did, &usable)) {
|
||||
newW = std::min(newW, usable.w);
|
||||
newH = std::min(newH, usable.h);
|
||||
}
|
||||
}
|
||||
|
||||
dpiResizePending = true;
|
||||
SDL_SetWindowSize(window, newW, newH);
|
||||
lastKnownW = newW;
|
||||
lastKnownH = newH;
|
||||
// Arm deferred retry — the SetWindowSize above may
|
||||
// not stick if the user is mid-drag (modal loop).
|
||||
dpiTargetW = newW;
|
||||
dpiTargetH = newH;
|
||||
dpiResizeRetries = 30; // retry for ~30 frames
|
||||
SDL_SetWindowMinimumSize(window,
|
||||
(int)(1024 * newScale),
|
||||
(int)(720 * newScale));
|
||||
DEBUG_LOGF(" Window resized: %dx%d (scale %.2f, pct %d)\n",
|
||||
newW, newH, newScale, newPct);
|
||||
|
||||
// 2) Rebuild font atlas at the new DPI scale
|
||||
typo.reload(io, newScale);
|
||||
|
||||
// 3) Reset style → reapply base theme → scale by new DPI
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
dragonx::ui::schema::UISchema::instance().reapplyColorsToImGui();
|
||||
if (newScale > 1.01f) {
|
||||
ImGui::GetStyle().ScaleAllSizes(newScale);
|
||||
}
|
||||
|
||||
DEBUG_LOGF(" DPI transition complete\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Ensure ObsidianDragon config directory exists early (before any file I/O)
|
||||
@@ -941,59 +1119,10 @@ int main(int argc, char* argv[])
|
||||
ImDrawList* bgDL = ImGui::GetBackgroundDrawList();
|
||||
ImVec2 p0 = vp->Pos;
|
||||
ImVec2 p1 = ImVec2(vp->Pos.x + vp->Size.x, vp->Pos.y + vp->Size.y);
|
||||
ImTextureID curGradTex2 = ctx->app->getGradientTexture();
|
||||
float winOp2 = ctx->app->settings() ? ctx->app->settings()->getWindowOpacity() : 1.0f;
|
||||
|
||||
// Scale only background layer alpha by window opacity so the
|
||||
// desktop shows through while UI elements stay fully opaque.
|
||||
auto scaleA2 = [&](ImU32 col) -> ImU32 {
|
||||
uint8_t a = (col >> IM_COL32_A_SHIFT) & 0xFF;
|
||||
a = (uint8_t)(a * winOp2);
|
||||
return (col & ~(0xFFu << IM_COL32_A_SHIFT)) | ((ImU32)a << IM_COL32_A_SHIFT);
|
||||
};
|
||||
|
||||
if (curGradTex2 != 0) {
|
||||
const auto& S1 = dragonx::ui::schema::UI();
|
||||
ImU32 baseTop1 = S1.resolveColor(S1.drawElement("backdrop", "base-color-top").color, IM_COL32(18,28,65,200));
|
||||
ImU32 baseBottom1 = S1.resolveColor(S1.drawElement("backdrop", "base-color-bottom").color, IM_COL32(8,12,35,200));
|
||||
int tintAlpha1 = (int)S1.drawElement("backdrop", "texture-tint-alpha").size;
|
||||
if (tintAlpha1 <= 0) tintAlpha1 = 140;
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, scaleA2(baseTop1), scaleA2(baseTop1),
|
||||
scaleA2(baseBottom1), scaleA2(baseBottom1));
|
||||
int scaledTA1 = (int)(tintAlpha1 * winOp2);
|
||||
ImU32 tintCol1 = IM_COL32(255, 255, 255, scaledTA1);
|
||||
bgDL->AddImage(curGradTex2, p0, p1, ImVec2(0, 0), ImVec2(1, 1), tintCol1);
|
||||
} else if (ctx->backdrop_active) {
|
||||
const auto& S = dragonx::ui::schema::UI();
|
||||
auto bde = [&](const char* key, float fb) {
|
||||
float v = S.drawElement("backdrop", key).size;
|
||||
return v >= 0 ? v : fb;
|
||||
};
|
||||
ImU32 colTop = IM_COL32((int)bde("gradient-top-r",8), (int)bde("gradient-top-g",12),
|
||||
(int)bde("gradient-top-b",28), (int)(bde("gradient-top-a",80) * winOp2));
|
||||
ImU32 colBottom = IM_COL32((int)bde("gradient-bottom-r",6), (int)bde("gradient-bottom-g",8),
|
||||
(int)bde("gradient-bottom-b",18), (int)(bde("gradient-bottom-a",60) * winOp2));
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, colTop, colTop, colBottom, colBottom);
|
||||
}
|
||||
// Acrylic capture callback — must fire BEFORE the noise
|
||||
// overlay so the noise grain is not captured and blurred
|
||||
// into the glass cards.
|
||||
auto captureCb2 = dragonx::ui::effects::ImGuiAcrylic::GetBackgroundCaptureCallback();
|
||||
if (captureCb2) {
|
||||
bgDL->AddCallback(captureCb2, nullptr);
|
||||
bgDL->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
||||
}
|
||||
|
||||
// WindowBg alpha stays at its base theme value — NOT scaled
|
||||
// by window opacity. Only the background gradient/texture
|
||||
// layers are scaled so the desktop shows through while UI
|
||||
// elements (cards, text, buttons) remain fully readable.
|
||||
if (ctx->backdrop_active || curGradTex2 != 0) {
|
||||
const auto& S4 = dragonx::ui::schema::UI();
|
||||
float baseBgAlpha2 = S4.drawElement("backdrop", "background-alpha").sizeOr(0.40f);
|
||||
ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = baseBgAlpha2;
|
||||
}
|
||||
|
||||
// Resize path historically always sampled the texture and always
|
||||
// added the capture callback — pass lowSpec = false to preserve
|
||||
// that behavior exactly.
|
||||
drawWindowBackdrop(*ctx->app, bgDL, p0, p1, ctx->backdrop_active, /*lowSpec=*/false);
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
@@ -1143,76 +1272,9 @@ int main(int argc, char* argv[])
|
||||
#ifdef __APPLE__
|
||||
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
||||
#endif
|
||||
auto& typo = dragonx::ui::material::Typography::instance();
|
||||
float oldScale = typo.getDpiScale();
|
||||
if (std::abs(newScale - oldScale) > 0.01f) {
|
||||
float ratio = newScale / oldScale;
|
||||
DEBUG_LOGF("Display scale changed (idle): %.2f -> %.2f (ratio %.2f)\n",
|
||||
oldScale, newScale, ratio);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
#ifdef DRAGONX_USE_DX11
|
||||
g_borderlessDpi = newScale;
|
||||
#endif
|
||||
|
||||
// Save the last known size (before auto-resize) under
|
||||
// the OLD scale. We use lastKnownW/H instead of
|
||||
// SDL_GetWindowSize() because by now WM_DPICHANGED
|
||||
// may have already auto-resized the window.
|
||||
{
|
||||
int oldPct = (int)lroundf(oldScale * 100.0f);
|
||||
savedSizeForScale[oldPct] = {lastKnownW, lastKnownH};
|
||||
DEBUG_LOGF(" Saved %dx%d for scale %d%%\n",
|
||||
lastKnownW, lastKnownH, oldPct);
|
||||
}
|
||||
|
||||
// Compute new physical size: if we've been to this
|
||||
// scale before, restore that exact size; otherwise
|
||||
// proportionally scale from the current size.
|
||||
int newPct = (int)lroundf(newScale * 100.0f);
|
||||
int newW, newH;
|
||||
auto it = savedSizeForScale.find(newPct);
|
||||
if (it != savedSizeForScale.end()) {
|
||||
newW = it->second.first;
|
||||
newH = it->second.second;
|
||||
} else {
|
||||
// Use lastKnownW/H (pre-auto-resize) instead of
|
||||
// SDL_GetWindowSize() which already reflects the
|
||||
// WM_DPICHANGED auto-resize.
|
||||
newW = (int)lroundf((float)lastKnownW * newScale / oldScale);
|
||||
newH = (int)lroundf((float)lastKnownH * newScale / oldScale);
|
||||
}
|
||||
|
||||
// Clamp to the target display's work area so the
|
||||
// window doesn't overflow a smaller/higher-density monitor.
|
||||
SDL_DisplayID did = SDL_GetDisplayForWindow(window);
|
||||
if (did) {
|
||||
SDL_Rect usable;
|
||||
if (SDL_GetDisplayUsableBounds(did, &usable)) {
|
||||
newW = std::min(newW, usable.w);
|
||||
newH = std::min(newH, usable.h);
|
||||
}
|
||||
}
|
||||
|
||||
dpiResizePending = true;
|
||||
SDL_SetWindowSize(window, newW, newH);
|
||||
lastKnownW = newW;
|
||||
lastKnownH = newH;
|
||||
// Arm deferred retry — the SetWindowSize above may
|
||||
// not stick if the user is mid-drag (modal loop).
|
||||
dpiTargetW = newW;
|
||||
dpiTargetH = newH;
|
||||
dpiResizeRetries = 30; // retry for ~30 frames
|
||||
SDL_SetWindowMinimumSize(window,
|
||||
(int)(1024 * newScale), (int)(720 * newScale));
|
||||
|
||||
typo.reload(io, newScale);
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
dragonx::ui::schema::UISchema::instance().reapplyColorsToImGui();
|
||||
if (newScale > 1.01f) {
|
||||
ImGui::GetStyle().ScaleAllSizes(newScale);
|
||||
}
|
||||
}
|
||||
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
||||
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
||||
dpiResizeRetries, dpiResizePending);
|
||||
}
|
||||
needsRedraw = true; // Got an event — redraw
|
||||
}
|
||||
@@ -1323,84 +1385,9 @@ int main(int argc, char* argv[])
|
||||
#ifdef __APPLE__
|
||||
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
||||
#endif
|
||||
auto& typo = dragonx::ui::material::Typography::instance();
|
||||
float oldScale = typo.getDpiScale();
|
||||
if (std::abs(newScale - oldScale) > 0.01f) {
|
||||
float ratio = newScale / oldScale;
|
||||
DEBUG_LOGF("Display scale changed: %.2f -> %.2f (ratio %.2f)\n",
|
||||
oldScale, newScale, ratio);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
#ifdef DRAGONX_USE_DX11
|
||||
g_borderlessDpi = newScale;
|
||||
#endif
|
||||
|
||||
// Save the last known size (before auto-resize) under
|
||||
// the OLD scale. We use lastKnownW/H instead of
|
||||
// SDL_GetWindowSize() because by now WM_DPICHANGED
|
||||
// may have already auto-resized the window.
|
||||
{
|
||||
int oldPct = (int)lroundf(oldScale * 100.0f);
|
||||
savedSizeForScale[oldPct] = {lastKnownW, lastKnownH};
|
||||
DEBUG_LOGF(" Saved %dx%d for scale %d%%\n",
|
||||
lastKnownW, lastKnownH, oldPct);
|
||||
}
|
||||
|
||||
// Compute new physical size: if we've been to this
|
||||
// scale before, restore that exact size; otherwise
|
||||
// proportionally scale from the current size.
|
||||
int newPct = (int)lroundf(newScale * 100.0f);
|
||||
int newW, newH;
|
||||
auto it = savedSizeForScale.find(newPct);
|
||||
if (it != savedSizeForScale.end()) {
|
||||
newW = it->second.first;
|
||||
newH = it->second.second;
|
||||
} else {
|
||||
// Use lastKnownW/H (pre-auto-resize) instead of
|
||||
// SDL_GetWindowSize() which already reflects the
|
||||
// WM_DPICHANGED auto-resize.
|
||||
newW = (int)lroundf((float)lastKnownW * newScale / oldScale);
|
||||
newH = (int)lroundf((float)lastKnownH * newScale / oldScale);
|
||||
}
|
||||
|
||||
// Clamp to the target display's work area so the
|
||||
// window doesn't overflow a smaller/higher-density monitor.
|
||||
SDL_DisplayID did = SDL_GetDisplayForWindow(window);
|
||||
if (did) {
|
||||
SDL_Rect usable;
|
||||
if (SDL_GetDisplayUsableBounds(did, &usable)) {
|
||||
newW = std::min(newW, usable.w);
|
||||
newH = std::min(newH, usable.h);
|
||||
}
|
||||
}
|
||||
|
||||
dpiResizePending = true;
|
||||
SDL_SetWindowSize(window, newW, newH);
|
||||
lastKnownW = newW;
|
||||
lastKnownH = newH;
|
||||
// Arm deferred retry — the SetWindowSize above may
|
||||
// not stick if the user is mid-drag (modal loop).
|
||||
dpiTargetW = newW;
|
||||
dpiTargetH = newH;
|
||||
dpiResizeRetries = 30; // retry for ~30 frames
|
||||
SDL_SetWindowMinimumSize(window,
|
||||
(int)(1024 * newScale),
|
||||
(int)(720 * newScale));
|
||||
DEBUG_LOGF(" Window resized: %dx%d (scale %.2f, pct %d)\n",
|
||||
newW, newH, newScale, newPct);
|
||||
|
||||
// 2) Rebuild font atlas at the new DPI scale
|
||||
typo.reload(io, newScale);
|
||||
|
||||
// 3) Reset style → reapply base theme → scale by new DPI
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
dragonx::ui::schema::UISchema::instance().reapplyColorsToImGui();
|
||||
if (newScale > 1.01f) {
|
||||
ImGui::GetStyle().ScaleAllSizes(newScale);
|
||||
}
|
||||
|
||||
DEBUG_LOGF(" DPI transition complete\n");
|
||||
}
|
||||
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
||||
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
||||
dpiResizeRetries, dpiResizePending);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1503,82 +1490,8 @@ int main(int argc, char* argv[])
|
||||
ImDrawList* bgDL = ImGui::GetBackgroundDrawList();
|
||||
ImVec2 p0 = vp->Pos;
|
||||
ImVec2 p1 = ImVec2(vp->Pos.x + vp->Size.x, vp->Pos.y + vp->Size.y);
|
||||
|
||||
// Read the current gradient texture from the app each frame
|
||||
// so hot-reloaded / skin-changed images are picked up immediately.
|
||||
ImTextureID curGradTex = app.getGradientTexture();
|
||||
|
||||
// Window opacity: scale only background layer alpha so the
|
||||
// desktop shows through while UI stays fully opaque.
|
||||
float winOpacity = app.settings() ? app.settings()->getWindowOpacity() : 1.0f;
|
||||
|
||||
bool lowSpec = dragonx::ui::effects::isLowSpecMode();
|
||||
|
||||
// Scale a single color's alpha channel by window opacity
|
||||
auto scaleAlpha = [&](ImU32 col) -> ImU32 {
|
||||
uint8_t a = (col >> IM_COL32_A_SHIFT) & 0xFF;
|
||||
a = (uint8_t)(a * winOpacity);
|
||||
return (col & ~(0xFFu << IM_COL32_A_SHIFT)) | ((ImU32)a << IM_COL32_A_SHIFT);
|
||||
};
|
||||
|
||||
if (curGradTex != 0) {
|
||||
// Base color gradient underneath the texture — read from ui.toml
|
||||
const auto& S2 = dragonx::ui::schema::UI();
|
||||
ImU32 baseTop = S2.resolveColor(S2.drawElement("backdrop", "base-color-top").color, IM_COL32(18,28,65,200));
|
||||
ImU32 baseBottom = S2.resolveColor(S2.drawElement("backdrop", "base-color-bottom").color, IM_COL32(8,12,35,200));
|
||||
|
||||
if (lowSpec) {
|
||||
// Low-spec: skip texture sampling — just draw the base gradient.
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, scaleAlpha(baseTop), scaleAlpha(baseTop),
|
||||
scaleAlpha(baseBottom), scaleAlpha(baseBottom));
|
||||
} else {
|
||||
int tintAlpha = (int)S2.drawElement("backdrop", "texture-tint-alpha").size;
|
||||
if (tintAlpha <= 0) tintAlpha = 140;
|
||||
|
||||
// Scale background gradient + texture tint alpha by
|
||||
// window opacity — only affects the backdrop, not UI.
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, scaleAlpha(baseTop), scaleAlpha(baseTop),
|
||||
scaleAlpha(baseBottom), scaleAlpha(baseBottom));
|
||||
int scaledTintAlpha = (int)(tintAlpha * winOpacity);
|
||||
ImU32 tintCol = IM_COL32(255, 255, 255, scaledTintAlpha);
|
||||
bgDL->AddImage(curGradTex, p0, p1, ImVec2(0, 0), ImVec2(1, 1), tintCol);
|
||||
}
|
||||
} else if (backdrop_active) {
|
||||
// Programmatic gradient tint (fallback when texture unavailable)
|
||||
const auto& S = dragonx::ui::schema::UI();
|
||||
auto bde = [&](const char* key, float fb) {
|
||||
float v = S.drawElement("backdrop", key).size;
|
||||
return v >= 0 ? v : fb;
|
||||
};
|
||||
ImU32 colTop = IM_COL32((int)bde("gradient-top-r",8), (int)bde("gradient-top-g",12),
|
||||
(int)bde("gradient-top-b",28), (int)(bde("gradient-top-a",80) * winOpacity));
|
||||
ImU32 colBottom = IM_COL32((int)bde("gradient-bottom-r",6), (int)bde("gradient-bottom-g",8),
|
||||
(int)bde("gradient-bottom-b",18), (int)(bde("gradient-bottom-a",60) * winOpacity));
|
||||
bgDL->AddRectFilledMultiColor(p0, p1, colTop, colTop, colBottom, colBottom);
|
||||
}
|
||||
|
||||
// Insert acrylic capture callback BEFORE the noise overlay
|
||||
// so the noise grain is not captured and blurred into the
|
||||
// glass cards (the noise should only be a visual overlay).
|
||||
// Skip in low-spec mode — acrylic is disabled so the FBO
|
||||
// capture/blur callback is unnecessary GPU work.
|
||||
if (!lowSpec) {
|
||||
auto captureCb = dragonx::ui::effects::ImGuiAcrylic::GetBackgroundCaptureCallback();
|
||||
if (captureCb) {
|
||||
bgDL->AddCallback(captureCb, nullptr);
|
||||
bgDL->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// WindowBg alpha stays at its base theme value — NOT scaled
|
||||
// by window opacity. Only the background gradient/texture
|
||||
// layers fade so UI elements remain fully readable.
|
||||
if (backdrop_active || curGradTex != 0) {
|
||||
const auto& S3 = dragonx::ui::schema::UI();
|
||||
float baseBgAlpha = S3.drawElement("backdrop", "background-alpha").sizeOr(0.40f);
|
||||
ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = baseBgAlpha;
|
||||
}
|
||||
|
||||
drawWindowBackdrop(app, bgDL, p0, p1, backdrop_active,
|
||||
dragonx::ui::effects::isLowSpecMode());
|
||||
}
|
||||
|
||||
PERF_END("Backdrop", _perfBackdrop);
|
||||
|
||||
Reference in New Issue
Block a user