refactor(audit): batch 3 — decompose App::update() + collapse acrylic fork

Three P1 structural refactors from the audit; no behavior change.

1. Extract the ~180-line daemon-stdout rescan/witness parser out of the
   ~790-line App::update() into a pure, static, unit-testable
   NetworkRefreshService::parseDaemonRescanOutput() returning a
   DaemonRescanScan result struct (sits next to the existing static parse*
   siblings). The scanning half moved verbatim; App::update() keeps the
   state-application half, now reading scan.* fields. Adds
   testParseDaemonRescanOutput() covering all six daemon signals + edge
   cases against fixture log snippets — previously untestable without a
   live daemon.

2. Extract the ~95-line global keyboard-shortcut block (Ctrl+, / theme
   cycle / F5 / low-spec / effects / gradient / wizard) out of
   App::update() into App::handleGlobalShortcuts().

3. Collapse the ImGuiAcrylic frontend fork: the GLAD and DX11 namespaces
   were ~375 lines of semantically-identical code (the frontend makes zero
   direct GL/DX calls — all backend work delegates to AcrylicMaterial, which
   has a backend per API). Extended the single frontend's guard to
   #if defined(DRAGONX_HAS_GLAD) || defined(DRAGONX_USE_DX11) and deleted the
   DX11 duplicate, leaving the no-backend stub. Kills the Windows/DX11 drift
   hazard (acrylic frontend changes now made once).

Verified: full-node + Lite build clean; ctest 1/1 (incl. the new parser
tests); source-hygiene clean; and the collapsed acrylic path was
compile-verified under DX11 via the mingw-w64 Windows cross-build
(ObsidianDragon.exe links).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 16:40:40 -05:00
parent 7891c689fb
commit 280a71e973
6 changed files with 331 additions and 602 deletions

View File

@@ -107,7 +107,10 @@ int MatchPreset(AcrylicQuality q, float blur)
} } } } // namespace dragonx::ui::effects::ImGuiAcrylic
#ifdef DRAGONX_HAS_GLAD
// Single ImGuiAcrylic frontend for BOTH GL (GLAD) and DX11: the frontend makes no
// direct GL/DX calls — every backend op is delegated to AcrylicMaterial, which has a
// backend for each API — so it must not fork per-backend.
#if defined(DRAGONX_HAS_GLAD) || defined(DRAGONX_USE_DX11)
namespace dragonx {
namespace ui {
@@ -638,408 +641,6 @@ void ApplyBlurAmount(float blur)
} // namespace ui
} // namespace dragonx
#endif // DRAGONX_HAS_GLAD
// ============================================================================
// Stub Implementations (No GLAD)
// ============================================================================
#ifndef DRAGONX_HAS_GLAD
#ifdef DRAGONX_USE_DX11
// ============================================================================
// DX11 Implementation — mirrors the GLAD version above, delegating to
// AcrylicMaterial which now has a full DX11 backend.
// ============================================================================
namespace dragonx {
namespace ui {
namespace effects {
namespace ImGuiAcrylic {
static bool s_initialized = false;
static int s_viewportWidth = 0;
static int s_viewportHeight = 0;
static bool s_backgroundCaptured = false;
struct AcrylicWindowState {
ImVec2 windowPos;
ImVec2 windowSize;
AcrylicParams params;
bool active;
};
static AcrylicWindowState s_currentWindow = {};
bool Init()
{
if (s_initialized) return true;
AcrylicMaterial& acrylic = getAcrylicMaterial();
if (!acrylic.init()) {
DEBUG_LOGF("ImGuiAcrylic DX11: Failed to initialize acrylic material\n");
return false;
}
s_initialized = true;
DEBUG_LOGF("ImGuiAcrylic DX11: Initialized successfully\n");
return true;
}
void Shutdown()
{
if (!s_initialized) return;
getAcrylicMaterial().shutdown();
s_initialized = false;
}
bool IsAvailable()
{
return s_initialized && getAcrylicMaterial().isInitialized();
}
void BeginFrame(int width, int height)
{
if (!s_initialized) return;
if (width != s_viewportWidth || height != s_viewportHeight) {
s_viewportWidth = width;
s_viewportHeight = height;
getAcrylicMaterial().resize(width, height);
}
// Capture the first few frames to ensure a clean initial blur,
// then stop. Resize / theme change already sets dirtyFrames_ via markBackgroundDirty().
static int s_warmupFrames = 3;
if (s_warmupFrames > 0) {
getAcrylicMaterial().markBackgroundDirty();
--s_warmupFrames;
}
s_backgroundCaptured = false;
}
void CaptureBackground()
{
if (!s_initialized || s_backgroundCaptured) return;
getAcrylicMaterial().captureBackground();
s_backgroundCaptured = true;
}
void InvalidateCapture()
{
if (!s_initialized) return;
getAcrylicMaterial().markBackgroundDirty();
}
static void BackgroundCaptureCallback(const ImDrawList*, const ImDrawCmd*)
{
if (!s_initialized) return;
getAcrylicMaterial().captureBackgroundDirect();
s_backgroundCaptured = true;
}
ImDrawCallback GetBackgroundCaptureCallback()
{
return BackgroundCaptureCallback;
}
void DrawAcrylicRect(ImDrawList* drawList,
const ImVec2& pMin, const ImVec2& pMax,
const AcrylicParams& params,
float rounding)
{
if (!drawList) return;
if (!IsAvailable() || !IsEnabled() || !params.enabled) {
drawList->AddRectFilled(pMin, pMax,
ImGui::ColorConvertFloat4ToU32(params.fallbackColor), rounding);
return;
}
getAcrylicMaterial().drawRect(drawList, pMin, pMax, params, rounding);
}
void DrawAcrylicRect(ImDrawList* drawList,
const ImVec2& pMin, const ImVec2& pMax,
float rounding)
{
DrawAcrylicRect(drawList, pMin, pMax, AcrylicMaterial::getDarkPreset(), rounding);
}
bool BeginAcrylicWindow(const char* name, bool* p_open,
ImGuiWindowFlags flags,
const AcrylicParams& params)
{
s_currentWindow.params = params;
s_currentWindow.active = true;
if (!(flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
ImGui::SetNextWindowFocus();
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::Begin(name, p_open, flags);
if (result) {
s_currentWindow.windowPos = ImGui::GetWindowPos();
s_currentWindow.windowSize = ImGui::GetWindowSize();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pMin = s_currentWindow.windowPos;
ImVec2 pMax = ImVec2(pMin.x + s_currentWindow.windowSize.x,
pMin.y + s_currentWindow.windowSize.y);
float rounding = ImGui::GetStyle().WindowRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
}
return result;
}
void EndAcrylicWindow()
{
ImGui::End();
ImGui::PopStyleColor();
s_currentWindow.active = false;
}
bool BeginAcrylicChild(const char* str_id,
const ImVec2& size,
const AcrylicParams& params,
ImGuiChildFlags child_flags,
ImGuiWindowFlags window_flags)
{
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::BeginChild(str_id, size, child_flags, window_flags);
if (result) {
ImVec2 childPos = ImGui::GetWindowPos();
ImVec2 childSize = ImGui::GetWindowSize();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pMin = childPos;
ImVec2 pMax = ImVec2(pMin.x + childSize.x, pMin.y + childSize.y);
float rounding = ImGui::GetStyle().ChildRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
}
return result;
}
void EndAcrylicChild()
{
ImGui::EndChild();
ImGui::PopStyleColor();
}
bool BeginAcrylicPopup(const char* str_id,
ImGuiWindowFlags flags,
const AcrylicParams& params)
{
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::BeginPopup(str_id, flags);
if (result) {
ImVec2 popupPos = ImGui::GetWindowPos();
ImVec2 popupSize = ImGui::GetWindowSize();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pMin = popupPos;
ImVec2 pMax = ImVec2(pMin.x + popupSize.x, pMin.y + popupSize.y);
float rounding = ImGui::GetStyle().PopupRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
} else {
ImGui::PopStyleColor();
}
return result;
}
void EndAcrylicPopup()
{
ImGui::EndPopup();
ImGui::PopStyleColor();
}
bool BeginAcrylicPopupModal(const char* name, bool* p_open,
ImGuiWindowFlags flags,
const AcrylicParams& params)
{
ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::BeginPopupModal(name, p_open, flags);
ImGui::PopStyleColor(); // ModalWindowDimBg
if (result) {
ImDrawList* drawList = ImGui::GetWindowDrawList();
DrawFrostedScrim(drawList);
ImVec2 popupPos = ImGui::GetWindowPos();
ImVec2 popupSize = ImGui::GetWindowSize();
ImVec2 pMin = popupPos;
ImVec2 pMax = ImVec2(pMin.x + popupSize.x, pMin.y + popupSize.y);
float rounding = ImGui::GetStyle().PopupRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
} else {
ImGui::PopStyleColor();
}
return result;
}
bool BeginAcrylicContextItem(const char* str_id,
ImGuiPopupFlags popup_flags,
const AcrylicParams& params)
{
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::BeginPopupContextItem(str_id, popup_flags);
if (result) {
ImVec2 popupPos = ImGui::GetWindowPos();
ImVec2 popupSize = ImGui::GetWindowSize();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pMin = popupPos;
ImVec2 pMax = ImVec2(pMin.x + popupSize.x, pMin.y + popupSize.y);
float rounding = ImGui::GetStyle().PopupRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
} else {
ImGui::PopStyleColor();
}
return result;
}
bool BeginAcrylicContextWindow(const char* str_id,
ImGuiPopupFlags popup_flags,
const AcrylicParams& params)
{
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
bool result = ImGui::BeginPopupContextWindow(str_id, popup_flags);
if (result) {
ImVec2 popupPos = ImGui::GetWindowPos();
ImVec2 popupSize = ImGui::GetWindowSize();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pMin = popupPos;
ImVec2 pMax = ImVec2(pMin.x + popupSize.x, pMin.y + popupSize.y);
float rounding = ImGui::GetStyle().PopupRounding;
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
} else {
ImGui::PopStyleColor();
}
return result;
}
void DrawSidebarBackground(float width, const AcrylicParams& params)
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
float menuBarHeight = ImGui::GetFrameHeight();
ImVec2 pMin = ImVec2(viewport->WorkPos.x, viewport->WorkPos.y + menuBarHeight);
ImVec2 pMax = ImVec2(pMin.x + width, viewport->WorkPos.y + viewport->WorkSize.y);
ImDrawList* drawList = ImGui::GetBackgroundDrawList();
DrawAcrylicRect(drawList, pMin, pMax, params, 0.0f);
}
void SetQuality(AcrylicQuality quality)
{
if (s_initialized) getAcrylicMaterial().setQuality(quality);
}
AcrylicQuality GetQuality()
{
if (s_initialized) return getAcrylicMaterial().getQuality();
return AcrylicQuality::Off;
}
void SetEnabled(bool enabled)
{
if (s_initialized) getAcrylicMaterial().setEnabled(enabled);
}
bool IsEnabled()
{
if (s_initialized) return getAcrylicMaterial().isEnabled();
return false;
}
void SetBlurMultiplier(float multiplier)
{
if (s_initialized) getAcrylicMaterial().setBlurMultiplier(multiplier);
}
float GetBlurMultiplier()
{
if (s_initialized) return getAcrylicMaterial().getBlurMultiplier();
return 1.0f;
}
void SetReducedTransparency(bool reduced)
{
if (s_initialized) getAcrylicMaterial().setReducedTransparency(reduced);
}
bool GetReducedTransparency()
{
if (s_initialized) return getAcrylicMaterial().getReducedTransparency();
return false;
}
void SetUIOpacity(float opacity)
{
if (s_initialized) getAcrylicMaterial().setUIOpacity(opacity);
}
float GetUIOpacity()
{
if (s_initialized) return getAcrylicMaterial().getUIOpacity();
return 1.0f;
}
void SetNoiseOpacity(float multiplier)
{
if (s_initialized) getAcrylicMaterial().setNoiseOpacityMultiplier(multiplier);
}
float GetNoiseOpacity()
{
if (s_initialized) return getAcrylicMaterial().getNoiseOpacityMultiplier();
return 1.0f;
}
const AcrylicSettings& GetSettings()
{
return getAcrylicMaterial().getSettings();
}
void SetSettings(const AcrylicSettings& settings)
{
if (s_initialized) getAcrylicMaterial().setSettings(settings);
}
void SetPreset(int preset)
{
if (preset < 0 || preset >= kPresetCount) preset = 3;
SetEnabled(preset != 0);
SetQuality(PresetQuality(preset));
SetBlurMultiplier(PresetBlur(preset));
}
int GetPreset()
{
return MatchPreset(GetQuality(), GetBlurMultiplier());
}
void ApplyBlurAmount(float blur)
{
if (blur < 0.001f) {
SetEnabled(false);
SetQuality(AcrylicQuality::Off);
SetBlurMultiplier(0.0f);
} else {
SetEnabled(true);
SetQuality(AcrylicQuality::Low);
SetBlurMultiplier(blur);
}
}
} // namespace ImGuiAcrylic
} // namespace effects
} // namespace ui
} // namespace dragonx
#else // neither GLAD nor DX11
namespace dragonx {
@@ -1173,6 +774,5 @@ void ApplyBlurAmount(float) {}
} // namespace ui
} // namespace dragonx
#endif // DRAGONX_USE_DX11
#endif // !DRAGONX_HAS_GLAD
#endif // DRAGONX_HAS_GLAD || DRAGONX_USE_DX11