Переглянути джерело

ctkLayoutManager was not properly cleaning the layout

clearLayout() was not deleting the layout elements (e.g. Splitters, QTabWidget)
created previously by setupLayout().
It was a problem when reparenting the views when applying the new layout in the viewport in setupLayout.
The reparenting of a QTabWidget child was setting the WA_WState_ExplicitShowHide flag on the widgets which
was preventing _q_showIfNotHidden() from setting the visibility of the child back at the next update.
Moreover the QTabWidgets and QSplitters were not destroyed until ctkLayoutManager was destroyed.
When clearing layout, it is enforced that the widgets don't redraw (QWidget::setUpdatesEnabled(false)) and the layouts don't resize (QLayout::setEnabled(false))to prevent flickering.
Julien Finet 13 роки тому
батько
коміт
0afd8925cf

+ 111 - 10
Libs/Widgets/Testing/Cpp/ctkLayoutManagerTest1.cpp

@@ -20,10 +20,12 @@
 
 
 // Qt includse
 // Qt includse
 #include <QApplication>
 #include <QApplication>
+#include <QDebug>
 #include <QPushButton>
 #include <QPushButton>
 #include <QTimer>
 #include <QTimer>
 
 
 // CTK includes
 // CTK includes
+#include "ctkSliderWidget.h"
 #include "ctkSimpleLayoutManager.h"
 #include "ctkSimpleLayoutManager.h"
 
 
 // STD includes
 // STD includes
@@ -34,7 +36,7 @@ QString vboxLayout("<layout type=\"vertical\"><item><view/></item><item><view/><
 QString gridLayout(
 QString gridLayout(
 "<layout type=\"grid\">"
 "<layout type=\"grid\">"
 " <item><view/></item>"
 " <item><view/></item>"
-" <item col=\"1\"><view/></item>"
+" <item column=\"1\"><view/></item>"
 " <item row=\"1\"><view/></item>"
 " <item row=\"1\"><view/></item>"
 " <item row=\"1\" column=\"1\"><view/></item>"
 " <item row=\"1\" column=\"1\"><view/></item>"
 " <item row=\"2\" colspan=\"2\"><view/></item>"
 " <item row=\"2\" colspan=\"2\"><view/></item>"
@@ -70,11 +72,25 @@ QString nestedLayout(
 " <item><view name=\"tab3\"/></item>"
 " <item><view name=\"tab3\"/></item>"
 "</layout>");
 "</layout>");
 
 
-class ctkPushButton: public QPushButton
+/// \ingroup Widgets
+struct ctkCachedInstanciator
+  : public ctkWidgetInstanciator
 {
 {
-  Q_OBJECT
-public:
-  Q_INVOKABLE ctkPushButton(): QPushButton(0){}
+  int CreateWidgetCount;
+  QWidgetList CachedWidgets;
+  virtual void beginSetupLayout()
+    {
+    this->CreateWidgetCount = 0;
+    }
+  virtual QWidget* createWidget()
+    {
+    if (this->CreateWidgetCount >= this->CachedWidgets.size())
+      {
+      QWidget* widget = new ctkSliderWidget;
+      this->CachedWidgets.push_back(widget);
+      }
+    return this->CachedWidgets[this->CreateWidgetCount++];
+    }
 };
 };
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
@@ -94,7 +110,6 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
     return EXIT_FAILURE;
     return EXIT_FAILURE;
     }
     }
 
 
-  //layoutManager.setViewMetaObject(QPushButton::staticMetaObject);
   ctkTemplateInstanciator<QPushButton> pButtonInstanciator;
   ctkTemplateInstanciator<QPushButton> pButtonInstanciator;
   layoutManager.setViewInstanciator(&pButtonInstanciator);
   layoutManager.setViewInstanciator(&pButtonInstanciator);
 
 
@@ -123,7 +138,6 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
     }
     }
   viewport.show();
   viewport.show();
 
 
-
   QWidget vbox;
   QWidget vbox;
   vbox.setWindowTitle("Vertical Box Layout");
   vbox.setWindowTitle("Vertical Box Layout");
   ctkSimpleLayoutManager vboxLayoutManager;
   ctkSimpleLayoutManager vboxLayoutManager;
@@ -141,7 +155,7 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
   grid.show();
   grid.show();
 
 
   QWidget tab;
   QWidget tab;
-  grid.setWindowTitle("Tab Layout");
+  tab.setWindowTitle("Tab Layout");
   ctkSimpleLayoutManager tabLayoutManager;
   ctkSimpleLayoutManager tabLayoutManager;
   tabLayoutManager.setViewInstanciator(&pButtonInstanciator);
   tabLayoutManager.setViewInstanciator(&pButtonInstanciator);
   tabLayoutManager.setLayout(tabLayoutDoc);
   tabLayoutManager.setLayout(tabLayoutDoc);
@@ -149,22 +163,109 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
   tab.show();
   tab.show();
 
 
   QWidget nested;
   QWidget nested;
-  grid.setWindowTitle("Nested Layout");
+  nested.setWindowTitle("Nested Layout");
   ctkSimpleLayoutManager nestedLayoutManager;
   ctkSimpleLayoutManager nestedLayoutManager;
   nestedLayoutManager.setViewInstanciator(&pButtonInstanciator);
   nestedLayoutManager.setViewInstanciator(&pButtonInstanciator);
   nestedLayoutManager.setLayout(nestedLayoutDoc);
   nestedLayoutManager.setLayout(nestedLayoutDoc);
   nestedLayoutManager.setViewport(&nested);
   nestedLayoutManager.setViewport(&nested);
   nested.show();
   nested.show();
 
 
+  // TabToGrid
+  QWidget tabToGrid;
+  tabToGrid.setWindowTitle("Tab to Grid Layout");
+  ctkCachedInstanciator tabToGridInstanciator;
+  ctkSimpleLayoutManager tabToGridLayoutManager;
+  tabToGridLayoutManager.setViewInstanciator(&tabToGridInstanciator);
+  tabToGridLayoutManager.setLayout(tabLayoutDoc);
+  tabToGridLayoutManager.setViewport(&tabToGrid);
+  tabToGrid.show();
+
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
+  tabToGridLayoutManager.setLayout(gridLayoutDoc);
+
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
+  if (tabToGridInstanciator.CachedWidgets[0]->isHidden() ||
+      tabToGridInstanciator.CachedWidgets[1]->isHidden() ||
+      tabToGridInstanciator.CachedWidgets[2]->isHidden() ||
+      tabToGridInstanciator.CachedWidgets[3]->isHidden())
+    {
+    std::cout << __LINE__ << " TabToGrid: "
+              << "ctkLayoutManager::setupLayout() failed to show/hide widgets"
+              << tabToGridInstanciator.CachedWidgets[0]->isHidden() << " "
+              << tabToGridInstanciator.CachedWidgets[1]->isHidden() << " "
+              << tabToGridInstanciator.CachedWidgets[2]->isHidden() << " "
+              << tabToGridInstanciator.CachedWidgets[3]->isHidden() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // TabToSimple
   QWidget tabToSimple;
   QWidget tabToSimple;
   tabToSimple.setWindowTitle("Tab to Simple Layout");
   tabToSimple.setWindowTitle("Tab to Simple Layout");
+  ctkCachedInstanciator tabToSimpleInstanciator;
   ctkSimpleLayoutManager tabToSimpleLayoutManager;
   ctkSimpleLayoutManager tabToSimpleLayoutManager;
-  tabToSimpleLayoutManager.setViewInstanciator(&pButtonInstanciator);
+  tabToSimpleLayoutManager.setViewInstanciator(&tabToSimpleInstanciator);
+  //tabToSimpleLayoutManager.setLayout(gridLayoutDoc);
   tabToSimpleLayoutManager.setLayout(tabLayoutDoc);
   tabToSimpleLayoutManager.setLayout(tabLayoutDoc);
   tabToSimpleLayoutManager.setViewport(&tabToSimple);
   tabToSimpleLayoutManager.setViewport(&tabToSimple);
   tabToSimple.show();
   tabToSimple.show();
+
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
   tabToSimpleLayoutManager.setLayout(simpleLayoutDoc);
   tabToSimpleLayoutManager.setLayout(simpleLayoutDoc);
 
 
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
+  if (tabToSimpleInstanciator.CachedWidgets[0]->isHidden() ||
+      tabToSimpleInstanciator.CachedWidgets[1]->isVisible() ||
+      tabToSimpleInstanciator.CachedWidgets[2]->isVisible())
+    {
+    std::cout << __LINE__ << " TabToSimple: "
+              << "ctkLayoutManager::setupLayout() failed to show/hide widgets"
+              << tabToSimpleInstanciator.CachedWidgets[0]->isHidden() << " "
+              << tabToSimpleInstanciator.CachedWidgets[1]->isVisible() << " "
+              << tabToSimpleInstanciator.CachedWidgets[2]->isVisible() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // NestedToTab
+  QWidget nestedToTab;
+  nestedToTab.setWindowTitle("Nested to Tab Layout");
+  ctkCachedInstanciator nestedToTabInstanciator;
+  ctkSimpleLayoutManager nestedToTabLayoutManager;
+  nestedToTabLayoutManager.setViewInstanciator(&nestedToTabInstanciator);
+  nestedToTabLayoutManager.setLayout(nestedLayoutDoc);
+  nestedToTabLayoutManager.setViewport(&nestedToTab);
+  nestedToTab.show();
+
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
+  nestedToTabLayoutManager.setLayout(tabLayoutDoc);
+
+  QTimer::singleShot(200, &app, SLOT(quit()));
+  app.exec();
+
+  if (nestedToTabInstanciator.CachedWidgets[0]->isHidden() ||
+      nestedToTabInstanciator.CachedWidgets[1]->isVisible() ||
+      nestedToTabInstanciator.CachedWidgets[2]->isVisible() ||
+      nestedToTabInstanciator.CachedWidgets[3]->isVisible())
+    {
+    std::cout << __LINE__ << " NestedToTab: "
+              << "ctkLayoutManager::setupLayout() failed to show/hide widgets"
+              << nestedToTabInstanciator.CachedWidgets[0]->isHidden() << " "
+              << nestedToTabInstanciator.CachedWidgets[1]->isVisible() << " "
+              << nestedToTabInstanciator.CachedWidgets[2]->isVisible() << " "
+              << nestedToTabInstanciator.CachedWidgets[3]->isVisible() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+
   if (argc < 2 || QString(argv[1]) != "-I" )
   if (argc < 2 || QString(argv[1]) != "-I" )
     {
     {
     QTimer::singleShot(200, &app, SLOT(quit()));
     QTimer::singleShot(200, &app, SLOT(quit()));

+ 69 - 14
Libs/Widgets/ctkLayoutManager.cpp

@@ -52,23 +52,78 @@ void ctkLayoutManagerPrivate::init()
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
+void ctkLayoutManagerPrivate::clearWidget(QWidget* widget, QLayout* parentLayout)
+{
+  if (!this->LayoutWidgets.contains(widget))
+    {
+    widget->setVisible(false);
+    if (parentLayout)
+      {
+      parentLayout->removeWidget(widget);
+      }
+    widget->setParent(this->Viewport);
+    }
+  else
+    {
+    if (widget->layout())
+      {
+      this->clearLayout(widget->layout());
+      }
+    else if (qobject_cast<QTabWidget*>(widget))
+      {
+      QTabWidget* tabWidget = qobject_cast<QTabWidget*>(widget);
+      while (tabWidget->count())
+        {
+        QWidget* page = tabWidget->widget(0);
+        this->clearWidget(page);
+        }
+      }
+    else if (qobject_cast<QSplitter*>(widget))
+      {
+      QSplitter* splitterWidget = qobject_cast<QSplitter*>(widget);
+      // Hide the splitter before removing pages. Removing pages
+      // would resize the other children if the splitter is visible.
+      // We don't want to resize the children as we are trying to clear the
+      // layout, it would set intermediate sizes to widgets.
+      // See QSplitter::childEvent
+      splitterWidget->setVisible(false);
+      while (splitterWidget->count())
+        {
+        QWidget* page = splitterWidget->widget(0);
+        this->clearWidget(page);
+        }
+      }
+    this->LayoutWidgets.remove(widget);
+    if (parentLayout)
+      {
+      parentLayout->removeWidget(widget);
+      }
+    delete widget;
+    }
+}
+
+//-----------------------------------------------------------------------------
 void ctkLayoutManagerPrivate::clearLayout(QLayout* layout)
 void ctkLayoutManagerPrivate::clearLayout(QLayout* layout)
 {
 {
   if (!layout)
   if (!layout)
     {
     {
     return;
     return;
     }
     }
+  layout->setEnabled(false);
   QLayoutItem * layoutItem = 0;
   QLayoutItem * layoutItem = 0;
-  while ((layoutItem = layout->takeAt(0)) != 0)
+  while ((layoutItem = layout->itemAt(0)) != 0)
     {
     {
     if (layoutItem->widget())
     if (layoutItem->widget())
       {
       {
-      layoutItem->widget()->setVisible(false);
-      layout->removeWidget(layoutItem->widget());
+      this->clearWidget(layoutItem->widget(), layout);
       }
       }
     else if (layoutItem->layout())
     else if (layoutItem->layout())
       {
       {
+      /// Warning, this might delete the layouts of "custom" widgets, not just
+      /// the layouts generated by ctkLayoutManager
       this->clearLayout(layoutItem->layout());
       this->clearLayout(layoutItem->layout());
+      layout->removeItem(layoutItem);
+      delete layoutItem;
       }
       }
     }
     }
   if (layout->parentWidget() && layout->parentWidget()->layout() == layout)
   if (layout->parentWidget() && layout->parentWidget()->layout() == layout)
@@ -156,6 +211,7 @@ void ctkLayoutManager::clearLayout()
     }
     }
   // TODO: post an event on the event queue
   // TODO: post an event on the event queue
   d->clearLayout(d->Viewport->layout());
   d->clearLayout(d->Viewport->layout());
+  Q_ASSERT(d->LayoutWidgets.size() == 0);
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
@@ -168,6 +224,7 @@ void ctkLayoutManager::setupLayout()
     return;
     return;
     }
     }
   d->Views.clear();
   d->Views.clear();
+  d->LayoutWidgets.clear();
   Q_ASSERT(!d->Viewport->layout());
   Q_ASSERT(!d->Viewport->layout());
   QLayoutItem* layoutItem = this->processElement(
   QLayoutItem* layoutItem = this->processElement(
     d->Layout.documentElement());
     d->Layout.documentElement());
@@ -180,18 +237,7 @@ void ctkLayoutManager::setupLayout()
     hboxLayout->addItem(layoutItem);
     hboxLayout->addItem(layoutItem);
     layout = hboxLayout;
     layout = hboxLayout;
     }
     }
-  // setting the layout to the widget will reparent all the 1 level widgets.
-  // Unfortunately, it has the side effect of hiding
-  // (testAttribute(Qt::WA_WState_Hidden)) the widgets that were already having
-  // a parent (read doc for QWidget::isHidden()).
-  // we then need to manually display the widgets again. Views is not probably
-  // not the best list to use to retrieve the widgets to remove the hidden flag
-  // it seems to fit the bill for the moment so we can keep using it.
   d->Viewport->setLayout(layout);
   d->Viewport->setLayout(layout);
-  foreach(QWidget* view, d->Views)
-    {
-    view->setHidden(false);
-    }
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
@@ -272,12 +318,19 @@ QLayoutItem* ctkLayoutManager::processLayoutElement(QDomElement layoutElement)
 
 
   QLayoutItem* layoutItem = this->layoutFromXML(layoutElement);
   QLayoutItem* layoutItem = this->layoutFromXML(layoutElement);
   QLayout* layout = layoutItem->layout();
   QLayout* layout = layoutItem->layout();
+  QWidget* widget = layoutItem->widget();
 
 
   if (layout)
   if (layout)
     {
     {
     layout->setContentsMargins(0,0,0,0);
     layout->setContentsMargins(0,0,0,0);
     layout->setSpacing(d->Spacing);
     layout->setSpacing(d->Spacing);
     }
     }
+  else if (widget)
+    {
+    // mark the widget as ctkLayoutManager own widget so that it can be
+    // deleted when the layout is cleared.
+    d->LayoutWidgets << widget;
+    }
   for(QDomNode child = layoutElement.firstChild();
   for(QDomNode child = layoutElement.firstChild();
       !child.isNull();
       !child.isNull();
       child = child.nextSibling())
       child = child.nextSibling())
@@ -349,6 +402,7 @@ void ctkLayoutManager::processItemElement(QDomElement itemElement, QLayoutItem*
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 void ctkLayoutManager::addChildItemToLayout(QDomElement itemElement, QLayoutItem* childItem, QLayoutItem* layoutItem)
 void ctkLayoutManager::addChildItemToLayout(QDomElement itemElement, QLayoutItem* childItem, QLayoutItem* layoutItem)
 {
 {
+  Q_D(ctkLayoutManager);
   Q_ASSERT(childItem);
   Q_ASSERT(childItem);
   QString itemName = itemElement.attribute("name");
   QString itemName = itemElement.attribute("name");
   if (itemName.isEmpty() && childItem->widget())
   if (itemName.isEmpty() && childItem->widget())
@@ -378,6 +432,7 @@ void ctkLayoutManager::addChildItemToLayout(QDomElement itemElement, QLayoutItem
     if (!childWidget)
     if (!childWidget)
       {
       {
       childWidget = new QWidget();
       childWidget = new QWidget();
+      d->LayoutWidgets << childWidget;
       childWidget->setLayout(childItem->layout());
       childWidget->setLayout(childItem->layout());
       }
       }
     if (tabWidget)
     if (tabWidget)

+ 1 - 1
Libs/Widgets/ctkLayoutManager.h

@@ -65,7 +65,7 @@ protected:
 
 
   virtual void onViewportChanged();
   virtual void onViewportChanged();
   void clearLayout();
   void clearLayout();
-  void setupLayout();
+  virtual void setupLayout();
 
 
   virtual void setLayout(const QDomDocument& newLayout);
   virtual void setLayout(const QDomDocument& newLayout);
   const QDomDocument layout()const;
   const QDomDocument layout()const;

+ 2 - 0
Libs/Widgets/ctkLayoutManager_p.h

@@ -47,10 +47,12 @@ public:
 
 
   virtual void init();
   virtual void init();
   void clearLayout(QLayout* layout);
   void clearLayout(QLayout* layout);
+  void clearWidget(QWidget* widget, QLayout* parentLayout = 0);
 
 
   QWidget*       Viewport;
   QWidget*       Viewport;
   QDomDocument   Layout;
   QDomDocument   Layout;
   QSet<QWidget*> Views;
   QSet<QWidget*> Views;
+  QSet<QWidget*> LayoutWidgets;
   int            Spacing;
   int            Spacing;
 };
 };
 
 

+ 15 - 0
Libs/Widgets/ctkSimpleLayoutManager.cpp

@@ -108,3 +108,18 @@ QWidget* ctkSimpleLayoutManager::viewFromXML(QDomElement viewElement)
     }
     }
   return d->ViewInstanciator->createWidget();
   return d->ViewInstanciator->createWidget();
 }
 }
+
+//-----------------------------------------------------------------------------
+void ctkSimpleLayoutManager::setupLayout()
+{
+  Q_D(ctkSimpleLayoutManager);
+  if (d->ViewInstanciator)
+    {
+    d->ViewInstanciator->beginSetupLayout();
+    }
+  this->ctkLayoutManager::setupLayout();
+  if (d->ViewInstanciator)
+    {
+    d->ViewInstanciator->endSetupLayout();
+    }
+}

+ 4 - 2
Libs/Widgets/ctkSimpleLayoutManager.h

@@ -31,6 +31,8 @@ class ctkSimpleLayoutManagerPrivate;
 /// \ingroup Widgets
 /// \ingroup Widgets
 struct ctkWidgetInstanciator
 struct ctkWidgetInstanciator
 {
 {
+  virtual void beginSetupLayout(){}
+  virtual void endSetupLayout(){}
   virtual QWidget* createWidget() = 0;
   virtual QWidget* createWidget() = 0;
 };
 };
 
 
@@ -62,8 +64,8 @@ public:
   ctkWidgetInstanciator* viewInstanciator()const;
   ctkWidgetInstanciator* viewInstanciator()const;
 
 
 protected:
 protected:
- virtual QWidget* viewFromXML(QDomElement viewElement);
-
+  virtual QWidget* viewFromXML(QDomElement viewElement);
+  virtual void setupLayout();
 private:
 private:
   Q_DECLARE_PRIVATE(ctkSimpleLayoutManager);
   Q_DECLARE_PRIVATE(ctkSimpleLayoutManager);
   Q_DISABLE_COPY(ctkSimpleLayoutManager);
   Q_DISABLE_COPY(ctkSimpleLayoutManager);