@@ -201,47 +201,60 @@ void LayoutManager::removeLayout(Layout* layout)
201201 return ;
202202 }
203203
204- // Store ID and state BEFORE any operations that might invalidate the pointer
204+ // Store state BEFORE any operations that might invalidate the pointer
205205 const QUuid layoutId = layout->id ();
206206 const bool wasActive = (m_activeLayout == layout);
207207 const QString filePath = layoutFilePath (layoutId);
208+ const QString systemPath = layout->systemSourcePath ();
208209
209210 // Remove from layouts list
210211 m_layouts.removeOne (layout);
211212
212- // Remove any assignments using stored ID (safe - ID is copied)
213- for (auto it = m_assignments.begin (); it != m_assignments.end ();) {
214- if (it.value () == layoutId) {
215- it = m_assignments.erase (it);
216- } else {
217- ++it;
218- }
219- }
213+ // Delete user layout file
214+ QFile::remove (filePath);
220215
221- // Remove from quick shortcuts using stored ID
222- for (auto it = m_quickLayoutShortcuts.begin (); it != m_quickLayoutShortcuts.end ();) {
223- if (it.value () == layoutId) {
224- it = m_quickLayoutShortcuts.erase (it);
225- } else {
226- ++it;
227- }
216+ // Clear stale pointer before deletion
217+ if (m_previousLayout == layout) {
218+ m_previousLayout = nullptr ;
228219 }
229220
230- // Update active layout if needed
231- if (wasActive) {
232- setActiveLayout (defaultLayout ());
233- }
221+ Q_EMIT layoutRemoved (layout);
222+ layout->deleteLater ();
234223
235- // Delete layout file (using stored path)
236- QFile::remove (filePath);
224+ // If this was a user override of a system layout, restore the system original.
225+ // Uses the stored system path — no filesystem scanning needed.
226+ Layout* restored = restoreSystemLayout (layoutId, systemPath);
237227
238- // Emit signals before deleting
239- Q_EMIT layoutRemoved (layout);
240- Q_EMIT layoutsChanged ();
228+ if (restored) {
229+ // System layout restored — assignments and shortcuts stay valid (same UUID).
230+ // If deleted layout was active, activate the restored system layout.
231+ if (wasActive) {
232+ setActiveLayout (restored);
233+ }
234+ } else {
235+ // Truly deleted — clean up assignments and shortcuts referencing this layout
236+ for (auto it = m_assignments.begin (); it != m_assignments.end ();) {
237+ if (it.value () == layoutId) {
238+ it = m_assignments.erase (it);
239+ } else {
240+ ++it;
241+ }
242+ }
241243
242- // Schedule deletion (safe - we've already removed from all containers)
243- layout->deleteLater ();
244+ for (auto it = m_quickLayoutShortcuts.begin (); it != m_quickLayoutShortcuts.end ();) {
245+ if (it.value () == layoutId) {
246+ it = m_quickLayoutShortcuts.erase (it);
247+ } else {
248+ ++it;
249+ }
250+ }
251+
252+ if (wasActive) {
253+ setActiveLayout (defaultLayout ());
254+ }
255+ }
244256
257+ Q_EMIT layoutsChanged ();
245258 saveAssignments ();
246259}
247260
@@ -745,6 +758,10 @@ void LayoutManager::loadLayoutsFromDirectory(const QString& directory)
745758 // if we find a duplicate, the new one is from user directory and should replace
746759 if (!layout->isSystemLayout () && existing->isSystemLayout ()) {
747760 // User layout overrides system layout - replace the existing one
761+ // Preserve the system origin path so we can restore on deletion
762+ if (layout->systemSourcePath ().isEmpty ()) {
763+ layout->setSystemSourcePath (existing->sourcePath ());
764+ }
748765 int index = m_layouts.indexOf (existing);
749766 disconnect (existing, &Layout::layoutModified, this , nullptr );
750767 m_layouts.replace (index, layout);
@@ -772,6 +789,13 @@ void LayoutManager::saveLayout(Layout* layout)
772789
773790 ensureLayoutDirectory ();
774791
792+ // If this is a system layout being saved to user dir for the first time,
793+ // capture the system origin path before toJson/sourcePath changes
794+ if (layout->isSystemLayout () && !layout->hasSystemOrigin ()) {
795+ layout->setSystemSourcePath (layout->sourcePath ());
796+ qCInfo (lcLayout) << " Captured system origin for" << layout->name () << " from=" << layout->sourcePath ();
797+ }
798+
775799 const QString filePath = layoutFilePath (layout->id ());
776800 QFile file (filePath);
777801
@@ -781,6 +805,7 @@ void LayoutManager::saveLayout(Layout* layout)
781805 return ;
782806 }
783807
808+ // toJson() includes systemSourcePath so it persists across daemon restarts
784809 QJsonDocument doc (layout->toJson ());
785810 const QByteArray data = doc.toJson (QJsonDocument::Indented);
786811
@@ -1037,6 +1062,39 @@ void LayoutManager::exportLayout(Layout* layout, const QString& filePath)
10371062 qCInfo (lcLayout) << " Successfully exported layout:" << layout->name () << " to" << filePath;
10381063}
10391064
1065+ Layout* LayoutManager::restoreSystemLayout (const QUuid& id, const QString& systemPath)
1066+ {
1067+ if (systemPath.isEmpty () || layoutById (id)) {
1068+ return nullptr ;
1069+ }
1070+
1071+ QFile file (systemPath);
1072+ if (!file.exists () || !file.open (QIODevice::ReadOnly)) {
1073+ qCWarning (lcLayout) << " System layout file missing:" << systemPath;
1074+ return nullptr ;
1075+ }
1076+
1077+ QJsonParseError parseError;
1078+ const auto doc = QJsonDocument::fromJson (file.readAll (), &parseError);
1079+ if (parseError.error != QJsonParseError::NoError) {
1080+ qCWarning (lcLayout) << " System layout parse error:" << systemPath << parseError.errorString ();
1081+ return nullptr ;
1082+ }
1083+
1084+ auto * layout = Layout::fromJson (doc.object (), this );
1085+ if (!layout || layout->id () != id) {
1086+ delete layout;
1087+ return nullptr ;
1088+ }
1089+
1090+ layout->setSourcePath (systemPath);
1091+ m_layouts.append (layout);
1092+ connect (layout, &Layout::layoutModified, this , [this , layout]() { saveLayout (layout); });
1093+ Q_EMIT layoutAdded (layout);
1094+ qCInfo (lcLayout) << " Restored system layout name=" << layout->name () << " from=" << systemPath;
1095+ return layout;
1096+ }
1097+
10401098void LayoutManager::ensureLayoutDirectory ()
10411099{
10421100 QDir dir (m_layoutDirectory);
0 commit comments