BrowserWindow.qml Example File

webengine/quicknanobrowser/BrowserWindow.qml

  /****************************************************************************
  **
  ** Copyright (C) 2016 The Qt Company Ltd.
  ** Contact: https://www.qt.io/licensing/
  **
  ** This file is part of the QtWebEngine module of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:BSD$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see https://www.qt.io/terms-conditions. For further
  ** information use the contact form at https://www.qt.io/contact-us.
  **
  ** BSD License Usage
  ** Alternatively, you may use this file under the terms of the BSD license
  ** as follows:
  **
  ** "Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions are
  ** met:
  **   * Redistributions of source code must retain the above copyright
  **     notice, this list of conditions and the following disclaimer.
  **   * Redistributions in binary form must reproduce the above copyright
  **     notice, this list of conditions and the following disclaimer in
  **     the documentation and/or other materials provided with the
  **     distribution.
  **   * Neither the name of The Qt Company Ltd nor the names of its
  **     contributors may be used to endorse or promote products derived
  **     from this software without specific prior written permission.
  **
  **
  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/

  import Qt.labs.settings 1.0
  import QtQml 2.2
  import QtQuick 2.2
  import QtQuick.Controls 1.0
  import QtQuick.Controls.Private 1.0 as QQCPrivate
  import QtQuick.Controls.Styles 1.0
  import QtQuick.Dialogs 1.2
  import QtQuick.Layouts 1.0
  import QtQuick.Window 2.1
  import QtWebEngine 1.3

  ApplicationWindow {
      id: browserWindow
      property QtObject applicationRoot
      property Item currentWebView: tabs.currentIndex < tabs.count ? tabs.getTab(tabs.currentIndex).item : null
      property int previousVisibility: Window.Windowed

      width: 1300
      height: 900
      visible: true
      title: currentWebView && currentWebView.title

      // Make sure the Qt.WindowFullscreenButtonHint is set on OS X.
      Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint

      // Create a styleItem to determine the platform.
      // When using style "mac", ToolButtons are not supposed to accept focus.
      QQCPrivate.StyleItem { id: styleItem }
      property bool platformIsMac: styleItem.style == "mac"

      Settings {
          id : appSettings
          property alias autoLoadImages: loadImages.checked
          property alias javaScriptEnabled: javaScriptEnabled.checked
          property alias errorPageEnabled: errorPageEnabled.checked
          property alias pluginsEnabled: pluginsEnabled.checked
          property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked
          property alias autoLoadIconsForPage: autoLoadIconsForPage.checked
          property alias touchIconsEnabled: touchIconsEnabled.checked
      }

      Action {
          shortcut: "Ctrl+D"
          onTriggered: {
              downloadView.visible = !downloadView.visible;
          }
      }
      Action {
          id: focus
          shortcut: "Ctrl+L"
          onTriggered: {
              addressBar.forceActiveFocus();
              addressBar.selectAll();
          }
      }
      Action {
          shortcut: StandardKey.Refresh
          onTriggered: {
              if (currentWebView)
                  currentWebView.reload();
          }
      }
      Action {
          shortcut: StandardKey.AddTab
          onTriggered: {
              tabs.createEmptyTab(currentWebView.profile);
              tabs.currentIndex = tabs.count - 1;
              addressBar.forceActiveFocus();
              addressBar.selectAll();
          }
      }
      Action {
          shortcut: StandardKey.Close
          onTriggered: {
              currentWebView.triggerWebAction(WebEngineView.RequestClose);
          }
      }
      Action {
          shortcut: "Escape"
          onTriggered: {
              if (currentWebView.state == "FullScreen") {
                  browserWindow.visibility = browserWindow.previousVisibility;
                  fullScreenNotification.hide();
                  currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
              }
          }
      }
      Action {
          shortcut: "Ctrl+0"
          onTriggered: currentWebView.zoomFactor = 1.0
      }
      Action {
          shortcut: StandardKey.ZoomOut
          onTriggered: currentWebView.zoomFactor -= 0.1
      }
      Action {
          shortcut: StandardKey.ZoomIn
          onTriggered: currentWebView.zoomFactor += 0.1
      }

      Action {
          shortcut: StandardKey.Copy
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Copy)
      }
      Action {
          shortcut: StandardKey.Cut
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Cut)
      }
      Action {
          shortcut: StandardKey.Paste
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Paste)
      }
      Action {
          shortcut: "Shift+"+StandardKey.Paste
          onTriggered: currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle)
      }
      Action {
          shortcut: StandardKey.SelectAll
          onTriggered: currentWebView.triggerWebAction(WebEngineView.SelectAll)
      }
      Action {
          shortcut: StandardKey.Undo
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Undo)
      }
      Action {
          shortcut: StandardKey.Redo
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Redo)
      }
      Action {
          shortcut: StandardKey.Back
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Back)
      }
      Action {
          shortcut: StandardKey.Forward
          onTriggered: currentWebView.triggerWebAction(WebEngineView.Forward)
      }

      toolBar: ToolBar {
          id: navigationBar
              RowLayout {
                  anchors.fill: parent
                  ToolButton {
                      enabled: currentWebView && (currentWebView.canGoBack || currentWebView.canGoForward)
                      menu:Menu {
                          id: historyMenu

                          Instantiator {
                              model: currentWebView && currentWebView.navigationHistory.items
                              MenuItem {
                                  text: model.title
                                  onTriggered: currentWebView.goBackOrForward(model.offset)
                                  checkable: !enabled
                                  checked: !enabled
                                  enabled: model.offset
                              }

                              onObjectAdded: historyMenu.insertItem(index, object)
                              onObjectRemoved: historyMenu.removeItem(object)
                          }
                      }
                  }

                  ToolButton {
                      id: backButton
                      iconSource: "icons/go-previous.png"
                      onClicked: currentWebView.goBack()
                      enabled: currentWebView && currentWebView.canGoBack
                      activeFocusOnTab: !browserWindow.platformIsMac
                  }
                  ToolButton {
                      id: forwardButton
                      iconSource: "icons/go-next.png"
                      onClicked: currentWebView.goForward()
                      enabled: currentWebView && currentWebView.canGoForward
                      activeFocusOnTab: !browserWindow.platformIsMac
                  }
                  ToolButton {
                      id: reloadButton
                      iconSource: currentWebView && currentWebView.loading ? "icons/process-stop.png" : "icons/view-refresh.png"
                      onClicked: currentWebView && currentWebView.loading ? currentWebView.stop() : currentWebView.reload()
                      activeFocusOnTab: !browserWindow.platformIsMac
                  }
                  TextField {
                      id: addressBar
                      Image {
                          anchors.verticalCenter: addressBar.verticalCenter;
                          x: 5
                          z: 2
                          id: faviconImage
                          width: 16; height: 16
                          sourceSize: Qt.size(width, height)
                          source: currentWebView && currentWebView.icon
                      }
                      style: TextFieldStyle {
                          padding {
                              left: 26;
                          }
                      }
                      focus: true
                      Layout.fillWidth: true
                      text: currentWebView && currentWebView.url
                      onAccepted: currentWebView.url = utils.fromUserInput(text)
                  }
                  ToolButton {
                      id: settingsMenuButton
                      menu: Menu {
                          MenuItem {
                              id: loadImages
                              text: "Autoload images"
                              checkable: true
                              checked: WebEngine.settings.autoLoadImages
                          }
                          MenuItem {
                              id: javaScriptEnabled
                              text: "JavaScript On"
                              checkable: true
                              checked: WebEngine.settings.javascriptEnabled
                          }
                          MenuItem {
                              id: errorPageEnabled
                              text: "ErrorPage On"
                              checkable: true
                              checked: WebEngine.settings.errorPageEnabled
                          }
                          MenuItem {
                              id: pluginsEnabled
                              text: "Plugins On"
                              checkable: true
                              checked: true
                          }
                          MenuItem {
                              id: fullScreenSupportEnabled
                              text: "FullScreen On"
                              checkable: true
                              checked: WebEngine.settings.fullScreenSupportEnabled
                          }
                          MenuItem {
                              id: offTheRecordEnabled
                              text: "Off The Record"
                              checkable: true
                              checked: currentWebView.profile.offTheRecord
                              onToggled: currentWebView.profile = checked ? otrProfile : defaultProfile;
                          }
                          MenuItem {
                              id: httpDiskCacheEnabled
                              text: "HTTP Disk Cache"
                              checkable: !currentWebView.profile.offTheRecord
                              checked: (currentWebView.profile.httpCacheType === WebEngineProfile.DiskHttpCache)
                              onToggled: currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache
                          }
                          MenuItem {
                              id: autoLoadIconsForPage
                              text: "Icons On"
                              checkable: true
                              checked: WebEngine.settings.autoLoadIconsForPage
                          }
                          MenuItem {
                              id: touchIconsEnabled
                              text: "Touch Icons On"
                              checkable: true
                              checked: WebEngine.settings.touchIconsEnabled
                              enabled: autoLoadIconsForPage.checked
                          }
                      }
                  }
              }
              ProgressBar {
                  id: progressBar
                  height: 3
                  anchors {
                      left: parent.left
                      top: parent.bottom
                      right: parent.right
                      leftMargin: -parent.leftMargin
                      rightMargin: -parent.rightMargin
                  }
                  style: ProgressBarStyle {
                      background: Item {}
                  }
                  z: -2;
                  minimumValue: 0
                  maximumValue: 100
                  value: (currentWebView && currentWebView.loadProgress < 100) ? currentWebView.loadProgress : 0
              }
      }

      TabView {
          id: tabs
          function createEmptyTab(profile) {
              var tab = addTab("", tabComponent);
              // We must do this first to make sure that tab.active gets set so that tab.item gets instantiated immediately.
              tab.active = true;
              tab.title = Qt.binding(function() { return tab.item.title });
              tab.item.profile = profile;
              return tab;
          }

          anchors.fill: parent
          Component.onCompleted: createEmptyTab(defaultProfile)

          Component {
              id: tabComponent
              WebEngineView {
                  id: webEngineView
                  focus: true

                  onLinkHovered: {
                      if (hoveredUrl == "")
                          resetStatusText.start();
                      else {
                          resetStatusText.stop();
                          statusText.text = hoveredUrl;
                      }
                  }

                  states: [
                      State {
                          name: "FullScreen"
                          PropertyChanges {
                              target: tabs
                              frameVisible: false
                              tabsVisible: false
                          }
                          PropertyChanges {
                              target: navigationBar
                              visible: false
                          }
                      }
                  ]
                  settings.autoLoadImages: appSettings.autoLoadImages
                  settings.javascriptEnabled: appSettings.javaScriptEnabled
                  settings.errorPageEnabled: appSettings.errorPageEnabled
                  settings.pluginsEnabled: appSettings.pluginsEnabled
                  settings.fullScreenSupportEnabled: appSettings.fullScreenSupportEnabled
                  settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage
                  settings.touchIconsEnabled: appSettings.touchIconsEnabled

                  onCertificateError: {
                      error.defer();
                      sslDialog.enqueue(error);
                  }

                  onNewViewRequested: {
                      if (!request.userInitiated)
                          print("Warning: Blocked a popup window.");
                      else if (request.destination == WebEngineView.NewViewInTab) {
                          var tab = tabs.createEmptyTab(currentWebView.profile);
                          tabs.currentIndex = tabs.count - 1;
                          request.openIn(tab.item);
                      } else if (request.destination == WebEngineView.NewViewInBackgroundTab) {
                          var backgroundTab = tabs.createEmptyTab(currentWebView.profile);
                          request.openIn(backgroundTab.item);
                      } else if (request.destination == WebEngineView.NewViewInDialog) {
                          var dialog = applicationRoot.createDialog(currentWebView.profile);
                          request.openIn(dialog.currentWebView);
                      } else {
                          var window = applicationRoot.createWindow(currentWebView.profile);
                          request.openIn(window.currentWebView);
                      }
                  }

                  onFullScreenRequested: {
                      if (request.toggleOn) {
                          webEngineView.state = "FullScreen";
                          browserWindow.previousVisibility = browserWindow.visibility;
                          browserWindow.showFullScreen();
                          fullScreenNotification.show();
                      } else {
                          webEngineView.state = "";
                          browserWindow.visibility = browserWindow.previousVisibility;
                          fullScreenNotification.hide();
                      }
                      request.accept();
                  }

                  onRenderProcessTerminated: {
                      var status = "";
                      switch (terminationStatus) {
                      case WebEngineView.NormalTerminationStatus:
                          status = "(normal exit)";
                          break;
                      case WebEngineView.AbnormalTerminationStatus:
                          status = "(abnormal exit)";
                          break;
                      case WebEngineView.CrashedTerminationStatus:
                          status = "(crashed)";
                          break;
                      case WebEngineView.KilledTerminationStatus:
                          status = "(killed)";
                          break;
                      }

                      print("Render process exited with code " + exitCode + " " + status);
                      reloadTimer.running = true;
                  }

                  onWindowCloseRequested: {
                      if (tabs.count == 1)
                          browserWindow.close();
                      else
                          tabs.removeTab(tabs.currentIndex);
                  }

                  Timer {
                      id: reloadTimer
                      interval: 0
                      running: false
                      repeat: false
                      onTriggered: currentWebView.reload()
                  }
              }
          }
      }
      MessageDialog {
          id: sslDialog

          property var certErrors: []
          icon: StandardIcon.Warning
          standardButtons: StandardButton.No | StandardButton.Yes
          title: "Server's certificate not trusted"
          text: "Do you wish to continue?"
          detailedText: "If you wish so, you may continue with an unverified certificate. " +
                        "Accepting an unverified certificate means " +
                        "you may not be connected with the host you tried to connect to.\n" +
                        "Do you wish to override the security check and continue?"
          onYes: {
              certErrors.shift().ignoreCertificateError();
              presentError();
          }
          onNo: reject()
          onRejected: reject()

          function reject(){
              certErrors.shift().rejectCertificate();
              presentError();
          }
          function enqueue(error){
              certErrors.push(error);
              presentError();
          }
          function presentError(){
              visible = certErrors.length > 0
          }
      }

      FullScreenNotification {
          id: fullScreenNotification
      }

      DownloadView {
          id: downloadView
          visible: false
          anchors.fill: parent
      }

      function onDownloadRequested(download) {
          downloadView.visible = true;
          downloadView.append(download);
          download.accept();
      }

      Rectangle {
          id: statusBubble
          color: "oldlace"
          property int padding: 8

          anchors.left: parent.left
          anchors.bottom: parent.bottom
          width: statusText.paintedWidth + padding
          height: statusText.paintedHeight + padding

          Text {
              id: statusText
              anchors.centerIn: statusBubble
              elide: Qt.ElideMiddle

              Timer {
                  id: resetStatusText
                  interval: 750
                  onTriggered: statusText.text = ""
              }
          }
      }
  }