Compare commits
3 Commits
e24ca015d1
...
ce8c7696d4
| Author | SHA1 | Date | |
|---|---|---|---|
| ce8c7696d4 | |||
| 99d1e73676 | |||
| 755cf22ad0 |
@@ -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),
|
||||||
|
|||||||
@@ -177,8 +177,28 @@ void App::renderFirstRunWizard() {
|
|||||||
// DPI scale factor — multiply all pixel constants by dp
|
// DPI scale factor — multiply all pixel constants by dp
|
||||||
const float dp = ui::Layout::dpiScale();
|
const float dp = ui::Layout::dpiScale();
|
||||||
|
|
||||||
|
// Vertical scroll: the wizard cards are hand-drawn at absolute Y offsets and grow ~1.5x with the
|
||||||
|
// font-scale setting, so at high scale the focused card's primary button (Continue / Encrypt & Continue
|
||||||
|
// / Skip) can fall below the fixed window. Offset the whole layout by a wheel-driven scroll, clamped to
|
||||||
|
// last frame's measured content height, so every control stays reachable. The window keeps
|
||||||
|
// NoScrollWithMouse, so ImGui doesn't consume the wheel — we read the raw delta and apply our own offset.
|
||||||
|
static float s_wizScroll = 0.0f, s_wizContentH = 0.0f;
|
||||||
|
if (ImGui::IsWindowAppearing()) s_wizScroll = 0.0f;
|
||||||
|
const float wizMaxScroll = std::max(0.0f, s_wizContentH - winSize.y);
|
||||||
|
// Don't steal the wheel from an open combo popup (e.g. the 9-item Language dropdown, which is a
|
||||||
|
// scrollable popup): NoPopupHierarchy stops the popup counting as hovering the wizard, and the
|
||||||
|
// IsPopupOpen guard ensures no wheel is consumed for the whole wizard while any popup is showing.
|
||||||
|
const bool wizPopupOpen = ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel);
|
||||||
|
if (wizMaxScroll > 0.0f && !wizPopupOpen &&
|
||||||
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) {
|
||||||
|
float wheel = ImGui::GetIO().MouseWheel;
|
||||||
|
if (wheel != 0.0f) s_wizScroll -= wheel * 60.0f * dp;
|
||||||
|
}
|
||||||
|
s_wizScroll = std::max(0.0f, std::min(s_wizScroll, wizMaxScroll));
|
||||||
|
const float scrollY = s_wizScroll;
|
||||||
|
|
||||||
// --- Header: Logo + Welcome ---
|
// --- Header: Logo + Welcome ---
|
||||||
float headerCy = winPos.y + 20.0f * dp;
|
float headerCy = winPos.y - scrollY + 20.0f * dp;
|
||||||
float logoSize = S.drawElement("screens.first-run", "logo").sizeOr(56.0f);
|
float logoSize = S.drawElement("screens.first-run", "logo").sizeOr(56.0f);
|
||||||
if (logo_tex_ != 0) {
|
if (logo_tex_ != 0) {
|
||||||
float aspect = (logo_h_ > 0) ? (float)logo_w_ / (float)logo_h_ : 1.0f;
|
float aspect = (logo_h_ > 0) ? (float)logo_w_ / (float)logo_h_ : 1.0f;
|
||||||
@@ -1428,6 +1448,21 @@ void App::renderFirstRunWizard() {
|
|||||||
// Merge channels: backgrounds → content → overlays
|
// Merge channels: backgrounds → content → overlays
|
||||||
dl->ChannelsMerge();
|
dl->ChannelsMerge();
|
||||||
|
|
||||||
|
// Measure this frame's content height (feeds next frame's scroll clamp) and, when it overflows the
|
||||||
|
// window, draw a slim scroll indicator so the off-screen content is discoverable.
|
||||||
|
{
|
||||||
|
float contentBottom = std::max(card0Bot, std::max(card1Bot, card2Bot));
|
||||||
|
s_wizContentH = (contentBottom - winPos.y + scrollY) + 24.0f * dp;
|
||||||
|
if (wizMaxScroll > 0.0f && s_wizContentH > 0.0f) {
|
||||||
|
float trackH = winSize.y - 8.0f * dp;
|
||||||
|
float thumbH = std::min(trackH, std::max(32.0f * dp, trackH * (winSize.y / s_wizContentH)));
|
||||||
|
float thumbY = winPos.y + 4.0f * dp + (trackH - thumbH) * (scrollY / wizMaxScroll);
|
||||||
|
float barX = winPos.x + winSize.x - 6.0f * dp;
|
||||||
|
dl->AddRectFilled(ImVec2(barX, thumbY), ImVec2(barX + 3.0f * dp, thumbY + thumbH),
|
||||||
|
ui::material::WithAlpha(ui::material::OnSurface(), 55), 1.5f * dp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1403,6 +1403,7 @@ struct OverlayCardState {
|
|||||||
int stableCount = 0; // consecutive frames the height held steady (within 1px)
|
int stableCount = 0; // consecutive frames the height held steady (within 1px)
|
||||||
int appearFrames = 0; // frames since (re)appearing while still hidden — a safety cap
|
int appearFrames = 0; // frames since (re)appearing while still hidden — a safety cap
|
||||||
bool shown = false; // revealed (centered) at least once this open; don't re-hide after
|
bool shown = false; // revealed (centered) at least once this open; don't re-hide after
|
||||||
|
bool overflow = false; // content once exceeded the viewport → clamp to viewport + scroll (sticky/open)
|
||||||
};
|
};
|
||||||
inline std::unordered_map<std::string, OverlayCardState> g_overlayCardHeights;
|
inline std::unordered_map<std::string, OverlayCardState> g_overlayCardHeights;
|
||||||
inline std::string g_overlayCurrentKey;
|
inline std::string g_overlayCurrentKey;
|
||||||
@@ -1528,6 +1529,7 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
|||||||
float cardX = vp_pos.x + (vp_size.x - cardWidth) * 0.5f;
|
float cardX = vp_pos.x + (vp_size.x - cardWidth) * 0.5f;
|
||||||
float cardY, cardBottomY;
|
float cardY, cardBottomY;
|
||||||
bool hideForMeasure = false; // true on an auto-height dialog's first (unmeasured) frame
|
bool hideForMeasure = false; // true on an auto-height dialog's first (unmeasured) frame
|
||||||
|
bool autoOverflow = false; // auto-height content taller than the viewport → clamp + scroll
|
||||||
const bool fixedHeight = (spec.cardHeight > 0.0f);
|
const bool fixedHeight = (spec.cardHeight > 0.0f);
|
||||||
if (fixedHeight) {
|
if (fixedHeight) {
|
||||||
float cardH = std::min(spec.cardHeight * dp, vp_size.y - 32.0f);
|
float cardH = std::min(spec.cardHeight * dp, vp_size.y - 32.0f);
|
||||||
@@ -1537,9 +1539,16 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
|||||||
} else {
|
} else {
|
||||||
g_overlayCurrentKey = childId;
|
g_overlayCurrentKey = childId;
|
||||||
OverlayCardState& cs = g_overlayCardHeights[childId];
|
OverlayCardState& cs = g_overlayCardHeights[childId];
|
||||||
if (scrimAppearing) { cs.shown = false; cs.stableCount = 0; cs.appearFrames = 0; }
|
if (scrimAppearing) { cs.shown = false; cs.stableCount = 0; cs.appearFrames = 0; cs.overflow = false; }
|
||||||
if (!cs.shown) cs.appearFrames++;
|
if (!cs.shown) cs.appearFrames++;
|
||||||
const float measuredH = cs.height;
|
const float measuredH = cs.height;
|
||||||
|
const float maxCardH = vp_size.y - 32.0f;
|
||||||
|
// Once the measured content is taller than the viewport, lock the card to the viewport height and
|
||||||
|
// let its content child scroll (autoOverflow) so the footer/actions stay reachable. Sticky for this
|
||||||
|
// open: clamping makes next frame's measured height the clamped value, so re-deciding from it would
|
||||||
|
// oscillate — decide once and hold until the dialog re-opens.
|
||||||
|
if (measuredH > maxCardH) cs.overflow = true;
|
||||||
|
autoOverflow = cs.overflow;
|
||||||
// Reveal once the measured height has settled (auto-resize converges in ~2 frames) or it's
|
// Reveal once the measured height has settled (auto-resize converges in ~2 frames) or it's
|
||||||
// already been shown this open (don't re-hide on a mid-dialog content change); a frame cap
|
// already been shown this open (don't re-hide on a mid-dialog content change); a frame cap
|
||||||
// guarantees a pathological ever-changing height can't hide the dialog forever.
|
// guarantees a pathological ever-changing height can't hide the dialog forever.
|
||||||
@@ -1547,11 +1556,18 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
|||||||
(cs.shown || cs.stableCount >= 1 || cs.appearFrames >= 8);
|
(cs.shown || cs.stableCount >= 1 || cs.appearFrames >= 8);
|
||||||
if (ready) {
|
if (ready) {
|
||||||
cs.shown = true;
|
cs.shown = true;
|
||||||
// Center the measured content; if it's taller than the window, anchor at the top margin.
|
if (autoOverflow) {
|
||||||
cardY = (measuredH < vp_size.y - 32.0f)
|
// Taller than the screen: top-anchor at the 16px margin, clamp to the viewport; the
|
||||||
? vp_pos.y + (vp_size.y - measuredH) * 0.5f
|
// content child (below) becomes the scroll region so the footer/actions stay reachable.
|
||||||
: vp_pos.y + 16.0f;
|
cardY = vp_pos.y + 16.0f;
|
||||||
cardBottomY = cardY + measuredH;
|
cardBottomY = cardY + maxCardH;
|
||||||
|
} else {
|
||||||
|
// Center the measured content; if it's taller than the window, anchor at the top margin.
|
||||||
|
cardY = (measuredH < maxCardH)
|
||||||
|
? vp_pos.y + (vp_size.y - measuredH) * 0.5f
|
||||||
|
: vp_pos.y + 16.0f;
|
||||||
|
cardBottomY = cardY + measuredH;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Still settling: lay the content out (so the auto-height child gets measured) but keep
|
// Still settling: lay the content out (so the auto-height child gets measured) but keep
|
||||||
// the card hidden (hideForMeasure below) so it never flashes off-center — it appears,
|
// the card hidden (hideForMeasure below) so it never flashes off-center — it appears,
|
||||||
@@ -1584,14 +1600,21 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, floating ? 20.0f : 16.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, floating ? 20.0f : 16.0f);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28, 20) : ImVec2(28, 24));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28, 20) : ImVec2(28, 24));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // transparent (glass/blur behind)
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // transparent (glass/blur behind)
|
||||||
ImGuiChildFlags cflags = ImGuiChildFlags_AlwaysUseWindowPadding | (fixedHeight ? 0 : ImGuiChildFlags_AutoResizeY);
|
// A card with a known height is a fixed frame (fixed-height dialogs, and auto-height dialogs whose
|
||||||
|
// content overflowed the viewport); otherwise the child auto-resizes to its content.
|
||||||
|
const bool clampedCard = fixedHeight || autoOverflow;
|
||||||
|
ImGuiChildFlags cflags = ImGuiChildFlags_AlwaysUseWindowPadding | (clampedCard ? 0 : ImGuiChildFlags_AutoResizeY);
|
||||||
// NoScrollWithMouse (not just NoScrollbar): a modal is a fixed frame — the wheel must never drift
|
// NoScrollWithMouse (not just NoScrollbar): a modal is a fixed frame — the wheel must never drift
|
||||||
// the WHOLE card. If content marginally overflows a fixed card, the wheel would otherwise scroll
|
// the WHOLE card. If content marginally overflows a fixed card, the wheel would otherwise scroll
|
||||||
// the entire dialog (title + footer and all). Inner scroll regions (lists, notes) still scroll on
|
// the entire dialog (title + footer and all). Inner scroll regions (lists, notes) still scroll on
|
||||||
// their own; auto-height cards resize to content so they never overflow anyway.
|
// their own; auto-height cards resize to content so they normally never overflow — EXCEPT when the
|
||||||
|
// content is taller than the viewport (autoOverflow), where the card itself IS the scroll region.
|
||||||
|
ImGuiWindowFlags childScroll = autoOverflow
|
||||||
|
? ImGuiWindowFlags_None
|
||||||
|
: (ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
bool childVisible = ImGui::BeginChild(childId.c_str(),
|
bool childVisible = ImGui::BeginChild(childId.c_str(),
|
||||||
ImVec2(cardWidth, fixedHeight ? (cardBottomY - cardY) : 0.0f),
|
ImVec2(cardWidth, clampedCard ? (cardBottomY - cardY) : 0.0f),
|
||||||
cflags, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
cflags, childScroll);
|
||||||
// Floating (portfolio-style) cards: the padding applies to this content child only, so pop it
|
// Floating (portfolio-style) cards: the padding applies to this content child only, so pop it
|
||||||
// now (nested children mustn't inherit it), and center button labels. Net style-var count stays
|
// now (nested children mustn't inherit it), and center button labels. Net style-var count stays
|
||||||
// at 2 (ChildRounding + ButtonTextAlign) so EndOverlayDialog's PopStyleVar(2) is unchanged.
|
// at 2 (ChildRounding + ButtonTextAlign) so EndOverlayDialog's PopStyleVar(2) is unchanged.
|
||||||
@@ -1714,7 +1737,7 @@ inline void DialogWarningHeader(const char* warningLabel, const ImVec4& col = Wa
|
|||||||
inline void DialogConfirmFooter(const char* cancelId, const char* confirmLabel,
|
inline void DialogConfirmFooter(const char* cancelId, const char* confirmLabel,
|
||||||
bool danger, bool& outCancel, bool& outConfirm)
|
bool danger, bool& outCancel, bool& outConfirm)
|
||||||
{
|
{
|
||||||
float btnH = schema::UI().drawElement("components.overlay-dialog", "confirm-btn-height").sizeOr(40.0f);
|
float btnH = schema::UI().drawElement("components.overlay-dialog", "confirm-btn-height").sizeOr(40.0f) * Layout::dpiScale();
|
||||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
if (ImGui::Button(cancelId, ImVec2(btnW, btnH))) {
|
if (ImGui::Button(cancelId, ImVec2(btnW, btnH))) {
|
||||||
outCancel = true;
|
outCancel = true;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float v
|
|||||||
|
|
||||||
// Render the shared address list section (used by all layouts)
|
// Render the shared address list section (used by all layouts)
|
||||||
void RenderSharedAddressList(App* app, float listH, float availW,
|
void RenderSharedAddressList(App* app, float listH, float availW,
|
||||||
float glassRound, float hs, float vs) {
|
float glassRound, float hs, float vs, float reserveBelow) {
|
||||||
using namespace material;
|
using namespace material;
|
||||||
const auto& S = schema::UISchema::instance();
|
const auto& S = schema::UISchema::instance();
|
||||||
const float dp = Layout::dpiScale();
|
const float dp = Layout::dpiScale();
|
||||||
@@ -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);
|
||||||
@@ -225,6 +225,14 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
|
|
||||||
// ---- Glass panel container ----
|
// ---- Glass panel container ----
|
||||||
float addrListH = listH;
|
float addrListH = listH;
|
||||||
|
// Cap the card to the space that actually remains here (measured AFTER the title + toolbar are laid
|
||||||
|
// out, so no chrome modelling is needed) minus what the caller reserves for the section below it
|
||||||
|
// (recent-tx). Without this, a fixed dp-scaled listH grows ~1.5x at high font scale and evicts the
|
||||||
|
// Recent Transactions list off the bottom of the fixed, non-scrolling tab host.
|
||||||
|
if (reserveBelow > 0.0f) {
|
||||||
|
float maxH = ImGui::GetContentRegionAvail().y - reserveBelow;
|
||||||
|
if (maxH < addrListH) addrListH = maxH;
|
||||||
|
}
|
||||||
if (addrListH < 40.0f * dp) addrListH = 40.0f * dp;
|
if (addrListH < 40.0f * dp) addrListH = 40.0f * dp;
|
||||||
|
|
||||||
ImDrawList* dlPanel = ImGui::GetWindowDrawList();
|
ImDrawList* dlPanel = ImGui::GetWindowDrawList();
|
||||||
@@ -834,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());
|
||||||
|
|
||||||
@@ -849,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;
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ extern bool s_generating_z_address;
|
|||||||
void UpdateBalanceLerp(App* app);
|
void UpdateBalanceLerp(App* app);
|
||||||
void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs,
|
void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs,
|
||||||
float heroHeightOverride = -1.0f);
|
float heroHeightOverride = -1.0f);
|
||||||
void RenderSharedAddressList(App* app, float listH, float availW, float glassRound, float hs, float vs);
|
void RenderSharedAddressList(App* app, float listH, float availW, float glassRound, float hs, float vs,
|
||||||
|
float reserveBelow = 0.0f);
|
||||||
void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs);
|
void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs);
|
||||||
void RenderSyncBar(App* app, ImDrawList* dl, float vs);
|
void RenderSyncBar(App* app, ImDrawList* dl, float vs);
|
||||||
|
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ static void RenderBalanceClassic(App* app)
|
|||||||
float addrH = (classicAddrH >= 0.0f) ? classicAddrH * dp
|
float addrH = (classicAddrH >= 0.0f) ? classicAddrH * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, contentAvail.x, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, contentAvail.x, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, contentAvail.x, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, contentAvail.x, hs, vs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,7 +835,7 @@ static void RenderBalanceDonut(App* app) {
|
|||||||
float addrH = (donutAddrOverride >= 0.0f) ? donutAddrOverride * dp
|
float addrH = (donutAddrOverride >= 0.0f) ? donutAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1018,7 +1018,7 @@ static void RenderBalanceConsolidated(App* app) {
|
|||||||
float addrH = (consAddrOverride >= 0.0f) ? consAddrOverride * dp
|
float addrH = (consAddrOverride >= 0.0f) ? consAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,7 +1175,7 @@ static void RenderBalanceDashboard(App* app) {
|
|||||||
float addrH = (dashAddrOverride >= 0.0f) ? dashAddrOverride * dp
|
float addrH = (dashAddrOverride >= 0.0f) ? dashAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1354,7 +1354,7 @@ static void RenderBalanceVerticalStack(App* app) {
|
|||||||
float addrH = (vstackAddrOverride >= 0.0f) ? vstackAddrOverride * dp
|
float addrH = (vstackAddrOverride >= 0.0f) ? vstackAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1549,7 +1549,7 @@ static void RenderBalanceVertical2x2(App* app) {
|
|||||||
float addrH = (addrOverride >= 0.0f) ? addrOverride * dp
|
float addrH = (addrOverride >= 0.0f) ? addrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1731,7 +1731,7 @@ static void RenderBalanceShield(App* app) {
|
|||||||
float addrH = (shieldAddrOverride >= 0.0f) ? shieldAddrOverride * dp
|
float addrH = (shieldAddrOverride >= 0.0f) ? shieldAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1890,7 +1890,7 @@ static void RenderBalanceTimeline(App* app) {
|
|||||||
float addrH = (tlAddrOverride >= 0.0f) ? tlAddrOverride * dp
|
float addrH = (tlAddrOverride >= 0.0f) ? tlAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -2081,7 +2081,7 @@ static void RenderBalanceTwoRow(App* app) {
|
|||||||
float addrH = (twoRowAddrOverride >= 0.0f) ? twoRowAddrOverride * dp
|
float addrH = (twoRowAddrOverride >= 0.0f) ? twoRowAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2174,7 +2174,7 @@ static void RenderBalanceMinimal(App* app) {
|
|||||||
float addrH = (minAddrOverride >= 0.0f) ? minAddrOverride * dp
|
float addrH = (minAddrOverride >= 0.0f) ? minAddrOverride * dp
|
||||||
: ImGui::GetContentRegionAvail().y - recentReserve
|
: ImGui::GetContentRegionAvail().y - recentReserve
|
||||||
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
- Layout::spacingXl() - Type().h6()->LegacySize - Layout::spacingMd();
|
||||||
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs);
|
RenderSharedAddressList(app, addrH, availW, glassRound, hs, vs, recentReserve);
|
||||||
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
RenderSharedRecentTx(app, recentReserve, availW, hs, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ void BlockInfoDialog::render(App* app)
|
|||||||
|
|
||||||
// Height input
|
// Height input
|
||||||
ImGui::Text("%s", TR("block_height"));
|
ImGui::Text("%s", TR("block_height"));
|
||||||
ImGui::SetNextItemWidth(heightInput.width);
|
ImGui::SetNextItemWidth(heightInput.width * Layout::dpiScale());
|
||||||
ImGui::InputInt("##Height", &s_height);
|
ImGui::InputInt("##Height", &s_height);
|
||||||
if (s_height < 1) s_height = 1;
|
if (s_height < 1) s_height = 1;
|
||||||
// Clamp to the chain tip so navigation/typing can't request a height
|
// Clamp to the chain tip so navigation/typing can't request a height
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -776,7 +776,7 @@ void RenderChatTab(App* app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 avail = ImGui::GetContentRegionAvail();
|
const ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||||
const float listW = std::clamp(avail.x * 0.32f, 220.0f, 360.0f);
|
const float listW = std::clamp(avail.x * 0.32f, 220.0f * Layout::dpiScale(), 360.0f * Layout::dpiScale());
|
||||||
// Row geometry is logical px — scale by dpiScale() so rows/padding grow with the (DPI-scaled)
|
// Row geometry is logical px — scale by dpiScale() so rows/padding grow with the (DPI-scaled)
|
||||||
// fonts. Left raw, at higher DPI the row was too short for the enlarged text and the preview's
|
// fonts. Left raw, at higher DPI the row was too short for the enlarged text and the preview's
|
||||||
// right margin (rowW - pad) shrank to ~zero, clipping the last glyph mid-word.
|
// right margin (rowW - pad) shrank to ~zero, clipping the last glyph mid-word.
|
||||||
@@ -895,16 +895,22 @@ void RenderChatTab(App* app)
|
|||||||
}
|
}
|
||||||
const float textX = avC.x + avR + pad;
|
const float textX = avC.x + avR + pad;
|
||||||
|
|
||||||
// Name (top). Hidden conversations (shown via "Show hidden") render dimmed.
|
// Time (top-right, muted) — compact relative form (Q5). Measure/draw it FIRST so the name can be
|
||||||
dl->AddText(nameFont, nameSz, ImVec2(textX, p.y + pad),
|
// clipped to the column left of it — otherwise a long peer name overruns the timestamp (worse at
|
||||||
c.hidden ? material::OnSurfaceMedium() : material::OnSurface(), c.peerName.c_str());
|
// HiDPI, where the fixed-length name grows ~1.5x).
|
||||||
// Time (top-right, muted) — compact relative form (Q5).
|
|
||||||
const std::string when = relativeTime(c.lastTs);
|
const std::string when = relativeTime(c.lastTs);
|
||||||
|
float nameRight = mx.x - pad;
|
||||||
if (!when.empty()) {
|
if (!when.empty()) {
|
||||||
const ImVec2 wsz = metaFont->CalcTextSizeA(metaSz, FLT_MAX, 0.0f, when.c_str());
|
const ImVec2 wsz = metaFont->CalcTextSizeA(metaSz, FLT_MAX, 0.0f, when.c_str());
|
||||||
dl->AddText(metaFont, metaSz, ImVec2(mx.x - pad - wsz.x, p.y + pad + 1.0f),
|
dl->AddText(metaFont, metaSz, ImVec2(mx.x - pad - wsz.x, p.y + pad + 1.0f),
|
||||||
material::OnSurfaceMedium(), when.c_str());
|
material::OnSurfaceMedium(), when.c_str());
|
||||||
|
nameRight = mx.x - pad - wsz.x - pad; // reserve the timestamp column + a gap
|
||||||
}
|
}
|
||||||
|
// Name (top), clipped to the space left of the timestamp. Hidden conversations render dimmed.
|
||||||
|
dl->PushClipRect(ImVec2(textX, p.y), ImVec2(std::max(textX, nameRight), mx.y), true);
|
||||||
|
dl->AddText(nameFont, nameSz, ImVec2(textX, p.y + pad),
|
||||||
|
c.hidden ? material::OnSurfaceMedium() : material::OnSurface(), c.peerName.c_str());
|
||||||
|
dl->PopClipRect();
|
||||||
// Preview (bottom, clipped to the text column, muted).
|
// Preview (bottom, clipped to the text column, muted).
|
||||||
const std::string preview = previewOf(c.lastBody);
|
const std::string preview = previewOf(c.lastBody);
|
||||||
dl->PushClipRect(ImVec2(textX, p.y), ImVec2(mx.x - pad, mx.y), true);
|
dl->PushClipRect(ImVec2(textX, p.y), ImVec2(mx.x - pad, mx.y), true);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -1116,8 +1116,9 @@ static void renderBlockDetailModal(App* app) {
|
|||||||
|
|
||||||
// ── Info grid ──
|
// ── Info grid ──
|
||||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
float dp = Layout::dpiScale();
|
||||||
float rowH = capFont->LegacySize + Layout::spacingXs() + sub1->LegacySize;
|
float rowH = capFont->LegacySize + Layout::spacingXs() + sub1->LegacySize;
|
||||||
float labelW = S.drawElement("tabs.explorer", "label-column").size;
|
float labelW = S.drawElement("tabs.explorer", "label-column").size * dp;
|
||||||
{
|
{
|
||||||
ImVec2 gridPos = ImGui::GetCursorScreenPos();
|
ImVec2 gridPos = ImGui::GetCursorScreenPos();
|
||||||
float gx = gridPos.x;
|
float gx = gridPos.x;
|
||||||
@@ -1178,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();
|
||||||
|
|||||||
@@ -312,11 +312,11 @@ static void RenderLeftPoolCard(App* app, const WalletState& state, ImDrawList* d
|
|||||||
isCurrent ? Success() : OnSurfaceDisabled());
|
isCurrent ? Success() : OnSurfaceDisabled());
|
||||||
|
|
||||||
const float textY = rMin.y + (listRowH - capFont->LegacySize) * 0.5f;
|
const float textY = rMin.y + (listRowH - capFont->LegacySize) * 0.5f;
|
||||||
// Show the short host label (the narrow card can't fit host:port); the
|
|
||||||
// full stratum URL is in the hover tooltip.
|
|
||||||
cdl->AddText(capFont, capFont->LegacySize, ImVec2(rMin.x + 16 * dp, textY),
|
|
||||||
isCurrent ? Primary() : OnSurface(), kp.label.c_str());
|
|
||||||
|
|
||||||
|
// Build the right-side "<hashrate> N% fee" run first so we know its width
|
||||||
|
// and can bound (and ellipsis-truncate) the left host label to avoid a
|
||||||
|
// collision — both text runs grow ~1.5x at font_scale 1.5 while the card
|
||||||
|
// width barely does.
|
||||||
char right[64];
|
char right[64];
|
||||||
std::string hrStr = haveHr ? FormatHashrate(it->second.hashrateHs) : std::string("—");
|
std::string hrStr = haveHr ? FormatHashrate(it->second.hashrateHs) : std::string("—");
|
||||||
// Prefer the live fee the pool reports; fall back to the compile-time
|
// Prefer the live fee the pool reports; fall back to the compile-time
|
||||||
@@ -331,6 +331,16 @@ static void RenderLeftPoolCard(App* app, const WalletState& state, ImDrawList* d
|
|||||||
else
|
else
|
||||||
snprintf(right, sizeof(right), "%s", hrStr.c_str());
|
snprintf(right, sizeof(right), "%s", hrStr.c_str());
|
||||||
ImVec2 rSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, right);
|
ImVec2 rSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, right);
|
||||||
|
|
||||||
|
// Show the short host label (the narrow card can't fit host:port); the
|
||||||
|
// full stratum URL is in the hover tooltip. Truncate it to the gap left
|
||||||
|
// of the right-side run so a long hostname can't overlap the hashrate.
|
||||||
|
const float labelX = rMin.x + 16 * dp;
|
||||||
|
const float labelMaxW = (rMax.x - rSz.x - 6 * dp - gap) - labelX;
|
||||||
|
std::string label = TruncateToWidth(kp.label, capFont, capFont->LegacySize, labelMaxW);
|
||||||
|
cdl->AddText(capFont, capFont->LegacySize, ImVec2(labelX, textY),
|
||||||
|
isCurrent ? Primary() : OnSurface(), label.c_str());
|
||||||
|
|
||||||
cdl->AddText(capFont, capFont->LegacySize,
|
cdl->AddText(capFont, capFont->LegacySize,
|
||||||
ImVec2(rMax.x - rSz.x - 6 * dp, textY), OnSurfaceMedium(), right);
|
ImVec2(rMax.x - rSz.x - 6 * dp, textY), OnSurfaceMedium(), right);
|
||||||
|
|
||||||
|
|||||||
@@ -627,6 +627,7 @@ void RenderPeersTab(App* app)
|
|||||||
ImGui::Dummy(ImVec2(0, 20));
|
ImGui::Dummy(ImVec2(0, 20));
|
||||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("peers_no_connected"));
|
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("peers_no_connected"));
|
||||||
} else {
|
} else {
|
||||||
|
const float dp = ui::Layout::dpiScale();
|
||||||
float rowH = body2->LegacySize + capFont->LegacySize + Layout::spacingLg();
|
float rowH = body2->LegacySize + capFont->LegacySize + Layout::spacingLg();
|
||||||
float rowInset = Layout::spacingLg();
|
float rowInset = Layout::spacingLg();
|
||||||
float innerW = ImGui::GetContentRegionAvail().x - rowInset * 2;
|
float innerW = ImGui::GetContentRegionAvail().x - rowInset * 2;
|
||||||
@@ -643,13 +644,13 @@ void RenderPeersTab(App* app)
|
|||||||
ImVec2 rowEnd(rowPos.x + innerW, rowPos.y + rowH);
|
ImVec2 rowEnd(rowPos.x + innerW, rowPos.y + rowH);
|
||||||
|
|
||||||
if (is_selected) {
|
if (is_selected) {
|
||||||
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 20), S.drawElement("tabs.peers", "row-selection-rounding").size);
|
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 20), S.drawElement("tabs.peers", "row-selection-rounding").size * dp);
|
||||||
dl->AddRectFilled(rowPos, ImVec2(rowPos.x + S.drawElement("tabs.peers", "row-accent-width").size, rowEnd.y), Primary(), S.drawElement("tabs.peers", "row-accent-rounding").size);
|
dl->AddRectFilled(rowPos, ImVec2(rowPos.x + S.drawElement("tabs.peers", "row-accent-width").size * dp, rowEnd.y), Primary(), S.drawElement("tabs.peers", "row-accent-rounding").size * dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hovered = material::IsRectHovered(rowPos, rowEnd);
|
bool hovered = material::IsRectHovered(rowPos, rowEnd);
|
||||||
if (hovered && !is_selected) {
|
if (hovered && !is_selected) {
|
||||||
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 15), S.drawElement("tabs.peers", "row-selection-rounding").size);
|
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 15), S.drawElement("tabs.peers", "row-selection-rounding").size * dp);
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,9 +663,9 @@ void RenderPeersTab(App* app)
|
|||||||
else if (ping_ms < 500) dotCol = Warning();
|
else if (ping_ms < 500) dotCol = Warning();
|
||||||
else dotCol = Error();
|
else dotCol = Error();
|
||||||
float pingDotR = S.drawElement("tabs.peers", "ping-dot-radius-base").size + S.drawElement("tabs.peers", "ping-dot-radius-scale").size * hs;
|
float pingDotR = S.drawElement("tabs.peers", "ping-dot-radius-base").size + S.drawElement("tabs.peers", "ping-dot-radius-scale").size * hs;
|
||||||
dl->AddCircleFilled(ImVec2(cx + S.drawElement("tabs.peers", "ping-dot-x-offset").size, cy + body2->LegacySize * 0.5f), pingDotR, dotCol);
|
dl->AddCircleFilled(ImVec2(cx + S.drawElement("tabs.peers", "ping-dot-x-offset").size * dp, cy + body2->LegacySize * 0.5f), pingDotR, dotCol);
|
||||||
|
|
||||||
float addrX = cx + S.drawElement("tabs.peers", "address-x-offset").size;
|
float addrX = cx + S.drawElement("tabs.peers", "address-x-offset").size * dp;
|
||||||
// Reserve the line-1 trailing zone (ping + direction pill live on the far right,
|
// Reserve the line-1 trailing zone (ping + direction pill live on the far right,
|
||||||
// the nearest being the ping at innerW - pingW - spacingXl*3). A long IPv6+port
|
// the nearest being the ping at innerW - pingW - spacingXl*3). A long IPv6+port
|
||||||
// addr must clip before it so it can't run under those. Mirror the ping formula
|
// addr must clip before it so it can't run under those. Mirror the ping formula
|
||||||
@@ -693,9 +694,9 @@ void RenderPeersTab(App* app)
|
|||||||
ImU32 dirFg = peer.inbound ? WithAlpha(Success(), 200) : WithAlpha(Secondary(), 200);
|
ImU32 dirFg = peer.inbound ? WithAlpha(Success(), 200) : WithAlpha(Secondary(), 200);
|
||||||
ImVec2 dirSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, dirLabel);
|
ImVec2 dirSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, dirLabel);
|
||||||
float dirX = rowPos.x + innerW - dirSz.x - Layout::spacingXl();
|
float dirX = rowPos.x + innerW - dirSz.x - Layout::spacingXl();
|
||||||
ImVec2 pillMin(dirX - S.drawElement("tabs.peers", "dir-pill-padding").size, cy + S.drawElement("tabs.peers", "dir-pill-y-offset").size);
|
ImVec2 pillMin(dirX - S.drawElement("tabs.peers", "dir-pill-padding").size * dp, cy + S.drawElement("tabs.peers", "dir-pill-y-offset").size * dp);
|
||||||
ImVec2 pillMax(dirX + dirSz.x + S.drawElement("tabs.peers", "dir-pill-padding").size, cy + capFont->LegacySize + S.drawElement("tabs.peers", "dir-pill-y-bottom").size);
|
ImVec2 pillMax(dirX + dirSz.x + S.drawElement("tabs.peers", "dir-pill-padding").size * dp, cy + capFont->LegacySize + S.drawElement("tabs.peers", "dir-pill-y-bottom").size * dp);
|
||||||
dl->AddRectFilled(pillMin, pillMax, dirBg, S.drawElement("tabs.peers", "dir-pill-rounding").size);
|
dl->AddRectFilled(pillMin, pillMax, dirBg, S.drawElement("tabs.peers", "dir-pill-rounding").size * dp);
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(dirX, cy + 2), dirFg, dirLabel);
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(dirX, cy + 2), dirFg, dirLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,7 +709,7 @@ void RenderPeersTab(App* app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
float cy2 = cy + body2->LegacySize + Layout::spacingXs();
|
float cy2 = cy + body2->LegacySize + Layout::spacingXs();
|
||||||
float subverX = cx + S.drawElement("tabs.peers", "address-x-offset").size;
|
float subverX = cx + S.drawElement("tabs.peers", "address-x-offset").size * dp;
|
||||||
|
|
||||||
// Reserve the line-2 trailing widths up front so an untrusted, arbitrarily long
|
// Reserve the line-2 trailing widths up front so an untrusted, arbitrarily long
|
||||||
// remote subver cannot push the TLS badge / ban-score off the row. Order right→left:
|
// remote subver cannot push the TLS badge / ban-score off the row. Order right→left:
|
||||||
@@ -747,7 +748,7 @@ void RenderPeersTab(App* app)
|
|||||||
ImU32 tlsFg = WithAlpha(Success(), 200);
|
ImU32 tlsFg = WithAlpha(Success(), 200);
|
||||||
ImVec2 tlsMin(badgeX, cy2);
|
ImVec2 tlsMin(badgeX, cy2);
|
||||||
ImVec2 tlsMax(tlsMin.x + tlsBadgeW, tlsMin.y + capFont->LegacySize + 2);
|
ImVec2 tlsMax(tlsMin.x + tlsBadgeW, tlsMin.y + capFont->LegacySize + 2);
|
||||||
dl->AddRectFilled(tlsMin, tlsMax, tlsBg, S.drawElement("tabs.peers", "tls-badge-rounding").size);
|
dl->AddRectFilled(tlsMin, tlsMax, tlsBg, S.drawElement("tabs.peers", "tls-badge-rounding").size * dp);
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(tlsMin.x + 4, cy2 + 1), tlsFg, "TLS");
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(tlsMin.x + 4, cy2 + 1), tlsFg, "TLS");
|
||||||
} else {
|
} else {
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(badgeX, cy2),
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(badgeX, cy2),
|
||||||
@@ -815,7 +816,7 @@ void RenderPeersTab(App* app)
|
|||||||
|
|
||||||
if (i < state.peers.size() - 1) {
|
if (i < state.peers.size() - 1) {
|
||||||
ImVec2 divStart = ImGui::GetCursorScreenPos();
|
ImVec2 divStart = ImGui::GetCursorScreenPos();
|
||||||
dl->AddLine(ImVec2(divStart.x + pad + 18, divStart.y),
|
dl->AddLine(ImVec2(divStart.x + pad + 18 * dp, divStart.y),
|
||||||
ImVec2(divStart.x + innerW - pad, divStart.y),
|
ImVec2(divStart.x + innerW - pad, divStart.y),
|
||||||
IM_COL32(255, 255, 255, 15));
|
IM_COL32(255, 255, 255, 15));
|
||||||
}
|
}
|
||||||
@@ -832,7 +833,8 @@ void RenderPeersTab(App* app)
|
|||||||
ImGui::Dummy(ImVec2(0, 20));
|
ImGui::Dummy(ImVec2(0, 20));
|
||||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("peers_no_banned"));
|
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("peers_no_banned"));
|
||||||
} else {
|
} else {
|
||||||
float rowH = capFont->LegacySize + S.drawElement("tabs.peers", "banned-row-height-padding").size;
|
const float dp = ui::Layout::dpiScale();
|
||||||
|
float rowH = capFont->LegacySize + S.drawElement("tabs.peers", "banned-row-height-padding").size * dp;
|
||||||
float rowInsetB = pad;
|
float rowInsetB = pad;
|
||||||
float innerW = ImGui::GetContentRegionAvail().x - rowInsetB * 2;
|
float innerW = ImGui::GetContentRegionAvail().x - rowInsetB * 2;
|
||||||
listScrollY = ImGui::GetScrollY();
|
listScrollY = ImGui::GetScrollY();
|
||||||
@@ -848,21 +850,21 @@ void RenderPeersTab(App* app)
|
|||||||
ImVec2 rowEnd(rowPos.x + innerW, rowPos.y + rowH);
|
ImVec2 rowEnd(rowPos.x + innerW, rowPos.y + rowH);
|
||||||
|
|
||||||
if (is_selected) {
|
if (is_selected) {
|
||||||
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 20), S.drawElement("tabs.peers", "banned-row-rounding").size);
|
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 20), S.drawElement("tabs.peers", "banned-row-rounding").size * dp);
|
||||||
dl->AddRectFilled(rowPos, ImVec2(rowPos.x + S.drawElement("tabs.peers", "row-accent-width").size, rowEnd.y), WithAlpha(Error(), 200), S.drawElement("tabs.peers", "banned-accent-rounding").size);
|
dl->AddRectFilled(rowPos, ImVec2(rowPos.x + S.drawElement("tabs.peers", "row-accent-width").size * dp, rowEnd.y), WithAlpha(Error(), 200), S.drawElement("tabs.peers", "banned-accent-rounding").size * dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (material::IsRectHovered(rowPos, rowEnd) && !is_selected) {
|
if (material::IsRectHovered(rowPos, rowEnd) && !is_selected) {
|
||||||
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 15), S.drawElement("tabs.peers", "banned-row-rounding").size);
|
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(255, 255, 255, 15), S.drawElement("tabs.peers", "banned-row-rounding").size * dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
float cx = rowPos.x + pad;
|
float cx = rowPos.x + pad;
|
||||||
float cy = rowPos.y + Layout::spacingXs();
|
float cy = rowPos.y + Layout::spacingXs();
|
||||||
|
|
||||||
float banDotR = S.drawElement("tabs.peers", "ban-dot-radius-base").size + S.drawElement("tabs.peers", "ban-dot-radius-scale").size * hs;
|
float banDotR = S.drawElement("tabs.peers", "ban-dot-radius-base").size + S.drawElement("tabs.peers", "ban-dot-radius-scale").size * hs;
|
||||||
dl->AddCircleFilled(ImVec2(cx + S.drawElement("tabs.peers", "ban-dot-x-offset").size, cy + capFont->LegacySize * 0.4f), banDotR, WithAlpha(Error(), 200));
|
dl->AddCircleFilled(ImVec2(cx + S.drawElement("tabs.peers", "ban-dot-x-offset").size * dp, cy + capFont->LegacySize * 0.4f), banDotR, WithAlpha(Error(), 200));
|
||||||
|
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx + S.drawElement("tabs.peers", "banned-address-x-offset").size, cy),
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx + S.drawElement("tabs.peers", "banned-address-x-offset").size * dp, cy),
|
||||||
OnSurfaceDisabled(), banned.address.c_str());
|
OnSurfaceDisabled(), banned.address.c_str());
|
||||||
|
|
||||||
std::string banUntil = banned.getBannedUntilString();
|
std::string banUntil = banned.getBannedUntilString();
|
||||||
@@ -878,7 +880,7 @@ void RenderPeersTab(App* app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetCursorScreenPos(rowPos);
|
ImGui::SetCursorScreenPos(rowPos);
|
||||||
ImGui::InvisibleButton("##bannedRow", ImVec2(innerW - S.drawElement("tabs.peers", "banned-row-btn-reserve").size, rowH));
|
ImGui::InvisibleButton("##bannedRow", ImVec2(innerW - S.drawElement("tabs.peers", "banned-row-btn-reserve").size * dp, rowH));
|
||||||
if (ImGui::IsItemClicked(0)) {
|
if (ImGui::IsItemClicked(0)) {
|
||||||
s_selected_banned_idx = static_cast<int>(i);
|
s_selected_banned_idx = static_cast<int>(i);
|
||||||
}
|
}
|
||||||
@@ -898,7 +900,7 @@ void RenderPeersTab(App* app)
|
|||||||
|
|
||||||
if (i < state.bannedPeers.size() - 1) {
|
if (i < state.bannedPeers.size() - 1) {
|
||||||
ImVec2 divStart = ImGui::GetCursorScreenPos();
|
ImVec2 divStart = ImGui::GetCursorScreenPos();
|
||||||
dl->AddLine(ImVec2(divStart.x + pad + 8, divStart.y),
|
dl->AddLine(ImVec2(divStart.x + pad + 8 * dp, divStart.y),
|
||||||
ImVec2(divStart.x + innerW - pad, divStart.y),
|
ImVec2(divStart.x + innerW - pad, divStart.y),
|
||||||
IM_COL32(255, 255, 255, 15));
|
IM_COL32(255, 255, 255, 15));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ void RequestPaymentDialog::render(App* app)
|
|||||||
|
|
||||||
// Amount (optional)
|
// Amount (optional)
|
||||||
ImGui::Text("%s", TR("request_amount"));
|
ImGui::Text("%s", TR("request_amount"));
|
||||||
ImGui::SetNextItemWidth(amountInput.width);
|
ImGui::SetNextItemWidth(amountInput.width * Layout::dpiScale());
|
||||||
if (ImGui::InputDouble("##Amount", &s_amount, 0.1, 1.0, "%.8f")) {
|
if (ImGui::InputDouble("##Amount", &s_amount, 0.1, 1.0, "%.8f")) {
|
||||||
s_uri_dirty = true;
|
s_uri_dirty = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -539,6 +539,8 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
}
|
}
|
||||||
if (s_tx_status.empty() && !s_sending) return;
|
if (s_tx_status.empty() && !s_sending) return;
|
||||||
|
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
|
||||||
// Drive error styling from the authoritative result flag, not English substrings in a
|
// Drive error styling from the authoritative result flag, not English substrings in a
|
||||||
// (translatable) status string — otherwise a failed send renders as a green success
|
// (translatable) status string — otherwise a failed send renders as a green success
|
||||||
// under a non-English locale.
|
// under a non-English locale.
|
||||||
@@ -547,8 +549,8 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
// ---- ERROR: absolute-positioned overlay, does not displace layout ----
|
// ---- ERROR: absolute-positioned overlay, does not displace layout ----
|
||||||
if (is_error) {
|
if (is_error) {
|
||||||
float pad = Layout::spacingLg();
|
float pad = Layout::spacingLg();
|
||||||
float btnH = std::max(schema::UI().drawElement("tabs.send", "error-btn-min-height").size, schema::UI().drawElement("tabs.send", "error-btn-height").size);
|
float btnH = std::max(schema::UI().drawElement("tabs.send", "error-btn-min-height").size, schema::UI().drawElement("tabs.send", "error-btn-height").size) * dp;
|
||||||
float textWrapW = w - pad * 2 - schema::UI().drawElement("tabs.send", "error-icon-inset").size; // icon space
|
float textWrapW = w - pad * 2 - schema::UI().drawElement("tabs.send", "error-icon-inset").size * dp; // icon space
|
||||||
ImVec2 textSz = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, textWrapW, s_tx_status.c_str());
|
ImVec2 textSz = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, textWrapW, s_tx_status.c_str());
|
||||||
float contentH = textSz.y + Layout::spacingMd() + btnH + pad * 2;
|
float contentH = textSz.y + Layout::spacingMd() + btnH + pad * 2;
|
||||||
|
|
||||||
@@ -572,7 +574,7 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
DrawGlassPanel(fgDl, pMin, pMax, errGlass);
|
DrawGlassPanel(fgDl, pMin, pMax, errGlass);
|
||||||
|
|
||||||
// Red accent bar on left
|
// Red accent bar on left
|
||||||
fgDl->AddRectFilled(pMin, ImVec2(pMin.x + schema::UI().drawElement("tabs.send", "error-accent-bar-width").size, pMax.y), Error(), errGlass.rounding);
|
fgDl->AddRectFilled(pMin, ImVec2(pMin.x + schema::UI().drawElement("tabs.send", "error-accent-bar-width").size * dp, pMax.y), Error(), errGlass.rounding);
|
||||||
|
|
||||||
float ix = pMin.x + pad;
|
float ix = pMin.x + pad;
|
||||||
float iy = pMin.y + pad;
|
float iy = pMin.y + pad;
|
||||||
@@ -583,18 +585,18 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
const char* errIcon = ICON_MD_ERROR;
|
const char* errIcon = ICON_MD_ERROR;
|
||||||
ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, errIcon);
|
ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, errIcon);
|
||||||
fgDl->AddText(iconFont, iconFont->LegacySize,
|
fgDl->AddText(iconFont, iconFont->LegacySize,
|
||||||
ImVec2(ix + schema::UI().drawElement("tabs.send", "error-icon-x-offset").size, iy + body2->LegacySize * 0.5f - iSz.y * 0.5f),
|
ImVec2(ix + schema::UI().drawElement("tabs.send", "error-icon-x-offset").size * dp, iy + body2->LegacySize * 0.5f - iSz.y * 0.5f),
|
||||||
Error(), errIcon);
|
Error(), errIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error text (wrapped)
|
// Error text (wrapped)
|
||||||
fgDl->AddText(body2, body2->LegacySize, ImVec2(ix + schema::UI().drawElement("tabs.send", "error-text-x-offset").size, iy), Error(),
|
fgDl->AddText(body2, body2->LegacySize, ImVec2(ix + schema::UI().drawElement("tabs.send", "error-text-x-offset").size * dp, iy), Error(),
|
||||||
s_tx_status.c_str(), s_tx_status.c_str() + s_tx_status.size(), textWrapW);
|
s_tx_status.c_str(), s_tx_status.c_str() + s_tx_status.size(), textWrapW);
|
||||||
|
|
||||||
// Buttons row — use invisible window for interactive widgets on top of overlay
|
// Buttons row — use invisible window for interactive widgets on top of overlay
|
||||||
float btnY = iy + textSz.y + Layout::spacingMd();
|
float btnY = iy + textSz.y + Layout::spacingMd();
|
||||||
ImGui::SetNextWindowPos(ImVec2(ix, btnY));
|
ImGui::SetNextWindowPos(ImVec2(ix, btnY));
|
||||||
ImGui::SetNextWindowSize(ImVec2(w - pad * 2, btnH + schema::UI().drawElement("tabs.send", "error-btn-area-padding").size));
|
ImGui::SetNextWindowSize(ImVec2(w - pad * 2, btnH + schema::UI().drawElement("tabs.send", "error-btn-area-padding").size * dp));
|
||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||||
@@ -606,7 +608,7 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-bg-alpha").size)));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-bg-alpha").size)));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-hover-alpha").size)));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)schema::UI().drawElement("tabs.send", "error-btn-hover-alpha").size)));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, schema::UI().drawElement("tabs.send", "error-btn-rounding").size);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, schema::UI().drawElement("tabs.send", "error-btn-rounding").size * dp);
|
||||||
if (TactileSmallButton(TR("send_copy_error"), schema::UI().resolveFont("button"))) {
|
if (TactileSmallButton(TR("send_copy_error"), schema::UI().resolveFont("button"))) {
|
||||||
ImGui::SetClipboardText(s_tx_status.c_str());
|
ImGui::SetClipboardText(s_tx_status.c_str());
|
||||||
Notifications::instance().info(TR("send_error_copied"));
|
Notifications::instance().info(TR("send_error_copied"));
|
||||||
@@ -629,8 +631,8 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- SENDING / SUCCESS: inline progress card ----
|
// ---- SENDING / SUCCESS: inline progress card ----
|
||||||
float progCardH = schema::UI().drawElement("tabs.send", "progress-card-height").size;
|
float progCardH = schema::UI().drawElement("tabs.send", "progress-card-height").size * dp;
|
||||||
float progCardHTxid = schema::UI().drawElement("tabs.send", "progress-card-height-txid").size;
|
float progCardHTxid = schema::UI().drawElement("tabs.send", "progress-card-height-txid").size * dp;
|
||||||
float progH = s_result_txid.empty() ? progCardH : progCardHTxid;
|
float progH = s_result_txid.empty() ? progCardH : progCardHTxid;
|
||||||
ImVec2 pMin(x, y);
|
ImVec2 pMin(x, y);
|
||||||
ImVec2 pMax(x + w, y + progH);
|
ImVec2 pMax(x + w, y + progH);
|
||||||
@@ -639,8 +641,8 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
progGlass.rounding = Layout::glassRounding() * schema::UI().drawElement("tabs.send", "progress-glass-rounding-ratio").size;
|
progGlass.rounding = Layout::glassRounding() * schema::UI().drawElement("tabs.send", "progress-glass-rounding-ratio").size;
|
||||||
DrawGlassPanel(dl, pMin, pMax, progGlass);
|
DrawGlassPanel(dl, pMin, pMax, progGlass);
|
||||||
|
|
||||||
float progPadX = schema::UI().drawElement("tabs.send", "progress-card-pad-x").size;
|
float progPadX = schema::UI().drawElement("tabs.send", "progress-card-pad-x").size * dp;
|
||||||
float progPadY = schema::UI().drawElement("tabs.send", "progress-card-pad-y").size;
|
float progPadY = schema::UI().drawElement("tabs.send", "progress-card-pad-y").size * dp;
|
||||||
float ix = pMin.x + progPadX;
|
float ix = pMin.x + progPadX;
|
||||||
float iy = pMin.y + progPadY;
|
float iy = pMin.y + progPadY;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
@@ -654,7 +656,7 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
ImVec2(ix, iy), Primary(), spinIcon);
|
ImVec2(ix, iy), Primary(), spinIcon);
|
||||||
double elapsed = ImGui::GetTime() - s_send_start_time;
|
double elapsed = ImGui::GetTime() - s_send_start_time;
|
||||||
snprintf(buf, sizeof(buf), TR("send_submitting"), elapsed);
|
snprintf(buf, sizeof(buf), TR("send_submitting"), elapsed);
|
||||||
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size, iy), OnSurface(), buf);
|
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size * dp, iy), OnSurface(), buf);
|
||||||
} else {
|
} else {
|
||||||
// Success checkmark
|
// Success checkmark
|
||||||
ImFont* iconFont = material::Type().iconMed();
|
ImFont* iconFont = material::Type().iconMed();
|
||||||
@@ -662,19 +664,19 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
|||||||
ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, checkIcon);
|
ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, checkIcon);
|
||||||
dl->AddText(iconFont, iconFont->LegacySize,
|
dl->AddText(iconFont, iconFont->LegacySize,
|
||||||
ImVec2(ix, iy), Success(), checkIcon);
|
ImVec2(ix, iy), Success(), checkIcon);
|
||||||
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size, iy), Success(), TR("send_tx_sent"));
|
dl->AddText(body2, body2->LegacySize, ImVec2(ix + iSz.x + schema::UI().drawElement("tabs.send", "progress-icon-text-gap").size * dp, iy), Success(), TR("send_tx_sent"));
|
||||||
|
|
||||||
if (!s_result_txid.empty()) {
|
if (!s_result_txid.empty()) {
|
||||||
float txY = iy + body2->LegacySize + schema::UI().drawElement("tabs.send", "txid-y-offset").size;
|
float txY = iy + body2->LegacySize + schema::UI().drawElement("tabs.send", "txid-y-offset").size * dp;
|
||||||
int txidThreshold = (int)schema::UI().drawElement("tabs.send", "txid-display-threshold").size;
|
int txidThreshold = (int)schema::UI().drawElement("tabs.send", "txid-display-threshold").size;
|
||||||
int txidTruncLen = (int)schema::UI().drawElement("tabs.send", "txid-trunc-len").size;
|
int txidTruncLen = (int)schema::UI().drawElement("tabs.send", "txid-trunc-len").size;
|
||||||
std::string dispTxid = (int)s_result_txid.length() > txidThreshold
|
std::string dispTxid = (int)s_result_txid.length() > txidThreshold
|
||||||
? s_result_txid.substr(0, txidTruncLen) + "..." + s_result_txid.substr(s_result_txid.length() - txidTruncLen)
|
? s_result_txid.substr(0, txidTruncLen) + "..." + s_result_txid.substr(s_result_txid.length() - txidTruncLen)
|
||||||
: s_result_txid;
|
: s_result_txid;
|
||||||
snprintf(buf, sizeof(buf), TR("send_txid_label"), dispTxid.c_str());
|
snprintf(buf, sizeof(buf), TR("send_txid_label"), dispTxid.c_str());
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(ix + schema::UI().drawElement("tabs.send", "txid-label-x-offset").size, txY),
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(ix + schema::UI().drawElement("tabs.send", "txid-label-x-offset").size * dp, txY),
|
||||||
OnSurfaceDisabled(), buf);
|
OnSurfaceDisabled(), buf);
|
||||||
ImGui::SetCursorScreenPos(ImVec2(pMax.x - schema::UI().drawElement("tabs.send", "txid-copy-btn-right-offset").size, txY - schema::UI().drawElement("tabs.send", "txid-copy-btn-y-offset").size));
|
ImGui::SetCursorScreenPos(ImVec2(pMax.x - schema::UI().drawElement("tabs.send", "txid-copy-btn-right-offset").size * dp, txY - schema::UI().drawElement("tabs.send", "txid-copy-btn-y-offset").size * dp));
|
||||||
if (TactileSmallButton(TR("copy"), schema::UI().resolveFont("button"))) {
|
if (TactileSmallButton(TR("copy"), schema::UI().resolveFont("button"))) {
|
||||||
ImGui::SetClipboardText(s_result_txid.c_str());
|
ImGui::SetClipboardText(s_result_txid.c_str());
|
||||||
Notifications::instance().info(TR("send_txid_copied"));
|
Notifications::instance().info(TR("send_txid_copied"));
|
||||||
@@ -726,6 +728,7 @@ void RenderSendConfirmPopup(App* app) {
|
|||||||
float popupAvailW = ImGui::GetMainViewport()->Size.x * S.drawElement("tabs.send", "confirm-popup-width-ratio").size;
|
float popupAvailW = ImGui::GetMainViewport()->Size.x * S.drawElement("tabs.send", "confirm-popup-width-ratio").size;
|
||||||
float popupW = std::min(schema::UI().drawElement("tabs.send", "confirm-popup-max-width").size, popupAvailW);
|
float popupW = std::min(schema::UI().drawElement("tabs.send", "confirm-popup-max-width").size, popupAvailW);
|
||||||
float popVs = Layout::vScale();
|
float popVs = Layout::vScale();
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
material::OverlayDialogSpec ov;
|
material::OverlayDialogSpec ov;
|
||||||
ov.title = TR("confirm_send"); ov.p_open = nullptr;
|
ov.title = TR("confirm_send"); ov.p_open = nullptr;
|
||||||
ov.style = material::OverlayStyle::BlurFloat;
|
ov.style = material::OverlayStyle::BlurFloat;
|
||||||
@@ -797,7 +800,12 @@ void RenderSendConfirmPopup(App* app) {
|
|||||||
|
|
||||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_amount_details"));
|
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("send_amount_details"));
|
||||||
ImVec2 cMin = ImGui::GetCursorScreenPos();
|
ImVec2 cMin = ImGui::GetCursorScreenPos();
|
||||||
|
// Floor the row step at the *actual* scaled caption-row height (+ a small dp-scaled
|
||||||
|
// gap) so the Fee/Total divider — drawn at rowStep*0.5 below the Fee baseline —
|
||||||
|
// clears the (already-DPI-scaled) fee text instead of striking through it at HiDPI.
|
||||||
|
// capFont->LegacySize is already DPI-scaled; do not scale it again.
|
||||||
float rowStep = std::max(schema::UI().drawElement("tabs.send", "confirm-row-step-min").size, schema::UI().drawElement("tabs.send", "confirm-row-step").size * popVs);
|
float rowStep = std::max(schema::UI().drawElement("tabs.send", "confirm-row-step-min").size, schema::UI().drawElement("tabs.send", "confirm-row-step").size * popVs);
|
||||||
|
rowStep = std::max(rowStep, capFont->LegacySize + 6.0f * dp);
|
||||||
float configuredH = std::max(schema::UI().drawElement("tabs.send", "confirm-amount-card-min-height").size, schema::UI().drawElement("tabs.send", "confirm-amount-card-height").size * popVs);
|
float configuredH = std::max(schema::UI().drawElement("tabs.send", "confirm-amount-card-min-height").size, schema::UI().drawElement("tabs.send", "confirm-amount-card-height").size * popVs);
|
||||||
float contentH = Layout::spacingMd() * 2.0f + capFont->LegacySize * 2.0f + sub1->LegacySize + rowStep * 2.0f;
|
float contentH = Layout::spacingMd() * 2.0f + capFont->LegacySize * 2.0f + sub1->LegacySize + rowStep * 2.0f;
|
||||||
float cH = std::max(configuredH, contentH);
|
float cH = std::max(configuredH, contentH);
|
||||||
@@ -936,9 +944,10 @@ static bool RenderZeroBalanceCTA(App* app, ImDrawList* dl, float width) {
|
|||||||
|
|
||||||
ImFont* sub1 = Type().subtitle1();
|
ImFont* sub1 = Type().subtitle1();
|
||||||
ImFont* capFont = Type().caption();
|
ImFont* capFont = Type().caption();
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
|
||||||
ImVec2 ctaMin = ImGui::GetCursorScreenPos();
|
ImVec2 ctaMin = ImGui::GetCursorScreenPos();
|
||||||
float ctaH = schema::UI().drawElement("tabs.send", "cta-height").size;
|
float ctaH = schema::UI().drawElement("tabs.send", "cta-height").size * dp;
|
||||||
ImVec2 ctaMax(ctaMin.x + width, ctaMin.y + ctaH);
|
ImVec2 ctaMax(ctaMin.x + width, ctaMin.y + ctaH);
|
||||||
GlassPanelSpec ctaGlass;
|
GlassPanelSpec ctaGlass;
|
||||||
ctaGlass.rounding = Layout::glassRounding();
|
ctaGlass.rounding = Layout::glassRounding();
|
||||||
@@ -952,7 +961,7 @@ static bool RenderZeroBalanceCTA(App* app, ImDrawList* dl, float width) {
|
|||||||
TR("send_switch_to_receive"));
|
TR("send_switch_to_receive"));
|
||||||
cy += capFont->LegacySize + Layout::spacingMd();
|
cy += capFont->LegacySize + Layout::spacingMd();
|
||||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||||
if (TactileButton(TR("send_go_to_receive"), ImVec2(schema::UI().drawElement("tabs.send", "cta-button-width").size, schema::UI().drawElement("tabs.send", "cta-button-height").size), schema::UI().resolveFont("button"))) {
|
if (TactileButton(TR("send_go_to_receive"), ImVec2(schema::UI().drawElement("tabs.send", "cta-button-width").size * dp, schema::UI().drawElement("tabs.send", "cta-button-height").size * dp), schema::UI().resolveFont("button"))) {
|
||||||
app->setCurrentPage(NavPage::Receive);
|
app->setCurrentPage(NavPage::Receive);
|
||||||
}
|
}
|
||||||
ImGui::SetCursorScreenPos(ImVec2(ctaMin.x, ctaMax.y + Layout::spacingLg()));
|
ImGui::SetCursorScreenPos(ImVec2(ctaMin.x, ctaMax.y + Layout::spacingLg()));
|
||||||
@@ -1145,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,
|
||||||
@@ -1164,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());
|
||||||
|
|
||||||
@@ -1242,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
|
||||||
@@ -1419,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;
|
||||||
|
|
||||||
@@ -1492,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;
|
||||||
|
|||||||
@@ -478,7 +478,12 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
|||||||
|
|
||||||
// Confirmation dialog
|
// Confirmation dialog
|
||||||
if (s_confirm_clear_ztx) {
|
if (s_confirm_clear_ztx) {
|
||||||
if (material::BeginOverlayDialog(TR("confirm_clear_ztx_title"), &s_confirm_clear_ztx, 480.0f, 0.94f)) {
|
// Distinct idSuffix: this confirm renders nested inside (and the same frame as) the parent
|
||||||
|
// settings dialog, so it must not share the default ##OverlayDialogContent key — otherwise it
|
||||||
|
// inherits the parent's OverlayCardState (incl. the sticky overflow flag) and collides on the
|
||||||
|
// child window id.
|
||||||
|
if (material::BeginOverlayDialog(TR("confirm_clear_ztx_title"), &s_confirm_clear_ztx, 480.0f,
|
||||||
|
0.94f, 0.85f, "settings_clearztx")) {
|
||||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.6f, 0.0f, 1.0f));
|
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.6f, 0.0f, 1.0f));
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ void ShieldDialog::render(App* app)
|
|||||||
|
|
||||||
// Fee
|
// Fee
|
||||||
ImGui::Text("%s", TR("fee_label"));
|
ImGui::Text("%s", TR("fee_label"));
|
||||||
ImGui::SetNextItemWidth(feeInput.width);
|
ImGui::SetNextItemWidth(feeInput.width * Layout::dpiScale());
|
||||||
ImGui::InputDouble("##Fee", &s_fee, 0.0001, 0.001, "%.8f");
|
ImGui::InputDouble("##Fee", &s_fee, 0.0001, 0.001, "%.8f");
|
||||||
if (s_fee < 0.0) s_fee = 0.0; // no negative fee
|
if (s_fee < 0.0) s_fee = 0.0; // no negative fee
|
||||||
if (s_fee > 1.0) s_fee = 1.0; // guard a fat-fingered huge fee (mirrors utxo clamp)
|
if (s_fee > 1.0) s_fee = 1.0; // guard a fat-fingered huge fee (mirrors utxo clamp)
|
||||||
@@ -159,7 +159,7 @@ void ShieldDialog::render(App* app)
|
|||||||
|
|
||||||
// UTXO limit
|
// UTXO limit
|
||||||
ImGui::Text("%s", TR("shield_utxo_limit"));
|
ImGui::Text("%s", TR("shield_utxo_limit"));
|
||||||
ImGui::SetNextItemWidth(utxoInput.width);
|
ImGui::SetNextItemWidth(utxoInput.width * Layout::dpiScale());
|
||||||
ImGui::InputInt("##Limit", &s_utxo_limit);
|
ImGui::InputInt("##Limit", &s_utxo_limit);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextDisabled("%s", TR("shield_max_utxos"));
|
ImGui::TextDisabled("%s", TR("shield_max_utxos"));
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void TransactionDetailsDialog::render(App* app)
|
|||||||
char txid_buf[128];
|
char txid_buf[128];
|
||||||
strncpy(txid_buf, tx.txid.c_str(), sizeof(txid_buf) - 1);
|
strncpy(txid_buf, tx.txid.c_str(), sizeof(txid_buf) - 1);
|
||||||
txid_buf[sizeof(txid_buf) - 1] = '\0';
|
txid_buf[sizeof(txid_buf) - 1] = '\0';
|
||||||
ImGui::SetNextItemWidth(txidInput.width);
|
ImGui::SetNextItemWidth(txidInput.width); // negative = fill, reserving |width| px for the Copy button (a content-region margin, not raw px — must NOT be dpi-scaled)
|
||||||
ImGui::InputText("##TxID", txid_buf, sizeof(txid_buf), ImGuiInputTextFlags_ReadOnly);
|
ImGui::InputText("##TxID", txid_buf, sizeof(txid_buf), ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (material::StyledButton("Copy##TxID", ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
if (material::StyledButton("Copy##TxID", ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||||
@@ -139,13 +139,14 @@ void TransactionDetailsDialog::render(App* app)
|
|||||||
char addr_buf[512];
|
char addr_buf[512];
|
||||||
strncpy(addr_buf, tx.address.c_str(), sizeof(addr_buf) - 1);
|
strncpy(addr_buf, tx.address.c_str(), sizeof(addr_buf) - 1);
|
||||||
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
||||||
|
// width is a negative fill-with-right-margin sentinel — keep unscaled; only height is raw px.
|
||||||
ImGui::InputTextMultiline("##Address", addr_buf, sizeof(addr_buf),
|
ImGui::InputTextMultiline("##Address", addr_buf, sizeof(addr_buf),
|
||||||
ImVec2(addrInput.width, addrInput.height > 0 ? addrInput.height : 50), ImGuiInputTextFlags_ReadOnly);
|
ImVec2(addrInput.width, (addrInput.height > 0 ? addrInput.height : 50) * Layout::dpiScale()), ImGuiInputTextFlags_ReadOnly);
|
||||||
} else {
|
} else {
|
||||||
char addr_buf[128];
|
char addr_buf[128];
|
||||||
strncpy(addr_buf, tx.address.c_str(), sizeof(addr_buf) - 1);
|
strncpy(addr_buf, tx.address.c_str(), sizeof(addr_buf) - 1);
|
||||||
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
||||||
ImGui::SetNextItemWidth(addrInput.width);
|
ImGui::SetNextItemWidth(addrInput.width); // negative fill sentinel — must NOT be dpi-scaled
|
||||||
ImGui::InputText("##Address", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
|
ImGui::InputText("##Address", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|||||||
@@ -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