Преглед изворни кода

Merge branch 'ctkSizeGrip'

* ctkSizeGrip:
  Add ctkExpandableWidget and ctkSizeGrip
Julien Finet пре 12 година
родитељ
комит
b68885b0d9

+ 6 - 0
Libs/Widgets/CMakeLists.txt

@@ -75,6 +75,8 @@ set(KIT_SRCS
   ctkErrorLogWidget.h
   ctkExpandButton.cpp
   ctkExpandButton.h
+  ctkExpandableWidget.cpp
+  ctkExpandableWidget.h
   ctkFileDialog.cpp
   ctkFileDialog.h
   ctkFittedTextBrowser.cpp
@@ -139,6 +141,8 @@ set(KIT_SRCS
   ctkSignalMapper.h
   ctkSimpleLayoutManager.cpp
   ctkSimpleLayoutManager.h
+  ctkSizeGrip.cpp
+  ctkSizeGrip.h
   ctkSliderWidget.cpp
   ctkSliderWidget.h
   ctkTestApplication.cpp
@@ -214,6 +218,7 @@ set(KIT_MOC_SRCS
   ctkErrorLogWidget.h
   ctkErrorLogStatusMessageHandler.h
   ctkExpandButton.h
+  ctkExpandableWidget.h
   ctkFileDialog.h
   ctkFittedTextBrowser.h
   ctkFlatProxyModel.h
@@ -245,6 +250,7 @@ set(KIT_MOC_SRCS
   ctkSettingsPanel.h
   ctkSignalMapper.h
   ctkSimpleLayoutManager.h
+  ctkSizeGrip.h
   ctkSliderWidget.h
   ctkTestApplication.h
   ctkThumbnailLabel.h

+ 3 - 0
Libs/Widgets/Plugins/CMakeLists.txt

@@ -44,6 +44,8 @@ set(PLUGIN_SRCS
   ctkDynamicSpacerPlugin.h
   ctkExpandButtonPlugin.cpp
   ctkExpandButtonPlugin.h
+  ctkExpandableWidgetPlugin.cpp
+  ctkExpandableWidgetPlugin.h
   #ctkExtensionFactory.cpp
   #ctkExtensionFactory.h
   ctkFittedTextBrowserPlugin.cpp
@@ -114,6 +116,7 @@ set(PLUGIN_MOC_SRCS
   ctkDoubleSliderPlugin.h
   ctkDynamicSpacerPlugin.h
   ctkExpandButtonPlugin.h
+  ctkExpandableWidgetPlugin.h
   #ctkExtensionFactory.h
   ctkFittedTextBrowserPlugin.h
   ctkFontButtonPlugin.h

+ 8 - 4
Libs/Widgets/Plugins/ctkCollapsibleGroupBoxPlugin.cpp

@@ -26,15 +26,19 @@
 #include "ctkCollapsibleGroupBox.h"
 
 // --------------------------------------------------------------------------
-ctkCollapsibleGroupBoxPlugin::ctkCollapsibleGroupBoxPlugin(QObject *_parent) : QObject(_parent)
+ctkCollapsibleGroupBoxPlugin
+::ctkCollapsibleGroupBoxPlugin(QObject* parentObject)
+  : QObject(parentObject)
 {
 }
 
 // --------------------------------------------------------------------------
-QWidget *ctkCollapsibleGroupBoxPlugin::createWidget(QWidget *_parent)
+QWidget *ctkCollapsibleGroupBoxPlugin
+::createWidget(QWidget* futurParentWidget)
 {
-  ctkCollapsibleGroupBox* _widget = new ctkCollapsibleGroupBox(_parent);
-  return _widget;
+  ctkCollapsibleGroupBox* widget =
+    new ctkCollapsibleGroupBox(futurParentWidget);
+  return widget;
 }
 
 // --------------------------------------------------------------------------

+ 5 - 5
Libs/Widgets/Plugins/ctkCollapsibleGroupBoxPlugin.h

@@ -24,16 +24,16 @@
 // CTK includes
 #include "ctkWidgetsAbstractPlugin.h"
 
-class CTK_WIDGETS_PLUGINS_EXPORT ctkCollapsibleGroupBoxPlugin :
-  public QObject,
-  public ctkWidgetsAbstractPlugin
+class CTK_WIDGETS_PLUGINS_EXPORT ctkCollapsibleGroupBoxPlugin
+  : public QObject
+  , public ctkWidgetsAbstractPlugin
 {
   Q_OBJECT
 
 public:
-  ctkCollapsibleGroupBoxPlugin(QObject *_parent = 0);
+  ctkCollapsibleGroupBoxPlugin(QObject* parent = 0);
   
-  QWidget *createWidget(QWidget *_parent);
+  QWidget *createWidget(QWidget* parent);
   QString  domXml() const;
   QIcon    icon() const;
   QString  includeFile() const;

+ 79 - 0
Libs/Widgets/Plugins/ctkExpandableWidgetPlugin.cpp

@@ -0,0 +1,79 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkExpandableWidgetPlugin.h"
+#include "ctkExpandableWidget.h"
+
+// --------------------------------------------------------------------------
+ctkExpandableWidgetPlugin
+::ctkExpandableWidgetPlugin(QObject* parentObject)
+  : QObject(parentObject)
+{
+}
+
+// --------------------------------------------------------------------------
+QWidget *ctkExpandableWidgetPlugin
+::createWidget(QWidget* futurParentWidget)
+{
+  ctkExpandableWidget* widget =
+    new ctkExpandableWidget(futurParentWidget);
+  return widget;
+}
+
+// --------------------------------------------------------------------------
+QString ctkExpandableWidgetPlugin::domXml() const
+{
+  return "<widget class=\"ctkExpandableWidget\" \
+          name=\"ResizableFrame\">\n"
+          " <property name=\"geometry\">\n"
+          "  <rect>\n"
+          "   <x>0</x>\n"
+          "   <y>0</y>\n"
+          "   <width>300</width>\n"
+          "   <height>300</height>\n"
+          "  </rect>\n"
+          " </property>\n"
+          "</widget>\n";
+}
+
+// --------------------------------------------------------------------------
+QIcon ctkExpandableWidgetPlugin::icon() const
+{
+  return QIcon(":/Icons/widget.png");
+}
+
+// --------------------------------------------------------------------------
+QString ctkExpandableWidgetPlugin::includeFile() const
+{
+  return "ctkExpandableWidget.h";
+}
+
+// --------------------------------------------------------------------------
+bool ctkExpandableWidgetPlugin::isContainer() const
+{
+  return true;
+}
+
+// --------------------------------------------------------------------------
+QString ctkExpandableWidgetPlugin::name() const
+{
+  return "ctkExpandableWidget";
+}

+ 43 - 0
Libs/Widgets/Plugins/ctkExpandableWidgetPlugin.h

@@ -0,0 +1,43 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkExpandableWidgetPlugin_h
+#define __ctkExpandableWidgetPlugin_h
+
+// CTK includes
+#include "ctkWidgetsAbstractPlugin.h"
+
+class CTK_WIDGETS_PLUGINS_EXPORT ctkExpandableWidgetPlugin
+  : public QObject
+  , public ctkWidgetsAbstractPlugin
+{
+  Q_OBJECT
+public:
+  ctkExpandableWidgetPlugin(QObject* parent = 0);
+
+  QWidget *createWidget(QWidget* parent);
+  QString  domXml() const;
+  QIcon    icon() const;
+  QString  includeFile() const;
+  bool     isContainer() const;
+  QString  name() const;
+};
+
+#endif

+ 2 - 0
Libs/Widgets/Plugins/ctkWidgetsPlugins.h

@@ -42,6 +42,7 @@
 #include "ctkDoubleSliderPlugin.h"
 #include "ctkDynamicSpacerPlugin.h"
 #include "ctkExpandButtonPlugin.h"
+#include "ctkExpandableWidgetPlugin.h"
 #include "ctkFittedTextBrowserPlugin.h"
 #include "ctkFontButtonPlugin.h"
 #include "ctkMaterialPropertyPreviewLabelPlugin.h"
@@ -92,6 +93,7 @@ public:
             << new ctkDoubleSliderPlugin
             << new ctkDynamicSpacerPlugin
             << new ctkExpandButtonPlugin
+            << new ctkExpandableWidgetPlugin
             << new ctkFittedTextBrowserPlugin
             << new ctkFontButtonPlugin
             << new ctkMaterialPropertyPreviewLabelPlugin

+ 2 - 0
Libs/Widgets/Testing/Cpp/CMakeLists.txt

@@ -33,6 +33,7 @@ set(TEST_SOURCES
   ctkDynamicSpacerTest2.cpp
   ctkErrorLogWidgetTest1.cpp
   ctkExpandButtonTest1.cpp
+  ctkExpandableWidgetTest1.cpp
   ctkFileDialogTest1.cpp
   ctkFlatProxyModelTest.cpp
   ctkFittedTextBrowserTest1.cpp
@@ -256,6 +257,7 @@ SIMPLE_TEST( ctkRangeSliderTest )
 SIMPLE_TEST( ctkRangeSliderTest1 )
 SIMPLE_TEST( ctkRangeWidgetTest1 )
 SIMPLE_TEST( ctkRangeWidgetTest2 )
+SIMPLE_TEST( ctkResizableFrameTest1 )
 SIMPLE_TEST( ctkScreenshotDialogTest1 )
 SIMPLE_TEST( ctkSearchBoxTest1 )
 SIMPLE_TEST( ctkSearchBoxTest2 )

+ 104 - 0
Libs/Widgets/Testing/Cpp/ctkExpandableWidgetTest1.cpp

@@ -0,0 +1,104 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QDebug>
+#include <QGroupBox>
+#include <QLabel>
+#include <QStyle>
+#include <QTimer>
+#include <QTreeView>
+#include <QVBoxLayout>
+#include <QPlastiqueStyle>
+
+// CTK includes
+#include "ctkExpandableWidget.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+//-----------------------------------------------------------------------------
+int ctkExpandableWidgetTest1(int argc, char * argv [] )
+{
+  QApplication::setStyle(new QPlastiqueStyle);
+  QApplication app(argc, argv);
+
+  QWidget topLevel;
+  QHBoxLayout* topLevelLayout = new QHBoxLayout;
+  topLevel.setLayout(topLevelLayout);
+
+  QWidget frame(&topLevel);
+  //ctkExpandableWidget frame(&topLevel);
+  topLevelLayout->addWidget(&frame);
+
+  QGroupBox groupBox(&topLevel);
+  topLevelLayout->addWidget(&groupBox);
+
+  QLabel* label0 = new QLabel("should be invisible", &frame);
+  QLabel* label1 = new QLabel("should be visible", &frame);
+  QLabel* label2 = new QLabel("should be invisible", &frame);
+  QLabel* label3 = new QLabel("should be visible", &frame);
+  QLabel* label4 = new QLabel("should be invisible", &frame);
+  QLabel* label5 = new QLabel("should be visible", &frame);
+  QLabel* label6 = new QLabel("should be visible", &frame);
+  QLabel* label7 = new QLabel("should be visible", &frame);
+  QLabel* label8 = new QLabel("should be invisible", &frame);
+
+  ctkExpandableWidget resizableFrame(&frame);
+  resizableFrame.setSizeGripInside(false);
+  QTreeView* treeView = new QTreeView(&resizableFrame);
+  treeView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
+  QVBoxLayout* resizableFrameLayout = new QVBoxLayout(&resizableFrame);
+  resizableFrameLayout->setContentsMargins(0,0,0,0);
+  //resizableFrame.setSizeGripMargins(QSize(2, 2));
+  resizableFrame.setLayout(resizableFrameLayout);
+  resizableFrameLayout->addWidget(treeView);
+  resizableFrame.setOrientations(Qt::Horizontal);
+
+  label8->setVisible(false);
+  label2->setVisible(false);
+  label4->setVisible(false);
+  label8->setVisible(false);
+
+  QVBoxLayout *vbox = new QVBoxLayout;
+  vbox->setContentsMargins(0, 0, 0, 0);
+  vbox->addWidget(label0);
+  vbox->addWidget(label1);
+  vbox->addWidget(label2);
+  vbox->addWidget(label3);
+  vbox->addWidget(label4);
+  vbox->addWidget(label5);
+  vbox->addWidget(label6);
+  vbox->addWidget(label7);
+  vbox->addWidget(label8);
+  vbox->addWidget(&resizableFrame);
+  frame.setLayout(vbox);
+
+  topLevel.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
+}

+ 216 - 0
Libs/Widgets/ctkExpandableWidget.cpp

@@ -0,0 +1,216 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QEvent>
+#include <QLayout>
+
+// CTK includes
+#include "ctkExpandableWidget.h"
+#include "ctkSizeGrip.h"
+
+//-----------------------------------------------------------------------------
+class ctkExpandableWidgetPrivate
+{
+  Q_DECLARE_PUBLIC(ctkExpandableWidget);
+
+protected:
+  ctkExpandableWidget* const q_ptr;
+
+public:
+  ctkExpandableWidgetPrivate(ctkExpandableWidget& object);
+  ~ctkExpandableWidgetPrivate();
+
+  void init();
+  void positionSizeGrip();
+
+  ctkSizeGrip* SizeGrip;
+  QSize SizeGripMargins;
+  bool SizeGripInside;
+};
+
+//-----------------------------------------------------------------------------
+ctkExpandableWidgetPrivate::ctkExpandableWidgetPrivate(ctkExpandableWidget& object)
+  : q_ptr(&object)
+  , SizeGrip(0)
+  , SizeGripInside(true)
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkExpandableWidgetPrivate::~ctkExpandableWidgetPrivate()
+{
+  this->SizeGrip = 0; // will be deleted automatically
+}
+
+//-----------------------------------------------------------------------------
+void ctkExpandableWidgetPrivate::init()
+{
+  Q_Q(ctkExpandableWidget);
+
+  this->SizeGrip = new ctkSizeGrip(q, q);
+  this->SizeGrip->setResizeWidget(false);
+  q->connect(this->SizeGrip, SIGNAL(widgetSizeHintChanged(QSize)),
+             q, SLOT(updateSizeHint()));
+
+  this->positionSizeGrip();
+}
+
+//-----------------------------------------------------------------------------
+void ctkExpandableWidgetPrivate::positionSizeGrip()
+{
+  Q_Q(ctkExpandableWidget);
+  if (!this->SizeGrip)
+    {
+    return;
+    }
+  if (this->SizeGripInside)
+    {
+    q->setContentsMargins(0, 0, 0, 0);
+    }
+  else
+    {
+    int rightMargin = q->orientations() == Qt::Horizontal ?
+      this->SizeGrip->sizeHint().width() : 0;
+    int bottomMargin = q->orientations() == Qt::Vertical ?
+      this->SizeGrip->sizeHint().height() : 0;
+    q->setContentsMargins(0, 0, rightMargin, bottomMargin);
+    }
+  int w = q->orientations() == Qt::Vertical ? q->width() : this->SizeGrip->sizeHint().width();
+  int h = q->orientations() == Qt::Horizontal ? q->height() : this->SizeGrip->sizeHint().height();
+  int x = q->width() - w - this->SizeGripMargins.width();
+  int y = q->height() - h - this->SizeGripMargins.height();
+  this->SizeGrip->setGeometry(x, y, w, h);
+}
+
+//-----------------------------------------------------------------------------
+ctkExpandableWidget::ctkExpandableWidget(QWidget *parentWidget)
+  : Superclass(parentWidget)
+  , d_ptr(new ctkExpandableWidgetPrivate(*this))
+{
+  Q_D(ctkExpandableWidget);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkExpandableWidget::~ctkExpandableWidget()
+{
+}
+
+//------------------------------------------------------------------------------
+void ctkExpandableWidget::setOrientations(Qt::Orientations orientations)
+{
+  Q_D(ctkExpandableWidget);
+  d->SizeGrip->setOrientations(orientations);
+  d->positionSizeGrip();
+  this->updateGeometry();
+}
+
+//------------------------------------------------------------------------------
+Qt::Orientations ctkExpandableWidget::orientations()const
+{
+  Q_D(const ctkExpandableWidget);
+  return d->SizeGrip->orientations();
+}
+
+//------------------------------------------------------------------------------
+void ctkExpandableWidget::setSizeGripInside(bool inside)
+{
+  Q_D(ctkExpandableWidget);
+  d->SizeGripInside = inside;
+  d->positionSizeGrip();
+  this->updateGeometry();
+}
+
+//------------------------------------------------------------------------------
+bool ctkExpandableWidget::isSizeGripInside()const
+{
+  Q_D(const ctkExpandableWidget);
+  return d->SizeGripInside;
+}
+
+//------------------------------------------------------------------------------
+void ctkExpandableWidget::setSizeGripMargins(QSize margins)
+{
+  Q_D(ctkExpandableWidget);
+  d->SizeGripMargins = margins;
+  d->positionSizeGrip();
+  this->updateGeometry();
+}
+
+//------------------------------------------------------------------------------
+QSize ctkExpandableWidget::sizeGripMargins()const
+{
+  Q_D(const ctkExpandableWidget);
+  return d->SizeGripMargins;
+}
+
+/*
+//------------------------------------------------------------------------------
+QSize ctkExpandableWidget::minimumSizeHint()const
+{
+  Q_D(const ctkExpandableWidget);
+  return d->recomputeSizeHint(d->MinimumSizeHint);
+}
+*/
+
+//------------------------------------------------------------------------------
+QSize ctkExpandableWidget::sizeHint()const
+{
+  Q_D(const ctkExpandableWidget);
+  QSize sizeHint = this->Superclass::sizeHint();
+  if (d->SizeGrip->widgetSizeHint().width())
+    {
+    sizeHint.setWidth(d->SizeGrip->widgetSizeHint().width());
+    }
+  if (d->SizeGrip->widgetSizeHint().height())
+    {
+    sizeHint.setHeight(d->SizeGrip->widgetSizeHint().height());
+    }
+  return sizeHint;
+}
+
+//------------------------------------------------------------------------------
+void ctkExpandableWidget::resizeEvent(QResizeEvent* event)
+{
+  Q_D(ctkExpandableWidget);
+  this->Superclass::resizeEvent(event);
+  d->positionSizeGrip();
+}
+
+//------------------------------------------------------------------------------
+bool ctkExpandableWidget::event(QEvent* event)
+{
+  Q_D(ctkExpandableWidget);
+  bool res = this->Superclass::event(event);
+  if (event->type() == QEvent::ChildAdded &&
+      d->SizeGrip && d->SizeGripInside)
+    {
+    d->SizeGrip->raise();
+    }
+  return res;
+}
+
+//------------------------------------------------------------------------------
+void ctkExpandableWidget::updateSizeHint()
+{
+  this->updateGeometry();
+}

+ 81 - 0
Libs/Widgets/ctkExpandableWidget.h

@@ -0,0 +1,81 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkExpandableWidget_h
+#define __ctkExpandableWidget_h
+
+// Qt includes
+#include <QFrame>
+class QResizeEvent;
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+class ctkExpandableWidgetPrivate;
+
+/**
+ * \ingroup Widgets
+ * \brief Frame that can be resized by the user.
+*/
+class CTK_WIDGETS_EXPORT ctkExpandableWidget: public QFrame
+{
+  Q_OBJECT
+  // Qt::Horizontal|Qt::Vertical by default
+  Q_PROPERTY(Qt::Orientations orientations READ orientations WRITE setOrientations)
+  // true by default.
+  Q_PROPERTY(bool sizeGripInside READ isSizeGripInside WRITE setSizeGripInside)
+  Q_PROPERTY(QSize sizeGripMargins READ sizeGripMargins WRITE setSizeGripMargins)
+public:
+  typedef QFrame Superclass;
+
+  /** Constructor
+   *  /param parent       Parent widget
+  */
+  ctkExpandableWidget(QWidget *parent=0);
+  virtual ~ctkExpandableWidget();
+
+  void setOrientations(Qt::Orientations orientations);
+  Qt::Orientations orientations()const;
+
+  void setSizeGripInside(bool);
+  bool isSizeGripInside()const;
+
+  void setSizeGripMargins(QSize margins);
+  QSize sizeGripMargins()const;
+
+  //virtual QSize minimumSizeHint()const;
+  virtual QSize sizeHint()const;
+
+public Q_SLOTS:
+  void updateSizeHint();
+
+protected:
+  QScopedPointer<ctkExpandableWidgetPrivate> d_ptr;
+
+  virtual void resizeEvent(QResizeEvent* event);
+  virtual bool event(QEvent* event);
+
+private:
+  Q_DECLARE_PRIVATE(ctkExpandableWidget);
+  Q_DISABLE_COPY(ctkExpandableWidget);
+
+  //Q_PRIVATE_SLOT(d_ptr, void _q_recomputeCompleterPopupSize())
+};
+
+#endif // __ctkExpandableWidget_h

+ 369 - 0
Libs/Widgets/ctkSizeGrip.cpp

@@ -0,0 +1,369 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkSizeGrip.h"
+
+#include <QApplication>
+#include <QDebug>
+#include <QEvent>
+#include <QLayout>
+#include <QMouseEvent>
+#include <QPaintEvent>
+#include <QStyle>
+#include <QStyleOption>
+#include <QPainter>
+
+//------------------------------------------------------------------------------
+static inline bool hasHeightForWidth(QWidget *widget)
+{
+  if (!widget)
+    {
+    return false;
+    }
+  if (QLayout *layout = widget->layout())
+    {
+    return layout->hasHeightForWidth();
+    }
+  return widget->sizePolicy().hasHeightForWidth();
+}
+
+//------------------------------------------------------------------------------
+class ctkSizeGripPrivate
+{
+  Q_DECLARE_PUBLIC(ctkSizeGrip)
+
+protected:
+  ctkSizeGrip* const q_ptr;
+
+public:
+  ctkSizeGripPrivate(ctkSizeGrip& object);
+  void init();
+
+  Qt::Orientations Orientations;
+
+  QWidget* WidgetToResize;
+
+  QRect WidgetGeom;
+  QSize WidgetSizeHint;
+  QSize WidgetMaxSize;
+
+  QPoint StartPos;
+  bool Hover;
+  bool Pressed;
+  bool Resize;
+};
+
+//------------------------------------------------------------------------------
+ctkSizeGripPrivate::ctkSizeGripPrivate(ctkSizeGrip& object)
+  : q_ptr(&object)
+  , Orientations(Qt::Horizontal | Qt::Vertical)
+  , WidgetToResize(0)
+  , Hover(false)
+  , Pressed(false)
+  , Resize(false)
+{
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGripPrivate::init()
+{
+  Q_Q(ctkSizeGrip);
+  q->setOrientations(Qt::Horizontal | Qt::Vertical);
+}
+
+//------------------------------------------------------------------------------
+ctkSizeGrip::ctkSizeGrip(QWidget* parent)
+  : QWidget(parent)
+  , d_ptr(new ctkSizeGripPrivate(*this))
+{
+  Q_D(ctkSizeGrip);
+  d->WidgetToResize = parent;
+  d->init();
+}
+
+//------------------------------------------------------------------------------
+ctkSizeGrip::ctkSizeGrip(QWidget* target, QWidget* parent)
+  : QWidget(parent)
+  , d_ptr(new ctkSizeGripPrivate(*this))
+{
+  Q_D(ctkSizeGrip);
+  d->WidgetToResize = target;
+  d->init();
+}
+
+//------------------------------------------------------------------------------
+ctkSizeGrip::~ctkSizeGrip()
+{
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::setOrientations(Qt::Orientations newOrientations)
+{
+  Q_D(ctkSizeGrip);
+  d->Orientations = newOrientations;
+  QCursor newCursor;
+  QSizePolicy newSizePolicy;
+  switch(d->Orientations)
+    {
+    case Qt::Horizontal:
+      newCursor = Qt::SizeHorCursor;
+      newSizePolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+      break;
+    case Qt::Vertical:
+      newCursor = Qt::SizeVerCursor;
+      newSizePolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+      break;
+    default:
+      newCursor = this->isLeftToRight() ?
+        Qt::SizeFDiagCursor : Qt::SizeBDiagCursor;
+      newSizePolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+      break;
+    };
+  this->setCursor(newCursor);
+  this->setSizePolicy(newSizePolicy);
+  this->updateGeometry(); // sizeHint might change
+}
+
+//------------------------------------------------------------------------------
+Qt::Orientations ctkSizeGrip::orientations()const
+{
+  Q_D(const ctkSizeGrip);
+  return d->Orientations;
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::setWidgetToResize(QWidget* target)
+{
+  Q_D(ctkSizeGrip);
+  d->WidgetToResize = target;
+  d->WidgetSizeHint = QSize();
+}
+
+//------------------------------------------------------------------------------
+QWidget* ctkSizeGrip::widgetToResize()const
+{
+  Q_D(const ctkSizeGrip);
+  return d->WidgetToResize;
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::setResizeWidget(bool resize)
+{
+  Q_D(ctkSizeGrip);
+  d->Resize = resize;
+  this->setWidgetSizeHint(d->WidgetSizeHint);
+}
+
+//------------------------------------------------------------------------------
+bool ctkSizeGrip::resizeWidget()const
+{
+  Q_D(const ctkSizeGrip);
+  return d->Resize;
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::setWidgetSizeHint(QSize sizeHint)
+{
+  Q_D(ctkSizeGrip);
+  if (d->Resize && d->WidgetToResize)
+    {
+    QSize newSize = d->WidgetToResize->size();
+    if (sizeHint.width())
+      {
+      newSize.setWidth(sizeHint.width());
+      }
+    if (sizeHint.height())
+      {
+      newSize.setHeight(sizeHint.height());
+      }
+    d->WidgetToResize->resize(newSize);
+    }
+  if (d->WidgetSizeHint != sizeHint)
+    {
+    d->WidgetSizeHint = sizeHint;
+    emit widgetSizeHintChanged(d->WidgetSizeHint);
+    }
+}
+
+//------------------------------------------------------------------------------
+QSize ctkSizeGrip::widgetSizeHint()const
+{
+  Q_D(const ctkSizeGrip);
+  return d->WidgetSizeHint;
+}
+
+//------------------------------------------------------------------------------
+QSize ctkSizeGrip::sizeHint() const
+{
+  Q_D(const ctkSizeGrip);
+  QSize minSize;
+  QStyle::ContentsType contents;
+  switch (d->Orientations)
+    {
+    case Qt::Horizontal:
+    case Qt::Vertical:
+      {
+      contents = QStyle::CT_Splitter;
+      int splitterWidth = this->style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
+      minSize = QSize(splitterWidth, splitterWidth);
+      break;
+      }
+    default:
+      contents = QStyle::CT_SizeGrip;
+      minSize = QSize(13, 13);
+      break;
+    };
+  QStyleOption opt(0);
+  opt.init(this);
+  return (this->style()->sizeFromContents(contents, &opt, minSize, this).
+          expandedTo(QApplication::globalStrut()));
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::paintEvent(QPaintEvent *event)
+{
+  Q_UNUSED(event);
+  Q_D(ctkSizeGrip);
+  QPainter painter(this);
+  switch (d->Orientations)
+    {
+    case Qt::Horizontal:
+    case Qt::Vertical:
+      {
+      QStyleOption opt(0);
+      opt.rect = rect();
+      opt.palette = palette();
+      opt.state = (d->Orientations == Qt::Horizontal) ?
+        QStyle::State_Horizontal : QStyle::State_None;
+      if (d->Hover)
+        {
+        opt.state |= QStyle::State_MouseOver;
+        }
+      if (d->Pressed)
+        {
+        opt.state |= QStyle::State_Sunken;
+        }
+      if (isEnabled())
+        {
+        opt.state |= QStyle::State_Enabled;
+        }
+      this->style()->drawControl(QStyle::CE_Splitter, &opt, &painter, this);
+      break;
+      }
+    default:
+      {
+      QStyleOptionSizeGrip opt;
+      opt.init(this);
+      opt.corner = this->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
+      style()->drawControl(QStyle::CE_SizeGrip, &opt, &painter, this);
+      break;
+      }
+    };
+}
+
+//------------------------------------------------------------------------------
+bool ctkSizeGrip::event(QEvent *event)
+{
+  Q_D(ctkSizeGrip);
+  switch(event->type())
+    {
+    case QEvent::HoverEnter:
+      d->Hover = true;
+      update();
+      break;
+    case QEvent::HoverLeave:
+      d->Hover = false;
+      update();
+      break;
+    default:
+      break;
+    }
+  return this->Superclass::event(event);
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::mousePressEvent(QMouseEvent * e)
+{
+  if (e->button() != Qt::LeftButton)
+    {
+    QWidget::mousePressEvent(e);
+    return;
+    }
+
+  Q_D(ctkSizeGrip);
+  d->StartPos = e->globalPos();
+  d->Pressed = true;
+  d->WidgetGeom = d->WidgetToResize->geometry();
+  d->WidgetSizeHint = QSize();
+  d->WidgetMaxSize = d->WidgetToResize->maximumSize();
+}
+
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::mouseMoveEvent(QMouseEvent * e)
+{
+  if (e->buttons() != Qt::LeftButton)
+    {
+    this->Superclass::mouseMoveEvent(e);
+    return;
+    }
+
+  Q_D(ctkSizeGrip);
+  if (!d->Pressed || d->WidgetToResize->testAttribute(Qt::WA_WState_ConfigPending))
+    {
+    return;
+    }
+
+  QPoint newPos(e->globalPos());
+  QSize offset(newPos.x() - d->StartPos.x(), newPos.y() - d->StartPos.y());
+
+  QSize widgetSizeHint = d->WidgetGeom.size();
+  if (d->Orientations & Qt::Vertical)
+    {
+    widgetSizeHint.rheight() = d->WidgetGeom.height() + offset.height();
+    }
+
+  if (d->Orientations & Qt::Horizontal)
+    {
+    widgetSizeHint.rwidth() = d->WidgetGeom.width() + offset.width() * (this->isLeftToRight() ? 1 : -1);
+    }
+  widgetSizeHint = widgetSizeHint.boundedTo(d->WidgetMaxSize);
+
+  widgetSizeHint = QLayout::closestAcceptableSize(d->WidgetToResize, widgetSizeHint)
+    .expandedTo(QApplication::globalStrut());
+
+  this->setWidgetSizeHint(
+    QSize(d->Orientations & Qt::Horizontal ? widgetSizeHint.width() : 0,
+          d->Orientations & Qt::Vertical ? widgetSizeHint.height() : 0));
+}
+
+//------------------------------------------------------------------------------
+void ctkSizeGrip::mouseReleaseEvent(QMouseEvent *mouseEvent)
+{
+  if (mouseEvent->button() != Qt::LeftButton)
+    {
+    this->Superclass::mouseReleaseEvent(mouseEvent);
+    return;
+    }
+  Q_D(ctkSizeGrip);
+  d->Pressed = false;
+  d->StartPos = QPoint();
+}

+ 78 - 0
Libs/Widgets/ctkSizeGrip.h

@@ -0,0 +1,78 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkSizeGrip_h
+#define __ctkSizeGrip_h
+
+// Qt includes
+#include <QWidget>
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+class ctkSizeGripPrivate;
+
+class CTK_WIDGETS_EXPORT ctkSizeGrip
+  : public QWidget
+{
+  Q_OBJECT
+  Q_PROPERTY(Qt::Orientations orientations READ orientations WRITE setOrientations)
+  Q_PROPERTY(QSize widgetSizeHint READ widgetSizeHint WRITE setWidgetSizeHint)
+  Q_PROPERTY(bool resizeWidget READ resizeWidget WRITE setResizeWidget)
+
+public:
+  typedef QWidget Superclass;
+  explicit ctkSizeGrip(QWidget *parent);
+  explicit ctkSizeGrip(QWidget* widgetToResize, QWidget *parent);
+  virtual ~ctkSizeGrip();
+
+  void setOrientations(Qt::Orientations orientations);
+  Qt::Orientations orientations()const;
+
+  QWidget* widgetToResize()const;
+  void setWidgetToResize(QWidget* target);
+
+  QSize widgetSizeHint()const;
+
+  void setResizeWidget(bool resize);
+  bool resizeWidget()const;
+
+  virtual QSize sizeHint() const;
+
+public Q_SLOTS:
+  void setWidgetSizeHint(QSize sizeHint);
+
+Q_SIGNALS:
+  void widgetSizeHintChanged(QSize sizeHint);
+
+protected:
+  QScopedPointer<ctkSizeGripPrivate> d_ptr;
+
+  void paintEvent(QPaintEvent* );
+  bool event(QEvent* );
+  void mousePressEvent(QMouseEvent* );
+  void mouseMoveEvent(QMouseEvent* );
+  void mouseReleaseEvent(QMouseEvent* mouseEvent);
+
+private:
+  Q_DECLARE_PRIVATE(ctkSizeGrip)
+  Q_DISABLE_COPY(ctkSizeGrip)
+};
+
+#endif // __ctkSizeGrip_h