ui: reorganize settings page with collapsible sections
- Rename APPEARANCE section to THEME & LANGUAGE - Move font scale slider out of effects into main section - Collapse visual effects into "Advanced Effects..." toggle - Collapse wallet tools into "Tools & Actions..." toggle - Remove redundant Tools & Actions divider/section from wallet card - Add i18n strings: theme_language, advanced_effects, tools_actions
This commit is contained in:
@@ -122,6 +122,8 @@ static bool sp_verbose_logging = false;
|
|||||||
static std::set<std::string> sp_debug_categories;
|
static std::set<std::string> sp_debug_categories;
|
||||||
static bool sp_debug_cats_dirty = false; // true when changed but daemon not yet restarted
|
static bool sp_debug_cats_dirty = false; // true when changed but daemon not yet restarted
|
||||||
static bool sp_debug_expanded = false; // collapsible card state
|
static bool sp_debug_expanded = false; // collapsible card state
|
||||||
|
static bool sp_effects_expanded = false; // "Advanced Effects..." toggle
|
||||||
|
static bool sp_tools_expanded = false; // "Tools & Actions..." toggle
|
||||||
static bool sp_confirm_clear_ztx = false; // confirmation dialog for clearing z-tx history
|
static bool sp_confirm_clear_ztx = false; // confirmation dialog for clearing z-tx history
|
||||||
|
|
||||||
// (APPEARANCE card now uses ChannelsSplit like all other cards)
|
// (APPEARANCE card now uses ChannelsSplit like all other cards)
|
||||||
@@ -377,11 +379,11 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImFont* sub1 = Type().subtitle1();
|
ImFont* sub1 = Type().subtitle1();
|
||||||
|
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
// APPEARANCE — card (draw-first approach; avoids ChannelsSplit which
|
// THEME & LANGUAGE — card (draw-first approach; avoids ChannelsSplit
|
||||||
// breaks BeginCombo popup rendering in some ImGui versions)
|
// which breaks BeginCombo popup rendering in some ImGui versions)
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
{
|
{
|
||||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("appearance"));
|
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("theme_language"));
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
|
||||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||||
@@ -561,9 +563,59 @@ void RenderSettingsPage(App* app) {
|
|||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
// --- Visual Effects (checkboxes on one row, Quality+Blur paired) ---
|
// --- Font Scale slider (always visible) ---
|
||||||
{
|
{
|
||||||
ImGui::PushFont(body2);
|
ImGui::PushFont(body2);
|
||||||
|
ImGui::TextUnformatted(TR("font_scale"));
|
||||||
|
float fontSliderW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size, contentW);
|
||||||
|
ImGui::SetNextItemWidth(fontSliderW);
|
||||||
|
sp_font_scale = Layout::userFontScale();
|
||||||
|
float prev_font_scale = sp_font_scale;
|
||||||
|
{
|
||||||
|
char fs_fmt[16];
|
||||||
|
snprintf(fs_fmt, sizeof(fs_fmt), "%.2fx", sp_font_scale);
|
||||||
|
ImGui::SliderFloat("##FontScale", &sp_font_scale, 1.0f, 1.5f, fs_fmt,
|
||||||
|
ImGuiSliderFlags_AlwaysClamp);
|
||||||
|
}
|
||||||
|
sp_font_scale = std::max(1.0f, std::min(1.5f,
|
||||||
|
std::round(sp_font_scale * 20.0f) / 20.0f));
|
||||||
|
if (sp_font_scale != prev_font_scale)
|
||||||
|
Layout::setUserFontScaleVisual(sp_font_scale);
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
Layout::setUserFontScale(sp_font_scale);
|
||||||
|
saveSettingsPageState(app->settings());
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_font_scale"));
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
|
// --- Collapsible: Advanced Effects... ---
|
||||||
|
{
|
||||||
|
const char* arrow = sp_effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||||
|
ImGui::PushFont(body2);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
|
||||||
|
{
|
||||||
|
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||||
|
if (ImGui::Button("##EffectsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
|
||||||
|
sp_effects_expanded = !sp_effects_expanded;
|
||||||
|
}
|
||||||
|
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||||
|
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
|
||||||
|
ImFont* iconFont = Type().iconSmall();
|
||||||
|
if (!iconFont) iconFont = body2;
|
||||||
|
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||||
|
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sp_effects_expanded) {
|
||||||
|
ImGui::PushFont(body2);
|
||||||
|
|
||||||
// Checkbox row: Low-spec | Console scanline | Theme effects | Gradient background
|
// Checkbox row: Low-spec | Console scanline | Theme effects | Gradient background
|
||||||
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &sp_low_spec_mode)) {
|
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &sp_low_spec_mode)) {
|
||||||
@@ -603,7 +655,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_low_spec"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_low_spec"));
|
||||||
|
|
||||||
// Simple background is lightweight — always interactive, even in low-spec mode
|
|
||||||
ImGui::SameLine(0, Layout::spacingLg());
|
ImGui::SameLine(0, Layout::spacingLg());
|
||||||
if (ImGui::Checkbox(TrId("simple_background", "simple_bg").c_str(), &sp_gradient_background)) {
|
if (ImGui::Checkbox(TrId("simple_background", "simple_bg").c_str(), &sp_gradient_background)) {
|
||||||
schema::SkinManager::instance().setGradientMode(sp_gradient_background);
|
schema::SkinManager::instance().setGradientMode(sp_gradient_background);
|
||||||
@@ -635,12 +686,10 @@ void RenderSettingsPage(App* app) {
|
|||||||
float baseX = ImGui::GetCursorScreenPos().x;
|
float baseX = ImGui::GetCursorScreenPos().x;
|
||||||
float rightX = baseX + ctrlW + Layout::spacingLg();
|
float rightX = baseX + ctrlW + Layout::spacingLg();
|
||||||
|
|
||||||
// Acrylic label + slider (left column)
|
|
||||||
ImGui::TextUnformatted(TR("acrylic"));
|
ImGui::TextUnformatted(TR("acrylic"));
|
||||||
float row1Y = ImGui::GetCursorScreenPos().y;
|
float row1Y = ImGui::GetCursorScreenPos().y;
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
{
|
{
|
||||||
// Build display format: "Off" at zero, percentage otherwise
|
|
||||||
char blur_fmt[16];
|
char blur_fmt[16];
|
||||||
if (sp_blur_amount < 0.01f)
|
if (sp_blur_amount < 0.01f)
|
||||||
snprintf(blur_fmt, sizeof(blur_fmt), "Off");
|
snprintf(blur_fmt, sizeof(blur_fmt), "Off");
|
||||||
@@ -648,7 +697,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
snprintf(blur_fmt, sizeof(blur_fmt), "%.0f%%%%", sp_blur_amount * 25.0f);
|
snprintf(blur_fmt, sizeof(blur_fmt), "%.0f%%%%", sp_blur_amount * 25.0f);
|
||||||
if (ImGui::SliderFloat("##AcrylicBlur", &sp_blur_amount, 0.0f, 4.0f, blur_fmt,
|
if (ImGui::SliderFloat("##AcrylicBlur", &sp_blur_amount, 0.0f, 4.0f, blur_fmt,
|
||||||
ImGuiSliderFlags_AlwaysClamp)) {
|
ImGuiSliderFlags_AlwaysClamp)) {
|
||||||
// Snap to off when dragged near 0%
|
|
||||||
if (sp_blur_amount > 0.0f && sp_blur_amount < 0.15f) sp_blur_amount = 0.0f;
|
if (sp_blur_amount > 0.0f && sp_blur_amount < 0.15f) sp_blur_amount = 0.0f;
|
||||||
sp_acrylic_enabled = (sp_blur_amount > 0.001f);
|
sp_acrylic_enabled = (sp_blur_amount > 0.001f);
|
||||||
effects::ImGuiAcrylic::ApplyBlurAmount(sp_blur_amount);
|
effects::ImGuiAcrylic::ApplyBlurAmount(sp_blur_amount);
|
||||||
@@ -658,7 +706,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_blur"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_blur"));
|
||||||
float afterRow1Y = ImGui::GetCursorScreenPos().y;
|
float afterRow1Y = ImGui::GetCursorScreenPos().y;
|
||||||
|
|
||||||
// Noise label + slider (right column, same row)
|
|
||||||
float lblH = ImGui::GetTextLineHeight() + ImGui::GetStyle().ItemSpacing.y;
|
float lblH = ImGui::GetTextLineHeight() + ImGui::GetStyle().ItemSpacing.y;
|
||||||
ImGui::SetCursorScreenPos(ImVec2(rightX, row1Y - lblH));
|
ImGui::SetCursorScreenPos(ImVec2(rightX, row1Y - lblH));
|
||||||
ImGui::TextUnformatted(TR("noise"));
|
ImGui::TextUnformatted(TR("noise"));
|
||||||
@@ -678,10 +725,8 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_noise"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_noise"));
|
||||||
|
|
||||||
// Reset cursor to left column, past row 1
|
|
||||||
ImGui::SetCursorScreenPos(ImVec2(baseX, afterRow1Y));
|
ImGui::SetCursorScreenPos(ImVec2(baseX, afterRow1Y));
|
||||||
|
|
||||||
// Row 2: UI Opacity + Window Opacity (labels above)
|
|
||||||
ImGui::TextUnformatted(TR("ui_opacity"));
|
ImGui::TextUnformatted(TR("ui_opacity"));
|
||||||
float row2Y = ImGui::GetCursorScreenPos().y;
|
float row2Y = ImGui::GetCursorScreenPos().y;
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
@@ -697,7 +742,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_ui_opacity"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_ui_opacity"));
|
||||||
float afterRow2Y = ImGui::GetCursorScreenPos().y;
|
float afterRow2Y = ImGui::GetCursorScreenPos().y;
|
||||||
|
|
||||||
// Window label + slider (right column, same row)
|
|
||||||
ImGui::SetCursorScreenPos(ImVec2(rightX, row2Y - lblH));
|
ImGui::SetCursorScreenPos(ImVec2(rightX, row2Y - lblH));
|
||||||
ImGui::TextUnformatted(TR("window_opacity"));
|
ImGui::TextUnformatted(TR("window_opacity"));
|
||||||
ImGui::SetCursorScreenPos(ImVec2(rightX, row2Y));
|
ImGui::SetCursorScreenPos(ImVec2(rightX, row2Y));
|
||||||
@@ -712,12 +756,11 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_window_opacity"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_window_opacity"));
|
||||||
|
|
||||||
// Reset cursor to left column, past row 2
|
|
||||||
ImGui::SetCursorScreenPos(ImVec2(baseX, afterRow2Y));
|
ImGui::SetCursorScreenPos(ImVec2(baseX, afterRow2Y));
|
||||||
|
|
||||||
ImGui::EndDisabled(); // low-spec
|
ImGui::EndDisabled(); // low-spec
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
} // sp_effects_expanded
|
||||||
} else {
|
} else {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Narrow: stacked combos + 2-column effects (original layout)
|
// Narrow: stacked combos + 2-column effects (original layout)
|
||||||
@@ -810,11 +853,62 @@ void RenderSettingsPage(App* app) {
|
|||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
// --- Visual Effects (checkboxes + controls) ---
|
// --- Font Scale slider (always visible) ---
|
||||||
{
|
{
|
||||||
ImGui::PushFont(body2);
|
ImGui::PushFont(body2);
|
||||||
|
ImGui::TextUnformatted(TR("font_scale"));
|
||||||
|
float fontSliderW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size,
|
||||||
|
availWidth - pad * 2);
|
||||||
|
ImGui::SetNextItemWidth(fontSliderW);
|
||||||
|
sp_font_scale = Layout::userFontScale();
|
||||||
|
float prev_font_scale = sp_font_scale;
|
||||||
|
{
|
||||||
|
char fs_fmt[16];
|
||||||
|
snprintf(fs_fmt, sizeof(fs_fmt), "%.2fx", sp_font_scale);
|
||||||
|
ImGui::SliderFloat("##FontScale", &sp_font_scale, 1.0f, 1.5f, fs_fmt,
|
||||||
|
ImGuiSliderFlags_AlwaysClamp);
|
||||||
|
}
|
||||||
|
sp_font_scale = std::max(1.0f, std::min(1.5f,
|
||||||
|
std::round(sp_font_scale * 20.0f) / 20.0f));
|
||||||
|
if (sp_font_scale != prev_font_scale)
|
||||||
|
Layout::setUserFontScaleVisual(sp_font_scale);
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
Layout::setUserFontScale(sp_font_scale);
|
||||||
|
saveSettingsPageState(app->settings());
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_font_scale"));
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
|
// --- Collapsible: Advanced Effects... ---
|
||||||
|
{
|
||||||
|
const char* arrow = sp_effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||||
|
ImGui::PushFont(body2);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
|
||||||
|
{
|
||||||
|
float narrowContentW = availWidth - pad * 2;
|
||||||
|
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||||
|
if (ImGui::Button("##EffectsToggleN", ImVec2(narrowContentW, ImGui::GetFrameHeight()))) {
|
||||||
|
sp_effects_expanded = !sp_effects_expanded;
|
||||||
|
}
|
||||||
|
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||||
|
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
|
||||||
|
ImFont* iconFont = Type().iconSmall();
|
||||||
|
if (!iconFont) iconFont = body2;
|
||||||
|
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||||
|
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + narrowContentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sp_effects_expanded) {
|
||||||
|
ImGui::PushFont(body2);
|
||||||
|
|
||||||
// Checkbox row 1: Low-spec
|
|
||||||
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &sp_low_spec_mode)) {
|
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &sp_low_spec_mode)) {
|
||||||
effects::setLowSpecMode(sp_low_spec_mode);
|
effects::setLowSpecMode(sp_low_spec_mode);
|
||||||
if (sp_low_spec_mode) {
|
if (sp_low_spec_mode) {
|
||||||
@@ -852,7 +946,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_low_spec"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_low_spec"));
|
||||||
|
|
||||||
// Simple background is lightweight — always interactive, even in low-spec mode
|
|
||||||
if (ImGui::Checkbox(TrId("settings_gradient_bg", "gradient_bg").c_str(), &sp_gradient_background)) {
|
if (ImGui::Checkbox(TrId("settings_gradient_bg", "gradient_bg").c_str(), &sp_gradient_background)) {
|
||||||
schema::SkinManager::instance().setGradientMode(sp_gradient_background);
|
schema::SkinManager::instance().setGradientMode(sp_gradient_background);
|
||||||
saveSettingsPageState(app->settings());
|
saveSettingsPageState(app->settings());
|
||||||
@@ -861,7 +954,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
|
|
||||||
ImGui::BeginDisabled(sp_low_spec_mode);
|
ImGui::BeginDisabled(sp_low_spec_mode);
|
||||||
|
|
||||||
// Checkbox row 2: Console scanline | Theme effects
|
|
||||||
if (ImGui::Checkbox(TrId("console_scanline", "scanline").c_str(), &sp_scanline_enabled)) {
|
if (ImGui::Checkbox(TrId("console_scanline", "scanline").c_str(), &sp_scanline_enabled)) {
|
||||||
ConsoleTab::s_scanline_enabled = sp_scanline_enabled;
|
ConsoleTab::s_scanline_enabled = sp_scanline_enabled;
|
||||||
app->settings()->setScanlineEnabled(sp_scanline_enabled);
|
app->settings()->setScanlineEnabled(sp_scanline_enabled);
|
||||||
@@ -876,9 +968,8 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_theme_effects"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_theme_effects"));
|
||||||
|
|
||||||
// Acrylic blur slider (label above)
|
|
||||||
float ctrlW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size,
|
float ctrlW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size,
|
||||||
contentW);
|
availWidth - pad * 2.0f);
|
||||||
ImGui::TextUnformatted(TR("acrylic"));
|
ImGui::TextUnformatted(TR("acrylic"));
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
{
|
{
|
||||||
@@ -897,7 +988,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_blur"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_blur"));
|
||||||
|
|
||||||
// Noise opacity slider (label above)
|
|
||||||
ImGui::TextUnformatted(TR("noise"));
|
ImGui::TextUnformatted(TR("noise"));
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
{
|
{
|
||||||
@@ -914,7 +1004,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_noise"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_noise"));
|
||||||
|
|
||||||
// UI Opacity slider (label above)
|
|
||||||
ImGui::TextUnformatted(TR("ui_opacity"));
|
ImGui::TextUnformatted(TR("ui_opacity"));
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
{
|
{
|
||||||
@@ -928,7 +1017,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_ui_opacity"));
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_ui_opacity"));
|
||||||
|
|
||||||
// Window Opacity slider (label above)
|
|
||||||
ImGui::TextUnformatted(TR("window_opacity"));
|
ImGui::TextUnformatted(TR("window_opacity"));
|
||||||
ImGui::SetNextItemWidth(ctrlW);
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
{
|
{
|
||||||
@@ -943,44 +1031,7 @@ void RenderSettingsPage(App* app) {
|
|||||||
|
|
||||||
ImGui::EndDisabled(); // low-spec
|
ImGui::EndDisabled(); // low-spec
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
} // sp_effects_expanded
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Font Scale slider (always enabled, not affected by low-spec)
|
|
||||||
// ============================================================
|
|
||||||
{
|
|
||||||
ImGui::PushFont(body2);
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::TextUnformatted(TR("font_scale"));
|
|
||||||
float fontSliderW = std::max(S.drawElement("components.settings-page", "effects-input-min-width").size,
|
|
||||||
availWidth - pad * 2);
|
|
||||||
ImGui::SetNextItemWidth(fontSliderW);
|
|
||||||
// Sync from Layout so Alt+scroll hotkey changes are reflected
|
|
||||||
sp_font_scale = Layout::userFontScale();
|
|
||||||
float prev_font_scale = sp_font_scale;
|
|
||||||
{
|
|
||||||
char fs_fmt[16];
|
|
||||||
snprintf(fs_fmt, sizeof(fs_fmt), "%.2fx", sp_font_scale);
|
|
||||||
ImGui::SliderFloat("##FontScale", &sp_font_scale, 1.0f, 1.5f, fs_fmt,
|
|
||||||
ImGuiSliderFlags_AlwaysClamp);
|
|
||||||
}
|
|
||||||
// Snap to 0.05 increments for consistent stepping.
|
|
||||||
// Visual scaling uses FontScaleMain (no atlas rebuild),
|
|
||||||
// atlas rebuild is deferred to slider release for crisp text.
|
|
||||||
sp_font_scale = std::max(1.0f, std::min(1.5f,
|
|
||||||
std::round(sp_font_scale * 20.0f) / 20.0f));
|
|
||||||
if (sp_font_scale != prev_font_scale) {
|
|
||||||
// While dragging: update layout scale without atlas rebuild
|
|
||||||
Layout::setUserFontScaleVisual(sp_font_scale);
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
||||||
// On release: rebuild font atlas at final size
|
|
||||||
Layout::setUserFontScale(sp_font_scale);
|
|
||||||
saveSettingsPageState(app->settings());
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_font_scale"));
|
|
||||||
ImGui::PopFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom padding
|
// Bottom padding
|
||||||
@@ -1000,7 +1051,7 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::Dummy(ImVec2(0, gap));
|
ImGui::Dummy(ImVec2(0, gap));
|
||||||
|
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
// WALLET — card (Keys, Backup, Tools, Maintenance, Node/RPC)
|
// WALLET — card (privacy/daemon toggles + collapsible tools)
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
{
|
{
|
||||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("wallet"));
|
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("wallet"));
|
||||||
@@ -1013,56 +1064,6 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::Indent(pad);
|
ImGui::Indent(pad);
|
||||||
|
|
||||||
float contentW = availWidth - pad * 2;
|
float contentW = availWidth - pad * 2;
|
||||||
float btnSpacing = Layout::spacingMd();
|
|
||||||
float btnPad = S.drawElement("components.settings-page", "wallet-btn-padding").sizeOr(24.0f);
|
|
||||||
|
|
||||||
// Calculate button width that fits available space
|
|
||||||
// 6 buttons total: on wide screens 3+3, on narrow screens 2+2+2
|
|
||||||
int btnsPerRow = (contentW >= 600.0f) ? 3 : 2;
|
|
||||||
float bw = (contentW - btnSpacing * (btnsPerRow - 1)) / btnsPerRow;
|
|
||||||
// Clamp to reasonable size
|
|
||||||
float minBtnW = S.drawElement("components.settings-page", "wallet-btn-min-width").sizeOr(100.0f);
|
|
||||||
bw = std::max(minBtnW, bw);
|
|
||||||
|
|
||||||
// Row 1 — Tools & Actions
|
|
||||||
{
|
|
||||||
if (TactileButton(TR("settings_address_book"), ImVec2(bw, 0), S.resolveFont("button")))
|
|
||||||
AddressBookDialog::show();
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_address_book"));
|
|
||||||
ImGui::SameLine(0, btnSpacing);
|
|
||||||
if (TactileButton(TR("settings_validate_address"), ImVec2(bw, 0), S.resolveFont("button")))
|
|
||||||
ValidateAddressDialog::show();
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_validate"));
|
|
||||||
if (btnsPerRow >= 3) { ImGui::SameLine(0, btnSpacing); } else { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); }
|
|
||||||
if (TactileButton(TR("settings_request_payment"), ImVec2(bw, 0), S.resolveFont("button")))
|
|
||||||
RequestPaymentDialog::show();
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_request_payment"));
|
|
||||||
if (btnsPerRow >= 3) { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); } else { ImGui::SameLine(0, btnSpacing); }
|
|
||||||
if (TactileButton(TR("settings_shield_mining"), ImVec2(bw, 0), S.resolveFont("button")))
|
|
||||||
ShieldDialog::show(ShieldDialog::Mode::ShieldCoinbase);
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_shield_mining"));
|
|
||||||
ImGui::SameLine(0, btnSpacing);
|
|
||||||
if (TactileButton(TR("settings_merge_to_address"), ImVec2(bw, 0), S.resolveFont("button")))
|
|
||||||
ShieldDialog::show(ShieldDialog::Mode::MergeToAddress);
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_merge"));
|
|
||||||
if (btnsPerRow >= 3) { ImGui::SameLine(0, btnSpacing); } else { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); }
|
|
||||||
if (TactileButton(TR("settings_clear_ztx"), ImVec2(bw, 0), S.resolveFont("button"))) {
|
|
||||||
sp_confirm_clear_ztx = true;
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_clear_ztx"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thin divider
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
|
||||||
{
|
|
||||||
float divAlpha = S.drawElement("components.settings-page", "section-divider-alpha").opacity;
|
|
||||||
if (divAlpha <= 0.0f) divAlpha = 0.08f;
|
|
||||||
ImU32 baseDivCol = S.resolveColor("var(--status-divider)", IM_COL32(255, 255, 255, 20));
|
|
||||||
ImU32 divCol = material::ScaleAlpha(baseDivCol, divAlpha / 0.08f);
|
|
||||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
|
||||||
dl->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + contentW, p.y), divCol);
|
|
||||||
}
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
|
||||||
|
|
||||||
// Privacy, Network & Daemon checkboxes — all on one line, shrink text to fit
|
// Privacy, Network & Daemon checkboxes — all on one line, shrink text to fit
|
||||||
{
|
{
|
||||||
@@ -1111,47 +1112,94 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (scale < 1.0f) ImGui::SetWindowFontScale(1.0f);
|
if (scale < 1.0f) ImGui::SetWindowFontScale(1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mine when idle — checkbox + delay combo
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
|
// --- Collapsible: Tools & Actions... ---
|
||||||
{
|
{
|
||||||
if (ImGui::Checkbox(TrId("mine_when_idle", "mine_idle").c_str(), &sp_mine_when_idle)) {
|
const char* arrow = sp_tools_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||||
saveSettingsPageState(app->settings());
|
ImGui::PushFont(body2);
|
||||||
}
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
|
||||||
if (ImGui::IsItemHovered())
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
|
||||||
ImGui::SetTooltip("%s", TR("tt_mine_idle"));
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
|
||||||
|
{
|
||||||
if (sp_mine_when_idle) {
|
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||||
ImGui::SameLine(0, Layout::spacingMd());
|
if (ImGui::Button("##ToolsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
|
||||||
ImGui::AlignTextToFramePadding();
|
sp_tools_expanded = !sp_tools_expanded;
|
||||||
ImGui::TextColored(ImVec4(1, 1, 1, 0.5f), "%s", TR("settings_idle_after"));
|
|
||||||
ImGui::SameLine(0, Layout::spacingSm());
|
|
||||||
|
|
||||||
struct DelayOption { int seconds; const char* label; };
|
|
||||||
static const DelayOption delays[] = {
|
|
||||||
{30, "30s"}, {60, "1m"}, {120, "2m"}, {300, "5m"}, {600, "10m"}
|
|
||||||
};
|
|
||||||
const char* previewLabel = "2m";
|
|
||||||
for (const auto& d : delays) {
|
|
||||||
if (d.seconds == sp_mine_idle_delay) { previewLabel = d.label; break; }
|
|
||||||
}
|
}
|
||||||
|
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||||
ImGui::SetNextItemWidth(schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f));
|
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("tools_actions"));
|
||||||
if (ImGui::BeginCombo("##IdleDelay", previewLabel, ImGuiComboFlags_NoArrowButton)) {
|
ImFont* iconFont = Type().iconSmall();
|
||||||
for (const auto& d : delays) {
|
if (!iconFont) iconFont = body2;
|
||||||
bool selected = (d.seconds == sp_mine_idle_delay);
|
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||||
if (ImGui::Selectable(d.label, selected)) {
|
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||||
sp_mine_idle_delay = d.seconds;
|
|
||||||
saveSettingsPageState(app->settings());
|
|
||||||
}
|
|
||||||
if (selected) ImGui::SetItemDefaultFocus();
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered())
|
|
||||||
ImGui::SetTooltip("%s", TR("tt_idle_delay"));
|
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom row — Keys & Data left-aligned, Setup Wizard right-aligned
|
if (sp_tools_expanded) {
|
||||||
|
float btnSpacing = Layout::spacingMd();
|
||||||
|
int btnsPerRow = (contentW >= 600.0f) ? 3 : 2;
|
||||||
|
float bw = (contentW - btnSpacing * (btnsPerRow - 1)) / btnsPerRow;
|
||||||
|
float minBtnW = S.drawElement("components.settings-page", "wallet-btn-min-width").sizeOr(100.0f);
|
||||||
|
bw = std::max(minBtnW, bw);
|
||||||
|
|
||||||
|
if (TactileButton(TR("settings_address_book"), ImVec2(bw, 0), S.resolveFont("button")))
|
||||||
|
AddressBookDialog::show();
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_address_book"));
|
||||||
|
ImGui::SameLine(0, btnSpacing);
|
||||||
|
if (TactileButton(TR("settings_validate_address"), ImVec2(bw, 0), S.resolveFont("button")))
|
||||||
|
ValidateAddressDialog::show();
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_validate"));
|
||||||
|
if (btnsPerRow >= 3) { ImGui::SameLine(0, btnSpacing); } else { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); }
|
||||||
|
if (TactileButton(TR("settings_request_payment"), ImVec2(bw, 0), S.resolveFont("button")))
|
||||||
|
RequestPaymentDialog::show();
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_request_payment"));
|
||||||
|
if (btnsPerRow >= 3) { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); } else { ImGui::SameLine(0, btnSpacing); }
|
||||||
|
if (TactileButton(TR("settings_shield_mining"), ImVec2(bw, 0), S.resolveFont("button")))
|
||||||
|
ShieldDialog::show(ShieldDialog::Mode::ShieldCoinbase);
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_shield_mining"));
|
||||||
|
ImGui::SameLine(0, btnSpacing);
|
||||||
|
if (TactileButton(TR("settings_merge_to_address"), ImVec2(bw, 0), S.resolveFont("button")))
|
||||||
|
ShieldDialog::show(ShieldDialog::Mode::MergeToAddress);
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_merge"));
|
||||||
|
if (btnsPerRow >= 3) { ImGui::SameLine(0, btnSpacing); } else { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); }
|
||||||
|
if (TactileButton(TR("settings_clear_ztx"), ImVec2(bw, 0), S.resolveFont("button"))) {
|
||||||
|
sp_confirm_clear_ztx = true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_clear_ztx"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||||
|
ImGui::Unindent(pad);
|
||||||
|
|
||||||
|
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||||
|
dl->ChannelsSetCurrent(0);
|
||||||
|
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||||
|
dl->ChannelsMerge();
|
||||||
|
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||||
|
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, gap));
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// BACKUP & DATA — card
|
||||||
|
// ====================================================================
|
||||||
|
{
|
||||||
|
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("backup_data"));
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
|
||||||
|
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||||
|
dl->ChannelsSplit(2);
|
||||||
|
dl->ChannelsSetCurrent(1);
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||||
|
ImGui::Indent(pad);
|
||||||
|
|
||||||
|
float contentW = availWidth - pad * 2;
|
||||||
|
float btnPad = S.drawElement("components.settings-page", "wallet-btn-padding").sizeOr(24.0f);
|
||||||
|
|
||||||
{
|
{
|
||||||
const char* r1[] = {TR("settings_import_key"), TR("settings_export_key"), TR("settings_export_all"), TR("settings_backup"), TR("settings_export_csv")};
|
const char* r1[] = {TR("settings_import_key"), TR("settings_export_key"), TR("settings_export_all"), TR("settings_backup"), TR("settings_export_csv")};
|
||||||
const char* t1[] = {
|
const char* t1[] = {
|
||||||
@@ -1165,13 +1213,12 @@ void RenderSettingsPage(App* app) {
|
|||||||
float sp = Layout::spacingSm();
|
float sp = Layout::spacingSm();
|
||||||
ImFont* btnFont = S.resolveFont("button");
|
ImFont* btnFont = S.resolveFont("button");
|
||||||
|
|
||||||
// Measure natural widths
|
|
||||||
float btnPadX = btnPad * 2;
|
float btnPadX = btnPad * 2;
|
||||||
float naturalW = 0;
|
float naturalW = 0;
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
naturalW += ImGui::CalcTextSize(r1[i]).x + btnPadX;
|
naturalW += ImGui::CalcTextSize(r1[i]).x + btnPadX;
|
||||||
float wizW = ImGui::CalcTextSize(wizLabel).x + btnPadX;
|
float wizW = ImGui::CalcTextSize(wizLabel).x + btnPadX;
|
||||||
float totalW = naturalW + wizW + sp * 6; // 5 gaps between data btns + 1 gap before wizard
|
float totalW = naturalW + wizW + sp * 6;
|
||||||
|
|
||||||
float scale = (totalW > contentW) ? contentW / totalW : 1.0f;
|
float scale = (totalW > contentW) ? contentW / totalW : 1.0f;
|
||||||
if (scale < 1.0f) ImGui::SetWindowFontScale(scale);
|
if (scale < 1.0f) ImGui::SetWindowFontScale(scale);
|
||||||
|
|||||||
@@ -174,6 +174,9 @@ void I18n::loadBuiltinEnglish()
|
|||||||
|
|
||||||
// Settings sections
|
// Settings sections
|
||||||
strings_["appearance"] = "APPEARANCE";
|
strings_["appearance"] = "APPEARANCE";
|
||||||
|
strings_["theme_language"] = "THEME & LANGUAGE";
|
||||||
|
strings_["advanced_effects"] = "Advanced Effects...";
|
||||||
|
strings_["tools_actions"] = "Tools & Actions...";
|
||||||
strings_["wallet"] = "WALLET";
|
strings_["wallet"] = "WALLET";
|
||||||
strings_["node_security"] = "NODE & SECURITY";
|
strings_["node_security"] = "NODE & SECURITY";
|
||||||
strings_["node"] = "NODE";
|
strings_["node"] = "NODE";
|
||||||
|
|||||||
Reference in New Issue
Block a user