diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 73c3519..8d70981 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -734,9 +734,9 @@ static void RenderPortfolioEditor(App* app) // ---------------- Full-window blur overlay (shared framework) ---------------- // Backdrop + capture-once, floating card, plain heading, outside-click dismiss and effect - // suppression are owned by material::BeginOverlayDialog (BlurFloat). Esc still closes here; - // Esc/outside-click discard (only Close commits); LatchBlurOverlayActive (App::render) handles - // the acrylic re-capture on close. + // suppression are owned by material::BeginOverlayDialog (BlurFloat). Every dismiss gesture + // (Close button, Esc, outside-click, switching/adding/deleting groups) auto-saves the current + // group via pfCommitIfNeeded; LatchBlurOverlayActive (App::render) does the acrylic re-capture. material::OverlayDialogSpec ov; ov.title = TR("portfolio_manage_title"); ov.p_open = &s_pfEdit.open; @@ -745,11 +745,18 @@ static void RenderPortfolioEditor(App* app) ov.cardHeight = 760.0f; ov.idSuffix = "pf"; if (!material::BeginOverlayDialog(ov)) return; + // Outside-click dismiss is handled inside BeginOverlayDialog, which clears s_pfEdit.open mid-frame + // (the card still draws one final frame). Auto-save now, before that frame: the placeholder GC in + // mktDrawPortfolio already ran this frame while open was still true, so entry indices haven't + // shifted under us and pfCommitIfNeeded targets the right slot. + if (!s_pfEdit.open) pfCommitIfNeeded(app); // A first Escape should dismiss only an open popup (e.g. the custom-color picker); don't also tear - // down the whole editor — which discards uncommitted edits — while a popup is capturing the key. + // down the whole editor while a popup is capturing the key. Otherwise Escape auto-saves and closes. if (ImGui::IsKeyPressed(ImGuiKey_Escape) && - !ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel)) + !ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel)) { + pfCommitIfNeeded(app); s_pfEdit.open = false; + } { // Clamp/validate the selection. The in-editor delete already reloads the working state, but if the // list shrank via any other path, reload after clamping so a later commit can't overlay stale @@ -845,7 +852,9 @@ static void RenderPortfolioEditor(App* app) ImGui::PopID(); } if (delRow >= 0) { - // Operates on the stored entries; any uncommitted working edits are discarded. + // Auto-save edits to a *different* selected group before mutating the list; only the + // deleted row's own working state is dropped. (Re-read entries after the commit.) + if (delRow != s_pfEdit.sel) pfCommitIfNeeded(app); auto es = settings->getPortfolioEntries(); if (delRow < (int)es.size()) { es.erase(es.begin() + delRow); @@ -856,14 +865,16 @@ static void RenderPortfolioEditor(App* app) PortfolioBeginEdit(app, s_pfEdit.sel); } } else if (clickedSel != -999) { - PortfolioBeginEdit(app, clickedSel); // switching groups discards uncommitted edits + pfCommitIfNeeded(app); // auto-save the current group before switching + PortfolioBeginEdit(app, clickedSel); } } ImGui::EndChild(); ImGui::Dummy(ImVec2(0, Layout::spacingSm())); // Add immediately creates a persisted "Untitled" group and selects it for editing. if (material::TactileButton(TR("portfolio_add_entry"), ImVec2(masterW, addH))) { - // Adding switches to a new group; uncommitted edits to the current one are discarded. + // Auto-save the current group, then start a fresh one selected for editing. + pfCommitIfNeeded(app); auto es = settings->getPortfolioEntries(); config::Settings::PortfolioEntry ne; ne.label = TR("portfolio_untitled");