perf(ui): memoize the node-status banner ellipsis
While the node-status banner shows, its detail line was fitted every frame by popping one character at a time and re-measuring the whole "· " + detail + "…" string: O(L^2) per frame in the detail length L, which adds up on the ~1.5 KB Windows crash report. The pop also left a dangling UTF-8 lead byte before the "…", so a cut in non-ASCII text drew replacement characters in place of the ellipsis. - util::textfit::ellipsizeTail builds the same string in O(L + m log m), m being the length of the line the cut lands in: ImGui measures multi-line text as its widest line, so the old loop could only stop inside the first line that overflows on its own. Byte-identical for ASCII; the cut now lands on a code-point boundary. - EllipsisFitCache keeps the result until the detail, budget, font size, font (by FontId) or rasterizer density changes, so an unchanged frame costs one string compare. - When ImGui would draw a line wider than the per-line split predicts (a malformed byte joined with the following newline, as in CP-1252 output from the Windows daemon), the old whole-string loop is used for that text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+34
-14
@@ -75,6 +75,7 @@
|
||||
#include "util/connect_stall.h"
|
||||
#include "util/platform.h"
|
||||
#include "util/text_format.h"
|
||||
#include "util/text_fit.h"
|
||||
#include "util/payment_uri.h"
|
||||
#include "util/seed_phrase.h"
|
||||
#include "util/texture_loader.h"
|
||||
@@ -2683,20 +2684,39 @@ void App::renderNodeStatusBanner()
|
||||
ImGui::SetCursorPosY(txtCy);
|
||||
const float budget = winW - ImGui::GetCursorPosX() - actionReserve;
|
||||
if (budget > ImGui::CalcTextSize("W").x) {
|
||||
const std::string prefix = "\xC2\xB7 "; // "· "
|
||||
std::string detail = banner.detail;
|
||||
std::string shown = prefix + detail;
|
||||
if (ImGui::CalcTextSize(shown.c_str()).x > budget) {
|
||||
const std::string ell = "\xE2\x80\xA6"; // "…"
|
||||
while (!detail.empty() &&
|
||||
ImGui::CalcTextSize((prefix + detail + ell).c_str()).x > budget) {
|
||||
detail.pop_back();
|
||||
while (!detail.empty() &&
|
||||
(static_cast<unsigned char>(detail.back()) & 0xC0) == 0x80)
|
||||
detail.pop_back(); // drop the whole trailing UTF-8 code point
|
||||
}
|
||||
shown = prefix + detail + ell;
|
||||
}
|
||||
// "· " + detail, ellipsized with "…" to the budget: the same string the old pop-one-char loop built
|
||||
// (O(L^2) per frame on the ~1.5 KB Windows crash report), now valid UTF-8 at the cut. Memoized per
|
||||
// (detail, budget, font size, FontId + rasterizer density), so an unchanged frame costs one compare.
|
||||
static util::textfit::EllipsisFitCache s_detailFit; // UI thread only (render path)
|
||||
ImFont* detailFont = ImGui::GetFont();
|
||||
const std::string& shown = s_detailFit.get(
|
||||
banner.detail, budget, detailFont->FontId, ImGui::GetFontSize(),
|
||||
detailFont->CurrentRasterizerDensity,
|
||||
[&](const std::string& detail) {
|
||||
const std::string prefix = "\xC2\xB7 "; // "· "
|
||||
const std::string ell = "\xE2\x80\xA6"; // "…"
|
||||
std::string r = util::textfit::ellipsizeTail(prefix, detail, ell, budget,
|
||||
[](const char* b, const char* e) { return ImGui::CalcTextSize(b, e).x; });
|
||||
if (ImGui::CalcTextSize(r.c_str()).x > budget) {
|
||||
// ellipsizeTail splits lines on the raw '\n' byte, but ImGui decodes a malformed UTF-8
|
||||
// byte together with a following '\n' (e.g. CP-1252 bytes E9 0D 0A in the Windows daemon tail)
|
||||
// and draws one joined, wider line. Fall back to the old whole-string loop, which measures
|
||||
// exactly what is drawn (also reached when nothing fits; same result there).
|
||||
std::string d = detail;
|
||||
r = prefix + d;
|
||||
if (ImGui::CalcTextSize(r.c_str()).x > budget) {
|
||||
while (!d.empty() &&
|
||||
ImGui::CalcTextSize((prefix + d + ell).c_str()).x > budget) {
|
||||
d.pop_back();
|
||||
while (!d.empty() &&
|
||||
(static_cast<unsigned char>(d.back()) & 0xC0) == 0x80)
|
||||
d.pop_back(); // drop the whole trailing UTF-8 code point
|
||||
}
|
||||
r = prefix + d + ell;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
});
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, m::OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(shown.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
Reference in New Issue
Block a user