瀏覽代碼

Add support for vertical flow in ctkThumbnailListWidget

We can't let QScrollArea resize the viewport (ctkFlowLayout) as it only
knows about heightForWidth. When the flow is Qt::Vertical, widthForHeight
should be used instead.
Closes #241
Julien Finet 12 年之前
父節點
當前提交
a01fc60c7f

+ 1 - 1
Libs/Widgets/Resources/UI/ctkThumbnailListWidget.ui

@@ -20,7 +20,7 @@
    <item>
     <widget class="QScrollArea" name="ScrollArea">
      <property name="widgetResizable">
-      <bool>true</bool>
+      <bool>false</bool>
      </property>
      <widget class="QWidget" name="ScrollAreaContentWidget">
       <property name="geometry">

+ 21 - 2
Libs/Widgets/ctkFlowLayout.cpp

@@ -119,6 +119,7 @@ int ctkFlowLayoutPrivate::doLayout(const QRect& rect, bool testOnly)const
   int length = 0;
   int max = this->Orientation == Qt::Horizontal ?
     effectiveRect.right() + 1 : effectiveRect.bottom() + 1;
+  int maxX = left + right;
   int maxY = top + bottom;
   QSize maxItemSize = this->AlignItems ? this->maxSizeHint() : QSize();
 
@@ -162,12 +163,13 @@ int ctkFlowLayoutPrivate::doLayout(const QRect& rect, bool testOnly)const
       item->setGeometry(QRect(pos, item->sizeHint()));
       }
 
+    maxX = qMax( maxX , pos.x() + item->sizeHint().width() + right);
     maxY = qMax( maxY , pos.y() + item->sizeHint().height() + bottom);
     pos = next;
     length = qMax(length, this->Orientation == Qt::Horizontal ?
       itemSize.height() : itemSize.width());
     }
-  return maxY;
+  return this->Orientation == Qt::Horizontal ? maxY : maxX;
 }
 
 //-----------------------------------------------------------------------------
@@ -321,9 +323,26 @@ Qt::Orientations ctkFlowLayout::expandingDirections() const
 }
 
 // --------------------------------------------------------------------------
+bool ctkFlowLayout::hasWidthForHeight() const
+{
+  Q_D(const ctkFlowLayout);
+  return d->Orientation == Qt::Vertical;
+}
+
+// --------------------------------------------------------------------------
+int ctkFlowLayout::widthForHeight(int height) const
+{
+  Q_D(const ctkFlowLayout);
+  QRect rect(0, 0, 0, height);
+  int width = d->doLayout(rect, true);
+  return width;
+}
+
+// --------------------------------------------------------------------------
 bool ctkFlowLayout::hasHeightForWidth() const
 {
-  return true;
+  Q_D(const ctkFlowLayout);
+  return d->Orientation == Qt::Horizontal;
 }
 
 // --------------------------------------------------------------------------

+ 7 - 0
Libs/Widgets/ctkFlowLayout.h

@@ -104,6 +104,13 @@ public:
   /// indeed, a layout can have another layout as a parent, not only a widget.
   static ctkFlowLayout* replaceLayout(QWidget* widget);
 
+  /// When the orientation is Qt::Vertical, heightForWidth doesn't work
+  /// correctly with ctkFlowLayout. Ideally widthForHeight should be used
+  /// instead.
+  /// \sa orientation widthForHeight hasHeightForWidth
+  virtual bool hasWidthForHeight() const;
+  virtual int widthForHeight(int) const;
+
   /// Reimplemented for internal reasons
   virtual void addItem(QLayoutItem *item);
   virtual Qt::Orientations expandingDirections() const;

+ 59 - 1
Libs/Widgets/ctkThumbnailListWidget.cpp

@@ -27,6 +27,7 @@
 #include <QPixmap>
 #include <QPushButton>
 #include <QResizeEvent>
+#include <QScrollBar>
 
 // ctk includes
 #include "ctkLogger.h"
@@ -194,7 +195,26 @@ void ctkThumbnailListWidget::onThumbnailSelected(const ctkThumbnailLabel &widget
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidget::setThumbnailSize(QSize size){
+void ctkThumbnailListWidget::setFlow(Qt::Orientation flow)
+{
+  Q_D(ctkThumbnailListWidget);
+  ctkFlowLayout* flowLayout = qobject_cast<ctkFlowLayout*>(
+    d->ScrollAreaContentWidget->layout());
+  flowLayout->setOrientation(flow);
+}
+
+//----------------------------------------------------------------------------
+Qt::Orientation ctkThumbnailListWidget::flow()const
+{
+  Q_D(const ctkThumbnailListWidget);
+  ctkFlowLayout* flowLayout = qobject_cast<ctkFlowLayout*>(
+    d->ScrollAreaContentWidget->layout());
+  return flowLayout->orientation();
+}
+
+//----------------------------------------------------------------------------
+void ctkThumbnailListWidget::setThumbnailSize(QSize size)
+{
   Q_D(ctkThumbnailListWidget);
   if (size.isValid())
     {
@@ -220,3 +240,41 @@ void ctkThumbnailListWidget::clearThumbnails()
   Q_D(ctkThumbnailListWidget);
   d->clearAllThumbnails();
 }
+
+//----------------------------------------------------------------------------
+void ctkThumbnailListWidget::resizeEvent(QResizeEvent* event)
+{
+  Q_D(ctkThumbnailListWidget);
+
+  QSize newViewportSize = event->size() - QSize(2 * d->ScrollArea->lineWidth(),
+                                                2 * d->ScrollArea->lineWidth());
+
+  ctkFlowLayout* flowLayout = qobject_cast<ctkFlowLayout*>(
+    d->ScrollAreaContentWidget->layout());
+  newViewportSize = newViewportSize.expandedTo(flowLayout->minimumSize());
+  if (flowLayout->hasHeightForWidth())
+    {
+    int newViewPortHeight = newViewportSize.height();
+    newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
+    if (newViewportSize.height() > newViewPortHeight)
+      {
+      // The new width is too narrow, to fit everything, a vertical scrollbar
+      // is needed. Recompute with the scrollbar width.
+      newViewportSize.rwidth() -= d->ScrollArea->verticalScrollBar()->sizeHint().width();
+      newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
+      }
+    }
+  else if (flowLayout->hasWidthForHeight())
+    {
+    int newViewPortWidth = newViewportSize.width();
+    newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
+    if (newViewportSize.width() > newViewPortWidth)
+      {
+      // The new height is too narrow, to fit everything, an horizontal scrollbar
+      // is needed. Recompute with the scrollbar height.
+      newViewportSize.rheight() -= d->ScrollArea->horizontalScrollBar()->sizeHint().height();
+      newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
+      }
+    }
+  d->ScrollAreaContentWidget->resize(newViewportSize);
+}

+ 10 - 0
Libs/Widgets/ctkThumbnailListWidget.h

@@ -23,6 +23,7 @@
 
 // Qt includes
 #include <QWidget>
+class QResizeEvent;
 
 // CTK includes
 #include "ctkWidgetsExport.h"
@@ -34,6 +35,7 @@ class CTK_WIDGETS_EXPORT ctkThumbnailListWidget : public QWidget
 {
   Q_OBJECT
   Q_PROPERTY(int currentThumbnail READ currentThumbnail WRITE setCurrentThumbnail)
+  Q_PROPERTY(Qt::Orientation flow READ flow WRITE setFlow)
   Q_PROPERTY(QSize thumbnailSize READ thumbnailSize WRITE setThumbnailSize)
 public:
   typedef QWidget Superclass;
@@ -52,6 +54,12 @@ public:
   /// Clear all the thumbnails
   void clearThumbnails();
 
+  /// Flow of the layout
+  ///  - Qt::Horizontal: left to right
+  ///  - Qt::Vertical: top to bottom
+  void setFlow(Qt::Orientation orientation);
+  Qt::Orientation flow()const;
+
   /// Get thumbnail width
   QSize thumbnailSize()const;
 
@@ -70,6 +78,8 @@ protected:
   explicit ctkThumbnailListWidget(ctkThumbnailListWidgetPrivate* ptr, QWidget* parent=0);
   ctkThumbnailListWidgetPrivate* d_ptr;
 
+  virtual void resizeEvent(QResizeEvent* event);
+
 private:
   Q_DECLARE_PRIVATE(ctkThumbnailListWidget);
   Q_DISABLE_COPY(ctkThumbnailListWidget);