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) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 23:12:50 +02:00
parent 604ce635ec
commit 1bb4f2e0f8
+25 -7
View File
@@ -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();