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:
@@ -11,6 +11,7 @@
|
||||
#include "layout.h"
|
||||
#include "schema/ui_schema.h"
|
||||
#include "../embedded/IconsMaterialDesign.h"
|
||||
#include "../util/i18n.h"
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
@@ -34,25 +35,35 @@ enum class NavPage {
|
||||
};
|
||||
|
||||
struct NavItem {
|
||||
const char* label;
|
||||
const char* label; // fallback label (English)
|
||||
NavPage page;
|
||||
const char* section_label; // if non-null, render section label above this item
|
||||
const char* section_label; // if non-null, render section label above this item
|
||||
const char* tr_key; // i18n key for label
|
||||
const char* section_tr_key; // i18n key for section_label
|
||||
};
|
||||
|
||||
inline const NavItem kNavItems[] = {
|
||||
{ "Overview", NavPage::Overview, nullptr },
|
||||
{ "Send", NavPage::Send, nullptr },
|
||||
{ "Receive", NavPage::Receive, nullptr },
|
||||
{ "History", NavPage::History, nullptr },
|
||||
{ "Mining", NavPage::Mining, "TOOLS" },
|
||||
{ "Market", NavPage::Market, nullptr },
|
||||
{ "Console", NavPage::Console, "ADVANCED" },
|
||||
{ "Network", NavPage::Peers, nullptr },
|
||||
{ "Settings", NavPage::Settings, nullptr },
|
||||
{ "Overview", NavPage::Overview, nullptr, "overview", nullptr },
|
||||
{ "Send", NavPage::Send, nullptr, "send", nullptr },
|
||||
{ "Receive", NavPage::Receive, nullptr, "receive", nullptr },
|
||||
{ "History", NavPage::History, nullptr, "history", nullptr },
|
||||
{ "Mining", NavPage::Mining, "TOOLS", "mining", "tools" },
|
||||
{ "Market", NavPage::Market, nullptr, "market", nullptr },
|
||||
{ "Console", NavPage::Console, "ADVANCED","console", "advanced" },
|
||||
{ "Network", NavPage::Peers, nullptr, "network", nullptr },
|
||||
{ "Settings", NavPage::Settings, nullptr, "settings", nullptr },
|
||||
};
|
||||
static_assert(sizeof(kNavItems) / sizeof(kNavItems[0]) == (int)NavPage::Count_,
|
||||
"kNavItems must match NavPage::Count_");
|
||||
|
||||
// Get translated nav label at runtime
|
||||
inline const char* NavLabel(const NavItem& item) {
|
||||
return item.tr_key ? TR(item.tr_key) : item.label;
|
||||
}
|
||||
inline const char* NavSectionLabel(const NavItem& item) {
|
||||
return item.section_tr_key ? TR(item.section_tr_key) : item.section_label;
|
||||
}
|
||||
|
||||
// Get the Material Design icon string for a navigation page.
|
||||
inline const char* GetNavIconMD(NavPage page)
|
||||
{
|
||||
@@ -541,7 +552,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
float olFsz = ScaledFontSize(olFont);
|
||||
dl->AddText(olFont, olFsz,
|
||||
ImVec2(wp.x + sbSectionLabelPadLeft, labelY),
|
||||
ImGui::ColorConvertFloat4ToU32(olCol), item.section_label);
|
||||
ImGui::ColorConvertFloat4ToU32(olCol), NavSectionLabel(item));
|
||||
ImGui::Dummy(ImVec2(0, olFsz + 2.0f));
|
||||
} else if (item.section_label && !showLabels) {
|
||||
// Collapsed: thin separator instead of label
|
||||
@@ -609,11 +620,19 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
ImU32 textCol = selected ? Primary() : OnSurfaceMedium();
|
||||
|
||||
if (showLabels) {
|
||||
// Measure total width of icon + gap + label, then center
|
||||
// Measure total width of icon + gap + label, then center.
|
||||
// If the translated label is too wide, shrink the font to fit.
|
||||
ImFont* font = selected ? Type().subtitle2() : Type().body2();
|
||||
float gap = iconLabelGap;
|
||||
float lblFsz = ScaledFontSize(font);
|
||||
ImVec2 labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, item.label);
|
||||
float btnW = indMax.x - indMin.x;
|
||||
float maxLabelW = btnW - iconS * 2.0f - gap - Layout::spacingXs() * 2;
|
||||
ImVec2 labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
|
||||
if (labelSz.x > maxLabelW && maxLabelW > 0) {
|
||||
float shrink = maxLabelW / labelSz.x;
|
||||
lblFsz *= shrink;
|
||||
labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
|
||||
}
|
||||
float totalW = iconS * 2.0f + gap + labelSz.x;
|
||||
float btnCX = (indMin.x + indMax.x) * 0.5f;
|
||||
float startX = btnCX - totalW * 0.5f;
|
||||
@@ -625,7 +644,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
ImVec4 lc = ImGui::ColorConvertU32ToFloat4(textCol);
|
||||
lc.w *= expandFrac;
|
||||
dl->AddText(font, lblFsz, ImVec2(labelX, textY),
|
||||
ImGui::ColorConvertFloat4ToU32(lc), item.label);
|
||||
ImGui::ColorConvertFloat4ToU32(lc), NavLabel(item));
|
||||
} else {
|
||||
float iconCX = (indMin.x + indMax.x) * 0.5f;
|
||||
DrawNavIcon(dl, item.page, iconCX, iconCY, iconS, textCol);
|
||||
@@ -633,7 +652,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
|
||||
// Tooltip when collapsed + hovered
|
||||
if (!showLabels && hovered) {
|
||||
ImGui::SetTooltip("%s", item.label);
|
||||
ImGui::SetTooltip("%s", NavLabel(item));
|
||||
}
|
||||
|
||||
// ---- Badge indicator ----
|
||||
|
||||
Reference in New Issue
Block a user