1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50
51 #include "tabwidget.h"
52 #include "webpage.h"
53 #include "webview.h"
54 #include <QMenu>
55 #include <QTabBar>
56 #include <QWebEngineProfile>
57
TabWidget(QWebEngineProfile * profile,QWidget * parent)58 TabWidget::TabWidget(QWebEngineProfile *profile, QWidget *parent)
59 : QTabWidget(parent)
60 , m_profile(profile)
61 {
62 QTabBar *tabBar = this->tabBar();
63 tabBar->setTabsClosable(true);
64 tabBar->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
65 tabBar->setMovable(true);
66 tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
67 connect(tabBar, &QTabBar::customContextMenuRequested, this, &TabWidget::handleContextMenuRequested);
68 connect(tabBar, &QTabBar::tabCloseRequested, this, &TabWidget::closeTab);
69 connect(tabBar, &QTabBar::tabBarDoubleClicked, [this](int index) {
70 if (index == -1)
71 createTab();
72 });
73
74 setDocumentMode(true);
75 setElideMode(Qt::ElideRight);
76
77 connect(this, &QTabWidget::currentChanged, this, &TabWidget::handleCurrentChanged);
78 }
79
handleCurrentChanged(int index)80 void TabWidget::handleCurrentChanged(int index)
81 {
82 if (index != -1) {
83 WebView *view = webView(index);
84 if (!view->url().isEmpty())
85 view->setFocus();
86 emit titleChanged(view->title());
87 emit loadProgress(view->loadProgress());
88 emit urlChanged(view->url());
89 emit favIconChanged(view->favIcon());
90 emit webActionEnabledChanged(QWebEnginePage::Back, view->isWebActionEnabled(QWebEnginePage::Back));
91 emit webActionEnabledChanged(QWebEnginePage::Forward, view->isWebActionEnabled(QWebEnginePage::Forward));
92 emit webActionEnabledChanged(QWebEnginePage::Stop, view->isWebActionEnabled(QWebEnginePage::Stop));
93 emit webActionEnabledChanged(QWebEnginePage::Reload,view->isWebActionEnabled(QWebEnginePage::Reload));
94 } else {
95 emit titleChanged(QString());
96 emit loadProgress(0);
97 emit urlChanged(QUrl());
98 emit favIconChanged(QIcon());
99 emit webActionEnabledChanged(QWebEnginePage::Back, false);
100 emit webActionEnabledChanged(QWebEnginePage::Forward, false);
101 emit webActionEnabledChanged(QWebEnginePage::Stop, false);
102 emit webActionEnabledChanged(QWebEnginePage::Reload, true);
103 }
104 }
105
handleContextMenuRequested(const QPoint & pos)106 void TabWidget::handleContextMenuRequested(const QPoint &pos)
107 {
108 QMenu menu;
109 menu.addAction(tr("New &Tab"), this, &TabWidget::createTab, QKeySequence::AddTab);
110 int index = tabBar()->tabAt(pos);
111 if (index != -1) {
112 QAction *action = menu.addAction(tr("Clone Tab"));
113 connect(action, &QAction::triggered, this, [this,index]() {
114 cloneTab(index);
115 });
116 menu.addSeparator();
117 action = menu.addAction(tr("&Close Tab"));
118 action->setShortcut(QKeySequence::Close);
119 connect(action, &QAction::triggered, this, [this,index]() {
120 closeTab(index);
121 });
122 action = menu.addAction(tr("Close &Other Tabs"));
123 connect(action, &QAction::triggered, this, [this,index]() {
124 closeOtherTabs(index);
125 });
126 menu.addSeparator();
127 action = menu.addAction(tr("Reload Tab"));
128 action->setShortcut(QKeySequence::Refresh);
129 connect(action, &QAction::triggered, this, [this,index]() {
130 reloadTab(index);
131 });
132 } else {
133 menu.addSeparator();
134 }
135 menu.addAction(tr("Reload All Tabs"), this, &TabWidget::reloadAllTabs);
136 menu.exec(QCursor::pos());
137 }
138
currentWebView() const139 WebView *TabWidget::currentWebView() const
140 {
141 return webView(currentIndex());
142 }
143
webView(int index) const144 WebView *TabWidget::webView(int index) const
145 {
146 return qobject_cast<WebView*>(widget(index));
147 }
148
setupView(WebView * webView)149 void TabWidget::setupView(WebView *webView)
150 {
151 QWebEnginePage *webPage = webView->page();
152
153 connect(webView, &QWebEngineView::titleChanged, [this, webView](const QString &title) {
154 int index = indexOf(webView);
155 if (index != -1) {
156 setTabText(index, title);
157 setTabToolTip(index, title);
158 }
159 if (currentIndex() == index)
160 emit titleChanged(title);
161 });
162 connect(webView, &QWebEngineView::urlChanged, [this, webView](const QUrl &url) {
163 int index = indexOf(webView);
164 if (index != -1)
165 tabBar()->setTabData(index, url);
166 if (currentIndex() == index)
167 emit urlChanged(url);
168 });
169 connect(webView, &QWebEngineView::loadProgress, [this, webView](int progress) {
170 if (currentIndex() == indexOf(webView))
171 emit loadProgress(progress);
172 });
173 connect(webPage, &QWebEnginePage::linkHovered, [this, webView](const QString &url) {
174 if (currentIndex() == indexOf(webView))
175 emit linkHovered(url);
176 });
177 connect(webView, &WebView::favIconChanged, [this, webView](const QIcon &icon) {
178 int index = indexOf(webView);
179 if (index != -1)
180 setTabIcon(index, icon);
181 if (currentIndex() == index)
182 emit favIconChanged(icon);
183 });
184 connect(webView, &WebView::webActionEnabledChanged, [this, webView](QWebEnginePage::WebAction action, bool enabled) {
185 if (currentIndex() == indexOf(webView))
186 emit webActionEnabledChanged(action,enabled);
187 });
188 connect(webPage, &QWebEnginePage::windowCloseRequested, [this, webView]() {
189 int index = indexOf(webView);
190 if (index >= 0)
191 closeTab(index);
192 });
193 }
194
createTab()195 WebView *TabWidget::createTab()
196 {
197 WebView *webView = createBackgroundTab();
198 setCurrentWidget(webView);
199 return webView;
200 }
201
createBackgroundTab()202 WebView *TabWidget::createBackgroundTab()
203 {
204 WebView *webView = new WebView;
205 WebPage *webPage = new WebPage(m_profile, webView);
206 webView->setPage(webPage);
207 setupView(webView);
208 int index = addTab(webView, tr("(Untitled)"));
209 setTabIcon(index, webView->favIcon());
210 // Workaround for QTBUG-61770
211 webView->resize(currentWidget()->size());
212 webView->show();
213 return webView;
214 }
215
reloadAllTabs()216 void TabWidget::reloadAllTabs()
217 {
218 for (int i = 0; i < count(); ++i)
219 webView(i)->reload();
220 }
221
closeOtherTabs(int index)222 void TabWidget::closeOtherTabs(int index)
223 {
224 for (int i = count() - 1; i > index; --i)
225 closeTab(i);
226 for (int i = index - 1; i >= 0; --i)
227 closeTab(i);
228 }
229
closeTab(int index)230 void TabWidget::closeTab(int index)
231 {
232 if (WebView *view = webView(index)) {
233 bool hasFocus = view->hasFocus();
234 removeTab(index);
235 if (hasFocus && count() > 0)
236 currentWebView()->setFocus();
237 if (count() == 0)
238 createTab();
239 view->deleteLater();
240 }
241 }
242
cloneTab(int index)243 void TabWidget::cloneTab(int index)
244 {
245 if (WebView *view = webView(index)) {
246 WebView *tab = createTab();
247 tab->setUrl(view->url());
248 }
249 }
250
setUrl(const QUrl & url)251 void TabWidget::setUrl(const QUrl &url)
252 {
253 if (WebView *view = currentWebView()) {
254 view->setUrl(url);
255 view->setFocus();
256 }
257 }
258
triggerWebPageAction(QWebEnginePage::WebAction action)259 void TabWidget::triggerWebPageAction(QWebEnginePage::WebAction action)
260 {
261 if (WebView *webView = currentWebView()) {
262 webView->triggerPageAction(action);
263 webView->setFocus();
264 }
265 }
266
nextTab()267 void TabWidget::nextTab()
268 {
269 int next = currentIndex() + 1;
270 if (next == count())
271 next = 0;
272 setCurrentIndex(next);
273 }
274
previousTab()275 void TabWidget::previousTab()
276 {
277 int next = currentIndex() - 1;
278 if (next < 0)
279 next = count() - 1;
280 setCurrentIndex(next);
281 }
282
reloadTab(int index)283 void TabWidget::reloadTab(int index)
284 {
285 if (WebView *view = webView(index))
286 view->reload();
287 }
288