瀏覽代碼

ENH: Allow layout manager to adjust view sizes

- For layouts with splitter: added "splitSize" attribute to specify default item size.
- For layouts without splitter: added "horizontalStretch" and "verticalStretch" attributes to specify relative size of views.

Updated documentation and test accordingly.

This improvement is required for fixing https://issues.slicer.org/view.php?id=4530.
Andras Lasso 7 年之前
父節點
當前提交
32c68bc0fd

+ 13 - 13
Libs/Widgets/Testing/Cpp/ctkLayoutManagerTest1.cpp

@@ -25,7 +25,7 @@
 #include <QTimer>
 
 // CTK includes
-#include "ctkSliderWidget.h"
+#include "ctkPushButton.h"
 #include "ctkLayoutFactory.h"
 #include "ctkLayoutViewFactory.h"
 
@@ -38,8 +38,8 @@ QString gridLayout(
 "<layout type=\"grid\">"
 " <item><view/></item>"
 " <item column=\"1\"><view/></item>"
-" <item row=\"1\"><view/></item>"
-" <item row=\"1\" column=\"1\"><view/></item>"
+" <item row=\"1\"><view horizontalStretch=\"1\"/></item>"
+" <item row=\"1\" column=\"1\"><view horizontalStretch=\"2\"/></item>"
 " <item row=\"2\" colspan=\"2\"><view/></item>"
 " <item row=\"3\" rowspan=\"2\"><view/></item>"
 "</layout>");
@@ -56,9 +56,9 @@ QString tabMultipleLayout(
 QString nestedLayout(
 "<layout type=\"tab\">"
 " <item>"
-"  <layout type=\"horizontal\">"
-"   <item><view/></item>"
-"   <item>"
+"  <layout type=\"horizontal\" split=\"true\">"
+"   <item splitSize=\"400\"><view/></item>"
+"   <item splitSize=\"100\">"
 "    <layout type=\"vertical\">"
 "     <item><view/></item>"
 "     <item><view/></item>"
@@ -70,7 +70,7 @@ QString nestedLayout(
 "     </item>"
 "    </layout>"
 "   </item>"
-"   <item><view/></item>"
+"   <item splitSize=\"100\"><view/></item>"
 "  </layout>"
 " </item>"
 " <item><view name=\"tab2\"/></item>"
@@ -164,8 +164,8 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
   // TabToGrid
   QWidget tabToGrid;
   tabToGrid.setWindowTitle("Tab to Grid Layout");
-  ctkTemplateLayoutViewFactory<ctkSliderWidget>* tabToGridInstanciator =
-    new ctkTemplateLayoutViewFactory<ctkSliderWidget>(&viewport);
+  ctkTemplateLayoutViewFactory<ctkPushButton>* tabToGridInstanciator =
+    new ctkTemplateLayoutViewFactory<ctkPushButton>(&viewport);
   ctkLayoutFactory tabToGridLayoutManager;
   tabToGridLayoutManager.registerViewFactory(tabToGridInstanciator);
   tabToGridLayoutManager.setLayout(tabLayoutDoc);
@@ -199,8 +199,8 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
   // TabToSimple
   QWidget tabToSimple;
   tabToSimple.setWindowTitle("Tab to Simple Layout");
-  ctkTemplateLayoutViewFactory<ctkSliderWidget>* tabToSimpleInstanciator =
-    new ctkTemplateLayoutViewFactory<ctkSliderWidget>(&viewport);
+  ctkTemplateLayoutViewFactory<ctkPushButton>* tabToSimpleInstanciator =
+    new ctkTemplateLayoutViewFactory<ctkPushButton>(&viewport);
   ctkLayoutFactory tabToSimpleLayoutManager;
   tabToSimpleLayoutManager.registerViewFactory(tabToSimpleInstanciator);
   //tabToSimpleLayoutManager.setLayout(gridLayoutDoc);
@@ -232,8 +232,8 @@ int ctkLayoutManagerTest1(int argc, char * argv [] )
   // NestedToTab
   QWidget nestedToTab;
   nestedToTab.setWindowTitle("Nested to Tab Layout");
-  ctkTemplateLayoutViewFactory<ctkSliderWidget>* nestedToTabInstanciator =
-    new ctkTemplateLayoutViewFactory<ctkSliderWidget>(&viewport);
+  ctkTemplateLayoutViewFactory<ctkPushButton>* nestedToTabInstanciator =
+    new ctkTemplateLayoutViewFactory<ctkPushButton>(&viewport);
   ctkLayoutFactory nestedToTabLayoutManager;
   nestedToTabLayoutManager.registerViewFactory(nestedToTabInstanciator);
   nestedToTabLayoutManager.setLayout(nestedLayoutDoc);

+ 23 - 0
Libs/Widgets/ctkLayoutManager.cpp

@@ -332,6 +332,7 @@ QLayoutItem* ctkLayoutManager::processLayoutElement(QDomElement layoutElement)
     // deleted when the layout is cleared.
     d->LayoutWidgets << widget;
     }
+  QList<int> splitSizes;
   for(QDomNode child = layoutElement.firstChild();
       !child.isNull();
       child = child.nextSibling())
@@ -341,8 +342,30 @@ QLayoutItem* ctkLayoutManager::processLayoutElement(QDomElement layoutElement)
       {
       continue;
       }
+    int splitSize = child.toElement().attribute("splitSize", QString::number(0)).toInt();
+    splitSizes << splitSize;
     this->processItemElement(child.toElement(), layoutItem);
     }
+
+  // Set child item split sizes (initial position of the splitter)
+  QSplitter* splitter = qobject_cast<QSplitter*>(widget);
+  if (splitter)
+    {
+    bool splitSizeSpecified = false;
+    foreach(int i, splitSizes)
+      {
+      if (i > 0)
+        {
+        splitSizeSpecified = true;
+        break;
+        }
+      }
+    if (splitSizeSpecified)
+      {
+      splitter->setSizes(splitSizes);
+      }
+    }
+
   return layoutItem;
 }
 

+ 20 - 11
Libs/Widgets/ctkLayoutManager.h

@@ -41,35 +41,44 @@ class ctkLayoutViewFactory;
 /// \code
 /// <layout type=\"tab\">
 ///  <item>
-///   <layout type=\"horizontal\">
-///    <item><view/></item>
-///    <item>
+///   <layout type=\"horizontal\" split=\"true\">
+///    <item splitSize=\"500\"><view/></item>
+///    <item splitSize=\"250\">
 ///     <layout type=\"vertical\">
-///      <item><view/></item>
-///      <item><view/></item>
+///      <item><view verticalStretch=\"1\"/></item>
+///      <item><view verticalStretch=\"2\"/></item>
 ///      <item>
 ///       <layout type=\"grid\">
-///        <item row=\"0\" column=\"1\"><view/></item>
-///        <item row=\"1\" column=\"0\"><view/></item>
+///        <item row=\"0\" column=\"1\"><view verticalStretch=\"1\"/></item>
+///        <item row=\"1\" column=\"0\"><view verticalStretch=\"1\"/></item>
 ///       </layout>
 ///      </item>
 ///     </layout>
 ///    </item>
-///    <item><view/></item>
+///    <item splitSize=\"250\"><view/></item>
 ///   </layout>
 ///  </item>
 ///  <item><view name=\"tab2\"/></item>
 ///  <item><view name=\"tab3\"/></item>
 /// </layout>
 /// \endcode
-/// The layout elements describe widget containers that embed one or mulitple
-/// items.
+/// The layout elements describe widget containers that embed one or multiple
+/// items. Arrangement of items are specified by type attribute of the layout element;
+/// supported values: vertical, horizontal, grid, tab.
 /// The item elements describe widgets or layouts that are children of
 /// layouts.
 /// The view elements can be any type of QWidget. viewFromXML() must be
 /// reimplemented to return the type(s) of QWidget(s) to use wherever the view
 /// element is listed in the layout. The XML element can contain any XML
 /// attribute to be parsed by viewFromXML() method.
+///
+/// For horizontal and vertical layouts, setting split attribute to "true" makes the
+/// views resizeable. Default size can be set using splitSize attribute of child items.
+///
+/// Relative size of views can be adjusted by specifying stretch factors in
+/// horizontalStretch and verticalStretch attributes. The stretch factor must be an
+/// integer in the range of [0,255].
+///
 /// \sa ctkSimpleLayoutManager, ctkLayoutViewFactory
 class CTK_WIDGETS_EXPORT ctkLayoutManager: public QObject
 {
@@ -155,7 +164,7 @@ protected:
   /// The returned widgets will automatically be layout into their parent
   /// layout (e.g. boxlayout).
   /// This method can be reimplemented. Returns viewFromXML() by default.
-  /// \sa viewFromXML(), 
+  /// \sa viewFromXML(),
   virtual QList<QWidget*> viewsFromXML(QDomElement layoutElement);
 
 private:

+ 15 - 0
Libs/Widgets/ctkLayoutViewFactory.cpp

@@ -123,6 +123,21 @@ void ctkLayoutViewFactory::setupView(QDomElement viewElement, QWidget* view)
 {
   Q_ASSERT(view);
   view->setVisible(true);
+
+  // Allow tuning of relative view sizes
+  int horizontalStretch = viewElement.attribute("horizontalStretch", QString::number(-1)).toInt();
+  int verticalStretch = viewElement.attribute("verticalStretch", QString::number(-1)).toInt();
+  QSizePolicy sizePolicy = view->sizePolicy();
+  if (horizontalStretch >= 0)
+    {
+    sizePolicy.setHorizontalStretch(horizontalStretch);
+    }
+  if (verticalStretch >= 0)
+    {
+    sizePolicy.setVerticalStretch(verticalStretch);
+    }
+  view->setSizePolicy(sizePolicy);
+
   this->registerView(viewElement, view);
 }