fix: large-wallet sync starvation + shutdown/console-flash UX (Windows full node) #2

Open
DanS wants to merge 12 commits from fix/balance-poll-sync-contention into dev
4 changed files with 68 additions and 23 deletions
Showing only changes of commit b37d3d97b6 - Show all commits

View File

@@ -179,6 +179,17 @@ inline float kSidePanelWidthRatio() { return schema::UI().drawElement("panels",
// [layout] content-max-width; default is generous so data-dense screens stay comfortable.
inline float kContentMaxWidth() { return schema::UI().drawElement("layout", "content-max-width").sizeOr(1600.0f) * dpiScale(); }
// Shared compose-card envelope for the Send + Receive tabs (and any tab wanting the same box): fill the
// available column up to the content-max-width cap, then center the leftover as margin. Both tabs MUST
// derive their card width/offset from this so the two envelopes stay byte-for-byte identical — they
// previously drifted (Send capped at 760dp, Receive at 860dp), so the Send card rendered narrower than
// Receive on any window wider than ~860dp. Returns {width, offsetX} in the same units as availW.
struct CardBox { float width; float offsetX; };
inline CardBox mainComposeCardBox(float availW) {
float w = std::min(availW, kContentMaxWidth());
return CardBox{ w, std::max(0.0f, (availW - w) * 0.5f) };
}
inline float kTableMinHeight() { return schema::UI().drawElement("panels", "table").getFloat("min-height", 150.0f) * dpiScale(); }
inline float kTableHeightRatio() { return schema::UI().drawElement("panels", "table").getFloat("height-ratio", 0.45f); }

View File

@@ -544,11 +544,16 @@ inline bool TactileButton(const char* label, const ImVec2& size = ImVec2(0, 0),
ImVec2 bMin = ImGui::GetItemRectMin();
ImVec2 bMax = ImGui::GetItemRectMax();
// For icon fonts, manually draw centered icon after getting button rect
// For icon fonts, manually draw centered icon after getting button rect. Measure/draw only the
// VISIBLE label (up to the "##id" separator): CalcTextSizeA/AddText don't strip "##" the way
// ImGui's own text render does, so an id suffix like "##pickContact" would inflate textSz and
// shove the glyph left off-center (and try to draw the notdef id chars).
if (isIconFont && size.x > 0 && size.y > 0) {
ImVec2 textSz = useFont->CalcTextSizeA(useFont->LegacySize, FLT_MAX, 0, label);
const char* labelEnd = label;
while (*labelEnd && !(labelEnd[0] == '#' && labelEnd[1] == '#')) ++labelEnd;
ImVec2 textSz = useFont->CalcTextSizeA(useFont->LegacySize, FLT_MAX, 0, label, labelEnd);
ImVec2 textPos(bMin.x + (size.x - textSz.x) * 0.5f, bMin.y + (size.y - textSz.y) * 0.5f);
dl->AddText(useFont, useFont->LegacySize, textPos, ImGui::GetColorU32(ImGuiCol_Text), label);
dl->AddText(useFont, useFont->LegacySize, textPos, ImGui::GetColorU32(ImGuiCol_Text), label, labelEnd);
}
float rounding = ImGui::GetStyle().FrameRounding;

View File

@@ -175,6 +175,11 @@ static void RenderAddressDropdown(App* app, float width) {
}
}
// Combo/button widths first — the preview truncates to the combo's real pixel width below.
float copyBtnW = std::max(schema::UI().drawElement("tabs.receive", "copy-btn-min-width").size, schema::UI().drawElement("tabs.receive", "copy-btn-width").size * Layout::hScale(width));
float newBtnW = std::max(schema::UI().drawElement("tabs.receive", "new-btn-min-width").size, schema::UI().drawElement("tabs.receive", "new-btn-width").size * Layout::hScale(width));
float dropdownW = width - copyBtnW - newBtnW - Layout::spacingSm() * 2;
// Build preview string
if (!app->isConnected()) {
s_source_preview = TR(app->isLiteBuild() ? "lite_no_wallet_short" : "not_connected");
@@ -183,18 +188,27 @@ static void RenderAddressDropdown(App* app, float width) {
const auto& addr = state.addresses[s_selected_address_idx];
bool isZ = addr.type == "shielded";
const char* tag = isZ ? "[Z]" : "[T]";
std::string trunc = util::truncateMiddle(addr.address,
static_cast<int>(std::max(schema::UI().drawElement("tabs.receive", "addr-preview-trunc-min").size, width / schema::UI().drawElement("tabs.receive", "addr-preview-trunc-divisor").size)));
snprintf(buf, sizeof(buf), "%s %s \xe2\x80\x94 %.8f %s",
tag, trunc.c_str(), addr.balance, DRAGONX_TICKER);
// Reserve pixel room for the tag prefix and the trailing balance, then middle-truncate the
// address to whatever remains — measured with the combo's own Body2 font. Char-count
// truncation kept MORE chars as the column widened, so at 150% the scaled font overflowed
// and the combo hard-clipped "— 12.00000000 DRGX" to "— 1"; measuring in pixels keeps the
// balance visible at any scale.
ImFont* comboFont = Type().getFont(TypeStyle::Body2);
float comboFontSz = comboFont->LegacySize;
char prefix[16]; snprintf(prefix, sizeof(prefix), "%s ", tag);
char suffix[64]; snprintf(suffix, sizeof(suffix), " \xe2\x80\x94 %.8f %s", addr.balance, DRAGONX_TICKER);
float fixedW = comboFont->CalcTextSizeA(comboFontSz, FLT_MAX, 0.0f, prefix).x
+ comboFont->CalcTextSizeA(comboFontSz, FLT_MAX, 0.0f, suffix).x;
// Combo interior = dropdownW minus its dropdown-arrow button (~frame height) and both frame paddings.
float addrBudget = dropdownW - ImGui::GetFrameHeight() - ImGui::GetStyle().FramePadding.x * 2.0f - fixedW;
if (addrBudget < 24.0f) addrBudget = 24.0f; // floor: truncate to a stub rather than overflow
std::string trunc = material::TruncateToWidth(addr.address, comboFont, comboFontSz, addrBudget);
snprintf(buf, sizeof(buf), "%s%s%s", prefix, trunc.c_str(), suffix);
s_source_preview = buf;
} else {
s_source_preview = TR("select_receiving_address");
}
float copyBtnW = std::max(schema::UI().drawElement("tabs.receive", "copy-btn-min-width").size, schema::UI().drawElement("tabs.receive", "copy-btn-width").size * Layout::hScale(width));
float newBtnW = std::max(schema::UI().drawElement("tabs.receive", "new-btn-min-width").size, schema::UI().drawElement("tabs.receive", "new-btn-width").size * Layout::hScale(width));
float dropdownW = width - copyBtnW - newBtnW - Layout::spacingSm() * 2;
ImGui::SetNextItemWidth(dropdownW);
ImGui::PushFont(Type().getFont(TypeStyle::Body2));
if (ImGui::BeginCombo("##RecvAddr", s_source_preview.c_str())) {
@@ -584,12 +598,14 @@ void RenderReceiveTab(App* app)
// ================================================================
{
// Cap + center the card so the address/amount column stops stretching while the
// QR plateaus. cardW never exceeds formW (only shrinks); leftover becomes margin.
// QR plateaus. Shared with the Send tab via mainComposeCardBox() so the two card
// envelopes are identical (they must never drift in width/position again).
// The RECENT RECEIVED list below stays on the uncapped formW (handled separately).
float cardDp = Layout::dpiScale();
float cardW = std::min(formW, 860.0f * cardDp);
Layout::CardBox cardBox = Layout::mainComposeCardBox(formW);
float cardW = cardBox.width;
float cardLeftX = ImGui::GetCursorScreenPos().x;
float cardOffsetX = std::max(0.0f, (formW - cardW) * 0.5f);
float cardOffsetX = cardBox.offsetX;
if (cardOffsetX > 0.0f)
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + cardOffsetX);
@@ -911,7 +927,13 @@ void RenderReceiveTab(App* app)
{
float btnGap = Layout::spacingMd();
float btnH = std::max(schema::UI().drawElement("tabs.receive", "action-btn-min-height").size, schema::UI().drawElement("tabs.receive", "action-btn-height").size * vScale);
float otherBtnW = std::max(S.drawElement("tabs.receive", "action-btn-min-width").size, innerW * S.drawElement("tabs.receive", "action-btn-width-ratio").size);
// Justify the footer edge-to-edge like Send's [Review Send][Cancel] row instead of packing
// fixed-width buttons from the left (which left a large dead gap on the right). Split innerW
// into equal shares over the live button count: 2 by default (Clear Request + Explorer),
// 4 when an amount is requested (+ Copy URI + Share).
int nBtns = (s_request_amount > 0 ? 2 : 0) + 2;
float otherBtnW = std::max(S.drawElement("tabs.receive", "action-btn-min-width").size,
(innerW - (nBtns - 1) * btnGap) / (float)nBtns);
bool firstBtn = true;

View File

@@ -1272,12 +1272,13 @@ void RenderSendTab(App* app)
float contentStartY = ImGui::GetCursorPosY();
float formAvailW = ImGui::GetContentRegionAvail().x;
// The compose form reads best as a centered fixed-width column, not edge-to-edge.
// Cap the card to a readable form width and center it; the recent-sends list below
// deliberately keeps the full column width (formAvailW).
const float sendDp = Layout::dpiScale();
float formCardW = std::min(formAvailW, 760.0f * sendDp);
float formOffsetX = std::max(0.0f, (formAvailW - formCardW) * 0.5f);
// Fill the available column up to the content-max-width cap, then center. Shared with the
// Receive tab via mainComposeCardBox() so the two card envelopes are identical in width and
// position (Send previously capped at 760dp vs Receive's 860dp, so Send rendered narrower).
// The recent-sends list below deliberately keeps the full column width (formAvailW).
Layout::CardBox formBox = Layout::mainComposeCardBox(formAvailW);
float formCardW = formBox.width;
float formOffsetX = formBox.offsetX;
float formW = formAvailW;
ImGui::BeginGroup();
@@ -1367,7 +1368,10 @@ void RenderSendTab(App* app)
float pasteW = std::max(schema::UI().drawElement("tabs.send", "paste-btn-min-width").size, colW * schema::UI().drawElement("tabs.send", "paste-btn-width-ratio").size);
float contactsW = ImGui::GetFrameHeight(); // compact square icon button for the contact picker
ImGui::PushItemWidth(colW - pasteW - contactsW - Layout::spacingSm() * 2.0f);
// Reserve the TWO real SameLine gaps (each = ItemSpacing.x) between input|Paste|icon.
// Reserving spacingSm (a smaller token) under-counted the gap, so the row overshot colW by
// ~2*(ItemSpacing.x - spacingSm) and the icon's right border clipped past the card edge.
ImGui::PushItemWidth(colW - pasteW - contactsW - ImGui::GetStyle().ItemSpacing.x * 2.0f);
// Show clipboard preview as transparent overlay when paste button is hovered
bool paste_hovered = false;
@@ -1425,9 +1429,12 @@ void RenderSendTab(App* app)
}
}
// Contact picker — pick a saved contact's address as the recipient.
// Contact picker — pick a saved contact's address as the recipient. Pin the height to the
// frame height (== the input + Paste height) so the larger iconMed font doesn't auto-size
// this square button taller than its row-mates. (Passing a non-zero size also routes
// TactileButton through its precise InvisibleButton + centered-glyph path.)
ImGui::SameLine();
if (material::TactileButton(ICON_MD_CONTACTS "##pickContact", ImVec2(contactsW, 0),
if (material::TactileButton(ICON_MD_CONTACTS "##pickContact", ImVec2(contactsW, ImGui::GetFrameHeight()),
material::Type().iconMed()))
ImGui::OpenPopup("##ContactPickerPopup");
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("send_contacts_button"));