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:
@@ -214,7 +214,7 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
auto& S = schema::UI();
|
||||
char buf[256];
|
||||
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "SENDING FROM");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_sending_from"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Auto-select the address with the largest balance on first load
|
||||
@@ -237,7 +237,7 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
|
||||
// Build preview string for selected address
|
||||
if (!app->isConnected()) {
|
||||
s_source_preview = "Not connected to daemon";
|
||||
s_source_preview = TR("not_connected");
|
||||
} else if (s_selected_from_idx >= 0 &&
|
||||
s_selected_from_idx < (int)state.addresses.size()) {
|
||||
const auto& addr = state.addresses[s_selected_from_idx];
|
||||
@@ -249,7 +249,7 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
tag, trunc.c_str(), addr.balance, DRAGONX_TICKER);
|
||||
s_source_preview = buf;
|
||||
} else {
|
||||
s_source_preview = "Select a source address...";
|
||||
s_source_preview = TR("send_select_source");
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(width);
|
||||
@@ -257,7 +257,7 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
ImGui::PushFont(Type().getFont(TypeStyle::Body2));
|
||||
if (ImGui::BeginCombo("##SendFrom", s_source_preview.c_str())) {
|
||||
if (!app->isConnected() || state.addresses.empty()) {
|
||||
ImGui::TextDisabled("No addresses available");
|
||||
ImGui::TextDisabled("%s", TR("no_addresses_available"));
|
||||
} else {
|
||||
// Sort by balance descending, only show addresses with balance
|
||||
std::vector<size_t> sortedIdx;
|
||||
@@ -272,7 +272,7 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
});
|
||||
|
||||
if (sortedIdx.empty()) {
|
||||
ImGui::TextDisabled("No addresses with balance");
|
||||
ImGui::TextDisabled("%s", TR("send_no_balance"));
|
||||
} else {
|
||||
size_t addrTruncLen = static_cast<size_t>(std::max(S.drawElement("tabs.send", "addr-dropdown-trunc-min").size, width / S.drawElement("tabs.send", "addr-dropdown-trunc-divisor").size));
|
||||
|
||||
@@ -352,7 +352,7 @@ static void RenderAddressSuggestions(const WalletState& state, float width, cons
|
||||
// ============================================================================
|
||||
static void RenderFeeTierSelector(const char* suffix = "") {
|
||||
auto& S = schema::UI();
|
||||
const char* feeLabels[] = { "Low", "Normal", "High" };
|
||||
const char* feeLabels[] = { TR("send_fee_low"), TR("send_fee_normal"), TR("send_fee_high") };
|
||||
const double feeValues[] = { FEE_LOW, FEE_NORMAL, FEE_HIGH };
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
@@ -569,12 +569,12 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-bg-alpha").size)));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-hover-alpha").size)));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, schema::UI().drawElement("tabs.send", "error-btn-rounding").size);
|
||||
if (TactileSmallButton("Copy Error##txErr", schema::UI().resolveFont("button"))) {
|
||||
if (TactileSmallButton(TR("send_copy_error"), schema::UI().resolveFont("button"))) {
|
||||
ImGui::SetClipboardText(s_tx_status.c_str());
|
||||
Notifications::instance().info("Error copied to clipboard");
|
||||
Notifications::instance().info(TR("send_error_copied"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileSmallButton("Dismiss##txErr", schema::UI().resolveFont("button"))) {
|
||||
if (TactileSmallButton(TR("send_dismiss"), schema::UI().resolveFont("button"))) {
|
||||
s_tx_status.clear();
|
||||
s_result_txid.clear();
|
||||
s_status_success = false;
|
||||
@@ -615,7 +615,7 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
||||
dl->AddText(iconFont, iconFont->LegacySize,
|
||||
ImVec2(ix, iy), Primary(), spinIcon);
|
||||
double elapsed = ImGui::GetTime() - s_send_start_time;
|
||||
snprintf(buf, sizeof(buf), "Submitting transaction... (%.0fs)", elapsed);
|
||||
snprintf(buf, sizeof(buf), TR("send_submitting"), elapsed);
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size, iy), OnSurface(), buf);
|
||||
} else {
|
||||
// Success checkmark
|
||||
@@ -624,7 +624,7 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
||||
ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, checkIcon);
|
||||
dl->AddText(iconFont, iconFont->LegacySize,
|
||||
ImVec2(ix, iy), Success(), checkIcon);
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size, iy), Success(), "Transaction sent!");
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size, iy), Success(), TR("send_tx_sent"));
|
||||
|
||||
if (!s_result_txid.empty()) {
|
||||
float txY = iy + body2->LegacySize + schema::UI().drawElement("tabs.send", "txid-y-offset").size;
|
||||
@@ -633,13 +633,13 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
||||
std::string dispTxid = (int)s_result_txid.length() > txidThreshold
|
||||
? s_result_txid.substr(0, txidTruncLen) + "..." + s_result_txid.substr(s_result_txid.length() - txidTruncLen)
|
||||
: s_result_txid;
|
||||
snprintf(buf, sizeof(buf), "TxID: %s", dispTxid.c_str());
|
||||
snprintf(buf, sizeof(buf), TR("send_txid_label"), dispTxid.c_str());
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(ix + schema::UI().drawElement("tabs.send", "txid-label-x-offset").size, txY),
|
||||
OnSurfaceDisabled(), buf);
|
||||
ImGui::SetCursorScreenPos(ImVec2(pMax.x - schema::UI().drawElement("tabs.send", "txid-copy-btn-right-offset").size, txY - schema::UI().drawElement("tabs.send", "txid-copy-btn-y-offset").size));
|
||||
if (TactileSmallButton("Copy##TxID", schema::UI().resolveFont("button"))) {
|
||||
if (TactileSmallButton(TR("copy"), schema::UI().resolveFont("button"))) {
|
||||
ImGui::SetClipboardText(s_result_txid.c_str());
|
||||
Notifications::instance().info("TxID copied to clipboard");
|
||||
Notifications::instance().info(TR("send_txid_copied"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -688,7 +688,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
|
||||
// FROM card
|
||||
{
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "FROM");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("from_upper"));
|
||||
ImVec2 cMin = ImGui::GetCursorScreenPos();
|
||||
ImVec2 cMax(cMin.x + popW, cMin.y + addrCardH);
|
||||
GlassPanelSpec gs; gs.rounding = popGlassRound;
|
||||
@@ -702,7 +702,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
|
||||
// TO card
|
||||
{
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "TO");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("to_upper"));
|
||||
ImVec2 cMin = ImGui::GetCursorScreenPos();
|
||||
ImVec2 cMax(cMin.x + popW, cMin.y + addrCardH);
|
||||
GlassPanelSpec gs; gs.rounding = popGlassRound;
|
||||
@@ -716,7 +716,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
|
||||
// Fee tier selector
|
||||
{
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "NETWORK FEE");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_network_fee"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
RenderFeeTierSelector("##confirm");
|
||||
// Recalculate total after potential fee change
|
||||
@@ -729,7 +729,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
float valX = std::max(schema::UI().drawElement("tabs.send", "confirm-val-col-min-x").size, schema::UI().drawElement("tabs.send", "confirm-val-col-x").size * popVs);
|
||||
float usdX = popW - std::max(schema::UI().drawElement("tabs.send", "confirm-usd-col-min-x").size, schema::UI().drawElement("tabs.send", "confirm-usd-col-x").size * popVs);
|
||||
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "AMOUNT DETAILS");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_amount_details"));
|
||||
ImVec2 cMin = ImGui::GetCursorScreenPos();
|
||||
float cH = std::max(schema::UI().drawElement("tabs.send", "confirm-amount-card-min-height").size, schema::UI().drawElement("tabs.send", "confirm-amount-card-height").size * popVs);
|
||||
ImVec2 cMax(cMin.x + popW, cMin.y + cH);
|
||||
@@ -740,7 +740,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
float cy = cMin.y + Layout::spacingSm() + Layout::spacingXs();
|
||||
float rowStep = std::max(schema::UI().drawElement("tabs.send", "confirm-row-step-min").size, schema::UI().drawElement("tabs.send", "confirm-row-step").size * popVs);
|
||||
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), "Amount");
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), TR("send_amount"));
|
||||
snprintf(buf, sizeof(buf), "%.8f %s", s_amount, DRAGONX_TICKER);
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx + valX, cy), OnSurface(), buf);
|
||||
if (market.price_usd > 0) {
|
||||
@@ -749,7 +749,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
}
|
||||
cy += rowStep;
|
||||
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), "Fee");
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), TR("send_fee"));
|
||||
snprintf(buf, sizeof(buf), "%.8f %s", s_fee, DRAGONX_TICKER);
|
||||
popDl->AddText(capFont, capFont->LegacySize, ImVec2(cx + valX, cy), OnSurface(), buf);
|
||||
if (market.price_usd > 0) {
|
||||
@@ -762,7 +762,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
ImGui::GetColorU32(Divider()), S.drawElement("tabs.send", "confirm-divider-thickness").size);
|
||||
cy += rowStep;
|
||||
|
||||
popDl->AddText(sub1, sub1->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), "Total");
|
||||
popDl->AddText(sub1, sub1->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(), TR("send_total"));
|
||||
snprintf(buf, sizeof(buf), "%.8f %s", total, DRAGONX_TICKER);
|
||||
DrawTextShadow(popDl, sub1, sub1->LegacySize, ImVec2(cx + valX, cy), Primary(), buf);
|
||||
if (market.price_usd > 0) {
|
||||
@@ -774,7 +774,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
}
|
||||
|
||||
if (s_memo[0] != '\0' && is_valid_z) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "MEMO");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("memo_upper"));
|
||||
Type().textColored(TypeStyle::Caption, OnSurface(), s_memo);
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
}
|
||||
@@ -782,7 +782,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
if (s_sending) {
|
||||
Type().text(TypeStyle::Body2, "Sending...");
|
||||
Type().text(TypeStyle::Body2, TR("sending"));
|
||||
} else {
|
||||
if (TactileButton(TR("confirm_and_send"), ImVec2(S.button("tabs.send", "confirm-button").width, std::max(schema::UI().drawElement("tabs.send", "confirm-btn-min-height").size, schema::UI().drawElement("tabs.send", "confirm-btn-base-height").size * popVs)), S.resolveFont(S.button("tabs.send", "confirm-button").font))) {
|
||||
s_sending = true;
|
||||
@@ -801,26 +801,26 @@ void RenderSendConfirmPopup(App* app) {
|
||||
s_sending = false;
|
||||
s_status_timestamp = ImGui::GetTime();
|
||||
if (success) {
|
||||
s_tx_status = "Transaction sent!";
|
||||
s_tx_status = TR("send_tx_sent");
|
||||
s_result_txid = result;
|
||||
s_status_success = true;
|
||||
Notifications::instance().success("Transaction sent successfully!");
|
||||
Notifications::instance().success(TR("send_tx_success"));
|
||||
s_to_address[0] = '\0';
|
||||
s_amount = 0.0;
|
||||
s_memo[0] = '\0';
|
||||
s_send_max = false;
|
||||
} else {
|
||||
s_tx_status = "Error: " + result;
|
||||
s_tx_status = std::string(TR("send_error_prefix")) + result;
|
||||
s_result_txid.clear();
|
||||
s_status_success = false;
|
||||
Notifications::instance().error("Transaction failed: " + result);
|
||||
Notifications::instance().error(std::string(TR("send_tx_failed")) + result);
|
||||
}
|
||||
}
|
||||
);
|
||||
s_show_confirm = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton("Cancel", ImVec2(S.button("tabs.send", "cancel-button").width, std::max(schema::UI().drawElement("tabs.send", "confirm-btn-min-height").size, schema::UI().drawElement("tabs.send", "confirm-btn-base-height").size * popVs)), S.resolveFont(S.button("tabs.send", "cancel-button").font))) {
|
||||
if (TactileButton(TR("cancel"), ImVec2(S.button("tabs.send", "cancel-button").width, std::max(schema::UI().drawElement("tabs.send", "confirm-btn-min-height").size, schema::UI().drawElement("tabs.send", "confirm-btn-base-height").size * popVs)), S.resolveFont(S.button("tabs.send", "cancel-button").font))) {
|
||||
s_show_confirm = false;
|
||||
}
|
||||
}
|
||||
@@ -851,13 +851,13 @@ static bool RenderZeroBalanceCTA(App* app, ImDrawList* dl, float width) {
|
||||
|
||||
float cx = ctaMin.x + Layout::spacingXl();
|
||||
float cy = ctaMin.y + Layout::spacingLg();
|
||||
dl->AddText(sub1, sub1->LegacySize, ImVec2(cx, cy), OnSurface(), "Your wallet is empty");
|
||||
dl->AddText(sub1, sub1->LegacySize, ImVec2(cx, cy), OnSurface(), TR("send_wallet_empty"));
|
||||
cy += sub1->LegacySize + Layout::spacingSm();
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), OnSurfaceMedium(),
|
||||
"Switch to Receive to get your address and start receiving funds.");
|
||||
TR("send_switch_to_receive"));
|
||||
cy += capFont->LegacySize + Layout::spacingMd();
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||
if (TactileButton("Go to Receive", ImVec2(schema::UI().drawElement("tabs.send", "cta-button-width").size, schema::UI().drawElement("tabs.send", "cta-button-height").size), schema::UI().resolveFont("button"))) {
|
||||
if (TactileButton(TR("send_go_to_receive"), ImVec2(schema::UI().drawElement("tabs.send", "cta-button-width").size, schema::UI().drawElement("tabs.send", "cta-button-height").size), schema::UI().resolveFont("button"))) {
|
||||
app->setCurrentPage(NavPage::Receive);
|
||||
}
|
||||
ImGui::SetCursorScreenPos(ImVec2(ctaMin.x, ctaMax.y + Layout::spacingLg()));
|
||||
@@ -904,19 +904,19 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
|
||||
if (!can_send && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
if (!app->isConnected())
|
||||
ImGui::SetTooltip("Not connected to daemon");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_not_connected"));
|
||||
else if (state.sync.syncing)
|
||||
ImGui::SetTooltip("Blockchain is still syncing");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_syncing"));
|
||||
else if (s_from_address[0] == '\0')
|
||||
ImGui::SetTooltip("Select a source address above");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_select_source"));
|
||||
else if (!is_valid_address)
|
||||
ImGui::SetTooltip("Enter a valid recipient address");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_invalid_address"));
|
||||
else if (s_amount <= 0)
|
||||
ImGui::SetTooltip("Enter an amount to send");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_enter_amount"));
|
||||
else if (total > available)
|
||||
ImGui::SetTooltip("Amount exceeds available balance");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_exceeds_balance"));
|
||||
else if (s_sending)
|
||||
ImGui::SetTooltip("Transaction in progress...");
|
||||
ImGui::SetTooltip("%s", TR("send_tooltip_in_progress"));
|
||||
}
|
||||
if (!can_send) ImGui::PopStyleColor(3);
|
||||
|
||||
@@ -946,14 +946,14 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
s_clear_confirm_pending = false;
|
||||
}
|
||||
if (ImGui::BeginPopup(confirmClearId)) {
|
||||
ImGui::Text("Clear all form fields?");
|
||||
ImGui::Text("%s", TR("send_clear_fields"));
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Yes, Clear", ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-yes-width").size, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TR("send_yes_clear"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-yes-width").size, 0), S.resolveFont("button"))) {
|
||||
ClearFormWithUndo();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton("Keep", ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-keep-width").size, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TR("send_keep"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-keep-width").size, 0), S.resolveFont("button"))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
@@ -972,7 +972,7 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
snprintf(undoId, sizeof(undoId), "Undo Clear%s", suffix);
|
||||
if (TactileButton(undoId, ImVec2(width, btnH), S.resolveFont("button"))) {
|
||||
RestoreFormSnapshot();
|
||||
Notifications::instance().info("Form restored");
|
||||
Notifications::instance().info(TR("send_form_restored"));
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar();
|
||||
@@ -989,7 +989,7 @@ static void RenderRecentSends(ImDrawList* dl, const WalletState& state,
|
||||
float width, ImFont* capFont, App* app) {
|
||||
auto& S = schema::UI();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingLg()));
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "RECENT SENDS");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_recent_sends"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||
@@ -1012,7 +1012,7 @@ static void RenderRecentSends(ImDrawList* dl, const WalletState& state,
|
||||
}
|
||||
|
||||
if (sends.empty()) {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), "No recent sends");
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("send_no_recent"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1055,11 +1055,11 @@ static void RenderRecentSends(ImDrawList* dl, const WalletState& state,
|
||||
|
||||
// Type label (first line)
|
||||
float labelX = cx + iconSz * 2.0f + Layout::spacingSm();
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(labelX, cy), sendCol, "Sent");
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(labelX, cy), sendCol, TR("sent_upper"));
|
||||
|
||||
// Time (next to type)
|
||||
std::string ago = timeAgo(tx.timestamp);
|
||||
float typeW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, "Sent").x;
|
||||
float typeW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, TR("sent_upper")).x;
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(labelX + typeW + Layout::spacingLg(), cy),
|
||||
OnSurfaceDisabled(), ago.c_str());
|
||||
|
||||
@@ -1096,12 +1096,12 @@ static void RenderRecentSends(ImDrawList* dl, const WalletState& state,
|
||||
const char* statusStr;
|
||||
ImU32 statusCol;
|
||||
if (tx.confirmations == 0) {
|
||||
statusStr = "Pending"; statusCol = Warning();
|
||||
statusStr = TR("pending"); statusCol = Warning();
|
||||
} else if (tx.confirmations < (int)S.drawElement("tabs.send", "confirmed-threshold").size) {
|
||||
snprintf(buf, sizeof(buf), "%d conf", tx.confirmations);
|
||||
snprintf(buf, sizeof(buf), TR("conf_count"), tx.confirmations);
|
||||
statusStr = buf; statusCol = Warning();
|
||||
} else {
|
||||
statusStr = "Confirmed"; statusCol = greenCol;
|
||||
statusStr = TR("confirmed"); statusCol = greenCol;
|
||||
}
|
||||
ImVec2 sSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, statusStr);
|
||||
float statusX = amtX - sSz.x - Layout::spacingXxl();
|
||||
@@ -1238,7 +1238,7 @@ void RenderSendTab(App* app)
|
||||
static bool s_paste_previewing = false;
|
||||
static std::string s_preview_text;
|
||||
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "RECIPIENT");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_recipient"));
|
||||
|
||||
// Validation indicator — inline next to title (no height change)
|
||||
// Check the preview text during hover, otherwise check actual address
|
||||
@@ -1254,9 +1254,9 @@ void RenderSendTab(App* app)
|
||||
if (vz || vt) {
|
||||
ImGui::SameLine();
|
||||
if (vz)
|
||||
Type().textColored(TypeStyle::Caption, Success(), "Valid shielded address");
|
||||
Type().textColored(TypeStyle::Caption, Success(), TR("send_valid_shielded"));
|
||||
else
|
||||
Type().textColored(TypeStyle::Caption, Warning(), "Valid transparent address");
|
||||
Type().textColored(TypeStyle::Caption, Warning(), TR("send_valid_transparent"));
|
||||
} else if (!checkPreview) {
|
||||
ImGui::SameLine();
|
||||
Type().textColored(TypeStyle::Caption, Error(), TR("invalid_address"));
|
||||
@@ -1341,7 +1341,7 @@ void RenderSendTab(App* app)
|
||||
|
||||
// ---- AMOUNT ----
|
||||
{
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "AMOUNT");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_amount_upper"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Toggle between DRGX and USD input
|
||||
@@ -1439,7 +1439,7 @@ void RenderSendTab(App* app)
|
||||
|
||||
// Amount error
|
||||
if (s_amount > 0 && s_amount + s_fee > available && available > 0) {
|
||||
snprintf(buf, sizeof(buf), "Exceeds available (%.8f)", available - s_fee);
|
||||
snprintf(buf, sizeof(buf), TR("send_exceeds_available"), available - s_fee);
|
||||
Type().textColored(TypeStyle::Caption, Error(), buf);
|
||||
}
|
||||
}
|
||||
@@ -1455,7 +1455,7 @@ void RenderSendTab(App* app)
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, innerGap * 0.5f));
|
||||
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), "MEMO (OPTIONAL)");
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("memo_optional"));
|
||||
ImGui::Dummy(ImVec2(0, S.drawElement("tabs.send", "memo-label-gap").size));
|
||||
|
||||
float memoInputH = std::max(S.drawElement("tabs.send", "memo-min-height").size, S.drawElement("tabs.send", "memo-base-height").size * vScale);
|
||||
@@ -1502,7 +1502,7 @@ void RenderSendTab(App* app)
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(containerMin.x, containerMax.y));
|
||||
ImGui::Dummy(ImVec2(formW, 0));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Pass card bottom Y so error overlay can anchor to it
|
||||
float cardBottom = containerMax.y;
|
||||
|
||||
Reference in New Issue
Block a user