소스 검색

Add ctkPushButton, an advanced QPushButton

For now, it controls the text and icon alignments.
Julien Finet 12 년 전
부모
커밋
6adb573b65

+ 4 - 0
Libs/Widgets/CMakeLists.txt

@@ -122,6 +122,9 @@ set(KIT_SRCS
   ctkPopupWidget_p.h
   ctkProxyStyle.cpp
   ctkProxyStyle.h
+  ctkPushButton.cpp
+  ctkPushButton.h
+  ctkPushButton_p.h
   ctkQImageView.cpp
   ctkQImageView.h
   ctkRangeSlider.cpp
@@ -244,6 +247,7 @@ set(KIT_MOC_SRCS
   ctkPopupWidget.h
   ctkPopupWidget_p.h
   ctkProxyStyle.h
+  ctkPushButton.h
   ctkQImageView.h
   ctkRangeSlider.h
   ctkRangeWidget.h

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

@@ -72,6 +72,8 @@ set(PLUGIN_SRCS
   ctkPathListWidgetPlugin.h
   ctkPopupWidgetPlugin.cpp
   ctkPopupWidgetPlugin.h
+  ctkPushButtonPlugin.cpp
+  ctkPushButtonPlugin.h
   ctkRangeSliderPlugin.cpp
   ctkRangeSliderPlugin.h
   ctkRangeWidgetPlugin.cpp
@@ -132,6 +134,7 @@ set(PLUGIN_MOC_SRCS
   ctkPathListButtonsWidgetPlugin.h
   ctkPathListWidgetPlugin.h
   ctkPopupWidgetPlugin.h
+  ctkPushButtonPlugin.h
   ctkRangeSliderPlugin.h
   ctkRangeWidgetPlugin.h
   ctkThumbnailLabelPlugin.h

+ 68 - 0
Libs/Widgets/Plugins/ctkPushButtonPlugin.cpp

@@ -0,0 +1,68 @@
+/*=========================================================================
+
+  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 "ctkPushButtonPlugin.h"
+#include "ctkPushButton.h"
+
+//-----------------------------------------------------------------------------
+ctkPushButtonPlugin::ctkPushButtonPlugin(QObject* pluginParent)
+  : QObject(pluginParent)
+{
+}
+
+//-----------------------------------------------------------------------------
+QWidget *ctkPushButtonPlugin::createWidget(QWidget* parentForWidget)
+{
+  ctkPushButton* newWidget =
+    new ctkPushButton(parentForWidget);
+  return newWidget;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkPushButtonPlugin::domXml() const
+{
+  return "<widget class=\"ctkPushButton\" name=\"PushButton\">\n"
+         "</widget>\n";
+}
+
+// --------------------------------------------------------------------------
+QIcon ctkPushButtonPlugin::icon() const
+{
+  return QIcon(":/Icons/pushbutton.png");
+}
+
+//-----------------------------------------------------------------------------
+QString ctkPushButtonPlugin::includeFile() const
+{
+  return "ctkPushButton.h";
+}
+
+//-----------------------------------------------------------------------------
+bool ctkPushButtonPlugin::isContainer() const
+{
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkPushButtonPlugin::name() const
+{
+  return "ctkPushButton";
+}

+ 45 - 0
Libs/Widgets/Plugins/ctkPushButtonPlugin.h

@@ -0,0 +1,45 @@
+/*=========================================================================
+
+  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 __ctkPushButtonPlugin_h
+#define __ctkPushButtonPlugin_h
+
+// CTK includes
+#include "ctkWidgetsAbstractPlugin.h"
+
+class CTK_WIDGETS_PLUGINS_EXPORT ctkPushButtonPlugin
+  : public QObject
+  , public ctkWidgetsAbstractPlugin
+{
+  Q_OBJECT
+
+public:
+  ctkPushButtonPlugin(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

@@ -54,6 +54,7 @@
 #include "ctkPathListButtonsWidgetPlugin.h"
 #include "ctkPathListWidgetPlugin.h"
 #include "ctkPopupWidgetPlugin.h"
+#include "ctkPushButtonPlugin.h"
 #include "ctkRangeSliderPlugin.h"
 #include "ctkRangeWidgetPlugin.h"
 #include "ctkSearchBoxPlugin.h"
@@ -106,6 +107,7 @@ public:
             << new ctkPathListButtonsWidgetPlugin
             << new ctkPathListWidgetPlugin
             << new ctkPopupWidgetPlugin
+            << new ctkPushButtonPlugin
             << new ctkRangeSliderPlugin
             << new ctkRangeWidgetPlugin
             << new ctkSearchBoxPlugin

+ 48 - 250
Libs/Widgets/ctkCheckablePushButton.cpp

@@ -36,33 +36,29 @@
 
 // CTK includes
 #include "ctkCheckablePushButton.h"
+#include "ctkPushButton_p.h"
 
 //-----------------------------------------------------------------------------
-class ctkCheckablePushButtonPrivate
+class ctkCheckablePushButtonPrivate: public ctkPushButtonPrivate
 {
   Q_DECLARE_PUBLIC(ctkCheckablePushButton);
 protected:
   ctkCheckablePushButton* const q_ptr;
 public:
   ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object);
-  void init();
-
-  QRect checkboxRect() const;
-  QSize buttonSizeHint()const;
+  virtual void init();
+  virtual QStyleOptionButton drawIcon(QPainter* p);
 
   // Tuning of the button look&feel
-  Qt::Alignment TextAlignment;
-  Qt::Alignment IndicatorAlignment;
   Qt::ItemFlags CheckBoxFlags;
   Qt::CheckState CheckState;
 };
 
 //-----------------------------------------------------------------------------
 ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object)
-  :q_ptr(&object)
+  : ctkPushButtonPrivate(object)
+  , q_ptr(&object)
 {
-  this->TextAlignment = Qt::AlignLeft | Qt::AlignVCenter;
-  this->IndicatorAlignment = Qt::AlignLeft | Qt::AlignVCenter;
   this->CheckBoxFlags = Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
   this->CheckState = Qt::Unchecked;
 }
@@ -70,110 +66,58 @@ ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushBut
 //-----------------------------------------------------------------------------
 void ctkCheckablePushButtonPrivate::init()
 {
-}
+  Q_Q(ctkCheckablePushButton);
 
-//-----------------------------------------------------------------------------
-QRect ctkCheckablePushButtonPrivate::checkboxRect()const
-{
-  Q_Q(const ctkCheckablePushButton);
-  QRect rect;
   QStyleOptionButton opt;
-  q->initStyleOption(&opt);
+  opt.initFrom(q);
 
   QSize indicatorSize = QSize(q->style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, q),
                               q->style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, q));
-  int buttonHeight = opt.rect.height();
-  uint tf = this->TextAlignment;
-  if (q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
-    {
-    tf |= Qt::TextShowMnemonic;
-    }
-  else
-    {
-    tf |= Qt::TextHideMnemonic;
-    }
-  int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
-  int indicatorSpacing = q->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, q);
-  int buttonMargin = q->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, q);
-  if (this->IndicatorAlignment & Qt::AlignLeft)
-    {
-    rect = QRect((buttonHeight - indicatorSize.width()) / 2,
-                 (buttonHeight - indicatorSize.height()) / 2,
-                 indicatorSize.width(), indicatorSize.height());
-    }
-  else if (this->IndicatorAlignment & Qt::AlignHCenter)
-    {
-    int w = indicatorSize.width();
-    if (!opt.text.isEmpty() && (this->TextAlignment & Qt::AlignHCenter))
-      {
-      w += textWidth + indicatorSpacing;
-      }
-    rect = QRect(opt.rect.x()+ opt.rect.width() /2 - w / 2,
-                 (buttonHeight - indicatorSize.height()) / 2,
-                 indicatorSize.width(), indicatorSize.height());
-    if (this->TextAlignment & Qt::AlignLeft &&
-        rect.left() < opt.rect.x() + buttonMargin + textWidth)
-      {
-      rect.moveLeft(opt.rect.x() + buttonMargin + textWidth);
-      }
-    else if (this->TextAlignment & Qt::AlignRight &&
-             rect.right() > opt.rect.right() - buttonMargin - textWidth)
-      {
-      rect.moveRight(opt.rect.right() - buttonMargin - textWidth);
-      }
-    }
-  else if (this->IndicatorAlignment & Qt::AlignRight)
-    {
-    rect = QRect(opt.rect.width() - (buttonHeight - indicatorSize.width()) / 2
-                                  - indicatorSize.width(),
-                 (buttonHeight - indicatorSize.height()) / 2,
-                 indicatorSize.width(), indicatorSize.height());
-    }
-  return rect;
+  q->setIconSize(indicatorSize);
+  this->IconSpacing = q->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, q);
 }
 
 //-----------------------------------------------------------------------------
-QSize ctkCheckablePushButtonPrivate::buttonSizeHint()const
+QStyleOptionButton ctkCheckablePushButtonPrivate::drawIcon(QPainter* p)
 {
-  Q_Q(const ctkCheckablePushButton);
-  int w = 0, h = 0;
+  Q_Q(ctkCheckablePushButton);
 
-  QStyleOptionButton opt;
-  opt.initFrom(q);
-  
-  // indicator
-  QSize indicatorSize = QSize(q->style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, q),
-                              q->style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, q));
-  int indicatorSpacing = q->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, q);
-  int ih = indicatorSize.height();
-  int iw = indicatorSize.width() + indicatorSpacing;
-  w += iw;
-  h = qMax(h, ih);
-  
-  // text 
-  QString string(q->text());
-  bool empty = string.isEmpty();
-  if (empty)
+  QStyleOptionButton indicatorOpt;
+
+  indicatorOpt.init(q);
+  if (!(this->CheckBoxFlags & Qt::ItemIsUserCheckable))
     {
-    string = QString::fromLatin1("XXXX");
+    indicatorOpt.state &= ~QStyle::State_Enabled;
+    }
+  if (q->checkBoxControlsButton())
+    {
+    // Hack: calling setCheckable() instead of setCheckState while being in a
+    // control button mode leads to an inconsistent state, we need to make
+    // synchronize the 2 properties.
+    q->setCheckState(q->isCheckable() ? Qt::Checked : Qt::Unchecked);
     }
-  QFontMetrics fm = q->fontMetrics();
-  QSize sz = fm.size(Qt::TextShowMnemonic, string);
-  if(!empty || !w)
+  switch (this->CheckState)
     {
-    w += sz.width();
+    case Qt::Checked:
+      indicatorOpt.state |= QStyle::State_On;
+      break;
+    case Qt::PartiallyChecked:
+      indicatorOpt.state |= QStyle::State_NoChange;
+      break;
+    default:
+    case Qt::Unchecked:
+      indicatorOpt.state |= QStyle::State_Off;
+      break;
     }
-  h = qMax(h, sz.height());
-  //opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
-  QSize buttonSize = (q->style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), q).
-                      expandedTo(QApplication::globalStrut()));
-  return buttonSize;
+  indicatorOpt.rect = this->iconRect();
+  q->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &indicatorOpt, p, 0);
+  return indicatorOpt;
 }
 
+
 //-----------------------------------------------------------------------------
 ctkCheckablePushButton::ctkCheckablePushButton(QWidget* _parent)
-  :QPushButton(_parent)
-  , d_ptr(new ctkCheckablePushButtonPrivate(*this))
+  : ctkPushButton(new ctkCheckablePushButtonPrivate(*this), _parent)
 {
   Q_D(ctkCheckablePushButton);
   d->init();
@@ -181,44 +125,28 @@ ctkCheckablePushButton::ctkCheckablePushButton(QWidget* _parent)
 
 //-----------------------------------------------------------------------------
 ctkCheckablePushButton::ctkCheckablePushButton(const QString& title, QWidget* _parent)
-  :QPushButton(title, _parent)
-  , d_ptr(new ctkCheckablePushButtonPrivate(*this))
-{
-}
-
-//-----------------------------------------------------------------------------
-ctkCheckablePushButton::~ctkCheckablePushButton()
-{
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckablePushButton::setButtonTextAlignment(Qt::Alignment textAlignment)
+  : ctkPushButton(new ctkCheckablePushButtonPrivate(*this), _parent)
 {
   Q_D(ctkCheckablePushButton);
-  d->TextAlignment = textAlignment;
-  this->update();
+  d->init();
+  this->setText(title);
 }
 
 //-----------------------------------------------------------------------------
-Qt::Alignment ctkCheckablePushButton::buttonTextAlignment()const
+ctkCheckablePushButton::~ctkCheckablePushButton()
 {
-  Q_D(const ctkCheckablePushButton);
-  return d->TextAlignment;
 }
 
 //-----------------------------------------------------------------------------
 void ctkCheckablePushButton::setIndicatorAlignment(Qt::Alignment indicatorAlignment)
 {
-  Q_D(ctkCheckablePushButton);
-  d->IndicatorAlignment = indicatorAlignment;
-  this->update();
+  this->setIconAlignment(indicatorAlignment);
 }
 
 //-----------------------------------------------------------------------------
 Qt::Alignment ctkCheckablePushButton::indicatorAlignment()const
 {
-  Q_D(const ctkCheckablePushButton);
-  return d->IndicatorAlignment;
+  return this->iconAlignment();
 }
 
 //-----------------------------------------------------------------------------
@@ -303,144 +231,14 @@ bool ctkCheckablePushButton::isCheckBoxUserCheckable()const
 }
 
 //-----------------------------------------------------------------------------
-QSize ctkCheckablePushButton::minimumSizeHint()const
-{
-  Q_D(const ctkCheckablePushButton);
-  return d->buttonSizeHint();
-}
-
-//-----------------------------------------------------------------------------
-QSize ctkCheckablePushButton::sizeHint()const
-{
-  return this->minimumSizeHint();
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckablePushButton::paintEvent(QPaintEvent * _event)
-{
-  Q_UNUSED(_event);
-  Q_D(ctkCheckablePushButton);
-
-  QPainter p(this);
-  // Draw Button
-  QStyleOptionButton opt;
-  this->initStyleOption(&opt);
-
-  // Checkbox size
-  QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
-                              style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
-  // Replace the icon size by the checkbox size
-  opt.iconSize = indicatorSize;
-  // Draw the panel of the button (no text, no icon)
-  style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this);
-  // TBD is PE_PanelButtonCommand better ?
-  //style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
-  //int buttonHeight = opt.rect.height();
-  uint tf = d->TextAlignment;
-  if (this->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
-    {
-    tf |= Qt::TextShowMnemonic;
-    }
-  else
-    {
-    tf |= Qt::TextHideMnemonic;
-    }
-  int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
-  // Spacing between the text and the checkbox
-  int indicatorSpacing = this->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
-  int buttonMargin = this->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this);
-  // Draw Indicator
-  QStyleOptionButton indicatorOpt;
-  indicatorOpt.init(this);
-  if (!(d->CheckBoxFlags & Qt::ItemIsUserCheckable))
-    {
-    indicatorOpt.state &= ~QStyle::State_Enabled;
-    }
-  if (this->checkBoxControlsButton())
-    {
-    // Hack: calling setCheckable() instead of setCheckState while being in a
-    // control button mode leads to an inconsistent state, we need to make
-    // synchronize the 2 properties.
-    this->setCheckState(this->isCheckable() ? Qt::Checked : Qt::Unchecked);
-    }
-  switch (d->CheckState)
-    {
-    case Qt::Checked:
-      indicatorOpt.state |= QStyle::State_On;
-      break;
-    case Qt::PartiallyChecked:
-      indicatorOpt.state |= QStyle::State_NoChange;
-      break;
-    default:
-    case Qt::Unchecked:
-      indicatorOpt.state |= QStyle::State_Off;
-      break;
-    }
-  indicatorOpt.rect = d->checkboxRect();
-  this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &indicatorOpt, &p, 0);
-
-  // Draw Text
-  if (d->TextAlignment & Qt::AlignLeft)
-    {
-    if (d->IndicatorAlignment & Qt::AlignLeft)
-      {
-      opt.rect.setLeft(indicatorOpt.rect.right() + indicatorSpacing);
-      }
-    else
-      {
-      opt.rect.setLeft(opt.rect.x() + buttonMargin);
-      }
-    }
-  else if (d->TextAlignment & Qt::AlignHCenter)
-    {
-    if (d->IndicatorAlignment & Qt::AlignHCenter)
-      {
-      opt.rect.setLeft(indicatorOpt.rect.right() + indicatorSpacing);
-      }
-    else
-      {
-      opt.rect.setLeft(opt.rect.x() + opt.rect.width() / 2 - textWidth / 2);
-      if (d->IndicatorAlignment & Qt::AlignLeft)
-        {
-        opt.rect.setLeft( qMax(indicatorOpt.rect.right() + indicatorSpacing, opt.rect.left()) );
-        }
-      }
-    }
-  else if (d->TextAlignment & Qt::AlignRight)
-    {
-    if (d->IndicatorAlignment & Qt::AlignRight)
-      {
-      opt.rect.setLeft(indicatorOpt.rect.left() - indicatorSpacing - textWidth);
-      }
-    else
-      {
-      opt.rect.setLeft(opt.rect.right() - buttonMargin - textWidth);
-      }
-    }
-  // all the computations have been made infering the text would be left oriented
-  tf &= ~Qt::AlignHCenter & ~Qt::AlignRight;
-  tf |= Qt::AlignLeft;
-  this->style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
-                        opt.text, QPalette::ButtonText);
-}
-
-//-----------------------------------------------------------------------------
 bool ctkCheckablePushButton::hitButton(const QPoint & _pos)const
 {
   Q_D(const ctkCheckablePushButton);
-  return !d->checkboxRect().contains(_pos) 
+  return !d->iconRect().contains(_pos)
     && this->QPushButton::hitButton(_pos);
 }
 
 //-----------------------------------------------------------------------------
-void ctkCheckablePushButton::initStyleOption(QStyleOptionButton* option)const
-{
-  this->QPushButton::initStyleOption(option);
-  option->iconSize = QSize(this->style()->pixelMetric(QStyle::PM_IndicatorWidth, option, this),
-                           this->style()->pixelMetric(QStyle::PM_IndicatorHeight, option, this));
-}
-
-//-----------------------------------------------------------------------------
 void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
 {
   Q_D(ctkCheckablePushButton);
@@ -449,7 +247,7 @@ void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
     {
     return;
     }
-  if (d->checkboxRect().contains(e->pos()) &&
+  if (d->iconRect().contains(e->pos()) &&
       (d->CheckBoxFlags & Qt::ItemIsUserCheckable))
     {
     Qt::CheckState newCheckState;

+ 4 - 22
Libs/Widgets/ctkCheckablePushButton.h

@@ -21,12 +21,9 @@
 #ifndef __ctkCheckablePushButton_h
 #define __ctkCheckablePushButton_h
 
-// Qt includes
-#include <QPushButton>
-
 // CTK includes
 #include <ctkPimpl.h>
-
+#include "ctkPushButton.h"
 #include "ctkWidgetsExport.h"
 
 class ctkCheckablePushButtonPrivate;
@@ -48,10 +45,11 @@ class ctkCheckablePushButtonPrivate;
 /// setChecked(bool) slot.
 /// \warning The checkbox is drawn in place of the pushbuton icon, any icon
 /// will then be ignored.
-class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public QPushButton
+class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
 {
   Q_OBJECT
-  Q_PROPERTY(Qt::Alignment buttonTextAlignment READ buttonTextAlignment WRITE setButtonTextAlignment)
+  /// This property controls the location of the checkbox with regard to the text.
+  /// Qt::AlignLeft|Qt::AlignVCenter by default
   Q_PROPERTY(Qt::Alignment indicatorAlignment READ indicatorAlignment WRITE setIndicatorAlignment)
   Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged)
   Q_PROPERTY(bool checkBoxControlsButton READ checkBoxControlsButton WRITE setCheckBoxControlsButton)
@@ -62,21 +60,11 @@ public:
   ctkCheckablePushButton(const QString& text, QWidget *parent = 0);
   virtual ~ctkCheckablePushButton();
 
-  ///
-  /// Set the alignment of the text on the button,
-  /// Qt::AlignLeft|Qt::AlignVCenter by default.
-  void setButtonTextAlignment(Qt::Alignment textAlignment);
-  Qt::Alignment buttonTextAlignment()const;
-
-  ///
   /// Set the alignment of the indicator (arrow) on the button,
   /// Qt::AlignLeft|Qt::AlignVCenter by default.
   void setIndicatorAlignment(Qt::Alignment indicatorAlignment);
   Qt::Alignment indicatorAlignment()const;
 
-  virtual QSize minimumSizeHint()const;
-  virtual QSize sizeHint()const;
-
   virtual Qt::CheckState checkState()const;
   virtual void setCheckState(Qt::CheckState checkState);
 
@@ -94,15 +82,9 @@ Q_SIGNALS:
 
 protected:
   /// Reimplemented for internal reasons
-  virtual void paintEvent(QPaintEvent*);
-  /// Reimplemented for internal reasons
   virtual void mousePressEvent(QMouseEvent* event);
   /// Reimplemented for internal reasons
   virtual bool hitButton(const QPoint & pos) const;
-  /// Reimplemented for internal reasons
-  virtual void initStyleOption ( QStyleOptionButton * option ) const;
-protected:
-  QScopedPointer<ctkCheckablePushButtonPrivate> d_ptr;
 
 private:
   Q_DECLARE_PRIVATE(ctkCheckablePushButton);

+ 326 - 0
Libs/Widgets/ctkPushButton.cpp

@@ -0,0 +1,326 @@
+/*=========================================================================
+
+  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 <QLayout>
+#include <QPainter>
+#include <QStyle>
+#include <QStyleOptionButton>
+#include <QStylePainter>
+
+// CTK includes
+#include "ctkPushButton_p.h"
+
+//-----------------------------------------------------------------------------
+ctkPushButtonPrivate::ctkPushButtonPrivate(ctkPushButton& object)
+  :q_ptr(&object)
+{
+  this->ButtonTextAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
+  this->IconAlignment = Qt::AlignLeft | Qt::AlignVCenter;
+  this->IconSpacing = 4;
+}
+
+//-----------------------------------------------------------------------------
+void ctkPushButtonPrivate::init()
+{
+}
+
+//-----------------------------------------------------------------------------
+QRect ctkPushButtonPrivate::iconRect()const
+{
+  Q_Q(const ctkPushButton);
+  QRect rect;
+  QStyleOptionButton opt;
+  q->initStyleOption(&opt);
+
+  QSize iconSize = q->iconSize();
+  int buttonHeight = opt.rect.height();
+  uint tf = this->ButtonTextAlignment;
+  if (q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
+    {
+    tf |= Qt::TextShowMnemonic;
+    }
+  else
+    {
+    tf |= Qt::TextHideMnemonic;
+    }
+  int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
+  int iconSpacing = this->IconSpacing;
+  int buttonMargin = q->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, q);
+  if (this->IconAlignment & Qt::AlignLeft)
+    {
+    rect = QRect((buttonHeight - iconSize.width()) / 2,
+                 (buttonHeight - iconSize.height()) / 2,
+                 iconSize.width(), iconSize.height());
+    }
+  else if (this->IconAlignment & Qt::AlignHCenter)
+    {
+    int w = iconSize.width();
+    if (!opt.text.isEmpty() && (this->ButtonTextAlignment & Qt::AlignHCenter))
+      {
+      w += textWidth + iconSpacing;
+      }
+    rect = QRect(opt.rect.x()+ opt.rect.width() /2 - w / 2,
+                 (buttonHeight - iconSize.height()) / 2,
+                 iconSize.width(), iconSize.height());
+    if (this->ButtonTextAlignment & Qt::AlignLeft &&
+        rect.left() < opt.rect.x() + buttonMargin + textWidth)
+      {
+      rect.moveLeft(opt.rect.x() + buttonMargin + textWidth);
+      }
+    else if (this->ButtonTextAlignment & Qt::AlignRight &&
+             rect.right() > opt.rect.right() - buttonMargin - textWidth)
+      {
+      rect.moveRight(opt.rect.right() - buttonMargin - textWidth);
+      }
+    }
+  else if (this->IconAlignment & Qt::AlignRight)
+    {
+    rect = QRect(opt.rect.width() - (buttonHeight - iconSize.width()) / 2
+                                  - iconSize.width(),
+                 (buttonHeight - iconSize.height()) / 2,
+                 iconSize.width(), iconSize.height());
+    }
+  return rect;
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkPushButtonPrivate::buttonSizeHint()const
+{
+  Q_Q(const ctkPushButton);
+  int w = 0, h = 0;
+
+  QStyleOptionButton opt;
+  opt.initFrom(q);
+
+  // icon
+  QSize iconSize = q->iconSize();
+  int ih = iconSize.height();
+  int iw = iconSize.width() + this->IconSpacing;
+  w += iw;
+  h = qMax(h, ih);
+
+  // text
+  QString string(q->text());
+  bool empty = string.isEmpty();
+  if (empty)
+    {
+    string = QString::fromLatin1("XXXX");
+    }
+  QFontMetrics fm = q->fontMetrics();
+  QSize sz = fm.size(Qt::TextShowMnemonic, string);
+  if(!empty || !w)
+    {
+    w += sz.width();
+    }
+  h = qMax(h, sz.height());
+  //opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+  QSize buttonSize = (q->style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), q).
+                      expandedTo(QApplication::globalStrut()));
+  return buttonSize;
+}
+
+//-----------------------------------------------------------------------------
+QStyleOptionButton ctkPushButtonPrivate::drawIcon(QPainter* p)
+{
+  Q_Q(ctkPushButton);
+  QStyleOptionButton iconOpt;
+  iconOpt.init(q);
+  iconOpt.rect = this->iconRect();
+  QIcon::Mode mode = iconOpt.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
+  if (mode == QIcon::Normal && iconOpt.state & QStyle::State_HasFocus)
+    {
+    mode = QIcon::Active;
+    }
+  QIcon::State state = QIcon::Off;
+  if (iconOpt.state & QStyle::State_On)
+    {
+    state = QIcon::On;
+    }
+
+  QPixmap pixmap = q->icon().pixmap(iconOpt.rect.size(), mode, state);
+  p->drawPixmap(iconOpt.rect, pixmap);
+  return iconOpt;
+}
+
+//-----------------------------------------------------------------------------
+ctkPushButton::ctkPushButton(QWidget* _parent)
+  : QPushButton(_parent)
+  , d_ptr(new ctkPushButtonPrivate(*this))
+{
+  Q_D(ctkPushButton);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkPushButton::ctkPushButton(const QString& title, QWidget* _parent)
+  : QPushButton(title, _parent)
+  , d_ptr(new ctkPushButtonPrivate(*this))
+{
+  Q_D(ctkPushButton);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkPushButton::ctkPushButton(const QIcon& icon, const QString& title,
+                             QWidget* _parent)
+  : QPushButton(icon, title, _parent)
+  , d_ptr(new ctkPushButtonPrivate(*this))
+{
+  Q_D(ctkPushButton);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkPushButton::ctkPushButton(ctkPushButtonPrivate* pimpl, QWidget* _parent)
+  : QPushButton(_parent)
+  , d_ptr(pimpl)
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkPushButton::~ctkPushButton()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkPushButton::setButtonTextAlignment(Qt::Alignment newButtonTextAlignment)
+{
+  Q_D(ctkPushButton);
+  d->ButtonTextAlignment = newButtonTextAlignment;
+  this->update();
+}
+
+//-----------------------------------------------------------------------------
+Qt::Alignment ctkPushButton::buttonTextAlignment()const
+{
+  Q_D(const ctkPushButton);
+  return d->ButtonTextAlignment;
+}
+
+//-----------------------------------------------------------------------------
+void ctkPushButton::setIconAlignment(Qt::Alignment newIconAlignment)
+{
+  Q_D(ctkPushButton);
+  d->IconAlignment = newIconAlignment;
+  this->update();
+}
+
+//-----------------------------------------------------------------------------
+Qt::Alignment ctkPushButton::iconAlignment()const
+{
+  Q_D(const ctkPushButton);
+  return d->IconAlignment;
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkPushButton::minimumSizeHint()const
+{
+  Q_D(const ctkPushButton);
+  return d->buttonSizeHint();
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkPushButton::sizeHint()const
+{
+  return this->minimumSizeHint();
+}
+
+//-----------------------------------------------------------------------------
+void ctkPushButton::paintEvent(QPaintEvent * _event)
+{
+  Q_UNUSED(_event);
+  Q_D(ctkPushButton);
+
+  QPainter p(this);
+  // Draw Button
+  QStyleOptionButton opt;
+  this->initStyleOption(&opt);
+
+  // Checkbox size
+  QSize iconSize = this->iconSize();
+  // Replace the icon size by the checkbox size
+  opt.iconSize = iconSize;
+  // Draw the panel of the button (no text, no icon)
+  style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this);
+  // TBD is PE_PanelButtonCommand better ?
+  //style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
+  //int buttonHeight = opt.rect.height();
+  uint tf = d->ButtonTextAlignment;
+  if (this->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
+    {
+    tf |= Qt::TextShowMnemonic;
+    }
+  else
+    {
+    tf |= Qt::TextHideMnemonic;
+    }
+  int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
+  int buttonMargin = this->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this);
+  // Draw Icon
+  QStyleOptionButton iconOpt = d->drawIcon(&p);
+  // Spacing between the text and the checkbox
+  int iconSpacing = d->IconSpacing;
+
+  // Draw Text
+  if (d->ButtonTextAlignment & Qt::AlignLeft)
+    {
+    if (d->IconAlignment & Qt::AlignLeft)
+      {
+      opt.rect.setLeft(iconOpt.rect.right() + iconSpacing);
+      }
+    else
+      {
+      opt.rect.setLeft(opt.rect.x() + buttonMargin);
+      }
+    }
+  else if (d->ButtonTextAlignment & Qt::AlignHCenter)
+    {
+    if (d->IconAlignment & Qt::AlignHCenter)
+      {
+      opt.rect.setLeft(iconOpt.rect.right() + iconSpacing);
+      }
+    else
+      {
+      opt.rect.setLeft(opt.rect.x() + opt.rect.width() / 2 - textWidth / 2);
+      if (d->IconAlignment & Qt::AlignLeft)
+        {
+        opt.rect.setLeft( qMax(iconOpt.rect.right() + iconSpacing, opt.rect.left()) );
+        }
+      }
+    }
+  else if (d->ButtonTextAlignment & Qt::AlignRight)
+    {
+    if (d->IconAlignment & Qt::AlignRight)
+      {
+      opt.rect.setLeft(iconOpt.rect.left() - iconSpacing - textWidth);
+      }
+    else
+      {
+      opt.rect.setLeft(opt.rect.right() - buttonMargin - textWidth);
+      }
+    }
+  // all the computations have been made infering the text would be left oriented
+  tf &= ~Qt::AlignHCenter & ~Qt::AlignRight;
+  tf |= Qt::AlignLeft;
+  this->style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
+                              opt.text, QPalette::ButtonText);
+}

+ 85 - 0
Libs/Widgets/ctkPushButton.h

@@ -0,0 +1,85 @@
+/*=========================================================================
+
+  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 __ctkPushButton_h
+#define __ctkPushButton_h
+
+// Qt includes
+#include <QPushButton>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "ctkWidgetsExport.h"
+
+class ctkPushButtonPrivate;
+
+/// \ingroup Widgets
+/// Description
+/// ctkPushButton is an advanced QPushButton. It can control the alignment of text and icons.
+class CTK_WIDGETS_EXPORT ctkPushButton : public QPushButton
+{
+  Q_OBJECT
+  /// Set the alignment of the text on the button,
+  /// Qt::AlignHCenter|Qt::AlignVCenter by default.
+  /// \sa textAlignment(), setTextAlignment(), iconAlignment
+  Q_PROPERTY(Qt::Alignment buttonTextAlignment READ buttonTextAlignment WRITE setButtonTextAlignment)
+  /// Set the alignment of the icon with regard to the text.
+  /// Qt::AlignLeft|Qt::AlignVCenter by default.
+  /// \sa iconAlignment(), setIconAlignment(), textAlignment
+  Q_PROPERTY(Qt::Alignment iconAlignment READ iconAlignment WRITE setIconAlignment)
+
+public:
+  ctkPushButton(QWidget *parent = 0);
+  ctkPushButton(const QString& text, QWidget *parent = 0);
+  ctkPushButton(const QIcon& icon, const QString& text, QWidget *parent = 0);
+  virtual ~ctkPushButton();
+
+  /// Set the buttonTextAlignment property value.
+  /// \sa buttonTextAlignment
+  void setButtonTextAlignment(Qt::Alignment buttonTextAlignment);
+  /// Return the buttonTextAlignment property value.
+  /// \sa buttonTextAlignment
+  Qt::Alignment buttonTextAlignment()const;
+
+  /// Set the iconAlignment property value.
+  /// \sa iconAlignment
+  void setIconAlignment(Qt::Alignment iconAlignment);
+  /// Return the iconAlignment property value.
+  /// \sa iconAlignment
+  Qt::Alignment iconAlignment()const;
+
+  virtual QSize minimumSizeHint()const;
+  virtual QSize sizeHint()const;
+
+protected:
+  /// Reimplemented for internal reasons
+  virtual void paintEvent(QPaintEvent*);
+
+protected:
+  QScopedPointer<ctkPushButtonPrivate> d_ptr;
+  ctkPushButton(ctkPushButtonPrivate*, QWidget* parent = 0);
+
+private:
+  Q_DECLARE_PRIVATE(ctkPushButton);
+  Q_DISABLE_COPY(ctkPushButton);
+};
+
+#endif

+ 42 - 0
Libs/Widgets/ctkPushButton_p.h

@@ -0,0 +1,42 @@
+/*=========================================================================
+
+  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 "ctkPushButton.h"
+
+//-----------------------------------------------------------------------------
+class ctkPushButtonPrivate
+{
+  Q_DECLARE_PUBLIC(ctkPushButton);
+protected:
+  ctkPushButton* const q_ptr;
+public:
+  ctkPushButtonPrivate(ctkPushButton& object);
+  void init();
+
+  virtual QRect iconRect() const;
+  virtual QSize buttonSizeHint()const;
+  virtual QStyleOptionButton drawIcon(QPainter* p);
+
+  // Tuning of the button look&feel
+  Qt::Alignment ButtonTextAlignment;
+  Qt::Alignment IconAlignment;
+  int IconSpacing;
+};