File indexing completed on 2024-05-12 04:58:24

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2014-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "tabicon.h"
0019 #include "webtab.h"
0020 #include "webpage.h"
0021 #include "iconprovider.h"
0022 #include "tabbedwebview.h"
0023 
0024 #include <QTimer>
0025 #include <QToolTip>
0026 #include <QMouseEvent>
0027 #include <QPainter>
0028 
0029 TabIcon::TabIcon(QWidget* parent)
0030     : QWidget(parent)
0031     , m_tab(nullptr)
0032     , m_currentFrame(0)
0033     , m_animationRunning(false)
0034     , m_audioIconDisplayed(false)
0035 {
0036     setObjectName(QSL("tab-icon"));
0037 
0038     m_updateTimer = new QTimer(this);
0039     m_updateTimer->setInterval(data()->animationInterval);
0040     connect(m_updateTimer, &QTimer::timeout, this, &TabIcon::updateAnimationFrame);
0041 
0042     m_hideTimer = new QTimer(this);
0043     m_hideTimer->setInterval(250);
0044     connect(m_hideTimer, &QTimer::timeout, this, &TabIcon::hide);
0045 
0046     resize(16, 16);
0047 }
0048 
0049 void TabIcon::setWebTab(WebTab* tab)
0050 {
0051     m_tab = tab;
0052 
0053     connect(m_tab->webView(), &QWebEngineView::loadStarted, this, &TabIcon::showLoadingAnimation);
0054     connect(m_tab->webView(), &QWebEngineView::loadFinished, this, &TabIcon::hideLoadingAnimation);
0055     connect(m_tab->webView(), &WebView::iconChanged, this, &TabIcon::updateIcon);
0056     connect(m_tab->webView(), &WebView::backgroundActivityChanged, this, [this]() { update(); });
0057 
0058     auto pageChanged = [this](WebPage *page) {
0059         connect(page, &QWebEnginePage::recentlyAudibleChanged, this, &TabIcon::updateAudioIcon);
0060     };
0061     pageChanged(m_tab->webView()->page());
0062     connect(m_tab->webView(), &WebView::pageChanged, this, pageChanged);
0063 
0064     updateIcon();
0065 }
0066 
0067 void TabIcon::showLoadingAnimation()
0068 {
0069     m_currentFrame = 0;
0070 
0071     updateAnimationFrame();
0072     show();
0073 }
0074 
0075 void TabIcon::hideLoadingAnimation()
0076 {
0077     m_animationRunning = false;
0078 
0079     m_updateTimer->stop();
0080     updateIcon();
0081 }
0082 
0083 void TabIcon::updateIcon()
0084 {
0085     m_sitePixmap = m_tab->icon(/*allowNull*/ true).pixmap(16);
0086     if (m_sitePixmap.isNull()) {
0087         if (m_tab->url().isEmpty() || m_tab->url().scheme() == QL1S("falkon")) {
0088             hide();
0089         } else {
0090             m_hideTimer->start();
0091         }
0092     } else {
0093         show();
0094     }
0095     update();
0096 }
0097 
0098 // static
0099 TabIcon::Data *TabIcon::data()
0100 {
0101     static Data *data = nullptr;
0102     if (!data) {
0103         data = new TabIcon::Data;
0104         data->animationInterval = 70;
0105         data->animationPixmap = QIcon(QSL(":icons/other/loading.png")).pixmap(288, 16);
0106         data->framesCount = data->animationPixmap.width() / data->animationPixmap.height();
0107         data->audioPlayingPixmap = QIcon::fromTheme(QSL("audio-volume-high"), QIcon(QSL(":icons/other/audioplaying.svg"))).pixmap(16);
0108         data->audioMutedPixmap = QIcon::fromTheme(QSL("audio-volume-muted"), QIcon(QSL(":icons/other/audiomuted.svg"))).pixmap(16);
0109     }
0110     return data;
0111 }
0112 
0113 void TabIcon::updateAnimationFrame()
0114 {
0115     if (!m_animationRunning) {
0116         m_updateTimer->start();
0117         m_animationRunning = true;
0118     }
0119 
0120     update();
0121     m_currentFrame = (m_currentFrame + 1) % data()->framesCount;
0122 }
0123 
0124 void TabIcon::show()
0125 {
0126     if (!shouldBeVisible()) {
0127         return;
0128     }
0129 
0130     m_hideTimer->stop();
0131 
0132     if (isVisible() && width() == 16) {
0133         return;
0134     }
0135 
0136     setFixedSize(16, qMax(minimumHeight(), 16));
0137     Q_EMIT resized();
0138     QWidget::show();
0139 }
0140 
0141 void TabIcon::hide()
0142 {
0143     if (shouldBeVisible()) {
0144         return;
0145     }
0146 
0147     if (isHidden() && width() == 1) {
0148         return;
0149     }
0150 
0151     setFixedSize(1, qMax(minimumHeight(), 16));
0152     Q_EMIT resized();
0153     QWidget::hide();
0154 }
0155 
0156 bool TabIcon::shouldBeVisible() const
0157 {
0158     if (m_tab && m_tab->isPinned()) {
0159         return true;
0160     }
0161     return !m_sitePixmap.isNull() || m_animationRunning || m_audioIconDisplayed || (m_tab && m_tab->isPinned());
0162 }
0163 
0164 bool TabIcon::event(QEvent *event)
0165 {
0166     if (event->type() == QEvent::ToolTip) {
0167         auto *e = static_cast<QHelpEvent*>(event);
0168         if (m_audioIconDisplayed && m_audioIconRect.contains(e->pos())) {
0169             QToolTip::showText(e->globalPos(), m_tab->isMuted() ? tr("Unmute Tab") : tr("Mute Tab"), this);
0170             event->accept();
0171             return true;
0172         }
0173     }
0174 
0175     return QWidget::event(event);
0176 }
0177 
0178 void TabIcon::updateAudioIcon(bool recentlyAudible)
0179 {
0180     if (m_tab->isMuted() || (!m_tab->isMuted() && recentlyAudible)) {
0181         m_audioIconDisplayed = true;
0182         show();
0183     } else {
0184         m_audioIconDisplayed = false;
0185         hide();
0186     }
0187 
0188     update();
0189 }
0190 
0191 void TabIcon::paintEvent(QPaintEvent* event)
0192 {
0193     Q_UNUSED(event);
0194 
0195     QPainter p(this);
0196     p.setRenderHint(QPainter::Antialiasing);
0197 
0198     const int size = 16;
0199     const int pixmapSize = qRound(size * data()->animationPixmap.devicePixelRatioF());
0200 
0201     // Center the pixmap in rect
0202     QRect r = rect();
0203     r.setX((r.width() - size) / 2);
0204     r.setY((r.height() - size) / 2);
0205     r.setWidth(size);
0206     r.setHeight(size);
0207 
0208     if (m_animationRunning) {
0209         p.drawPixmap(r, data()->animationPixmap, QRect(m_currentFrame * pixmapSize, 0, pixmapSize, pixmapSize));
0210     } else if (m_audioIconDisplayed && !m_tab->isPinned()) {
0211         m_audioIconRect = r;
0212         p.drawPixmap(r, m_tab->isMuted() ? data()->audioMutedPixmap : data()->audioPlayingPixmap);
0213     } else if (!m_sitePixmap.isNull()) {
0214         p.drawPixmap(r, m_sitePixmap);
0215     } else if (m_tab && m_tab->isPinned()) {
0216         p.drawPixmap(r, IconProvider::emptyWebIcon().pixmap(size));
0217     }
0218 
0219     // Draw audio icon on top of site icon for pinned tabs
0220     if (!m_animationRunning && m_audioIconDisplayed && m_tab->isPinned()) {
0221         const int s = size - 4;
0222         const QRect r(width() - s, 0, s, s);
0223         m_audioIconRect = r;
0224         QColor c = palette().color(QPalette::Window);
0225         c.setAlpha(180);
0226         p.setPen(c);
0227         p.setBrush(c);
0228         p.drawEllipse(r);
0229         p.drawPixmap(r, m_tab->isMuted() ? data()->audioMutedPixmap : data()->audioPlayingPixmap);
0230     }
0231 
0232     // Draw background activity indicator
0233     if (m_tab && m_tab->isPinned() && m_tab->webView()->backgroundActivity()) {
0234         const int s = 5;
0235         // Background
0236         const QRect r1(width() - s - 2, height() - s - 2, s + 2, s + 2);
0237         QColor c1 = palette().color(QPalette::Window);
0238         c1.setAlpha(180);
0239         p.setPen(Qt::transparent);
0240         p.setBrush(c1);
0241         p.drawEllipse(r1);
0242         // Foreground
0243         const QRect r2(width() - s - 1, height() - s - 1, s, s);
0244         QColor c2 = palette().color(QPalette::Text);
0245         p.setPen(Qt::transparent);
0246         p.setBrush(c2);
0247         p.drawEllipse(r2);
0248     }
0249 }
0250 
0251 void TabIcon::mousePressEvent(QMouseEvent *event)
0252 {
0253     // If audio icon is clicked - we don't propagate mouse press to the tab
0254     if (m_audioIconDisplayed && event->button() == Qt::LeftButton && m_audioIconRect.contains(event->position().toPoint())) {
0255         m_tab->toggleMuted();
0256         return;
0257     }
0258 
0259     QWidget::mousePressEvent(event);
0260 }