├── .appveyor.yml ├── .travis.yml ├── README.md ├── deploy ├── icon.png └── qml-browser.desktop ├── docs ├── docs │ └── index.md └── mkdocs.yml ├── examples ├── google │ ├── logo.png │ └── main.qml └── live-editor │ └── main.qml ├── history.cpp ├── history.hpp ├── images ├── go-jump.png ├── go-next.png ├── go-previous.png └── view-refresh.png ├── main.cpp ├── mainwindow.cpp ├── mainwindow.hpp ├── mainwindow.ui ├── qml-browser.pro ├── qml ├── LinkArea.qml ├── TextLink.qml ├── WelcomePage.qml └── import.qml ├── resources.qrc ├── windowapi.cpp └── windowapi.hpp /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{branch}-{build}' 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | init: 8 | # Create our AppVeyor version 9 | - ps: $env:commit = $env:appveyor_repo_commit.SubString(0,7) 10 | - ps: Update-AppveyorBuild -Version ("{0}-{1}-{2}" -f $env:appveyor_repo_branch,$env:appveyor_build_number,$env:commit ) 11 | 12 | 13 | environment: 14 | VSVER: 2017 15 | matrix: 16 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 17 | MSVC_VERSION: 17 18 | RUNTIME_LINKAGE: static 19 | QT_VERSION: 5.12.5 20 | QT_LINKAGE: static 21 | COVERITY_BUILD_CANDIDATE: True 22 | #https://www.appveyor.com/docs/build-environment/#qt 23 | QTDIR: C:\Qt\5.12.5\msvc2017_64 24 | PRJLIBS: VCRUNTIME140.dll MSVCP140.dll 25 | QML_PLUGINS: QtQml QtQuick QtQuick.2 QtMultimedia QtGraphicalEffects QtWebEngine 26 | 27 | configuration: 28 | - release 29 | - debug 30 | 31 | install: 32 | # Set some vars 33 | - '%QTDIR%\bin\qtenv2.bat' 34 | - qmake -v 35 | - set PRJLIBDIR=%WINDIR%\SysWOW64 # Find vclibs 36 | - set QTDIR 37 | - if %QTDIR:_64=%==%QTDIR% ( set ARCH=x86 ) else set ARCH=x64 38 | - if %QTDIR:msvc=%==%QTDIR% g++ --version 39 | - if %QTDIR:msvc=%==%QTDIR% set make=mingw32-make.exe 40 | - if %QTDIR:msvc=%==%QTDIR% %make% --version 41 | - if not %QTDIR:msvc=%==%QTDIR% call "%ProgramFiles(x86)%\Microsoft Visual Studio\%VSVER%\Community\VC\Auxiliary\Build\vcvarsall.bat" %ARCH% 42 | - if not %QTDIR:msvc=%==%QTDIR% set make=nmake.exe 43 | - if not %QTDIR:msvc=%==%QTDIR% %make% /? > nul 44 | - set BIN=qml-browser 45 | 46 | before_build: 47 | # Prepare the out-of-source build directory. 48 | - cd %APPVEYOR_BUILD_FOLDER% 49 | - mkdir %APPVEYOR_BUILD_FOLDER%-build 50 | - qmake -o %APPVEYOR_BUILD_FOLDER%-build -r -Wall -Wlogic -Wparser CONFIG+=%CONFIGURATION% %APPVEYOR_BUILD_FOLDER% 51 | 52 | build_script: 53 | # Compile it and check for .exe 54 | - cd %APPVEYOR_BUILD_FOLDER%-build 55 | - '%make%' 56 | - dir /b /s *.exe 57 | 58 | after_build: 59 | # Add a link to the build output within the source directory. This is needed because AppVeyor does 60 | # not support extracting artifacts from out-of-source build directories. See 'artifacts' below. 61 | - dir /b /s *.exe 62 | - mkdir deploy 63 | - copy %APPVEYOR_BUILD_FOLDER%-build\%CONFIGURATION%\%BIN%.exe deploy\%BIN%.exe 64 | - windeployqt --qmldir %APPVEYOR_BUILD_FOLDER%/qml --%CONFIGURATION% deploy/%BIN%.exe --verbose=2 65 | - for %%I in (%PRJLIBS%) do copy %PRJLIBDIR%\%%I deploy\ 66 | - for %%I in (%QML_PLUGINS%) do xcopy /E /I /Y "%QTDIR%\qml\%%I" deploy\%%I 67 | - 7z a -tzip %BIN%_%CONFIGURATION%.zip deploy -r 68 | - copy %APPVEYOR_BUILD_FOLDER%-build\%BIN%_%CONFIGURATION%.zip %APPVEYOR_BUILD_FOLDER%\%BIN%_%CONFIGURATION%.zip 69 | - dir /b /s *.zip 70 | 71 | artifacts: 72 | - path: '%BIN%_%CONFIGURATION%.zip' 73 | name: '%BIN%' 74 | 75 | deploy: 76 | - provider: GitHub 77 | release: continuous 78 | artifact: '%BIN%' 79 | draft: false 80 | prerelease: true 81 | auth_token: 82 | secure: ug9glRaJ9gHQBVIfkT4TdE7bBd9Ff1QhWsce3oebN54o4BUaK0NLDQEqjpZvkvIn 83 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: cpp 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | env: 9 | global: 10 | - BIN=qml-browser 11 | 12 | matrix: 13 | include: 14 | - os: linux 15 | dist: xenial 16 | sudo: require 17 | addons: 18 | apt: 19 | sources: 20 | # Add ppa for gcc-7 21 | - sourceline: 'ppa:ubuntu-toolchain-r/test' 22 | # Set ppa to get a modern version of qt 23 | - sourceline: 'ppa:beineri/opt-qt-5.12.3-xenial' 24 | packages: 25 | - g++-7 26 | - git 27 | env: 28 | - APPIMAGE="true" 29 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 30 | 31 | - os: osx 32 | osx_image: xcode10.1 33 | compiler: clang 34 | env: 35 | - DMG="true" 36 | - MATRIX_EVAL="COMPILER=clang++" 37 | 38 | install: 39 | - if [ ! -z "${APPIMAGE}" ]; then 40 | export QTV=qt512; 41 | sudo apt update -qq; 42 | echo "Install qt libraries"; 43 | sudo apt install build-essential ${QTV}base 44 | ${QTV}quickcontrols ${QTV}quickcontrols2 45 | ${QTV}webengine ${QTV}multimedia 46 | ${QTV}graphicaleffects ${QTV}svg ${QTV}scxml libgl1-mesa-dev; 47 | export ARTIFACT="$BIN*.AppImage"; 48 | fi 49 | - if [ ! -z "${DMG}" ]; then 50 | echo "Download last version of Qt with brew"; 51 | brew update > /dev/null; 52 | brew install qt5; 53 | chmod -R 755 /usr/local/opt/qt5/*; 54 | export QTDIR="/usr/local/opt/qt5"; 55 | export PATH="$QTDIR/bin:$PATH"; 56 | export ARTIFACT="$BIN*.dmg"; 57 | fi 58 | 59 | script: 60 | - eval "${MATRIX_EVAL}" 61 | - if [ ! -z "${APPIMAGE}" ]; then 62 | echo "Load qt env"; 63 | source /opt/qt*/bin/qt*-env.sh; 64 | echo "Build project"; 65 | mkdir build && cd build; 66 | qmake ..; 67 | make; 68 | echo "Create deploy folder"; 69 | mkdir deploy; 70 | cp ../deploy/icon.png deploy/; 71 | cp ../deploy/$BIN.desktop deploy/; 72 | mv $BIN deploy/; 73 | echo "Donwload and run linuxdeployqt"; 74 | wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage"; 75 | chmod a+x linuxdeployqt*.AppImage; 76 | unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH; 77 | mkdir -p deploy/usr/share/doc/libc6/; 78 | cp /usr/share/doc/libc6/copyright deploy/usr/share/doc/libc6/copyright; 79 | ./linuxdeployqt*.AppImage deploy/$BIN.desktop -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=iconengines,imageformats -verbose=2 -qmldir=../qml -appimage; 80 | mv $BIN*.AppImage $BIN.AppImage; 81 | fi 82 | - if [ ! -z "${DMG}" ]; then 83 | echo "Build project"; 84 | mkdir build && cd build; 85 | qmake ..; 86 | make; 87 | echo "Run macdeployqt"; 88 | macdeployqt $BIN.app/ -qmldir=../qml -dmg; 89 | find . | grep dmg; 90 | fi 91 | 92 | after_success: 93 | - if [ "${TRAVIS_PULL_REQUEST}" != false ] || [ "${TRAVIS_BRANCH}" != "master" ]; then 94 | echo "This is not the master branch, no deploy will be done"; 95 | travis_terminate 0; 96 | fi 97 | - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh 98 | - if [ ! -z "${APPIMAGE}" ]; then bash ./upload.sh $ARTIFACT; fi 99 | - if [ ! -z "${DMG}" ]; then bash ./upload.sh $ARTIFACT; fi 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QML-Browser 2 | 3 | QML-Browser. as the name suggests, is a browser for QML files like any other HTML(Web) Browser. 4 | 5 | ## Features 6 | 7 | * Basic navigation: back, previous, reload 8 | * JavaScript API like standard Web APIs 9 | 10 | ## Installing 11 | 12 | ### From source 13 | 14 | ```sh 15 | $ qmake 16 | $ make 17 | ``` 18 | 19 | ### Binary 20 | 21 | - :computer: [Windows](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser_release.zip) 22 | - :apple: [MacOS](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser.dmg) 23 | - :penguin: [Linux](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser.AppImage) 24 | 25 | ## Running 26 | 27 | You need to serve QML files using any normal webserver. For example: 28 | 29 | ``` 30 | cd examples/google 31 | python3 -m http.server 32 | ``` 33 | 34 | Run the `qml-browser` and type in the address bar: http://localhost:8000/main.qml 35 | 36 | ## JavaScript API 37 | 38 | ### Navigation 39 | 40 | `window.back()` [method] 41 | 42 | The back method moves back one page in the history 43 | 44 | `window.forward()` [method] 45 | 46 | Moves forward one page in the session history 47 | 48 | `window.location` [property] 49 | 50 | This property sets or returns the entire URL of the current page 51 | 52 | ### Window 53 | 54 | `window.title` [property] 55 | 56 | ### Dialog 57 | 58 | This property sets or returns the title of the current page 59 | 60 | `window.alert(message)` [method] 61 | 62 | Displays an alert dialog with th specified message and an OK button. 63 | 64 | `window.prompt(message, value)` [method] 65 | 66 | Returns the text entered by the user in a prompt dialog. 67 | 68 | `window.confirm(message)` [method] 69 | 70 | Displays a dialog with a message that the user needs to respond to. 71 | 72 | ## QML module 73 | 74 | The QmlBrowser module provides QML types similar to basic Web Elements like links (HTML a tag). 75 | 76 | The QML types can be imported into your application using the following import statement in your .qml file. 77 | 78 | `import QmlBrowser 1.0` 79 | 80 | ### LinkArea 81 | 82 | Identical to `MouseArea`, but with `href` property and a default handler for mouse clicks. 83 | 84 | Example: 85 | 86 | ```qml 87 | LinkArea { 88 | anchors.fill: parent 89 | href: "http://localhost:8000/page2.qml" 90 | } 91 | ``` 92 | 93 | ### TextLink 94 | 95 | Identical to `Text`, but with `href` property and a `LinkArea` inside. 96 | 97 | Example: 98 | 99 | ```qml 100 | TextLink { 101 | text: "Link to page2.qml" 102 | href: "http://localhost:8000/page2.qml" 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /deploy/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/deploy/icon.png -------------------------------------------------------------------------------- /deploy/qml-browser.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=qml-browser 4 | Icon=icon 5 | Exec=qml-browser 6 | Terminal=false 7 | Categories=Network; 8 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | # What is QML Browser? 2 | 3 | QML-Browser, as the name suggests, is a browser for QML files like any other HTML(Web) Browser. 4 | 5 | ## Why? 6 | 7 | Currently, the web is built in HTML, CSS & JavaScript. And that's great, but there are other language that deserve more attention: QML 8 | 9 | ## What is QML? 10 | 11 | QML it is a declarative markup language (like HTML) but instead of being based on XML, it's closer to JSON and supports Javascript for scripting. It can perform everything HTML/CSS can and even more. 12 | 13 | ## QML vs HTML 14 | 15 | Recently, I came across a few articles [1, 2, 3] comparing Qt's modern UI framework - Qt Quick and its own declarative language QML to HTML, with QML coming out as a clear winner in several categories: 16 | 17 | * Speed of learning 18 | * Ease of use 19 | * Performance 20 | * Cross-platform compatibility 21 | 22 | # Features 23 | 24 | * Basic navigation: back, previous, reload 25 | * JavaScript API like standard Web APIs 26 | 27 | ## Installing 28 | 29 | ### From source 30 | 31 | ```sh 32 | $ qmake 33 | $ make 34 | ``` 35 | 36 | ### Binary 37 | 38 | - :computer: [Windows](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser_release.zip) 39 | - :apple: [MacOS](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser.dmg) 40 | - :penguin: [Linux](https://github.com/gustavosbarreto/qml-browser/releases/download/continuous/qml-browser.AppImage) 41 | 42 | ## Running 43 | 44 | You need to serve QML files using any normal webserver. For example: 45 | 46 | ``` 47 | cd examples/google 48 | python3 -m http.server 49 | ``` 50 | 51 | Run the `qml-browser` and type in the address bar: http://localhost:8000/main.qml 52 | 53 | ## JavaScript API 54 | 55 | ### Navigation 56 | 57 | `window.back()` [method] 58 | 59 | The back method moves back one page in the history 60 | 61 | `window.forward()` [method] 62 | 63 | Moves forward one page in the session history 64 | 65 | `window.location` [property] 66 | 67 | This property sets or returns the entire URL of the current page 68 | 69 | ### Window 70 | 71 | `window.title` [property] 72 | 73 | ### Dialog 74 | 75 | This property sets or returns the title of the current page 76 | 77 | `window.alert(message)` [method] 78 | 79 | Displays an alert dialog with th specified message and an OK button. 80 | 81 | `window.prompt(message, value)` [method] 82 | 83 | Returns the text entered by the user in a prompt dialog. 84 | 85 | `window.confirm(message)` [method] 86 | 87 | Displays a dialog with a message that the user needs to respond to. 88 | 89 | ## QML module 90 | 91 | The QmlBrowser module provides QML types similar to basic Web Elements like links (HTML a tag). 92 | 93 | The QML types can be imported into your application using the following import statement in your .qml file. 94 | 95 | `import QmlBrowser 1.0` 96 | 97 | ### LinkArea 98 | 99 | Identical to `MouseArea`, but with `href` property and a default handler for mouse clicks. 100 | 101 | Example: 102 | 103 | ```qml 104 | LinkArea { 105 | anchors.fill: parent 106 | href: "http://localhost:8000/page2.qml" 107 | } 108 | ``` 109 | 110 | ### TextLink 111 | 112 | Identical to `Text`, but with `href` property and a `LinkArea` inside. 113 | 114 | Example: 115 | 116 | ```qml 117 | TextLink { 118 | text: "Link to page2.qml" 119 | href: "http://localhost:8000/page2.qml" 120 | } 121 | ``` 122 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: QML Browser 2 | nav: 3 | - What is QML Browser?: index.md 4 | theme: material 5 | markdown_extensions: 6 | - pymdownx.arithmatex 7 | - pymdownx.betterem: 8 | smart_enable: all 9 | - pymdownx.caret 10 | - pymdownx.critic 11 | - pymdownx.details 12 | - pymdownx.emoji: 13 | emoji_generator: !!python/name:pymdownx.emoji.to_svg 14 | - pymdownx.inlinehilite 15 | - pymdownx.magiclink 16 | - pymdownx.mark 17 | - pymdownx.smartsymbols 18 | - pymdownx.superfences 19 | - pymdownx.tasklist: 20 | custom_checkbox: true 21 | - pymdownx.tilde 22 | 23 | -------------------------------------------------------------------------------- /examples/google/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/examples/google/logo.png -------------------------------------------------------------------------------- /examples/google/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.6 2 | import QtQuick.Layouts 1.12 3 | 4 | Rectangle { 5 | Component.onCompleted: window.title = "Google Loves QML" 6 | 7 | Column { 8 | anchors.centerIn: parent 9 | spacing: 8 10 | 11 | Image { 12 | source: "logo.png" 13 | anchors.horizontalCenter: parent.horizontalCenter 14 | } 15 | 16 | Rectangle { 17 | width: 400 18 | height: 30 19 | border.color: "#ccc" 20 | border.width: 1 21 | radius: 12 22 | 23 | TextInput { 24 | anchors.fill: parent 25 | anchors.leftMargin: 8 26 | anchors.rightMargin: 8 27 | topPadding: 8 28 | clip: true 29 | focus: true 30 | } 31 | } 32 | 33 | Row { 34 | anchors.horizontalCenter: parent.horizontalCenter 35 | 36 | height: 30 37 | spacing: 8 38 | 39 | Rectangle { 40 | color: "#eee" 41 | height: 30 42 | width: 180 43 | 44 | Text { 45 | text: "Pesquisar" 46 | anchors.centerIn: parent 47 | } 48 | 49 | MouseArea { 50 | anchors.fill: parent 51 | cursorShape: Qt.PointingHandCursor 52 | onClicked: window.alert("not implemented yet") 53 | } 54 | } 55 | 56 | Rectangle { 57 | color: "#eee" 58 | height: 30 59 | width: 180 60 | 61 | Text { 62 | text: "Estou com sorte" 63 | anchors.centerIn: parent 64 | } 65 | 66 | MouseArea { 67 | anchors.fill: parent 68 | cursorShape: Qt.PointingHandCursor 69 | onClicked: window.alert("not implemented yet") 70 | } 71 | } 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/live-editor/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.6 2 | 3 | Rectangle { 4 | width: 800 5 | height: 600 6 | 7 | focus: true 8 | 9 | color: "#fff" 10 | 11 | Timer { 12 | id: evalTimer 13 | interval: 500 14 | 15 | onTriggered: { 16 | if (playground.program != textEditor.text) { 17 | playground.program = textEditor.text; 18 | var playgroundItem = Qt.createQmlObject("import QtQuick 2.6\n"+textEditor.text, playground, "playgroundItem"); 19 | if (playgroundItem) { 20 | if (playground.item) 21 | playground.item.destroy(); 22 | 23 | playground.item = playgroundItem; 24 | } 25 | } 26 | } 27 | } 28 | 29 | Rectangle { 30 | id: textEditorRect 31 | color: Qt.rgba(0, 0, 0, 0.7) 32 | width: parent.width/2 33 | height: parent.height 34 | 35 | TextEdit { 36 | id: textEditor 37 | focus: true 38 | 39 | anchors.fill: parent 40 | anchors.leftMargin: 10 41 | anchors.rightMargin: 10 42 | anchors.topMargin: 10 43 | anchors.bottomMargin: 10 44 | 45 | color: "#fff" 46 | 47 | font.pixelSize: 18 48 | 49 | Keys.onPressed: { 50 | evalTimer.restart(); 51 | } 52 | 53 | Keys.onTabPressed: { 54 | insert(" "); 55 | } 56 | 57 | function insert(s) { 58 | var before = text.slice(0, cursorPosition); 59 | var after = text.slice(cursorPosition); 60 | var pos = cursorPosition; 61 | text = before + s + after; 62 | cursorPosition = pos + s.length; 63 | } 64 | } 65 | } 66 | 67 | Item { 68 | id: playground 69 | 70 | property string program 71 | property variant item 72 | 73 | anchors.left: textEditorRect.right 74 | anchors.right: parent.right 75 | anchors.top: parent.top 76 | anchors.bottom: parent.bottom 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /history.cpp: -------------------------------------------------------------------------------- 1 | #include "history.hpp" 2 | 3 | History::History(QObject *parent) : QObject(parent) 4 | { 5 | } 6 | 7 | void History::append(const QString &url) 8 | { 9 | if (m_index > 0) { 10 | while (m_history.size() > m_index + 1) { 11 | m_history.removeLast(); 12 | } 13 | } 14 | 15 | m_index++; 16 | m_history << url; 17 | } 18 | 19 | void History::back() 20 | { 21 | if (m_index > 0) { 22 | emit updated(m_history[--m_index]); 23 | } 24 | } 25 | 26 | void History::forward() 27 | { 28 | if (m_index < m_history.size() - 1) { 29 | emit updated(m_history[++m_index]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /history.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HISTORY_HPP 2 | #define HISTORY_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class History : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit History(QObject *parent = nullptr); 15 | 16 | void append(const QString &url); 17 | 18 | signals: 19 | void updated(const QString &url); 20 | 21 | public slots: 22 | void back(); 23 | void forward(); 24 | 25 | private: 26 | QStringList m_history; 27 | int m_index = -1; 28 | }; 29 | 30 | #endif // HISTORY_HPP 31 | -------------------------------------------------------------------------------- /images/go-jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/images/go-jump.png -------------------------------------------------------------------------------- /images/go-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/images/go-next.png -------------------------------------------------------------------------------- /images/go-previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/images/go-previous.png -------------------------------------------------------------------------------- /images/view-refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gustavosbarreto/qml-browser/7aa98e059e93dc4a275177dfa71e9a1146df963c/images/view-refresh.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWindow w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | #include "ui_mainwindow.h" 3 | #include "windowapi.hpp" 4 | #include "history.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | MainWindow::MainWindow(QWidget *parent) 12 | : QMainWindow(parent) 13 | , ui(new Ui::MainWindow) 14 | { 15 | ui->setupUi(this); 16 | 17 | ui->quickWidget->setClearColor(Qt::white); 18 | ui->quickWidget->setSource(QUrl("qrc:/qml/WelcomePage.qml")); 19 | 20 | m_addressLineEdit = new QLineEdit(this); 21 | 22 | QTimer::singleShot(0, this, [=]() { 23 | m_addressLineEdit->setFocus(); 24 | }); 25 | 26 | ui->toolBar->insertWidget(ui->actionGo, m_addressLineEdit); 27 | 28 | connect(m_addressLineEdit, &QLineEdit::returnPressed, this, &MainWindow::loadAddress); 29 | connect(ui->actionGo, &QAction::triggered, this, &MainWindow::loadAddress); 30 | 31 | m_history = new History(this); 32 | 33 | connect(m_history, &History::updated, this, [=](const QString &url) { 34 | QTimer::singleShot(0, this, [=]() { 35 | ui->quickWidget->setSource(QUrl(url)); 36 | m_addressLineEdit->setText(url); 37 | }); 38 | }); 39 | 40 | connect(ui->actionBack, &QAction::triggered, m_history, &History::back); 41 | connect(ui->actionForward, &QAction::triggered, m_history, &History::forward); 42 | connect(ui->actionRefresh, &QAction::triggered, this, [=]() { 43 | const QUrl &url = ui->quickWidget->source(); 44 | ui->quickWidget->engine()->clearComponentCache(); 45 | ui->quickWidget->setSource(QUrl()); 46 | ui->quickWidget->setSource(url); 47 | }); 48 | 49 | m_window = new WindowAPI(this); 50 | m_window->setHistory(m_history); 51 | 52 | connect(m_window, &WindowAPI::locationChanged, this, [=](const QString &location) { 53 | m_addressLineEdit->setText(location); 54 | 55 | // Wait for QML signal handlers in progress 56 | QTimer::singleShot(0, [=]() { 57 | loadAddress(); 58 | }); 59 | }); 60 | 61 | connect(m_window, &WindowAPI::titleChanged, this, [=](const QString &title) { 62 | setWindowTitle(title); 63 | }); 64 | 65 | qmlRegisterType(QUrl("qrc:/qml/TextLink.qml"), "QmlBrowser", 1, 0, "TextLink"); 66 | 67 | injectAPI(); 68 | } 69 | 70 | MainWindow::~MainWindow() 71 | { 72 | delete ui; 73 | } 74 | 75 | void MainWindow::loadAddress() 76 | { 77 | ui->quickWidget->engine()->clearComponentCache(); 78 | ui->quickWidget->setSource(QUrl(m_addressLineEdit->text())); 79 | ui->quickWidget->setFocus(); 80 | m_history->append(ui->quickWidget->source().toString()); 81 | } 82 | 83 | void MainWindow::injectAPI() 84 | { 85 | QQmlContext *context = ui->quickWidget->engine()->rootContext(); 86 | 87 | context->setContextProperty("window", m_window); 88 | } 89 | -------------------------------------------------------------------------------- /mainwindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_HPP 2 | #define MAINWINDOW_HPP 3 | 4 | #include 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { class MainWindow; } 8 | QT_END_NAMESPACE 9 | 10 | class QLineEdit; 11 | 12 | class WindowAPI; 13 | class History; 14 | 15 | class MainWindow : public QMainWindow 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | MainWindow(QWidget *parent = nullptr); 21 | ~MainWindow(); 22 | 23 | private slots: 24 | void loadAddress(); 25 | 26 | private: 27 | void injectAPI(); 28 | 29 | private: 30 | Ui::MainWindow *ui; 31 | 32 | QLineEdit *m_addressLineEdit; 33 | WindowAPI *m_window; 34 | History *m_history; 35 | }; 36 | #endif // MAINWINDOW_HPP 37 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | QQuickWidget::SizeRootObjectToView 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 0 31 | 0 32 | 800 33 | 20 34 | 35 | 36 | 37 | 38 | 39 | 40 | toolBar 41 | 42 | 43 | false 44 | 45 | 46 | 47 | 22 48 | 22 49 | 50 | 51 | 52 | Qt::ToolButtonIconOnly 53 | 54 | 55 | TopToolBarArea 56 | 57 | 58 | false 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | :/images/go-previous.png:/images/go-previous.png 70 | 71 | 72 | Back 73 | 74 | 75 | Back 76 | 77 | 78 | 79 | 80 | 81 | :/images/go-next.png:/images/go-next.png 82 | 83 | 84 | Forward 85 | 86 | 87 | Forward 88 | 89 | 90 | 91 | 92 | 93 | :/images/view-refresh.png:/images/view-refresh.png 94 | 95 | 96 | Refresh 97 | 98 | 99 | Refresh 100 | 101 | 102 | 103 | 104 | 105 | :/images/go-jump.png:/images/go-jump.png 106 | 107 | 108 | Go 109 | 110 | 111 | Go 112 | 113 | 114 | 115 | 116 | 117 | QQuickWidget 118 | QWidget 119 |
QtQuickWidgets/QQuickWidget
120 |
121 |
122 | 123 | 124 | 125 | 126 |
127 | -------------------------------------------------------------------------------- /qml-browser.pro: -------------------------------------------------------------------------------- 1 | QT += core gui quickwidgets webengine 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | CONFIG += c++11 6 | 7 | # The following define makes your compiler emit warnings if you use 8 | # any Qt feature that has been marked deprecated (the exact warnings 9 | # depend on your compiler). Please consult the documentation of the 10 | # deprecated API in order to know how to port your code away from it. 11 | DEFINES += QT_DEPRECATED_WARNINGS 12 | 13 | # You can also make your code fail to compile if it uses deprecated APIs. 14 | # In order to do so, uncomment the following line. 15 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 17 | 18 | SOURCES += \ 19 | history.cpp \ 20 | main.cpp \ 21 | mainwindow.cpp \ 22 | windowapi.cpp 23 | 24 | HEADERS += \ 25 | history.hpp \ 26 | mainwindow.hpp \ 27 | windowapi.hpp 28 | 29 | FORMS += \ 30 | mainwindow.ui 31 | 32 | # Default rules for deployment. 33 | qnx: target.path = /tmp/$${TARGET}/bin 34 | else: unix:!android: target.path = /opt/$${TARGET}/bin 35 | !isEmpty(target.path): INSTALLS += target 36 | 37 | RESOURCES += \ 38 | resources.qrc 39 | -------------------------------------------------------------------------------- /qml/LinkArea.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | MouseArea { 4 | property string href: "" 5 | 6 | onClicked: window.location = href 7 | } 8 | -------------------------------------------------------------------------------- /qml/TextLink.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Text { 4 | property string href: "" 5 | 6 | LinkArea { 7 | href: parent.href 8 | anchors.fill: parent 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /qml/WelcomePage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | Text { 5 | font.pointSize: 32 6 | text: "Welcome to QML-Browser" 7 | anchors.centerIn: parent 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /qml/import.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQml 2.12 3 | import QtQml.Models 2.12 4 | import QtMultimedia 5.8 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls 2.12 7 | import QtQuick.Dialogs 1.3 8 | import QtQuick.Extras 1.4 9 | import QtQuick.Layouts 1.12 10 | import QtQuick.Shapes 1.12 11 | import QtWebEngine 1.8 12 | import QtWebSockets 1.1 13 | 14 | Item { 15 | } 16 | -------------------------------------------------------------------------------- /resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/go-jump.png 4 | images/go-next.png 5 | images/go-previous.png 6 | images/view-refresh.png 7 | qml/TextLink.qml 8 | qml/LinkArea.qml 9 | qml/WelcomePage.qml 10 | 11 | 12 | -------------------------------------------------------------------------------- /windowapi.cpp: -------------------------------------------------------------------------------- 1 | #include "windowapi.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "history.hpp" 9 | 10 | WindowAPI::WindowAPI(QObject *parent) : QObject(parent) 11 | { 12 | } 13 | 14 | void WindowAPI::setHistory(History *history) 15 | { 16 | m_history = history; 17 | 18 | connect(history, &History::updated, [=](const QString &url) { 19 | m_location = url; 20 | }); 21 | } 22 | 23 | void WindowAPI::back() 24 | { 25 | m_history->back(); 26 | } 27 | 28 | void WindowAPI::forward() 29 | { 30 | m_history->back(); 31 | } 32 | 33 | void WindowAPI::alert(const QString &message) 34 | { 35 | QMessageBox::warning((QWidget *)parent(), "Warning", message); 36 | } 37 | 38 | QVariant WindowAPI::prompt(const QString &title, const QVariant &defaultValue) 39 | { 40 | QVariant value = QInputDialog::getText((QWidget *)parent(), m_title, title, QLineEdit::Normal, defaultValue.toString()); 41 | return value; 42 | } 43 | 44 | bool WindowAPI::confirm(const QString &message) 45 | { 46 | return QMessageBox::question((QWidget *)parent(), m_title, message) == QMessageBox::Yes; 47 | } 48 | -------------------------------------------------------------------------------- /windowapi.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WINDOWAPI_HPP 2 | #define WINDOWAPI_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class History; 9 | 10 | class WindowAPI : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(QString location MEMBER m_location NOTIFY locationChanged) 14 | Q_PROPERTY(QString title MEMBER m_title NOTIFY titleChanged) 15 | 16 | public: 17 | explicit WindowAPI(QObject *parent = nullptr); 18 | 19 | void setHistory(History *history); 20 | 21 | signals: 22 | void locationChanged(const QString &location); 23 | void titleChanged(const QString &title); 24 | 25 | public slots: 26 | void back(); 27 | void forward(); 28 | 29 | void alert(const QString &message); 30 | QVariant prompt(const QString &title, const QVariant &defaultValue = ""); 31 | bool confirm(const QString &message); 32 | 33 | private: 34 | History *m_history; 35 | QString m_location; 36 | QString m_title; 37 | }; 38 | 39 | #endif // WINDOWAPI_HPP 40 | --------------------------------------------------------------------------------