Kaynağa Gözat

Add ctkCheckablePushButton

Julien Finet 14 yıl önce
ebeveyn
işleme
055c6e0512

+ 3 - 0
Libs/Widgets/CMakeLists.txt

@@ -24,6 +24,8 @@ SET(KIT_SRCS
   ctkCheckableHeaderView.h
   ctkCheckBoxPixmaps.cpp
   ctkCheckBoxPixmaps.h
+  ctkCheckablePushButton.cpp
+  ctkCheckablePushButton.h
   ctkComboBox.cpp
   ctkComboBox.h
   ctkCollapsibleButton.cpp
@@ -109,6 +111,7 @@ SET(KIT_MOC_SRCS
   ctkAddRemoveComboBox.h
   ctkButtonGroup.h
   ctkCheckableHeaderView.h
+  ctkCheckablePushButton.h
   ctkCheckBoxPixmaps.h
   ctkComboBox.h
   ctkCollapsibleButton.h

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

@@ -7,6 +7,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cxx
   ctkButtonGroupTest1.cpp
   ctkCheckBoxPixmapsTest1.cpp
   ctkCheckableHeaderViewTest1.cpp
+  ctkCheckablePushButtonTest1.cpp
   ctkCollapsibleButtonTest1.cpp
   ctkCollapsibleGroupBoxTest1.cpp
   ctkColorPickerButtonTest1.cpp
@@ -73,6 +74,7 @@ SIMPLE_TEST( ctkAxesWidgetTest1 )
 SIMPLE_TEST( ctkButtonGroupTest1 )
 SIMPLE_TEST( ctkCheckBoxPixmapsTest1 )
 SIMPLE_TEST( ctkCheckableHeaderViewTest1 )
+SIMPLE_TEST( ctkCheckablePushButtonTest1 )
 SIMPLE_TEST( ctkCollapsibleButtonTest1 )
 SIMPLE_TEST( ctkCollapsibleGroupBoxTest1 )
 SIMPLE_TEST( ctkColorPickerButtonTest1 )

+ 96 - 0
Libs/Widgets/Testing/Cpp/ctkCheckablePushButtonTest1.cpp

@@ -0,0 +1,96 @@
+/*=========================================================================
+
+  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.commontk.org/LICENSE
+
+  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 <QCheckBox>
+#include <QVBoxLayout>
+#include <QStyle>
+#include <QTimer>
+
+// CTK includes
+#include "ctkCheckablePushButton.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+//-----------------------------------------------------------------------------
+int ctkCheckablePushButtonTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  QWidget topLevel;
+  ctkCheckablePushButton button1(QObject::tr("Button1"));
+  ctkCheckablePushButton button2(QObject::tr("Button2"));
+  ctkCheckablePushButton button3(QObject::tr("Button3"));
+  ctkCheckablePushButton button4(QObject::tr("Button4"));
+  ctkCheckablePushButton button5(QObject::tr("Button5"));
+  ctkCheckablePushButton button6(QObject::tr("Button6"));
+  QCheckBox checkBox1(QObject::tr("Checkbox1"));
+
+  QVBoxLayout *layout= new QVBoxLayout;
+  layout->addWidget(&button1);
+  layout->addWidget(&button2);
+  layout->addWidget(&button3);
+  layout->addWidget(&button4);
+  layout->addWidget(&button5);
+  layout->addWidget(&button6);
+  layout->addWidget(&checkBox1);
+  topLevel.setLayout(layout);
+
+  topLevel.show();
+  button1.setButtonTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+  if (button1.buttonTextAlignment() != (Qt::AlignRight | Qt::AlignVCenter))
+    {
+    std::cerr << "ctkCheckablePushButton::setButtonTextAlignment failed."
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  button2.setIndicatorAlignment(Qt::AlignRight);
+
+  if (button2.indicatorAlignment() != Qt::AlignRight)
+    {
+    std::cerr << "ctkCheckablePushButton::setIndicatorAlignment failed."
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  button3.setButtonTextAlignment(Qt::AlignCenter);
+  button3.setIndicatorAlignment(Qt::AlignCenter);
+  button3.setCheckable(true);
+  
+  button4.setCheckable(true);
+  button4.toggle();
+  
+  button5.setButtonTextAlignment(Qt::AlignCenter);
+  button5.setIndicatorAlignment(Qt::AlignRight);
+  
+  button6.setIndicatorAlignment(Qt::AlignTop);
+  
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
+}

+ 303 - 0
Libs/Widgets/ctkCheckablePushButton.cpp

@@ -0,0 +1,303 @@
+/*=========================================================================
+
+  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.commontk.org/LICENSE
+
+  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 <QCleanlooksStyle>
+#include <QDebug>
+#include <QDesktopWidget>
+#include <QLayout>
+#include <QMouseEvent>
+#include <QMenu>
+#include <QPainter>
+#include <QPointer>
+#include <QPushButton>
+#include <QStyle>
+#include <QStyleOptionButton>
+#include <QStylePainter>
+#include <QToolBar>
+
+// CTK includes
+#include "ctkCheckablePushButton.h"
+
+//-----------------------------------------------------------------------------
+class ctkCheckablePushButtonPrivate
+{
+  Q_DECLARE_PUBLIC(ctkCheckablePushButton);
+protected:
+  ctkCheckablePushButton* const q_ptr;
+public:
+  ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object);
+  void init();
+
+  QRect checkboxRect() const;
+
+  // Tuning of the button look&feel
+  Qt::Alignment TextAlignment;
+  Qt::Alignment IndicatorAlignment;
+};
+
+//-----------------------------------------------------------------------------
+ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object)
+  :q_ptr(&object)
+{
+  this->TextAlignment = Qt::AlignLeft | Qt::AlignVCenter;
+  this->IndicatorAlignment = Qt::AlignLeft | Qt::AlignVCenter;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckablePushButtonPrivate::init()
+{
+  Q_Q(ctkCheckablePushButton);
+}
+
+//-----------------------------------------------------------------------------
+QRect ctkCheckablePushButtonPrivate::checkboxRect()const
+{
+  Q_Q(const ctkCheckablePushButton);
+  QRect rect;
+  QStyleOptionButton opt;
+  q->initStyleOption(&opt);
+
+  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;
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckablePushButton::ctkCheckablePushButton(QWidget* _parent)
+  :QPushButton(_parent)
+  , d_ptr(new ctkCheckablePushButtonPrivate(*this))
+{
+  Q_D(ctkCheckablePushButton);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckablePushButton::ctkCheckablePushButton(const QString& title, QWidget* _parent)
+  :QPushButton(title, _parent)
+  , d_ptr(new ctkCheckablePushButtonPrivate(*this))
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckablePushButton::~ctkCheckablePushButton()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckablePushButton::setButtonTextAlignment(Qt::Alignment textAlignment)
+{
+  Q_D(ctkCheckablePushButton);
+  d->TextAlignment = textAlignment;
+  this->update();
+}
+
+//-----------------------------------------------------------------------------
+Qt::Alignment ctkCheckablePushButton::buttonTextAlignment()const
+{
+  Q_D(const ctkCheckablePushButton);
+  return d->TextAlignment;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckablePushButton::setIndicatorAlignment(Qt::Alignment indicatorAlignment)
+{
+  Q_D(ctkCheckablePushButton);
+  d->IndicatorAlignment = indicatorAlignment;
+  this->update();
+}
+
+//-----------------------------------------------------------------------------
+Qt::Alignment ctkCheckablePushButton::indicatorAlignment()const
+{
+  Q_D(const ctkCheckablePushButton);
+  return d->IndicatorAlignment;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckablePushButton::paintEvent(QPaintEvent * _event)
+{
+  Q_UNUSED(_event);
+  Q_D(ctkCheckablePushButton);
+
+  QPainter p(this);
+  // Draw Button
+  QStyleOptionButton opt;
+  this->initStyleOption(&opt);
+
+  QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
+                              style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
+  opt.iconSize = indicatorSize;
+  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();
+  int indicatorSpacing = this->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
+  int buttonMargin = this->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this);
+  // Draw Indicator
+  QStyleOption indicatorOpt;
+  indicatorOpt.init(this);
+  if (this->isCheckable())
+    {
+    indicatorOpt.state |= QStyle::State_On;
+    }
+  else
+    {
+    indicatorOpt.state |= QStyle::State_Off;
+    }
+  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( qMin(indicatorOpt.rect.left() + 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) 
+    && 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);
+  this->QPushButton::mousePressEvent(e);
+  if (e->isAccepted())
+    {
+    return;
+    }
+  if (d->checkboxRect().contains(e->pos()))
+    {
+    //check the checkbox
+    this->setCheckable(!this->isCheckable());
+    this->update();
+    e->accept();
+    }
+}

+ 78 - 0
Libs/Widgets/ctkCheckablePushButton.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.commontk.org/LICENSE
+
+  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 __ctkCheckablePushButton_h
+#define __ctkCheckablePushButton_h
+
+// Qt includes
+#include <QPushButton>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "ctkWidgetsExport.h"
+
+class ctkCheckablePushButtonPrivate;
+
+/// Description
+/// ctkCheckablePushButton is a QPushButton with a checkbox that controls the
+/// checkable property of the button
+class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public QPushButton
+{
+  Q_OBJECT
+  Q_PROPERTY(Qt::Alignment buttonTextAlignment READ buttonTextAlignment WRITE setButtonTextAlignment)
+  Q_PROPERTY(Qt::Alignment indicatorAlignment READ indicatorAlignment WRITE setIndicatorAlignment)
+
+public:
+  ctkCheckablePushButton(QWidget *parent = 0);
+  ctkCheckablePushButton(const QString& text, QWidget *parent = 0);
+  virtual ~ctkCheckablePushButton();
+
+  ///
+  /// Set the alignment of the text on the button,
+  /// Qt::Left|Qt::VCenter by default.
+  void setButtonTextAlignment(Qt::Alignment textAlignment);
+  Qt::Alignment buttonTextAlignment()const;
+
+  ///
+  /// Set the alignment of the indicator (arrow) on the button,
+  /// Qt::Left|Qt::VCenter by default.
+  void setIndicatorAlignment(Qt::Alignment indicatorAlignment);
+  Qt::Alignment indicatorAlignment()const;
+
+
+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);
+  Q_DISABLE_COPY(ctkCheckablePushButton);
+};
+
+#endif