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 }