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:
187
src/ui/layout.h
187
src/ui/layout.h
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user