]> rtime.felk.cvut.cz Git - coffee/qtwebbrowser.git/blob - src/qml/PageView.qml
Remove experimental import and add debug output with --log-level=0
[coffee/qtwebbrowser.git] / src / qml / PageView.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtBrowser project.
7 **
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.
16 **
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.
24 **
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.
32 **
33 **
34 ** $QT_END_LICENSE$
35 **
36 ****************************************************************************/
37
38 import QtQuick 2.5
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
44
45 import io.qt.browser 1.0
46 import "assets"
47
48 Rectangle {
49     id: root
50
51     property int itemWidth: browserWindow.width / 2
52     property int itemHeight: browserWindow.height / 2
53
54     property bool interactive: true
55
56     property alias currentIndex: pathView.currentIndex
57     property alias count: pathView.count
58
59     property string viewState: "page"
60
61     onViewStateChanged: {
62         if (viewState == "page")
63             homeScreen.state = "disabled"
64     }
65
66     property QtObject otrProfile: WebEngineProfile {
67         offTheRecord: true
68     }
69
70     property QtObject defaultProfile: WebEngineProfile {
71         storageName: "YABProfile"
72         offTheRecord: false
73     }
74
75     Component {
76         id: tabComponent
77         Rectangle {
78             id: tabItem
79             property alias webView: webEngineView
80             property alias title: webEngineView.title
81
82             property var image: QtObject {
83                 property var snapshot: null
84                 property string url: "about:blank"
85             }
86
87             visible: opacity != 0.0
88
89             Behavior on opacity {
90                 NumberAnimation { duration: animationDuration }
91             }
92
93             anchors.fill: parent
94
95             Action {
96                 shortcut: "Ctrl+F"
97                 onTriggered: {
98                     findBar.visible = !findBar.visible
99                     if (findBar.visible) {
100                         findTextField.forceActiveFocus()
101                     }
102                 }
103             }
104
105             FeaturePermissionBar {
106                 id: permBar
107                 view: webEngineView
108                 anchors {
109                     left: parent.left
110                     right: parent.right
111                     top: parent.top
112                 }
113                 z: 3
114             }
115
116             WebEngineView {
117                 id: webEngineView
118
119                 anchors {
120                     fill: parent
121                     top: permBar.bottom
122                 }
123
124                 profile: settingsView.privateBrowsingEnabled ? otrProfile : defaultProfile
125                 enabled: root.interactive
126
127                 function takeSnapshot() {
128                     if (webEngineView.url == "" || webEngineView.url == "about:blank") {
129                         tabItem.image.url = "about:blank"
130                         tabItem.image.snapshot = null
131                         return
132                     }
133
134                     if (tabItem.image.url == webEngineView.url || tabItem.opacity != 1.0)
135                         return
136
137                     tabItem.image.url = webEngineView.url
138                     webEngineView.grabToImage(function(result) {
139                         tabItem.image.snapshot = result;
140                         console.log("takeSnapshot("+result.url+")")
141                     });
142                 }
143
144                 // Trigger a refresh to check if the new url is bookmarked.
145                 onUrlChanged: navigation.refresh()
146
147
148                 settings.autoLoadImages: settingsView.autoLoadImages
149                 settings.javascriptEnabled: !settingsView.javaScriptDisabled
150
151                 // This should be enabled as we can switch to Qt 5.6 (i.e. import QtWebEngine 1.2)
152                 // settings.pluginsEnabled: settingsView.pluginsEnabled
153
154                 onLoadingChanged: {
155                     if (loading)
156                         navigation.state = "enabled"
157                 }
158
159                 onCertificateError: {
160                     if (!acceptedCertificates.shouldAutoAccept(error)){
161                         error.defer()
162                         sslDialog.enqueue(error)
163                     } else{
164                         error.ignoreCertificateError()
165                     }
166                 }
167
168                 onNewViewRequested: {
169                     webEngineView.takeSnapshot()
170                     var tab
171                     if (!request.userInitiated) {
172                         print("Warning: Blocked a popup window.")
173                         return
174                     }
175
176                     tab = tabView.createEmptyTab()
177
178                     if (!tab)
179                         return
180
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)
190                     } else {
191                         request.openIn(tab.webView)
192                     }
193                 }
194
195                 onFeaturePermissionRequested: {
196                     permBar.securityOrigin = securityOrigin;
197                     permBar.requestedFeature = feature;
198                     permBar.visible = true;
199                 }
200             }
201
202             Desaturate {
203                 id: desaturate
204                 visible: desaturation != 0.0
205                 anchors.fill: webEngineView
206                 source: webEngineView
207                 desaturation: root.interactive ? 0.0 : 1.0
208
209                 Behavior on desaturation {
210                     NumberAnimation { duration: animationDuration }
211                 }
212             }
213
214             FastBlur {
215                 id: blur
216                 visible: radius != 0.0
217                 anchors.fill: desaturate
218                 source: desaturate
219                 radius: desaturate.desaturation * 25
220             }
221
222             TouchTracker {
223                 id: tracker
224                 enabled: root.interactive
225                 target: webEngineView
226                 anchors.fill: parent
227                 onTouchYChanged: browserWindow.touchY = tracker.touchY
228                 onYVelocityChanged: browserWindow.velocityY = yVelocity
229                 onTouchBegin: {
230                     browserWindow.touchY = tracker.touchY
231                     browserWindow.velocityY = yVelocity
232                     browserWindow.touchReference = tracker.touchY
233                     browserWindow.touchGesture = true
234                     navigation.state = "tracking"
235                 }
236                 onTouchEnd: {
237                     browserWindow.velocityY = yVelocity
238                     browserWindow.touchGesture = false
239                     navigation.state = "tracking"
240                 }
241                 onScrollDirectionChanged: {
242                     browserWindow.velocityY = 0
243                     browserWindow.touchReference = tracker.touchY
244                 }
245             }
246
247             Rectangle {
248                 opacity: {
249                     if (inputPanel.state === "visible")
250                         return 0.0
251                     if (webEngineView.url == "" || webEngineView.url == "about:blank")
252                         return 1.0
253                     return 0.0
254                 }
255                 anchors.fill: parent
256                 visible: opacity != 0.0
257                 color: "white"
258                 Image {
259                     id: placeholder
260                     anchors {
261                         verticalCenterOffset: -toolBarSize
262                         centerIn: parent
263                     }
264                     source: "qrc:///icon"
265                 }
266                 Text {
267                     anchors {
268                         top: placeholder.bottom
269                         topMargin: 20
270                         horizontalCenter: placeholder.horizontalCenter
271                     }
272                     font.family: defaultFontFamily
273                     font.pixelSize: 28
274                     color: uiColor
275                     text: "Qt Browser"
276                 }
277
278                 Behavior on opacity {
279                     NumberAnimation { duration: animationDuration }
280                 }
281             }
282
283             Rectangle {
284                 id: findBar
285                 anchors {
286                     right: webEngineView.right
287                     left: webEngineView.left
288                     top: webEngineView.top
289                 }
290                 height: toolBarSize / 2 + 10
291                 visible: false
292                 color: uiColor
293
294                 RowLayout {
295                     spacing: 0
296                     anchors.fill: parent
297                     Rectangle {
298                         width: 5
299                         anchors {
300                             top: parent.top
301                             bottom: parent.bottom
302                         }
303                         color: uiColor
304                     }
305                     TextField {
306                         id: findTextField
307                         Layout.fillWidth: true
308                         onAccepted: {
309                             webEngineView.findText(text)
310                         }
311                         style: TextFieldStyle {
312                             textColor: "black"
313                             font.family: defaultFontFamily
314                             font.pixelSize: 28
315                             selectionColor: uiHighlightColor
316                             selectedTextColor: "black"
317                             placeholderTextColor: placeholderColor
318                             background: Rectangle {
319                                 implicitWidth: 514
320                                 implicitHeight: toolBarSize / 2
321                                 border.color: textFieldStrokeColor
322                                 border.width: 1
323                             }
324                         }
325                     }
326                     Rectangle {
327                         width: 5
328                         anchors {
329                             top: parent.top
330                             bottom: parent.bottom
331                         }
332                         color: uiColor
333                     }
334                     Rectangle {
335                         width: 1
336                         anchors {
337                             top: parent.top
338                             bottom: parent.bottom
339                         }
340                         color: uiSeparatorColor
341                     }
342                     UIButton {
343                         id: findBackwardButton
344                         iconSource: "qrc:///back"
345                         implicitHeight: parent.height
346                         onClicked: webEngineView.findText(findTextField.text, WebEngineView.FindBackward)
347                     }
348                     Rectangle {
349                         width: 1
350                         anchors {
351                             top: parent.top
352                             bottom: parent.bottom
353                         }
354                         color: uiSeparatorColor
355                     }
356                     UIButton {
357                         id: findForwardButton
358                         iconSource: "qrc:///forward"
359                         implicitHeight: parent.height
360                         onClicked: webEngineView.findText(findTextField.text)
361                     }
362                     Rectangle {
363                         width: 1
364                         anchors {
365                             top: parent.top
366                             bottom: parent.bottom
367                         }
368                         color: uiSeparatorColor
369                     }
370                     UIButton {
371                         id: findCancelButton
372                         iconSource: "qrc:///stop"
373                         implicitHeight: parent.height
374                         onClicked: findBar.visible = false
375                     }
376                 }
377             }
378         }
379     }
380
381     ListModel {
382         id: listModel
383     }
384
385     function makeCurrent(index) {
386         viewState = "list"
387         pathView.positionViewAtIndex(index, PathView.Center)
388         viewState = "page"
389     }
390
391     function createEmptyTab() {
392         var tab = add(tabComponent)
393         return tab
394     }
395
396     function add(component) {
397         if (listModel.count === tabViewMaxTabs) {
398             homeScreen.messageBox.state = "tabsfull"
399             homeScreen.state = "enabled"
400             homeScreen.forceActiveFocus()
401             return null
402         }
403
404         var element = {"item": null }
405         element.item = component.createObject(root, { "width": root.width, "height": root.height, "opacity": 0.0 })
406
407         if (element.item == null) {
408             console.log("PageView::add(): Error creating object");
409             return
410         }
411
412         listModel.append(element)
413         return element.item
414     }
415
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()
425     }
426
427     function get(index) {
428         return listModel.get(index)
429     }
430
431     Component {
432         id: delegate
433
434         Rectangle {
435             id: wrapper
436
437             parent: item
438
439             property real visibility: 0.0
440             property bool isCurrentItem: PathView.isCurrentItem
441
442             visible: PathView.onPath && visibility != 0.0
443             state: isCurrentItem ? root.viewState : "list"
444
445             Behavior on scale {
446                 NumberAnimation { duration: animationDuration }
447             }
448
449             states: [
450                 State {
451                     name: "page"
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" }
456                 },
457                 State {
458                     name: "list"
459                     PropertyChanges { target: wrapper; width: itemWidth; height: itemHeight; visibility: 1.0 }
460                     PropertyChanges { target: pathView; interactive: true }
461                     PropertyChanges { target: item; opacity: 0.0 }
462                 }
463             ]
464
465             transitions: Transition {
466                 ParallelAnimation {
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 }
470                 }
471             }
472
473             width: itemWidth; height: itemHeight
474             scale: {
475                 if (pathView.count == 1)
476                     return 1.0
477                 if (pathView.count < 4)
478                     return isCurrentItem ? 1.0 : 0.5
479
480                 if (isCurrentItem)
481                     return 1.0
482
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
487
488                 if (index1 < 0)
489                     index1 = pathView.count + index1
490                 if (index2 < 0)
491                     index2 = pathView.count + index2
492
493                 switch (index) {
494                 case index1 :
495                     return 0.25
496                 case index2:
497                     return 0.5
498                 case index4:
499                     return 0.5
500                 case index5:
501                     return 0.25
502                 }
503
504                 return 0.25
505             }
506             z: PathView.itemZ
507
508             MouseArea {
509                 enabled: pathView.interactive
510                 anchors.fill: wrapper
511                 onClicked: {
512                     mouse.accepted = true
513                     if (index < 0)
514                         return
515
516                     if (index == pathView.currentIndex) {
517                         if (root.viewState == "list")
518                             root.viewState = "page"
519                         return
520                     }
521                     pathView.currentIndex = index
522                 }
523             }
524             Rectangle {
525                 id: shadow
526                 visible: false
527                 property real size: 24
528                 anchors {
529                     top: parent.top
530                     topMargin: 9
531                     horizontalCenter: parent.horizontalCenter
532                 }
533                 color: iconOverlayColor
534                 radius: size / 2
535                 width: snapshot.width
536                 height: snapshot.height
537             }
538             GaussianBlur {
539                 anchors.fill: shadow
540                 source: shadow
541                 radius: shadow.size
542                 samples: shadow.size * 2
543                 opacity: 0.3
544                 transparentBorder: true
545                 visible: wrapper.visibility == 1.0
546             }
547
548             Rectangle {
549                 id: snapshot
550                 color: uiColor
551
552                 Image {
553                     source: {
554                         if (!item.image.snapshot)
555                             return "qrc:///about"
556                         return item.image.snapshot.url
557                     }
558                     anchors.fill: parent
559                     Rectangle {
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
565                         radius: width / 2
566                         color: iconOverlayColor
567                         anchors {
568                             horizontalCenter: parent.right
569                             verticalCenter: parent.top
570                         }
571                         Image {
572                             id: image
573                             opacity: {
574                                 if (closeButton.pressed)
575                                     return 0.70
576                                 return 1.0
577                             }
578                             anchors {
579                                 top: parent.top
580                                 left: parent.left
581                             }
582                             source: "qrc:///delete"
583                             MouseArea {
584                                 id: closeButton
585                                 anchors.fill: parent
586                                 onClicked: {
587                                     mouse.accepted = true
588                                     remove(pathView.currentIndex)
589                                 }
590                             }
591                         }
592                         Behavior on opacity {
593                             NumberAnimation { duration: animationDuration / 2 }
594                         }
595                     }
596                 }
597                 anchors.fill: wrapper
598             }
599
600             Text {
601                 anchors {
602                     topMargin: -25
603                     top: parent.top
604                     horizontalCenter: parent.horizontalCenter
605                 }
606                 horizontalAlignment: Text.AlignHCenter
607                 width: parent.width - image.width
608                 elide: Text.ElideRight
609                 text: item.title
610                 font.pixelSize: 16
611                 font.family: defaultFontFamily
612                 color: settingsView.privateBrowsingEnabled ? "white" : "#0B508C"
613                 visible: wrapper.isCurrentItem && wrapper.visibility == 1.0
614             }
615         }
616     }
617
618     Rectangle {
619         color: uiColor
620         anchors.fill: parent
621     }
622
623     PathView {
624         id: pathView
625         pathItemCount: 5
626         anchors.fill: parent
627         model: listModel
628         delegate: delegate
629         highlightMoveDuration: animationDuration
630         highlightRangeMode: PathView.StrictlyEnforceRange
631         snapMode: PathView.SnapToItem
632         preferredHighlightBegin: 0.5
633         preferredHighlightEnd: 0.5
634
635         dragMargin: itemHeight
636
637         focus: pathView.interactive
638
639         property real offset: 30
640
641         property real margin: {
642             if (count == 2)
643                 return root.width / 4 - offset
644             if (count == 3)
645                 return root.width / 8 + offset
646             if (count == 4)
647                 return root.width / 8 - offset
648
649             return offset
650         }
651
652         property real middle: {
653             if (currentItem)
654                 return (pathView.height / 2) - (currentItem.visibility * 50)
655             return (pathView.height / 2 - 50)
656         }
657
658         path: Path {
659             startX: pathView.margin
660             startY: pathView.middle
661
662             PathPercent { value: 0.0 }
663             PathAttribute { name: "itemZ"; value: 0 }
664             PathLine {
665                 x: (pathView.width - itemWidth) / 2 + 106
666                 y: pathView.middle
667             }
668             PathPercent { value: 0.49 }
669             PathAttribute { name: "itemZ"; value: 6 }
670
671             PathLine { relativeX: 0; relativeY: 0 }
672
673             PathLine {
674                 x: (pathView.width - itemWidth) / 2 + itemWidth - 106
675                 y: pathView.middle
676             }
677             PathPercent { value: 0.51 }
678
679             PathLine { relativeX: 0; relativeY: 0 }
680
681             PathAttribute { name: "itemZ"; value: 4 }
682             PathLine {
683                 x: pathView.width - pathView.margin
684                 y: pathView.middle
685             }
686             PathPercent { value: 1 }
687             PathAttribute { name: "itemZ"; value: 2 }
688         }
689
690         Keys.onLeftPressed: decrementCurrentIndex()
691         Keys.onRightPressed: incrementCurrentIndex()
692     }
693 }