| 318 | // ============================================================================ |
| 319 | |
| 320 | void NotesTreePanel::focusNote(QTreeWidgetItem* noteItem) |
| 321 | { |
| 322 | if (m_focusedItem == noteItem) return; |
| 323 | |
| 324 | // Tear down the previous editor (if any). |
| 325 | // Note: QTreeWidget::removeItemWidget() deletes the widget it owns. |
| 326 | // We must NOT deleteLater() afterwards, hence the single-path teardown. |
| 327 | if (m_focusedItem) { |
| 328 | // Drop any signal connections back to this panel before the widget |
| 329 | // gets destroyed. Keeps `updateFocusedItemSizeHint()` from firing |
| 330 | // against a half-torn-down state. |
| 331 | if (m_focusedEntry) { |
| 332 | disconnect(m_focusedEntry.data(), nullptr, this, nullptr); |
| 333 | } |
| 334 | m_tree->removeItemWidget(m_focusedItem, 0); |
| 335 | m_focusedEntry = nullptr; |
| 336 | // Restore delegate painting + compact sizeHint so the row renders as |
| 337 | // a normal compact L3 again. |
| 338 | m_focusedItem->setData(0, NotesTreeDelegate::FocusedRole, false); |
| 339 | m_focusedItem->setData(0, Qt::SizeHintRole, QVariant()); |
| 340 | m_focusedItem = nullptr; |
| 341 | m_focusedLinkId.clear(); |
| 342 | m_focusedNoteId.clear(); |
| 343 | } |
| 344 | |
| 345 | if (!noteItem) return; |
| 346 | |
| 347 | const QString linkId = noteItem->data(0, LinkIdRole).toString(); |
| 348 | const QString noteId = noteItem->data(0, NoteIdRole).toString(); |
| 349 | if (linkId.isEmpty() || noteId.isEmpty() || m_notesDir.isEmpty()) return; |
| 350 | |
| 351 | auto it = m_linkIndex.find(linkId); |
| 352 | if (it == m_linkIndex.end()) return; |
| 353 | const LinkOutlineEntry& entry = m_outline[*it]; |
| 354 | |
| 355 | // Load the full note once. This is intentionally the only place in the |
| 356 | // panel that calls loadFromFile(). |
| 357 | const QString path = m_notesDir + QStringLiteral("/") + noteId |
| 358 | + QStringLiteral(".md"); |
| 359 | const MarkdownNote note = MarkdownNote::loadFromFile(path); |
| 360 | |
| 361 | NoteDisplayData data; |
| 362 | data.noteId = noteId; |
| 363 | data.title = note.title; |
| 364 | data.content = note.content; |
| 365 | data.linkObjectId = entry.linkObjectId; |
| 366 | data.color = entry.iconColor; |
| 367 | data.description = entry.description; |
| 368 | |
| 369 | m_focusedEntry = new MarkdownNoteEntry(data); |
| 370 | // Cache IDs now so setOutline() can find the note to re-focus even if the |
| 371 | // item pointer becomes invalid during rebuild. |
| 372 | m_focusedItem = noteItem; |
| 373 | m_focusedLinkId = linkId; |
| 374 | m_focusedNoteId = noteId; |
| 375 | |
| 376 | // Forward the editor's signals unchanged. |
| 377 | connect(m_focusedEntry, &MarkdownNoteEntry::contentChanged, |