Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
1179 lines
33 KiB
C++
1179 lines
33 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "imgui_acrylic.h"
|
|
#include "../schema/ui_schema.h"
|
|
#include "../../util/noise_texture.h"
|
|
|
|
#ifdef DRAGONX_HAS_GLAD
|
|
#include <glad/gl.h>
|
|
#endif
|
|
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
#include "../../util/logger.h"
|
|
|
|
// ============================================================================
|
|
// Frosted scrim — shared between GLAD and non-GLAD builds.
|
|
// Drawn on the popup's draw list as the first command so it appears behind
|
|
// the popup content but over the rest of the UI. Replaces ImGui's default
|
|
// solid-colour ModalWindowDimBg with a translucent frosted overlay + noise.
|
|
// ============================================================================
|
|
namespace dragonx { namespace ui { namespace effects { namespace ImGuiAcrylic {
|
|
static void DrawFrostedScrim(ImDrawList* dl)
|
|
{
|
|
const auto& S = ui::schema::UI();
|
|
auto sde = [&](const char* key, float fb) -> float {
|
|
float v = S.drawElement("components.modal-scrim", key).size;
|
|
return v >= 0.0f ? v : fb;
|
|
};
|
|
|
|
ImGuiViewport* vp = ImGui::GetMainViewport();
|
|
ImVec2 vpMin = vp->Pos;
|
|
ImVec2 vpMax(vp->Pos.x + vp->Size.x, vp->Pos.y + vp->Size.y);
|
|
|
|
// Semi-transparent white fill (the "frost")
|
|
int fillAlpha = (int)sde("fill-alpha", 12.0f);
|
|
dl->AddRectFilled(vpMin, vpMax, IM_COL32(255, 255, 255, fillAlpha));
|
|
|
|
// Noise grain overlay — single draw call via pre-tiled texture
|
|
{
|
|
int noiseAlpha = (int)sde("noise-alpha", 14.0f);
|
|
ImU32 noiseTint = IM_COL32(255, 255, 255, noiseAlpha);
|
|
dragonx::util::DrawTiledNoiseRect(dl, vpMin, vpMax, noiseTint);
|
|
}
|
|
}
|
|
} } } } // namespace dragonx::ui::effects::ImGuiAcrylic
|
|
|
|
// ============================================================================
|
|
// Preset table — shared by all backends
|
|
// ============================================================================
|
|
namespace dragonx { namespace ui { namespace effects { namespace ImGuiAcrylic {
|
|
|
|
struct PresetDef {
|
|
const char* label;
|
|
AcrylicQuality quality;
|
|
float blur;
|
|
};
|
|
|
|
static const PresetDef kPresets[kPresetCount] = {
|
|
{ "Off", AcrylicQuality::Off, 0.0f },
|
|
{ "Subtle", AcrylicQuality::Low, 0.6f },
|
|
{ "Light", AcrylicQuality::Medium, 1.0f },
|
|
{ "Standard", AcrylicQuality::Medium, 1.5f },
|
|
{ "Strong", AcrylicQuality::High, 2.5f },
|
|
{ "Frosted", AcrylicQuality::High, 4.0f },
|
|
};
|
|
|
|
const char* GetPresetLabel(int preset)
|
|
{
|
|
if (preset < 0 || preset >= kPresetCount) return "?";
|
|
return kPresets[preset].label;
|
|
}
|
|
|
|
AcrylicQuality PresetQuality(int preset)
|
|
{
|
|
if (preset < 0 || preset >= kPresetCount) return AcrylicQuality::Medium;
|
|
return kPresets[preset].quality;
|
|
}
|
|
|
|
float PresetBlur(int preset)
|
|
{
|
|
if (preset < 0 || preset >= kPresetCount) return 1.0f;
|
|
return kPresets[preset].blur;
|
|
}
|
|
|
|
// Find closest preset matching given quality + blur
|
|
int MatchPreset(AcrylicQuality q, float blur)
|
|
{
|
|
// Exact match first
|
|
for (int i = 0; i < kPresetCount; ++i) {
|
|
if (kPresets[i].quality == q &&
|
|
std::abs(kPresets[i].blur - blur) < 0.01f)
|
|
return i;
|
|
}
|
|
// Closest blur within same quality
|
|
int best = 3; // Standard fallback
|
|
float bestDist = 999.f;
|
|
for (int i = 0; i < kPresetCount; ++i) {
|
|
if (kPresets[i].quality == q) {
|
|
float d = std::abs(kPresets[i].blur - blur);
|
|
if (d < bestDist) { bestDist = d; best = i; }
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
} } } } // namespace dragonx::ui::effects::ImGuiAcrylic
|
|
|
|
#ifdef DRAGONX_HAS_GLAD
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
namespace effects {
|
|
|
|
namespace ImGuiAcrylic {
|
|
|
|
// ============================================================================
|
|
// Static State
|
|
// ============================================================================
|
|
|
|
static bool s_initialized = false;
|
|
static int s_viewportWidth = 0;
|
|
static int s_viewportHeight = 0;
|
|
static bool s_backgroundCaptured = false;
|
|
|
|
// Track window state for acrylic rendering
|
|
struct AcrylicWindowState {
|
|
ImVec2 windowPos;
|
|
ImVec2 windowSize;
|
|
AcrylicParams params;
|
|
bool active;
|
|
};
|
|
|
|
static AcrylicWindowState s_currentWindow = {};
|
|
|
|
// ============================================================================
|
|
// Initialization
|
|
// ============================================================================
|
|
|
|
bool Init()
|
|
{
|
|
if (s_initialized) {
|
|
return true;
|
|
}
|
|
|
|
AcrylicMaterial& acrylic = getAcrylicMaterial();
|
|
if (!acrylic.init()) {
|
|
DEBUG_LOGF("ImGuiAcrylic: Failed to initialize acrylic material\n");
|
|
return false;
|
|
}
|
|
|
|
s_initialized = true;
|
|
DEBUG_LOGF("ImGuiAcrylic: Initialized successfully\n");
|
|
return true;
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
if (!s_initialized) {
|
|
return;
|
|
}
|
|
|
|
getAcrylicMaterial().shutdown();
|
|
s_initialized = false;
|
|
DEBUG_LOGF("ImGuiAcrylic: Shut down\n");
|
|
}
|
|
|
|
bool IsAvailable()
|
|
{
|
|
return s_initialized && getAcrylicMaterial().isInitialized();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Frame Operations
|
|
// ============================================================================
|
|
|
|
void BeginFrame(int width, int height)
|
|
{
|
|
if (!s_initialized) {
|
|
return;
|
|
}
|
|
|
|
// Update viewport dimensions if changed
|
|
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. The background (gradient/image/noise) is static;
|
|
// re-capturing + re-blurring every frame causes a subtle flicker
|
|
// from GPU floating-point non-determinism in the blur shader.
|
|
// 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();
|
|
}
|
|
|
|
// Draw callback: captures framebuffer 0 (which at this point during
|
|
// RenderDrawData() contains only the rasterized BackgroundDrawList —
|
|
// no UI windows yet) into the acrylic capture buffer.
|
|
static void BackgroundCaptureCallback(const ImDrawList*, const ImDrawCmd*)
|
|
{
|
|
if (!s_initialized) return;
|
|
getAcrylicMaterial().captureBackgroundDirect();
|
|
s_backgroundCaptured = true;
|
|
}
|
|
|
|
ImDrawCallback GetBackgroundCaptureCallback()
|
|
{
|
|
return BackgroundCaptureCallback;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Drawing Functions
|
|
// ============================================================================
|
|
|
|
void DrawAcrylicRect(ImDrawList* drawList,
|
|
const ImVec2& pMin, const ImVec2& pMax,
|
|
const AcrylicParams& params,
|
|
float rounding)
|
|
{
|
|
if (!drawList) {
|
|
return;
|
|
}
|
|
|
|
// If acrylic not available or disabled, draw fallback
|
|
if (!IsAvailable() || !IsEnabled() || !params.enabled) {
|
|
drawList->AddRectFilled(pMin, pMax,
|
|
ImGui::ColorConvertFloat4ToU32(params.fallbackColor), rounding);
|
|
return;
|
|
}
|
|
|
|
// Background capture is now handled by a draw callback inserted
|
|
// into the BackgroundDrawList (see GetBackgroundCaptureCallback()).
|
|
// On the very first frame the callback hasn't fired yet, so
|
|
// drawRect() will fall back to a tinted fill automatically.
|
|
|
|
// Draw using acrylic material
|
|
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);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Window Wrappers
|
|
// ============================================================================
|
|
|
|
bool BeginAcrylicWindow(const char* name, bool* p_open,
|
|
ImGuiWindowFlags flags,
|
|
const AcrylicParams& params)
|
|
{
|
|
// Store params for use in EndAcrylicWindow
|
|
s_currentWindow.params = params;
|
|
s_currentWindow.active = true;
|
|
|
|
// Ensure dialog windows stay above background windows (MainContent, StatusBar, MenuBar)
|
|
if (!(flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
|
|
ImGui::SetNextWindowFocus();
|
|
|
|
// Make window background transparent so acrylic shows through
|
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
|
|
|
|
bool result = ImGui::Begin(name, p_open, flags);
|
|
|
|
if (result) {
|
|
// Get window bounds for acrylic rendering
|
|
s_currentWindow.windowPos = ImGui::GetWindowPos();
|
|
s_currentWindow.windowSize = ImGui::GetWindowSize();
|
|
|
|
// Draw acrylic background
|
|
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
|
ImVec2 pMin = s_currentWindow.windowPos;
|
|
ImVec2 pMax = ImVec2(pMin.x + s_currentWindow.windowSize.x,
|
|
pMin.y + s_currentWindow.windowSize.y);
|
|
|
|
// Get window rounding from style
|
|
float rounding = ImGui::GetStyle().WindowRounding;
|
|
|
|
DrawAcrylicRect(drawList, pMin, pMax, params, rounding);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void EndAcrylicWindow()
|
|
{
|
|
ImGui::End();
|
|
ImGui::PopStyleColor(); // WindowBg
|
|
s_currentWindow.active = false;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Child Region Wrappers
|
|
// ============================================================================
|
|
|
|
bool BeginAcrylicChild(const char* str_id,
|
|
const ImVec2& size,
|
|
const AcrylicParams& params,
|
|
ImGuiChildFlags child_flags,
|
|
ImGuiWindowFlags window_flags)
|
|
{
|
|
// Make child background transparent
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
|
|
|
|
bool result = ImGui::BeginChild(str_id, size, child_flags, window_flags);
|
|
|
|
if (result) {
|
|
// Get child bounds
|
|
ImVec2 childPos = ImGui::GetWindowPos();
|
|
ImVec2 childSize = ImGui::GetWindowSize();
|
|
|
|
// Draw acrylic background
|
|
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(); // ChildBg
|
|
}
|
|
|
|
// ============================================================================
|
|
// Popup Wrappers
|
|
// ============================================================================
|
|
|
|
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(); // Only pop if not opened
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void EndAcrylicPopup()
|
|
{
|
|
ImGui::EndPopup();
|
|
ImGui::PopStyleColor(); // PopupBg
|
|
}
|
|
|
|
bool BeginAcrylicPopupModal(const char* name, bool* p_open,
|
|
ImGuiWindowFlags flags,
|
|
const AcrylicParams& params)
|
|
{
|
|
// Suppress ImGui's built-in dark scrim — we draw our own frosted one
|
|
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 (only needed during Begin)
|
|
|
|
if (result) {
|
|
// Draw frosted scrim behind the popup on its own draw list
|
|
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;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Context Menu Helpers
|
|
// ============================================================================
|
|
|
|
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;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Sidebar Helper
|
|
// ============================================================================
|
|
|
|
void DrawSidebarBackground(float width, const AcrylicParams& params)
|
|
{
|
|
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
|
|
// Sidebar starts below menu bar
|
|
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);
|
|
|
|
// Draw to background draw list
|
|
ImDrawList* drawList = ImGui::GetBackgroundDrawList();
|
|
|
|
DrawAcrylicRect(drawList, pMin, pMax, params, 0.0f);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Settings
|
|
// ============================================================================
|
|
|
|
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
|
|
|
|
#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 {
|
|
namespace ui {
|
|
namespace effects {
|
|
|
|
namespace ImGuiAcrylic {
|
|
|
|
bool Init() { return false; }
|
|
void Shutdown() {}
|
|
bool IsAvailable() { return false; }
|
|
|
|
void BeginFrame(int, int) {}
|
|
void CaptureBackground() {}
|
|
void InvalidateCapture() {}
|
|
ImDrawCallback GetBackgroundCaptureCallback() { return nullptr; }
|
|
|
|
void DrawAcrylicRect(ImDrawList* drawList,
|
|
const ImVec2& pMin, const ImVec2& pMax,
|
|
const AcrylicParams& params,
|
|
float rounding)
|
|
{
|
|
if (drawList) {
|
|
drawList->AddRectFilled(pMin, pMax,
|
|
ImGui::ColorConvertFloat4ToU32(params.fallbackColor), rounding);
|
|
}
|
|
}
|
|
|
|
void DrawAcrylicRect(ImDrawList* drawList,
|
|
const ImVec2& pMin, const ImVec2& pMax,
|
|
float rounding)
|
|
{
|
|
if (drawList) {
|
|
AcrylicParams params;
|
|
drawList->AddRectFilled(pMin, pMax,
|
|
ImGui::ColorConvertFloat4ToU32(params.fallbackColor), rounding);
|
|
}
|
|
}
|
|
|
|
bool BeginAcrylicWindow(const char* name, bool* p_open,
|
|
ImGuiWindowFlags flags,
|
|
const AcrylicParams&)
|
|
{
|
|
if (!(flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
|
|
ImGui::SetNextWindowFocus();
|
|
return ImGui::Begin(name, p_open, flags);
|
|
}
|
|
|
|
void EndAcrylicWindow()
|
|
{
|
|
ImGui::End();
|
|
}
|
|
|
|
bool BeginAcrylicChild(const char* str_id,
|
|
const ImVec2& size,
|
|
const AcrylicParams&,
|
|
ImGuiChildFlags child_flags,
|
|
ImGuiWindowFlags window_flags)
|
|
{
|
|
return ImGui::BeginChild(str_id, size, child_flags, window_flags);
|
|
}
|
|
|
|
void EndAcrylicChild()
|
|
{
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
bool BeginAcrylicPopup(const char* str_id,
|
|
ImGuiWindowFlags flags,
|
|
const AcrylicParams&)
|
|
{
|
|
return ImGui::BeginPopup(str_id, flags);
|
|
}
|
|
|
|
void EndAcrylicPopup()
|
|
{
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
bool BeginAcrylicPopupModal(const char* name, bool* p_open,
|
|
ImGuiWindowFlags flags,
|
|
const AcrylicParams&)
|
|
{
|
|
ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, ImVec4(0, 0, 0, 0));
|
|
bool result = ImGui::BeginPopupModal(name, p_open, flags);
|
|
ImGui::PopStyleColor();
|
|
if (result) {
|
|
DrawFrostedScrim(ImGui::GetWindowDrawList());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool BeginAcrylicContextItem(const char* str_id,
|
|
ImGuiPopupFlags popup_flags,
|
|
const AcrylicParams&)
|
|
{
|
|
return ImGui::BeginPopupContextItem(str_id, popup_flags);
|
|
}
|
|
|
|
bool BeginAcrylicContextWindow(const char* str_id,
|
|
ImGuiPopupFlags popup_flags,
|
|
const AcrylicParams&)
|
|
{
|
|
return ImGui::BeginPopupContextWindow(str_id, popup_flags);
|
|
}
|
|
|
|
void DrawSidebarBackground(float, const AcrylicParams&) {}
|
|
|
|
void SetQuality(AcrylicQuality) {}
|
|
AcrylicQuality GetQuality() { return AcrylicQuality::Off; }
|
|
void SetEnabled(bool) {}
|
|
bool IsEnabled() { return false; }
|
|
void SetBlurMultiplier(float) {}
|
|
float GetBlurMultiplier() { return 1.0f; }
|
|
void SetReducedTransparency(bool) {}
|
|
bool GetReducedTransparency() { return false; }
|
|
void SetUIOpacity(float) {}
|
|
float GetUIOpacity() { return 1.0f; }
|
|
void SetNoiseOpacity(float) {}
|
|
float GetNoiseOpacity() { return 1.0f; }
|
|
static AcrylicSettings s_stubSettings;
|
|
const AcrylicSettings& GetSettings() { return s_stubSettings; }
|
|
void SetSettings(const AcrylicSettings&) {}
|
|
void SetPreset(int) {}
|
|
int GetPreset() { return 0; }
|
|
void ApplyBlurAmount(float) {}
|
|
|
|
} // namespace ImGuiAcrylic
|
|
|
|
} // namespace effects
|
|
} // namespace ui
|
|
} // namespace dragonx
|
|
|
|
#endif // DRAGONX_USE_DX11
|
|
|
|
#endif // !DRAGONX_HAS_GLAD
|