tdesktop/Telegram/SourceFiles/ui/widgets/tooltip.cpp

213 lines
6.2 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop 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.
It 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.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/tooltip.h"
#include "application.h"
#include "styles/style_widgets.h"
namespace Ui {
Tooltip *TooltipInstance = nullptr;
const style::Tooltip *AbstractTooltipShower::tooltipSt() const {
return &st::defaultTooltip;
}
AbstractTooltipShower::~AbstractTooltipShower() {
if (TooltipInstance && TooltipInstance->_shower == this) {
TooltipInstance->_shower = 0;
}
}
Tooltip::Tooltip() : TWidget(nullptr) {
TooltipInstance = this;
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
void Tooltip::onShow() {
if (_shower) {
auto text = QString();
if (auto window = App::wnd()) {
window->updateIsActive(0);
if (window->isActive()) {
text = _shower->tooltipText();
}
}
if (text.isEmpty()) {
Hide();
} else {
TooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
}
}
}
void Tooltip::onWndActiveChanged() {
if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) {
Tooltip::Hide();
}
}
bool Tooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10);
} else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop();
} else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide();
}
}
return TWidget::eventFilter(o, e);
}
void Tooltip::onHideByLeave() {
Hide();
}
Tooltip::~Tooltip() {
if (TooltipInstance == this) {
TooltipInstance = 0;
}
}
void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
Sandbox::installEventFilter(this);
}
_point = m;
_st = st;
_text = Text(_st->textStyle, text, _textPlainOptions, _st->widthMax, true);
_useTransparency = Platform::TransparentWindowsSupported(_point);
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();
// count tooltip size
QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
if (s.width() > _st->widthMax) {
s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
s.setHeight(addh + _text.countHeight(s.width() - addw));
}
int32 maxh = addh + (_st->linesMax * _st->textStyle.font->height);
if (s.height() > maxh) {
s.setHeight(maxh);
}
// count tooltip position
QPoint p(m + _st->shift);
if (rtl()) {
p.setX(m.x() - s.width() - _st->shift.x());
}
if (s.width() < 2 * _st->shift.x()) {
p.setX(m.x() - (s.width() / 2));
}
// adjust tooltip position
QRect r(QApplication::desktop()->screenGeometry(m));
if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
}
if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
}
if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
p.setY(m.y() - s.height() - _st->skip);
}
if (r.y() > p.x()) {
p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
}
setGeometry(QRect(p, s));
_hideByLeaveTimer.stop();
show();
}
void Tooltip::paintEvent(QPaintEvent *e) {
Painter p(this);
#ifdef OS_MAC_OLD
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(e->rect(), Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD
if (_useTransparency) {
p.setPen(_st->textBorder);
p.setBrush(_st->textBg);
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(QRectF(0.5, 0.5, width() - 1., height() - 1.), st::buttonRadius, st::buttonRadius);
} else {
p.fillRect(rect(), _st->textBg);
p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
}
int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textStyle.font->height);
p.setPen(_st->textFg);
_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}
void Tooltip::hideEvent(QHideEvent *e) {
if (TooltipInstance == this) {
Hide();
}
}
void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
if (!TooltipInstance) {
new Tooltip();
}
TooltipInstance->_shower = shower;
if (delay >= 0) {
TooltipInstance->_showTimer.start(delay);
} else {
TooltipInstance->onShow();
}
}
void Tooltip::Hide() {
if (auto instance = TooltipInstance) {
TooltipInstance = nullptr;
instance->_showTimer.stop();
instance->_hideByLeaveTimer.stop();
instance->hide();
instance->deleteLater();
}
}
} // namespace Ui