#pragma once #include "../PLUG_Services.hpp" #include "../toon_boom_layout.hpp" #include "QtXml/qdom.h" #include #include #include #include #include #include template concept isQWidget = std::is_base_of::value; /** * @brief Simple base class for implementing TULayoutView with a QWidget, with all the ugly parts abstracted away. * @tparam T The QWidget type to use */ template class TUWidgetLayoutViewBase : public TULayoutView { public: TUWidgetLayoutViewBase() { m_widget = nullptr; m_parentConnected = nullptr; }; TUWidgetLayoutViewBase(T *widget) { m_widget = widget; m_parentConnected = nullptr; }; virtual ~TUWidgetLayoutViewBase() {}; virtual void triggerMenuChanged() override {} QWidget *widget() override { return reinterpret_cast(static_cast(this)); } const QWidget *getWidget() const override { const_cast(this)->ensureWidget(); const_cast(this)->connectToParentIfNeeded(); return m_widget; } QWidget *getWidget() override { ensureWidget(); connectToParentIfNeeded(); return m_widget; } // Note: initiate() is NOT called by Toon Boom! The actual parenting // is done externally via getWidget() + setParent(). We keep this for // API compatibility but the real work is done in connectToParentIfNeeded(). TULayoutView *initiate(QWidget *parent) override { ensureWidget(); if (parent && m_widget) { m_widget->setParent(parent); connectToParent(parent); } return this; } void isTULayoutView() override {} void disconnectView() override {} protected: QPointer m_widget; QWidget *m_parentConnected; // Track which parent we've connected to void ensureWidget() { if (!m_widget) { m_widget = createWidget(); m_widget->setAttribute(Qt::WA_DeleteOnClose, false); m_widget->setAttribute(Qt::WA_QuitOnClose, false); } } // Connect to the widget's current parent to unparent before deletion // This prevents cross-DLL heap corruption when Qt tries to delete our widget void connectToParentIfNeeded() { if (!m_widget) return; QWidget *parent = m_widget->parentWidget(); if (parent && parent != m_parentConnected) { connectToParent(parent); } } void connectToParent(QWidget *parent) { if (!parent || !m_widget) return; m_parentConnected = parent; QObject::connect( parent, &QObject::destroyed, m_widget.data(), [this]() { std::cout << "[parent destroyed] Unparenting widget to prevent " "cross-DLL heap deletion" << std::endl; if (m_widget) { m_widget->setParent(nullptr); } m_parentConnected = nullptr; onParentDisconnect(); }, Qt::DirectConnection); } virtual T *createWidget() = 0; };