the-libs/lib/tpopover.cpp

348 lines
12 KiB
C++
Raw Permalink Normal View History

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
#ifdef Q_OS_MAC
2020-05-28 10:33:51 -04:00
#include <QDialog>
#include <QBoxLayout>
#endif
2019-01-07 23:21:06 -05:00
struct tPopoverPrivate {
QWidget* popoverWidget;
QWidget* parentWidget;
QFrame* verticalSeperator;
#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
#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();
#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;
#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();
#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;
}