feat: Full UI internationalization, pool hashrate stats, and layout caching

- Replace all hardcoded English strings with TR() translation keys across
  every tab, dialog, and component (~20 UI files)
- Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with
  complete translations (~37k lines added)
- Improve i18n loader with exe-relative path fallback and English base
  fallback for missing keys
- Add pool-side hashrate polling via pool stats API in xmrig_manager
- Introduce Layout::beginFrame() per-frame caching and refresh balance
  layout config only on schema generation change
- Offload daemon output parsing to worker thread
- Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
This commit is contained in:
dan_s
2026-03-11 00:40:50 -05:00
parent cc617dd5be
commit 96c27bb949
71 changed files with 43567 additions and 5267 deletions

View File

@@ -185,6 +185,102 @@ inline float kItemSpacing() { return schema::UI().drawElement("spacing", "it
inline float kLabelValueGap() { return schema::UI().drawElement("spacing", "label-value").sizeOr(4.0f) * dpiScale(); }
inline float kSeparatorGap() { return schema::UI().drawElement("spacing", "separator").sizeOr(20.0f) * dpiScale(); }
// ============================================================================
// Per-frame cache — populated once via beginFrame(), avoids repeated
// schema hash-map lookups for the hottest accessors.
// ============================================================================
namespace detail {
struct FrameCache {
uint32_t gen = 0; // schema generation when last populated
float rawDp = 1.0f; // rawDpiScale() snapshot
float dp = 1.0f; // dpiScale() snapshot
// Spacing tokens (raw, unscaled)
float spXs = 2.0f;
float spSm = 4.0f;
float spMd = 8.0f;
float spLg = 12.0f;
float spXl = 16.0f;
float spXxl = 24.0f;
// Responsive scale config
float refW = 1200.0f;
float refH = 700.0f;
float minHS = 0.5f;
float maxHS = 1.5f;
float minVS = 0.5f;
float maxVS = 1.4f;
float minDen = 0.6f;
float maxDen = 1.2f;
// Breakpoint thresholds (raw, unscaled)
float compactW = 500.0f;
float compactH = 450.0f;
float expandedW = 900.0f;
float expandedH = 750.0f;
// Glass / card helpers (raw, unscaled)
float glassRnd = 8.0f;
float cardPad = 12.0f;
float cardGap = 8.0f;
};
inline FrameCache& frameCache() { static FrameCache c; return c; }
} // namespace detail
/**
* @brief Refresh the per-frame layout cache.
*
* Call once per frame (e.g., from App::preFrame) BEFORE any rendering.
* Reads all high-frequency TOML values into fast statics. Invalidated
* automatically when the schema generation changes (hot-reload).
*/
inline void beginFrame() {
auto& c = detail::frameCache();
uint32_t g = schema::UI().generation();
// Also capture DPI each frame (can change on display-scale events)
float curRawDp = rawDpiScale();
float curDp = dpiScale();
if (g == c.gen && curRawDp == c.rawDp && curDp == c.dp) return;
c.gen = g;
c.rawDp = curRawDp;
c.dp = curDp;
const auto& S = schema::UI();
// Spacing tokens
c.spXs = S.drawElement("spacing-tokens", "xs").sizeOr(2.0f);
c.spSm = S.drawElement("spacing-tokens", "sm").sizeOr(4.0f);
c.spMd = S.drawElement("spacing-tokens", "md").sizeOr(8.0f);
c.spLg = S.drawElement("spacing-tokens", "lg").sizeOr(12.0f);
c.spXl = S.drawElement("spacing-tokens", "xl").sizeOr(16.0f);
c.spXxl = S.drawElement("spacing-tokens", "xxl").sizeOr(24.0f);
// Responsive config
c.refW = S.drawElement("responsive", "ref-width").sizeOr(1200.0f);
c.refH = S.drawElement("responsive", "ref-height").sizeOr(700.0f);
c.minHS = S.drawElement("responsive", "min-h-scale").sizeOr(0.5f);
c.maxHS = S.drawElement("responsive", "max-h-scale").sizeOr(1.5f);
c.minVS = S.drawElement("responsive", "min-v-scale").sizeOr(0.5f);
c.maxVS = S.drawElement("responsive", "max-v-scale").sizeOr(1.4f);
c.minDen = S.drawElement("responsive", "min-density").sizeOr(0.6f);
c.maxDen = S.drawElement("responsive", "max-density").sizeOr(1.2f);
// Breakpoints
c.compactW = S.drawElement("responsive", "compact-width").sizeOr(500.0f);
c.compactH = S.drawElement("responsive", "compact-height").sizeOr(450.0f);
c.expandedW = S.drawElement("responsive", "expanded-width").sizeOr(900.0f);
c.expandedH = S.drawElement("responsive", "expanded-height").sizeOr(750.0f);
// Glass / card
c.glassRnd = S.drawElement("responsive", "glass-rounding").sizeOr(8.0f);
c.cardPad = S.drawElement("responsive", "card-inner-padding").sizeOr(12.0f);
c.cardGap = S.drawElement("responsive", "card-gap").sizeOr(8.0f);
}
// ============================================================================
// Layout Tier (responsive breakpoints)
// ============================================================================
@@ -203,12 +299,12 @@ enum class LayoutTier { Compact, Normal, Expanded };
* Call after ImGui::BeginChild for the content area, or pass explicit avail.
*/
inline LayoutTier currentTier() {
const auto& S = schema::UI();
float dp = dpiScale();
float cw = S.drawElement("responsive", "compact-width").sizeOr(500.0f) * dp;
float ch = S.drawElement("responsive", "compact-height").sizeOr(450.0f) * dp;
float ew = S.drawElement("responsive", "expanded-width").sizeOr(900.0f) * dp;
float eh = S.drawElement("responsive", "expanded-height").sizeOr(750.0f) * dp;
const auto& c = detail::frameCache();
float dp = c.dp;
float cw = c.compactW * dp;
float ch = c.compactH * dp;
float ew = c.expandedW * dp;
float eh = c.expandedH * dp;
ImVec2 avail = ImGui::GetContentRegionAvail();
if (avail.x < cw || avail.y < ch) return LayoutTier::Compact;
if (avail.x > ew && avail.y > eh) return LayoutTier::Expanded;
@@ -216,12 +312,12 @@ inline LayoutTier currentTier() {
}
inline LayoutTier currentTier(float availW, float availH) {
const auto& S = schema::UI();
float dp = dpiScale();
float cw = S.drawElement("responsive", "compact-width").sizeOr(500.0f) * dp;
float ch = S.drawElement("responsive", "compact-height").sizeOr(450.0f) * dp;
float ew = S.drawElement("responsive", "expanded-width").sizeOr(900.0f) * dp;
float eh = S.drawElement("responsive", "expanded-height").sizeOr(750.0f) * dp;
const auto& c = detail::frameCache();
float dp = c.dp;
float cw = c.compactW * dp;
float ch = c.compactH * dp;
float ew = c.expandedW * dp;
float eh = c.expandedH * dp;
if (availW < cw || availH < ch) return LayoutTier::Compact;
if (availW > ew && availH > eh) return LayoutTier::Expanded;
return LayoutTier::Normal;
@@ -240,15 +336,10 @@ inline LayoutTier currentTier(float availW, float availH) {
* while emitting physical-pixel results.
*/
inline float hScale(float availWidth) {
const auto& S = schema::UI();
float rawDp = rawDpiScale(); // reference uses hardware DPI only
float dp = dpiScale(); // output includes user font scale
float rw = S.drawElement("responsive", "ref-width").sizeOr(1200.0f) * rawDp;
float minH = S.drawElement("responsive", "min-h-scale").sizeOr(0.5f);
float maxH = S.drawElement("responsive", "max-h-scale").sizeOr(1.5f);
// Clamp the logical (DPI-neutral) portion, then apply effective DPI.
float logical = std::clamp(availWidth / rw, minH, maxH);
return logical * dp;
const auto& c = detail::frameCache();
float rw = c.refW * c.rawDp;
float logical = std::clamp(availWidth / rw, c.minHS, c.maxHS);
return logical * c.dp;
}
inline float hScale() {
@@ -261,14 +352,10 @@ inline float hScale() {
* Same decomposition as hScale — logical clamp × DPI.
*/
inline float vScale(float availHeight) {
const auto& S = schema::UI();
float rawDp = rawDpiScale(); // reference uses hardware DPI only
float dp = dpiScale(); // output includes user font scale
float rh = S.drawElement("responsive", "ref-height").sizeOr(700.0f) * rawDp;
float minV = S.drawElement("responsive", "min-v-scale").sizeOr(0.5f);
float maxV = S.drawElement("responsive", "max-v-scale").sizeOr(1.4f);
float logical = std::clamp(availHeight / rh, minV, maxV);
return logical * dp;
const auto& c = detail::frameCache();
float rh = c.refH * c.rawDp;
float logical = std::clamp(availHeight / rh, c.minVS, c.maxVS);
return logical * c.dp;
}
inline float vScale() {
@@ -282,14 +369,10 @@ inline float vScale() {
* values scale proportionally with fonts and style.
*/
inline float densityScale(float availHeight) {
const auto& S = schema::UI();
float rawDp = rawDpiScale(); // reference uses hardware DPI only
float dp = dpiScale(); // output includes user font scale
float rh = S.drawElement("responsive", "ref-height").sizeOr(700.0f) * rawDp;
float minDen = S.drawElement("responsive", "min-density").sizeOr(0.6f);
float maxDen = S.drawElement("responsive", "max-density").sizeOr(1.2f);
float logical = std::clamp(availHeight / rh, minDen, maxDen);
return logical * dp;
const auto& c = detail::frameCache();
float rh = c.refH * c.rawDp;
float logical = std::clamp(availHeight / rh, c.minDen, c.maxDen);
return logical * c.dp;
}
inline float densityScale() {
@@ -301,33 +384,33 @@ inline float densityScale() {
// ============================================================================
/** @brief Get spacing token scaled by current density. */
inline float spacingXs() { return schema::UI().drawElement("spacing-tokens", "xs").sizeOr(2.0f) * densityScale(); }
inline float spacingSm() { return schema::UI().drawElement("spacing-tokens", "sm").sizeOr(4.0f) * densityScale(); }
inline float spacingMd() { return schema::UI().drawElement("spacing-tokens", "md").sizeOr(8.0f) * densityScale(); }
inline float spacingLg() { return schema::UI().drawElement("spacing-tokens", "lg").sizeOr(12.0f) * densityScale(); }
inline float spacingXl() { return schema::UI().drawElement("spacing-tokens", "xl").sizeOr(16.0f) * densityScale(); }
inline float spacingXxl() { return schema::UI().drawElement("spacing-tokens", "xxl").sizeOr(24.0f) * densityScale(); }
inline float spacingXs() { return detail::frameCache().spXs * densityScale(); }
inline float spacingSm() { return detail::frameCache().spSm * densityScale(); }
inline float spacingMd() { return detail::frameCache().spMd * densityScale(); }
inline float spacingLg() { return detail::frameCache().spLg * densityScale(); }
inline float spacingXl() { return detail::frameCache().spXl * densityScale(); }
inline float spacingXxl() { return detail::frameCache().spXxl * densityScale(); }
/** @brief Get raw (unscaled) spacing token. */
inline float spacingXsRaw() { return schema::UI().drawElement("spacing-tokens", "xs").sizeOr(2.0f); }
inline float spacingSmRaw() { return schema::UI().drawElement("spacing-tokens", "sm").sizeOr(4.0f); }
inline float spacingMdRaw() { return schema::UI().drawElement("spacing-tokens", "md").sizeOr(8.0f); }
inline float spacingLgRaw() { return schema::UI().drawElement("spacing-tokens", "lg").sizeOr(12.0f); }
inline float spacingXlRaw() { return schema::UI().drawElement("spacing-tokens", "xl").sizeOr(16.0f); }
inline float spacingXxlRaw() { return schema::UI().drawElement("spacing-tokens", "xxl").sizeOr(24.0f); }
inline float spacingXsRaw() { return detail::frameCache().spXs; }
inline float spacingSmRaw() { return detail::frameCache().spSm; }
inline float spacingMdRaw() { return detail::frameCache().spMd; }
inline float spacingLgRaw() { return detail::frameCache().spLg; }
inline float spacingXlRaw() { return detail::frameCache().spXl; }
inline float spacingXxlRaw() { return detail::frameCache().spXxl; }
// ============================================================================
// Responsive Globals Helpers
// ============================================================================
/** @brief Default glass panel rounding (8.0 default). */
inline float glassRounding() { return schema::UI().drawElement("responsive", "glass-rounding").sizeOr(8.0f) * dpiScale(); }
inline float glassRounding() { return detail::frameCache().glassRnd * dpiScale(); }
/** @brief Default card inner padding (12.0 default). */
inline float cardInnerPadding() { return schema::UI().drawElement("responsive", "card-inner-padding").sizeOr(12.0f) * dpiScale(); }
inline float cardInnerPadding() { return detail::frameCache().cardPad * dpiScale(); }
/** @brief Default card gap (8.0 default). */
inline float cardGap() { return schema::UI().drawElement("responsive", "card-gap").sizeOr(8.0f) * dpiScale(); }
inline float cardGap() { return detail::frameCache().cardGap * dpiScale(); }
/**
* @brief Compute a responsive card height from a base value.