123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- /*=========================================================================
- 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>
- #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
- # include <QCleanlooksStyle>
- #endif
- #include <QDebug>
- #include <QLayout>
- #include <QMouseEvent>
- #include <QPainter>
- #include <QPushButton>
- #include <QStyle>
- #include <QStyleOptionButton>
- #include <QStyleOptionFrameV3>
- // CTK includes
- #include "ctkCollapsibleButton.h"
- //-----------------------------------------------------------------------------
- class ctkCollapsibleButtonPrivate
- {
- Q_DECLARE_PUBLIC(ctkCollapsibleButton);
- protected:
- ctkCollapsibleButton* const q_ptr;
- public:
- ctkCollapsibleButtonPrivate(ctkCollapsibleButton& object);
- void init();
- void setChildVisibility(QWidget* childWidget);
- bool Collapsed;
- // Contents frame
- QFrame::Shape ContentsFrameShape;
- QFrame::Shadow ContentsFrameShadow;
- int ContentsLineWidth;
- int ContentsMidLineWidth;
- int CollapsedHeight;
- bool Flat;
- bool ExclusiveMouseOver;
- bool LookOffWhenChecked;
- /// We change the visibility of the chidren in setChildrenVisibility
- /// and we track when the visibility is changed to force it back to possibly
- /// force the child to be hidden. To prevent infinite loop we need to know
- /// who is changing children's visibility.
- bool ForcingVisibility;
- /// Sometimes the creation of the widget is not done inside setVisible,
- /// as we need to do special processing the first time the button is
- /// setVisible, we track its created state with the variable
- bool IsStateCreated;
- int MaximumHeight; // use carefully
- // Tuning of the button look&feel
- Qt::Alignment TextAlignment;
- Qt::Alignment IndicatorAlignment;
- };
- //-----------------------------------------------------------------------------
- ctkCollapsibleButtonPrivate::ctkCollapsibleButtonPrivate(ctkCollapsibleButton& object)
- :q_ptr(&object)
- {
- this->Flat = false;
- this->ForcingVisibility = false;
- this->IsStateCreated = false;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButtonPrivate::init()
- {
- Q_Q(ctkCollapsibleButton);
- q->setCheckable(true);
- // checked and Collapsed are synchronized: checked != Collapsed
- q->setChecked(true);
- this->Collapsed = false;
- this->ContentsFrameShape = QFrame::NoFrame;
- this->ContentsFrameShadow = QFrame::Plain;
- this->ContentsLineWidth = 1;
- this->ContentsMidLineWidth = 0;
- this->CollapsedHeight = 14;
- this->ExclusiveMouseOver = false;
- this->LookOffWhenChecked = true; // set as a prop ?
-
- this->MaximumHeight = q->maximumHeight();
- this->TextAlignment = Qt::AlignLeft | Qt::AlignVCenter;
- this->IndicatorAlignment = Qt::AlignLeft | Qt::AlignVCenter;
- q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,
- QSizePolicy::Preferred,
- QSizePolicy::DefaultType));
- q->setContentsMargins(0, q->buttonSizeHint().height(),0 , 0);
- // by default QAbstractButton changed the background role to Button
- // we want a regular background
- q->setBackgroundRole(QPalette::Window);
- QObject::connect(q, SIGNAL(toggled(bool)),
- q, SLOT(onToggled(bool)));
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButtonPrivate::setChildVisibility(QWidget* childWidget)
- {
- Q_Q(ctkCollapsibleButton);
- // Don't hide children while the widget is not yet created (before show() is
- // called). If we hide them (but don't set ExplicitShowHide), they would be
- // shown anyway when they will be created (because ExplicitShowHide is not set).
- // If we set ExplicitShowHide, then calling setVisible(false) on them would
- // be a no (because they are already hidden and ExplicitShowHide is set).
- // So we don't hide/show the children until the widget is created.
- if (!q->testAttribute(Qt::WA_WState_Created))
- {
- return;
- }
- this->ForcingVisibility = true;
- bool visible= !this->Collapsed;
- // if the widget has been explicity hidden, then hide it.
- if (childWidget->property("visibilityToParent").isValid()
- && !childWidget->property("visibilityToParent").toBool())
- {
- visible = false;
- }
- // Setting Qt::WA_WState_Visible to true during child construction can have
- // undesirable side effects.
- if (childWidget->testAttribute(Qt::WA_WState_Created) ||
- !visible)
- {
- childWidget->setVisible(visible);
- }
- // setVisible() has set the ExplicitShowHide flag, restore it as we don't want
- // to make it like it was an explicit visible set because we want
- // to allow the children to be explicitly hidden by the user.
- if ((!childWidget->property("visibilityToParent").isValid() ||
- childWidget->property("visibilityToParent").toBool()))
- {
- childWidget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
- }
- this->ForcingVisibility = false;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::initStyleOption(QStyleOptionButton* option)const
- {
- Q_D(const ctkCollapsibleButton);
- if (option == 0)
- {
- return;
- }
- option->initFrom(this);
- if (this->isDown() )
- {
- option->state |= QStyle::State_Sunken;
- }
- if (this->isChecked() && !d->LookOffWhenChecked)
- {
- option->state |= QStyle::State_On;
- }
- if (!this->isDown())
- {
- option->state |= QStyle::State_Raised;
- }
- if (d->Flat)
- {
- option->features |= QStyleOptionButton::Flat;
- }
- option->text = this->text();
- option->icon = this->icon();
- option->iconSize = QSize(this->style()->pixelMetric(QStyle::PM_IndicatorWidth, option, this),
- this->style()->pixelMetric(QStyle::PM_IndicatorHeight, option, this));
- int buttonHeight = this->buttonSizeHint().height();//qMax(this->fontMetrics().height(), option->iconSize.height());
- option->rect.setHeight(buttonHeight);
- }
- //-----------------------------------------------------------------------------
- ctkCollapsibleButton::ctkCollapsibleButton(QWidget* _parent)
- :QAbstractButton(_parent)
- , d_ptr(new ctkCollapsibleButtonPrivate(*this))
- {
- Q_D(ctkCollapsibleButton);
- d->init();
- }
- //-----------------------------------------------------------------------------
- ctkCollapsibleButton::ctkCollapsibleButton(const QString& title, QWidget* _parent)
- :QAbstractButton(_parent)
- , d_ptr(new ctkCollapsibleButtonPrivate(*this))
- {
- Q_D(ctkCollapsibleButton);
- d->init();
- this->setText(title);
- }
- //-----------------------------------------------------------------------------
- ctkCollapsibleButton::~ctkCollapsibleButton()
- {
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setCollapsed(bool c)
- {
- if (!this->isCheckable())
- {
- // not sure if one should handle this case...
- this->collapse(c);
- return;
- }
- this->setChecked(!c);
- }
- //-----------------------------------------------------------------------------
- bool ctkCollapsibleButton::collapsed()const
- {
- Q_D(const ctkCollapsibleButton);
- return d->Collapsed;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setCollapsedHeight(int h)
- {
- Q_D(ctkCollapsibleButton);
- d->CollapsedHeight = h;
- this->updateGeometry();
- }
- //-----------------------------------------------------------------------------
- int ctkCollapsibleButton::collapsedHeight()const
- {
- Q_D(const ctkCollapsibleButton);
- return d->CollapsedHeight;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setFlat(bool flat)
- {
- Q_D(ctkCollapsibleButton);
- d->Flat = flat;
- this->update();
- }
- //-----------------------------------------------------------------------------
- bool ctkCollapsibleButton::isFlat()const
- {
- Q_D(const ctkCollapsibleButton);
- return d->Flat;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::onToggled(bool checked)
- {
- if (this->isCheckable())
- {
- this->collapse(!checked);
- }
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::collapse(bool collapsed)
- {
- Q_D(ctkCollapsibleButton);
- if (collapsed == d->Collapsed)
- {
- return;
- }
- d->Collapsed = collapsed;
- // we do that here as setVisible calls will correctly refresh the widget
- if (collapsed)
- {
- d->MaximumHeight = this->maximumHeight();
- this->setMaximumHeight(this->sizeHint().height());
- //this->updateGeometry();
- }
- else
- {
- // restore maximumheight
- this->setMaximumHeight(d->MaximumHeight);
- this->updateGeometry();
- }
- // Update the visibility of all the children
- // We can't use findChildren as it would return the grandchildren
- foreach(QObject* child, this->children())
- {
- QWidget* childWidget = qobject_cast<QWidget*>(child);
- if (childWidget)
- {
- d->setChildVisibility(childWidget);
- }
- }
- // this might be too many updates...
- QWidget* _parent = this->parentWidget();
- if (!d->Collapsed && (!_parent || !_parent->layout()))
- {
- this->resize(this->sizeHint());
- }
- else
- {
- this->updateGeometry();
- }
- //this->update(QRect(QPoint(0,0), this->sizeHint()));
- //this->repaint(QRect(QPoint(0,0), this->sizeHint()));
- emit contentsCollapsed(collapsed);
- }
- //-----------------------------------------------------------------------------
- QFrame::Shape ctkCollapsibleButton::contentsFrameShape() const
- {
- Q_D(const ctkCollapsibleButton);
- return d->ContentsFrameShape;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setContentsFrameShape(QFrame::Shape s)
- {
- Q_D(ctkCollapsibleButton);
- d->ContentsFrameShape = s;
- }
- //-----------------------------------------------------------------------------
- QFrame::Shadow ctkCollapsibleButton::contentsFrameShadow() const
- {
- Q_D(const ctkCollapsibleButton);
- return d->ContentsFrameShadow;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setContentsFrameShadow(QFrame::Shadow s)
- {
- Q_D(ctkCollapsibleButton);
- d->ContentsFrameShadow = s;
- }
- //-----------------------------------------------------------------------------
- int ctkCollapsibleButton:: contentsLineWidth() const
- {
- Q_D(const ctkCollapsibleButton);
- return d->ContentsLineWidth;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setContentsLineWidth(int w)
- {
- Q_D(ctkCollapsibleButton);
- d->ContentsLineWidth = w;
- }
- //-----------------------------------------------------------------------------
- int ctkCollapsibleButton::contentsMidLineWidth() const
- {
- Q_D(const ctkCollapsibleButton);
- return d->ContentsMidLineWidth;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setContentsMidLineWidth(int w)
- {
- Q_D(ctkCollapsibleButton);
- d->ContentsMidLineWidth = w;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setButtonTextAlignment(Qt::Alignment textAlignment)
- {
- Q_D(ctkCollapsibleButton);
- d->TextAlignment = textAlignment;
- this->update();
- }
- //-----------------------------------------------------------------------------
- Qt::Alignment ctkCollapsibleButton::buttonTextAlignment()const
- {
- Q_D(const ctkCollapsibleButton);
- return d->TextAlignment;
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setIndicatorAlignment(Qt::Alignment indicatorAlignment)
- {
- Q_D(ctkCollapsibleButton);
- d->IndicatorAlignment = indicatorAlignment;
- this->update();
- }
- //-----------------------------------------------------------------------------
- Qt::Alignment ctkCollapsibleButton::indicatorAlignment()const
- {
- Q_D(const ctkCollapsibleButton);
- return d->IndicatorAlignment;
- }
- //-----------------------------------------------------------------------------
- QSize ctkCollapsibleButton::buttonSizeHint()const
- {
- int w = 0, h = 0;
- QStyleOptionButton opt;
- opt.initFrom(this);
-
- // indicator
- QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
- style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
- int indicatorSpacing = style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
- int ih = indicatorSize.height();
- int iw = indicatorSize.width() + indicatorSpacing;
- w += iw;
- h = qMax(h, ih);
-
- // text
- QString string(this->text());
- bool empty = string.isEmpty();
- if (empty)
- {
- string = QString::fromLatin1("XXXX");
- }
- QFontMetrics fm = this->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 = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this).
- expandedTo(QApplication::globalStrut()));
- return buttonSize;
- }
- //-----------------------------------------------------------------------------
- QSize ctkCollapsibleButton::minimumSizeHint()const
- {
- Q_D(const ctkCollapsibleButton);
- QSize buttonSize = this->buttonSizeHint();
- if (d->Collapsed)
- {
- return buttonSize + QSize(0,d->CollapsedHeight);
- }
- // open
- if (this->layout() == 0)
- {// no layout, means the button is empty ?
- return buttonSize;
- }
- QSize s = this->QAbstractButton::minimumSizeHint();
- return s.expandedTo(buttonSize);
- }
- //-----------------------------------------------------------------------------
- QSize ctkCollapsibleButton::sizeHint()const
- {
- Q_D(const ctkCollapsibleButton);
- QSize buttonSize = this->buttonSizeHint();
- if (d->Collapsed)
- {
- return buttonSize + QSize(0,d->CollapsedHeight);
- }
- // open
- // QAbstractButton works well only if a layout is set
- QSize s = this->QAbstractButton::sizeHint();
- return s.expandedTo(buttonSize + QSize(0, d->CollapsedHeight));
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::paintEvent(QPaintEvent * _event)
- {
- Q_D(ctkCollapsibleButton);
- QPainter p(this);
- // Draw Button
- QStyleOptionButton opt;
- this->initStyleOption(&opt);
- // We don't want to have the highlight effect on the button when mouse is
- // over a child. We want the highlight effect only when the mouse is just
- // over itself.
- // same as this->underMouse()
- bool exclusiveMouseOver = false;
- if (opt.state & QStyle::State_MouseOver)
- {
- QRect buttonRect = opt.rect;
- QList<QWidget*> _children = this->findChildren<QWidget*>();
- QList<QWidget*>::ConstIterator it;
- for (it = _children.constBegin(); it != _children.constEnd(); ++it )
- {
- if ((*it)->underMouse())
- {
- // the mouse has been moved from the collapsible button to one
- // of its children. The paint event rect is the child rect, this
- // is why we have to request another paint event to redraw the
- // button to remove the highlight effect.
- if (!_event->rect().contains(buttonRect))
- {// repaint the button rect.
- this->update(buttonRect);
- }
- opt.state &= ~QStyle::State_MouseOver;
- exclusiveMouseOver = true;
- break;
- }
- }
- if (d->ExclusiveMouseOver && !exclusiveMouseOver)
- {
- // the mouse is over the widget, but not over the children. As it
- // has been de-highlighted in the past, we should refresh the button
- // rect to re-highlight the button.
- if (!_event->rect().contains(buttonRect))
- {// repaint the button rect.
- this->update(buttonRect);
- }
- }
- }
- d->ExclusiveMouseOver = exclusiveMouseOver;
- 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 (d->IndicatorAlignment & Qt::AlignLeft)
- {
- indicatorOpt.rect = QRect((buttonHeight - indicatorSize.width()) / 2,
- (buttonHeight - indicatorSize.height()) / 2,
- indicatorSize.width(), indicatorSize.height());
- }
- else if (d->IndicatorAlignment & Qt::AlignHCenter)
- {
- int w = indicatorSize.width();
- if (!opt.text.isEmpty() && (d->TextAlignment & Qt::AlignHCenter))
- {
- w += textWidth + indicatorSpacing;
- }
- indicatorOpt.rect = QRect(opt.rect.x()+ opt.rect.width() /2 - w / 2,
- (buttonHeight - indicatorSize.height()) / 2,
- indicatorSize.width(), indicatorSize.height());
- if (d->TextAlignment & Qt::AlignLeft &&
- indicatorOpt.rect.left() < opt.rect.x() + buttonMargin + textWidth)
- {
- indicatorOpt.rect.moveLeft(opt.rect.x() + buttonMargin + textWidth);
- }
- else if (d->TextAlignment & Qt::AlignRight &&
- indicatorOpt.rect.right() > opt.rect.right() - buttonMargin - textWidth)
- {
- indicatorOpt.rect.moveRight(opt.rect.right() - buttonMargin - textWidth);
- }
- }
- else if (d->IndicatorAlignment & Qt::AlignRight)
- {
- indicatorOpt.rect = QRect(opt.rect.width() - (buttonHeight - indicatorSize.width()) / 2
- - indicatorSize.width(),
- (buttonHeight - indicatorSize.height()) / 2,
- indicatorSize.width(), indicatorSize.height());
- }
- if (d->Collapsed)
- {
- style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &indicatorOpt, &p, this);
- }
- else
- {
- style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &indicatorOpt, &p, this);
- }
- // 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;
- style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
- opt.text, QPalette::ButtonText);
- // Draw Frame around contents
- #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
- QStyleOptionFrameV3 fopt;
- #else
- QStyleOptionFrame fopt;
- #endif
- fopt.init(this);
- // HACK: on some styles, the frame doesn't exactly touch the button.
- // this is because the button has some kind of extra border.
- #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
- if (qobject_cast<QCleanlooksStyle*>(this->style()) != 0)
- {
- fopt.rect.setTop(buttonHeight - 1);
- }
- else
- #endif
- {
- fopt.rect.setTop(buttonHeight);
- }
- fopt.frameShape = d->ContentsFrameShape;
- switch (d->ContentsFrameShadow)
- {
- case QFrame::Sunken:
- fopt.state |= QStyle::State_Sunken;
- break;
- case QFrame::Raised:
- fopt.state |= QStyle::State_Raised;
- break;
- default:
- case QFrame::Plain:
- break;
- }
- fopt.lineWidth = d->ContentsLineWidth;
- fopt.midLineWidth = d->ContentsMidLineWidth;
- style()->drawControl(QStyle::CE_ShapedFrame, &fopt, &p, this);
- }
- //-----------------------------------------------------------------------------
- bool ctkCollapsibleButton::hitButton(const QPoint & _pos)const
- {
- QStyleOptionButton opt;
- this->initStyleOption(&opt);
- return opt.rect.contains(_pos);
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::childEvent(QChildEvent* c)
- {
- Q_D(ctkCollapsibleButton);
- QObject* child = c->child();
- if (c && c->type() == QEvent::ChildAdded &&
- child && child->isWidgetType())
- {
- QWidget *childWidget = qobject_cast<QWidget*>(c->child());
- // Handle the case where the child has already it's visibility set before
- // being added to the widget
- if (childWidget->testAttribute(Qt::WA_WState_ExplicitShowHide) &&
- childWidget->testAttribute(Qt::WA_WState_Hidden))
- {
- // if the widget has explicitly set to hidden, then mark it as such
- childWidget->setProperty("visibilityToParent", false);
- }
- // We want to catch all the child's Show/Hide events.
- child->installEventFilter(this);
- // If the child is added while ctkCollapsibleButton is collapsed, then we
- // need to hide the child.
- d->setChildVisibility(childWidget);
- }
- this->QAbstractButton::childEvent(c);
- }
- //-----------------------------------------------------------------------------
- void ctkCollapsibleButton::setVisible(bool show)
- {
- Q_D(ctkCollapsibleButton);
- // calling QWidget::setVisible() on ctkCollapsibleButton will eventually
- // call QWidget::showChildren() or hideChildren() which will generate
- // ShowToParent/HideToParent events but we want to ignore that case in
- // eventFilter().
- d->ForcingVisibility = true;
- this->QWidget::setVisible(show);
- d->ForcingVisibility = false;
- // We have been ignoring setChildVisibility() while the collapsible button
- // is not yet created, now that it is created, ensure that the children
- // are correctly shown/hidden depending on their explicit visibility and
- // the collapsed property of the button.
- if (!d->IsStateCreated && this->testAttribute(Qt::WA_WState_Created))
- {
- d->IsStateCreated = true;
- foreach(QObject* child, this->children())
- {
- QWidget* childWidget = qobject_cast<QWidget*>(child);
- if (childWidget)
- {
- d->setChildVisibility(childWidget);
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- bool ctkCollapsibleButton::eventFilter(QObject* child, QEvent* e)
- {
- Q_D(ctkCollapsibleButton);
- Q_ASSERT(child && e);
- // Make sure the Show/QHide events are not generated by one of our
- // ctkCollapsibleButton function.
- if (d->ForcingVisibility)
- {
- return false;
- }
- // When we are here, it's because somewhere (not in ctkCollapsibleButton),
- // someone explicitly called setVisible() on a child widget.
- // If the collapsible button is collapsed/closed, then even if someone
- // request the widget to be visible, we force it back to be hidden because
- // they meant to be hidden to its parent, the collapsible button. However the
- // child will later be shown when the button will be expanded/opened.
- // On the other hand, if the user explicitly hide the child when the button
- // is collapsed/closed, then we want to keep it hidden next time the
- // collapsible button is expanded/opened.
- if (e->type() == QEvent::ShowToParent)
- {
- child->setProperty("visibilityToParent", true);
- Q_ASSERT(qobject_cast<QWidget*>(child));
- // force the widget to be hidden if the button is collapsed.
- d->setChildVisibility(qobject_cast<QWidget*>(child));
- }
- else if(e->type() == QEvent::HideToParent)
- {
- // we don't need to force the widget to be visible here.
- child->setProperty("visibilityToParent", false);
- }
- return this->QWidget::eventFilter(child, e);
- }
- //-----------------------------------------------------------------------------
- bool ctkCollapsibleButton::event(QEvent *event)
- {
- if (event->type() == QEvent::StyleChange
- || event->type() == QEvent::FontChange
- #ifdef Q_WS_MAC
- || event->type() == QEvent::MacSizeChange
- #endif
- )
- {
- this->setContentsMargins(0, this->buttonSizeHint().height(),0 , 0);
- if (this->collapsed())
- {
- this->setMaximumHeight(this->sizeHint().height());
- }
- }
- return QAbstractButton::event(event);
- }
|