Falkon Develop
Cross-platform Qt-based web browser
tabicon.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2014-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "tabicon.h"
19#include "webtab.h"
20#include "webpage.h"
21#include "iconprovider.h"
22#include "tabbedwebview.h"
23
24#include <QTimer>
25#include <QToolTip>
26#include <QMouseEvent>
27#include <QPainter>
28
29TabIcon::TabIcon(QWidget* parent)
30 : QWidget(parent)
31 , m_tab(nullptr)
32 , m_currentFrame(0)
33 , m_animationRunning(false)
34 , m_audioIconDisplayed(false)
35{
36 setObjectName(QSL("tab-icon"));
37
38 m_updateTimer = new QTimer(this);
39 m_updateTimer->setInterval(data()->animationInterval);
40 connect(m_updateTimer, &QTimer::timeout, this, &TabIcon::updateAnimationFrame);
41
42 m_hideTimer = new QTimer(this);
43 m_hideTimer->setInterval(250);
44 connect(m_hideTimer, &QTimer::timeout, this, &TabIcon::hide);
45
46 resize(16, 16);
47}
48
50{
51 m_tab = tab;
52
53 connect(m_tab->webView(), &QWebEngineView::loadStarted, this, &TabIcon::showLoadingAnimation);
54 connect(m_tab->webView(), &QWebEngineView::loadFinished, this, &TabIcon::hideLoadingAnimation);
55 connect(m_tab->webView(), &WebView::iconChanged, this, &TabIcon::updateIcon);
56 connect(m_tab->webView(), &WebView::backgroundActivityChanged, this, [this]() { update(); });
57
58 auto pageChanged = [this](WebPage *page) {
59 connect(page, &QWebEnginePage::recentlyAudibleChanged, this, &TabIcon::updateAudioIcon);
60 };
61 pageChanged(m_tab->webView()->page());
62 connect(m_tab->webView(), &WebView::pageChanged, this, pageChanged);
63
64 updateIcon();
65}
66
67void TabIcon::showLoadingAnimation()
68{
69 m_currentFrame = 0;
70
71 updateAnimationFrame();
72 show();
73}
74
75void TabIcon::hideLoadingAnimation()
76{
77 m_animationRunning = false;
78
79 m_updateTimer->stop();
80 updateIcon();
81}
82
84{
85 m_sitePixmap = m_tab->icon(/*allowNull*/ true).pixmap(16);
86 if (m_sitePixmap.isNull()) {
87 if (m_tab->url().isEmpty() || m_tab->url().scheme() == QL1S("falkon")) {
88 hide();
89 } else {
90 m_hideTimer->start();
91 }
92 } else {
93 show();
94 }
95 update();
96}
97
98// static
100{
101 static Data *data = nullptr;
102 if (!data) {
103 data = new TabIcon::Data;
105 data->animationPixmap = QIcon(QSL(":icons/other/loading.png")).pixmap(288, 16);
107 data->audioPlayingPixmap = QIcon::fromTheme(QSL("audio-volume-high"), QIcon(QSL(":icons/other/audioplaying.svg"))).pixmap(16);
108 data->audioMutedPixmap = QIcon::fromTheme(QSL("audio-volume-muted"), QIcon(QSL(":icons/other/audiomuted.svg"))).pixmap(16);
109 }
110 return data;
111}
112
113void TabIcon::updateAnimationFrame()
114{
115 if (!m_animationRunning) {
116 m_updateTimer->start();
117 m_animationRunning = true;
118 }
119
120 update();
121 m_currentFrame = (m_currentFrame + 1) % data()->framesCount;
122}
123
124void TabIcon::show()
125{
126 if (!shouldBeVisible()) {
127 return;
128 }
129
130 m_hideTimer->stop();
131
132 if (isVisible() && width() == 16) {
133 return;
134 }
135
136 setFixedSize(16, qMax(minimumHeight(), 16));
137 Q_EMIT resized();
138 QWidget::show();
139}
140
141void TabIcon::hide()
142{
143 if (shouldBeVisible()) {
144 return;
145 }
146
147 if (isHidden() && width() == 1) {
148 return;
149 }
150
151 setFixedSize(1, qMax(minimumHeight(), 16));
152 Q_EMIT resized();
153 QWidget::hide();
154}
155
156bool TabIcon::shouldBeVisible() const
157{
158 if (m_tab && m_tab->isPinned()) {
159 return true;
160 }
161 return !m_sitePixmap.isNull() || m_animationRunning || m_audioIconDisplayed || (m_tab && m_tab->isPinned());
162}
163
164bool TabIcon::event(QEvent *event)
165{
166 if (event->type() == QEvent::ToolTip) {
167 auto *e = static_cast<QHelpEvent*>(event);
168 if (m_audioIconDisplayed && m_audioIconRect.contains(e->pos())) {
169 QToolTip::showText(e->globalPos(), m_tab->isMuted() ? tr("Unmute Tab") : tr("Mute Tab"), this);
170 event->accept();
171 return true;
172 }
173 }
174
175 return QWidget::event(event);
176}
177
178void TabIcon::updateAudioIcon(bool recentlyAudible)
179{
180 if (m_tab->isMuted() || (!m_tab->isMuted() && recentlyAudible)) {
181 m_audioIconDisplayed = true;
182 show();
183 } else {
184 m_audioIconDisplayed = false;
185 hide();
186 }
187
188 update();
189}
190
191void TabIcon::paintEvent(QPaintEvent* event)
192{
193 Q_UNUSED(event);
194
195 QPainter p(this);
196 p.setRenderHint(QPainter::Antialiasing);
197
198 const int size = 16;
199 const int pixmapSize = qRound(size * data()->animationPixmap.devicePixelRatioF());
200
201 // Center the pixmap in rect
202 QRect r = rect();
203 r.setX((r.width() - size) / 2);
204 r.setY((r.height() - size) / 2);
205 r.setWidth(size);
206 r.setHeight(size);
207
208 if (m_animationRunning) {
209 p.drawPixmap(r, data()->animationPixmap, QRect(m_currentFrame * pixmapSize, 0, pixmapSize, pixmapSize));
210 } else if (m_audioIconDisplayed && !m_tab->isPinned()) {
211 m_audioIconRect = r;
212 p.drawPixmap(r, m_tab->isMuted() ? data()->audioMutedPixmap : data()->audioPlayingPixmap);
213 } else if (!m_sitePixmap.isNull()) {
214 p.drawPixmap(r, m_sitePixmap);
215 } else if (m_tab && m_tab->isPinned()) {
216 p.drawPixmap(r, IconProvider::emptyWebIcon().pixmap(size));
217 }
218
219 // Draw audio icon on top of site icon for pinned tabs
220 if (!m_animationRunning && m_audioIconDisplayed && m_tab->isPinned()) {
221 const int s = size - 4;
222 const QRect r(width() - s, 0, s, s);
223 m_audioIconRect = r;
224 QColor c = palette().color(QPalette::Window);
225 c.setAlpha(180);
226 p.setPen(c);
227 p.setBrush(c);
228 p.drawEllipse(r);
229 p.drawPixmap(r, m_tab->isMuted() ? data()->audioMutedPixmap : data()->audioPlayingPixmap);
230 }
231
232 // Draw background activity indicator
233 if (m_tab && m_tab->isPinned() && m_tab->webView()->backgroundActivity()) {
234 const int s = 5;
235 // Background
236 const QRect r1(width() - s - 2, height() - s - 2, s + 2, s + 2);
237 QColor c1 = palette().color(QPalette::Window);
238 c1.setAlpha(180);
239 p.setPen(Qt::transparent);
240 p.setBrush(c1);
241 p.drawEllipse(r1);
242 // Foreground
243 const QRect r2(width() - s - 1, height() - s - 1, s, s);
244 QColor c2 = palette().color(QPalette::Text);
245 p.setPen(Qt::transparent);
246 p.setBrush(c2);
247 p.drawEllipse(r2);
248 }
249}
250
251void TabIcon::mousePressEvent(QMouseEvent *event)
252{
253 // If audio icon is clicked - we don't propagate mouse press to the tab
254 if (m_audioIconDisplayed && event->button() == Qt::LeftButton && m_audioIconRect.contains(event->position().toPoint())) {
255 m_tab->toggleMuted();
256 return;
257 }
258
259 QWidget::mousePressEvent(event);
260}
static QIcon emptyWebIcon()
void updateIcon()
Definition: tabicon.cpp:83
void setWebTab(WebTab *tab)
Definition: tabicon.cpp:49
void resized()
TabIcon(QWidget *parent=nullptr)
Definition: tabicon.cpp:29
static Data * data()
Definition: tabicon.cpp:99
Definition: webtab.h:40
bool isMuted() const
Definition: webtab.cpp:433
QUrl url() const
Definition: webtab.cpp:263
void toggleMuted()
Definition: webtab.cpp:448
bool isPinned() const
Definition: webtab.cpp:414
TabbedWebView * webView() const
Definition: webtab.cpp:209
QIcon icon(bool allowNull=false) const
Definition: webtab.cpp:286
bool backgroundActivity() const
Definition: webview.cpp:224
void backgroundActivityChanged(bool)
WebPage * page() const
Definition: webview.cpp:132
void pageChanged(WebPage *page)
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40
int framesCount
Definition: tabicon.h:36
QPixmap audioPlayingPixmap
Definition: tabicon.h:39
int animationInterval
Definition: tabicon.h:37
QPixmap animationPixmap
Definition: tabicon.h:38
QPixmap audioMutedPixmap
Definition: tabicon.h:40