fix(ui): finish HiDPI pass — cosmetic ×dpiScale, narrow-width reflow, recent-list reserves
The tail of the DPI/font-scale/responsiveness audit — ~26 remaining findings.
Container / recent-list (Theme-1 leftovers):
- Send: drop NoScrollbar|NoScrollWithMouse on ##SendFormScroll so Recent Sends is
reachable at font_scale 1.5 (parity with receive).
- Receive: cap the QR/form card via std::min(mainCardTargetH, availH - recentReserve)
so RECENT RECEIVED stays on-screen (identity at 1.0x).
- Wallets dialog: size the capped-mode list to whole rows so it no longer clips a
partial row / crowds "Create a new wallet".
Narrow-width (1024px) reflow:
- Console toolbar reserves space for ALL trailing controls (both icon toggles + zoom
buttons) so the +/- zoom no longer runs off-window.
- History sort combo sized to its measured widest localized label ("Newest first").
- Settings Theme/Layout/Language row: scale the wide→stacked breakpoint by dpiScale so
it drops to full-width stacked combos at 1.5x (Consolidated Card no longer clips).
- Recent-tx type label: derive the address column X from the measured label width so it
can't collide at narrow widths.
- Mining Recent Pool Payouts: floor the panel height to fit the empty-state caption.
Cosmetic ×dpiScale() on absolute geometry (no-ops at 1.0x): mining SOLO|POOL toggle &
idle combos, market pair-chips, password/PIN strength bars, receive/send currency
toggles, explorer search bar/rows/rounding, About-card logo, chat empty-state wrap,
recent-list address/time offsets, address-toolbar & two-row action buttons, console
line-gap/status-dot/pane rounding.
Verified at font_scale 1.5 and at 1024px across full-node + Lite + Windows (ctest
green) and an adversarial diff review (one over-reserve regression fixed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1265,7 +1265,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
else if (tier == 1) { strengthLabel = TR("wiz_strength_fair"); strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; }
|
else if (tier == 1) { strengthLabel = TR("wiz_strength_fair"); strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; }
|
||||||
|
|
||||||
float barW = ImGui::GetContentRegionAvail().x;
|
float barW = ImGui::GetContentRegionAvail().x;
|
||||||
float barH = 4.0f;
|
float barH = 4.0f * ui::Layout::dpiScale();
|
||||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
||||||
@@ -1315,7 +1315,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
// Indeterminate progress bar
|
// Indeterminate progress bar
|
||||||
{
|
{
|
||||||
float barW = ImGui::GetContentRegionAvail().x;
|
float barW = ImGui::GetContentRegionAvail().x;
|
||||||
float barH = 6.0f;
|
float barH = 6.0f * ui::Layout::dpiScale();
|
||||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
||||||
@@ -1815,7 +1815,7 @@ void App::renderDecryptWalletDialog() {
|
|||||||
// Indeterminate progress bar
|
// Indeterminate progress bar
|
||||||
{
|
{
|
||||||
float barW = ImGui::GetContentRegionAvail().x;
|
float barW = ImGui::GetContentRegionAvail().x;
|
||||||
float barH = 6.0f;
|
float barH = 6.0f * ui::Layout::dpiScale();
|
||||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
dl->AddRectFilled(p, ImVec2(p.x + barW, p.y + barH),
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ void RenderSettingsPage(App* app) {
|
|||||||
float contentW = availWidth - pad * 2;
|
float contentW = availWidth - pad * 2;
|
||||||
float comboGap = S.drawElement("components.settings-page", "combo-row-gap").size;
|
float comboGap = S.drawElement("components.settings-page", "combo-row-gap").size;
|
||||||
float compactBP = S.drawElement("components.settings-page", "compact-breakpoint").size;
|
float compactBP = S.drawElement("components.settings-page", "compact-breakpoint").size;
|
||||||
bool wideLayout = availWidth >= compactBP;
|
bool wideLayout = availWidth >= compactBP * dp; // scale the breakpoint so the 3-combo row drops to the stacked layout at high font scale (else the combos clip)
|
||||||
float refreshBtnW = S.drawElement("components.settings-page", "refresh-btn-width").size;
|
float refreshBtnW = S.drawElement("components.settings-page", "refresh-btn-width").size;
|
||||||
|
|
||||||
// --- Skin data ---
|
// --- Skin data ---
|
||||||
@@ -762,6 +762,8 @@ void RenderSettingsPage(App* app) {
|
|||||||
float lblThemeW = ImGui::CalcTextSize(TR("theme")).x + lblGap;
|
float lblThemeW = ImGui::CalcTextSize(TR("theme")).x + lblGap;
|
||||||
float lblLayoutW = ImGui::CalcTextSize(TR("balance_layout")).x + lblGap;
|
float lblLayoutW = ImGui::CalcTextSize(TR("balance_layout")).x + lblGap;
|
||||||
float lblLangW = ImGui::CalcTextSize(TR("language")).x + lblGap;
|
float lblLangW = ImGui::CalcTextSize(TR("language")).x + lblGap;
|
||||||
|
// Budget matches the RAW draws below (SameLine(0, comboGap) and ImVec2(refreshBtnW, 0)) —
|
||||||
|
// don't dpi-scale these terms or the budget over-reserves and the combos shrink needlessly.
|
||||||
float totalFixed = lblThemeW + lblLayoutW + lblLangW
|
float totalFixed = lblThemeW + lblLayoutW + lblLangW
|
||||||
+ comboGap * 2 + Layout::spacingSm() + refreshBtnW;
|
+ comboGap * 2 + Layout::spacingSm() + refreshBtnW;
|
||||||
float comboW = std::max(80.0f, (contentW - totalFixed) / 3.0f);
|
float comboW = std::max(80.0f, (contentW - totalFixed) / 3.0f);
|
||||||
@@ -2533,7 +2535,7 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImVec2 logoPos = ImGui::GetCursorScreenPos();
|
ImVec2 logoPos = ImGui::GetCursorScreenPos();
|
||||||
float logoAspect = (app->getLogoHeight() > 0)
|
float logoAspect = (app->getLogoHeight() > 0)
|
||||||
? (float)app->getLogoWidth() / (float)app->getLogoHeight() : 1.0f;
|
? (float)app->getLogoWidth() / (float)app->getLogoHeight() : 1.0f;
|
||||||
float logoReserveH = schema::UI().drawElement("components.settings-page", "about-logo-size").sizeOr(150.0f);
|
float logoReserveH = schema::UI().drawElement("components.settings-page", "about-logo-size").sizeOr(150.0f) * dp;
|
||||||
if (logoTex != 0) {
|
if (logoTex != 0) {
|
||||||
logoAreaW = logoReserveH * logoAspect + Layout::spacingLg();
|
logoAreaW = logoReserveH * logoAspect + Layout::spacingLg();
|
||||||
ImGui::Indent(logoAreaW);
|
ImGui::Indent(logoAreaW);
|
||||||
|
|||||||
@@ -188,8 +188,8 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float buttonWidth = (addrBtn.width > 0) ? addrBtn.width : 140.0f;
|
float buttonWidth = ((addrBtn.width > 0) ? addrBtn.width : 140.0f) * dp;
|
||||||
float spacing = (addrBtn.gap > 0) ? addrBtn.gap : 8.0f;
|
float spacing = ((addrBtn.gap > 0) ? addrBtn.gap : 8.0f) * dp;
|
||||||
float totalButtonsWidth = buttonWidth * 2 + spacing;
|
float totalButtonsWidth = buttonWidth * 2 + spacing;
|
||||||
float kMinButtonsPosition = std::max(S.drawElement("tabs.balance", "min-buttons-position").size,
|
float kMinButtonsPosition = std::max(S.drawElement("tabs.balance", "min-buttons-position").size,
|
||||||
S.drawElement("tabs.balance", "buttons-position").size * hs);
|
S.drawElement("tabs.balance", "buttons-position").size * hs);
|
||||||
@@ -842,7 +842,11 @@ void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float
|
|||||||
dl->AddText(capFont, capFont->LegacySize,
|
dl->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(tx_x, rowPos.y + 2 * dp), OnSurfaceMedium(), display.typeText.c_str());
|
ImVec2(tx_x, rowPos.y + 2 * dp), OnSurfaceMedium(), display.typeText.c_str());
|
||||||
|
|
||||||
float addrX = tx_x + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f) * hs;
|
// Start the address column past the MEASURED type-label width (plus a fixed gap) so it can
|
||||||
|
// never overlap the label — a fixed schema offset shrinks below the label width at narrow
|
||||||
|
// widths (hs < 1) and collides. Mirrors how amtX/agoSz measure their own text below.
|
||||||
|
ImVec2 typeSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, display.typeText.c_str());
|
||||||
|
float addrX = tx_x + typeSz.x + Layout::spacingMd();
|
||||||
dl->AddText(capFont, capFont->LegacySize,
|
dl->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(addrX, rowPos.y + 2 * dp), OnSurfaceDisabled(), display.addressText.c_str());
|
ImVec2(addrX, rowPos.y + 2 * dp), OnSurfaceDisabled(), display.addressText.c_str());
|
||||||
|
|
||||||
@@ -857,7 +861,7 @@ void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float
|
|||||||
|
|
||||||
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, display.timeText.c_str());
|
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, display.timeText.c_str());
|
||||||
dl->AddText(capFont, capFont->LegacySize,
|
dl->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f), rowPos.y + 2 * dp),
|
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f) * hs, rowPos.y + 2 * dp),
|
||||||
OnSurfaceDisabled(), display.timeText.c_str());
|
OnSurfaceDisabled(), display.timeText.c_str());
|
||||||
|
|
||||||
float rowW = ImGui::GetContentRegionAvail().x;
|
float rowW = ImGui::GetContentRegionAvail().x;
|
||||||
|
|||||||
@@ -1939,7 +1939,7 @@ static void RenderBalanceTwoRow(App* app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Action buttons right-aligned
|
// Action buttons right-aligned
|
||||||
float btnW = S.drawElement("tabs.balance.two-row", "action-btn-width").sizeOr(80.0f);
|
float btnW = S.drawElement("tabs.balance.two-row", "action-btn-width").sizeOr(80.0f) * dp;
|
||||||
float rightEdge = ImGui::GetWindowWidth() - Layout::spacingLg();
|
float rightEdge = ImGui::GetWindowWidth() - Layout::spacingLg();
|
||||||
ImGui::SameLine(rightEdge - btnW * 2 - Layout::spacingSm());
|
ImGui::SameLine(rightEdge - btnW * 2 - Layout::spacingSm());
|
||||||
// Stable ## ids keep the button identity fixed across translations.
|
// Stable ## ids keep the button identity fixed across translations.
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ void centeredEmptyState(const char* icon, const char* title, const char* hint) {
|
|||||||
ImFont* titleF = material::Type().subtitle1();
|
ImFont* titleF = material::Type().subtitle1();
|
||||||
ImFont* hintF = material::Type().body2();
|
ImFont* hintF = material::Type().body2();
|
||||||
const float gap = 8.0f * Layout::dpiScale();
|
const float gap = 8.0f * Layout::dpiScale();
|
||||||
const float wrap = std::min(avail.x - 40.0f, 360.0f);
|
const float wrap = std::min(avail.x - 40.0f * Layout::dpiScale(), 360.0f * Layout::dpiScale());
|
||||||
const float iconSz = iconF ? scaledSize(iconF) : 40.0f;
|
const float iconSz = iconF ? scaledSize(iconF) : 40.0f;
|
||||||
const float iconH = iconF ? iconF->CalcTextSizeA(iconSz, FLT_MAX, 0.0f, icon).y : 0.0f;
|
const float iconH = iconF ? iconF->CalcTextSizeA(iconSz, FLT_MAX, 0.0f, icon).y : 0.0f;
|
||||||
const float titleH = titleF->CalcTextSizeA(scaledSize(titleF), FLT_MAX, 0.0f, title).y;
|
const float titleH = titleF->CalcTextSizeA(scaledSize(titleF), FLT_MAX, 0.0f, title).y;
|
||||||
|
|||||||
@@ -617,7 +617,7 @@ void ConsoleTab::drawToolbarStatus(ConsoleCommandExecutor& exec)
|
|||||||
ConsoleStatusLine st = exec.toolbarStatus();
|
ConsoleStatusLine st = exec.toolbarStatus();
|
||||||
if (!st.text.empty()) {
|
if (!st.text.empty()) {
|
||||||
ImVec2 cp = ImGui::GetCursorScreenPos();
|
ImVec2 cp = ImGui::GetCursorScreenPos();
|
||||||
float dotR = schema::UI().drawElement("tabs.console", "status-dot-radius-base").size + schema::UI().drawElement("tabs.console", "status-dot-radius-scale").size * Layout::hScale();
|
float dotR = (schema::UI().drawElement("tabs.console", "status-dot-radius-base").size + schema::UI().drawElement("tabs.console", "status-dot-radius-scale").size) * Layout::hScale();
|
||||||
float dotY = cp.y + ImGui::GetTextLineHeight() * 0.5f;
|
float dotY = cp.y + ImGui::GetTextLineHeight() * 0.5f;
|
||||||
float dotX = cp.x + dotR + 2.0f * Layout::dpiScale();
|
float dotX = cp.x + dotR + 2.0f * Layout::dpiScale();
|
||||||
|
|
||||||
@@ -687,9 +687,14 @@ void ConsoleTab::drawLogFilterToggles(const ConsoleLogFilterCaps& caps)
|
|||||||
void ConsoleTab::drawFilterInput()
|
void ConsoleTab::drawFilterInput()
|
||||||
{
|
{
|
||||||
using namespace material;
|
using namespace material;
|
||||||
float zoomBtnSpace = ImGui::GetFrameHeight() * 2.0f + Layout::spacingSm() * 3.0f;
|
// Reserve room for EVERY trailing same-line control drawn AFTER this filter on the toolbar
|
||||||
float filterAvail = ImGui::GetContentRegionAvail().x - zoomBtnSpace;
|
// row: the two icon toggles (accent-fill + text-color) and the two zoom buttons, plus the
|
||||||
float filterW = std::min(schema::UI().drawElement("tabs.console", "filter-max-width").size, filterAvail * schema::UI().drawElement("tabs.console", "filter-width-ratio").size);
|
// group spacers between them. Otherwise the filter eats the row and the trailing controls
|
||||||
|
// run off-window (worst at 1024px / font_scale 1.5). All four are GetFrameHeight() wide.
|
||||||
|
float trailingBtnSpace = ImGui::GetFrameHeight() * 4.0f + Layout::spacingSm() * 7.0f;
|
||||||
|
float filterAvail = ImGui::GetContentRegionAvail().x - trailingBtnSpace;
|
||||||
|
float filterMaxW = schema::UI().drawElement("tabs.console", "filter-max-width").size * Layout::dpiScale();
|
||||||
|
float filterW = std::min(filterMaxW, filterAvail * schema::UI().drawElement("tabs.console", "filter-width-ratio").size);
|
||||||
ImGui::SetNextItemWidth(filterW);
|
ImGui::SetNextItemWidth(filterW);
|
||||||
ImGui::InputTextWithHint("##ConsoleFilter", TR("console_filter_hint"), filter_text_, sizeof(filter_text_));
|
ImGui::InputTextWithHint("##ConsoleFilter", TR("console_filter_hint"), filter_text_, sizeof(filter_text_));
|
||||||
if (filter_text_[0] != '\0') {
|
if (filter_text_[0] != '\0') {
|
||||||
@@ -768,7 +773,10 @@ void ConsoleTab::renderOutput()
|
|||||||
// height. The inter-line gap is added explicitly to layout_.heights
|
// height. The inter-line gap is added explicitly to layout_.heights
|
||||||
// so that layout_.cumulativeY stays perfectly in sync with actual
|
// so that layout_.cumulativeY stays perfectly in sync with actual
|
||||||
// cursor positions (avoiding selection-offset drift).
|
// cursor positions (avoiding selection-offset drift).
|
||||||
float interLineGap = S.drawElement("tabs.console", "output").getFloat("line-spacing", 0.0f);
|
// Raw logical px from the schema; scale it so the inter-line gap grows at font_scale 1.5
|
||||||
|
// (it is added to the already-DPI-scaled GetTextLineHeight in BuildConsoleLayout — scale the
|
||||||
|
// gap only, never the line height).
|
||||||
|
float interLineGap = S.drawElement("tabs.console", "output").getFloat("line-spacing", 0.0f) * Layout::dpiScale();
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
|
|
||||||
// Inner padding for glass panel
|
// Inner padding for glass panel
|
||||||
@@ -1894,7 +1902,7 @@ void ConsoleTab::renderCommandsPopup(ConsoleCommandExecutor& exec)
|
|||||||
|
|
||||||
// Both panes sit on soft Material glass surfaces (no hard 1px child border) with inner padding.
|
// Both panes sit on soft Material glass surfaces (no hard 1px child border) with inner padding.
|
||||||
GlassPanelSpec paneGlass;
|
GlassPanelSpec paneGlass;
|
||||||
paneGlass.rounding = 14.0f;
|
paneGlass.rounding = 14.0f * dp;
|
||||||
paneGlass.fillAlpha = 30;
|
paneGlass.fillAlpha = 30;
|
||||||
paneGlass.borderAlpha = 30;
|
paneGlass.borderAlpha = 30;
|
||||||
|
|
||||||
|
|||||||
@@ -446,10 +446,10 @@ static void renderSearchBar(App* app, float availWidth) {
|
|||||||
float navW = navBtnSz * 2.0f + pageW + navGap * 2.0f;
|
float navW = navBtnSz * 2.0f + pageW + navGap * 2.0f;
|
||||||
|
|
||||||
float inputW = std::min(
|
float inputW = std::min(
|
||||||
S.drawElement("tabs.explorer", "search-input-width").size,
|
S.drawElement("tabs.explorer", "search-input-width").size * Layout::dpiScale(),
|
||||||
availWidth * 0.65f);
|
availWidth * 0.65f);
|
||||||
float btnW = S.drawElement("tabs.explorer", "search-button-width").size;
|
float btnW = S.drawElement("tabs.explorer", "search-button-width").size * Layout::dpiScale();
|
||||||
float barH = S.drawElement("tabs.explorer", "search-bar-height").size;
|
float barH = S.drawElement("tabs.explorer", "search-bar-height").size * Layout::dpiScale();
|
||||||
|
|
||||||
// Clamp so search bar never overflows
|
// Clamp so search bar never overflows
|
||||||
float maxInputW = availWidth - btnW - navW - pad * 4 - Type().iconMed()->LegacySize;
|
float maxInputW = availWidth - btnW - navW - pad * 4 - Type().iconMed()->LegacySize;
|
||||||
@@ -755,8 +755,8 @@ static void renderRecentBlocks(App* app, float availWidth) {
|
|||||||
ImFont* body2 = Type().body2();
|
ImFont* body2 = Type().body2();
|
||||||
ImFont* sub1 = Type().subtitle1();
|
ImFont* sub1 = Type().subtitle1();
|
||||||
|
|
||||||
float baseRowH = S.drawElement("tabs.explorer", "row-height").size;
|
float baseRowH = S.drawElement("tabs.explorer", "row-height").size * dp;
|
||||||
float rowRound = S.drawElement("tabs.explorer", "row-rounding").size;
|
float rowRound = S.drawElement("tabs.explorer", "row-rounding").size * dp;
|
||||||
float headerH = ovFont->LegacySize + Layout::spacingSm() + pad * 0.5f;
|
float headerH = ovFont->LegacySize + Layout::spacingSm() + pad * 0.5f;
|
||||||
|
|
||||||
// Stretch card to fill the remaining tab height; rows scroll inside.
|
// Stretch card to fill the remaining tab height; rows scroll inside.
|
||||||
@@ -989,7 +989,7 @@ static void renderRecentBlocks(App* app, float availWidth) {
|
|||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
float fadeZone = S.drawElement("tabs.explorer", "scroll-fade-zone").size;
|
float fadeZone = S.drawElement("tabs.explorer", "scroll-fade-zone").size * dp;
|
||||||
ApplyScrollEdgeMask(dl, parentVtx, childDL, childVtx,
|
ApplyScrollEdgeMask(dl, parentVtx, childDL, childVtx,
|
||||||
rowAreaTop, rowAreaTop + rowAreaH, fadeZone, scrollY, scrollMaxY);
|
rowAreaTop, rowAreaTop + rowAreaH, fadeZone, scrollY, scrollMaxY);
|
||||||
|
|
||||||
@@ -1179,7 +1179,7 @@ static void renderBlockDetailModal(App* app) {
|
|||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
float txRowH = S.drawElement("tabs.explorer", "tx-row-height").size;
|
float txRowH = S.drawElement("tabs.explorer", "tx-row-height").size * dp;
|
||||||
ImU32 linkCol = schema::UI().resolveColor("var(--secondary-light)");
|
ImU32 linkCol = schema::UI().resolveColor("var(--secondary-light)");
|
||||||
|
|
||||||
for (int i = 0; i < (int)s_detail_txids.size(); i++) {
|
for (int i = 0; i < (int)s_detail_txids.size(); i++) {
|
||||||
|
|||||||
@@ -1347,9 +1347,10 @@ static void mktDrawPairSelector(App* app, const std::vector<data::ExchangeInfo>&
|
|||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, S.drawElement("tabs.market", "exchange-top-gap").size));
|
ImGui::Dummy(ImVec2(0, S.drawElement("tabs.market", "exchange-top-gap").size));
|
||||||
{
|
{
|
||||||
float chipH = S.drawElement("tabs.market", "pair-chip-height").height;
|
float dp = Layout::dpiScale();
|
||||||
float chipR = S.drawElement("tabs.market", "pair-chip-radius").radius;
|
float chipH = S.drawElement("tabs.market", "pair-chip-height").height * dp;
|
||||||
float chipSpacing = S.drawElement("tabs.market", "pair-chip-spacing").size;
|
float chipR = S.drawElement("tabs.market", "pair-chip-radius").radius * dp;
|
||||||
|
float chipSpacing = S.drawElement("tabs.market", "pair-chip-spacing").size * dp;
|
||||||
float innerGap = Layout::spacingSm();
|
float innerGap = Layout::spacingSm();
|
||||||
float sidePad = Layout::spacingMd();
|
float sidePad = Layout::spacingMd();
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
|||||||
for (const auto& d : delays) {
|
for (const auto& d : delays) {
|
||||||
if (d.seconds == curDelay) { previewLabel = d.label; break; }
|
if (d.seconds == curDelay) { previewLabel = d.label; break; }
|
||||||
}
|
}
|
||||||
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
|
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f) * dp;
|
||||||
float comboX = idleRightEdge - comboW;
|
float comboX = idleRightEdge - comboW;
|
||||||
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
||||||
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
||||||
@@ -332,7 +332,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
|||||||
if (curVal <= 0) curVal = hwThreads;
|
if (curVal <= 0) curVal = hwThreads;
|
||||||
char previewBuf[16];
|
char previewBuf[16];
|
||||||
snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
|
snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
|
||||||
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
|
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f) * dp;
|
||||||
float comboX = idleRightEdge - comboW;
|
float comboX = idleRightEdge - comboW;
|
||||||
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
||||||
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
||||||
@@ -371,7 +371,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
|||||||
if (curVal <= 0) curVal = std::max(1, hwThreads / 2);
|
if (curVal <= 0) curVal = std::max(1, hwThreads / 2);
|
||||||
char previewBuf[16];
|
char previewBuf[16];
|
||||||
snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
|
snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
|
||||||
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
|
float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f) * dp;
|
||||||
float comboX = idleRightEdge - comboW;
|
float comboX = idleRightEdge - comboW;
|
||||||
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
|
||||||
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
|
||||||
|
|||||||
@@ -533,7 +533,18 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
|||||||
float recentAvailH = ImGui::GetContentRegionAvail().y - sHdr - gapOver;
|
float recentAvailH = ImGui::GetContentRegionAvail().y - sHdr - gapOver;
|
||||||
float minRows = recentMined.empty() ? 2.0f : (float)recentMined.size();
|
float minRows = recentMined.empty() ? 2.0f : (float)recentMined.size();
|
||||||
float contentH_blocks = rowH_blocks * minRows + pad * 2.5f;
|
float contentH_blocks = rowH_blocks * minRows + pad * 2.5f;
|
||||||
float recentH = std::clamp(contentH_blocks, 30.0f * dp, std::max(30.0f * dp, recentAvailH));
|
// Lower bound for the panel height. When the list is empty the centred empty-state
|
||||||
|
// (top pad + icon + gap + caption + bottom pad) needs more vertical room than the bare
|
||||||
|
// 30*dp floor, otherwise the caption is clipped once the thread-tile grid wraps to two
|
||||||
|
// rows and recentAvailH shrinks (font metrics/Layout helpers are already DPI-scaled — do
|
||||||
|
// not multiply them by dp; pad is a scaled param).
|
||||||
|
float recentMinH = 30.0f * dp;
|
||||||
|
if (recentMined.empty()) {
|
||||||
|
recentMinH = std::max(recentMinH,
|
||||||
|
pad * 0.5f + Type().iconMed()->LegacySize + Layout::spacingXs()
|
||||||
|
+ capFont->LegacySize + pad * 0.5f);
|
||||||
|
}
|
||||||
|
float recentH = std::clamp(contentH_blocks, recentMinH, std::max(recentMinH, recentAvailH));
|
||||||
|
|
||||||
// Glass panel wrapping the list + scroll-edge mask state
|
// Glass panel wrapping the list + scroll-edge mask state
|
||||||
ImVec2 recentPanelMin = ImGui::GetCursorScreenPos();
|
ImVec2 recentPanelMin = ImGui::GetCursorScreenPos();
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
|||||||
|
|
||||||
const bool soloMiningAvailable = app->supportsSoloMining();
|
const bool soloMiningAvailable = app->supportsSoloMining();
|
||||||
float toggleW = schema::UI().drawElement("tabs.mining", "mode-toggle-width").size * hs;
|
float toggleW = schema::UI().drawElement("tabs.mining", "mode-toggle-width").size * hs;
|
||||||
float toggleH = schema::UI().drawElement("tabs.mining", "mode-toggle-height").size;
|
float toggleH = schema::UI().drawElement("tabs.mining", "mode-toggle-height").size * hs;
|
||||||
float toggleRnd = schema::UI().drawElement("tabs.mining", "mode-toggle-rounding").size;
|
float toggleRnd = schema::UI().drawElement("tabs.mining", "mode-toggle-rounding").size * hs;
|
||||||
float totalW = soloMiningAvailable ? (toggleW * 2) : toggleW;
|
float totalW = soloMiningAvailable ? (toggleW * 2) : toggleW;
|
||||||
|
|
||||||
ImVec2 tMin = ImGui::GetCursorScreenPos();
|
ImVec2 tMin = ImGui::GetCursorScreenPos();
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
|
|||||||
ImVec2(txX, rowPos.y + 2.0f * dp), OnSurfaceMedium(), typeText);
|
ImVec2(txX, rowPos.y + 2.0f * dp), OnSurfaceMedium(), typeText);
|
||||||
|
|
||||||
// Address (second line)
|
// Address (second line)
|
||||||
float addrX = txX + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f);
|
float addrX = txX + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f) * hs;
|
||||||
std::string addrDisplay = util::truncateMiddle(tx.address,
|
std::string addrDisplay = util::truncateMiddle(tx.address,
|
||||||
(int)S.drawElement("tabs.balance", "recent-tx-addr-trunc").sizeOr(20.0f));
|
(int)S.drawElement("tabs.balance", "recent-tx-addr-trunc").sizeOr(20.0f));
|
||||||
rowDL->AddText(capFont, capFont->LegacySize,
|
rowDL->AddText(capFont, capFont->LegacySize,
|
||||||
@@ -424,7 +424,7 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
|
|||||||
std::string ago = recvTimeAgo(tx.timestamp);
|
std::string ago = recvTimeAgo(tx.timestamp);
|
||||||
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
|
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
|
||||||
rowDL->AddText(capFont, capFont->LegacySize,
|
rowDL->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f),
|
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f) * hs,
|
||||||
rowPos.y + 2.0f * dp),
|
rowPos.y + 2.0f * dp),
|
||||||
OnSurfaceDisabled(), ago.c_str());
|
OnSurfaceDisabled(), ago.c_str());
|
||||||
|
|
||||||
@@ -488,6 +488,16 @@ void RenderReceiveTab(App* app)
|
|||||||
float groupStartY = ImGui::GetCursorPosY();
|
float groupStartY = ImGui::GetCursorPosY();
|
||||||
float contentStartY = ImGui::GetCursorPosY();
|
float contentStartY = ImGui::GetCursorPosY();
|
||||||
|
|
||||||
|
// Reserve a slice of the available height for RECENT RECEIVED (ratio — mirrors
|
||||||
|
// balance_tab's recent-tx-reserve). The main card's target height is capped so it can
|
||||||
|
// never grow past (available - reserve): a no-op at 1.0x (mainCardTargetH already fits
|
||||||
|
// well within scrollAvailH there), but at HiDPI it prevents the card — whose QR (280*dp)
|
||||||
|
// and pads scale with dp while scrollAvailH is physical px — from eating the whole child
|
||||||
|
// and evicting the list.
|
||||||
|
float recvReserveRatio = S.drawElement("tabs.balance", "recent-tx-reserve-ratio").sizeOr(0.18f);
|
||||||
|
float recvRecentReserve = std::max(0.0f, scrollAvailH) * recvReserveRatio;
|
||||||
|
float recvCardCapH = std::max(0.0f, scrollAvailH - recvRecentReserve);
|
||||||
|
|
||||||
float formAvailW = ImGui::GetContentRegionAvail().x;
|
float formAvailW = ImGui::GetContentRegionAvail().x;
|
||||||
float formW = formAvailW;
|
float formW = formAvailW;
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
@@ -603,7 +613,7 @@ void RenderReceiveTab(App* app)
|
|||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
// Amount input with currency toggle
|
// Amount input with currency toggle
|
||||||
float toggleW = S.drawElement("tabs.receive", "currency-toggle-width").size;
|
float toggleW = S.drawElement("tabs.receive", "currency-toggle-width").size * Layout::dpiScale();
|
||||||
float amtInputW = addrColW - toggleW - Layout::spacingMd();
|
float amtInputW = addrColW - toggleW - Layout::spacingMd();
|
||||||
if (amtInputW < S.drawElement("tabs.receive", "amount-input-min-width").size) amtInputW = S.drawElement("tabs.receive", "amount-input-min-width").size;
|
if (amtInputW < S.drawElement("tabs.receive", "amount-input-min-width").size) amtInputW = S.drawElement("tabs.receive", "amount-input-min-width").size;
|
||||||
double usd_price = state.market.price_usd;
|
double usd_price = state.market.price_usd;
|
||||||
@@ -667,8 +677,8 @@ void RenderReceiveTab(App* app)
|
|||||||
float bH = bMax.y - bMin.y;
|
float bH = bMax.y - bMin.y;
|
||||||
ImFont* font = ImGui::GetFont();
|
ImFont* font = ImGui::GetFont();
|
||||||
ImVec2 textSz = font->CalcTextSizeA(font->LegacySize, 10000, 0, currLabel);
|
ImVec2 textSz = font->CalcTextSizeA(font->LegacySize, 10000, 0, currLabel);
|
||||||
float iconW = schema::UI().drawElement("tabs.receive", "currency-icon-width").size;
|
float iconW = schema::UI().drawElement("tabs.receive", "currency-icon-width").size * Layout::dpiScale();
|
||||||
float iconGap2 = schema::UI().drawElement("tabs.receive", "currency-icon-gap").size;
|
float iconGap2 = schema::UI().drawElement("tabs.receive", "currency-icon-gap").size * Layout::dpiScale();
|
||||||
float totalW2 = iconW + iconGap2 + textSz.x;
|
float totalW2 = iconW + iconGap2 + textSz.x;
|
||||||
float startX = bMin.x + ((bMax.x - bMin.x) - totalW2) * 0.5f;
|
float startX = bMin.x + ((bMax.x - bMin.x) - totalW2) * 0.5f;
|
||||||
float cy = bMin.y + bH * 0.5f;
|
float cy = bMin.y + bH * 0.5f;
|
||||||
@@ -866,7 +876,7 @@ void RenderReceiveTab(App* app)
|
|||||||
S.drawElement("tabs.receive", "action-btn-height").size * vScale);
|
S.drawElement("tabs.receive", "action-btn-height").size * vScale);
|
||||||
float footerH = innerGap + actionBtnH + pad;
|
float footerH = innerGap + actionBtnH + pad;
|
||||||
float currentCardH = ImGui::GetCursorScreenPos().y - containerMin.y;
|
float currentCardH = ImGui::GetCursorScreenPos().y - containerMin.y;
|
||||||
float targetCardH = Layout::mainCardTargetH(formW, vScale);
|
float targetCardH = std::min(Layout::mainCardTargetH(formW, vScale), recvCardCapH);
|
||||||
float footerTopH = targetCardH - footerH;
|
float footerTopH = targetCardH - footerH;
|
||||||
if (currentCardH < footerTopH) {
|
if (currentCardH < footerTopH) {
|
||||||
ImGui::Dummy(ImVec2(0, footerTopH - currentCardH));
|
ImGui::Dummy(ImVec2(0, footerTopH - currentCardH));
|
||||||
@@ -959,10 +969,11 @@ void RenderReceiveTab(App* app)
|
|||||||
ImGui::Dummy(ImVec2(0, pad));
|
ImGui::Dummy(ImVec2(0, pad));
|
||||||
ImGui::Unindent(pad);
|
ImGui::Unindent(pad);
|
||||||
|
|
||||||
// Enforce shared card height (matches QR-driven target)
|
// Enforce shared card height (matches QR-driven target), capped so the reserved
|
||||||
|
// RECENT RECEIVED slice below stays on-screen at HiDPI.
|
||||||
{
|
{
|
||||||
float currentCardH = ImGui::GetCursorScreenPos().y - containerMin.y;
|
float currentCardH = ImGui::GetCursorScreenPos().y - containerMin.y;
|
||||||
float targetCardH = Layout::mainCardTargetH(formW, vScale);
|
float targetCardH = std::min(Layout::mainCardTargetH(formW, vScale), recvCardCapH);
|
||||||
if (currentCardH < targetCardH)
|
if (currentCardH < targetCardH)
|
||||||
ImGui::Dummy(ImVec2(0, targetCardH - currentCardH));
|
ImGui::Dummy(ImVec2(0, targetCardH - currentCardH));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1154,7 +1154,7 @@ static void RenderRecentSends(const WalletState& state, float width, ImFont* cap
|
|||||||
ImVec2(txX, rowPos.y + 2.0f * dp), OnSurfaceMedium(), TR("sent_type"));
|
ImVec2(txX, rowPos.y + 2.0f * dp), OnSurfaceMedium(), TR("sent_type"));
|
||||||
|
|
||||||
// Address (second line)
|
// Address (second line)
|
||||||
float addrX = txX + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f);
|
float addrX = txX + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f) * hs;
|
||||||
std::string addrDisplay = util::truncateMiddle(tx.address,
|
std::string addrDisplay = util::truncateMiddle(tx.address,
|
||||||
(int)S.drawElement("tabs.balance", "recent-tx-addr-trunc").sizeOr(20.0f));
|
(int)S.drawElement("tabs.balance", "recent-tx-addr-trunc").sizeOr(20.0f));
|
||||||
rowDL->AddText(capFont, capFont->LegacySize,
|
rowDL->AddText(capFont, capFont->LegacySize,
|
||||||
@@ -1173,7 +1173,7 @@ static void RenderRecentSends(const WalletState& state, float width, ImFont* cap
|
|||||||
std::string ago = timeAgo(tx.timestamp);
|
std::string ago = timeAgo(tx.timestamp);
|
||||||
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
|
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
|
||||||
rowDL->AddText(capFont, capFont->LegacySize,
|
rowDL->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f),
|
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f) * hs,
|
||||||
rowPos.y + 2.0f * dp),
|
rowPos.y + 2.0f * dp),
|
||||||
OnSurfaceDisabled(), ago.c_str());
|
OnSurfaceDisabled(), ago.c_str());
|
||||||
|
|
||||||
@@ -1251,8 +1251,12 @@ void RenderSendTab(App* app)
|
|||||||
// SCROLLABLE CONTENT
|
// SCROLLABLE CONTENT
|
||||||
// ================================================================
|
// ================================================================
|
||||||
ImVec2 formAvail = ImGui::GetContentRegionAvail();
|
ImVec2 formAvail = ImGui::GetContentRegionAvail();
|
||||||
|
// NOTE: no NoScrollbar/NoScrollWithMouse here (mirrors receive_tab's ##ReceiveScroll).
|
||||||
|
// At font_scale 1.5 the form card grows to mainCardTargetH and pushes the appended
|
||||||
|
// "Recent Sends" list below the fold; letting this child scroll keeps it reachable.
|
||||||
|
// No-op at 1.0x where the content already fits (no scrollbar appears).
|
||||||
ImGui::BeginChild("##SendFormScroll", formAvail, false,
|
ImGui::BeginChild("##SendFormScroll", formAvail, false,
|
||||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
ImGuiWindowFlags_NoBackground);
|
||||||
dl = ImGui::GetWindowDrawList();
|
dl = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
// Top-aligned content — consistent vertical position across all tabs
|
// Top-aligned content — consistent vertical position across all tabs
|
||||||
@@ -1428,7 +1432,7 @@ void RenderSendTab(App* app)
|
|||||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
// Toggle between DRGX and USD input
|
// Toggle between DRGX and USD input
|
||||||
float toggleW = schema::UI().drawElement("tabs.send", "toggle-currency-width").size;
|
float toggleW = schema::UI().drawElement("tabs.send", "toggle-currency-width").size * Layout::dpiScale();
|
||||||
float amtInputW = colW - toggleW - Layout::spacingMd();
|
float amtInputW = colW - toggleW - Layout::spacingMd();
|
||||||
if (amtInputW < schema::UI().drawElement("tabs.send", "amount-input-min-width").size) amtInputW = schema::UI().drawElement("tabs.send", "amount-input-min-width").size;
|
if (amtInputW < schema::UI().drawElement("tabs.send", "amount-input-min-width").size) amtInputW = schema::UI().drawElement("tabs.send", "amount-input-min-width").size;
|
||||||
|
|
||||||
@@ -1501,8 +1505,8 @@ void RenderSendTab(App* app)
|
|||||||
float bH = bMax.y - bMin.y;
|
float bH = bMax.y - bMin.y;
|
||||||
ImFont* font = ImGui::GetFont();
|
ImFont* font = ImGui::GetFont();
|
||||||
ImVec2 textSz = font->CalcTextSizeA(font->LegacySize, 10000, 0, currLabel);
|
ImVec2 textSz = font->CalcTextSizeA(font->LegacySize, 10000, 0, currLabel);
|
||||||
float iconW = schema::UI().drawElement("tabs.send", "swap-icon-width").size;
|
float iconW = schema::UI().drawElement("tabs.send", "swap-icon-width").size * Layout::dpiScale();
|
||||||
float iconGap = schema::UI().drawElement("tabs.send", "swap-icon-gap").size;
|
float iconGap = schema::UI().drawElement("tabs.send", "swap-icon-gap").size * Layout::dpiScale();
|
||||||
float totalW = iconW + iconGap + textSz.x;
|
float totalW = iconW + iconGap + textSz.x;
|
||||||
float startX = bMin.x + ((bMax.x - bMin.x) - totalW) * 0.5f;
|
float startX = bMin.x + ((bMax.x - bMin.x) - totalW) * 0.5f;
|
||||||
float cy = bMin.y + bH * 0.5f;
|
float cy = bMin.y + bH * 0.5f;
|
||||||
|
|||||||
@@ -293,11 +293,21 @@ void RenderTransactionsTab(App* app)
|
|||||||
TR("mined_filter"), TR("chat_filter") };
|
TR("mined_filter"), TR("chat_filter") };
|
||||||
ImGui::Combo("##TxType", &type_filter, types, IM_ARRAYSIZE(types));
|
ImGui::Combo("##TxType", &type_filter, types, IM_ARRAYSIZE(types));
|
||||||
|
|
||||||
// Sort selector
|
// Sort selector — the sort labels ("Newest first"/localized) run longer than the type
|
||||||
|
// labels, so this combo needs its own width. Size it to the widest localized sort option
|
||||||
|
// measured with the active (default) font — the same font Combo renders with — plus ImGui's
|
||||||
|
// combo chrome (2x horizontal frame padding + the dropdown arrow button, GetFrameHeight()).
|
||||||
|
// CalcTextSize and the style paddings are already DPI-scaled, so this grows at font_scale 1.5
|
||||||
|
// without any hs multiply; clamp to at least the shared type-filter comboW.
|
||||||
ImGui::SameLine(0, filterGap);
|
ImGui::SameLine(0, filterGap);
|
||||||
ImGui::SetNextItemWidth(comboW);
|
|
||||||
const char* sorts[] = { TR("sort_date_newest"), TR("sort_date_oldest"),
|
const char* sorts[] = { TR("sort_date_newest"), TR("sort_date_oldest"),
|
||||||
TR("sort_amount_high"), TR("sort_amount_low") };
|
TR("sort_amount_high"), TR("sort_amount_low") };
|
||||||
|
float sortTextW = 0.0f;
|
||||||
|
for (const char* s : sorts) sortTextW = std::max(sortTextW, ImGui::CalcTextSize(s).x);
|
||||||
|
float sortComboW = std::max(comboW,
|
||||||
|
sortTextW + ImGui::GetStyle().FramePadding.x * 2.0f
|
||||||
|
+ ImGui::GetFrameHeight());
|
||||||
|
ImGui::SetNextItemWidth(sortComboW);
|
||||||
ImGui::Combo("##TxSort", &s_sort_mode, sorts, IM_ARRAYSIZE(sorts));
|
ImGui::Combo("##TxSort", &s_sort_mode, sorts, IM_ARRAYSIZE(sorts));
|
||||||
|
|
||||||
ImGui::SameLine(0, filterGap);
|
ImGui::SameLine(0, filterGap);
|
||||||
|
|||||||
@@ -200,7 +200,20 @@ public:
|
|||||||
// create/scan/footer controls so it scrolls internally and those stay visible. Uncapped,
|
// create/scan/footer controls so it scrolls internally and those stay visible. Uncapped,
|
||||||
// it equals the content height exactly (no spurious scrollbar — the common case).
|
// it equals the content height exactly (no spurious scrollbar — the common case).
|
||||||
float listHFit = listH;
|
float listHFit = listH;
|
||||||
if (capped) listHFit = std::max(walRowH, ImGui::GetContentRegionAvail().y - belowH);
|
if (capped) {
|
||||||
|
// Fill the height left above the pinned controls, but clip on a CLEAN row boundary:
|
||||||
|
// floor the available space to a whole number of card strides (walRowH + cardGap, the
|
||||||
|
// per-card advance from the loop) so the list never ends mid-row. Keep the same bottom
|
||||||
|
// pad as the uncapped listH so the last visible card stays off the clip edge.
|
||||||
|
const float stride = walRowH + cardGap;
|
||||||
|
const float avail = ImGui::GetContentRegionAvail().y - belowH;
|
||||||
|
// N rows occupy N*walRowH + (N-1)*cardGap = N*stride - cardGap; solve for the most whole
|
||||||
|
// rows that fit (a non-negative cast truncates toward zero = floor here), clamped to at
|
||||||
|
// least one so a tiny viewport still shows a row.
|
||||||
|
int nRows = stride > 0.0f ? (int)((avail + cardGap) / stride) : 1;
|
||||||
|
if (nRows < 1) nRows = 1;
|
||||||
|
listHFit = (float)nRows * stride - cardGap + Layout::spacingSm();
|
||||||
|
}
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||||
// NoScrollWithMouse + ApplySmoothScroll gives the wheel the same eased scrolling as the
|
// NoScrollWithMouse + ApplySmoothScroll gives the wheel the same eased scrolling as the
|
||||||
// Settings page (ApplySmoothScroll handles the wheel itself, so let it own that input).
|
// Settings page (ApplySmoothScroll handles the wheel itself, so let it own that input).
|
||||||
@@ -253,13 +266,13 @@ public:
|
|||||||
bool hov = ImGui::IsWindowHovered() && ImGui::IsMouseHoveringRect(rMin, rMax);
|
bool hov = ImGui::IsWindowHovered() && ImGui::IsMouseHoveringRect(rMin, rMax);
|
||||||
|
|
||||||
// Card background + state
|
// Card background + state
|
||||||
GlassPanelSpec g; g.rounding = 10.0f; g.fillAlpha = hov ? 40 : 26; g.borderAlpha = 45;
|
GlassPanelSpec g; g.rounding = 10.0f * dp; g.fillAlpha = hov ? 40 : 26; g.borderAlpha = 45;
|
||||||
DrawGlassPanel(dl, rMin, rMax, g);
|
DrawGlassPanel(dl, rMin, rMax, g);
|
||||||
if (isCurrent) {
|
if (isCurrent) {
|
||||||
dl->AddRectFilled(rMin, rMax, WithAlpha(Success(), 16), 10.0f);
|
dl->AddRectFilled(rMin, rMax, WithAlpha(Success(), 16), 10.0f * dp);
|
||||||
dl->AddRect(rMin, rMax, WithAlpha(Success(), 130), 10.0f, 0, 1.6f * dp);
|
dl->AddRect(rMin, rMax, WithAlpha(Success(), 130), 10.0f * dp, 0, 1.6f * dp);
|
||||||
} else if (hov) {
|
} else if (hov) {
|
||||||
dl->AddRect(rMin, rMax, WithAlpha(OnSurface(), 70), 10.0f, 0, 1.0f);
|
dl->AddRect(rMin, rMax, WithAlpha(OnSurface(), 70), 10.0f * dp, 0, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float padX = Layout::spacingMd();
|
const float padX = Layout::spacingMd();
|
||||||
|
|||||||
Reference in New Issue
Block a user