From 1bb4f2e0f8d45d9a3b913282e20c506b9f0c3acf Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 5 May 2026 23:12:50 +0200 Subject: [PATCH] fix(ui): notification popup horizontal oscillation Tamano FIJO del popup (Always + SizeConstraints) y flags NoResize/NoMove para evitar feedback loop entre auto-resize del popup y TextWrapped/SameLine internos. Reemplaza GetWindowContentRegionMax() por offsets explicitos calculados a partir del ancho fijo, ya que ese valor fluctua frame a frame con padding/borders y provocaba el ensanche/encogido continuo. Co-Authored-By: Claude Opus 4.7 (1M context) --- cpp/functions/core/toast.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/cpp/functions/core/toast.cpp b/cpp/functions/core/toast.cpp index a5d10f38..246b4b7f 100644 --- a/cpp/functions/core/toast.cpp +++ b/cpp/functions/core/toast.cpp @@ -235,13 +235,23 @@ void toast_inbox_button(const char* id) { ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(spacing::sm, spacing::sm)); - ImGui::SetNextWindowSize(ImVec2(360.0f, 0.0f), ImGuiCond_Appearing); - if (ImGui::BeginPopup(popup_id)) { - // Header + // Tamano FIJO del popup (Always + SizeConstraints) para evitar feedback + // loop entre auto-resize del popup y TextWrapped/SameLine internos. + constexpr float kInboxW = 360.0f; + constexpr float kInboxH = 380.0f; + ImGui::SetNextWindowSize(ImVec2(kInboxW, kInboxH), ImGuiCond_Always); + ImGui::SetNextWindowSizeConstraints(ImVec2(kInboxW, kInboxH), + ImVec2(kInboxW, kInboxH)); + if (ImGui::BeginPopup(popup_id, ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoMove)) { + // Header — usar offset desde la izquierda calculado a partir del ancho + // FIJO del popup, NO de GetWindowContentRegionMax() (que cambia frame + // a frame con padding y provoca oscilacion horizontal). + const float content_w = kInboxW - 2.0f * spacing::sm; ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); ImGui::TextUnformatted("Notifications"); ImGui::PopStyleColor(); - ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 50.0f); + ImGui::SameLine(content_w - 50.0f); if (ImGui::SmallButton("Clear")) { toast_history_clear(); snapshot.clear(); @@ -254,7 +264,10 @@ void toast_inbox_button(const char* id) { ImGui::PopStyleColor(); } else { const auto now = std::chrono::steady_clock::now(); - ImGui::BeginChild("##inbox_list", ImVec2(0, 320.0f)); + // Reservar el espacio restante para la lista — alto fijo evita + // oscilacion vertical del popup. + ImGui::BeginChild("##inbox_list", ImVec2(content_w, 0.0f)); + const float list_w = ImGui::GetContentRegionAvail().x; // Mostrar mas reciente arriba for (auto it = snapshot.rbegin(); it != snapshot.rend(); ++it) { const auto& t = *it; @@ -262,9 +275,14 @@ void toast_inbox_button(const char* id) { ImGui::TextUnformatted(glyph_for(t.kind)); ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::TextWrapped("%s", t.text.c_str()); + // Wrap manual hasta x = list_w - 30 (espacio para el age), + // usando coordenadas del cursor relativas al window. + const float wrap_x = ImGui::GetCursorPosX() + (list_w - ImGui::GetCursorPosX()) - 36.0f; + ImGui::PushTextWrapPos(wrap_x); + ImGui::TextUnformatted(t.text.c_str()); + ImGui::PopTextWrapPos(); ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim); - ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 30.0f); + ImGui::SameLine(list_w - 30.0f); ImGui::TextUnformatted(format_age(t.created, now).c_str()); ImGui::PopStyleColor(); ImGui::Separator();