1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the QtBrowser project.
8 ** $QT_BEGIN_LICENSE:GPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 2 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.GPLv2 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU General Public License version 2 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 3.0 as published by the Free Software
28 ** Foundation and appearing in the file LICENSE.GPL included in the
29 ** packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 3.0 requirements will be
31 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ****************************************************************************/
39 import QtWebEngine 1.1
40 import QtQuick.Controls 1.4
41 import QtQuick.Controls.Styles 1.4
42 import QtQuick.Layouts 1.2
43 import QtGraphicalEffects 1.0
45 import io.qt.browser 1.0
51 property int itemWidth: browserWindow.width / 2
52 property int itemHeight: browserWindow.height / 2
54 property bool interactive: true
56 property alias currentIndex: pathView.currentIndex
57 property alias count: pathView.count
59 property string viewState: "page"
62 if (viewState == "page")
63 homeScreen.state = "disabled"
66 property QtObject otrProfile: WebEngineProfile {
70 property QtObject defaultProfile: WebEngineProfile {
71 storageName: "YABProfile"
79 property alias webView: webEngineView
80 property alias title: webEngineView.title
82 property var image: QtObject {
83 property var snapshot: null
84 property string url: "about:blank"
87 visible: opacity != 0.0
90 NumberAnimation { duration: animationDuration }
98 findBar.visible = !findBar.visible
99 if (findBar.visible) {
100 findTextField.forceActiveFocus()
105 FeaturePermissionBar {
124 profile: settingsView.privateBrowsingEnabled ? otrProfile : defaultProfile
125 enabled: root.interactive
127 function takeSnapshot() {
128 if (webEngineView.url == "" || webEngineView.url == "about:blank") {
129 tabItem.image.url = "about:blank"
130 tabItem.image.snapshot = null
134 if (tabItem.image.url == webEngineView.url || tabItem.opacity != 1.0)
137 tabItem.image.url = webEngineView.url
138 webEngineView.grabToImage(function(result) {
139 tabItem.image.snapshot = result;
140 console.log("takeSnapshot("+result.url+")")
144 // Trigger a refresh to check if the new url is bookmarked.
145 onUrlChanged: navigation.refresh()
148 settings.autoLoadImages: settingsView.autoLoadImages
149 settings.javascriptEnabled: !settingsView.javaScriptDisabled
151 // This should be enabled as we can switch to Qt 5.6 (i.e. import QtWebEngine 1.2)
152 // settings.pluginsEnabled: settingsView.pluginsEnabled
156 navigation.state = "enabled"
159 onCertificateError: {
160 if (!acceptedCertificates.shouldAutoAccept(error)){
162 sslDialog.enqueue(error)
164 error.ignoreCertificateError()
168 onNewViewRequested: {
169 webEngineView.takeSnapshot()
171 if (!request.userInitiated) {
172 print("Warning: Blocked a popup window.")
176 tab = tabView.createEmptyTab()
181 if (request.destination == WebEngineView.NewViewInTab) {
182 pathView.positionViewAtIndex(tabView.count - 1, PathView.Center)
183 request.openIn(tab.webView)
184 } else if (request.destination == WebEngineView.NewViewInBackgroundTab) {
185 var index = pathView.currentIndex
186 request.openIn(tab.webView)
187 pathView.positionViewAtIndex(index, PathView.Center)
188 } else if (request.destination == WebEngineView.NewViewInDialog) {
189 request.openIn(tab.webView)
191 request.openIn(tab.webView)
195 onFeaturePermissionRequested: {
196 permBar.securityOrigin = securityOrigin;
197 permBar.requestedFeature = feature;
198 permBar.visible = true;
204 visible: desaturation != 0.0
205 anchors.fill: webEngineView
206 source: webEngineView
207 desaturation: root.interactive ? 0.0 : 1.0
209 Behavior on desaturation {
210 NumberAnimation { duration: animationDuration }
216 visible: radius != 0.0
217 anchors.fill: desaturate
219 radius: desaturate.desaturation * 25
224 enabled: root.interactive
225 target: webEngineView
227 onTouchYChanged: browserWindow.touchY = tracker.touchY
228 onYVelocityChanged: browserWindow.velocityY = yVelocity
230 browserWindow.touchY = tracker.touchY
231 browserWindow.velocityY = yVelocity
232 browserWindow.touchReference = tracker.touchY
233 browserWindow.touchGesture = true
234 navigation.state = "tracking"
237 browserWindow.velocityY = yVelocity
238 browserWindow.touchGesture = false
239 navigation.state = "tracking"
241 onScrollDirectionChanged: {
242 browserWindow.velocityY = 0
243 browserWindow.touchReference = tracker.touchY
249 if (inputPanel.state === "visible")
251 if (webEngineView.url == "" || webEngineView.url == "about:blank")
256 visible: opacity != 0.0
261 verticalCenterOffset: -toolBarSize
264 source: "qrc:///icon"
268 top: placeholder.bottom
270 horizontalCenter: placeholder.horizontalCenter
272 font.family: defaultFontFamily
278 Behavior on opacity {
279 NumberAnimation { duration: animationDuration }
286 right: webEngineView.right
287 left: webEngineView.left
288 top: webEngineView.top
290 height: toolBarSize / 2 + 10
301 bottom: parent.bottom
307 Layout.fillWidth: true
309 webEngineView.findText(text)
311 style: TextFieldStyle {
313 font.family: defaultFontFamily
315 selectionColor: uiHighlightColor
316 selectedTextColor: "black"
317 placeholderTextColor: placeholderColor
318 background: Rectangle {
320 implicitHeight: toolBarSize / 2
321 border.color: textFieldStrokeColor
330 bottom: parent.bottom
338 bottom: parent.bottom
340 color: uiSeparatorColor
343 id: findBackwardButton
344 iconSource: "qrc:///back"
345 implicitHeight: parent.height
346 onClicked: webEngineView.findText(findTextField.text, WebEngineView.FindBackward)
352 bottom: parent.bottom
354 color: uiSeparatorColor
357 id: findForwardButton
358 iconSource: "qrc:///forward"
359 implicitHeight: parent.height
360 onClicked: webEngineView.findText(findTextField.text)
366 bottom: parent.bottom
368 color: uiSeparatorColor
372 iconSource: "qrc:///stop"
373 implicitHeight: parent.height
374 onClicked: findBar.visible = false
385 function makeCurrent(index) {
387 pathView.positionViewAtIndex(index, PathView.Center)
391 function createEmptyTab() {
392 var tab = add(tabComponent)
396 function add(component) {
397 if (listModel.count === tabViewMaxTabs) {
398 homeScreen.messageBox.state = "tabsfull"
399 homeScreen.state = "enabled"
400 homeScreen.forceActiveFocus()
404 var element = {"item": null }
405 element.item = component.createObject(root, { "width": root.width, "height": root.height, "opacity": 0.0 })
407 if (element.item == null) {
408 console.log("PageView::add(): Error creating object");
412 listModel.append(element)
416 function remove(index) {
417 pathView.interactive = false
418 pathView.currentItem.state = ""
419 pathView.currentItem.visible = false
420 listModel.remove(index)
421 pathView.decrementCurrentIndex()
422 pathView.interactive = true
423 if (listModel.count == 0)
424 engine.rootWindow.close()
427 function get(index) {
428 return listModel.get(index)
439 property real visibility: 0.0
440 property bool isCurrentItem: PathView.isCurrentItem
442 visible: PathView.onPath && visibility != 0.0
443 state: isCurrentItem ? root.viewState : "list"
446 NumberAnimation { duration: animationDuration }
452 PropertyChanges { target: wrapper; width: root.width; height: root.height; visibility: 0.0 }
453 PropertyChanges { target: pathView; interactive: false }
454 PropertyChanges { target: item; opacity: 1.0 }
455 PropertyChanges { target: navigation; state: "enabled" }
459 PropertyChanges { target: wrapper; width: itemWidth; height: itemHeight; visibility: 1.0 }
460 PropertyChanges { target: pathView; interactive: true }
461 PropertyChanges { target: item; opacity: 0.0 }
465 transitions: Transition {
467 PropertyAnimation { property: "visibility"; duration: animationDuration; easing.type : Easing.InSine }
468 PropertyAnimation { properties: "x,y"; duration: animationDuration; easing.type: Easing.InSine }
469 PropertyAnimation { properties: "width,height"; duration: animationDuration; easing.type: Easing.InSine }
473 width: itemWidth; height: itemHeight
475 if (pathView.count == 1)
477 if (pathView.count < 4)
478 return isCurrentItem ? 1.0 : 0.5
483 var index1 = pathView.currentIndex - 2
484 var index2 = pathView.currentIndex - 1
485 var index4 = (pathView.currentIndex + 1) % pathView.count
486 var index5 = (pathView.currentIndex + 2) % pathView.count
489 index1 = pathView.count + index1
491 index2 = pathView.count + index2
509 enabled: pathView.interactive
510 anchors.fill: wrapper
512 mouse.accepted = true
516 if (index == pathView.currentIndex) {
517 if (root.viewState == "list")
518 root.viewState = "page"
521 pathView.currentIndex = index
527 property real size: 24
531 horizontalCenter: parent.horizontalCenter
533 color: iconOverlayColor
535 width: snapshot.width
536 height: snapshot.height
542 samples: shadow.size * 2
544 transparentBorder: true
545 visible: wrapper.visibility == 1.0
554 if (!item.image.snapshot)
555 return "qrc:///about"
556 return item.image.snapshot.url
560 enabled: index == pathView.currentIndex && !pathView.moving && !pathView.flicking && wrapper.visibility == 1.0
561 opacity: enabled ? 1.0 : 0.0
562 visible: wrapper.visibility == 1.0
563 width: image.sourceSize.width
564 height: image.sourceSize.height - 2
566 color: iconOverlayColor
568 horizontalCenter: parent.right
569 verticalCenter: parent.top
574 if (closeButton.pressed)
582 source: "qrc:///delete"
587 mouse.accepted = true
588 remove(pathView.currentIndex)
592 Behavior on opacity {
593 NumberAnimation { duration: animationDuration / 2 }
597 anchors.fill: wrapper
604 horizontalCenter: parent.horizontalCenter
606 horizontalAlignment: Text.AlignHCenter
607 width: parent.width - image.width
608 elide: Text.ElideRight
611 font.family: defaultFontFamily
612 color: settingsView.privateBrowsingEnabled ? "white" : "#0B508C"
613 visible: wrapper.isCurrentItem && wrapper.visibility == 1.0
629 highlightMoveDuration: animationDuration
630 highlightRangeMode: PathView.StrictlyEnforceRange
631 snapMode: PathView.SnapToItem
632 preferredHighlightBegin: 0.5
633 preferredHighlightEnd: 0.5
635 dragMargin: itemHeight
637 focus: pathView.interactive
639 property real offset: 30
641 property real margin: {
643 return root.width / 4 - offset
645 return root.width / 8 + offset
647 return root.width / 8 - offset
652 property real middle: {
654 return (pathView.height / 2) - (currentItem.visibility * 50)
655 return (pathView.height / 2 - 50)
659 startX: pathView.margin
660 startY: pathView.middle
662 PathPercent { value: 0.0 }
663 PathAttribute { name: "itemZ"; value: 0 }
665 x: (pathView.width - itemWidth) / 2 + 106
668 PathPercent { value: 0.49 }
669 PathAttribute { name: "itemZ"; value: 6 }
671 PathLine { relativeX: 0; relativeY: 0 }
674 x: (pathView.width - itemWidth) / 2 + itemWidth - 106
677 PathPercent { value: 0.51 }
679 PathLine { relativeX: 0; relativeY: 0 }
681 PathAttribute { name: "itemZ"; value: 4 }
683 x: pathView.width - pathView.margin
686 PathPercent { value: 1 }
687 PathAttribute { name: "itemZ"; value: 2 }
690 Keys.onLeftPressed: decrementCurrentIndex()
691 Keys.onRightPressed: incrementCurrentIndex()