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
|
||||||
|
|
@ -4,9 +4,11 @@ DEFINES += BOOST_ENABLE_ASSERT_HANDLER
|
||||||
|
|
||||||
|
|
||||||
SUBDIRS += core \
|
SUBDIRS += core \
|
||||||
|
ctk \
|
||||||
pgsql \
|
pgsql \
|
||||||
pglab \
|
pglablib \
|
||||||
pglablib
|
pglab
|
||||||
|
|
||||||
|
|
||||||
CONFIG(debug, debug|release) {
|
CONFIG(debug, debug|release) {
|
||||||
SUBDIRS += tests
|
SUBDIRS += tests
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue