|
@@ -55,35 +55,54 @@ class ctkCollapsibleGroupBoxStyle:public QProxyStyle
|
|
|
|
|
|
#endif
|
|
|
|
|
|
-//-----------------------------------------------------------------------------
|
|
|
-ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(QWidget* _parent)
|
|
|
- :QGroupBox(_parent)
|
|
|
-{
|
|
|
- this->init();
|
|
|
-}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(const QString& title, QWidget* _parent)
|
|
|
- :QGroupBox(title, _parent)
|
|
|
+class ctkCollapsibleGroupBoxPrivate
|
|
|
{
|
|
|
- this->init();
|
|
|
-}
|
|
|
+ Q_DECLARE_PUBLIC(ctkCollapsibleGroupBox);
|
|
|
+protected:
|
|
|
+ ctkCollapsibleGroupBox* const q_ptr;
|
|
|
+public:
|
|
|
+ ctkCollapsibleGroupBoxPrivate(ctkCollapsibleGroupBox& object);
|
|
|
+ void init();
|
|
|
+ void setChildVisibility(QWidget* childWidget);
|
|
|
+
|
|
|
+ /// Size of the widget for collapsing
|
|
|
+ QSize OldSize;
|
|
|
+ /// Maximum allowed height
|
|
|
+ int MaxHeight;
|
|
|
+
|
|
|
+ /// 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;
|
|
|
+};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-ctkCollapsibleGroupBox::~ctkCollapsibleGroupBox()
|
|
|
+ctkCollapsibleGroupBoxPrivate::ctkCollapsibleGroupBoxPrivate(
|
|
|
+ ctkCollapsibleGroupBox& object)
|
|
|
+ :q_ptr(&object)
|
|
|
{
|
|
|
-
|
|
|
+ this->ForcingVisibility = false;
|
|
|
+ this->IsStateCreated = false;
|
|
|
+ this->MaxHeight = 0;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-void ctkCollapsibleGroupBox::init()
|
|
|
+void ctkCollapsibleGroupBoxPrivate::init()
|
|
|
{
|
|
|
- this->setCheckable(true);
|
|
|
- connect(this, SIGNAL(toggled(bool)), this, SLOT(expand(bool)));
|
|
|
+ Q_Q(ctkCollapsibleGroupBox);
|
|
|
+ q->setCheckable(true);
|
|
|
+ QObject::connect(q, SIGNAL(toggled(bool)), q, SLOT(expand(bool)));
|
|
|
|
|
|
- this->MaxHeight = this->maximumHeight();
|
|
|
+ this->MaxHeight = q->maximumHeight();
|
|
|
#if QT_VERSION >= 0x040600
|
|
|
- this->setStyle(new ctkCollapsibleGroupBoxStyle);
|
|
|
+ q->setStyle(new ctkCollapsibleGroupBoxStyle);
|
|
|
#else
|
|
|
this->setStyleSheet(
|
|
|
"ctkCollapsibleGroupBox::indicator:checked{"
|
|
@@ -92,55 +111,98 @@ void ctkCollapsibleGroupBox::init()
|
|
|
"image: url(:/Icons/expand-down.png);}");
|
|
|
#endif
|
|
|
}
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCollapsibleGroupBoxPrivate::setChildVisibility(QWidget* childWidget)
|
|
|
+{
|
|
|
+ Q_Q(ctkCollapsibleGroupBox);
|
|
|
+ // 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= !q->collapsed();
|
|
|
+ // if the widget has been explicity hidden, then hide it.
|
|
|
+ if (childWidget->property("visibilityToParent").isValid()
|
|
|
+ && !childWidget->property("visibilityToParent").toBool())
|
|
|
+ {
|
|
|
+ visible = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(QWidget* _parent)
|
|
|
+ :QGroupBox(_parent)
|
|
|
+ , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
|
|
|
+{
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
+ d->init();
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(const QString& title, QWidget* _parent)
|
|
|
+ :QGroupBox(title, _parent)
|
|
|
+ , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
|
|
|
+{
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
+ d->init();
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+ctkCollapsibleGroupBox::~ctkCollapsibleGroupBox()
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void ctkCollapsibleGroupBox::expand(bool _expand)
|
|
|
{
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
if (!_expand)
|
|
|
{
|
|
|
- this->OldSize = this->size();
|
|
|
+ d->OldSize = this->size();
|
|
|
}
|
|
|
|
|
|
- QObjectList childList = this->children();
|
|
|
- for (int i = 0; i < childList.size(); ++i)
|
|
|
+ // Update the visibility of all the children
|
|
|
+ // We can't use findChildren as it would return the grandchildren
|
|
|
+ foreach(QObject* childObject, this->children())
|
|
|
{
|
|
|
- QObject *o = childList.at(i);
|
|
|
- if (o && o->isWidgetType())
|
|
|
+ if (childObject->isWidgetType())
|
|
|
{
|
|
|
- QWidget *w = static_cast<QWidget *>(o);
|
|
|
- if ( w )
|
|
|
- {
|
|
|
- w->setVisible(_expand);
|
|
|
- }
|
|
|
+ d->setChildVisibility(qobject_cast<QWidget*>(childObject));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (_expand)
|
|
|
{
|
|
|
- this->setMaximumHeight(this->MaxHeight);
|
|
|
- this->resize(this->OldSize);
|
|
|
+ this->setMaximumHeight(d->MaxHeight);
|
|
|
+ this->resize(d->OldSize);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- this->MaxHeight = this->maximumHeight();
|
|
|
+ d->MaxHeight = this->maximumHeight();
|
|
|
this->setMaximumHeight(22);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-//-----------------------------------------------------------------------------
|
|
|
-void ctkCollapsibleGroupBox::childEvent(QChildEvent* c)
|
|
|
-{
|
|
|
- if(c && c->type() == QEvent::ChildAdded)
|
|
|
- {
|
|
|
- if (c->child() && c->child()->isWidgetType())
|
|
|
- {
|
|
|
- QWidget *w = static_cast<QWidget*>(c->child());
|
|
|
- w->setVisible(this->isChecked());
|
|
|
- }
|
|
|
- }
|
|
|
- QGroupBox::childEvent(c);
|
|
|
-}
|
|
|
-
|
|
|
#if QT_VERSION < 0x040600
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void ctkCollapsibleGroupBox::paintEvent(QPaintEvent* e)
|
|
@@ -189,29 +251,91 @@ void ctkCollapsibleGroupBox::mouseReleaseEvent(QMouseEvent *event)
|
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-QSize ctkCollapsibleGroupBox::minimumSizeHint() const
|
|
|
-{
|
|
|
- //qDebug() << "ctkCollapsibleGroupBox::minimumSizeHint::" << this->QGroupBox::minimumSizeHint() ;
|
|
|
- return this->QGroupBox::minimumSizeHint();
|
|
|
-}
|
|
|
-
|
|
|
-//-----------------------------------------------------------------------------
|
|
|
-QSize ctkCollapsibleGroupBox::sizeHint() const
|
|
|
+void ctkCollapsibleGroupBox::childEvent(QChildEvent* c)
|
|
|
{
|
|
|
- //qDebug() << "ctkCollapsibleGroupBox::sizeHint::" << this->QGroupBox::sizeHint() ;
|
|
|
- return this->QGroupBox::sizeHint();
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
+ 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->QGroupBox::childEvent(c);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-int ctkCollapsibleGroupBox::heightForWidth(int w) const
|
|
|
+void ctkCollapsibleGroupBox::setVisible(bool show)
|
|
|
{
|
|
|
- //qDebug() << "ctkCollapsibleGroupBox::heightForWidth::" << this->QGroupBox::heightForWidth(w) ;
|
|
|
- return this->QGroupBox::heightForWidth(w);
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
+ // calling QWidget::setVisible() on ctkCollapsibleGroupBox 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->QGroupBox::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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
-void ctkCollapsibleGroupBox::resizeEvent ( QResizeEvent * _event )
|
|
|
+bool ctkCollapsibleGroupBox::eventFilter(QObject* child, QEvent* e)
|
|
|
{
|
|
|
- //qDebug() << "ctkCollapsibleGroupBox::resizeEvent::" << _event->oldSize() << _event->size() ;
|
|
|
- return this->QGroupBox::resizeEvent(_event);
|
|
|
+ Q_D(ctkCollapsibleGroupBox);
|
|
|
+ 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->QGroupBox::eventFilter(child, e);
|
|
|
}
|