feat(balance): inline period-earnings in the address list and totals

Surfaces the period-earnings aggregator in the Balance tab:
- Toolbar (shared address list): interval pills (1H/1D/1M/1Y), a received-
  vs-net mode toggle, and a global show-all toggle, placed right of "Hide 0
  Balances". The New Z-Address / New mining address buttons move to a footer
  row across from "Showing X of Y" to make room.
- Per address: the row draws its earnings on line 2, left of the $USD value
  (green positive, red negative in net mode), shown when the global toggle
  is on or the address is opted in via a new "Show/Hide earnings" right-click
  item. Works in every layout via the shared list.
- Total: FormatTotalEarnings (gated on the global toggle, so the default
  appearance is unchanged) is wired into the compact hero (dashboard/minimal)
  and the classic layout, left of / beside their USD figure. The remaining
  layouts' totals are not wired yet.
- Settings > Appearance: an "Earnings Display Format" selector (Coin /
  Coin + fiat / Fiat).

New i18n keys are translated into all eight locales; the CJK subset font is
rebuilt for the new glyphs. Builds + test suites pass on both variants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 11:22:12 -05:00
co-authored by Claude Opus 4.8
parent 2ef91ea7d4
commit 064c9d1b61
14 changed files with 335 additions and 40 deletions
Binary file not shown.
+12
View File
@@ -639,6 +639,11 @@
"download": "Herunterladen", "download": "Herunterladen",
"download_bootstrap": "Bootstrap herunterladen", "download_bootstrap": "Bootstrap herunterladen",
"dragonx_green": "DragonX (Grün)", "dragonx_green": "DragonX (Grün)",
"earnings_format": "Anzeigeformat für Erträge",
"earnings_format_coin": "Nur Münze",
"earnings_format_coin_fiat": "Münze + Fiat",
"earnings_format_fiat": "Nur Fiat",
"earnings_label": "Erträge",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"empty_wallet_keys_suffix": "Schlüssel", "empty_wallet_keys_suffix": "Schlüssel",
"empty_wallet_open_manager": "Wallet-Verwaltung öffnen", "empty_wallet_open_manager": "Wallet-Verwaltung öffnen",
@@ -942,6 +947,7 @@
"hidden_tag": " (versteckt)", "hidden_tag": " (versteckt)",
"hide": "Ausblenden", "hide": "Ausblenden",
"hide_address": "Adresse ausblenden", "hide_address": "Adresse ausblenden",
"hide_earnings": "Erträge ausblenden",
"hide_qr": "QR ausblenden", "hide_qr": "QR ausblenden",
"hide_zero_balances": "Nullsalden ausblenden", "hide_zero_balances": "Nullsalden ausblenden",
"history": "Verlauf", "history": "Verlauf",
@@ -1920,6 +1926,7 @@
"shielded_type": "Abgeschirmt", "shielded_type": "Abgeschirmt",
"shielding_notice": "Hinweis: Dies wird Gelder von einer transparenten (T) Adresse auf eine private (Z) Adresse schirmen.", "shielding_notice": "Hinweis: Dies wird Gelder von einer transparenten (T) Adresse auf eine private (Z) Adresse schirmen.",
"show": "Anzeigen", "show": "Anzeigen",
"show_earnings": "Erträge anzeigen",
"show_hidden": "Ausgeblendete anzeigen (%d)", "show_hidden": "Ausgeblendete anzeigen (%d)",
"show_qr": "QR anzeigen", "show_qr": "QR anzeigen",
"show_qr_code": "QR-Code anzeigen", "show_qr_code": "QR-Code anzeigen",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "Debug-Protokollierungsoptionen ausklappen", "tt_debug_expand": "Debug-Protokollierungsoptionen ausklappen",
"tt_delete_blockchain": "Alle Blockchain-Daten löschen und neu synchronisieren. wallet.dat und Konfiguration bleiben erhalten.", "tt_delete_blockchain": "Alle Blockchain-Daten löschen und neu synchronisieren. wallet.dat und Konfiguration bleiben erhalten.",
"tt_download_bootstrap": "Blockchain-Bootstrap herunterladen, um die Synchronisierung zu beschleunigen\nVorhandene Blockdaten werden ersetzt", "tt_download_bootstrap": "Blockchain-Bootstrap herunterladen, um die Synchronisierung zu beschleunigen\nVorhandene Blockdaten werden ersetzt",
"tt_earn_interval": "Ertragszeitraum: 1H = diese Stunde, 1D = heute, 1M = dieser Monat, 1Y = dieses Jahr",
"tt_earn_mode_inflows": "Ertragsmodus: erhaltene Beträge (einschließlich Mining)",
"tt_earn_mode_net": "Ertragsmodus: netto (erhalten minus gesendet)",
"tt_earn_show_all": "Ertragswert für jede Adresse anzeigen",
"tt_earnings_format": "Wie die Ertragswerte pro Adresse und insgesamt angezeigt werden",
"tt_encrypt": "wallet.dat mit einer Passphrase verschlüsseln", "tt_encrypt": "wallet.dat mit einer Passphrase verschlüsseln",
"tt_export_all": "Alle privaten Schlüssel in eine Datei exportieren", "tt_export_all": "Alle privaten Schlüssel in eine Datei exportieren",
"tt_export_csv": "Transaktionsverlauf als CSV-Tabelle exportieren", "tt_export_csv": "Transaktionsverlauf als CSV-Tabelle exportieren",
+12
View File
@@ -639,6 +639,11 @@
"download": "Descargar", "download": "Descargar",
"download_bootstrap": "Descargar Bootstrap", "download_bootstrap": "Descargar Bootstrap",
"dragonx_green": "DragonX (Verde)", "dragonx_green": "DragonX (Verde)",
"earnings_format": "Formato de visualización de ganancias",
"earnings_format_coin": "Solo moneda",
"earnings_format_coin_fiat": "Moneda + fíat",
"earnings_format_fiat": "Solo fíat",
"earnings_label": "Ganancias",
"edit": "Editar", "edit": "Editar",
"empty_wallet_keys_suffix": "claves", "empty_wallet_keys_suffix": "claves",
"empty_wallet_open_manager": "Abrir administrador de carteras", "empty_wallet_open_manager": "Abrir administrador de carteras",
@@ -942,6 +947,7 @@
"hidden_tag": " (oculto)", "hidden_tag": " (oculto)",
"hide": "Ocultar", "hide": "Ocultar",
"hide_address": "Ocultar dirección", "hide_address": "Ocultar dirección",
"hide_earnings": "Ocultar ganancias",
"hide_qr": "Ocultar QR", "hide_qr": "Ocultar QR",
"hide_zero_balances": "Ocultar saldos 0", "hide_zero_balances": "Ocultar saldos 0",
"history": "Historial", "history": "Historial",
@@ -1920,6 +1926,7 @@
"shielded_type": "Protegido", "shielded_type": "Protegido",
"shielding_notice": "Nota: Esto blindará fondos de una dirección transparente (T) a una dirección privada (Z).", "shielding_notice": "Nota: Esto blindará fondos de una dirección transparente (T) a una dirección privada (Z).",
"show": "Mostrar", "show": "Mostrar",
"show_earnings": "Mostrar ganancias",
"show_hidden": "Mostrar ocultos (%d)", "show_hidden": "Mostrar ocultos (%d)",
"show_qr": "Mostrar QR", "show_qr": "Mostrar QR",
"show_qr_code": "Mostrar Código QR", "show_qr_code": "Mostrar Código QR",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "Expandir opciones de registro de depuración", "tt_debug_expand": "Expandir opciones de registro de depuración",
"tt_delete_blockchain": "Eliminar todos los datos de la blockchain e iniciar una nueva sincronización. Se preservan wallet.dat y la configuración.", "tt_delete_blockchain": "Eliminar todos los datos de la blockchain e iniciar una nueva sincronización. Se preservan wallet.dat y la configuración.",
"tt_download_bootstrap": "Descargar bootstrap de blockchain para acelerar la sincronización\nLos datos de bloques existentes serán reemplazados", "tt_download_bootstrap": "Descargar bootstrap de blockchain para acelerar la sincronización\nLos datos de bloques existentes serán reemplazados",
"tt_earn_interval": "Periodo de ganancias: 1H = esta hora, 1D = hoy, 1M = este mes, 1Y = este año",
"tt_earn_mode_inflows": "Modo de ganancias: importes recibidos (incluida la minería)",
"tt_earn_mode_net": "Modo de ganancias: neto (recibido menos enviado)",
"tt_earn_show_all": "Mostrar la cifra de ganancias para cada dirección",
"tt_earnings_format": "Cómo se muestran las cifras de ganancias por dirección y totales",
"tt_encrypt": "Cifrar wallet.dat con una contraseña", "tt_encrypt": "Cifrar wallet.dat con una contraseña",
"tt_export_all": "Exportar todas las claves privadas a un archivo", "tt_export_all": "Exportar todas las claves privadas a un archivo",
"tt_export_csv": "Exportar historial de transacciones como hoja de cálculo CSV", "tt_export_csv": "Exportar historial de transacciones como hoja de cálculo CSV",
+12
View File
@@ -639,6 +639,11 @@
"download": "Télécharger", "download": "Télécharger",
"download_bootstrap": "Télécharger Bootstrap", "download_bootstrap": "Télécharger Bootstrap",
"dragonx_green": "DragonX (Vert)", "dragonx_green": "DragonX (Vert)",
"earnings_format": "Format d'affichage des gains",
"earnings_format_coin": "Pièce uniquement",
"earnings_format_coin_fiat": "Pièce + fiat",
"earnings_format_fiat": "Fiat uniquement",
"earnings_label": "Gains",
"edit": "Modifier", "edit": "Modifier",
"empty_wallet_keys_suffix": "clés", "empty_wallet_keys_suffix": "clés",
"empty_wallet_open_manager": "Ouvrir le gestionnaire de portefeuilles", "empty_wallet_open_manager": "Ouvrir le gestionnaire de portefeuilles",
@@ -942,6 +947,7 @@
"hidden_tag": " (masqué)", "hidden_tag": " (masqué)",
"hide": "Masquer", "hide": "Masquer",
"hide_address": "Masquer l'adresse", "hide_address": "Masquer l'adresse",
"hide_earnings": "Masquer les gains",
"hide_qr": "Masquer le QR", "hide_qr": "Masquer le QR",
"hide_zero_balances": "Masquer les soldes à 0", "hide_zero_balances": "Masquer les soldes à 0",
"history": "Historique", "history": "Historique",
@@ -1920,6 +1926,7 @@
"shielded_type": "Blindé", "shielded_type": "Blindé",
"shielding_notice": "Note : Cela blindera des fonds d'une adresse transparente (T) vers une adresse privée (Z).", "shielding_notice": "Note : Cela blindera des fonds d'une adresse transparente (T) vers une adresse privée (Z).",
"show": "Afficher", "show": "Afficher",
"show_earnings": "Afficher les gains",
"show_hidden": "Afficher masqués (%d)", "show_hidden": "Afficher masqués (%d)",
"show_qr": "Afficher le QR", "show_qr": "Afficher le QR",
"show_qr_code": "Afficher le code QR", "show_qr_code": "Afficher le code QR",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "Développer les options de journalisation de débogage", "tt_debug_expand": "Développer les options de journalisation de débogage",
"tt_delete_blockchain": "Supprimer toutes les données de la blockchain et démarrer une nouvelle synchronisation. wallet.dat et la configuration sont préservés.", "tt_delete_blockchain": "Supprimer toutes les données de la blockchain et démarrer une nouvelle synchronisation. wallet.dat et la configuration sont préservés.",
"tt_download_bootstrap": "Télécharger le bootstrap blockchain pour accélérer la synchronisation\nLes données de blocs existantes seront remplacées", "tt_download_bootstrap": "Télécharger le bootstrap blockchain pour accélérer la synchronisation\nLes données de blocs existantes seront remplacées",
"tt_earn_interval": "Période de gains : 1H = cette heure, 1D = aujourd'hui, 1M = ce mois, 1Y = cette année",
"tt_earn_mode_inflows": "Mode de gains : montants reçus (minage inclus)",
"tt_earn_mode_net": "Mode de gains : net (reçu moins envoyé)",
"tt_earn_show_all": "Afficher le montant des gains pour chaque adresse",
"tt_earnings_format": "Comment les montants de gains par adresse et totaux sont affichés",
"tt_encrypt": "Chiffrer wallet.dat avec une phrase secrète", "tt_encrypt": "Chiffrer wallet.dat avec une phrase secrète",
"tt_export_all": "Exporter toutes les clés privées dans un fichier", "tt_export_all": "Exporter toutes les clés privées dans un fichier",
"tt_export_csv": "Exporter l'historique des transactions en feuille de calcul CSV", "tt_export_csv": "Exporter l'historique des transactions en feuille de calcul CSV",
+12
View File
@@ -639,6 +639,11 @@
"download": "ダウンロード", "download": "ダウンロード",
"download_bootstrap": "ブートストラップをダウンロード", "download_bootstrap": "ブートストラップをダウンロード",
"dragonx_green": "DragonX(グリーン)", "dragonx_green": "DragonX(グリーン)",
"earnings_format": "収益の表示形式",
"earnings_format_coin": "コインのみ",
"earnings_format_coin_fiat": "コイン+法定通貨",
"earnings_format_fiat": "法定通貨のみ",
"earnings_label": "収益",
"edit": "編集", "edit": "編集",
"empty_wallet_keys_suffix": "個の鍵", "empty_wallet_keys_suffix": "個の鍵",
"empty_wallet_open_manager": "ウォレットマネージャーを開く", "empty_wallet_open_manager": "ウォレットマネージャーを開く",
@@ -942,6 +947,7 @@
"hidden_tag": " (非表示)", "hidden_tag": " (非表示)",
"hide": "非表示", "hide": "非表示",
"hide_address": "アドレスを非表示", "hide_address": "アドレスを非表示",
"hide_earnings": "収益を非表示",
"hide_qr": "QRを非表示", "hide_qr": "QRを非表示",
"hide_zero_balances": "残高0を非表示", "hide_zero_balances": "残高0を非表示",
"history": "履歴", "history": "履歴",
@@ -1920,6 +1926,7 @@
"shielded_type": "シールド", "shielded_type": "シールド",
"shielding_notice": "注意:トランスペアレント (T) アドレスからプライベート (Z) アドレスへ資金をシールドします。", "shielding_notice": "注意:トランスペアレント (T) アドレスからプライベート (Z) アドレスへ資金をシールドします。",
"show": "表示", "show": "表示",
"show_earnings": "収益を表示",
"show_hidden": "非表示を表示 (%d)", "show_hidden": "非表示を表示 (%d)",
"show_qr": "QRを表示", "show_qr": "QRを表示",
"show_qr_code": "QRコードを表示", "show_qr_code": "QRコードを表示",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "デバッグログオプションを展開", "tt_debug_expand": "デバッグログオプションを展開",
"tt_delete_blockchain": "すべてのブロックチェーンデータを削除して新規同期を開始します。wallet.dat と設定は保持されます。", "tt_delete_blockchain": "すべてのブロックチェーンデータを削除して新規同期を開始します。wallet.dat と設定は保持されます。",
"tt_download_bootstrap": "ブロックチェーンブートストラップをダウンロードして同期を高速化\n既存のブロックデータは置き換えられます", "tt_download_bootstrap": "ブロックチェーンブートストラップをダウンロードして同期を高速化\n既存のブロックデータは置き換えられます",
"tt_earn_interval": "収益期間:1H=今の時間、1D=今日、1M=今月、1Y=今年",
"tt_earn_mode_inflows": "収益モード:受取額(マイニングを含む)",
"tt_earn_mode_net": "収益モード:純額(受取から送金を差し引いた額)",
"tt_earn_show_all": "すべてのアドレスの収益額を表示",
"tt_earnings_format": "アドレスごとおよび合計の収益額の表示方法",
"tt_encrypt": "パスフレーズで wallet.dat を暗号化", "tt_encrypt": "パスフレーズで wallet.dat を暗号化",
"tt_export_all": "すべての秘密鍵をファイルにエクスポート", "tt_export_all": "すべての秘密鍵をファイルにエクスポート",
"tt_export_csv": "トランザクション履歴を CSV スプレッドシートとしてエクスポート", "tt_export_csv": "トランザクション履歴を CSV スプレッドシートとしてエクスポート",
+12
View File
@@ -639,6 +639,11 @@
"download": "다운로드", "download": "다운로드",
"download_bootstrap": "부트스트랩 다운로드", "download_bootstrap": "부트스트랩 다운로드",
"dragonx_green": "DragonX(그린)", "dragonx_green": "DragonX(그린)",
"earnings_format": "수익 표시 형식",
"earnings_format_coin": "코인만",
"earnings_format_coin_fiat": "코인 + 법정화폐",
"earnings_format_fiat": "법정화폐만",
"earnings_label": "수익",
"edit": "편집", "edit": "편집",
"empty_wallet_keys_suffix": "개 키", "empty_wallet_keys_suffix": "개 키",
"empty_wallet_open_manager": "지갑 관리자 열기", "empty_wallet_open_manager": "지갑 관리자 열기",
@@ -942,6 +947,7 @@
"hidden_tag": " (숨김)", "hidden_tag": " (숨김)",
"hide": "숨기기", "hide": "숨기기",
"hide_address": "주소 숨기기", "hide_address": "주소 숨기기",
"hide_earnings": "수익 숨기기",
"hide_qr": "QR 숨기기", "hide_qr": "QR 숨기기",
"hide_zero_balances": "잔액 0 숨기기", "hide_zero_balances": "잔액 0 숨기기",
"history": "내역", "history": "내역",
@@ -1920,6 +1926,7 @@
"shielded_type": "차폐", "shielded_type": "차폐",
"shielding_notice": "참고: 투명 (T) 주소에서 프라이빗 (Z) 주소로 자금을 실딩합니다.", "shielding_notice": "참고: 투명 (T) 주소에서 프라이빗 (Z) 주소로 자금을 실딩합니다.",
"show": "표시", "show": "표시",
"show_earnings": "수익 표시",
"show_hidden": "숨겨진 항목 표시 (%d)", "show_hidden": "숨겨진 항목 표시 (%d)",
"show_qr": "QR 표시", "show_qr": "QR 표시",
"show_qr_code": "QR 코드 표시", "show_qr_code": "QR 코드 표시",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "디버그 로깅 옵션 펼치기", "tt_debug_expand": "디버그 로깅 옵션 펼치기",
"tt_delete_blockchain": "모든 블록체인 데이터를 삭제하고 새로 동기화합니다. wallet.dat 및 설정은 보존됩니다.", "tt_delete_blockchain": "모든 블록체인 데이터를 삭제하고 새로 동기화합니다. wallet.dat 및 설정은 보존됩니다.",
"tt_download_bootstrap": "블록체인 부트스트랩을 다운로드하여 동기화 가속\n기존 블록 데이터가 교체됩니다", "tt_download_bootstrap": "블록체인 부트스트랩을 다운로드하여 동기화 가속\n기존 블록 데이터가 교체됩니다",
"tt_earn_interval": "수익 기간: 1H = 현재 시간, 1D = 오늘, 1M = 이번 달, 1Y = 올해",
"tt_earn_mode_inflows": "수익 모드: 받은 금액(채굴 포함)",
"tt_earn_mode_net": "수익 모드: 순액(받은 금액에서 보낸 금액을 뺀 값)",
"tt_earn_show_all": "모든 주소의 수익 금액 표시",
"tt_earnings_format": "주소별 및 합계 수익 금액의 표시 방식",
"tt_encrypt": "비밀번호로 wallet.dat 암호화", "tt_encrypt": "비밀번호로 wallet.dat 암호화",
"tt_export_all": "모든 개인키를 파일로 내보내기", "tt_export_all": "모든 개인키를 파일로 내보내기",
"tt_export_csv": "거래 내역을 CSV 스프레드시트로 내보내기", "tt_export_csv": "거래 내역을 CSV 스프레드시트로 내보내기",
+12
View File
@@ -639,6 +639,11 @@
"download": "Baixar", "download": "Baixar",
"download_bootstrap": "Baixar Bootstrap", "download_bootstrap": "Baixar Bootstrap",
"dragonx_green": "DragonX (Verde)", "dragonx_green": "DragonX (Verde)",
"earnings_format": "Formato de exibição de ganhos",
"earnings_format_coin": "Apenas moeda",
"earnings_format_coin_fiat": "Moeda + fiduciária",
"earnings_format_fiat": "Apenas fiduciária",
"earnings_label": "Ganhos",
"edit": "Editar", "edit": "Editar",
"empty_wallet_keys_suffix": "chaves", "empty_wallet_keys_suffix": "chaves",
"empty_wallet_open_manager": "Abrir gerenciador de carteiras", "empty_wallet_open_manager": "Abrir gerenciador de carteiras",
@@ -942,6 +947,7 @@
"hidden_tag": " (oculto)", "hidden_tag": " (oculto)",
"hide": "Ocultar", "hide": "Ocultar",
"hide_address": "Ocultar endereço", "hide_address": "Ocultar endereço",
"hide_earnings": "Ocultar ganhos",
"hide_qr": "Ocultar QR", "hide_qr": "Ocultar QR",
"hide_zero_balances": "Ocultar saldos zero", "hide_zero_balances": "Ocultar saldos zero",
"history": "Histórico", "history": "Histórico",
@@ -1920,6 +1926,7 @@
"shielded_type": "Blindado", "shielded_type": "Blindado",
"shielding_notice": "Nota: Isso blindará fundos de um endereço transparente (T) para um endereço privado (Z).", "shielding_notice": "Nota: Isso blindará fundos de um endereço transparente (T) para um endereço privado (Z).",
"show": "Mostrar", "show": "Mostrar",
"show_earnings": "Mostrar ganhos",
"show_hidden": "Mostrar ocultos (%d)", "show_hidden": "Mostrar ocultos (%d)",
"show_qr": "Mostrar QR", "show_qr": "Mostrar QR",
"show_qr_code": "Mostrar Código QR", "show_qr_code": "Mostrar Código QR",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "Expandir opções de registro de depuração", "tt_debug_expand": "Expandir opções de registro de depuração",
"tt_delete_blockchain": "Excluir todos os dados da blockchain e iniciar uma nova sincronização. wallet.dat e configuração são preservados.", "tt_delete_blockchain": "Excluir todos os dados da blockchain e iniciar uma nova sincronização. wallet.dat e configuração são preservados.",
"tt_download_bootstrap": "Baixar bootstrap da blockchain para acelerar a sincronização\nDados de blocos existentes serão substituídos", "tt_download_bootstrap": "Baixar bootstrap da blockchain para acelerar a sincronização\nDados de blocos existentes serão substituídos",
"tt_earn_interval": "Período de ganhos: 1H = esta hora, 1D = hoje, 1M = este mês, 1Y = este ano",
"tt_earn_mode_inflows": "Modo de ganhos: valores recebidos (incluindo mineração)",
"tt_earn_mode_net": "Modo de ganhos: líquido (recebido menos enviado)",
"tt_earn_show_all": "Mostrar o valor de ganhos para cada endereço",
"tt_earnings_format": "Como os valores de ganhos por endereço e totais são exibidos",
"tt_encrypt": "Encriptar wallet.dat com uma frase secreta", "tt_encrypt": "Encriptar wallet.dat com uma frase secreta",
"tt_export_all": "Exportar todas as chaves privadas para um arquivo", "tt_export_all": "Exportar todas as chaves privadas para um arquivo",
"tt_export_csv": "Exportar histórico de transações como planilha CSV", "tt_export_csv": "Exportar histórico de transações como planilha CSV",
+12
View File
@@ -639,6 +639,11 @@
"download": "Скачать", "download": "Скачать",
"download_bootstrap": "Скачать бутстрап", "download_bootstrap": "Скачать бутстрап",
"dragonx_green": "DragonX (Зелёная)", "dragonx_green": "DragonX (Зелёная)",
"earnings_format": "Формат отображения дохода",
"earnings_format_coin": "Только монета",
"earnings_format_coin_fiat": "Монета + фиат",
"earnings_format_fiat": "Только фиат",
"earnings_label": "Доход",
"edit": "Редактировать", "edit": "Редактировать",
"empty_wallet_keys_suffix": "ключей", "empty_wallet_keys_suffix": "ключей",
"empty_wallet_open_manager": "Открыть менеджер кошельков", "empty_wallet_open_manager": "Открыть менеджер кошельков",
@@ -942,6 +947,7 @@
"hidden_tag": " (скрыт)", "hidden_tag": " (скрыт)",
"hide": "Скрыть", "hide": "Скрыть",
"hide_address": "Скрыть адрес", "hide_address": "Скрыть адрес",
"hide_earnings": "Скрыть доход",
"hide_qr": "Скрыть QR", "hide_qr": "Скрыть QR",
"hide_zero_balances": "Скрыть нулевые балансы", "hide_zero_balances": "Скрыть нулевые балансы",
"history": "История", "history": "История",
@@ -1920,6 +1926,7 @@
"shielded_type": "Экранированный", "shielded_type": "Экранированный",
"shielding_notice": "Примечание: это переведёт средства с прозрачного (T) адреса на приватный (Z) адрес.", "shielding_notice": "Примечание: это переведёт средства с прозрачного (T) адреса на приватный (Z) адрес.",
"show": "Показать", "show": "Показать",
"show_earnings": "Показать доход",
"show_hidden": "Показать скрытые (%d)", "show_hidden": "Показать скрытые (%d)",
"show_qr": "Показать QR", "show_qr": "Показать QR",
"show_qr_code": "Показать QR-код", "show_qr_code": "Показать QR-код",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "Развернуть параметры журнала отладки", "tt_debug_expand": "Развернуть параметры журнала отладки",
"tt_delete_blockchain": "Удалить все данные блокчейна и начать синхронизацию заново. wallet.dat и конфигурация сохраняются.", "tt_delete_blockchain": "Удалить все данные блокчейна и начать синхронизацию заново. wallet.dat и конфигурация сохраняются.",
"tt_download_bootstrap": "Скачать бутстрап блокчейна для ускорения синхронизации\nСуществующие данные блоков будут заменены", "tt_download_bootstrap": "Скачать бутстрап блокчейна для ускорения синхронизации\nСуществующие данные блоков будут заменены",
"tt_earn_interval": "Период дохода: 1H — текущий час, 1D — сегодня, 1M — текущий месяц, 1Y — текущий год",
"tt_earn_mode_inflows": "Режим дохода: полученные суммы (включая майнинг)",
"tt_earn_mode_net": "Режим дохода: нетто (получено минус отправлено)",
"tt_earn_show_all": "Показывать сумму дохода для каждого адреса",
"tt_earnings_format": "Как отображаются суммы дохода по адресам и итогам",
"tt_encrypt": "Зашифровать wallet.dat паролем", "tt_encrypt": "Зашифровать wallet.dat паролем",
"tt_export_all": "Экспортировать все приватные ключи в файл", "tt_export_all": "Экспортировать все приватные ключи в файл",
"tt_export_csv": "Экспортировать историю транзакций в виде таблицы CSV", "tt_export_csv": "Экспортировать историю транзакций в виде таблицы CSV",
+12
View File
@@ -639,6 +639,11 @@
"download": "下载", "download": "下载",
"download_bootstrap": "下载引导程序", "download_bootstrap": "下载引导程序",
"dragonx_green": "DragonX(绿色)", "dragonx_green": "DragonX(绿色)",
"earnings_format": "收益显示格式",
"earnings_format_coin": "仅代币",
"earnings_format_coin_fiat": "代币+法币",
"earnings_format_fiat": "仅法币",
"earnings_label": "收益",
"edit": "编辑", "edit": "编辑",
"empty_wallet_keys_suffix": "个密钥", "empty_wallet_keys_suffix": "个密钥",
"empty_wallet_open_manager": "打开钱包管理器", "empty_wallet_open_manager": "打开钱包管理器",
@@ -942,6 +947,7 @@
"hidden_tag": " (已隐藏)", "hidden_tag": " (已隐藏)",
"hide": "隐藏", "hide": "隐藏",
"hide_address": "隐藏地址", "hide_address": "隐藏地址",
"hide_earnings": "隐藏收益",
"hide_qr": "隐藏二维码", "hide_qr": "隐藏二维码",
"hide_zero_balances": "隐藏零余额", "hide_zero_balances": "隐藏零余额",
"history": "历史", "history": "历史",
@@ -1920,6 +1926,7 @@
"shielded_type": "屏蔽", "shielded_type": "屏蔽",
"shielding_notice": "注意:这将把资金从透明 (T) 地址转移到隐私 (Z) 地址。", "shielding_notice": "注意:这将把资金从透明 (T) 地址转移到隐私 (Z) 地址。",
"show": "显示", "show": "显示",
"show_earnings": "显示收益",
"show_hidden": "显示已隐藏 (%d)", "show_hidden": "显示已隐藏 (%d)",
"show_qr": "显示二维码", "show_qr": "显示二维码",
"show_qr_code": "显示二维码", "show_qr_code": "显示二维码",
@@ -2068,6 +2075,11 @@
"tt_debug_expand": "展开调试日志选项", "tt_debug_expand": "展开调试日志选项",
"tt_delete_blockchain": "删除所有区块链数据并重新同步。wallet.dat 和配置将被保留。", "tt_delete_blockchain": "删除所有区块链数据并重新同步。wallet.dat 和配置将被保留。",
"tt_download_bootstrap": "下载区块链引导程序以加速同步\n现有区块数据将被替换", "tt_download_bootstrap": "下载区块链引导程序以加速同步\n现有区块数据将被替换",
"tt_earn_interval": "收益周期:1H=本小时,1D=今天,1M=本月,1Y=今年",
"tt_earn_mode_inflows": "收益模式:收到的金额(含挖矿)",
"tt_earn_mode_net": "收益模式:净额(收到减去发送)",
"tt_earn_show_all": "为每个地址显示收益数值",
"tt_earnings_format": "按地址和总计的收益数值的显示方式",
"tt_encrypt": "使用密码加密 wallet.dat", "tt_encrypt": "使用密码加密 wallet.dat",
"tt_export_all": "将所有私钥导出到文件", "tt_export_all": "将所有私钥导出到文件",
"tt_export_csv": "将交易历史导出为 CSV 电子表格", "tt_export_csv": "将交易历史导出为 CSV 电子表格",
+22
View File
@@ -107,6 +107,7 @@ struct SettingsPageState {
float ui_opacity = 1.0f; float ui_opacity = 1.0f;
float window_opacity = 1.0f; float window_opacity = 1.0f;
float favorite_opacity = 1.0f; float favorite_opacity = 1.0f;
int earnings_format = 1;
std::string balance_layout = "classic"; std::string balance_layout = "classic";
bool scanline_enabled = true; bool scanline_enabled = true;
bool theme_effects_enabled = true; bool theme_effects_enabled = true;
@@ -450,6 +451,7 @@ static void loadSettingsPageState(App* app) {
s_settingsState.ui_opacity = settings->getUIOpacity(); s_settingsState.ui_opacity = settings->getUIOpacity();
s_settingsState.window_opacity = settings->getWindowOpacity(); s_settingsState.window_opacity = settings->getWindowOpacity();
s_settingsState.favorite_opacity = settings->getFavoriteOpacity(); s_settingsState.favorite_opacity = settings->getFavoriteOpacity();
s_settingsState.earnings_format = settings->getEarningsFormat();
s_settingsState.noise_opacity = settings->getNoiseOpacity(); s_settingsState.noise_opacity = settings->getNoiseOpacity();
s_settingsState.gradient_background = settings->getGradientBackground(); s_settingsState.gradient_background = settings->getGradientBackground();
@@ -535,6 +537,7 @@ static void saveSettingsPageState(config::Settings* settings) {
settings->setUIOpacity(lowSpecOverrides ? realPrefs.ui_opacity : s_settingsState.ui_opacity); settings->setUIOpacity(lowSpecOverrides ? realPrefs.ui_opacity : s_settingsState.ui_opacity);
settings->setWindowOpacity(s_settingsState.window_opacity); // 1.0 under low-spec, as the hotkey saves it settings->setWindowOpacity(s_settingsState.window_opacity); // 1.0 under low-spec, as the hotkey saves it
settings->setFavoriteOpacity(s_settingsState.favorite_opacity); // not low-spec gated — cosmetic settings->setFavoriteOpacity(s_settingsState.favorite_opacity); // not low-spec gated — cosmetic
settings->setEarningsFormat(s_settingsState.earnings_format);
settings->setNoiseOpacity(s_settingsState.noise_opacity); settings->setNoiseOpacity(s_settingsState.noise_opacity);
settings->setGradientBackground(s_settingsState.gradient_background); settings->setGradientBackground(s_settingsState.gradient_background);
settings->setScanlineEnabled(lowSpecOverrides ? realPrefs.scanline_enabled : s_settingsState.scanline_enabled); settings->setScanlineEnabled(lowSpecOverrides ? realPrefs.scanline_enabled : s_settingsState.scanline_enabled);
@@ -1052,6 +1055,25 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingMd())); ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
// --- Earnings display format (Balance tab per-address + total earnings figures) ---
{
ImGui::PushFont(body2);
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(OnSurfaceMedium()));
ImGui::TextUnformatted(TR("earnings_format"));
ImGui::PopStyleColor();
float efW = std::min(std::max(S.drawElement("components.settings-page", "effects-input-min-width").size, contentW), 360.0f * dp);
ImGui::SetNextItemWidth(efW);
const char* efItems[] = { TR("earnings_format_coin"), TR("earnings_format_coin_fiat"), TR("earnings_format_fiat") };
if (ImGui::Combo("##EarningsFormat", &s_settingsState.earnings_format, efItems, IM_ARRAYSIZE(efItems))) {
s_settingsState.earnings_format = std::max(0, std::min(2, s_settingsState.earnings_format));
if (app->settings()) { app->settings()->setEarningsFormat(s_settingsState.earnings_format); app->settings()->save(); }
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_earnings_format"));
ImGui::PopFont();
}
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
// --- Effect toggles (always visible, horizontal wrapping flow) --- // --- Effect toggles (always visible, horizontal wrapping flow) ---
{ {
ImGui::PushFont(body2); ImGui::PushFont(body2);
+191 -39
View File
@@ -36,6 +36,8 @@
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <chrono>
#include "../../data/earnings_series.h"
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -56,6 +58,44 @@ void UpdateBalanceLerp(App* app) {
StepDisplayLerp(s_dispUnconfirmed, state.unconfirmed_balance, dt, kBalanceLerpSpeed, instant); StepDisplayLerp(s_dispUnconfirmed, state.unconfirmed_balance, dt, kBalanceLerpSpeed, instant);
} }
// Total period-earnings for the whole wallet, formatted per the earnings settings. Only shown when the
// global earnings toggle is on, so the default hero/layout appearance is unchanged. Own revision-keyed
// cache (independent of the per-row one in RenderSharedAddressList).
bool FormatTotalEarnings(App* app, std::string& outText, ImU32& outCol) {
using namespace material;
if (!app->settings() || !app->settings()->getEarningsShowAll()) return false;
auto& S = schema::UI();
const auto& state = app->state();
auto* est = app->settings();
static dragonx::data::EarningsCache s_totalEarnCache;
auto iv = (dragonx::data::EarningsInterval)est->getEarningsInterval();
auto md = (dragonx::data::EarningsMode)est->getEarningsMode();
const int fmt = est->getEarningsFormat();
const std::int64_t nowSec = (std::int64_t)std::time(nullptr);
const std::int64_t nowMs = (std::int64_t)std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
dragonx::data::EarningsKey ek;
ek.txRevision = state.tx_revision;
ek.txCount = state.transactions.size();
ek.interval = iv;
ek.mode = md;
ek.periodStart = dragonx::data::earningsPeriodStart(nowSec, iv);
const auto& r = s_totalEarnCache.get(ek, state.transactions, nowSec, nowMs);
const double ev = r.total;
const double fiat = ev * state.market.price_usd;
const bool haveFiat = state.market.price_usd > 0.0;
const int f = haveFiat ? fmt : 0;
char buf[64];
if (f == 0) snprintf(buf, sizeof(buf), "%+.4f", ev);
else if (f == 2) snprintf(buf, sizeof(buf), "%s$%.2f", ev < 0 ? "-" : "+", std::fabs(fiat));
else snprintf(buf, sizeof(buf), "%+.4f ($%.2f)", ev, fiat);
outText = buf;
outCol = ev > 1e-12 ? S.resolveColor("var(--accent-shielded)", Success())
: (ev < -1e-12 ? S.resolveColor("var(--accent-error)", ReadableError())
: OnSurfaceDisabled());
return true;
}
// Render compact hero line: logo + balance + USD + mining on one line // Render compact hero line: logo + balance + USD + mining on one line
void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs, float heroHeightOverride) { void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs, float heroHeightOverride) {
using namespace material; using namespace material;
@@ -96,6 +136,18 @@ void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float v
ImGui::Dummy(ImVec2(capFont->CalcTextSizeA(tickerFontSize, 10000, 0, DRAGONX_TICKER).x + Layout::spacingLg(), lineH)); ImGui::Dummy(ImVec2(capFont->CalcTextSizeA(tickerFontSize, 10000, 0, DRAGONX_TICKER).x + Layout::spacingLg(), lineH));
ImGui::SameLine(); ImGui::SameLine();
// Total period-earnings (left of the USD) when the global earnings toggle is on.
{
std::string earnText; ImU32 earnCol;
if (FormatTotalEarnings(app, earnText, earnCol)) {
dl->AddText(capFont, tickerFontSize,
ImVec2(ImGui::GetCursorScreenPos().x, tickerY), earnCol, earnText.c_str());
float ew = capFont->CalcTextSizeA(tickerFontSize, 10000, 0, earnText.c_str()).x;
ImGui::Dummy(ImVec2(ew + Layout::spacingLg(), lineH));
ImGui::SameLine();
}
}
// USD value // USD value
const auto& state = app->state(); const auto& state = app->state();
double usd_value = state.getBalanceUSD(); double usd_value = state.getBalanceUSD();
@@ -164,6 +216,29 @@ void RenderSharedAddressList(App* app, float listH, float availW,
addRows(state.t_addresses, false); addRows(state.t_addresses, false);
auto rows = BuildAddressListRows(rowInputs, addr_search, s_hideZeroBalances, s_showHidden); auto rows = BuildAddressListRows(rowInputs, addr_search, s_hideZeroBalances, s_showHidden);
// ---- Inline period-earnings: compute once (cached on tx_revision + interval/mode/period) ----
static dragonx::data::EarningsCache s_earnCache;
const dragonx::data::EarningsResult* earnResult = nullptr;
bool earnShowAll = false;
int earnFormat = 1; // 0=coin 1=coin+fiat 2=fiat
if (app->settings()) {
auto* est = app->settings();
earnShowAll = est->getEarningsShowAll();
earnFormat = est->getEarningsFormat();
auto iv = (dragonx::data::EarningsInterval)est->getEarningsInterval();
auto md = (dragonx::data::EarningsMode)est->getEarningsMode();
const std::int64_t nowSec = (std::int64_t)std::time(nullptr);
const std::int64_t nowMs = (std::int64_t)std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
dragonx::data::EarningsKey ek;
ek.txRevision = state.tx_revision;
ek.txCount = state.transactions.size();
ek.interval = iv;
ek.mode = md;
ek.periodStart = dragonx::data::earningsPeriodStart(nowSec, iv);
earnResult = &s_earnCache.get(ek, state.transactions, nowSec, nowMs);
}
// ---- Toolbar: search + checkboxes + create buttons ---- // ---- Toolbar: search + checkboxes + create buttons ----
float avail = ImGui::GetContentRegionAvail().x; float avail = ImGui::GetContentRegionAvail().x;
float schemaMaxW = (searchIn.maxWidth >= 0) ? searchIn.maxWidth : 250.0f; float schemaMaxW = (searchIn.maxWidth >= 0) ? searchIn.maxWidth : 250.0f;
@@ -185,39 +260,46 @@ void RenderSharedAddressList(App* app, float listH, float availW,
} }
} }
float buttonWidth = ((addrBtn.width > 0) ? addrBtn.width : 140.0f) * dp; // ---- Inline period-earnings controls: interval pills (1H/1D/1M/1Y), mode toggle (received /
float spacing = ((addrBtn.gap > 0) ? addrBtn.gap : 8.0f) * dp; // net), and a global show-all toggle. The create-address buttons moved below the list (footer). ----
float totalButtonsWidth = buttonWidth * 2 + spacing; if (app->settings()) {
float kMinButtonsPosition = std::max(S.drawElement("tabs.balance", "min-buttons-position").size, auto* est = app->settings();
S.drawElement("tabs.balance", "buttons-position").size * hs); ImFont* icoF = Type().iconSmall();
ImGui::SameLine(std::max(kMinButtonsPosition, const float segH = ImGui::GetFrameHeight(); // fixed height so pills + icon buttons align
ImGui::GetWindowWidth() - totalButtonsWidth - // A compact selected/unselected toggle. `font` nullptr = default font (text pills).
S.drawElement("tabs.balance", "button-row-right-margin").sizeOr(16.0f))); auto segBtn = [&](const char* idlabel, bool selected, const char* tip, ImFont* font) -> bool {
if (font) ImGui::PushFont(font);
ImGui::PushStyleColor(ImGuiCol_Button, selected ? WithAlpha(Primary(), 70) : IM_COL32(255, 255, 255, 12));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, WithAlpha(Primary(), 110));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, WithAlpha(Primary(), 140));
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(selected ? OnSurface() : OnSurfaceMedium()));
bool clk = ImGui::Button(idlabel, ImVec2(0, segH));
ImGui::PopStyleColor(4);
if (font) ImGui::PopFont();
if (tip && ImGui::IsItemHovered()) material::Tooltip("%s", tip);
return clk;
};
bool sharedAddrSyncing = state.sync.syncing && !state.sync.isSynced(); ImGui::SameLine(0, Layout::spacingLg());
ImGui::BeginDisabled(sharedAddrSyncing); // Interval pills
if (s_generating_z_address) { const char* ivText[4] = { "1H", "1D", "1M", "1Y" };
char genLabel[64]; int iv = est->getEarningsInterval();
snprintf(genLabel, sizeof(genLabel), "%s%s##shared_z", TR("generating"), material::LoadingDots()); for (int k = 0; k < 4; ++k) {
TactileButton(genLabel, ImVec2(buttonWidth, 0), char lbl[16]; snprintf(lbl, sizeof(lbl), "%s##earniv%d", ivText[k], k);
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font)); if (segBtn(lbl, iv == k, TR("tt_earn_interval"), nullptr)) { est->setEarningsInterval(k); est->save(); }
} else if (TactileButton(TR("new_z_address"), ImVec2(buttonWidth, 0), if (k < 3) ImGui::SameLine(0, 2.0f * dp);
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) { }
s_generating_z_address = true; // Mode toggle (received-only vs net)
app->createNewZAddress([](const std::string& addr) { ImGui::SameLine(0, Layout::spacingMd());
s_generating_z_address = false; int emode = est->getEarningsMode();
DEBUG_LOGF("Created new z-address: %s\n", addr.c_str()); if (segBtn(ICON_MD_CALL_RECEIVED "##earnmodein", emode == 0, TR("tt_earn_mode_inflows"), icoF)) { est->setEarningsMode(0); est->save(); }
}); ImGui::SameLine(0, 2.0f * dp);
if (segBtn(ICON_MD_SWAP_VERT "##earnmodenet", emode == 1, TR("tt_earn_mode_net"), icoF)) { est->setEarningsMode(1); est->save(); }
// Global earnings-column toggle
ImGui::SameLine(0, Layout::spacingMd());
bool showAll = est->getEarningsShowAll();
if (segBtn(ICON_MD_TRENDING_UP "##earnshowall", showAll, TR("tt_earn_show_all"), icoF)) { est->setEarningsShowAll(!showAll); est->save(); }
} }
ImGui::SameLine();
if (TactileButton(TR("new_mining_address"), ImVec2(buttonWidth, 0),
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
app->createNewTAddress([](const std::string& addr) {
DEBUG_LOGF("Created new t-address: %s\n", addr.c_str());
});
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_new_mining_address"));
ImGui::EndDisabled();
ImGui::Dummy(ImVec2(0, Layout::spacingXs())); ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
@@ -279,6 +361,7 @@ void RenderSharedAddressList(App* app, float listH, float availW,
addrScrollMaxY = ImGui::GetScrollMaxY(); addrScrollMaxY = ImGui::GetScrollMaxY();
ImU32 greenCol = S.resolveColor("var(--accent-shielded)", Success()); ImU32 greenCol = S.resolveColor("var(--accent-shielded)", Success());
ImU32 goldCol = S.resolveColor("var(--accent-transparent)", Warning()); ImU32 goldCol = S.resolveColor("var(--accent-transparent)", Warning());
ImU32 earnNegCol = S.resolveColor("var(--accent-error)", material::ReadableError()); // negative net earnings
float rowPadLeft = Layout::cardInnerPadding(); float rowPadLeft = Layout::cardInnerPadding();
float rowPadRight = Layout::cardInnerPadding(); float rowPadRight = Layout::cardInnerPadding();
float rowIconSz = std::max(S.drawElement("tabs.balance", "address-icon-min-size").size, float rowIconSz = std::max(S.drawElement("tabs.balance", "address-icon-min-size").size,
@@ -594,9 +677,33 @@ void RenderSharedAddressList(App* app, float listH, float availW,
} }
} }
// ---- Line-2 right content widths: USD + optional period-earnings to its left ----
std::string usdText = FormatAddressUsdValue(addr.balance, state.market.price_usd);
float usdW = usdText.empty() ? 0.0f
: capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, usdText.c_str()).x;
const bool haveFiat = state.market.price_usd > 0.0;
bool earnShown = earnResult && app->settings() &&
(earnShowAll || app->settings()->isAddressEarningsShown(addr.address));
char earnBuf[48]; earnBuf[0] = '\0';
float earnW = 0.0f; ImU32 earnCol = OnSurfaceDisabled();
if (earnShown) {
double ev = dragonx::data::earningsForAddress(*earnResult, addr.address);
double fiat = ev * state.market.price_usd;
int fmt = (!haveFiat) ? 0 : earnFormat; // no price -> coin only
if (fmt == 0) snprintf(earnBuf, sizeof(earnBuf), "%+.4f", ev);
else if (fmt == 2) snprintf(earnBuf, sizeof(earnBuf), "%s$%.2f", ev < 0 ? "-" : "+", std::fabs(fiat));
else snprintf(earnBuf, sizeof(earnBuf), "%+.4f ($%.2f)", ev, fiat);
earnW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, earnBuf).x;
earnCol = ev > 1e-12 ? greenCol : (ev < -1e-12 ? earnNegCol : OnSurfaceDisabled());
if (row.hidden) earnCol = OnSurfaceDisabled();
}
// Left edge of the line-2 right content, so the address truncates before it.
const float line2RightStart = contentRight - usdW
- (earnShown && earnBuf[0] ? (earnW + Layout::spacingMd()) : 0.0f);
// ---- Address (second line) ---- // ---- Address (second line) ----
{ {
float addrAvailW = contentRight - labelX; float addrAvailW = line2RightStart - Layout::spacingMd() - labelX;
ImVec2 fullAddrSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0.0f, addr.address.c_str()); ImVec2 fullAddrSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0.0f, addr.address.c_str());
std::string display_addr; std::string display_addr;
if (fullAddrSz.x <= addrAvailW) { if (fullAddrSz.x <= addrAvailW) {
@@ -624,14 +731,17 @@ void RenderSharedAddressList(App* app, float listH, float availW,
DrawTextShadow(dl, body2, body2->LegacySize, ImVec2(balX, cy), balCol, balBuf); DrawTextShadow(dl, body2, body2->LegacySize, ImVec2(balX, cy), balCol, balBuf);
} }
// ---- USD value (second line, right-aligned) ---- // ---- Line 2 right: period-earnings (left of) + USD value, right-aligned ----
{ {
std::string usdText = FormatAddressUsdValue(addr.balance, state.market.price_usd); const float line2Y = cy + body2->LegacySize + Layout::spacingXs();
if (!usdText.empty()) { if (earnShown && earnBuf[0]) {
ImVec2 usdSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, usdText.c_str());
dl->AddText(capFont, capFont->LegacySize, dl->AddText(capFont, capFont->LegacySize,
ImVec2(contentRight - usdSz.x, ImVec2(contentRight - usdW - Layout::spacingMd() - earnW, line2Y),
cy + body2->LegacySize + Layout::spacingXs()), earnCol, earnBuf);
}
if (!usdText.empty()) {
dl->AddText(capFont, capFont->LegacySize,
ImVec2(contentRight - usdW, line2Y),
OnSurfaceDisabled(), usdText.c_str()); OnSurfaceDisabled(), usdText.c_str());
} }
} }
@@ -728,6 +838,15 @@ void RenderSharedAddressList(App* app, float listH, float availW,
if (ImGui::MenuItem(TR("favorite_address"))) if (ImGui::MenuItem(TR("favorite_address")))
app->favoriteAddress(addr.address); app->favoriteAddress(addr.address);
} }
// Per-address earnings column toggle (when the global show-all toggle is off).
if (app->settings()) {
const bool shown = app->settings()->isAddressEarningsShown(addr.address);
if (ImGui::MenuItem(shown ? TR("hide_earnings") : TR("show_earnings"))) {
if (shown) app->settings()->hideAddressEarnings(addr.address);
else app->settings()->showAddressEarnings(addr.address);
app->settings()->save();
}
}
// Portfolio membership — toggle this address in each Market-tab portfolio entry. // Portfolio membership — toggle this address in each Market-tab portfolio entry.
ImGui::Separator(); ImGui::Separator();
if (ImGui::BeginMenu(TR("portfolio_add_to"))) { if (ImGui::BeginMenu(TR("portfolio_add_to"))) {
@@ -818,7 +937,8 @@ void RenderSharedAddressList(App* app, float listH, float availW,
listPanelMin.y, listPanelMax.y, fadeZone, addrScrollY, addrScrollMaxY); listPanelMin.y, listPanelMax.y, fadeZone, addrScrollY, addrScrollMaxY);
} }
// Address count // Address count + create-address buttons (relocated below the list to free the toolbar row for
// the earnings controls). Count on the left, New Z / New mining right-aligned on the same row.
{ {
ImGui::Dummy(ImVec2(0, Layout::spacingSm())); ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
char countBuf[128]; char countBuf[128];
@@ -826,6 +946,38 @@ void RenderSharedAddressList(App* app, float listH, float availW,
snprintf(countBuf, sizeof(countBuf), TR("showing_x_of_y"), snprintf(countBuf, sizeof(countBuf), TR("showing_x_of_y"),
(int)rows.size(), totalAddrs); (int)rows.size(), totalAddrs);
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), countBuf); Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), countBuf);
float buttonWidth = ((addrBtn.width > 0) ? addrBtn.width : 140.0f) * dp;
float spacing = ((addrBtn.gap > 0) ? addrBtn.gap : 8.0f) * dp;
float totalButtonsWidth = buttonWidth * 2 + spacing;
float rightMargin = S.drawElement("tabs.balance", "button-row-right-margin").sizeOr(16.0f);
ImGui::SameLine(std::max(ImGui::GetCursorPosX(),
ImGui::GetWindowWidth() - totalButtonsWidth - rightMargin));
bool sharedAddrSyncing = state.sync.syncing && !state.sync.isSynced();
ImGui::BeginDisabled(sharedAddrSyncing);
if (s_generating_z_address) {
char genLabel[64];
snprintf(genLabel, sizeof(genLabel), "%s%s##shared_z", TR("generating"), material::LoadingDots());
TactileButton(genLabel, ImVec2(buttonWidth, 0),
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font));
} else if (TactileButton(TR("new_z_address"), ImVec2(buttonWidth, 0),
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
s_generating_z_address = true;
app->createNewZAddress([](const std::string& addr) {
s_generating_z_address = false;
DEBUG_LOGF("Created new z-address: %s\n", addr.c_str());
});
}
ImGui::SameLine();
if (TactileButton(TR("new_mining_address"), ImVec2(buttonWidth, 0),
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
app->createNewTAddress([](const std::string& addr) {
DEBUG_LOGF("Created new t-address: %s\n", addr.c_str());
});
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_new_mining_address"));
ImGui::EndDisabled();
} }
} }
+7
View File
@@ -8,6 +8,8 @@
#pragma once #pragma once
#include <string>
#include "imgui.h" #include "imgui.h"
namespace dragonx { namespace dragonx {
@@ -31,5 +33,10 @@ void RenderSharedAddressList(App* app, float listH, float availW, float glassRou
void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs); void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs);
void RenderSyncBar(App* app, ImDrawList* dl, float vs); void RenderSyncBar(App* app, ImDrawList* dl, float vs);
// Total period-earnings for the whole wallet, formatted per the earnings settings. Returns false
// (and leaves out* untouched) when the global earnings toggle is off, so layouts show it only when
// the user has opted into the earnings view. Shared by RenderCompactHero and the layout totals.
bool FormatTotalEarnings(App* app, std::string& outText, ImU32& outCol);
} // namespace ui } // namespace ui
} // namespace dragonx } // namespace dragonx
+7 -1
View File
@@ -364,7 +364,7 @@ static void RenderBalanceClassic(App* app)
OnSurfaceMedium(), DRAGONX_TICKER); OnSurfaceMedium(), DRAGONX_TICKER);
cy += sub1->LegacySize + valGap; cy += sub1->LegacySize + valGap;
// USD value // USD value (+ total period-earnings to its right when the earnings toggle is on)
{ {
double usd_value = state.getBalanceUSD(); double usd_value = state.getBalanceUSD();
if (usd_value > 0.0) if (usd_value > 0.0)
@@ -373,6 +373,12 @@ static void RenderBalanceClassic(App* app)
snprintf(buf, sizeof(buf), "$-.-- USD"); snprintf(buf, sizeof(buf), "$-.-- USD");
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy), dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy),
OnSurfaceDisabled(), buf); OnSurfaceDisabled(), buf);
std::string earnText; ImU32 earnCol;
if (FormatTotalEarnings(app, earnText, earnCol)) {
float usdW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, buf).x;
dl->AddText(capFont, capFont->LegacySize,
ImVec2(cx + usdW + tickGap * 3.0f, cy), earnCol, earnText.c_str());
}
} }
cy += capFont->LegacySize + 2 * dp; cy += capFont->LegacySize + 2 * dp;
+12
View File
@@ -1065,6 +1065,18 @@ void I18n::loadBuiltinEnglish()
strings_["filter"] = "Filter..."; strings_["filter"] = "Filter...";
strings_["no_addresses_yet"] = "No addresses yet"; strings_["no_addresses_yet"] = "No addresses yet";
strings_["showing_x_of_y"] = "Showing %d of %d addresses"; strings_["showing_x_of_y"] = "Showing %d of %d addresses";
strings_["tt_earn_interval"] = "Earnings period: 1H = this hour, 1D = today, 1M = this month, 1Y = this year";
strings_["tt_earn_mode_inflows"] = "Earnings mode: amounts received (including mined)";
strings_["tt_earn_mode_net"] = "Earnings mode: net (received minus sent)";
strings_["tt_earn_show_all"] = "Show the earnings figure for every address";
strings_["show_earnings"] = "Show earnings";
strings_["hide_earnings"] = "Hide earnings";
strings_["earnings_label"] = "Earnings";
strings_["earnings_format"] = "Earnings Display Format";
strings_["tt_earnings_format"] = "How the per-address and total earnings figures are shown";
strings_["earnings_format_coin"] = "Coin only";
strings_["earnings_format_coin_fiat"] = "Coin + fiat";
strings_["earnings_format_fiat"] = "Fiat only";
strings_["set_label"] = "Set Label"; strings_["set_label"] = "Set Label";
strings_["copied"] = "Copied!"; strings_["copied"] = "Copied!";
strings_["hidden_tag"] = " (hidden)"; strings_["hidden_tag"] = " (hidden)";