improved font scaling text and window adjustment, added ctrl + scroll hotkey for font scaling

This commit is contained in:
dan_s
2026-03-05 01:22:20 -06:00
parent 45a2ccd9f3
commit 68c2a59d09
4 changed files with 183 additions and 101 deletions

View File

@@ -957,6 +957,10 @@ int main(int argc, char* argv[])
// resizing on a 200% screen doesn't clobber the 100% size.
std::map<int, std::pair<int,int>> savedSizeForScale;
bool dpiResizePending = false;
bool fontScaleResizePending = false;
// Font-scale window reference: size at scale 1.0 (updated on user resize)
float fontScaleRefFS = 0.0f;
int fontScaleRefW = 0, fontScaleRefH = 0;
// Track last known window size at the current DPI scale.
// Updated on user-initiated resizes (not DPI auto-resizes).
// When DISPLAY_SCALE_CHANGED fires, SDL_GetWindowSize() already
@@ -1140,7 +1144,28 @@ int main(int argc, char* argv[])
// Poll remaining events
SDL_Event event;
// Deferred font-scale commit: smooth visual update during Alt+scroll,
// atlas rebuild after scrolling stops (~200ms idle).
static Uint64 fontScaleCommitTick = 0; // 0 = nothing pending
while (SDL_PollEvent(&event)) {
// Alt + scroll wheel: smooth font scale adjustment
if (event.type == SDL_EVENT_MOUSE_WHEEL) {
SDL_Keymod mods = SDL_GetModState();
if (mods & SDL_KMOD_ALT) {
float step = 0.05f * event.wheel.y;
float cur = dragonx::ui::Layout::userFontScale();
float next = std::max(1.0f, std::min(1.5f, cur + step));
if (next != cur) {
// Smooth preview — no atlas rebuild yet
dragonx::ui::Layout::setUserFontScaleVisual(next);
// Schedule atlas rebuild after scrolling stops
fontScaleCommitTick = SDL_GetTicks() + 200;
}
// Don't pass Alt+scroll to ImGui (would scroll windows)
continue;
}
}
ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT) {
@@ -1167,11 +1192,20 @@ int main(int argc, char* argv[])
bool isDpiResize = dpiResizePending ||
std::abs(actualScale - storedScale) > 0.01f;
if (dpiResizePending) dpiResizePending = false;
bool isFSResize = fontScaleResizePending;
if (fontScaleResizePending) fontScaleResizePending = false;
if (!isDpiResize) {
int pct = (int)lroundf(storedScale * 100.0f);
savedSizeForScale[pct] = {event.window.data1, event.window.data2};
lastKnownW = event.window.data1;
lastKnownH = event.window.data2;
// User manually resized — update the font-scale reference
// so the next scale change is relative to this size.
if (!isFSResize) {
float fs = dragonx::ui::Layout::userFontScale();
fontScaleRefW = (int)lroundf((float)event.window.data1 / fs);
fontScaleRefH = (int)lroundf((float)event.window.data2 / fs);
}
}
}
// Window restored from minimized — trigger immediate data refresh
@@ -1279,6 +1313,15 @@ int main(int argc, char* argv[])
// --- PerfLog: begin frame ---
dragonx::util::PerfLog::instance().beginFrame();
// Commit deferred font-scale change once scrolling has stopped.
if (fontScaleCommitTick != 0 && SDL_GetTicks() >= fontScaleCommitTick) {
fontScaleCommitTick = 0;
float fs = dragonx::ui::Layout::userFontScale();
dragonx::ui::Layout::setUserFontScale(fs);
app.settings()->setFontScale(fs);
app.settings()->save();
}
// Pre-frame: font atlas rebuilds and schema hot-reload must
// happen BEFORE NewFrame() because NewFrame() caches font ptrs.
app.preFrame();
@@ -1294,25 +1337,24 @@ int main(int argc, char* argv[])
}
// If font scale changed, resize window proportionally.
// anchorW/H = the window size at the moment the anchor was set.
// anchorFS = the font scale at that moment.
// target = anchorW * curFS / anchorFS (symmetric, no drift).
// Reference size (at scale 1.0) is updated on user-initiated resizes
// so the next scale change is relative to the current window, not a
// stale snapshot. target = ref * curFS — no rounding drift.
{
static float anchorFS = 0.0f;
static int anchorW = 0, anchorH = 0;
float curFS = dragonx::ui::Layout::userFontScale();
if (anchorFS < 0.001f) {
// First frame: the current window IS the reference for
// whatever font scale is loaded — no resize.
anchorFS = curFS;
SDL_GetWindowSize(window, &anchorW, &anchorH);
if (fontScaleRefFS < 0.001f) {
// First frame — record reference, no resize.
fontScaleRefFS = curFS;
int w, h;
SDL_GetWindowSize(window, &w, &h);
fontScaleRefW = (int)lroundf((float)w / curFS);
fontScaleRefH = (int)lroundf((float)h / curFS);
}
if (std::fabs(curFS - anchorFS) > 0.001f) {
float ratio = curFS / anchorFS;
int newW = (int)lroundf((float)anchorW * ratio);
int newH = (int)lroundf((float)anchorH * ratio);
if (std::fabs(curFS - fontScaleRefFS) > 0.001f) {
int newW = (int)lroundf((float)fontScaleRefW * curFS);
int newH = (int)lroundf((float)fontScaleRefH * curFS);
// Clamp to display work area
SDL_DisplayID did = SDL_GetDisplayForWindow(window);
@@ -1325,21 +1367,15 @@ int main(int argc, char* argv[])
}
float hwDpi = dragonx::ui::Layout::rawDpiScale();
// Update minimum size BEFORE resizing so the window can
// actually shrink when the font scale decreases.
SDL_SetWindowMinimumSize(window,
(int)(1024 * hwDpi * curFS),
(int)(720 * hwDpi * curFS));
(int)(1024 * hwDpi),
(int)(720 * hwDpi));
fontScaleResizePending = true;
SDL_SetWindowSize(window, newW, newH);
lastKnownW = newW;
lastKnownH = newH;
// Update anchor so we don't re-fire every frame.
// The new anchor becomes the current size/scale so
// subsequent slider movements are relative to here.
anchorW = newW;
anchorH = newH;
anchorFS = curFS;
fontScaleRefFS = curFS;
}
}

View File

@@ -70,8 +70,10 @@ inline void setFontAtlasScale(float v) { detail::fontAtlasScaleRef() = v; }
*/
inline void setUserFontScale(float v) {
v = std::max(1.0f, std::min(1.5f, v));
if (v != detail::userFontScaleRef()) {
detail::userFontScaleRef() = v;
detail::userFontScaleRef() = v;
// Compare against the atlas scale, not the live ref — setUserFontScaleVisual
// may have already updated the ref during slider drag.
if (v != detail::fontAtlasScaleRef()) {
detail::fontReloadNeededRef() = true;
}
}

View File

@@ -916,6 +916,8 @@ void RenderSettingsPage(App* app) {
float fontSliderW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size,
availWidth - pad * 2);
ImGui::SetNextItemWidth(fontSliderW);
// Sync from Layout so Alt+scroll hotkey changes are reflected
sp_font_scale = Layout::userFontScale();
float prev_font_scale = sp_font_scale;
{
char fs_fmt[16];
@@ -923,10 +925,11 @@ void RenderSettingsPage(App* app) {
ImGui::SliderFloat("##FontScale", &sp_font_scale, 1.0f, 1.5f, fs_fmt,
ImGuiSliderFlags_AlwaysClamp);
}
// Smooth continuous scaling while dragging.
// Snap to 0.05 increments for consistent stepping.
// Visual scaling uses FontScaleMain (no atlas rebuild),
// atlas rebuild is deferred to slider release for crisp text.
sp_font_scale = std::max(1.0f, std::min(1.5f, sp_font_scale));
sp_font_scale = std::max(1.0f, std::min(1.5f,
std::round(sp_font_scale * 20.0f) / 20.0f));
if (sp_font_scale != prev_font_scale) {
// While dragging: update layout scale without atlas rebuild
Layout::setUserFontScaleVisual(sp_font_scale);

View File

@@ -388,9 +388,10 @@ static void RenderBalanceClassic(App* app)
float classicCardH = S.drawElement("tabs.balance.classic", "card-height").size;
float cardH;
if (classicCardH >= 0.0f) {
cardH = classicCardH; // explicit override from ui.toml
// TOML override scaled by dp so it grows with DPI + user font scale
cardH = std::max(classicCardH * dp, marketContentH);
} else {
float minH = S.drawElement("tabs.balance.classic", "card-min-height").sizeOr(70.0f);
float minH = S.drawElement("tabs.balance.classic", "card-min-height").sizeOr(70.0f) * dp;
cardH = std::max(StatCardHeight(vs, minH), marketContentH);
}
@@ -814,7 +815,7 @@ static void RenderBalanceClassic(App* app)
float classicAddrH = S.drawElement("tabs.balance.classic", "address-table-height").size;
float addrListH;
if (classicAddrH >= 0.0f) {
addrListH = classicAddrH; // explicit override from ui.toml
addrListH = classicAddrH * dp; // scaled for DPI + font scale
} else {
addrListH = ImGui::GetContentRegionAvail().y - recentTxReserve;
}
@@ -1881,14 +1882,20 @@ static void RenderBalanceDonut(App* app) {
// --- Donut + legend panel ---
{
ImFont* donutOv = Type().overline();
ImFont* donutCap = Type().caption();
// Font-content floor: legend needs overline + 3 caption lines + spacing
float donutFontFloor = Layout::spacingLg() * 2
+ donutOv->LegacySize + Layout::spacingMd()
+ donutCap->LegacySize * 3 + Layout::spacingSm() * 3;
float donutCardH = S.drawElement("tabs.balance.donut", "card-height").size;
float panelH;
if (donutCardH >= 0.0f) {
panelH = donutCardH; // explicit override from ui.toml
panelH = std::max(donutCardH * dp, donutFontFloor);
} else {
panelH = std::max(
S.drawElement("tabs.balance.donut", "panel-min-height").sizeOr(80.0f),
contentAvail.y * S.drawElement("tabs.balance.donut", "panel-height-ratio").sizeOr(0.20f));
panelH = std::max({donutFontFloor,
S.drawElement("tabs.balance.donut", "panel-min-height").sizeOr(80.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.donut", "panel-height-ratio").sizeOr(0.20f)});
}
ImVec2 panelMin = ImGui::GetCursorScreenPos();
ImVec2 panelMax(panelMin.x + availW, panelMin.y + panelH);
@@ -1985,7 +1992,7 @@ static void RenderBalanceDonut(App* app) {
// --- Shared address list + recent tx ---
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float donutAddrOverride = S.drawElement("tabs.balance.donut", "address-table-height").size;
float addrH = (donutAddrOverride >= 0.0f) ? donutAddrOverride
float addrH = (donutAddrOverride >= 0.0f) ? donutAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2016,14 +2023,25 @@ static void RenderBalanceConsolidated(App* app) {
if (consTopMargin >= 0.0f)
ImGui::Dummy(ImVec2(0, consTopMargin));
ImFont* heroFont = Type().h2();
ImFont* sub1 = Type().subtitle1();
ImFont* capFont = Type().caption();
ImFont* ovFont = Type().overline();
float consPadOverride = S.drawElement("tabs.balance.consolidated", "card-padding").size;
float pad = (consPadOverride >= 0.0f) ? consPadOverride : Layout::spacingLg();
// Font-content floor: pad + overline + hero + caption + subtitle + pad
float consFontFloor = pad + ovFont->LegacySize + Layout::spacingSm()
+ heroFont->LegacySize + Layout::spacingSm()
+ capFont->LegacySize + Layout::spacingSm()
+ sub1->LegacySize + pad;
float consCardH = S.drawElement("tabs.balance.consolidated", "card-height").size;
float cardH;
if (consCardH >= 0.0f) {
cardH = consCardH; // explicit override from ui.toml
cardH = std::max(consCardH * dp, consFontFloor);
} else {
cardH = std::max(
S.drawElement("tabs.balance.consolidated", "card-min-height").sizeOr(90.0f),
contentAvail.y * S.drawElement("tabs.balance.consolidated", "card-height-ratio").sizeOr(0.22f));
cardH = std::max({consFontFloor,
S.drawElement("tabs.balance.consolidated", "card-min-height").sizeOr(90.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.consolidated", "card-height-ratio").sizeOr(0.22f)});
}
ImVec2 cardMin = ImGui::GetCursorScreenPos();
ImVec2 cardMax(cardMin.x + availW, cardMin.y + cardH);
@@ -2031,17 +2049,11 @@ static void RenderBalanceConsolidated(App* app) {
spec.rounding = glassRound;
DrawGlassPanel(dl, cardMin, cardMax, spec);
float consPadOverride = S.drawElement("tabs.balance.consolidated", "card-padding").size;
float pad = (consPadOverride >= 0.0f) ? consPadOverride : Layout::spacingLg();
float cx = cardMin.x + pad;
float cy = cardMin.y + pad;
// Coin logo
ImTextureID logoTex = app->getCoinLogoTexture();
ImFont* heroFont = Type().h2();
ImFont* sub1 = Type().subtitle1();
ImFont* capFont = Type().caption();
ImFont* ovFont = Type().overline();
float logoSz = heroFont->LegacySize + capFont->LegacySize + 4.0f * dp;
if (logoTex != 0) {
@@ -2157,7 +2169,7 @@ static void RenderBalanceConsolidated(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float consAddrOverride = S.drawElement("tabs.balance.consolidated", "address-table-height").size;
float addrH = (consAddrOverride >= 0.0f) ? consAddrOverride
float addrH = (consAddrOverride >= 0.0f) ? consAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2199,22 +2211,28 @@ static void RenderBalanceDashboard(App* app) {
: (int)S.drawElement("tabs.balance.dashboard", "tile-num-cols").sizeOr(4.0f);
int numRows = (numCols == 2) ? 2 : 1;
float tileW = (availW - (numCols - 1) * cGap) / numCols;
ImFont* ovFont = Type().overline();
ImFont* sub1 = Type().subtitle1();
ImFont* capFont = Type().caption();
// Font-content floor: pad + overline + subtitle1 + caption + pad
float dashFontFloor = Layout::spacingLg()
+ ovFont->LegacySize + Layout::spacingSm()
+ sub1->LegacySize + Layout::spacingSm()
+ capFont->LegacySize
+ Layout::spacingLg();
float dashCardH = S.drawElement("tabs.balance.dashboard", "card-height").size;
float tileH;
if (dashCardH >= 0.0f) {
tileH = dashCardH; // explicit override from ui.toml
tileH = std::max(dashCardH * dp, dashFontFloor);
} else {
tileH = std::max(
S.drawElement("tabs.balance.dashboard", "tile-min-height").sizeOr(70.0f),
contentAvail.y * S.drawElement("tabs.balance.dashboard", "tile-height-ratio").sizeOr(0.16f) / numRows);
tileH = std::max({dashFontFloor,
S.drawElement("tabs.balance.dashboard", "tile-min-height").sizeOr(70.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.dashboard", "tile-height-ratio").sizeOr(0.16f) / numRows});
}
ImVec2 origin = ImGui::GetCursorScreenPos();
GlassPanelSpec tileSpec;
tileSpec.rounding = glassRound;
ImFont* ovFont = Type().overline();
ImFont* sub1 = Type().subtitle1();
ImFont* capFont = Type().caption();
struct TileInfo {
const char* label;
@@ -2299,7 +2317,7 @@ static void RenderBalanceDashboard(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float dashAddrOverride = S.drawElement("tabs.balance.dashboard", "address-table-height").size;
float addrH = (dashAddrOverride >= 0.0f) ? dashAddrOverride
float addrH = (dashAddrOverride >= 0.0f) ? dashAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2329,23 +2347,28 @@ static void RenderBalanceVerticalStack(App* app) {
if (vstackTopMargin >= 0.0f)
ImGui::Dummy(ImVec2(0, vstackTopMargin));
float vstackCardH = S.drawElement("tabs.balance.vertical-stack", "card-height").size;
float stackH;
if (vstackCardH >= 0.0f) {
stackH = vstackCardH; // explicit override from ui.toml
} else {
stackH = std::max(
S.drawElement("tabs.balance.vertical-stack", "stack-min-height").sizeOr(80.0f),
contentAvail.y * S.drawElement("tabs.balance.vertical-stack", "stack-height-ratio").sizeOr(0.16f));
}
float rowGap = S.drawElement("tabs.balance.vertical-stack", "row-gap").sizeOr(2.0f);
float rowH = (stackH - 3 * rowGap) / 4.0f;
float rowMinH = S.drawElement("tabs.balance.vertical-stack", "row-min-height").sizeOr(20.0f);
if (rowH < rowMinH) rowH = rowMinH;
ImFont* capFont = Type().caption();
ImFont* body2 = Type().body2();
ImFont* sub1 = Type().subtitle1();
// Font-content floor per row: icon + label + value must fit
float vstackRowFontFloor = std::max(body2->LegacySize, capFont->LegacySize)
+ Layout::spacingSm() * 2;
float rowGap = S.drawElement("tabs.balance.vertical-stack", "row-gap").sizeOr(2.0f);
float vstackFontFloor = vstackRowFontFloor * 4 + rowGap * 3;
float vstackCardH = S.drawElement("tabs.balance.vertical-stack", "card-height").size;
float stackH;
if (vstackCardH >= 0.0f) {
stackH = std::max(vstackCardH * dp, vstackFontFloor);
} else {
stackH = std::max({vstackFontFloor,
S.drawElement("tabs.balance.vertical-stack", "stack-min-height").sizeOr(80.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.vertical-stack", "stack-height-ratio").sizeOr(0.16f)});
}
float rowH = (stackH - 3 * rowGap) / 4.0f;
float rowMinH = std::max(
S.drawElement("tabs.balance.vertical-stack", "row-min-height").sizeOr(20.0f) * dp,
vstackRowFontFloor);
if (rowH < rowMinH) rowH = rowMinH;
float total = (float)s_dispTotal;
float shieldRatio = (total > 1e-9f) ? (float)(s_dispShielded / total) : 0.5f;
@@ -2473,7 +2496,7 @@ static void RenderBalanceVerticalStack(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float vstackAddrOverride = S.drawElement("tabs.balance.vertical-stack", "address-table-height").size;
float addrH = (vstackAddrOverride >= 0.0f) ? vstackAddrOverride
float addrH = (vstackAddrOverride >= 0.0f) ? vstackAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2505,26 +2528,30 @@ static void RenderBalanceVertical2x2(App* app) {
if (topMargin >= 0.0f)
ImGui::Dummy(ImVec2(0, topMargin));
ImFont* capFont = Type().caption();
ImFont* iconFont = Type().iconSmall();
// Font-content floor per row: caption text + vertical padding
float v2x2RowFontFloor = capFont->LegacySize + Layout::spacingSm() * 2;
float rowGap = S.drawElement(cfgSec, "row-gap").sizeOr(2.0f);
float colGap = S.drawElement(cfgSec, "col-gap").sizeOr(8.0f);
float v2x2FontFloor = v2x2RowFontFloor * 2 + rowGap;
float cardHOverride = S.drawElement(cfgSec, "card-height").size;
float stackH;
if (cardHOverride >= 0.0f) {
stackH = cardHOverride;
stackH = std::max(cardHOverride * dp, v2x2FontFloor);
} else {
stackH = std::max(
S.drawElement(cfgSec, "stack-min-height").sizeOr(60.0f),
contentAvail.y * S.drawElement(cfgSec, "stack-height-ratio").sizeOr(0.12f));
stackH = std::max({v2x2FontFloor,
S.drawElement(cfgSec, "stack-min-height").sizeOr(60.0f) * dp,
contentAvail.y * S.drawElement(cfgSec, "stack-height-ratio").sizeOr(0.12f)});
}
float rowGap = S.drawElement(cfgSec, "row-gap").sizeOr(2.0f);
float colGap = S.drawElement(cfgSec, "col-gap").sizeOr(8.0f);
float rowH = (stackH - rowGap) / 2.0f;
float rowMinH = S.drawElement(cfgSec, "row-min-height").sizeOr(24.0f);
float rowMinH = std::max(
S.drawElement(cfgSec, "row-min-height").sizeOr(24.0f) * dp,
v2x2RowFontFloor);
if (rowH < rowMinH) rowH = rowMinH;
float colW = (availW - colGap) / 2.0f;
ImFont* capFont = Type().caption();
ImFont* iconFont = Type().iconSmall();
float padOverride = S.drawElement(cfgSec, "card-padding").size;
float rowPad = (padOverride >= 0.0f) ? padOverride : Layout::spacingLg();
float rowBgAlpha = S.drawElement(cfgSec, "row-bg-alpha").sizeOr(8.0f);
@@ -2664,7 +2691,7 @@ static void RenderBalanceVertical2x2(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float addrOverride = S.drawElement(cfgSec, "address-table-height").size;
float addrH = (addrOverride >= 0.0f) ? addrOverride
float addrH = (addrOverride >= 0.0f) ? addrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2722,14 +2749,20 @@ static void RenderBalanceShield(App* app) {
// Shield gauge panel
{
ImFont* shieldCap = Type().caption();
ImFont* shieldSub1 = Type().subtitle1();
// Font-content floor: gauge area + legend text (subtitle + 2 captions)
float shieldFontFloor = Layout::spacingLg() * 2
+ shieldSub1->LegacySize + Layout::spacingSm()
+ shieldCap->LegacySize * 2 + Layout::spacingSm() * 2;
float shieldCardH = S.drawElement("tabs.balance.shield", "card-height").size;
float gaugeH;
if (shieldCardH >= 0.0f) {
gaugeH = shieldCardH; // explicit override from ui.toml
gaugeH = std::max(shieldCardH * dp, shieldFontFloor);
} else {
gaugeH = std::max(
S.drawElement("tabs.balance.shield", "gauge-min-height").sizeOr(80.0f),
contentAvail.y * S.drawElement("tabs.balance.shield", "gauge-height-ratio").sizeOr(0.18f));
gaugeH = std::max({shieldFontFloor,
S.drawElement("tabs.balance.shield", "gauge-min-height").sizeOr(80.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.shield", "gauge-height-ratio").sizeOr(0.18f)});
}
ImVec2 panelMin = ImGui::GetCursorScreenPos();
ImVec2 panelMax(panelMin.x + availW, panelMin.y + gaugeH);
@@ -2834,7 +2867,7 @@ static void RenderBalanceShield(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float shieldAddrOverride = S.drawElement("tabs.balance.shield", "address-table-height").size;
float addrH = (shieldAddrOverride >= 0.0f) ? shieldAddrOverride
float addrH = (shieldAddrOverride >= 0.0f) ? shieldAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -2895,10 +2928,10 @@ static void RenderBalanceTimeline(App* app) {
float tlChartH = S.drawElement("tabs.balance.timeline", "chart-height").size;
float chartH;
if (tlChartH >= 0.0f) {
chartH = tlChartH; // explicit override from ui.toml
chartH = tlChartH * dp; // scaled by dp for DPI + font scale
} else {
chartH = std::max(
S.drawElement("tabs.balance.timeline", "chart-min-height").sizeOr(80.0f),
S.drawElement("tabs.balance.timeline", "chart-min-height").sizeOr(80.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.timeline", "chart-height-ratio").sizeOr(0.20f));
}
ImVec2 chartMin = ImGui::GetCursorScreenPos();
@@ -2935,21 +2968,25 @@ static void RenderBalanceTimeline(App* app) {
// Compact 3 summary cards
ImGui::Dummy(ImVec2(0, cGap));
{
ImFont* ovFont = Type().overline();
ImFont* capFont = Type().caption();
// Font-content floor: pad + overline + gap + caption + pad
float tlPadVal = S.drawElement("tabs.balance.timeline", "card-padding").size;
float tlPad = (tlPadVal >= 0.0f) ? tlPadVal : Layout::spacingXs();
float tlFontFloor = tlPad + ovFont->LegacySize + 2.0f * dp + capFont->LegacySize + tlPad;
float tlSummaryH = S.drawElement("tabs.balance.timeline", "summary-card-height").size;
float cardH;
if (tlSummaryH >= 0.0f) {
cardH = tlSummaryH; // explicit override from ui.toml
cardH = std::max(tlSummaryH * dp, tlFontFloor);
} else {
cardH = std::max(
S.drawElement("tabs.balance.timeline", "summary-min-height").sizeOr(44.0f),
contentAvail.y * S.drawElement("tabs.balance.timeline", "summary-height-ratio").sizeOr(0.08f));
cardH = std::max({tlFontFloor,
S.drawElement("tabs.balance.timeline", "summary-min-height").sizeOr(44.0f) * dp,
contentAvail.y * S.drawElement("tabs.balance.timeline", "summary-height-ratio").sizeOr(0.08f)});
}
float cardW = (availW - 2 * cGap) / 3.0f;
ImVec2 origin = ImGui::GetCursorScreenPos();
GlassPanelSpec spec;
spec.rounding = glassRound;
ImFont* ovFont = Type().overline();
ImFont* capFont = Type().caption();
struct SumCard { const char* label; ImU32 col; double val; bool isMoney; };
SumCard cards[3] = {
@@ -2983,7 +3020,7 @@ static void RenderBalanceTimeline(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float tlAddrOverride = S.drawElement("tabs.balance.timeline", "address-table-height").size;
float addrH = (tlAddrOverride >= 0.0f) ? tlAddrOverride
float addrH = (tlAddrOverride >= 0.0f) ? tlAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -3055,14 +3092,17 @@ static void RenderBalanceTwoRow(App* app) {
// Row 2: 3 mini-cards inline
{
ImFont* twoRowCap = Type().caption();
// Font-content floor: caption centered + vertical padding
float twoRowFontFloor = twoRowCap->LegacySize + Layout::spacingSm() * 2;
float twoRowCardH = S.drawElement("tabs.balance.two-row", "card-height").size;
float miniH;
if (twoRowCardH >= 0.0f) {
miniH = twoRowCardH; // explicit override from ui.toml
miniH = std::max(twoRowCardH * dp, twoRowFontFloor);
} else {
miniH = std::max(
S.drawElement("tabs.balance.two-row", "mini-min-height").sizeOr(28.0f),
S.drawElement("tabs.balance.two-row", "mini-base-height").sizeOr(36.0f) * vs);
miniH = std::max({twoRowFontFloor,
S.drawElement("tabs.balance.two-row", "mini-min-height").sizeOr(28.0f) * dp,
S.drawElement("tabs.balance.two-row", "mini-base-height").sizeOr(36.0f) * vs});
}
float miniW = (availW - 2 * cGap) / 3.0f;
ImVec2 origin = ImGui::GetCursorScreenPos();
@@ -3170,7 +3210,7 @@ static void RenderBalanceTwoRow(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float twoRowAddrOverride = S.drawElement("tabs.balance.two-row", "address-table-height").size;
float addrH = (twoRowAddrOverride >= 0.0f) ? twoRowAddrOverride
float addrH = (twoRowAddrOverride >= 0.0f) ? twoRowAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
@@ -3191,6 +3231,7 @@ static void RenderBalanceMinimal(App* app) {
float hs = Layout::hScale(availW);
float vs = Layout::vScale(contentAvail.y);
float glassRound = Layout::glassRounding();
const float dp = Layout::dpiScale();
ImDrawList* dl = ImGui::GetWindowDrawList();
char buf[64];
@@ -3262,7 +3303,7 @@ static void RenderBalanceMinimal(App* app) {
float recentReserve = contentAvail.y * S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
float minAddrOverride = S.drawElement("tabs.balance.minimal", "address-table-height").size;
float addrH = (minAddrOverride >= 0.0f) ? minAddrOverride
float addrH = (minAddrOverride >= 0.0f) ? minAddrOverride * dp
: ImGui::GetContentRegionAvail().y - recentReserve
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);