Added some parts of ctk lib.
As it looks now that lib contains way more then I want to pull in so i'm just copying the files i'm interested in.
This commit is contained in:
parent
a06c752029
commit
3a425ab7c1
11 changed files with 2162 additions and 2 deletions
48
ctk/ctk.pro
Normal file
48
ctk/ctk.pro
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2017-02-26T10:51:14
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += widgets
|
||||
|
||||
TARGET = ctk
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib c++14
|
||||
|
||||
QMAKE_CXXFLAGS += /std:c++17
|
||||
|
||||
INCLUDEPATH += C:\prog\include \
|
||||
C:\Prog\include\pgsql \
|
||||
C:\VSproj\boost32\include\boost-1_65_1
|
||||
|
||||
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
|
||||
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += ctkBasePopupWidget.cpp \
|
||||
ctkPopupWidget.cpp \
|
||||
ctkWidgetsUtils.cpp
|
||||
|
||||
|
||||
HEADERS += ctkBasePopupWidget.h \
|
||||
ctkBasePopupWidget_p.h \
|
||||
ctkPopupWidget.h \
|
||||
ctkPopupWidget_p.h \
|
||||
ctkWidgetsUtils.h \
|
||||
ctkWidgetsExport.h
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
INSTALLS += target
|
||||
}
|
||||
843
ctk/ctkBasePopupWidget.cpp
Normal file
843
ctk/ctkBasePopupWidget.cpp
Normal file
|
|
@ -0,0 +1,843 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QMoveEvent>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
|
||||
// CTK includes
|
||||
#include "ctkBasePopupWidget_p.h"
|
||||
#include "ctkWidgetsUtils.h"
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QGradient* duplicateGradient(const QGradient* gradient)
|
||||
{
|
||||
QGradient* newGradient = 0;
|
||||
switch (gradient->type())
|
||||
{
|
||||
case QGradient::LinearGradient:
|
||||
{
|
||||
const QLinearGradient* linearGradient = static_cast<const QLinearGradient*>(gradient);
|
||||
newGradient = new QLinearGradient(linearGradient->start(), linearGradient->finalStop());
|
||||
break;
|
||||
}
|
||||
case QGradient::RadialGradient:
|
||||
{
|
||||
const QRadialGradient* radialGradient = static_cast<const QRadialGradient*>(gradient);
|
||||
newGradient = new QRadialGradient(radialGradient->center(), radialGradient->radius());
|
||||
break;
|
||||
}
|
||||
case QGradient::ConicalGradient:
|
||||
{
|
||||
const QConicalGradient* conicalGradient = static_cast<const QConicalGradient*>(gradient);
|
||||
newGradient = new QConicalGradient(conicalGradient->center(), conicalGradient->angle());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!newGradient)
|
||||
{
|
||||
Q_ASSERT(gradient->type() != QGradient::NoGradient);
|
||||
return newGradient;
|
||||
}
|
||||
newGradient->setCoordinateMode(gradient->coordinateMode());
|
||||
newGradient->setSpread(gradient->spread());
|
||||
newGradient->setStops(gradient->stops());
|
||||
return newGradient;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidgetPrivate::ctkBasePopupWidgetPrivate(ctkBasePopupWidget& object)
|
||||
:q_ptr(&object)
|
||||
{
|
||||
this->Effect = ctkBasePopupWidget::ScrollEffect;
|
||||
this->EffectDuration = 333; // in ms
|
||||
this->EffectAlpha = 1.;
|
||||
this->AlphaAnimation = 0;
|
||||
this->ForcedTranslucent = false;
|
||||
this->ScrollAnimation = 0;
|
||||
this->PopupPixmapWidget = 0;
|
||||
// Geometry attributes
|
||||
this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
|
||||
this->Orientations = Qt::Vertical;
|
||||
this->VerticalDirection = ctkBasePopupWidget::TopToBottom;
|
||||
this->HorizontalDirection = Qt::LeftToRight;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidgetPrivate::~ctkBasePopupWidgetPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidgetPrivate::init()
|
||||
{
|
||||
Q_Q(ctkBasePopupWidget);
|
||||
// By default, Tooltips are shown only on active windows. In a popup widget
|
||||
// case, we sometimes aren't the active window but we still would like to
|
||||
// show the children tooltips.
|
||||
q->setAttribute(Qt::WA_AlwaysShowToolTips, true);
|
||||
//q->setAttribute(Qt::WA_MacAlwaysShowToolWindow, true);
|
||||
|
||||
this->AlphaAnimation = new QPropertyAnimation(q, "effectAlpha", q);
|
||||
this->AlphaAnimation->setDuration(this->EffectDuration);
|
||||
this->AlphaAnimation->setStartValue(0.);
|
||||
this->AlphaAnimation->setEndValue(1.);
|
||||
QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
|
||||
q, SLOT(onEffectFinished()));
|
||||
|
||||
this->PopupPixmapWidget = new QLabel(q, Qt::ToolTip | Qt::FramelessWindowHint);
|
||||
|
||||
this->ScrollAnimation = new QPropertyAnimation(q, "effectGeometry", q);
|
||||
this->ScrollAnimation->setDuration(this->EffectDuration);
|
||||
QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
|
||||
q, SLOT(onEffectFinished()));
|
||||
QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
|
||||
this->PopupPixmapWidget, SLOT(hide()));
|
||||
|
||||
q->setAnimationEffect(this->Effect);
|
||||
q->setEasingCurve(QEasingCurve::OutCubic);
|
||||
q->setBaseWidget(q->parentWidget());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QPropertyAnimation* ctkBasePopupWidgetPrivate::currentAnimation()const
|
||||
{
|
||||
return this->Effect == ctkBasePopupWidget::ScrollEffect ?
|
||||
this->ScrollAnimation : this->AlphaAnimation;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkBasePopupWidgetPrivate::isOpening()const
|
||||
{
|
||||
return this->currentAnimation()->state() == QAbstractAnimation::Running &&
|
||||
this->currentAnimation()->direction() == QAbstractAnimation::Forward;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkBasePopupWidgetPrivate::isClosing()const
|
||||
{
|
||||
return this->currentAnimation()->state() == QAbstractAnimation::Running &&
|
||||
this->currentAnimation()->direction() == QAbstractAnimation::Backward;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkBasePopupWidgetPrivate::wasClosing()const
|
||||
{
|
||||
Q_Q(const ctkBasePopupWidget);
|
||||
return qobject_cast<QAbstractAnimation*>(q->sender())->direction()
|
||||
== QAbstractAnimation::Backward;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QWidgetList ctkBasePopupWidgetPrivate::focusWidgets(bool onlyVisible)const
|
||||
{
|
||||
Q_Q(const ctkBasePopupWidget);
|
||||
QWidgetList res;
|
||||
if (!onlyVisible || q->isVisible())
|
||||
{
|
||||
res << const_cast<ctkBasePopupWidget*>(q);
|
||||
}
|
||||
if (!this->BaseWidget.isNull() && (!onlyVisible || this->BaseWidget->isVisible()))
|
||||
{
|
||||
res << this->BaseWidget;
|
||||
}
|
||||
if (this->PopupPixmapWidget && (!onlyVisible || this->PopupPixmapWidget->isVisible()))
|
||||
{
|
||||
res << this->PopupPixmapWidget;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QWidget* ctkBasePopupWidgetPrivate::mouseOver()
|
||||
{
|
||||
QList<QWidget*> widgets = this->focusWidgets(true);
|
||||
foreach(QWidget* widget, widgets)
|
||||
{
|
||||
if (widget->underMouse())
|
||||
{
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
// Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
|
||||
const QPoint pos = QCursor::pos();
|
||||
QWidget* widgetUnderCursor = qApp->widgetAt(pos);
|
||||
foreach(const QWidget* focusWidget, widgets)
|
||||
{
|
||||
if (this->isAncestorOf(focusWidget, widgetUnderCursor) &&
|
||||
// Ignore when cursor is above a title bar of a focusWidget, underMouse
|
||||
// wouldn't have return false, but QApplication::widgetAt would return
|
||||
// the widget
|
||||
(focusWidget != widgetUnderCursor ||
|
||||
QRect(QPoint(0,0), focusWidget->size()).contains(
|
||||
focusWidget->mapFromGlobal(pos))))
|
||||
{
|
||||
return widgetUnderCursor;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkBasePopupWidgetPrivate::isAncestorOf(const QWidget* ancestor, const QWidget* child)const
|
||||
{
|
||||
while (child)
|
||||
{
|
||||
if (child == ancestor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
child = child->parentWidget();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidgetPrivate::setupPopupPixmapWidget()
|
||||
{
|
||||
Q_Q(ctkBasePopupWidget);
|
||||
this->PopupPixmapWidget->setAlignment(this->pixmapAlignment());
|
||||
QPixmap pixmap;
|
||||
if (q->testAttribute(Qt::WA_TranslucentBackground))
|
||||
{
|
||||
// only QImage handle transparency correctly
|
||||
QImage image(q->geometry().size(), QImage::Format_ARGB32);
|
||||
image.fill(0);
|
||||
q->render(&image);
|
||||
pixmap = QPixmap::fromImage(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixmap = QPixmap::fromImage(
|
||||
ctk::grabWidget(q, QRect(QPoint(0,0), q->geometry().size())));
|
||||
}
|
||||
this->PopupPixmapWidget->setPixmap(pixmap);
|
||||
this->PopupPixmapWidget->setAttribute(
|
||||
Qt::WA_TranslucentBackground, q->testAttribute(Qt::WA_TranslucentBackground));
|
||||
this->PopupPixmapWidget->setWindowOpacity(q->windowOpacity());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
Qt::Alignment ctkBasePopupWidgetPrivate::pixmapAlignment()const
|
||||
{
|
||||
Qt::Alignment alignment;
|
||||
if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
|
||||
{
|
||||
alignment |= Qt::AlignBottom;
|
||||
}
|
||||
else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
|
||||
{
|
||||
alignment |= Qt::AlignTop;
|
||||
}
|
||||
|
||||
if (this->HorizontalDirection == Qt::LeftToRight)
|
||||
{
|
||||
alignment |= Qt::AlignRight;
|
||||
}
|
||||
else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
|
||||
{
|
||||
alignment |= Qt::AlignLeft;
|
||||
}
|
||||
return alignment;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidgetPrivate::closedGeometry()const
|
||||
{
|
||||
Q_Q(const ctkBasePopupWidget);
|
||||
return this->closedGeometry(q->geometry());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidgetPrivate::closedGeometry(QRect openGeom)const
|
||||
{
|
||||
if (this->Orientations & Qt::Vertical)
|
||||
{
|
||||
if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
|
||||
{
|
||||
openGeom.moveTop(openGeom.bottom());
|
||||
}
|
||||
openGeom.setHeight(0);
|
||||
}
|
||||
if (this->Orientations & Qt::Horizontal)
|
||||
{
|
||||
if (this->HorizontalDirection == Qt::RightToLeft)
|
||||
{
|
||||
openGeom.moveLeft(openGeom.right());
|
||||
}
|
||||
openGeom.setWidth(0);
|
||||
}
|
||||
return openGeom;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidgetPrivate::baseGeometry()const
|
||||
{
|
||||
if (this->BaseWidget.isNull())
|
||||
{
|
||||
return QRect();
|
||||
}
|
||||
return QRect(this->mapToGlobal(this->BaseWidget->geometry().topLeft()),
|
||||
this->BaseWidget->size());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QPoint ctkBasePopupWidgetPrivate::mapToGlobal(const QPoint& baseWidgetPoint)const
|
||||
{
|
||||
QPoint mappedPoint = baseWidgetPoint;
|
||||
if (!this->BaseWidget.isNull() && this->BaseWidget->parentWidget())
|
||||
{
|
||||
mappedPoint = this->BaseWidget->parentWidget()->mapToGlobal(mappedPoint);
|
||||
}
|
||||
return mappedPoint;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry()const
|
||||
{
|
||||
return this->desiredOpenGeometry(this->baseGeometry());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry(QRect baseGeometry)const
|
||||
{
|
||||
Q_Q(const ctkBasePopupWidget);
|
||||
QSize size = q->size();
|
||||
if (!q->testAttribute(Qt::WA_WState_Created))
|
||||
{
|
||||
size = q->sizeHint();
|
||||
}
|
||||
|
||||
if (baseGeometry.isNull())
|
||||
{
|
||||
return QRect(q->pos(), size);
|
||||
}
|
||||
|
||||
QRect geometry;
|
||||
if (this->Alignment & Qt::AlignJustify)
|
||||
{
|
||||
if (this->Orientations & Qt::Vertical)
|
||||
{
|
||||
size.setWidth(baseGeometry.width());
|
||||
}
|
||||
}
|
||||
if (this->Alignment & Qt::AlignTop &&
|
||||
this->Alignment & Qt::AlignBottom)
|
||||
{
|
||||
size.setHeight(baseGeometry.height());
|
||||
}
|
||||
|
||||
geometry.setSize(size);
|
||||
|
||||
QPoint topLeft = baseGeometry.topLeft();
|
||||
QPoint bottomRight = baseGeometry.bottomRight();
|
||||
|
||||
if (this->Alignment & Qt::AlignLeft)
|
||||
{
|
||||
if (this->HorizontalDirection == Qt::LeftToRight)
|
||||
{
|
||||
geometry.moveLeft(topLeft.x());
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry.moveRight(topLeft.x() - 1);
|
||||
}
|
||||
}
|
||||
else if (this->Alignment & Qt::AlignRight)
|
||||
{
|
||||
if (this->HorizontalDirection == Qt::LeftToRight)
|
||||
{
|
||||
geometry.moveLeft(bottomRight.x() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry.moveRight(bottomRight.x());
|
||||
}
|
||||
}
|
||||
else if (this->Alignment & Qt::AlignHCenter)
|
||||
{
|
||||
geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
|
||||
}
|
||||
else if (this->Alignment & Qt::AlignJustify)
|
||||
{
|
||||
geometry.moveLeft(topLeft.x());
|
||||
}
|
||||
|
||||
if (this->Alignment & Qt::AlignTop)
|
||||
{
|
||||
if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
|
||||
{
|
||||
geometry.moveTop(topLeft.y());
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry.moveBottom(topLeft.y() - 1);
|
||||
}
|
||||
}
|
||||
else if (this->Alignment & Qt::AlignBottom)
|
||||
{
|
||||
if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
|
||||
{
|
||||
geometry.moveTop(bottomRight.y() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry.moveBottom(bottomRight.y());
|
||||
}
|
||||
}
|
||||
else if (this->Alignment & Qt::AlignVCenter)
|
||||
{
|
||||
geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidgetPrivate::hideAll()
|
||||
{
|
||||
Q_Q(ctkBasePopupWidget);
|
||||
|
||||
// It is possible to have the popup widget not being a popup but inside
|
||||
// a layout: maybe the popup has been pin-down in a way that it gets parented
|
||||
// In that case, there is no reason to hide the popup.
|
||||
if (!(q->windowFlags() & PopupWindowType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Before hiding, transfer the active window flag to its parent, this will
|
||||
// prevent the application to send a ApplicationDeactivate signal that
|
||||
// doesn't need to be done.
|
||||
#ifndef Q_OS_MAC // See Slicer issue #3850
|
||||
if (q->isActiveWindow() && !this->BaseWidget.isNull())
|
||||
{
|
||||
qApp->setActiveWindow(this->BaseWidget->window());
|
||||
}
|
||||
#endif
|
||||
|
||||
q->hide();
|
||||
this->PopupPixmapWidget->hide();
|
||||
|
||||
// If there is a popup open in the ctkBasePopupWidget children, then hide it
|
||||
// as well so we don't have a popup open while the ctkBasePopupWidget is hidden.
|
||||
QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();
|
||||
if (activePopupWidget && this->isAncestorOf(q, activePopupWidget))
|
||||
{
|
||||
activePopupWidget->close();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Qt::FramelessWindowHint is required on Windows for Translucent background
|
||||
// Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
|
||||
// click outside the widget (typically a click in the BaseWidget)
|
||||
ctkBasePopupWidget::ctkBasePopupWidget(QWidget* parentWidget)
|
||||
//: Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
|
||||
: Superclass(parentWidget,
|
||||
PopupWindowType | Qt::FramelessWindowHint)
|
||||
, d_ptr(new ctkBasePopupWidgetPrivate(*this))
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->init();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidget::ctkBasePopupWidget(ctkBasePopupWidgetPrivate* pimpl, QWidget* parentWidget)
|
||||
//: //Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
|
||||
: Superclass(parentWidget,
|
||||
PopupWindowType | Qt::FramelessWindowHint)
|
||||
, d_ptr(pimpl)
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidget::~ctkBasePopupWidget()
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QWidget* ctkBasePopupWidget::baseWidget()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->BaseWidget;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setBaseWidget(QWidget* widget)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
if (!d->BaseWidget.isNull())
|
||||
{
|
||||
//disconnect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
|
||||
// this, SLOT(onBaseWidgetDestroyed()));
|
||||
}
|
||||
d->BaseWidget = widget;
|
||||
if (!d->BaseWidget.isNull())
|
||||
{
|
||||
//connect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
|
||||
// this, SLOT(onBaseWidgetDestroyed()));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::onBaseWidgetDestroyed()
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->hideAll();
|
||||
this->setBaseWidget(0);
|
||||
// could be a property.
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidget::AnimationEffect ctkBasePopupWidget::animationEffect()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->Effect;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setAnimationEffect(ctkBasePopupWidget::AnimationEffect effect)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
/// TODO: handle the case where there is an animation running
|
||||
d->Effect = effect;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
int ctkBasePopupWidget::effectDuration()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->EffectDuration;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setEffectDuration(int duration)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->EffectDuration = duration;
|
||||
d->AlphaAnimation->setDuration(d->EffectDuration);
|
||||
d->ScrollAnimation->setDuration(d->EffectDuration);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QEasingCurve::Type ctkBasePopupWidget::easingCurve()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->AlphaAnimation->easingCurve().type();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setEasingCurve(QEasingCurve::Type easingCurve)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->AlphaAnimation->setEasingCurve(easingCurve);
|
||||
d->ScrollAnimation->setEasingCurve(easingCurve);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
Qt::Alignment ctkBasePopupWidget::alignment()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->Alignment;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setAlignment(Qt::Alignment alignment)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->Alignment = alignment;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
Qt::Orientations ctkBasePopupWidget::orientation()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->Orientations;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setOrientation(Qt::Orientations orientations)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->Orientations = orientations;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkBasePopupWidget::VerticalDirection ctkBasePopupWidget::verticalDirection()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->VerticalDirection;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setVerticalDirection(ctkBasePopupWidget::VerticalDirection verticalDirection)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->VerticalDirection = verticalDirection;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
Qt::LayoutDirection ctkBasePopupWidget::horizontalDirection()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->HorizontalDirection;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setHorizontalDirection(Qt::LayoutDirection horizontalDirection)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->HorizontalDirection = horizontalDirection;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::onEffectFinished()
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
if (d->ForcedTranslucent)
|
||||
{
|
||||
d->ForcedTranslucent = false;
|
||||
this->setAttribute(Qt::WA_TranslucentBackground, false);
|
||||
}
|
||||
if (d->wasClosing())
|
||||
{
|
||||
d->hideAll();
|
||||
emit this->popupOpened(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->show();
|
||||
emit this->popupOpened(true);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkBasePopupWidget::event(QEvent* event)
|
||||
{
|
||||
switch(event->type())
|
||||
{
|
||||
case QEvent::ParentChange:
|
||||
// For now the base widget is the parent widget
|
||||
this->setBaseWidget(this->parentWidget());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this->Superclass::event(event);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
QBrush brush = this->palette().window();
|
||||
if (brush.style() == Qt::LinearGradientPattern ||
|
||||
brush.style() == Qt::ConicalGradientPattern ||
|
||||
brush.style() == Qt::RadialGradientPattern)
|
||||
{
|
||||
QGradient* newGradient = duplicateGradient(brush.gradient());
|
||||
QGradientStops stops;
|
||||
foreach(QGradientStop stop, newGradient->stops())
|
||||
{
|
||||
stop.second.setAlpha(stop.second.alpha() * d->EffectAlpha);
|
||||
stops.push_back(stop);
|
||||
}
|
||||
newGradient->setStops(stops);
|
||||
brush = QBrush(*newGradient);
|
||||
delete newGradient;
|
||||
}
|
||||
else
|
||||
{
|
||||
QColor color = brush.color();
|
||||
color.setAlpha(color.alpha() * d->EffectAlpha);
|
||||
brush.setColor(color);
|
||||
}
|
||||
//QColor semiTransparentColor = this->palette().window().color();
|
||||
//semiTransparentColor.setAlpha(d->CurrentAlpha);
|
||||
painter.fillRect(this->rect(), brush);
|
||||
painter.end();
|
||||
// Let the QFrame draw itself if needed
|
||||
this->Superclass::paintEvent(event);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::showPopup()
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
|
||||
if ((this->isVisible() &&
|
||||
d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
|
||||
(!d->BaseWidget.isNull() && !d->BaseWidget->isVisible()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the layout has never been activated, the widget doesn't know its
|
||||
// minSize/maxSize and we then wouldn't know what's its true geometry.
|
||||
if (this->layout() && !this->testAttribute(Qt::WA_WState_Created))
|
||||
{
|
||||
this->layout()->activate();
|
||||
}
|
||||
this->setGeometry(d->desiredOpenGeometry());
|
||||
/// Maybe the popup doesn't allow the desiredOpenGeometry if the widget
|
||||
/// minimum size is larger than the desired size.
|
||||
QRect openGeometry = this->geometry();
|
||||
QRect closedGeometry = d->closedGeometry();
|
||||
|
||||
d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
|
||||
|
||||
switch(d->Effect)
|
||||
{
|
||||
case WindowOpacityFadeEffect:
|
||||
if (!this->testAttribute(Qt::WA_TranslucentBackground))
|
||||
{
|
||||
d->ForcedTranslucent = true;
|
||||
this->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
}
|
||||
this->show();
|
||||
break;
|
||||
case ScrollEffect:
|
||||
{
|
||||
d->PopupPixmapWidget->setGeometry(closedGeometry);
|
||||
d->ScrollAnimation->setStartValue(closedGeometry);
|
||||
d->ScrollAnimation->setEndValue(openGeometry);
|
||||
d->setupPopupPixmapWidget();
|
||||
d->PopupPixmapWidget->show();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch(d->currentAnimation()->state())
|
||||
{
|
||||
case QAbstractAnimation::Stopped:
|
||||
d->currentAnimation()->start();
|
||||
break;
|
||||
case QAbstractAnimation::Paused:
|
||||
d->currentAnimation()->resume();
|
||||
break;
|
||||
default:
|
||||
case QAbstractAnimation::Running:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::hidePopup()
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
|
||||
if (!this->isVisible() &&
|
||||
d->currentAnimation()->state() == QAbstractAnimation::Stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
|
||||
|
||||
QRect openGeometry = this->geometry();
|
||||
QRect closedGeometry = d->closedGeometry();
|
||||
|
||||
switch(d->Effect)
|
||||
{
|
||||
case WindowOpacityFadeEffect:
|
||||
if (!this->testAttribute(Qt::WA_TranslucentBackground))
|
||||
{
|
||||
d->ForcedTranslucent = true;
|
||||
this->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
}
|
||||
break;
|
||||
case ScrollEffect:
|
||||
{
|
||||
d->ScrollAnimation->setStartValue(closedGeometry);
|
||||
d->ScrollAnimation->setEndValue(openGeometry);
|
||||
d->setupPopupPixmapWidget();
|
||||
d->PopupPixmapWidget->setGeometry(this->geometry());
|
||||
d->PopupPixmapWidget->show();
|
||||
if (this->isActiveWindow())
|
||||
{
|
||||
qApp->setActiveWindow(!d->BaseWidget.isNull() ? d->BaseWidget->window() : 0);
|
||||
}
|
||||
this->hide();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch(d->currentAnimation()->state())
|
||||
{
|
||||
case QAbstractAnimation::Stopped:
|
||||
d->currentAnimation()->start();
|
||||
break;
|
||||
case QAbstractAnimation::Paused:
|
||||
d->currentAnimation()->resume();
|
||||
break;
|
||||
default:
|
||||
case QAbstractAnimation::Running:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
double ctkBasePopupWidget::effectAlpha()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->EffectAlpha;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setEffectAlpha(double alpha)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->EffectAlpha = alpha;
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
QRect ctkBasePopupWidget::effectGeometry()const
|
||||
{
|
||||
Q_D(const ctkBasePopupWidget);
|
||||
return d->PopupPixmapWidget->geometry();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::setEffectGeometry(QRect newGeometry)
|
||||
{
|
||||
Q_D(ctkBasePopupWidget);
|
||||
d->PopupPixmapWidget->setGeometry(newGeometry);
|
||||
d->PopupPixmapWidget->repaint();
|
||||
}
|
||||
225
ctk/ctkBasePopupWidget.h
Normal file
225
ctk/ctkBasePopupWidget.h
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 __ctkBasePopupWidget_h
|
||||
#define __ctkBasePopupWidget_h
|
||||
|
||||
// Qt includes
|
||||
#include <QEasingCurve>
|
||||
#include <QFrame>
|
||||
#include <QMetaType>
|
||||
|
||||
// CTK includes
|
||||
#include "ctkWidgetsExport.h"
|
||||
|
||||
class ctkBasePopupWidgetPrivate;
|
||||
|
||||
/// \ingroup Widgets
|
||||
/// ctkBasePopupWidget is a popup that opens under, above or on the side of
|
||||
/// another widget (baseWidget() or its parent widget by default).
|
||||
/// The children (widgets and layout) of the popup define of the content
|
||||
/// of the popup. Different effects can be applied during the opening or
|
||||
/// closing of the popup.
|
||||
/// See ctkPopupWidget for an automatic control of its opening and closing.
|
||||
/// \sa baseWidget(), animationEffect, ctkPopupWidget
|
||||
class CTK_WIDGETS_EXPORT ctkBasePopupWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_ENUMS(AnimationEffect)
|
||||
Q_ENUMS(VerticalDirection)
|
||||
|
||||
/// This property controls the effect to apply when the popup is being
|
||||
/// opened or closed. The total duration and the easing curve of the effect
|
||||
/// are controlled by \a effectDuration and \easingCurve respectively.
|
||||
/// ScrollEffect by default.
|
||||
/// \sa AnimationEffect, animationEffect(), setAnimationEffect(),
|
||||
/// effectDuration, easingCurve
|
||||
Q_PROPERTY( AnimationEffect animationEffect READ animationEffect WRITE setAnimationEffect)
|
||||
|
||||
/// The property controls the \a animationEffect duration in ms.
|
||||
/// If the popup state (open or close) is being changed during the animation,
|
||||
/// the active animation is stopped and a new animation is being created from
|
||||
/// the current state (geometry, transparency...) to the new final state.
|
||||
/// Default to 333ms
|
||||
/// \sa effectDuration(), setEffectDuration(), animationEffect, easingCurve
|
||||
Q_PROPERTY( int effectDuration READ effectDuration WRITE setEffectDuration);
|
||||
|
||||
/// The property controls the behavior of the opening or closing curve of the
|
||||
/// animation effect.
|
||||
/// QEasingCurve::InOutQuad by default
|
||||
/// \sa easingCurve(), setEasingCurve(), animationEffect, effectDuration
|
||||
Q_PROPERTY( QEasingCurve::Type easingCurve READ easingCurve WRITE setEasingCurve);
|
||||
|
||||
/// Where is the popup in relation to the BaseWidget
|
||||
/// To vertically justify, use Qt::AlignTop | Qt::AlignBottom.
|
||||
/// Qt::AlignJustify | Qt::AlignBottom by default
|
||||
Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment);
|
||||
|
||||
/// Direction of the scrolling effect, can be Qt::Vertical, Qt::Horizontal or
|
||||
/// both Qt::Vertical|Qt::Horizontal.
|
||||
/// Vertical by default
|
||||
Q_PROPERTY( Qt::Orientations orientation READ orientation WRITE setOrientation);
|
||||
|
||||
/// Control where the popup opens vertically.
|
||||
/// TopToBottom by default
|
||||
Q_PROPERTY( ctkBasePopupWidget::VerticalDirection verticalDirection READ verticalDirection WRITE setVerticalDirection);
|
||||
|
||||
/// Control where the popup opens horizontally.
|
||||
/// LeftToRight by default
|
||||
Q_PROPERTY( Qt::LayoutDirection horizontalDirection READ horizontalDirection WRITE setHorizontalDirection);
|
||||
|
||||
public:
|
||||
typedef QFrame Superclass;
|
||||
/// Although a popup widget is a top-level widget, if a parent is
|
||||
/// passed the popup widget will be deleted when that parent is
|
||||
/// destroyed (as with any other QObject).
|
||||
/// ctkBasePopupWidget is a top-level widget (Qt::ToolTip), so
|
||||
/// even if a parent is passed, the popup will display outside the possible
|
||||
/// parent layout.
|
||||
/// \sa baseWidget().
|
||||
explicit ctkBasePopupWidget(QWidget* parent = 0);
|
||||
virtual ~ctkBasePopupWidget();
|
||||
|
||||
/// Widget the popup is attached to. It opens right under \a baseWidget
|
||||
/// and if the ctkBasePopupWidget sizepolicy contains the growFlag/shrinkFlag,
|
||||
/// it tries to resize itself to fit the same width of \a baseWidget.
|
||||
/// By default, baseWidget is the parent widget.
|
||||
/// \sa setBaseWidget()
|
||||
QWidget* baseWidget()const;
|
||||
|
||||
enum AnimationEffect
|
||||
{
|
||||
WindowOpacityFadeEffect = 0,
|
||||
ScrollEffect,
|
||||
FadeEffect
|
||||
};
|
||||
|
||||
/// Return the animationEffect property value.
|
||||
/// \sa animationEffect
|
||||
AnimationEffect animationEffect()const;
|
||||
/// Set the animationEffect property value.
|
||||
/// \sa animationEffect
|
||||
void setAnimationEffect(AnimationEffect effect);
|
||||
|
||||
/// Return the effectDuration property value.
|
||||
/// \sa effectDuration
|
||||
int effectDuration()const;
|
||||
/// Set the effectDuration property value.
|
||||
/// \sa effectDuration
|
||||
void setEffectDuration(int duration);
|
||||
|
||||
/// Return the easingCurve property value.
|
||||
/// \sa easingCurve
|
||||
QEasingCurve::Type easingCurve()const;
|
||||
/// Set the easingCurve property value.
|
||||
/// \sa easingCurve
|
||||
void setEasingCurve(QEasingCurve::Type easingCurve);
|
||||
|
||||
/// Return the alignment property value.
|
||||
/// \sa alignment
|
||||
Qt::Alignment alignment()const;
|
||||
/// Set the alignment property value.
|
||||
/// \sa alignment
|
||||
void setAlignment(Qt::Alignment alignment);
|
||||
|
||||
/// Return the orientation property value.
|
||||
/// \sa orientation
|
||||
Qt::Orientations orientation()const;
|
||||
/// Set the orientation property value.
|
||||
/// \sa orientation
|
||||
void setOrientation(Qt::Orientations orientation);
|
||||
|
||||
enum VerticalDirection{
|
||||
TopToBottom = 1,
|
||||
BottomToTop = 2
|
||||
};
|
||||
|
||||
/// Return the verticalDirection property value.
|
||||
/// \sa verticalDirection
|
||||
VerticalDirection verticalDirection()const;
|
||||
/// Set the verticalDirection property value.
|
||||
/// \sa verticalDirection
|
||||
void setVerticalDirection(VerticalDirection direction);
|
||||
|
||||
/// Return the horizontalDirection property value.
|
||||
/// \sa horizontalDirection
|
||||
Qt::LayoutDirection horizontalDirection()const;
|
||||
/// Set the horizontalDirection property value.
|
||||
/// \sa horizontalDirection
|
||||
void setHorizontalDirection(Qt::LayoutDirection direction);
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Hide the popup if open or opening. It takes around 300ms
|
||||
/// for the fading effect to hide the popup.
|
||||
virtual void hidePopup();
|
||||
/// Open the popup if closed or closing. It takes around 300ms
|
||||
/// for the fading effect to open the popup.
|
||||
virtual void showPopup();
|
||||
/// Show/hide the popup. It can be conveniently linked to a QPushButton
|
||||
/// signal.
|
||||
inline void showPopup(bool show);
|
||||
|
||||
Q_SIGNALS:
|
||||
/// Fired when the popup finished its animation: opening (true) or closing (false).
|
||||
/// \sa showPopup(), hidePopup()
|
||||
void popupOpened(bool open);
|
||||
|
||||
protected:
|
||||
explicit ctkBasePopupWidget(ctkBasePopupWidgetPrivate* pimpl, QWidget* parent = 0);
|
||||
QScopedPointer<ctkBasePopupWidgetPrivate> d_ptr;
|
||||
Q_PROPERTY(double effectAlpha READ effectAlpha WRITE setEffectAlpha DESIGNABLE false)
|
||||
Q_PROPERTY(QRect effectGeometry READ effectGeometry WRITE setEffectGeometry DESIGNABLE false)
|
||||
|
||||
double effectAlpha()const;
|
||||
QRect effectGeometry()const;
|
||||
|
||||
virtual void setBaseWidget(QWidget* baseWidget);
|
||||
virtual bool event(QEvent* event);
|
||||
virtual void paintEvent(QPaintEvent*);
|
||||
|
||||
protected Q_SLOTS:
|
||||
virtual void onEffectFinished();
|
||||
void setEffectAlpha(double alpha);
|
||||
void setEffectGeometry(QRect geometry);
|
||||
void onBaseWidgetDestroyed();
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(ctkBasePopupWidget);
|
||||
Q_DISABLE_COPY(ctkBasePopupWidget);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ctkBasePopupWidget::AnimationEffect)
|
||||
Q_DECLARE_METATYPE(ctkBasePopupWidget::VerticalDirection)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkBasePopupWidget::showPopup(bool show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
this->showPopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->hidePopup();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
108
ctk/ctkBasePopupWidget_p.h
Normal file
108
ctk/ctkBasePopupWidget_p.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 __ctkBasePopupWidget_p_h
|
||||
#define __ctkBasePopupWidget_p_h
|
||||
|
||||
// Qt includes
|
||||
#include <QPointer>
|
||||
class QLabel;
|
||||
class QPropertyAnimation;
|
||||
|
||||
// CTK includes
|
||||
#include "ctkBasePopupWidget.h"
|
||||
#define PopupWindowType Qt::Tool
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/// \ingroup Widgets
|
||||
class CTK_WIDGETS_EXPORT ctkBasePopupWidgetPrivate
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PUBLIC(ctkBasePopupWidget);
|
||||
protected:
|
||||
ctkBasePopupWidget* const q_ptr;
|
||||
public:
|
||||
ctkBasePopupWidgetPrivate(ctkBasePopupWidget& object);
|
||||
~ctkBasePopupWidgetPrivate();
|
||||
virtual void init();
|
||||
|
||||
bool isOpening()const;
|
||||
bool isClosing()const;
|
||||
/// Return true if the animation was closing (direction == backward).
|
||||
/// It doesn't indicate if the action is still running or finished.
|
||||
/// Can only be called in a slot as it uses sender().
|
||||
bool wasClosing()const;
|
||||
|
||||
bool fitBaseWidgetSize()const;
|
||||
Qt::Alignment pixmapAlignment()const;
|
||||
void setupPopupPixmapWidget();
|
||||
|
||||
QWidgetList focusWidgets(bool onlyVisible = false)const;
|
||||
|
||||
// Return the widget if the mouse cursor is above any of the focus widgets or their
|
||||
// children.
|
||||
virtual QWidget* mouseOver();
|
||||
|
||||
// Same as QWidget::isAncestorOf() but don't restrain to the same window
|
||||
// and apply it to all the focusWidgets
|
||||
bool isAncestorOf(const QWidget* ancestor, const QWidget* child)const;
|
||||
|
||||
|
||||
/// Return the closed geometry for the popup based on the current geometry
|
||||
QRect closedGeometry()const;
|
||||
/// Return the closed geometry for a given open geometry
|
||||
QRect closedGeometry(QRect openGeom)const;
|
||||
|
||||
/// Return the desired geometry, maybe it won't happen if the size is too
|
||||
/// small for the popup.
|
||||
QRect desiredOpenGeometry()const;
|
||||
QRect desiredOpenGeometry(QRect baseGeometry)const;
|
||||
QRect baseGeometry()const;
|
||||
QPoint mapToGlobal(const QPoint& baseWidgetPoint)const;
|
||||
|
||||
QPropertyAnimation* currentAnimation()const;
|
||||
|
||||
//void temporarilyHiddenOn();
|
||||
//void temporarilyHiddenOff();
|
||||
|
||||
void hideAll();
|
||||
|
||||
protected:
|
||||
QPointer<QWidget> BaseWidget;
|
||||
|
||||
double EffectAlpha;
|
||||
|
||||
ctkBasePopupWidget::AnimationEffect Effect;
|
||||
int EffectDuration;
|
||||
QPropertyAnimation* AlphaAnimation;
|
||||
bool ForcedTranslucent;
|
||||
QPropertyAnimation* ScrollAnimation;
|
||||
QLabel* PopupPixmapWidget;
|
||||
|
||||
// Geometry attributes
|
||||
Qt::Alignment Alignment;
|
||||
Qt::Orientations Orientations;
|
||||
|
||||
ctkBasePopupWidget::VerticalDirection VerticalDirection;
|
||||
Qt::LayoutDirection HorizontalDirection;
|
||||
};
|
||||
|
||||
#endif
|
||||
560
ctk/ctkPopupWidget.cpp
Normal file
560
ctk/ctkPopupWidget.cpp
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 <QDesktopWidget>
|
||||
#include <QDialog>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QMoveEvent>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
|
||||
// CTK includes
|
||||
#include "ctkPopupWidget_p.h"
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
|
||||
:Superclass(object)
|
||||
{
|
||||
this->Active = false;
|
||||
this->AutoShow = true;
|
||||
this->ShowDelay = 20;
|
||||
this->AutoHide = true;
|
||||
this->HideDelay = 200;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkPopupWidgetPrivate::~ctkPopupWidgetPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidgetPrivate::init()
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
this->setParent(q);
|
||||
q->setActive(true);
|
||||
this->Superclass::init();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
QWidget* ctkPopupWidgetPrivate::mouseOver()
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
QWidget* widgetUnderCursor = this->Superclass::mouseOver();
|
||||
if (widgetUnderCursor &&
|
||||
!this->focusWidgets(true).contains(widgetUnderCursor))
|
||||
{
|
||||
widgetUnderCursor->installEventFilter(q);
|
||||
}
|
||||
return widgetUnderCursor;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkPopupWidgetPrivate::eventFilter(QObject* obj, QEvent* event)
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
QWidget* widget = qobject_cast<QWidget*>(obj);
|
||||
if (!widget)
|
||||
{
|
||||
return this->Superclass::eventFilter(obj, event);
|
||||
}
|
||||
// Here are the application events, it's a lot of events, so we need to be
|
||||
// careful to be fast.
|
||||
if (event->type() == QEvent::ApplicationDeactivate)
|
||||
{
|
||||
// We wait to see if there is no other window being active
|
||||
QTimer::singleShot(0, this, SLOT(onApplicationDeactivate()));
|
||||
}
|
||||
else if (event->type() == QEvent::ApplicationActivate)
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(updateVisibility()));
|
||||
}
|
||||
if (this->BaseWidget.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (event->type() == QEvent::Move && widget != this->BaseWidget)
|
||||
{
|
||||
if (widget->isAncestorOf(this->BaseWidget))
|
||||
{
|
||||
q->setGeometry(this->desiredOpenGeometry());
|
||||
}
|
||||
else if (this->isHidingCandidate(widget))
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(updateVisibility()));
|
||||
}
|
||||
}
|
||||
else if (event->type() == QEvent::Resize)
|
||||
{
|
||||
if (widget->isAncestorOf(this->BaseWidget))
|
||||
{
|
||||
q->setGeometry(this->desiredOpenGeometry());
|
||||
}
|
||||
else if (this->isHidingCandidate(widget))
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(updateVisibility()));
|
||||
}
|
||||
}
|
||||
else if (event->type() == QEvent::WindowStateChange &&
|
||||
this->isHidingCandidate(widget))
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(updateVisibility()));
|
||||
}
|
||||
else if ((event->type() == QEvent::WindowActivate ||
|
||||
event->type() == QEvent::WindowDeactivate) &&
|
||||
widget == this->BaseWidget->window())
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(updateVisibility()));
|
||||
}
|
||||
else if (event->type() == QEvent::RequestSoftwareInputPanel)
|
||||
{
|
||||
qApp->setActiveWindow(widget->window());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidgetPrivate::onApplicationDeactivate()
|
||||
{
|
||||
// Still no active window, that means the user now is controlling another
|
||||
// application, we have no control over when the other app moves over the
|
||||
// popup, so we hide the popup as it would show on top of the other app.
|
||||
if (!qApp->activeWindow())
|
||||
{
|
||||
this->temporarilyHiddenOn();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkPopupWidgetPrivate::isHidingCandidate(QWidget* widget)const
|
||||
{
|
||||
// The mac window manager is keeping the Qt:Tool widgets always on top,
|
||||
// so if a non modal dialog is moved near the popup widget, the popup will
|
||||
// always appear on top of the dialog. For this reason we manually have to
|
||||
// hide the popup when a dialog is intersecting with the popup.
|
||||
bool canWindowsHidePopup = false;
|
||||
#if defined Q_OS_MAC
|
||||
canWindowsHidePopup = true;
|
||||
#endif
|
||||
bool isWindow = widget->isWindow();
|
||||
QDialog* dialog = qobject_cast<QDialog*>(widget);
|
||||
bool isModal = dialog ? dialog->isModal() : false;
|
||||
bool isBasePopupWidget = qobject_cast<ctkBasePopupWidget*>(widget);
|
||||
bool isToolTip = widget->windowType() == Qt::ToolTip;
|
||||
bool isPopup = widget->windowType() == Qt::Popup;
|
||||
bool isSelf = (widget == (this->BaseWidget ? this->BaseWidget->window() : 0));
|
||||
|
||||
return canWindowsHidePopup && isWindow && !isModal && !isBasePopupWidget &&
|
||||
!isToolTip && !isPopup && !isSelf;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidgetPrivate::updateVisibility()
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
// If the BaseWidget window is active, then there is no reason to cover the
|
||||
// popup.
|
||||
if (this->BaseWidget.isNull() ||
|
||||
// the popupwidget active window is not active
|
||||
(!this->BaseWidget->window()->isActiveWindow() &&
|
||||
// and no other active window
|
||||
(!qApp->activeWindow() ||
|
||||
// or the active window is a popup
|
||||
(!qobject_cast<ctkBasePopupWidget*>(qApp->activeWindow()) && //->windowType() != PopupWindowType &&
|
||||
qApp->activeWindow()->windowType() != Qt::Popup))))
|
||||
{
|
||||
foreach(QWidget* topLevelWidget, qApp->topLevelWidgets())
|
||||
{
|
||||
// If there is at least 1 window (active or not) that covers the popup,
|
||||
// then we ensure the popup is hidden.
|
||||
// We have no way of knowing which toplevel is over (z-order) which one,
|
||||
// it is an OS specific information.
|
||||
// Of course, tooltips and popups don't count as covering windows.
|
||||
if (topLevelWidget->isVisible() &&
|
||||
!(topLevelWidget->windowState() & Qt::WindowMinimized) &&
|
||||
this->isHidingCandidate(topLevelWidget) &&
|
||||
topLevelWidget->frameGeometry().intersects(q->geometry()))
|
||||
{
|
||||
//qDebug() << "hide" << q << "because of: " << topLevelWidget
|
||||
// << " with windowType: " << topLevelWidget->windowType()
|
||||
// << topLevelWidget->isVisible()
|
||||
// << (this->BaseWidget ? this->BaseWidget->window() : 0)
|
||||
// << topLevelWidget->frameGeometry();
|
||||
this->temporarilyHiddenOn();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the base widget is hidden or minimized, we don't want to restore the
|
||||
// popup.
|
||||
if (!this->BaseWidget.isNull() &&
|
||||
(!this->BaseWidget->isVisible() ||
|
||||
this->BaseWidget->window()->windowState() & Qt::WindowMinimized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Restore the visibility of the popup if it was hidden
|
||||
this->temporarilyHiddenOff();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidgetPrivate::temporarilyHiddenOn()
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
if (!this->AutoHide &&
|
||||
(q->isVisible() || this->isOpening()) &&
|
||||
!(q->isHidden() || this->isClosing()))
|
||||
{
|
||||
this->setProperty("forcedClosed", this->isOpening() ? 2 : 1);
|
||||
}
|
||||
this->currentAnimation()->stop();
|
||||
this->hideAll();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidgetPrivate::temporarilyHiddenOff()
|
||||
{
|
||||
Q_Q(ctkPopupWidget);
|
||||
|
||||
int forcedClosed = this->property("forcedClosed").toInt();
|
||||
if (forcedClosed > 0)
|
||||
{
|
||||
q->show();
|
||||
if (forcedClosed == 2)
|
||||
{
|
||||
emit q->popupOpened(true);
|
||||
}
|
||||
this->setProperty("forcedClosed", 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
q->updatePopup();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Qt::FramelessWindowHint is required on Windows for Translucent background
|
||||
// Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
|
||||
// click outside the widget (typically a click in the BaseWidget)
|
||||
ctkPopupWidget::ctkPopupWidget(QWidget* parentWidget)
|
||||
: Superclass(new ctkPopupWidgetPrivate(*this), parentWidget)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
d->init();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
ctkPopupWidget::~ctkPopupWidget()
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkPopupWidget::isActive()const
|
||||
{
|
||||
Q_D(const ctkPopupWidget);
|
||||
return d->Active;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setActive(bool active)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
if (active == d->Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
d->Active = active;
|
||||
if (d->Active)
|
||||
{
|
||||
if (!d->BaseWidget.isNull())
|
||||
{
|
||||
d->BaseWidget->installEventFilter(this);
|
||||
}
|
||||
if (d->PopupPixmapWidget)
|
||||
{
|
||||
d->PopupPixmapWidget->installEventFilter(this);
|
||||
}
|
||||
qApp->installEventFilter(d);
|
||||
}
|
||||
else // not active
|
||||
{
|
||||
if (!d->BaseWidget.isNull())
|
||||
{
|
||||
d->BaseWidget->removeEventFilter(this);
|
||||
}
|
||||
if (d->PopupPixmapWidget)
|
||||
{
|
||||
d->PopupPixmapWidget->removeEventFilter(this);
|
||||
}
|
||||
qApp->removeEventFilter(d);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setBaseWidget(QWidget* widget)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
if (!d->BaseWidget.isNull())
|
||||
{
|
||||
d->BaseWidget->removeEventFilter(this);
|
||||
}
|
||||
this->Superclass::setBaseWidget(widget);
|
||||
if (!d->BaseWidget.isNull() && d->Active)
|
||||
{
|
||||
d->BaseWidget->installEventFilter(this);
|
||||
}
|
||||
QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkPopupWidget::autoShow()const
|
||||
{
|
||||
Q_D(const ctkPopupWidget);
|
||||
return d->AutoShow;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setAutoShow(bool mode)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
d->AutoShow = mode;
|
||||
QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
int ctkPopupWidget::showDelay()const
|
||||
{
|
||||
Q_D(const ctkPopupWidget);
|
||||
return d->ShowDelay;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setShowDelay(int delay)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
d->ShowDelay = delay;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
bool ctkPopupWidget::autoHide()const
|
||||
{
|
||||
Q_D(const ctkPopupWidget);
|
||||
return d->AutoHide;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setAutoHide(bool mode)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
d->AutoHide = mode;
|
||||
QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
int ctkPopupWidget::hideDelay()const
|
||||
{
|
||||
Q_D(const ctkPopupWidget);
|
||||
return d->HideDelay;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::setHideDelay(int delay)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
d->HideDelay = delay;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void ctkPopupWidget::onEffectFinished()
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
bool wasClosing = d->wasClosing();
|
||||
this->Superclass::onEffectFinished();
|
||||
if (wasClosing)
|
||||
{
|
||||
/// restore the AutoShow if needed.
|
||||
if (!this->property("AutoShowOnClose").isNull())
|
||||
{
|
||||
d->AutoShow = this->property("AutoShowOnClose").toBool();
|
||||
this->setProperty("AutoShowOnClose", QVariant());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkPopupWidget::leaveEvent(QEvent* event)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
|
||||
this->Superclass::leaveEvent(event);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkPopupWidget::enterEvent(QEvent* event)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
|
||||
this->Superclass::enterEvent(event);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
// Here we listen to PopupPixmapWidget, BaseWidget and ctkPopupWidget
|
||||
// children popups that were under the mouse
|
||||
switch(event->type())
|
||||
{
|
||||
case QEvent::Move:
|
||||
{
|
||||
if (obj != d->BaseWidget)
|
||||
{
|
||||
break;
|
||||
}
|
||||
QMoveEvent* moveEvent = dynamic_cast<QMoveEvent*>(event);
|
||||
QRect newBaseGeometry = d->baseGeometry();
|
||||
newBaseGeometry.moveTopLeft(d->mapToGlobal(moveEvent->pos()));
|
||||
QRect desiredGeometry = d->desiredOpenGeometry(newBaseGeometry);
|
||||
this->move(desiredGeometry.topLeft());
|
||||
//this->move(this->pos() + moveEvent->pos() - moveEvent->oldPos());
|
||||
this->update();
|
||||
break;
|
||||
}
|
||||
case QEvent::Hide:
|
||||
case QEvent::Close:
|
||||
// if the mouse was in a base widget child popup, then when we leave
|
||||
// the popup we want to check if it needs to be closed.
|
||||
if (obj != d->BaseWidget)
|
||||
{
|
||||
if (obj != d->PopupPixmapWidget &&
|
||||
qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
|
||||
{
|
||||
obj->removeEventFilter(this);
|
||||
QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
d->temporarilyHiddenOn();
|
||||
break;
|
||||
case QEvent::Show:
|
||||
if (obj != d->BaseWidget)
|
||||
{
|
||||
break;
|
||||
}
|
||||
this->setGeometry(d->desiredOpenGeometry());
|
||||
d->temporarilyHiddenOff();
|
||||
break;
|
||||
case QEvent::Enter:
|
||||
if ( d->currentAnimation()->state() == QAbstractAnimation::Stopped )
|
||||
{
|
||||
// Maybe the user moved the mouse on the widget by mistake, don't open
|
||||
// the popup instantly...
|
||||
QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// ... except if the popup is closing, we want to reopen it as sooon as
|
||||
// possible.
|
||||
this->updatePopup();
|
||||
}
|
||||
break;
|
||||
case QEvent::Leave:
|
||||
// Don't listen to base widget children that are popups as what
|
||||
// matters here is their close event instead
|
||||
if (obj != d->BaseWidget &&
|
||||
obj != d->PopupPixmapWidget &&
|
||||
qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// The mouse might have left the area that keeps the popup open
|
||||
QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
|
||||
if (obj != d->BaseWidget &&
|
||||
obj != d->PopupPixmapWidget)
|
||||
{
|
||||
obj->removeEventFilter(this);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this->QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkPopupWidget::updatePopup()
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
|
||||
// Querying mouseOver can be slow, don't do it if not needed.
|
||||
QWidget* mouseOver = (d->AutoShow || d->AutoHide) ? d->mouseOver() : 0;
|
||||
if ((d->AutoShow ||
|
||||
// Even if there is no AutoShow, we might still want to reopen the popup
|
||||
// when closing it inadvertently, except if we are un-pin-ing the popup
|
||||
(d->AutoHide && d->isClosing() && this->property("AutoShowOnClose").toBool())) &&
|
||||
// to be automatically open, the mouse has to be over a child widget
|
||||
mouseOver &&
|
||||
// disable opening the popup when the popup is disabled
|
||||
(d->BaseWidget.isNull() || d->BaseWidget->isEnabled()))
|
||||
{
|
||||
this->showPopup();
|
||||
}
|
||||
else if (d->AutoHide && !mouseOver)
|
||||
{
|
||||
this->hidePopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkPopupWidget::hidePopup()
|
||||
{
|
||||
// just in case it was set.
|
||||
this->setProperty("forcedClosed", 0);
|
||||
|
||||
this->Superclass::hidePopup();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
void ctkPopupWidget::pinPopup(bool pin)
|
||||
{
|
||||
Q_D(ctkPopupWidget);
|
||||
this->setAutoHide(!pin);
|
||||
if (pin)
|
||||
{
|
||||
this->showPopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
// When closing, we don't want to inadvertently re-open the menu.
|
||||
this->setProperty("AutoShowOnClose", this->autoShow());
|
||||
d->AutoShow = false;
|
||||
this->hidePopup();
|
||||
}
|
||||
}
|
||||
133
ctk/ctkPopupWidget.h
Normal file
133
ctk/ctkPopupWidget.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 __ctkPopupWidget_h
|
||||
#define __ctkPopupWidget_h
|
||||
|
||||
// CTK includes
|
||||
#include "ctkBasePopupWidget.h"
|
||||
|
||||
class ctkPopupWidgetPrivate;
|
||||
|
||||
/// \ingroup Widgets
|
||||
/// ctkPopupWidget is a specialization of ctkBasePopupWidget that handles
|
||||
/// the opening and closing of the popup.
|
||||
/// Below is an example of a popup slider that opens and closes next to a
|
||||
/// button
|
||||
/// \code
|
||||
/// ctkPopupWidget* popup = new ctkPopupWidget(pushButton);
|
||||
/// popup->setAlignment(Qt::AlignRight | Qt::AlignTop | Qt::AlignBottom);
|
||||
/// popup->setOrientation(Qt::Horizontal);
|
||||
/// QHBoxLayout* popupLayout = new QHBoxLayout(popup);
|
||||
/// QSlider* popupSlider = new QSlider(popup);
|
||||
/// popupLayout->addWidget(popupSlider);
|
||||
/// \endcode
|
||||
/// \sa ctkBasePopupWidget
|
||||
class CTK_WIDGETS_EXPORT ctkPopupWidget : public ctkBasePopupWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
/// Control whether the popup listens to the application and baseWidget
|
||||
/// events and decides if it needs to be permanently or temporarily hidden.
|
||||
/// You might want to setActive(false) when embedding the popup
|
||||
/// into a static layout intead of having it top-level (no parent).
|
||||
/// Consider also removing its windowFlags (Qt::ToolTip |
|
||||
/// Qt::FramelessWindowHint) and removing the baseWidget.
|
||||
/// True by default
|
||||
/// \sa isActive(), setActive()
|
||||
Q_PROPERTY( bool active READ isActive WRITE setActive)
|
||||
|
||||
/// Control wether the popup automatically opens when the mouse
|
||||
/// enter the widget. True by default
|
||||
/// \sa autoShow(), setAutoShow()
|
||||
Q_PROPERTY( bool autoShow READ autoShow WRITE setAutoShow)
|
||||
|
||||
/// Time in ms to wait before opening the popup if autoShow is set.
|
||||
/// 20ms by default
|
||||
/// \sa showDelay(), setShowDelay()
|
||||
Q_PROPERTY( int showDelay READ showDelay WRITE setShowDelay)
|
||||
|
||||
/// Control wether the popup automatically closes when the mouse
|
||||
/// leaves the widget. True by default.
|
||||
/// \sa autoHide(), setAutoHide()
|
||||
Q_PROPERTY( bool autoHide READ autoHide WRITE setAutoHide)
|
||||
|
||||
/// Time in ms to wait before closing the popup if autoHide is set.
|
||||
/// 200ms by default
|
||||
/// \sa hideDelay(), setHideDelay()
|
||||
Q_PROPERTY( int hideDelay READ hideDelay WRITE setHideDelay)
|
||||
|
||||
public:
|
||||
typedef ctkBasePopupWidget Superclass;
|
||||
/// By default, the parent is the \a baseWidget.
|
||||
/// \sa baseWidget()
|
||||
explicit ctkPopupWidget(QWidget* parent = 0);
|
||||
virtual ~ctkPopupWidget();
|
||||
|
||||
bool isActive()const;
|
||||
void setActive(bool);
|
||||
|
||||
bool autoShow()const;
|
||||
/// Calling setAutoShow automatically updates opens the popup if the cursor
|
||||
/// is above the popup or the base widget.
|
||||
void setAutoShow(bool);
|
||||
|
||||
int showDelay()const;
|
||||
void setShowDelay(int delay);
|
||||
|
||||
bool autoHide()const;
|
||||
/// Don't automatically close the popup when leaving the widget.
|
||||
/// Calling setAutoHide automatically updates the state close the popup
|
||||
/// if the mouse is not over the popup nor the base widget.
|
||||
void setAutoHide(bool autoHide);
|
||||
|
||||
int hideDelay()const;
|
||||
void setHideDelay(int delay);
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Convenient function that calls setAutoHide(!pin) and opens the popup
|
||||
/// if pin is true regardless of the value of \a AutoShow.
|
||||
/// It is typically connected with a checkable button to anchor the popup.
|
||||
void pinPopup(bool pin);
|
||||
|
||||
public:
|
||||
/// Reimplemented for internal reasons
|
||||
virtual void hidePopup();
|
||||
|
||||
protected:
|
||||
virtual void leaveEvent(QEvent* event);
|
||||
virtual void enterEvent(QEvent* event);
|
||||
virtual bool eventFilter(QObject* obj, QEvent* event);
|
||||
|
||||
/// Widget the popup is attached to. It opens right under \a baseWidget
|
||||
/// and if the ctkPopupWidget sizepolicy contains the growFlag/shrinkFlag,
|
||||
/// it tries to resize itself to fit the same width of \a baseWidget.
|
||||
virtual void setBaseWidget(QWidget* baseWidget);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void updatePopup();
|
||||
virtual void onEffectFinished();
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(ctkPopupWidget);
|
||||
Q_DISABLE_COPY(ctkPopupWidget);
|
||||
};
|
||||
|
||||
#endif
|
||||
67
ctk/ctkPopupWidget_p.h
Normal file
67
ctk/ctkPopupWidget_p.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 __ctkPopupWidget_p_h
|
||||
#define __ctkPopupWidget_p_h
|
||||
|
||||
// CTK includes
|
||||
#include "ctkBasePopupWidget_p.h"
|
||||
#include "ctkPopupWidget.h"
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/// \ingroup Widgets
|
||||
class CTK_WIDGETS_EXPORT ctkPopupWidgetPrivate
|
||||
: public ctkBasePopupWidgetPrivate
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PUBLIC(ctkPopupWidget);
|
||||
public:
|
||||
typedef ctkBasePopupWidgetPrivate Superclass;
|
||||
ctkPopupWidgetPrivate(ctkPopupWidget& object);
|
||||
~ctkPopupWidgetPrivate();
|
||||
|
||||
virtual void init();
|
||||
|
||||
// Return the widget if the mouse cursor is above any of the focus widgets or their
|
||||
// children.
|
||||
// If the cursor is above a child widget, install the event filter to listen
|
||||
// when the cursor leaves the widget.
|
||||
virtual QWidget* mouseOver();
|
||||
|
||||
virtual bool eventFilter(QObject* obj, QEvent* event);
|
||||
|
||||
void temporarilyHiddenOn();
|
||||
void temporarilyHiddenOff();
|
||||
|
||||
bool isHidingCandidate(QWidget* widget)const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateVisibility();
|
||||
void onApplicationDeactivate();
|
||||
|
||||
protected:
|
||||
bool Active;
|
||||
bool AutoShow;
|
||||
int ShowDelay;
|
||||
bool AutoHide;
|
||||
int HideDelay;
|
||||
};
|
||||
|
||||
#endif
|
||||
6
ctk/ctkWidgetsExport.h
Normal file
6
ctk/ctkWidgetsExport.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef CTKWIDGETSEXPORT_H
|
||||
#define CTKWIDGETSEXPORT_H
|
||||
|
||||
#define CTK_WIDGETS_EXPORT
|
||||
|
||||
#endif // CTKWIDGETSEXPORT_H
|
||||
115
ctk/ctkWidgetsUtils.cpp
Normal file
115
ctk/ctkWidgetsUtils.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 <QBuffer>
|
||||
#include <QImage>
|
||||
//#include <QGLWidget>
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
|
||||
// ctkWidgets includes
|
||||
#include "ctkWidgetsUtils.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
QString ctk::base64HTMLImageTagSrc(const QImage& image)
|
||||
{
|
||||
QByteArray byteArray;
|
||||
QBuffer buffer(&byteArray);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, "PNG");
|
||||
return QString("data:image/png;base64,%1")
|
||||
.arg(QString(buffer.data().toBase64()));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//QImage ctk::grabWidget(QWidget* widget, QRect rectangle)
|
||||
//{
|
||||
// if (!widget)
|
||||
// {
|
||||
// return QImage();
|
||||
// }
|
||||
// if (!rectangle.isValid())
|
||||
// {
|
||||
// // Let Qt trigger layout mechanism and compute widget size.
|
||||
// rectangle = QRect(0,0,-1,-1);
|
||||
// }
|
||||
//#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
// QPixmap widgetPixmap = QPixmap::grabWidget(widget, rectangle);
|
||||
//#else
|
||||
// QPixmap widgetPixmap = widget->grab(rectangle);
|
||||
//#endif
|
||||
// QImage widgetImage = widgetPixmap.toImage();
|
||||
// QPainter painter;
|
||||
// painter.begin(&widgetImage);
|
||||
// foreach(QGLWidget* glWidget, widget->findChildren<QGLWidget*>())
|
||||
// {
|
||||
// if (!glWidget->isVisible())
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// QRect subWidgetRect = QRect(glWidget->mapTo(widget, QPoint(0,0)), glWidget->size());
|
||||
// if (!rectangle.intersects(subWidgetRect))
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// QImage subImage = glWidget->grabFrameBuffer();
|
||||
// painter.drawImage(subWidgetRect, subImage);
|
||||
// }
|
||||
// painter.end();
|
||||
// return widgetImage;
|
||||
//}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
QImage ctk::kwIconToQImage(const unsigned char *data, int width, int height, int pixelSize, unsigned int bufferLength, int options)
|
||||
{
|
||||
Q_UNUSED(options); // not yet supported
|
||||
|
||||
QByteArray imageData = QByteArray::fromRawData(
|
||||
reinterpret_cast<const char *>(data), bufferLength);
|
||||
unsigned int expectedLength = width * height * pixelSize;
|
||||
// Start with a zlib header ? If not, then it must be base64 encoded
|
||||
if (bufferLength != expectedLength &&
|
||||
static_cast<unsigned char>(imageData[0]) != 0x78 &&
|
||||
static_cast<unsigned char>(imageData[1]) != 0xDA)
|
||||
{
|
||||
imageData = QByteArray::fromBase64(imageData);
|
||||
bufferLength = imageData.size();
|
||||
}
|
||||
|
||||
if (bufferLength != expectedLength &&
|
||||
static_cast<unsigned char>(imageData[0]) == 0x78 &&
|
||||
static_cast<unsigned char>(imageData[1]) == 0xDA)
|
||||
{
|
||||
imageData.prepend(static_cast<char>((expectedLength >> 0) & 0xFF));
|
||||
imageData.prepend(static_cast<char>((expectedLength >> 8) & 0xFF));
|
||||
imageData.prepend(static_cast<char>((expectedLength >> 16) & 0xFF));
|
||||
imageData.prepend(static_cast<char>((expectedLength >> 24) & 0xFF));
|
||||
imageData = qUncompress(imageData);
|
||||
}
|
||||
QImage image(reinterpret_cast<unsigned char*>(imageData.data()),
|
||||
width, height, width * pixelSize,
|
||||
pixelSize == 4 ? QImage::Format_ARGB32 : QImage::Format_RGB888);
|
||||
if (pixelSize == 4)
|
||||
{
|
||||
return image.rgbSwapped();
|
||||
}
|
||||
return image.copy();
|
||||
}
|
||||
53
ctk/ctkWidgetsUtils.h
Normal file
53
ctk/ctkWidgetsUtils.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*=========================================================================
|
||||
|
||||
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 __ctkWidgetsUtils_h
|
||||
#define __ctkWidgetsUtils_h
|
||||
|
||||
// Qt includes
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
class QImage;
|
||||
|
||||
#include "ctkWidgetsExport.h"
|
||||
|
||||
namespace ctk {
|
||||
///
|
||||
/// \ingroup Widgets
|
||||
/// Create a base 64 image tag. Can be used that way:
|
||||
/// QString("<img src=\"%1\">").arg(base64HTMLImageTagSrc(myImage);
|
||||
QString CTK_WIDGETS_EXPORT base64HTMLImageTagSrc(const QImage& image);
|
||||
|
||||
///
|
||||
/// \ingroup Widgets
|
||||
/// Grab the contents of a QWidget and all its children.
|
||||
/// Handle correctly the case of QGLWidgets.
|
||||
/// \sa QWidget::grab
|
||||
QImage CTK_WIDGETS_EXPORT grabWidget(QWidget* widget, QRect rectangle = QRect());
|
||||
|
||||
///
|
||||
/// \ingroup Widgets
|
||||
/// Convert an KWidget encoded image into a QImage
|
||||
/// The data can be base64 encoded and/or zlib compressed.
|
||||
QImage CTK_WIDGETS_EXPORT kwIconToQImage(const unsigned char *data, int width, int height, int pixelSize, unsigned int bufferLength, int options = 0);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue