2019-01-07 23:21:06 -05:00
|
|
|
/****************************************
|
|
|
|
*
|
|
|
|
* INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
|
|
|
|
* Copyright (C) 2019 Victor Tran
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* *************************************/
|
|
|
|
#include "tpopover.h"
|
|
|
|
|
|
|
|
#include <QFrame>
|
|
|
|
#include <QGraphicsOpacityEffect>
|
2020-10-19 07:33:56 -04:00
|
|
|
#include <QGraphicsBlurEffect>
|
|
|
|
#include <QPainter>
|
2020-10-21 10:42:42 -04:00
|
|
|
#include <QTimer>
|
2020-12-21 02:47:14 -05:00
|
|
|
#include "tscrim.h"
|
2019-01-07 23:21:06 -05:00
|
|
|
#include "tpropertyanimation.h"
|
2020-07-29 04:37:35 -04:00
|
|
|
#include "tcsdtools.h"
|
2019-01-07 23:21:06 -05:00
|
|
|
|
2019-03-04 06:10:52 -05:00
|
|
|
#ifdef Q_OS_MAC
|
2020-05-28 10:33:51 -04:00
|
|
|
#include <QDialog>
|
|
|
|
#include <QBoxLayout>
|
2019-03-04 06:10:52 -05:00
|
|
|
#endif
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
struct tPopoverPrivate {
|
|
|
|
QWidget* popoverWidget;
|
|
|
|
QWidget* parentWidget;
|
|
|
|
QFrame* verticalSeperator;
|
|
|
|
|
2019-03-04 06:10:52 -05:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
QDialog* wrapperDialog;
|
|
|
|
#endif
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
tPopover::PopoverSide side = tPopover::Trailing;
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
int width = -1;
|
|
|
|
bool showing = false;
|
2019-01-29 10:27:16 -05:00
|
|
|
bool performBlanking = true;
|
2019-03-06 05:02:59 -05:00
|
|
|
bool dismissable = true;
|
2019-01-29 03:43:56 -05:00
|
|
|
|
2020-10-19 07:33:56 -04:00
|
|
|
const int blankerOverscan = 100;
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
static QMap<QWidget*, tPopover*> activePopovers;
|
2019-01-07 23:21:06 -05:00
|
|
|
};
|
2019-01-29 03:43:56 -05:00
|
|
|
QMap<QWidget*, tPopover*> tPopoverPrivate::activePopovers = QMap<QWidget*, tPopover*>();
|
2019-01-07 23:21:06 -05:00
|
|
|
|
2020-05-28 10:33:51 -04:00
|
|
|
tPopover::tPopover(QWidget* popoverWidget, QObject* parent) : QObject(parent) {
|
2019-01-07 23:21:06 -05:00
|
|
|
d = new tPopoverPrivate();
|
|
|
|
d->popoverWidget = popoverWidget;
|
|
|
|
popoverWidget->setAutoFillBackground(true);
|
|
|
|
|
|
|
|
d->verticalSeperator = new QFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
tPopover::~tPopover() {
|
2019-01-29 03:43:56 -05:00
|
|
|
tPopoverPrivate::activePopovers.remove(d->popoverWidget);
|
2019-01-07 23:21:06 -05:00
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
bool tPopover::isOpeningOnRight() {
|
|
|
|
return (QApplication::layoutDirection() == Qt::LeftToRight && d->side == Trailing) ||
|
2020-05-28 10:33:51 -04:00
|
|
|
(QApplication::layoutDirection() == Qt::RightToLeft && d->side == Leading);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tPopover::updateGeometry() {
|
|
|
|
if (d->width == -1) {
|
|
|
|
d->popoverWidget->resize(d->parentWidget->width(), d->parentWidget->height());
|
|
|
|
} else if (d->width < 0) {
|
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->popoverWidget->setGeometry(0, -d->width, d->parentWidget->width(), d->parentWidget->height() + d->width);
|
|
|
|
} else if (isOpeningOnRight()) {
|
|
|
|
d->popoverWidget->setGeometry(-d->width, 0, d->parentWidget->width() + d->width, d->parentWidget->height());
|
|
|
|
} else {
|
|
|
|
d->popoverWidget->setGeometry(0, 0, d->parentWidget->width() - d->width, d->parentWidget->height());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->popoverWidget->setGeometry(0, d->parentWidget->height() - d->width, d->parentWidget->width(), d->width);
|
|
|
|
} else if (isOpeningOnRight()) {
|
|
|
|
d->popoverWidget->setGeometry(d->parentWidget->width() - d->width, 0, d->width, d->parentWidget->height());
|
|
|
|
} else {
|
|
|
|
d->popoverWidget->setGeometry(0, 0, d->width, d->parentWidget->height());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->verticalSeperator->setGeometry(0, d->popoverWidget->y() - 1, d->parentWidget->width(), 1);
|
|
|
|
} else if (isOpeningOnRight()) {
|
|
|
|
d->verticalSeperator->setGeometry(d->popoverWidget->x() - 1, 0, 1, d->parentWidget->height());
|
|
|
|
} else {
|
|
|
|
d->verticalSeperator->setGeometry(d->popoverWidget->geometry().right() + 1, 0, 1, d->parentWidget->height());
|
|
|
|
}
|
2019-01-29 03:43:56 -05:00
|
|
|
}
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
void tPopover::setPopoverWidth(int width) {
|
2020-05-28 10:33:51 -04:00
|
|
|
if (d->showing) {
|
|
|
|
int endValue;
|
|
|
|
|
|
|
|
if (width >= 0) {
|
|
|
|
//Normal width
|
|
|
|
endValue = width;
|
|
|
|
} else if (width == -1) {
|
|
|
|
//Full Screen
|
|
|
|
endValue = d->parentWidget->width();
|
|
|
|
} else {
|
|
|
|
//Based on parent width
|
|
|
|
//Add because width will be negative
|
|
|
|
endValue = d->parentWidget->width() + width;
|
|
|
|
}
|
|
|
|
|
|
|
|
tVariantAnimation* animation = new tVariantAnimation(this);
|
|
|
|
animation->setStartValue(d->popoverWidget->width());
|
|
|
|
animation->setEndValue(endValue);
|
|
|
|
animation->setDuration(250);
|
|
|
|
animation->setEasingCurve(QEasingCurve::OutCubic);
|
|
|
|
connect(animation, &tVariantAnimation::valueChanged, this, [ = ](QVariant value) {
|
|
|
|
d->width = value.toInt();
|
|
|
|
this->updateGeometry();
|
|
|
|
});
|
|
|
|
connect(animation, &tVariantAnimation::finished, this, [ = ] {
|
|
|
|
d->width = width;
|
|
|
|
this->updateGeometry();
|
|
|
|
animation->deleteLater();
|
|
|
|
});
|
|
|
|
animation->start();
|
|
|
|
} else {
|
|
|
|
d->width = width;
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
}
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
void tPopover::setPopoverSide(PopoverSide side) {
|
|
|
|
d->side = side;
|
|
|
|
}
|
|
|
|
|
2019-01-29 10:27:16 -05:00
|
|
|
void tPopover::setPerformBlanking(bool performBlanking) {
|
|
|
|
d->performBlanking = performBlanking;
|
|
|
|
}
|
|
|
|
|
2019-03-06 05:02:59 -05:00
|
|
|
void tPopover::setDismissable(bool dismissable) {
|
|
|
|
d->dismissable = dismissable;
|
|
|
|
}
|
|
|
|
|
2021-04-28 01:16:09 -04:00
|
|
|
void tPopover::setPerformBlur(bool performBlur) {
|
|
|
|
tScrim::scrimForWidget(d->parentWidget)->setBlurEnabled(performBlur);
|
|
|
|
}
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
void tPopover::show(QWidget* parent) {
|
2020-07-29 04:37:35 -04:00
|
|
|
parent = tCsdTools::widgetForPopover(parent);
|
2019-01-07 23:21:06 -05:00
|
|
|
if (d->showing) return;
|
2019-01-29 03:43:56 -05:00
|
|
|
tPopoverPrivate::activePopovers.insert(d->popoverWidget, this);
|
2019-01-07 23:21:06 -05:00
|
|
|
|
2019-03-04 06:10:52 -05:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
//Just show the popover as a sheet
|
|
|
|
|
|
|
|
d->wrapperDialog = new QDialog(parent);
|
|
|
|
d->wrapperDialog->resize(d->popoverWidget->size());
|
|
|
|
|
|
|
|
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
|
|
|
|
l->setMargin(0);
|
|
|
|
l->addWidget(d->popoverWidget);
|
|
|
|
d->wrapperDialog->setLayout(l);
|
|
|
|
|
|
|
|
d->wrapperDialog->setWindowModality(Qt::ApplicationModal); //Keyboard focus issues if this is window modal
|
|
|
|
d->wrapperDialog->setWindowFlag(Qt::Sheet);
|
|
|
|
d->wrapperDialog->open();
|
|
|
|
#else
|
|
|
|
d->popoverWidget->setWindowFlags(Qt::Widget);
|
2019-01-07 23:21:06 -05:00
|
|
|
d->parentWidget = parent;
|
|
|
|
d->parentWidget->installEventFilter(this);
|
|
|
|
|
2019-01-15 23:00:10 -05:00
|
|
|
d->verticalSeperator->hide();
|
|
|
|
d->popoverWidget->hide();
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
d->popoverWidget->setParent(parent);
|
|
|
|
d->verticalSeperator->setParent(parent);
|
|
|
|
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->verticalSeperator->resize(parent->width(), 1);
|
|
|
|
d->verticalSeperator->setFrameShape(QFrame::HLine);
|
|
|
|
} else {
|
|
|
|
d->verticalSeperator->resize(1, parent->height());
|
|
|
|
d->verticalSeperator->setFrameShape(QFrame::VLine);
|
|
|
|
}
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
if (d->width == -1) {
|
|
|
|
d->popoverWidget->resize(parent->width(), parent->height());
|
|
|
|
} else {
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
if (d->width < 0) {
|
|
|
|
d->popoverWidget->resize(parent->width(), parent->height() + d->width);
|
|
|
|
} else {
|
|
|
|
d->popoverWidget->resize(parent->width(), d->width);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (d->width < 0) {
|
|
|
|
d->popoverWidget->resize(parent->width() + d->width, parent->height());
|
|
|
|
} else {
|
|
|
|
d->popoverWidget->resize(d->width, parent->height());
|
|
|
|
}
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
}
|
|
|
|
|
2019-01-29 10:27:16 -05:00
|
|
|
if (d->performBlanking) {
|
2020-12-21 02:47:14 -05:00
|
|
|
tScrim* scrim = tScrim::scrimForWidget(parent);
|
|
|
|
if (d->dismissable) connect(scrim, &tScrim::scrimClicked, this, [ = ] {
|
|
|
|
this->dismiss();
|
2020-10-19 07:33:56 -04:00
|
|
|
});
|
2020-12-21 02:47:14 -05:00
|
|
|
scrim->show();
|
2019-01-29 10:27:16 -05:00
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
|
|
|
|
tVariantAnimation* popoverAnim = new tVariantAnimation();
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
popoverAnim->setStartValue(parent->height());
|
|
|
|
popoverAnim->setEndValue(parent->height() - d->popoverWidget->height());
|
|
|
|
} else if (isOpeningOnRight()) {
|
2019-01-29 03:43:56 -05:00
|
|
|
popoverAnim->setStartValue(parent->width());
|
|
|
|
popoverAnim->setEndValue(parent->width() - d->popoverWidget->width());
|
|
|
|
} else {
|
|
|
|
popoverAnim->setStartValue(-d->popoverWidget->width());
|
|
|
|
popoverAnim->setEndValue(0);
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
popoverAnim->setDuration(250);
|
|
|
|
popoverAnim->setEasingCurve(QEasingCurve::OutCubic);
|
2020-05-28 10:33:51 -04:00
|
|
|
connect(popoverAnim, &tVariantAnimation::valueChanged, [ = ](QVariant value) {
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->popoverWidget->move(0, value.toInt());
|
|
|
|
d->verticalSeperator->move(0, value.toInt() - 1);
|
|
|
|
} else if (isOpeningOnRight()) {
|
|
|
|
d->popoverWidget->move(value.toInt(), 0);
|
2019-01-29 03:43:56 -05:00
|
|
|
d->verticalSeperator->move(value.toInt() - 1, 0);
|
|
|
|
} else {
|
2019-07-26 04:00:40 -04:00
|
|
|
d->popoverWidget->move(value.toInt(), 0);
|
2019-01-29 03:43:56 -05:00
|
|
|
d->verticalSeperator->move(value.toInt() + d->popoverWidget->width(), 0);
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
});
|
|
|
|
connect(popoverAnim, &tVariantAnimation::finished, popoverAnim, &tVariantAnimation::deleteLater);
|
|
|
|
popoverAnim->start();
|
2020-10-19 07:33:56 -04:00
|
|
|
popoverAnim->valueChanged(popoverAnim->startValue());
|
2019-01-07 23:21:06 -05:00
|
|
|
|
|
|
|
d->verticalSeperator->show();
|
|
|
|
d->popoverWidget->show();
|
|
|
|
|
|
|
|
d->verticalSeperator->raise();
|
|
|
|
d->popoverWidget->raise();
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
d->popoverWidget->setFocus();
|
2019-03-04 06:10:52 -05:00
|
|
|
#endif
|
2019-01-29 03:43:56 -05:00
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
d->showing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tPopover::dismiss() {
|
|
|
|
if (!d->showing) return;
|
|
|
|
|
2019-03-04 06:10:52 -05:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
//d->popoverWidget->hide();
|
|
|
|
d->wrapperDialog->close();
|
|
|
|
d->wrapperDialog->deleteLater();
|
|
|
|
d->showing = false;
|
|
|
|
emit dismissed();
|
|
|
|
#else
|
2019-01-29 10:27:16 -05:00
|
|
|
if (d->performBlanking) {
|
2020-12-21 02:47:14 -05:00
|
|
|
tScrim* scrim = tScrim::scrimForWidget(d->parentWidget);
|
|
|
|
scrim->disconnect(this);
|
|
|
|
scrim->hide();
|
2019-01-29 10:27:16 -05:00
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
|
|
|
|
tVariantAnimation* popoverAnim = new tVariantAnimation();
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
//Opening on the bottom
|
|
|
|
popoverAnim->setStartValue(d->popoverWidget->y());
|
|
|
|
popoverAnim->setEndValue(d->parentWidget->height());
|
|
|
|
} else if (isOpeningOnRight()) {
|
2019-01-29 03:43:56 -05:00
|
|
|
//Opening on the right
|
2019-07-26 04:00:40 -04:00
|
|
|
popoverAnim->setStartValue(d->popoverWidget->x());
|
2019-01-29 03:43:56 -05:00
|
|
|
popoverAnim->setEndValue(d->parentWidget->width());
|
|
|
|
} else {
|
2019-07-26 04:00:40 -04:00
|
|
|
//Opening on the left
|
|
|
|
popoverAnim->setStartValue(d->popoverWidget->x());
|
2019-01-29 03:43:56 -05:00
|
|
|
popoverAnim->setEndValue(-d->parentWidget->width());
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
popoverAnim->setDuration(250);
|
|
|
|
popoverAnim->setEasingCurve(QEasingCurve::OutCubic);
|
2020-05-28 10:33:51 -04:00
|
|
|
connect(popoverAnim, &tVariantAnimation::valueChanged, [ = ](QVariant value) {
|
2019-07-26 04:00:40 -04:00
|
|
|
if (d->side == Bottom) {
|
|
|
|
d->popoverWidget->move(0, value.toInt());
|
|
|
|
d->verticalSeperator->move(0, value.toInt() - 1);
|
|
|
|
} else if (isOpeningOnRight()) {
|
|
|
|
d->popoverWidget->move(value.toInt(), 0);
|
2019-01-29 03:43:56 -05:00
|
|
|
d->verticalSeperator->move(value.toInt() - 1, 0);
|
|
|
|
} else {
|
2019-07-26 04:00:40 -04:00
|
|
|
d->popoverWidget->move(value.toInt(), 0);
|
2019-01-29 03:43:56 -05:00
|
|
|
d->verticalSeperator->move(value.toInt() + d->popoverWidget->width(), 0);
|
|
|
|
}
|
2019-01-07 23:21:06 -05:00
|
|
|
});
|
2020-05-28 10:33:51 -04:00
|
|
|
connect(popoverAnim, &tVariantAnimation::finished, [ = ] {
|
2019-01-07 23:21:06 -05:00
|
|
|
d->popoverWidget->hide();
|
|
|
|
d->verticalSeperator->hide();
|
|
|
|
|
|
|
|
d->parentWidget->removeEventFilter(this);
|
|
|
|
d->parentWidget = nullptr;
|
|
|
|
|
2019-01-29 03:43:56 -05:00
|
|
|
tPopoverPrivate::activePopovers.remove(d->popoverWidget);
|
|
|
|
|
2019-01-07 23:21:06 -05:00
|
|
|
d->showing = false;
|
|
|
|
emit dismissed();
|
|
|
|
});
|
|
|
|
popoverAnim->start();
|
2019-03-04 06:10:52 -05:00
|
|
|
#endif
|
2019-01-07 23:21:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool tPopover::eventFilter(QObject* watched, QEvent* event) {
|
|
|
|
if (watched == d->parentWidget) {
|
|
|
|
if (event->type() == QEvent::Resize) {
|
|
|
|
if (d->showing) {
|
2020-05-28 10:33:51 -04:00
|
|
|
updateGeometry();
|
2019-01-07 23:21:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-29 03:43:56 -05:00
|
|
|
|
2020-05-28 10:33:51 -04:00
|
|
|
tPopover* tPopover::popoverForWidget(QWidget* popoverWidget) {
|
2019-01-29 03:43:56 -05:00
|
|
|
return tPopoverPrivate::activePopovers.value(popoverWidget, nullptr);
|
|
|
|
}
|
2020-05-28 10:33:51 -04:00
|
|
|
|
|
|
|
tPopover* tPopover::popoverForPopoverWidget(QWidget* popoverWidget) {
|
|
|
|
QWidget* checkWidget = popoverWidget;
|
|
|
|
while (checkWidget) {
|
|
|
|
for (tPopover* popover : tPopoverPrivate::activePopovers.values()) {
|
|
|
|
if (popover->d->popoverWidget == checkWidget) return popover;
|
|
|
|
}
|
|
|
|
checkWidget = checkWidget->parentWidget();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|