瀏覽代碼

Merge branch 'ctkThumbnailListWidget-vertical'

* ctkThumbnailListWidget-vertical:
  Add support for vertical flow in ctkThumbnailListWidget
  Cleanup style in ctkThumbnailListWidget
Julien Finet 12 年之前
父節點
當前提交
c42727cfe8

+ 5 - 2
Libs/Widgets/Resources/UI/ctkThumbnailListWidget.ui

@@ -11,13 +11,16 @@
    </rect>
   </property>
   <property name="windowTitle">
-   <string>Form</string>
+   <string>ThumbnailList</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_2">
+   <property name="margin">
+    <number>0</number>
+   </property>
    <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;
 }
 
 // --------------------------------------------------------------------------

+ 42 - 19
Libs/Widgets/ctkFlowLayout.h

@@ -36,45 +36,61 @@ class ctkFlowLayoutPrivate;
 class CTK_WIDGETS_EXPORT ctkFlowLayout : public QLayout
 {
   Q_OBJECT
+  /// If orientation is Qt::Horizontal, items are layed out from left to right
+  /// then top to bottom if there is no more horizontal space.
+  /// If orientation is Qt::Vertical, items are layed out from top to bottom
+  /// then left to right if there is no more vertical space.
+  /// Qt::Horizontal by default
+  /// \sa preferredExpandingDirections
   Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+
+  /// Indicates how the size hint of the layout should behave. The preferred
+  /// expanding direction can be different than the \a orientation of the
+  /// layout.
+  /// It can be a combination of Qt::Horizontal and Qt::Vertical, in that case
+  /// the layout will try to expand in a square shape (evenly distribute the
+  /// number of rows and columns).
+  /// Qt::Horizontal | Qt::Vertical by default.
+  /// \sa orientation
+  Q_PROPERTY(Qt::Orientations preferredExpandingDirections READ preferredExpandingDirections WRITE setPreferredExpandingDirections)
+
+  /// Force the items to be horizontally aligned based on the largest item
+  /// to display.
+  /// True by default.
+  /// \sa orientation
+  Q_PROPERTY(bool alignItems READ alignItems WRITE setAlignItems)
+
+  /// Horizontal space between items, if the spacing is <0, a default spacing
+  /// set on the parent/style is used.
+  /// -1 by default.
+  /// \sa verticalSpacing
   Q_PROPERTY(int horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing)
+
+  /// Vertical space between items, if the spacing is <0, a default spacing
+  /// set on the parent/style is used.
+  /// -1 by default.
+  /// \sa horizontalSpacing
   Q_PROPERTY(int verticalSpacing READ verticalSpacing WRITE setVerticalSpacing)
-  Q_PROPERTY(bool alignItems READ alignItems WRITE setAlignItems)
-  Q_PROPERTY(Qt::Orientations preferredExpandingDirections READ preferredExpandingDirections WRITE setPreferredExpandingDirections)
+
 public:
   typedef QLayout Superclass;
   explicit ctkFlowLayout(Qt::Orientation orientation, QWidget* parent = 0);
   explicit ctkFlowLayout(QWidget* parent);
   explicit ctkFlowLayout();
   virtual ~ctkFlowLayout();
-  
-  /// If orientation is Qt::Horizontal, items are layed out from left to right
-  /// then top to bottom. If orientation is Qt::Vertical, items are layed out
-  /// from top to bottom then left to right.
+
   void setOrientation(Qt::Orientation orientation);
   Qt::Orientation orientation()const;
 
-  /// Indicates how the size hint of the layout should behave. The preferred
-  /// expanding direction can be different than the orientation of the layout.
-  /// It can be a combination of Qt::Horizontal and Qt::Vertical, in that case
-  /// the layout will try to expand in a square shape (evenly distribute the
-  /// number of rows and columns).
   void setPreferredExpandingDirections(Qt::Orientations directions);
   Qt::Orientations preferredExpandingDirections()const;
 
-  /// Horizontal space between items, if the spacing is <0, a default spacing
-  /// set on the parent/style will be used.
   int horizontalSpacing() const;
   void setHorizontalSpacing(int);
 
-  /// Vertical space between items, if the spacing is <0, a default spacing
-  /// set on the parent/style will be used. 
   int verticalSpacing() const;
   void setVerticalSpacing(int);
-  
-  /// Force the items to be horizontally aligned based on the largest item
-  /// to display.
-  /// True by default.
+
   bool alignItems()const;
   void setAlignItems(bool);
 
@@ -88,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;

+ 107 - 38
Libs/Widgets/ctkThumbnailListWidget.cpp

@@ -27,6 +27,7 @@
 #include <QPixmap>
 #include <QPushButton>
 #include <QResizeEvent>
+#include <QScrollBar>
 
 // ctk includes
 #include "ctkLogger.h"
@@ -48,43 +49,49 @@ static ctkLogger logger("org.commontk.Widgets.ctkThumbnailListWidget");
 // ctkThumbnailListWidgetPrivate methods
 
 //----------------------------------------------------------------------------
-ctkThumbnailListWidgetPrivate::ctkThumbnailListWidgetPrivate(ctkThumbnailListWidget* parent): q_ptr(parent){
-
+ctkThumbnailListWidgetPrivate
+::ctkThumbnailListWidgetPrivate(ctkThumbnailListWidget* parent)
+  : q_ptr(parent)
+{
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidgetPrivate::init(){
+void ctkThumbnailListWidgetPrivate::init()
+{
   Q_Q(ctkThumbnailListWidget);
 
   this->setupUi(q);
-  this->ScrollAreaContentWidget->setLayout(new ctkFlowLayout);
-  qobject_cast<ctkFlowLayout*>(this->ScrollAreaContentWidget->layout())->setHorizontalSpacing(4);
+  ctkFlowLayout* flowLayout = new ctkFlowLayout;
+  //flowLayout->setOrientation(Qt::Vertical);
+  //flowLayout->setPreferredExpandingDirections(Qt::Horizontal);
+  flowLayout->setHorizontalSpacing(4);
+  this->ScrollAreaContentWidget->setLayout(flowLayout);
+  this->ScrollArea->installEventFilter(q);
 
   this->ThumbnailSize = QSize(-1, -1);
   this->CurrentThumbnail = -1;
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidgetPrivate::clearAllThumbnails(){
-    Q_Q(ctkThumbnailListWidget);
+void ctkThumbnailListWidgetPrivate::clearAllThumbnails()
+{
+  Q_Q(ctkThumbnailListWidget);
 
-    // Remove previous displayed thumbnails
-    QLayoutItem* item;
-    while((item = this->ScrollAreaContentWidget->layout()->takeAt(0)))
+  // Remove previous displayed thumbnails
+  QLayoutItem* item;
+  while((item = this->ScrollAreaContentWidget->layout()->takeAt(0)))
     {
-        ctkThumbnailLabel* thumbnailWidget = qobject_cast<ctkThumbnailLabel*>(item->widget());
-        if(thumbnailWidget)
-        {
-            q->disconnect(thumbnailWidget, SIGNAL(selected(ctkThumbnailLabel)), q, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
-            q->disconnect(thumbnailWidget, SIGNAL(selected(ctkThumbnailLabel)), q, SIGNAL(selected(ctkThumbnailLabel)));
-            q->disconnect(thumbnailWidget, SIGNAL(doubleClicked(ctkThumbnailLabel)), q, SIGNAL(doubleClicked(ctkThumbnailLabel)));
-        }
-        item->widget()->deleteLater();
+    ctkThumbnailLabel* thumbnailWidget = qobject_cast<ctkThumbnailLabel*>(item->widget());
+    if(thumbnailWidget)
+      {
+      q->disconnect(thumbnailWidget, SIGNAL(selected(ctkThumbnailLabel)), q, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
+      q->disconnect(thumbnailWidget, SIGNAL(selected(ctkThumbnailLabel)), q, SIGNAL(selected(ctkThumbnailLabel)));
+      q->disconnect(thumbnailWidget, SIGNAL(doubleClicked(ctkThumbnailLabel)), q, SIGNAL(doubleClicked(ctkThumbnailLabel)));
+      }
+    item->widget()->deleteLater();
     }
 }
 
-
-
 //----------------------------------------------------------------------------
 // ctkThumbnailListWidget methods
 
@@ -111,9 +118,9 @@ ctkThumbnailListWidget::ctkThumbnailListWidget(ctkThumbnailListWidgetPrivate *_p
 //----------------------------------------------------------------------------
 ctkThumbnailListWidget::~ctkThumbnailListWidget()
 {
-    Q_D(ctkThumbnailListWidget);
+  Q_D(ctkThumbnailListWidget);
 
-    d->clearAllThumbnails();
+  d->clearAllThumbnails();
 }
 
 //----------------------------------------------------------------------------
@@ -122,22 +129,24 @@ void ctkThumbnailListWidget::addThumbnails(QList<QPixmap> thumbnails)
   Q_D(ctkThumbnailListWidget);
   for(int i=0; i<thumbnails.count(); i++)
     {
-      ctkThumbnailLabel* widget = new ctkThumbnailLabel(d->ScrollAreaContentWidget);
-      widget->setText("");
-      if(d->ThumbnailSize.isValid()){
-        widget->setFixedSize(d->ThumbnailSize);
+    ctkThumbnailLabel* widget = new ctkThumbnailLabel(d->ScrollAreaContentWidget);
+    widget->setText("");
+    if(d->ThumbnailSize.isValid())
+      {
+      widget->setFixedSize(d->ThumbnailSize);
       }
-      widget->setPixmap(thumbnails[i]);
-      d->ScrollAreaContentWidget->layout()->addWidget(widget);
+    widget->setPixmap(thumbnails[i]);
+    d->ScrollAreaContentWidget->layout()->addWidget(widget);
 
-      this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
-      this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SIGNAL(selected(ctkThumbnailLabel)));
-      this->connect(widget, SIGNAL(doubleClicked(ctkThumbnailLabel)), this, SIGNAL(doubleClicked(ctkThumbnailLabel)));
+    this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
+    this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SIGNAL(selected(ctkThumbnailLabel)));
+    this->connect(widget, SIGNAL(doubleClicked(ctkThumbnailLabel)), this, SIGNAL(doubleClicked(ctkThumbnailLabel)));
     }
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidget::setCurrentThumbnail(int index){
+void ctkThumbnailListWidget::setCurrentThumbnail(int index)
+{
   Q_D(ctkThumbnailListWidget);
 
   int count = d->ScrollAreaContentWidget->layout()->count();
@@ -164,14 +173,16 @@ void ctkThumbnailListWidget::setCurrentThumbnail(int index){
 }
 
 //----------------------------------------------------------------------------
-int ctkThumbnailListWidget::currentThumbnail(){
+int ctkThumbnailListWidget::currentThumbnail()
+{
   Q_D(ctkThumbnailListWidget);
 
   return d->CurrentThumbnail;
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidget::onThumbnailSelected(const ctkThumbnailLabel &widget){
+void ctkThumbnailListWidget::onThumbnailSelected(const ctkThumbnailLabel &widget)
+{
   Q_D(ctkThumbnailListWidget);
   for(int i=0; i<d->ScrollAreaContentWidget->layout()->count(); i++)
     {
@@ -184,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())
     {
@@ -198,14 +228,53 @@ void ctkThumbnailListWidget::setThumbnailSize(QSize size){
 }
 
 //----------------------------------------------------------------------------
-QSize ctkThumbnailListWidget::thumbnailSize()const{
+QSize ctkThumbnailListWidget::thumbnailSize()const
+{
   Q_D(const ctkThumbnailListWidget);
   return d->ThumbnailSize;
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidget::clearThumbnails(){
+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);
+}

+ 12 - 3
Libs/Widgets/ctkThumbnailListWidget.h

@@ -21,12 +21,12 @@
 #ifndef __ctkThumbnailListWidget_h
 #define __ctkThumbnailListWidget_h
 
-// Qt includes 
+// Qt includes
 #include <QWidget>
+class QResizeEvent;
 
+// CTK includes
 #include "ctkWidgetsExport.h"
-
-class QModelIndex;
 class ctkThumbnailListWidgetPrivate;
 class ctkThumbnailLabel;
 
@@ -35,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;
@@ -53,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;
 
@@ -71,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);

+ 2 - 1
Libs/Widgets/ctkThumbnailListWidget_p.h

@@ -28,7 +28,8 @@ class ctkThumbnailListWidget;
 
 //----------------------------------------------------------------------------
 /// \ingroup Widgets
-class CTK_WIDGETS_EXPORT ctkThumbnailListWidgetPrivate : public Ui_ctkThumbnailListWidget
+class CTK_WIDGETS_EXPORT ctkThumbnailListWidgetPrivate
+  : public Ui_ctkThumbnailListWidget
 {
   Q_DECLARE_PUBLIC(ctkThumbnailListWidget);
 public: