/* 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); 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