feat(settings): adjustable opacity for the favorite gold outline + star

Adds a "Favorite Highlight Opacity" slider (15-100%, default 100%) to
Settings > Appearance > Scale & Effects. It scales the alpha of the four
favorite gold tokens (fill/border/icon/outline) at their resolve site in
the Balance address list via material::ScaleAlpha, so the gold row outline
and the star button fade together. The multiplier is skin-agnostic (it
scales whatever alpha the theme resolved) and touches only favorited rows;
non-favorite rows and the white selection highlight are unchanged, and the
100% default is byte-identical to the previous look.

The preference lives in Settings (favorite_opacity, clamped 0.15-1.0 in both
the setter and on load), persists across restarts, updates live, and is not
low-spec gated (it's a pure color-alpha preference, independent of the
transparency sliders). New i18n keys favorite_opacity / tt_favorite_opacity
are translated into all eight locales; the CJK subset font is rebuilt to add
the two new glyphs the zh string needs.

Verified: full + lite builds clean, full + lite test suites pass, hygiene OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 09:58:17 -05:00
co-authored by Claude Opus 4.8
parent e40c6c0a7c
commit 5fcab14e4e
14 changed files with 61 additions and 4 deletions
Binary file not shown.
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "Wie zeige ich Fiat-Preise an?",
"faq_w_set_title": "Einstellungen & Erscheinungsbild",
"favorite_address": "Als Favorit markieren",
"favorite_opacity": "Deckkraft der Favoriten-Hervorhebung",
"fee": "Gebühr",
"fee_high": "Hoch",
"fee_label": "Gebühr:",
@@ -2069,6 +2070,7 @@
"tt_export_all": "Alle privaten Schlüssel in eine Datei exportieren",
"tt_export_csv": "Transaktionsverlauf als CSV-Tabelle exportieren",
"tt_export_key": "Den privaten Schlüssel der ausgewählten Adresse exportieren",
"tt_favorite_opacity": "Deckkraft des Favoriten-Sterns und seiner goldenen Umrandung in der Adressliste (mindestens 15 %)",
"tt_fetch_prices": "DRGX-Marktpreise von der CoinGecko-API abrufen",
"tt_font_scale": "Alle Texte und UI skalieren (1.0x = Standard, bis 1.5x).",
"tt_idle_delay": "Wie lange vor dem Start des Minings gewartet werden soll",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "¿Cómo muestro precios en moneda fiat?",
"faq_w_set_title": "Ajustes y apariencia",
"favorite_address": "Marcar como favorito",
"favorite_opacity": "Opacidad del resaltado de favoritos",
"fee": "Comisión",
"fee_high": "Alta",
"fee_label": "Comisión:",
@@ -2069,6 +2070,7 @@
"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_key": "Exportar la clave privada de la dirección seleccionada",
"tt_favorite_opacity": "Opacidad del botón de estrella de favoritos y su contorno dorado en la lista de direcciones (mínimo 15 %)",
"tt_fetch_prices": "Obtener precios de mercado de DRGX desde la API de CoinGecko",
"tt_font_scale": "Escalar todo el texto y la interfaz (1.0x = predeterminado, hasta 1.5x).",
"tt_idle_delay": "Cuánto tiempo esperar antes de empezar a minar",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "Comment afficher les prix en monnaie fiduciaire ?",
"faq_w_set_title": "Paramètres et apparence",
"favorite_address": "Ajouter aux favoris",
"favorite_opacity": "Opacité de la surbrillance des favoris",
"fee": "Frais",
"fee_high": "Élevés",
"fee_label": "Frais :",
@@ -2069,6 +2070,7 @@
"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_key": "Exporter la clé privée de l'adresse sélectionnée",
"tt_favorite_opacity": "Opacité du bouton étoile des favoris et de son contour doré dans la liste d'adresses (minimum 15 %)",
"tt_fetch_prices": "Récupérer les prix du marché DRGX depuis l'API CoinGecko",
"tt_font_scale": "Mettre à l'échelle tout le texte et l'interface (1.0x = par défaut, jusqu'à 1.5x).",
"tt_idle_delay": "Combien de temps attendre avant de commencer le minage",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "法定通貨での価格を表示するには?",
"faq_w_set_title": "設定と外観",
"favorite_address": "お気に入りに追加",
"favorite_opacity": "お気に入りハイライトの不透明度",
"fee": "手数料",
"fee_high": "高い",
"fee_label": "手数料:",
@@ -2069,6 +2070,7 @@
"tt_export_all": "すべての秘密鍵をファイルにエクスポート",
"tt_export_csv": "トランザクション履歴を CSV スプレッドシートとしてエクスポート",
"tt_export_key": "選択したアドレスの秘密鍵をエクスポート",
"tt_favorite_opacity": "アドレス一覧のお気に入りスターボタンとその金色の枠線の不透明度(最小15%)",
"tt_fetch_prices": "CoinGecko API から DRGX の市場価格を取得",
"tt_font_scale": "すべてのテキストと UI をスケーリング(1.0x = デフォルト、最大 1.5x)。",
"tt_idle_delay": "マイニング開始前の待機時間",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "법정화폐 가격을 어떻게 표시하나요?",
"faq_w_set_title": "설정 및 화면",
"favorite_address": "즐겨찾기에 추가",
"favorite_opacity": "즐겨찾기 강조 불투명도",
"fee": "수수료",
"fee_high": "높음",
"fee_label": "수수료:",
@@ -2069,6 +2070,7 @@
"tt_export_all": "모든 개인키를 파일로 내보내기",
"tt_export_csv": "거래 내역을 CSV 스프레드시트로 내보내기",
"tt_export_key": "선택한 주소의 개인키 내보내기",
"tt_favorite_opacity": "주소 목록에서 즐겨찾기 별 버튼과 금색 테두리의 불투명도(최소 15%)",
"tt_fetch_prices": "CoinGecko API에서 DRGX 시장 가격 가져오기",
"tt_font_scale": "모든 텍스트 및 UI 크기 조정 (1.0x = 기본, 최대 1.5x).",
"tt_idle_delay": "채굴 시작 전 대기 시간",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "Como exibo preços em moeda fiduciária?",
"faq_w_set_title": "Configurações e aparência",
"favorite_address": "Adicionar aos favoritos",
"favorite_opacity": "Opacidade do destaque de favoritos",
"fee": "Taxa",
"fee_high": "Alta",
"fee_label": "Taxa:",
@@ -2069,6 +2070,7 @@
"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_key": "Exportar a chave privada do endereço selecionado",
"tt_favorite_opacity": "Opacidade do botão de estrela de favoritos e do seu contorno dourado na lista de endereços (mínimo 15%)",
"tt_fetch_prices": "Obter preços de mercado DRGX da API CoinGecko",
"tt_font_scale": "Escalar todo o texto e interface (1.0x = padrão, até 1.5x).",
"tt_idle_delay": "Quanto tempo esperar antes de iniciar a mineração",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "Как показать цены в фиатной валюте?",
"faq_w_set_title": "Настройки и внешний вид",
"favorite_address": "Добавить в избранное",
"favorite_opacity": "Непрозрачность выделения избранного",
"fee": "Комиссия",
"fee_high": "Высокая",
"fee_label": "Комиссия:",
@@ -2069,6 +2070,7 @@
"tt_export_all": "Экспортировать все приватные ключи в файл",
"tt_export_csv": "Экспортировать историю транзакций в виде таблицы CSV",
"tt_export_key": "Экспортировать приватный ключ выбранного адреса",
"tt_favorite_opacity": "Непрозрачность кнопки-звёздочки избранного и её золотой обводки в списке адресов (минимум 15 %)",
"tt_fetch_prices": "Получить рыночные цены DRGX из API CoinGecko",
"tt_font_scale": "Масштабировать весь текст и интерфейс (1.0x = по умолчанию, до 1.5x).",
"tt_idle_delay": "Сколько ждать перед началом майнинга",
+2
View File
@@ -863,6 +863,7 @@
"faq_w_set_4_q": "如何显示法币价格?",
"faq_w_set_title": "设置与外观",
"favorite_address": "收藏地址",
"favorite_opacity": "收藏高亮不透明度",
"fee": "手续费",
"fee_high": "高",
"fee_label": "手续费:",
@@ -2069,6 +2070,7 @@
"tt_export_all": "将所有私钥导出到文件",
"tt_export_csv": "将交易历史导出为 CSV 电子表格",
"tt_export_key": "导出所选地址的私钥",
"tt_favorite_opacity": "地址列表中收藏星形按钮及其金色轮廓的不透明度(最低 15%)",
"tt_fetch_prices": "从 CoinGecko API 获取 DRGX 市场价格",
"tt_font_scale": "缩放所有文本和界面(1.0x = 默认,最大 1.5x)。",
"tt_idle_delay": "开始挖矿前等待多长时间",
+3
View File
@@ -204,6 +204,8 @@ bool Settings::load(const std::string& path)
ui_opacity_ = 1.0f; // legacy: reduced = fully opaque
}
loadScalar(j, "window_opacity", window_opacity_);
loadScalar(j, "favorite_opacity", favorite_opacity_);
favorite_opacity_ = std::clamp(favorite_opacity_, 0.15f, 1.0f); // matches setFavoriteOpacity()
if (j.contains("balance_layout")) {
if (j["balance_layout"].is_string())
balance_layout_ = j["balance_layout"].get<std::string>();
@@ -542,6 +544,7 @@ bool Settings::save(const std::string& path)
j["gradient_background"] = gradient_background_;
j["ui_opacity"] = ui_opacity_;
j["window_opacity"] = window_opacity_;
j["favorite_opacity"] = favorite_opacity_;
j["balance_layout"] = balance_layout_; // saved as string ID
j["portfolio_style"] = portfolio_style_;
j["contacts_view_mode"] = contacts_view_mode_;
+6
View File
@@ -261,6 +261,11 @@ public:
float getNoiseOpacity() const { return noise_opacity_; }
void setNoiseOpacity(float v) { noise_opacity_ = v; }
// Opacity multiplier for the favorite gold visuals (row outline + star button) in the
// Balance address list. 1.0 = theme default (unchanged); floor 0.15 so it can't vanish.
float getFavoriteOpacity() const { return favorite_opacity_; }
void setFavoriteOpacity(float v) { favorite_opacity_ = std::max(0.15f, std::min(1.0f, v)); }
// Gradient background mode (use gradient variant of theme background)
bool getGradientBackground() const { return gradient_background_; }
void setGradientBackground(bool v) { gradient_background_ = v; }
@@ -685,6 +690,7 @@ private:
int acrylic_quality_ = 2;
float blur_multiplier_ = 0.10f;
float noise_opacity_ = 0.5f;
float favorite_opacity_ = 1.0f; // Favorite gold outline/star opacity multiplier (0.15–1.0, 1.0 = theme default)
bool gradient_background_ = false;
#ifdef _WIN32
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque)
+27
View File
@@ -106,6 +106,7 @@ struct SettingsPageState {
float noise_opacity = 0.5f;
float ui_opacity = 1.0f;
float window_opacity = 1.0f;
float favorite_opacity = 1.0f;
std::string balance_layout = "classic";
bool scanline_enabled = true;
bool theme_effects_enabled = true;
@@ -448,6 +449,7 @@ static void loadSettingsPageState(App* app) {
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
s_settingsState.ui_opacity = settings->getUIOpacity();
s_settingsState.window_opacity = settings->getWindowOpacity();
s_settingsState.favorite_opacity = settings->getFavoriteOpacity();
s_settingsState.noise_opacity = settings->getNoiseOpacity();
s_settingsState.gradient_background = settings->getGradientBackground();
@@ -532,6 +534,7 @@ static void saveSettingsPageState(config::Settings* settings) {
settings->setBlurMultiplier(blur);
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->setFavoriteOpacity(s_settingsState.favorite_opacity); // not low-spec gated — cosmetic
settings->setNoiseOpacity(s_settingsState.noise_opacity);
settings->setGradientBackground(s_settingsState.gradient_background);
settings->setScanlineEnabled(lowSpecOverrides ? realPrefs.scanline_enabled : s_settingsState.scanline_enabled);
@@ -1025,6 +1028,30 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
// --- Favorite highlight opacity (gold outline + star button in the Balance address list) ---
// Pure color-alpha preference; not low-spec gated (unlike the transparency sliders below).
{
ImGui::PushFont(body2);
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(OnSurfaceMedium()));
ImGui::TextUnformatted(TR("favorite_opacity"));
ImGui::PopStyleColor();
float favSliderW = std::min(std::max(S.drawElement("components.settings-page", "effects-input-min-width").size, contentW), 360.0f * dp);
ImGui::SetNextItemWidth(favSliderW);
{
char favop_fmt[16];
snprintf(favop_fmt, sizeof(favop_fmt), "%.0f%%%%", s_settingsState.favorite_opacity * 100.0f);
if (ImGui::SliderFloat("##FavoriteOpacity", &s_settingsState.favorite_opacity, 0.15f, 1.0f, favop_fmt,
ImGuiSliderFlags_AlwaysClamp)) {
if (app->settings()) app->settings()->setFavoriteOpacity(s_settingsState.favorite_opacity); // live; persisted on release
}
}
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_favorite_opacity"));
ImGui::PopFont();
}
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
// --- Effect toggles (always visible, horizontal wrapping flow) ---
{
ImGui::PushFont(body2);
+7 -4
View File
@@ -285,10 +285,13 @@ void RenderSharedAddressList(App* app, float listH, float availW,
float innerW = ImGui::GetContentRegionAvail().x;
// Theme colors for buttons (resolved once)
ImU32 favGoldFill = S.resolveColor("var(--favorite-fill)", IM_COL32(255, 200, 50, 40));
ImU32 favGoldBorder = S.resolveColor("var(--favorite-border)", IM_COL32(255, 200, 50, 100));
ImU32 favGoldIcon = S.resolveColor("var(--favorite-icon)", IM_COL32(255, 200, 50, 255));
ImU32 favGoldOutline= S.resolveColor("var(--favorite-outline)", IM_COL32(255, 200, 50, 120));
// User-adjustable opacity for the favorite gold visuals (Settings > Appearance). 1.0 =
// theme default (unchanged); scales whatever alpha the skin resolved, so it is skin-agnostic.
const float favOp = app->settings() ? app->settings()->getFavoriteOpacity() : 1.0f;
ImU32 favGoldFill = ScaleAlpha(S.resolveColor("var(--favorite-fill)", IM_COL32(255, 200, 50, 40)), favOp);
ImU32 favGoldBorder = ScaleAlpha(S.resolveColor("var(--favorite-border)", IM_COL32(255, 200, 50, 100)), favOp);
ImU32 favGoldIcon = ScaleAlpha(S.resolveColor("var(--favorite-icon)", IM_COL32(255, 200, 50, 255)), favOp);
ImU32 favGoldOutline= ScaleAlpha(S.resolveColor("var(--favorite-outline)", IM_COL32(255, 200, 50, 120)), favOp);
ImU32 btnFill = S.resolveColor("var(--row-button-fill)", IM_COL32(255, 255, 255, 12));
ImU32 btnFillHov = S.resolveColor("var(--row-button-fill-hover)", IM_COL32(255, 255, 255, 25));
ImU32 btnBorder = S.resolveColor("var(--row-button-border)", IM_COL32(255, 255, 255, 25));
+2
View File
@@ -520,6 +520,7 @@ void I18n::loadBuiltinEnglish()
strings_["noise"] = "Noise";
strings_["ui_opacity"] = "UI Opacity";
strings_["window_opacity"] = "Window Opacity";
strings_["favorite_opacity"] = "Favorite Highlight Opacity";
strings_["font_scale"] = "Font Scale";
strings_["refresh"] = "Refresh";
strings_["website"] = "Website";
@@ -795,6 +796,7 @@ void I18n::loadBuiltinEnglish()
strings_["tt_noise"] = "Grain texture intensity (0% = off, 100% = maximum)";
strings_["tt_ui_opacity"] = "Card and sidebar opacity (100% = fully opaque, lower = more see-through)";
strings_["tt_window_opacity"] = "Background opacity (lower = desktop visible through window)";
strings_["tt_favorite_opacity"] = "Opacity of the favorite star button and its gold outline in the address list (minimum 15%)";
strings_["tt_font_scale"] = "Scale all text and UI (1.0x = default, up to 1.5x). Hotkey: Alt + Scroll Wheel";
strings_["tt_custom_theme"] = "Custom theme active";
strings_["tt_address_book"] = "Manage saved addresses for quick sending";