summaryrefslogtreecommitdiffstats
path: root/examples/webenginewidgets
diff options
context:
space:
mode:
Diffstat (limited to 'examples/webenginewidgets')
-rw-r--r--examples/webenginewidgets/CMakeLists.txt3
-rw-r--r--examples/webenginewidgets/markdowneditor/CMakeLists.txt58
-rw-r--r--examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.pngbin41883 -> 0 bytes
-rw-r--r--examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc159
-rw-r--r--examples/webenginewidgets/markdowneditor/document.cpp12
-rw-r--r--examples/webenginewidgets/markdowneditor/document.h26
-rw-r--r--examples/webenginewidgets/markdowneditor/main.cpp19
-rw-r--r--examples/webenginewidgets/markdowneditor/mainwindow.cpp147
-rw-r--r--examples/webenginewidgets/markdowneditor/mainwindow.h45
-rw-r--r--examples/webenginewidgets/markdowneditor/mainwindow.ui115
-rw-r--r--examples/webenginewidgets/markdowneditor/markdowneditor.pro32
-rw-r--r--examples/webenginewidgets/markdowneditor/previewpage.cpp17
-rw-r--r--examples/webenginewidgets/markdowneditor/previewpage.h19
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json34
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/default.md12
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/index.html32
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc8
-rw-r--r--examples/webenginewidgets/recipebrowser/CMakeLists.txt71
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKDOWN-LICENSE.txt (renamed from examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKDOWN-LICENSE.txt)0
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKED-LICENSE.txt (renamed from examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKED-LICENSE.txt)0
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/3rdparty/markdown.css (renamed from examples/webenginewidgets/markdowneditor/resources/3rdparty/markdown.css)0
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/3rdparty/marked.js (renamed from examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js)0
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/3rdparty/qt_attribution.json34
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/custom.css28
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/custom.js13
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/burger.html96
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/cupcakes.html75
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/burger.jpgbin0 -> 48882 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/cupcakes.jpgbin0 -> 38653 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/pasta.jpgbin0 -> 42411 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/pizza.jpgbin0 -> 49068 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/skewers.jpgbin0 -> 49246 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/soup.jpgbin0 -> 49028 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/images/steak.jpgbin0 -> 49202 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/pasta.html78
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/pizza.html69
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/skewers.html74
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/soup.html63
-rw-r--r--examples/webenginewidgets/recipebrowser/assets/pages/steak.html88
-rw-r--r--examples/webenginewidgets/recipebrowser/doc/images/recipebrowser.webpbin0 -> 34058 bytes
-rw-r--r--examples/webenginewidgets/recipebrowser/doc/src/recipebrowser.qdoc197
-rw-r--r--examples/webenginewidgets/recipebrowser/document.cpp48
-rw-r--r--examples/webenginewidgets/recipebrowser/document.h36
-rw-r--r--examples/webenginewidgets/recipebrowser/main.cpp (renamed from examples/webenginewidgets/stylesheetbrowser/main.cpp)4
-rw-r--r--examples/webenginewidgets/recipebrowser/mainwindow.cpp153
-rw-r--r--examples/webenginewidgets/recipebrowser/mainwindow.h (renamed from examples/webenginewidgets/stylesheetbrowser/mainwindow.h)14
-rw-r--r--examples/webenginewidgets/recipebrowser/mainwindow.ui106
-rw-r--r--examples/webenginewidgets/recipebrowser/recipebrowser.pro (renamed from examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro)12
-rw-r--r--examples/webenginewidgets/recipebrowser/recipebrowser.qrc15
-rw-r--r--examples/webenginewidgets/recipebrowser/stylesheetdialog.cpp (renamed from examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.cpp)23
-rw-r--r--examples/webenginewidgets/recipebrowser/stylesheetdialog.h (renamed from examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.h)2
-rw-r--r--examples/webenginewidgets/recipebrowser/stylesheetdialog.ui (renamed from examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.ui)0
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/3rdparty/COPYING1
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/3rdparty/qt_attribution.json24
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/3rdparty/view-refresh.pngbin1364 -> 0 bytes
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/CMakeLists.txt53
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/doc/images/stylesheetbrowser.pngbin45161 -> 0 bytes
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/doc/src/stylesheetbrowser.qdoc44
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/mainwindow.cpp122
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/mainwindow.ui133
-rw-r--r--examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.qrc5
-rw-r--r--examples/webenginewidgets/webenginewidgets.pro3
62 files changed, 1276 insertions, 1146 deletions
diff --git a/examples/webenginewidgets/CMakeLists.txt b/examples/webenginewidgets/CMakeLists.txt
index c15089420..cc4a4d6c0 100644
--- a/examples/webenginewidgets/CMakeLists.txt
+++ b/examples/webenginewidgets/CMakeLists.txt
@@ -5,14 +5,13 @@ qt_internal_add_example(contentmanipulation)
qt_internal_add_example(cookiebrowser)
qt_internal_add_example(notifications)
qt_internal_add_example(simplebrowser)
-qt_internal_add_example(stylesheetbrowser)
qt_internal_add_example(push-notifications)
qt_internal_add_example(videoplayer)
if(QT_FEATURE_webengine_geolocation)
qt_internal_add_example(maps)
endif()
if(QT_FEATURE_webengine_webchannel)
- qt_internal_add_example(markdowneditor)
+ qt_internal_add_example(recipebrowser)
endif()
if(QT_FEATURE_webengine_printing_and_pdf)
qt_internal_add_example(printme)
diff --git a/examples/webenginewidgets/markdowneditor/CMakeLists.txt b/examples/webenginewidgets/markdowneditor/CMakeLists.txt
deleted file mode 100644
index 808c69521..000000000
--- a/examples/webenginewidgets/markdowneditor/CMakeLists.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(markdowneditor LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/markdowneditor")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui WebChannel WebEngineWidgets)
-
-qt_add_executable(markdowneditor
- document.cpp document.h
- main.cpp
- mainwindow.cpp mainwindow.h mainwindow.ui
- previewpage.cpp previewpage.h
-)
-
-set_target_properties(markdowneditor PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(markdowneditor PUBLIC
- Qt::Core
- Qt::Gui
- Qt::WebChannel
- Qt::WebEngineWidgets
-)
-
-# Resources:
-set(markdowneditor_resource_files
- "resources/3rdparty/markdown.css"
- "resources/3rdparty/marked.js"
- "resources/default.md"
- "resources/index.html"
-)
-
-qt_add_resources(markdowneditor "markdowneditor"
- PREFIX
- "/"
- BASE
- "resources"
- FILES
- ${markdowneditor_resource_files}
-)
-
-install(TARGETS markdowneditor
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png b/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png
deleted file mode 100644
index 9f456c4db..000000000
--- a/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc b/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc
deleted file mode 100644
index f8c67fd63..000000000
--- a/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \example webenginewidgets/markdowneditor
- \title WebEngine Markdown Editor Example
- \ingroup webengine-widgetexamples
- \brief Demonstrates how to integrate a web engine in a hybrid desktop
- application.
-
- \image markdowneditor-example.png
-
- \e {Markdown Editor} demonstrates how to use QWebChannel and JavaScript
- libraries to provide a rich text preview tool for a custom markup language.
-
- \l{http://daringfireball.net/projects/markdown/}{Markdown} is a lightweight
- markup language with a plain text formatting syntax.
- Some services, such as \l{http://github.com}{github}, acknowledge the
- format, and render the content as rich text when viewed in a browser.
-
- The Markdown Editor main window is split into an editor and a preview area.
- The editor supports the Markdown syntax and is implemented by using
- QPlainTextEdit. The document is rendered as rich text in the preview area,
- which is implemented by using QWebEngineView. To render the text, the
- Markdown text is converted to HTML format with the help of a JavaScript
- library inside the web engine. The preview is updated from the editor
- through QWebChannel.
-
- \include examples-run.qdocinc
-
- \section1 Exposing Document Text
-
- Because we expose the current Markdown text to be rendered to the web engine
- through QWebChannel, we need to somehow make the current text available
- through the Qt metatype system. This is done by using a dedicated
- \c Document class that exposes the document text as a \c{Q_PROPERTY}:
-
- \quotefromfile webenginewidgets/markdowneditor/document.h
- \skipto class Document
- \printto #endif
-
- The \c Document class wraps a QString to be set on the C++ side with
- the \c setText() method and exposes it at runtime as a \c text property
- with a \c textChanged signal.
-
- We define the \c setText method as follows:
-
- \quotefromfile webenginewidgets/markdowneditor/document.cpp
- \skipto Document::setText
- \printuntil
-
- \section1 Previewing Text
-
- We implement our own \c PreviewPage class that publicly inherits from
- \c QWebEnginePage:
-
- \quotefromfile webenginewidgets/markdowneditor/previewpage.h
- \skipto class PreviewPage
- \printto #endif
-
- We reimplement the virtual \c acceptNavigationRequest method to
- stop the page from navigating away from the current document. Instead,
- we redirect external links to the system browser:
-
- \quotefromfile webenginewidgets/markdowneditor/previewpage.cpp
- \skipto acceptNavigationRequest
- \printuntil
-
- \section1 Creating the Main Window
-
- The \c MainWindow class inherits the QMainWindow class:
-
- \quotefromfile webenginewidgets/markdowneditor/mainwindow.h
- \skipto class MainWindow :
- \printto endif
-
- The class declares private slots that match the actions in the menu,
- as well as the \c isModified() helper method.
-
- The actual layout of the main window is specified in a \c .ui file.
- The widgets and actions are available at runtime in the \c ui member
- variable.
-
- \c m_filePath holds the file path to the currently loaded document.
- \c m_content is an instance of the \c Document class.
-
- The actual setup of the different objects is done in the \c MainWindow
- constructor:
-
- \quotefromfile webenginewidgets/markdowneditor/mainwindow.cpp
- \skipto MainWindow::MainWindow
- \printto PreviewPage
-
- The constructor first calls \c setupUi to construct the widgets and menu
- actions according to the UI file. The text editor font is set to one
- with a fixed character width, and the QWebEngineView widget is told not
- to show a context menu.
-
- \printto connect
-
- Here the constructor makes sure our custom
- \c PreviewPage is used by the QWebEngineView instance in \c{ui->preview}.
-
- \printto ui->preview
-
- Here the \c textChanged signal of the editor is connected to a lambda that
- updates the text in \c m_content. This object is then exposed to the JS side
- by \c QWebChannel under the name \c{content}.
-
- \printto connect
-
- Now we can actually load the \e index.html file from the
- resources. For more information about the file, see
- \l{Creating an Index File}.
-
- \printto defaultTextFile
-
- The menu items are connected to the corresponding member slots. The
- \uicontrol Save item is activated or deactivated depending on whether
- the user has edited the content.
-
- \printuntil }
-
- Finally, we load a default document \e default.md from the resources.
-
- \section1 Creating an Index File
-
- \quotefile webenginewidgets/markdowneditor/resources/index.html
-
- In the \e index.html, we load a custom stylesheet and two JavaScript
- libraries. \l{https://bitbucket.org/kevinburke/markdowncss/src/master/}{markdown.css} is
- a markdown-friendly stylesheet created by Kevin Burke.
- \l{https://github.com/chjj/marked}{marked.js} is a markdown parser and
- compiler designed for speed written by Christopher Jeffrey and
- \e qwebchannel.js is part of the \l{QWebChannel} module.
-
- In the \c <body> element we first define a \c placeholder element, and
- make it available as a JavaScript variable. We then define the \c updateText
- helper method that updates the content of \c placeholder with the HTML
- that the JavaScript method \c marked() returns.
-
- Finally, we set up the web channel to access the \c content proxy object
- and make sure that \c updateText() is called whenever \c content.text
- changes.
-
- \section1 Files and Attributions
-
- The example bundles the following code with third-party licenses:
-
- \table
- \row
- \li \l{markdowneditor-marked}{Marked}
- \li MIT License
- \row
- \li \l{markdowneditor-markdowncss}{Markdown.css}
- \li Apache License 2.0
- \endtable
-*/
-
diff --git a/examples/webenginewidgets/markdowneditor/document.cpp b/examples/webenginewidgets/markdowneditor/document.cpp
deleted file mode 100644
index 8ece76509..000000000
--- a/examples/webenginewidgets/markdowneditor/document.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "document.h"
-
-void Document::setText(const QString &text)
-{
- if (text == m_text)
- return;
- m_text = text;
- emit textChanged(m_text);
-}
diff --git a/examples/webenginewidgets/markdowneditor/document.h b/examples/webenginewidgets/markdowneditor/document.h
deleted file mode 100644
index f4eabbdaa..000000000
--- a/examples/webenginewidgets/markdowneditor/document.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef DOCUMENT_H
-#define DOCUMENT_H
-
-#include <QObject>
-#include <QString>
-
-class Document : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged FINAL)
-public:
- explicit Document(QObject *parent = nullptr) : QObject(parent) {}
-
- void setText(const QString &text);
-
-signals:
- void textChanged(const QString &text);
-
-private:
- QString m_text;
-};
-
-#endif // DOCUMENT_H
diff --git a/examples/webenginewidgets/markdowneditor/main.cpp b/examples/webenginewidgets/markdowneditor/main.cpp
deleted file mode 100644
index 98e76bfba..000000000
--- a/examples/webenginewidgets/markdowneditor/main.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "document.h"
-#include "mainwindow.h"
-
-#include <QApplication>
-#include <QFile>
-
-int main(int argc, char *argv[])
-{
- QCoreApplication::setOrganizationName("QtExamples");
- QApplication a(argc, argv);
-
- MainWindow window;
- window.show();
-
- return a.exec();
-}
diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.cpp b/examples/webenginewidgets/markdowneditor/mainwindow.cpp
deleted file mode 100644
index a4ef50a31..000000000
--- a/examples/webenginewidgets/markdowneditor/mainwindow.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "mainwindow.h"
-#include "previewpage.h"
-#include "ui_mainwindow.h"
-
-#include <QFile>
-#include <QFileDialog>
-#include <QFontDatabase>
-#include <QMessageBox>
-#include <QStatusBar>
-#include <QTextStream>
-#include <QWebChannel>
-
-MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
-{
- ui->setupUi(this);
- ui->editor->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
- ui->preview->setContextMenuPolicy(Qt::NoContextMenu);
-
- PreviewPage *page = new PreviewPage(this);
- ui->preview->setPage(page);
-
- connect(ui->editor, &QPlainTextEdit::textChanged,
- [this]() { m_content.setText(ui->editor->toPlainText()); });
-
- QWebChannel *channel = new QWebChannel(this);
- channel->registerObject(QStringLiteral("content"), &m_content);
- page->setWebChannel(channel);
-
- ui->preview->setUrl(QUrl("qrc:/index.html"));
-
- connect(ui->actionNew, &QAction::triggered, this, &MainWindow::onFileNew);
- connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onFileOpen);
- connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onFileSave);
- connect(ui->actionSaveAs, &QAction::triggered, this, &MainWindow::onFileSaveAs);
- connect(ui->actionExit, &QAction::triggered, this, &QWidget::close);
-
- connect(ui->editor->document(), &QTextDocument::modificationChanged,
- ui->actionSave, &QAction::setEnabled);
-
- QFile defaultTextFile(":/default.md");
- defaultTextFile.open(QIODevice::ReadOnly);
- ui->editor->setPlainText(defaultTextFile.readAll());
-}
-
-MainWindow::~MainWindow()
-{
- delete ui;
-}
-
-void MainWindow::openFile(const QString &path)
-{
- QFile f(path);
- if (!f.open(QIODevice::ReadOnly)) {
- QMessageBox::warning(this, windowTitle(),
- tr("Could not open file %1: %2").arg(
- QDir::toNativeSeparators(path), f.errorString()));
- return;
- }
- m_filePath = path;
- ui->editor->setPlainText(f.readAll());
- statusBar()->showMessage(tr("Opened %1").arg(QDir::toNativeSeparators(path)));
-}
-
-bool MainWindow::isModified() const
-{
- return ui->editor->document()->isModified();
-}
-
-void MainWindow::onFileNew()
-{
- if (isModified()) {
- QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(),
- tr("You have unsaved changes. Do you want to create a new document anyway?"));
- if (button != QMessageBox::Yes)
- return;
- }
-
- m_filePath.clear();
- ui->editor->setPlainText(tr("## New document"));
- ui->editor->document()->setModified(false);
-}
-
-void MainWindow::onFileOpen()
-{
- if (isModified()) {
- QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(),
- tr("You have unsaved changes. Do you want to open a new document anyway?"));
- if (button != QMessageBox::Yes)
- return;
- }
-
- QFileDialog dialog(this, tr("Open MarkDown File"));
- dialog.setMimeTypeFilters({"text/markdown"});
- dialog.setAcceptMode(QFileDialog::AcceptOpen);
- if (dialog.exec() == QDialog::Accepted)
- openFile(dialog.selectedFiles().constFirst());
-}
-
-void MainWindow::onFileSave()
-{
- if (m_filePath.isEmpty()) {
- onFileSaveAs();
- return;
- }
-
- QFile f(m_filePath);
- if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QMessageBox::warning(this, windowTitle(),
- tr("Could not write to file %1: %2").arg(
- QDir::toNativeSeparators(m_filePath), f.errorString()));
- return;
- }
- QTextStream str(&f);
- str << ui->editor->toPlainText();
-
- ui->editor->document()->setModified(false);
-
- statusBar()->showMessage(tr("Wrote %1").arg(QDir::toNativeSeparators(m_filePath)));
-}
-
-void MainWindow::onFileSaveAs()
-{
- QFileDialog dialog(this, tr("Save MarkDown File"));
- dialog.setMimeTypeFilters({"text/markdown"});
- dialog.setAcceptMode(QFileDialog::AcceptSave);
- dialog.setDefaultSuffix("md");
- if (dialog.exec() != QDialog::Accepted)
- return;
-
- m_filePath = dialog.selectedFiles().constFirst();
- onFileSave();
-}
-
-void MainWindow::closeEvent(QCloseEvent *e)
-{
- if (isModified()) {
- QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(),
- tr("You have unsaved changes. Do you want to exit anyway?"));
- if (button != QMessageBox::Yes)
- e->ignore();
- }
-}
diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.h b/examples/webenginewidgets/markdowneditor/mainwindow.h
deleted file mode 100644
index 271664852..000000000
--- a/examples/webenginewidgets/markdowneditor/mainwindow.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include "document.h"
-
-#include <QMainWindow>
-#include <QString>
-
-QT_BEGIN_NAMESPACE
-namespace Ui {
-class MainWindow;
-}
-QT_END_NAMESPACE
-
-class MainWindow : public QMainWindow
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
-
- void openFile(const QString &path);
-
-protected:
- void closeEvent(QCloseEvent *e) override;
-
-private slots:
- void onFileNew();
- void onFileOpen();
- void onFileSave();
- void onFileSaveAs();
-
-private:
- bool isModified() const;
-
- Ui::MainWindow *ui;
- QString m_filePath;
- Document m_content;
-};
-
-#endif // MAINWINDOW_H
diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.ui b/examples/webenginewidgets/markdowneditor/mainwindow.ui
deleted file mode 100644
index 36ab352b7..000000000
--- a/examples/webenginewidgets/markdowneditor/mainwindow.ui
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>800</width>
- <height>600</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>MarkDown Editor</string>
- </property>
- <widget class="QWidget" name="centralwidget">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QSplitter" name="splitter">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <widget class="QPlainTextEdit" name="editor"/>
- <widget class="QWebEngineView" name="preview" native="true"/>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QMenuBar" name="menubar">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>800</width>
- <height>26</height>
- </rect>
- </property>
- <widget class="QMenu" name="menu_File">
- <property name="title">
- <string>&amp;File</string>
- </property>
- <addaction name="actionNew"/>
- <addaction name="actionOpen"/>
- <addaction name="actionSave"/>
- <addaction name="actionSaveAs"/>
- <addaction name="separator"/>
- <addaction name="actionExit"/>
- </widget>
- <addaction name="menu_File"/>
- </widget>
- <widget class="QStatusBar" name="statusbar"/>
- <action name="actionOpen">
- <property name="text">
- <string>&amp;Open...</string>
- </property>
- <property name="toolTip">
- <string>Open document</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+O</string>
- </property>
- </action>
- <action name="actionSave">
- <property name="text">
- <string>&amp;Save</string>
- </property>
- <property name="toolTip">
- <string>Save current document</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+S</string>
- </property>
- </action>
- <action name="actionExit">
- <property name="text">
- <string>E&amp;xit</string>
- </property>
- <property name="toolTip">
- <string>Exit editor</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+Q</string>
- </property>
- </action>
- <action name="actionSaveAs">
- <property name="text">
- <string>Save &amp;As...</string>
- </property>
- <property name="toolTip">
- <string>Save document under different name</string>
- </property>
- </action>
- <action name="actionNew">
- <property name="text">
- <string>&amp;New</string>
- </property>
- <property name="toolTip">
- <string>Create new document</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+N</string>
- </property>
- </action>
- </widget>
- <customwidgets>
- <customwidget>
- <class>QWebEngineView</class>
- <extends>QWidget</extends>
- <header>qwebengineview.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/examples/webenginewidgets/markdowneditor/markdowneditor.pro b/examples/webenginewidgets/markdowneditor/markdowneditor.pro
deleted file mode 100644
index 099edf4b5..000000000
--- a/examples/webenginewidgets/markdowneditor/markdowneditor.pro
+++ /dev/null
@@ -1,32 +0,0 @@
-TEMPLATE = app
-
-QT += webenginewidgets webchannel
-
-HEADERS += \
- mainwindow.h \
- previewpage.h \
- document.h
-
-SOURCES = \
- main.cpp \
- mainwindow.cpp \
- previewpage.cpp \
- document.cpp
-
-RESOURCES = \
- resources/markdowneditor.qrc
-
-# Disable Qt Quick compiler because the example doesn't use QML, but more importantly so that
-# the source code of the .js files is not removed from the embedded qrc file.
-CONFIG -= qtquickcompiler
-
-FORMS += \
- mainwindow.ui
-
-DISTFILES += \
- resources/3rdparty/MARKDOWN-LICENSE.txt \
- resources/3rdparty/MARKED-LICENSE.txt
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/markdowneditor
-INSTALLS += target
diff --git a/examples/webenginewidgets/markdowneditor/previewpage.cpp b/examples/webenginewidgets/markdowneditor/previewpage.cpp
deleted file mode 100644
index 17249fdb0..000000000
--- a/examples/webenginewidgets/markdowneditor/previewpage.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "previewpage.h"
-
-#include <QDesktopServices>
-
-bool PreviewPage::acceptNavigationRequest(const QUrl &url,
- QWebEnginePage::NavigationType /*type*/,
- bool /*isMainFrame*/)
-{
- // Only allow qrc:/index.html.
- if (url.scheme() == QString("qrc"))
- return true;
- QDesktopServices::openUrl(url);
- return false;
-}
diff --git a/examples/webenginewidgets/markdowneditor/previewpage.h b/examples/webenginewidgets/markdowneditor/previewpage.h
deleted file mode 100644
index 4a5d98c7c..000000000
--- a/examples/webenginewidgets/markdowneditor/previewpage.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PREVIEWPAGE_H
-#define PREVIEWPAGE_H
-
-#include <QWebEnginePage>
-
-class PreviewPage : public QWebEnginePage
-{
- Q_OBJECT
-public:
- using QWebEnginePage::QWebEnginePage;
-
-protected:
- bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) override;
-};
-
-#endif // PREVIEWPAGE_H
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json b/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json
deleted file mode 100644
index d51ac744b..000000000
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json
+++ /dev/null
@@ -1,34 +0,0 @@
-[
- {
- "Id": "markdowneditor-marked",
- "Name": "Marked (WebEngine Markdown Editor example)",
- "QDocModule": "qtwebengine",
- "QtUsage": "Marked is used in the WebEngine MarkDown Editor example",
- "QtParts": [ "examples" ],
- "Files": "marked.js",
- "Description": "A full-featured markdown parser and compiler, written in JavaScript. Built for speed.",
- "Homepage": "https://github.com/chjj/marked",
- "Version": "0.4.0",
- "DownloadLocation": "https://github.com/markedjs/marked/blob/0.4.0/lib/marked.js",
- "Copyright": "Copyright (c) 2011-2018, Christopher Jeffrey",
- "License": "MIT License",
- "LicenseId": "MIT",
- "LicenseFile": "MARKED-LICENSE.txt"
- },
- {
- "Id": "markdowneditor-markdowncss",
- "Name": "Markdown.css (WebEngine Markdown Editor example)",
- "QDocModule": "qtwebengine",
- "QtUsage": "markdown.css is used in the WebEngine MarkDown Editor example",
- "QtParts": [ "examples" ],
- "Files": "markdown.css",
- "Description": "Markdown.css is better default styling for your Markdown files.",
- "Version": "188530e4b5d020d7e237fc6b26be13ebf4a8def3",
- "DownloadLocation": "https://bitbucket.org/kevinburke/markdowncss/src/188530e4b5d020d7e237fc6b26be13ebf4a8def3/markdown.css",
- "Copyright": "Copyright 2011 Kevin Burke
- Copyright Twitter Inc.",
- "License": "Apache License 2.0",
- "LicenseId": "Apache-2.0",
- "LicenseFile": "MARKDOWN-LICENSE.txt"
- }
-]
diff --git a/examples/webenginewidgets/markdowneditor/resources/default.md b/examples/webenginewidgets/markdowneditor/resources/default.md
deleted file mode 100644
index af835fa4d..000000000
--- a/examples/webenginewidgets/markdowneditor/resources/default.md
+++ /dev/null
@@ -1,12 +0,0 @@
-## WebEngine Markdown Editor Example
-
-This example uses [QWebEngineView](http://doc.qt.io/qt-5/qwebengineview.html)
-to preview text written using the [Markdown](https://en.wikipedia.org/wiki/Markdown)
-syntax.
-
-### Acknowledgments
-
-The conversion from Markdown to HTML is done with the help of the
-[marked JavaScript library](https://github.com/chjj/marked) by _Christopher Jeffrey_.
-The [style sheet](https://kevinburke.bitbucket.io/markdowncss/)
-was created by _Kevin Burke_.
diff --git a/examples/webenginewidgets/markdowneditor/resources/index.html b/examples/webenginewidgets/markdowneditor/resources/index.html
deleted file mode 100644
index c8e30b49b..000000000
--- a/examples/webenginewidgets/markdowneditor/resources/index.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!doctype html>
-<html lang="en">
-<meta charset="utf-8">
-<head>
- <link rel="stylesheet" type="text/css" href="3rdparty/markdown.css">
- <script src="3rdparty/marked.js"></script>
- <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
-</head>
-<body>
- <div id="placeholder"></div>
- <script>
- 'use strict';
-
- var placeholder = document.getElementById('placeholder');
-
- var updateText = function(text) {
- placeholder.innerHTML = marked.parse(text);
- }
-
- new QWebChannel(qt.webChannelTransport,
- function(channel) {
- var content = channel.objects.content;
- updateText(content.text);
- content.textChanged.connect(updateText);
- }
- );
- </script>
-</body>
-</html>
-
-
-
diff --git a/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc b/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc
deleted file mode 100644
index bc738f1cf..000000000
--- a/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc
+++ /dev/null
@@ -1,8 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>default.md</file>
- <file>index.html</file>
- <file>3rdparty/markdown.css</file>
- <file>3rdparty/marked.js</file>
- </qresource>
-</RCC>
diff --git a/examples/webenginewidgets/recipebrowser/CMakeLists.txt b/examples/webenginewidgets/recipebrowser/CMakeLists.txt
new file mode 100644
index 000000000..d10409c09
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/CMakeLists.txt
@@ -0,0 +1,71 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(recipebrowser LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/recipebrowser")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui WebEngineWidgets)
+
+qt_add_executable(recipebrowser
+ main.cpp
+ mainwindow.cpp mainwindow.h mainwindow.ui
+ stylesheetdialog.cpp stylesheetdialog.h stylesheetdialog.ui
+ document.cpp document.h
+)
+
+set_target_properties(recipebrowser PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(recipebrowser PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::WebEngineWidgets
+)
+
+# Resources:
+set(recipebrowser_resource_files
+ "assets/3rdparty/markdown.css"
+ "assets/3rdparty/marked.js"
+ "assets/custom.css"
+ "assets/custom.js"
+ "assets/pages/burger.html"
+ "assets/pages/cupcakes.html"
+ "assets/pages/images/burger.jpg"
+ "assets/pages/images/cupcakes.jpg"
+ "assets/pages/images/pasta.jpg"
+ "assets/pages/images/pizza.jpg"
+ "assets/pages/images/skewers.jpg"
+ "assets/pages/images/soup.jpg"
+ "assets/pages/images/steak.jpg"
+ "assets/pages/pasta.html"
+ "assets/pages/pizza.html"
+ "assets/pages/skewers.html"
+ "assets/pages/soup.html"
+ "assets/pages/steak.html"
+)
+
+qt_add_resources(recipebrowser "recipebrowser"
+ PREFIX
+ "/"
+ BASE
+ "assets"
+ FILES
+ ${recipebrowser_resource_files}
+)
+
+install(TARGETS recipebrowser
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKDOWN-LICENSE.txt b/examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKDOWN-LICENSE.txt
index 23c52cc43..23c52cc43 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKDOWN-LICENSE.txt
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKDOWN-LICENSE.txt
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKED-LICENSE.txt b/examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKED-LICENSE.txt
index 8e3ba0e0a..8e3ba0e0a 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/MARKED-LICENSE.txt
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/MARKED-LICENSE.txt
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/markdown.css b/examples/webenginewidgets/recipebrowser/assets/3rdparty/markdown.css
index 24fc2ffe2..24fc2ffe2 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/markdown.css
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/markdown.css
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js b/examples/webenginewidgets/recipebrowser/assets/3rdparty/marked.js
index 33c02d9cf..33c02d9cf 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/marked.js
diff --git a/examples/webenginewidgets/recipebrowser/assets/3rdparty/qt_attribution.json b/examples/webenginewidgets/recipebrowser/assets/3rdparty/qt_attribution.json
new file mode 100644
index 000000000..f8b0fd023
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/qt_attribution.json
@@ -0,0 +1,34 @@
+[
+ {
+ "Id" : "recipebrowser-marked",
+ "Name" : "Marked (WebEngine Recipe Browser example)",
+ "QDocModule" : "qtwebengine",
+ "QtUsage" : "Marked is used in the WebEngine Recipe Browser example",
+ "QtParts" : ["examples"],
+ "Files" : "marked.js",
+ "Description" :
+ "A full-featured markdown parser and compiler, written in JavaScript. Built for speed.",
+ "Homepage" : "https://github.com/chjj/marked",
+ "Version" : "0.4.0",
+ "DownloadLocation" : "https://github.com/markedjs/marked/blob/0.4.0/lib/marked.js",
+ "Copyright" : "Copyright (c) 2011-2018, Christopher Jeffrey",
+ "License" : "MIT License",
+ "LicenseId" : "MIT",
+ "LicenseFile" : "MARKED-LICENSE.txt"
+ },
+ {
+ "Id" : "recipebrowser-markdowncss",
+ "Name" : "Markdown.css (WebEngine Recipe Browser example)",
+ "QDocModule" : "qtwebengine",
+ "QtUsage" : "markdown.css is used in the WebEngine Recipe Browser example",
+ "QtParts" : ["examples"],
+ "Files" : "markdown.css",
+ "Description" : "Markdown.css is better default styling for your Markdown files.",
+ "Version" : "188530e4b5d020d7e237fc6b26be13ebf4a8def3",
+ "DownloadLocation" : "https://bitbucket.org/kevinburke/markdowncss/src/188530e4b5d020d7e237fc6b26be13ebf4a8def3/markdown.css",
+ "Copyright" : "Copyright 2011 Kevin Burke Copyright Twitter Inc.",
+ "License" : "Apache License 2.0",
+ "LicenseId" : "Apache-2.0",
+ "LicenseFile" : "MARKDOWN-LICENSE.txt"
+ }
+ ]
diff --git a/examples/webenginewidgets/recipebrowser/assets/custom.css b/examples/webenginewidgets/recipebrowser/assets/custom.css
new file mode 100644
index 000000000..cc1106af3
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/custom.css
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+body {
+ padding-top: 0;
+ margin-top: 0;
+}
+
+#content {
+ display: none;
+}
+
+img {
+ width: 100%;
+ height: 100%;
+}
+
+li {
+ margin-left: 25px;
+}
+
+ol li {
+ margin-bottom: 10px;
+}
+
+* {
+ max-width: 960px !important;
+}
diff --git a/examples/webenginewidgets/recipebrowser/assets/custom.js b/examples/webenginewidgets/recipebrowser/assets/custom.js
new file mode 100644
index 000000000..34470164e
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/custom.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+marked.setOptions({
+ renderer : new marked.Renderer(),
+ gfm : true,
+ tables : true,
+ breaks : false,
+ pedantic : false,
+ sanitize : false,
+ smartLists : true,
+ smartypants : false
+});
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/burger.html b/examples/webenginewidgets/recipebrowser/assets/pages/burger.html
new file mode 100644
index 000000000..99497959f
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/burger.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Insanity Burger</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/burger.jpg" alt="Insanity Burger" title="Insanity Burger" />
+
+Insanity burger
+===============
+
+### Ingredients
+
+* 800 g minced chuck steak
+* olive oil
+* 1 large red onion
+* 1 splash of white wine vinegar
+* 2 large gherkins
+* 4 sesame-topped brioche burger buns
+* 4-8 rashers of smoked streaky bacon
+* 4 teaspoons American mustard
+* Tabasco Chipotle sauce
+* 4 thin slices of Red Leicester cheese
+* 4 teaspoons tomato ketchup
+
+#### For the burger sauce
+* ¼ of an iceberg lettuce
+* 2 heaped tablespoons mayonnaise
+* 1 heaped tablespoon tomato ketchup
+* 1 teaspoon Tabasco Chipotle sauce
+* 1 teaspoon Worcestershire sauce
+* 1 teaspoon brandy, or bourbon (optional)
+
+### Instructions
+For the best burger, go to your butcher’s and ask them to mince 800g of chuck steak for you.
+This cut has a really good balance of fat and flavoursome meat. Divide it into 4 and, with wet
+hands, roll each piece into a ball, then press into flat patties roughly 12cm wide and about 2cm
+wider than your buns. Place on an oiled plate and chill in the fridge. Next, finely slice the red
+onion, then dress in a bowl with the vinegar and a pinch of sea salt. Slice the gherkins and halve
+the buns. Finely chop the lettuce and mix with the rest of the burger sauce ingredients in a bowl,
+then season to taste.
+
+I like to only cook 2 burgers at a time to achieve perfection, so get two pans on the go – a large
+non-stick pan on a high heat for your burgers and another on a medium heat for the bacon. Pat your
+burgers with oil and season them with salt and pepper. Put 2 burgers into the first pan, pressing
+down on them with a fish slice, then put half the bacon into the other pan. After 1 minute, flip
+the burgers and brush each cooked side with ½ a teaspoon of mustard and a dash of Tabasco. After
+another minute, flip onto the mustard side and brush again with another ½ teaspoon of mustard and
+a second dash of Tabasco on the other side. Cook for one more minute, by which point you can place
+some crispy bacon on top of each burger with a slice of cheese. Add a tiny splash of water to the
+pan and place a heatproof bowl over the burgers to melt the cheese – 30 seconds should do it. At the
+same time, toast 2 split buns in the bacon fat in the other pan until lightly golden. Repeat with
+the remaining two burgers.
+
+To build each burger, add a quarter of the burger sauce to the bun base, then top with a cheesy
+bacon burger, a quarter of the onions and gherkins. Rub the bun top with a teaspoon of ketchup,
+then gently press together. As the burger rests, juices will soak into the bun, so serve right
+away, which is great, or for an extra filthy experience, wrap each one in greaseproof paper, then
+give it a minute to go gorgeous and sloppy.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/cupcakes.html b/examples/webenginewidgets/recipebrowser/assets/pages/cupcakes.html
new file mode 100644
index 000000000..e8e14a9b6
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/cupcakes.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Cupcakes</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/cupcakes.jpg" alt="Cupcakes" title="Cupcakes" />
+
+Cupcakes
+=============
+
+### Ingredients
+
+* 300 g caster sugar
+* 150 ml sunflower oil
+* 1 teaspoon vanilla extract
+* 500 g dairy-free soya yoghurt
+* 2 teaspoons cider vinegar
+* 350 g plain flour
+* 1 teaspoon bicarbonate of soda
+* 1½ teaspoons baking powder
+
+#### For the vegan vanilla icing
+* 200 g dairy-free soya spread , chilled
+* 660 g icing sugar
+* ½ teaspoon vanilla extract
+
+### Instructions
+1. Preheat the oven to 170°C fan/375°F/gas 5.
+2. Place the sugar, oil and vanilla extract in a large bowl, then beat with an electric mixer for 1 to 2 minutes, until well combined.
+3. Mix the yoghurt and vinegar together in a bowl, then add to the mixture and beat for 1 to 2 minutes.
+4. Add the remaining cupcake ingredients and 1 teaspoon of fine sea salt, then whisk until smooth and just combined.
+5. Fill the paper cases two-thirds full with mixture, but don’t bother to smooth it out.
+6. Bake for 20 minutes, or until they spring back when touched. Leave to cool, transferring to a wire cooling rack after 5 minutes.
+7. Meanwhile, make the icing. Beat the soya spread with an electric mixer for 1 to 2 minutes, or until smooth.
+8. Sift the icing sugar into a large bowl, then add to the soya spread in two stages, beating well between each.
+9. Add the vanilla extract and a small splash of water, then whisk for a further few minutes, or until silky smooth – if it’s too stiff, add a splash more water to loosen.
+10. Once the cupcakes are cool, decorate them with the icing and add a few sprinkles too if you like – whatever takes your fancy – then enjoy.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/burger.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/burger.jpg
new file mode 100644
index 000000000..edc0c65de
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/burger.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/cupcakes.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/cupcakes.jpg
new file mode 100644
index 000000000..cce52ba23
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/cupcakes.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/pasta.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/pasta.jpg
new file mode 100644
index 000000000..7ac924b79
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/pasta.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/pizza.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/pizza.jpg
new file mode 100644
index 000000000..8d8f756af
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/pizza.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/skewers.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/skewers.jpg
new file mode 100644
index 000000000..6bb2f1172
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/skewers.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/soup.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/soup.jpg
new file mode 100644
index 000000000..fc9dff906
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/soup.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/images/steak.jpg b/examples/webenginewidgets/recipebrowser/assets/pages/images/steak.jpg
new file mode 100644
index 000000000..240b72eb4
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/images/steak.jpg
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/pasta.html b/examples/webenginewidgets/recipebrowser/assets/pages/pasta.html
new file mode 100644
index 000000000..c2b3d840a
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/pasta.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Pasta</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/pasta.jpg" alt="Pasta" title="Pasta" />
+
+Pasta
+=============
+
+### Ingredients
+
+* 2 red peppers , deseeded and sliced
+* 2 yellow peppers , deseeded and sliced
+* extra virgin olive oil
+* sea salt
+* freshly ground black pepper
+* 2 red onions , peeled and finely sliced
+* 2 cloves garlic , peeled and grated
+* 2 handfuls fresh flat-leaf parsley , leaves finely chopped, stalks reserved
+* 2 tablespoons red wine vinegar or balsamic vinegar
+* 2 handfuls Parmesan cheese , grated
+* 2 heaped tablespoons mascarpone cheese or crème fraîche , optional
+* 455 g rigatoni, penne or spaghetti
+
+
+### Instructions
+1. Put all the peppers in a large frying pan over a medium heat with a little olive oil and a pinch of salt and pepper.
+2. Place a lid on, and cook slowly for 15 minutes until softened. Don't rush this too much, as cooking the peppers slowly like this really helps to bring out the flavour.
+3. Add the onion and cook for a further 20 minutes.
+4. Then add the garlic and parsley stalks and toss around, keeping everything moving in the pan.
+5. Cook for about 3 minutes most. Have a little taste, and season with a bit more salt and pepper.
+5. Add the vinegar - it will sizzle away, so give everything a good toss.
+6. Then add one handful of the grated Parmesan and the mascarpone or crème fraîche if you are using it and turn the heat down to minimum while you cook the pasta.
+7. Meanwhile put a large pot of salted water on to boil.
+8. Add the pasta to the boiling water and cook according to the packet instructions.
+9. When cooked, drain in a colander, reserving some of the cooking water.
+10. Put the peppers, pasta and parsley leaves into a large warmed bowl.
+11. Give them a good toss together, then add a little of the pasta cooking water and a few good lugs of extra virgin olive oil to coat the pasta nicely.
+12. Serve straight away sprinkled with the rest of the Parmesan.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/pizza.html b/examples/webenginewidgets/recipebrowser/assets/pages/pizza.html
new file mode 100644
index 000000000..7e390a373
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/pizza.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Pizza Diavola</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/pizza.jpg" alt="Pizza Diavola" title="Pizza Diavola" />
+
+Pizza Diavola
+=============
+
+### Ingredients
+
+* 2 pizza dough balls
+* 100g Mozzarella
+* 400g Passata
+* 200 g Spicy Salami
+* 2 fresh chillies
+* Extra virgin olive oil
+* Salt
+
+### Instructions
+1. Preheat oven to 210 oc.
+2. Get your pizza dough balls ( depending on how many you are making) and roll them out to about 12 inches diameter using your hands in a circular stretching technique or using a rolling pin.
+3. With the dough prepared now get our sauce ready by mixing the passata with three tbsp of the olive oil and a good pinch of salt.
+4. Now slice up your salami into thin-ish slices and also finely chop the fresh chillies.
+5. Next, slice or dice your mozzarella and we are ready to dress the pizza.
+6. Add half the tomato sauce to each base and spread evenly leaving a half inch perimeter.
+7. Add the mozzarella, salami and finish with the chillies.
+8. Finally drizzle with some more olive oil and your are ready to cook your pizza diavola.
+9. Place in the oven for ten minutes or until golden.
+10. Remove from the oven, slice, and enjoy your pizza diavola.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/skewers.html b/examples/webenginewidgets/recipebrowser/assets/pages/skewers.html
new file mode 100644
index 000000000..db2df5472
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/skewers.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Grilled skewers</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/skewers.jpg" alt="Grilled skewers" title="Grilled skewers" />
+
+Grilled skewers
+======================
+
+### Ingredients
+
+* 3 cloves of garlic
+* 200g kefalotyri cheese
+* 2 medium aubergine
+* 1 iceberg lettuce
+* 1 tablespoon coriander seeds
+* 2 teaspoons dried oregano
+* 2 lemons
+* 4 tablespoons olive oil
+* 600g lamb neck fillet , in 2cm chunks
+* 12 fresh bay leaves
+
+### Instructions
+1. Peel and crush the garlic.
+2. Chop the cheese into bite-sized pieces, trim and chop the aubergines into 2cm chunks, and trim and finely slice the lettuce.
+3. Finely crush the coriander seeds in a pestle and mortar and add to a large bowl with the oregano and garlic. Finely grate in the lemon zest (reserve the zested lemons) and stir in the oil.
+4. Season, then add the cheese, lamb, aubergines and bay leaves. Leave to marinate for at least an hour, or overnight if you can.
+5. Meanwhile, pickle your cabbage. Trim, core and finely slice the cabbage, then place in a colander in the sink or over a bowl and toss with 2 teaspoons of sea salt. Cover and set aside for at least 2 hours, then rinse.
+6. Peel and finely slice the onions, then transfer to a bowl along with the cabbage.
+7. Put the remaining ingredients in a pan and bring to a boil. Simmer for 10 minutes, then pour over the cabbage and onions. Transfer to sterilised jars – this will keep for up to 1 month.
+8. For the flatbreads, put the flour, baking powder, buttermilk and half of the sesame seeds in a bowl and mix until everything is combined.
+9. Tip onto a lightly floured surface and knead briefly. Divide into six, using a rolling pin to roll them into 1 to 2mm rounds.
+10. Scatter with the remaining sesame seeds and run the rolling pin over them. Pop the flatbreads onto the hot barbecue for 1 to 2 minutes on each side.
+11. Load 12 skewers with the cheese, lamb, aubergines and bay leaves, then place on the barbecue (not directly over the coals) for 20 to 25 minutes, turning often, until the lamb is medium-rare and the aubergines are cooked.
+12. Serve the lamb with the flatbreads, cabbage, lettuce and lemon wedges.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/soup.html b/examples/webenginewidgets/recipebrowser/assets/pages/soup.html
new file mode 100644
index 000000000..ea51fc8a5
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/soup.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Soup</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/soup.jpg" alt="Soup" title="Soup" />
+
+Soup
+=============
+
+### Ingredients
+
+* 2 potatoes
+* 2 onions
+* 2 cloves of garlic
+* olive oil
+* 400 ml organic stock
+* 3 bunches of watercress
+
+### Instructions
+* Peel and roughly chop the potatoes, onions and garlic.
+* In a large saucepan, heat a little olive oil, then sauté the potato, onion and garlic until the onions are translucent.
+* Add the stock and simmer until the potato is soft. Chop and add the watercress and simmer for a further 3 to 4 minutes.
+* Using a hand blender, liquidise the soup until smooth.
+* Serve with a swirl of crème fraîche and some Fortt’s Bath Oliver biscuits, if you like.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/assets/pages/steak.html b/examples/webenginewidgets/recipebrowser/assets/pages/steak.html
new file mode 100644
index 000000000..26391b409
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/pages/steak.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Grilled steak and rice</title>
+ <link rel="stylesheet" type="text/css" href="../3rdparty/markdown.css">
+ <link rel="stylesheet" type="text/css" href="../custom.css">
+ <script src="../3rdparty/marked.js"></script>
+ <script src="../custom.js"></script>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
+</head>
+<body>
+ <div id="placeholder"></div>
+ <div id="content">
+
+<img src="images/steak.jpg" alt="Grilled steak and rice" title="Grilled steak and rice" />
+
+Grilled steak and rice
+======================
+
+### Ingredients
+
+#### For the ratatouille
+* courgette
+* 1 small aubergine
+* 2 mixed-color peppers
+* 1 red onion
+* 1 heaped teaspoon harissa
+* 2 anchovy fillets
+* 2-4 cloves of garlic
+* 700 g passata
+* 1 tablespoon balsamic vinegar
+* ½ bunch fresh basil
+* 2 tablespoons fat-free natural yoghurt
+
+#### For the rice
+* 1 mug (300g) 10-minute wholegrain or basmati rice
+* 1 good pinch saffron
+* ½ lemon
+
+#### For the steak
+* 2 x 250 g quality sirloin steaks, fat removed
+* 1 teaspoon sweet paprika olive oil
+* ½ bunch fresh flat-leaf parsley
+* 1 heaped teaspoon Dijon mustard
+* 1 tablespoon extra virgin olive oil
+* ½ lemon
+
+### Instructions
+1. Halve the courgette lengthways, slice the aubergine 1cm thick and place both on the griddle pan, turning when charred.
+2. Put 1 mug of rice, 2 mugs of boiling water, the saffron, lemon half and a pinch of salt into the small pan, cover and cook until fluffy, stirring occasionally.
+3. Tear the seeds and stalks out of the peppers, then roughly chop with the peeled red onion and put into the casserole pan with the harissa, anchovies and 1 teaspoon of their oil.
+4. Squash in the unpeeled garlic through a garlic crusher and stir regularly.
+5. Remove the charred courgette and aubergine from the griddle pan, leaving it on the heat, and roughly chop them on a board.
+6. Add them to the casserole pan along with the passata and vinegar, and boil with the lid on.
+7. Rub the steaks with salt, the paprika and 1 teaspoon of olive oil and place on the hot griddle pan, turning every minute until cooked to your liking.
+8. On a board, finely slice the parsley stalks and roughly chop the leaves.
+9. Add the mustard and extra virgin olive oil, season with salt and pepper and squeeze over the lemon juice, then mix together and spread over the board.
+10. When the steaks are done, transfer them to the board, turn in the dressing, then slice.
+11. Tear the top leafy half of the basil into the ratatouille, season to taste, and serve with yoghurt and saffron rice.
+
+**Enjoy!**
+
+ </div><!--End of content-->
+
+ <script>
+ 'use strict';
+
+ var jsContent = document.getElementById('content');
+ var placeholder = document.getElementById('placeholder');
+
+ var updateText = function(text) {
+ placeholder.innerHTML = marked.parse(text);
+ }
+
+ new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ var content = channel.objects.content;
+ content.setInitialText(jsContent.innerHTML);
+ content.textChanged.connect(updateText);
+ }
+ );
+ </script>
+</body>
+</html>
+
+
+
diff --git a/examples/webenginewidgets/recipebrowser/doc/images/recipebrowser.webp b/examples/webenginewidgets/recipebrowser/doc/images/recipebrowser.webp
new file mode 100644
index 000000000..8446bcde3
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/doc/images/recipebrowser.webp
Binary files differ
diff --git a/examples/webenginewidgets/recipebrowser/doc/src/recipebrowser.qdoc b/examples/webenginewidgets/recipebrowser/doc/src/recipebrowser.qdoc
new file mode 100644
index 000000000..4ac9973b3
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/doc/src/recipebrowser.qdoc
@@ -0,0 +1,197 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example webenginewidgets/recipebrowser
+ \title Recipe Browser
+ \meta category {Application Examples}
+ \meta tag {widgets, webengine, webchannel, webenginescript}
+ \ingroup webengine-widgetexamples
+ \brief Injecting custom stylsheets into web pages and providing a rich text preview
+ tool for a custom markup language.
+
+ \image recipebrowser.webp
+
+ \e {Recipe Browser} is a small hybrid web browser application. It demonstrates how to
+ use the \l{Qt WebEngine Widgets C++ Classes} {Qt WebEngine C++ classes} to combine
+ C++ and JavaScript logic in the following ways.
+
+ \list
+ \li Running arbitrary JavaScript code via \c QWebEnginePage::runJavaScript() to
+ inject custom CSS stylesheets
+ \li Using QWebEngineScript and QWebEngineScriptCollection to persist the JavaScript
+ code and inject it to every page
+ \li Using QWebChannel to interact with and provide a rich text preview for a custom
+ markup language
+ \endlist
+
+ \l{http://daringfireball.net/projects/markdown/}{Markdown} is a lightweight
+ markup language with a plain text formatting syntax.
+ Some services, such as \l{http://github.com}{github}, acknowledge the
+ format, and render the content as rich text when viewed in a browser.
+
+ The Recipe Browser main window is split into a navigation on the left and
+ a preview area on the right. The preview area on the right switches to an
+ editor when the user clicks the Edit button on the top left of the main window.
+ The editor supports the Markdown syntax and is implemented by using
+ QPlainTextEdit. The document is rendered as rich text in the preview area,
+ once the user clicks the View button,to which the Edit button transforms to.
+ This rendering is implemented by using QWebEngineView. To render the text,
+ a JavaScript library inside the web engine converts the Markdown text to HTML.
+ The preview is updated from the editor through QWebChannel.
+
+ \include examples-run.qdocinc
+
+ \section1 Exposing Document Text
+
+ To render the current Markdown text it needs to be exposed to the web engine through
+ QWebChannel. To achieve this it has to be part of Qt metatype system. This is done
+ by using a dedicated \c Document class that exposes the document text as a
+ \c {Q_PROPERTY}:
+
+ \quotefromfile webenginewidgets/recipebrowser/document.h
+ \skipto class Document
+ \printto #endif
+
+ The \c Document class wraps a QString \c m_currentText to be set on the C++
+ side with the \c setText() method and exposes it at runtime as a \c text
+ property with a \c textChanged signal. We define the \c setText method as
+ follows:
+
+ \quotefromfile webenginewidgets/recipebrowser/document.cpp
+ \skipto Document::setText(const QString &text)
+ \printuntil /^\}/
+
+ Additionally, the \c Document class keeps track of the current recipe via
+ \c m_currentPage. We call the recipes pages here, because each recipe has
+ its distinct HTML document that contains the initial text content.
+ Furthermore, \c m_textCollection is a QMap<QString, QString> that contains
+ the key/value pairs \{page, text\}, so that changes made to the text content
+ of a page is persisted between navigation. Nevertheless, we do not write the
+ modified text contents to the drive, but instead we persist them between
+ application start and shutdown via QSettings.
+
+ \section1 Creating the Main Window
+
+ The \c MainWindow class inherits the QMainWindow class:
+
+ \quotefromfile webenginewidgets/recipebrowser/mainwindow.h
+ \skipto class MainWindow :
+ \printto endif
+
+ The class declares private slots that match the two buttons on the
+ top left, over the navigation list view. Additionally, helper
+ methods for custom CSS stylesheets are declared.
+
+ The actual layout of the main window is specified in a \c .ui file.
+ The widgets and actions are available at runtime in the \c ui member
+ variable.
+
+ \c m_isEditMode is a boolean that toggles between the editor and the
+ preview area.
+ \c m_content is an instance of the \c Document class.
+
+ The actual setup of the different objects is done in the \c MainWindow
+ constructor:
+
+ \quotefromfile webenginewidgets/recipebrowser/mainwindow.cpp
+ \skipto MainWindow::MainWindow
+ \printto connect
+
+ The constructor first calls \c setupUi to construct the widgets and menu
+ actions according to the UI file. The text editor font is set to one
+ with a fixed character width, and the QWebEngineView widget is told not
+ to show a context menu. Furthermore, the editor is hidden away.
+
+ \printto ui->recipes
+
+ Here the \c clicked signals of QPushButton are connected to respective functions
+ that show the stylesheets dialog or toggle between edit and view mode, that is,
+ hide and show the editor and preview area respectively.
+
+ \printto m_content.setTextEdit
+
+ Here the navigation QListWidget on the left is setup with the 7 recipes.
+ Also, the currentItemChanged signal of QListWidget is connected to a lambda
+ that loads the new, current recipe page and updates the page in \c m_content.
+
+ \printto connect
+
+ Next, the pointer to the ui editor, a QPlainTextEdit, is passed to \c m_content to ensure that
+ calls to \c Document::setInitialText() work properly.
+
+ \printto QSettings
+
+ Here the \c textChanged signal of the editor is connected to a lambda that
+ updates the text in \c m_content. This object is then exposed to the JS side
+ by \c QWebChannel under the name \c{content}.
+
+ \printto ui->recipes
+
+ By using QSettings we persist stylesheets between application runs. If there
+ should be no stylesheets configured, for example, because the user deleted all of them
+ in a previous run, we load default ones.
+
+ \printto }
+
+ Finally, we set the currently selected list item to the first contained in the
+ navigation list widget. This triggers the previously mentioned
+ QListWidget::currentItemChanged signal and navigates to the page of the list item.
+
+ \section1 Working With Stylesheets
+
+ We use JavaScript to create and append CSS elements to the documents.
+ After declaring the script source, QWebEnginePage::runJavaScript() can run it
+ immediately and apply newly created styles on the current content of the web view.
+ Encapsulating the script into a QWebEngineScript and adding it to the script collection
+ of QWebEnginePage makes its effect permanent.
+
+ \quotefromfile webenginewidgets/recipebrowser/mainwindow.cpp
+ \skipto MainWindow::insertStyleSheet
+ \printuntil /^\}/
+
+ Removing stylesheets can be done similarly:
+
+ \quotefromfile webenginewidgets/recipebrowser/mainwindow.cpp
+ \skipto MainWindow::removeStyleSheet
+ \printuntil /^\}/
+
+ \section1 Creating a recipe file
+
+ \quotefile webenginewidgets/recipebrowser/assets/pages/burger.html
+
+ All the different recipe pages are set up the same way.
+
+ In the \c <head> part they
+ include two CSS files: \c markdown.css, that styles the markdown, and
+ custom.css, that does some further styling but most importantly hides the
+ \c <div> with id \e content, as this \c <div> only contains the unmodified,
+ initial content text. Also, three JS scripts are included. \c marked.js
+ is responsible for parsing the markdown and transforming it into HTML.
+ \c custom.js does some configuration of \c marked.js, and \c qwebchannel.js
+ exposes the QWebChannel JavaScript API.
+
+ In the body there are two \c <div> elements. The \c <div> with id \e placeholder
+ gets the markdown text injected that is rendered and visible. The \c <div> with id
+ \e content is hidden by \c custom.css and only contains the original, unmodified
+ text content of the recipe.
+
+ Finally, on the bottom of each recipe HTML file is a script that is responsible for
+ the communication between the C++ and JavaScript side via QWebChannel. The original,
+ unmodified text content inside the \c <div> with id \e content is passed to the C++
+ side and a callback is setup that is invoked when the \c textChanged signal of
+ \c m_content is emitted. The callback then updates the contents of the \c <div>
+ \e placeholder with the parsed markdown.
+
+ \section1 Files and Attributions
+
+ The example bundles the following code with third-party licenses:
+ \table
+ \row
+ \li \l{recipebrowser-marked}{Marked}
+ \li MIT License
+ \row
+ \li \l{recipebrowser-markdowncss}{Markdown.css}
+ \li Apache License 2.0
+ \endtable
+*/
diff --git a/examples/webenginewidgets/recipebrowser/document.cpp b/examples/webenginewidgets/recipebrowser/document.cpp
new file mode 100644
index 000000000..c991e14f8
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/document.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "document.h"
+
+#include <QSettings>
+
+Document::Document(QObject *parent) : QObject(parent)
+{
+ QSettings settings;
+ settings.beginGroup("textCollection");
+ QStringList pageTexts = settings.allKeys();
+ for (const QString &name : std::as_const(pageTexts)) {
+ QString pageText = settings.value(name).value<QString>();
+ if (!pageText.isEmpty())
+ m_textCollection.insert(name, pageText);
+ }
+ settings.endGroup();
+}
+
+void Document::setTextEdit(QPlainTextEdit *textEdit)
+{
+ m_textEdit = textEdit;
+}
+
+void Document::setCurrentPage(const QString &page)
+{
+ m_currentPage = page;
+}
+
+void Document::setInitialText(const QString &text)
+{
+ m_textEdit->setPlainText(m_textCollection.value(m_currentPage, text));
+}
+
+void Document::setText(const QString &text)
+{
+ if (text == m_currentText)
+ return;
+ m_currentText = text;
+ emit textChanged(m_currentText);
+
+ QSettings settings;
+ settings.beginGroup("textCollection");
+ settings.setValue(m_currentPage, text);
+ m_textCollection.insert(m_currentPage, text);
+ settings.endGroup();
+}
diff --git a/examples/webenginewidgets/recipebrowser/document.h b/examples/webenginewidgets/recipebrowser/document.h
new file mode 100644
index 000000000..f6b537eb8
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/document.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DOCUMENT_H
+#define DOCUMENT_H
+
+#include <QObject>
+#include <QString>
+#include <QPlainTextEdit>
+
+class Document : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text MEMBER m_currentText NOTIFY textChanged FINAL)
+public:
+ explicit Document(QObject *parent = nullptr);
+
+ void setTextEdit(QPlainTextEdit *textEdit);
+ void setCurrentPage(const QString &page);
+
+public slots:
+ void setInitialText(const QString &text);
+ void setText(const QString &text);
+
+signals:
+ void textChanged(const QString &text);
+
+private:
+ QPlainTextEdit *m_textEdit;
+
+ QString m_currentText;
+ QString m_currentPage;
+ QMap<QString, QString> m_textCollection;
+};
+
+#endif // DOCUMENT_H
diff --git a/examples/webenginewidgets/stylesheetbrowser/main.cpp b/examples/webenginewidgets/recipebrowser/main.cpp
index cf1e5f718..b0b42d16b 100644
--- a/examples/webenginewidgets/stylesheetbrowser/main.cpp
+++ b/examples/webenginewidgets/recipebrowser/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
@@ -12,7 +12,7 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("QtExamples");
QApplication a(argc, argv);
- MainWindow w(QUrl("http://qt.io"));
+ MainWindow w;
w.show();
return a.exec();
}
diff --git a/examples/webenginewidgets/recipebrowser/mainwindow.cpp b/examples/webenginewidgets/recipebrowser/mainwindow.cpp
new file mode 100644
index 000000000..7288fe4ce
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/mainwindow.cpp
@@ -0,0 +1,153 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "stylesheetdialog.h"
+#include "ui_mainwindow.h"
+
+#include <QWebChannel>
+
+static QMap<QString, QString> defaultStyleSheets = {
+ { "Upside down", "body { -webkit-transform: rotate(180deg); }" },
+ { "Darkmode",
+ "body { color: #FFF; background-color: #000 }"
+ "h1, h2, h3, h4, h5 { color: #FFF }" }
+};
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent), ui(new Ui::MainWindow), m_isEditMode(false)
+{
+ ui->setupUi(this);
+ ui->textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ ui->textEdit->hide();
+ ui->webEngineView->setContextMenuPolicy(Qt::NoContextMenu);
+
+ connect(ui->stylesheetsButton, &QPushButton::clicked, this, &MainWindow::showStyleSheetsDialog);
+ connect(ui->editViewButton, &QPushButton::clicked, this, &MainWindow::toggleEditView);
+
+ ui->recipes->insertItem(0, "Burger");
+ ui->recipes->insertItem(1, "Cupcakes");
+ ui->recipes->insertItem(2, "Pasta");
+ ui->recipes->insertItem(3, "Pizza");
+ ui->recipes->insertItem(4, "Skewers");
+ ui->recipes->insertItem(5, "Soup");
+ ui->recipes->insertItem(6, "Steak");
+ connect(ui->recipes, &QListWidget::currentItemChanged, this,
+ [this](QListWidgetItem *current, QListWidgetItem * /* previous */) {
+ const QString page = current->text().toLower();
+ const QString url = QStringLiteral("qrc:/pages/") + page + QStringLiteral(".html");
+ ui->webEngineView->setUrl(QUrl(url));
+ m_content.setCurrentPage(page);
+ });
+
+ m_content.setTextEdit(ui->textEdit);
+
+ connect(ui->textEdit, &QPlainTextEdit::textChanged, this,
+ [this]() { m_content.setText(ui->textEdit->toPlainText()); });
+
+ QWebChannel *channel = new QWebChannel(this);
+ channel->registerObject(QStringLiteral("content"), &m_content);
+ ui->webEngineView->page()->setWebChannel(channel);
+
+ QSettings settings;
+ settings.beginGroup("styleSheets");
+ QStringList styleSheets = settings.allKeys();
+ if (styleSheets.empty()) {
+ // Add back default style sheets if the user cleared them out
+ loadDefaultStyleSheets();
+ } else {
+ for (const auto &name : std::as_const(styleSheets)) {
+ StyleSheet styleSheet = settings.value(name).value<StyleSheet>();
+ if (styleSheet.second)
+ insertStyleSheet(name, styleSheet.first, false);
+ }
+ }
+ settings.endGroup();
+
+ ui->recipes->setCurrentItem(ui->recipes->item(0));
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::insertStyleSheet(const QString &name, const QString &source, bool immediately)
+{
+ QWebEngineScript script;
+ QString s = QString::fromLatin1("(function() {"
+ " css = document.createElement('style');"
+ " css.type = 'text/css';"
+ " css.id = '%1';"
+ " document.head.appendChild(css);"
+ " css.innerText = '%2';"
+ "})()")
+ .arg(name, source.simplified());
+ if (immediately)
+ ui->webEngineView->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
+
+ script.setName(name);
+ script.setSourceCode(s);
+ script.setInjectionPoint(QWebEngineScript::DocumentReady);
+ script.setRunsOnSubFrames(true);
+ script.setWorldId(QWebEngineScript::ApplicationWorld);
+ ui->webEngineView->page()->scripts().insert(script);
+}
+
+void MainWindow::removeStyleSheet(const QString &name, bool immediately)
+{
+ QString s = QString::fromLatin1("(function() {"
+ " var element = document.getElementById('%1');"
+ " element.outerHTML = '';"
+ " delete element;"
+ "})()")
+ .arg(name);
+ if (immediately)
+ ui->webEngineView->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
+
+ const QList<QWebEngineScript> scripts = ui->webEngineView->page()->scripts().find(name);
+ if (!scripts.isEmpty())
+ ui->webEngineView->page()->scripts().remove(scripts.first());
+}
+
+bool MainWindow::hasStyleSheet(const QString &name)
+{
+ const QList<QWebEngineScript> scripts = ui->webEngineView->page()->scripts().find(name);
+ return !scripts.isEmpty();
+}
+
+void MainWindow::loadDefaultStyleSheets()
+{
+ QSettings settings;
+ settings.beginGroup("styleSheets");
+
+ for (auto it = defaultStyleSheets.constBegin(); it != defaultStyleSheets.constEnd(); ++it) {
+ settings.setValue(it.key(), QVariant::fromValue(qMakePair(it.value(), true)));
+ insertStyleSheet(it.key(), it.value(), false);
+ }
+
+ settings.endGroup();
+}
+
+void MainWindow::showStyleSheetsDialog()
+{
+ StylesheetDialog *dialog = new StylesheetDialog(this);
+ dialog->show();
+}
+
+void MainWindow::toggleEditView()
+{
+ m_isEditMode = !m_isEditMode;
+
+ if (m_isEditMode) {
+ ui->webEngineView->hide();
+ ui->textEdit->show();
+
+ ui->editViewButton->setText(QStringLiteral("View"));
+ } else {
+ ui->textEdit->hide();
+ ui->webEngineView->show();
+
+ ui->editViewButton->setText(QStringLiteral("Edit"));
+ }
+}
diff --git a/examples/webenginewidgets/stylesheetbrowser/mainwindow.h b/examples/webenginewidgets/recipebrowser/mainwindow.h
index 92d6d02fe..959c82af8 100644
--- a/examples/webenginewidgets/stylesheetbrowser/mainwindow.h
+++ b/examples/webenginewidgets/recipebrowser/mainwindow.h
@@ -1,12 +1,15 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include "document.h"
#include <QMainWindow>
#include <QSettings>
#include <QWebEngineScriptCollection>
+#include <QWebEngineView>
+#include <QPlainTextEdit>
QT_BEGIN_NAMESPACE
namespace Ui {
@@ -19,7 +22,7 @@ class MainWindow : public QMainWindow
Q_OBJECT
public:
- explicit MainWindow(const QUrl &url);
+ explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void insertStyleSheet(const QString &name, const QString &source, bool immediately);
@@ -28,13 +31,14 @@ public:
void loadDefaultStyleSheets();
private slots:
- void urlEntered();
- void urlChanged(const QUrl &url);
void showStyleSheetsDialog();
- void reloadRequested();
+ void toggleEditView();
private:
Ui::MainWindow *ui;
+
+ bool m_isEditMode;
+ Document m_content;
};
#endif // MAINWINDOW_H
diff --git a/examples/webenginewidgets/recipebrowser/mainwindow.ui b/examples/webenginewidgets/recipebrowser/mainwindow.ui
new file mode 100644
index 000000000..b7c29f1b8
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/mainwindow.ui
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>713</width>
+ <height>455</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Recipe Browser</string>
+ </property>
+ <property name="unifiedTitleAndToolBarOnMac">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="navigation">
+ <item>
+ <layout class="QHBoxLayout" name="navigationBar">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Recipes</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="editViewButton">
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="stylesheetsButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Stylesheets</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QListWidget" name="recipes">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWebEngineView" name="webEngineView" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPlainTextEdit" name="textEdit"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+ <customwidget>
+ <class>QWebEngineView</class>
+ <extends>QWidget</extends>
+ <header>qwebengineview.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro b/examples/webenginewidgets/recipebrowser/recipebrowser.pro
index a9ff54400..8f8895b52 100644
--- a/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro
+++ b/examples/webenginewidgets/recipebrowser/recipebrowser.pro
@@ -1,22 +1,24 @@
TEMPLATE = app
-TARGET = stylesheetbrowser
+TARGET = recipebrowser
QT += webenginewidgets
HEADERS += \
mainwindow.h \
- stylesheetdialog.h
+ stylesheetdialog.h \
+ document.h
SOURCES += \
main.cpp \
mainwindow.cpp \
- stylesheetdialog.cpp
+ stylesheetdialog.cpp \
+ document.cpp
FORMS += \
mainwindow.ui \
stylesheetdialog.ui
-RESOURCES += stylesheetbrowser.qrc
+RESOURCES += recipebrowser.qrc
# install
-target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/stylesheetbrowser
+target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/recipebrowser
INSTALLS += target
diff --git a/examples/webenginewidgets/recipebrowser/recipebrowser.qrc b/examples/webenginewidgets/recipebrowser/recipebrowser.qrc
new file mode 100644
index 000000000..2dc003f30
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/recipebrowser.qrc
@@ -0,0 +1,15 @@
+<RCC>
+ <qresource prefix="/">
+ <file>assets/3rdparty/markdown.css</file>
+ <file>assets/3rdparty/marked.js</file>
+ <file>assets/custom.js</file>
+ <file>assets/custom.css</file>
+ <file>assets/pages/burger.html</file>
+ <file>assets/pages/cupcakes.html</file>
+ <file>assets/pages/pasta.html</file>
+ <file>assets/pages/pizza.html</file>
+ <file>assets/pages/skewers.html</file>
+ <file>assets/pages/soup.html</file>
+ <file>assets/pages/steak.html</file>
+ </qresource>
+</RCC>
diff --git a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.cpp b/examples/webenginewidgets/recipebrowser/stylesheetdialog.cpp
index 30409f107..2137617c3 100644
--- a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.cpp
+++ b/examples/webenginewidgets/recipebrowser/stylesheetdialog.cpp
@@ -1,18 +1,18 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include "stylesheetdialog.h"
#include "ui_stylesheetdialog.h"
-StylesheetDialog::StylesheetDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::StylesheetDialog)
+StylesheetDialog::StylesheetDialog(QWidget *parent) : QDialog(parent), ui(new Ui::StylesheetDialog)
{
ui->setupUi(this);
- connect(ui->styleSheetList, &QListWidget::currentItemChanged, this, &StylesheetDialog::currentStyleSheetChanged);
- connect(ui->styleSheetList, &QListWidget::itemClicked, this, &StylesheetDialog::listItemClicked);
+ connect(ui->styleSheetList, &QListWidget::currentItemChanged, this,
+ &StylesheetDialog::currentStyleSheetChanged);
+ connect(ui->styleSheetList, &QListWidget::itemClicked, this,
+ &StylesheetDialog::listItemClicked);
connect(ui->fileNameEdit, &QLineEdit::textChanged, this, &StylesheetDialog::fileNameChanged);
connect(ui->addButton, &QPushButton::clicked, this, &StylesheetDialog::addButtonClicked);
@@ -20,8 +20,8 @@ StylesheetDialog::StylesheetDialog(QWidget *parent) :
QSettings settings;
settings.beginGroup("styleSheets");
- for (auto name : settings.allKeys()) {
- QListWidgetItem *listItem = new QListWidgetItem(name, ui->styleSheetList);
+ for (const auto &name : settings.allKeys()) {
+ QListWidgetItem *listItem = new QListWidgetItem(name, ui->styleSheetList);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable);
bool checked = settings.value(name).value<StyleSheet>().second;
listItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
@@ -59,9 +59,8 @@ void StylesheetDialog::listItemClicked(QListWidgetItem *item)
{
MainWindow *window = static_cast<MainWindow *>(parent());
const QString name = item->text();
- bool checkedStateChanged =
- (item->checkState() == Qt::Checked && !window->hasStyleSheet(name)) ||
- (item->checkState() == Qt::Unchecked && window->hasStyleSheet(name));
+ bool checkedStateChanged = (item->checkState() == Qt::Checked && !window->hasStyleSheet(name))
+ || (item->checkState() == Qt::Unchecked && window->hasStyleSheet(name));
if (!checkedStateChanged)
return;
@@ -96,7 +95,7 @@ void StylesheetDialog::addButtonClicked()
if (name.isEmpty() || source.isEmpty())
return;
- QListWidgetItem *listItem = new QListWidgetItem(ui->fileNameEdit->text(), ui->styleSheetList);
+ QListWidgetItem *listItem = new QListWidgetItem(ui->fileNameEdit->text(), ui->styleSheetList);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable);
listItem->setCheckState(Qt::Checked);
diff --git a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.h b/examples/webenginewidgets/recipebrowser/stylesheetdialog.h
index 7bebff74f..ca1b4ae99 100644
--- a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.h
+++ b/examples/webenginewidgets/recipebrowser/stylesheetdialog.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef STYLESHEETDIALOG_H
diff --git a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.ui b/examples/webenginewidgets/recipebrowser/stylesheetdialog.ui
index 19db267e8..19db267e8 100644
--- a/examples/webenginewidgets/stylesheetbrowser/stylesheetdialog.ui
+++ b/examples/webenginewidgets/recipebrowser/stylesheetdialog.ui
diff --git a/examples/webenginewidgets/stylesheetbrowser/3rdparty/COPYING b/examples/webenginewidgets/stylesheetbrowser/3rdparty/COPYING
deleted file mode 100644
index 220881da6..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/3rdparty/COPYING
+++ /dev/null
@@ -1 +0,0 @@
-The icons in this repository are herefore released into the Public Domain.
diff --git a/examples/webenginewidgets/stylesheetbrowser/3rdparty/qt_attribution.json b/examples/webenginewidgets/stylesheetbrowser/3rdparty/qt_attribution.json
deleted file mode 100644
index f779da7e2..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/3rdparty/qt_attribution.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "Id": "stylesheetbrowser-tango",
- "Name": "Tango Icon Library",
- "QDocModule": "qtwebengine",
- "QtUsage": "Used in WebEngine StyleSheet Browser example.",
-
- "QtParts": [ "examples" ],
- "Description": "Selected icons from the Tango Icon Library",
- "Homepage": "http://tango.freedesktop.org/Tango_Icon_Library",
- "Version": "0.8.90",
- "DownloadLocation": "http://tango.freedesktop.org/releases/tango-icon-theme-0.8.90.tar.gz",
- "LicenseId": "urn:dje:license:public-domain",
- "License": "Public Domain",
- "LicenseFile": "COPYING",
- "Copyright": "Ulisse Perusin <uli.peru@gmail.com>
-Steven Garrity <sgarrity@silverorange.com>
-Lapo Calamandrei <calamandrei@gmail.com>
-Ryan Collier <rcollier@novell.com>
-Rodney Dawes <dobey@novell.com>
-Andreas Nilsson <nisses.mail@home.se>
-Tuomas Kuosmanen <tigert@tigert.com>
-Garrett LeSage <garrett@novell.com>
-Jakub Steiner <jimmac@novell.com>"
-}
diff --git a/examples/webenginewidgets/stylesheetbrowser/3rdparty/view-refresh.png b/examples/webenginewidgets/stylesheetbrowser/3rdparty/view-refresh.png
deleted file mode 100644
index cab4d02c7..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/3rdparty/view-refresh.png
+++ /dev/null
Binary files differ
diff --git a/examples/webenginewidgets/stylesheetbrowser/CMakeLists.txt b/examples/webenginewidgets/stylesheetbrowser/CMakeLists.txt
deleted file mode 100644
index 56ecd51f3..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/CMakeLists.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(stylesheetbrowser LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/stylesheetbrowser")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui WebEngineWidgets)
-
-qt_add_executable(stylesheetbrowser
- main.cpp
- mainwindow.cpp mainwindow.h mainwindow.ui
- stylesheetdialog.cpp stylesheetdialog.h stylesheetdialog.ui
-)
-
-set_target_properties(stylesheetbrowser PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(stylesheetbrowser PUBLIC
- Qt::Core
- Qt::Gui
- Qt::WebEngineWidgets
-)
-
-# Resources:
-set(stylesheetbrowser_resource_files
- "3rdparty/view-refresh.png"
-)
-
-qt_add_resources(stylesheetbrowser "stylesheetbrowser"
- PREFIX
- "/"
- BASE
- "3rdparty"
- FILES
- ${stylesheetbrowser_resource_files}
-)
-
-install(TARGETS stylesheetbrowser
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/webenginewidgets/stylesheetbrowser/doc/images/stylesheetbrowser.png b/examples/webenginewidgets/stylesheetbrowser/doc/images/stylesheetbrowser.png
deleted file mode 100644
index 32c7c43ed..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/doc/images/stylesheetbrowser.png
+++ /dev/null
Binary files differ
diff --git a/examples/webenginewidgets/stylesheetbrowser/doc/src/stylesheetbrowser.qdoc b/examples/webenginewidgets/stylesheetbrowser/doc/src/stylesheetbrowser.qdoc
deleted file mode 100644
index 80f333323..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/doc/src/stylesheetbrowser.qdoc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \example webenginewidgets/stylesheetbrowser
- \title WebEngine StyleSheet Browser Example
- \ingroup webengine-widgetexamples
- \brief Demonstrates how to inject CSS into web pages using user scripts.
-
- \image stylesheetbrowser.png
-
- \e {StyleSheet Browser} demonstrates how to use the \l{Qt WebEngine Widgets C++ Classes}
- {Qt WebEngine C++ classes} to inject user stylesheets into web pages.
-
- \include examples-run.qdocinc
-
- \section1 Working With Stylesheets
-
- We use JavaScript to create and append CSS elements to the documents.
- After declaring the script source, QWebEnginePage::runJavaScript() can run it
- immediately and apply newly created styles on the current content of the web view.
- Encapsulating the script into a QWebEngineScript and adding it to the script collection
- of QWebEnginePage makes its effect permanent.
-
- \quotefromfile webenginewidgets/stylesheetbrowser/mainwindow.cpp
- \skipto MainWindow::insertStyleSheet
- \printuntil /^\}/
-
- Removing stylesheets can be done similarly:
-
- \quotefromfile webenginewidgets/stylesheetbrowser/mainwindow.cpp
- \skipto MainWindow::removeStyleSheet
- \printuntil /^\}/
-
- \section1 Files and Attributions
-
- The example uses icons from the Tango Icon Library:
-
- \table
- \row
- \li \l{stylesheetbrowser-tango}{Tango Icon Library}
- \li Public Domain
- \endtable
-*/
diff --git a/examples/webenginewidgets/stylesheetbrowser/mainwindow.cpp b/examples/webenginewidgets/stylesheetbrowser/mainwindow.cpp
deleted file mode 100644
index ab786d5c9..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/mainwindow.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "mainwindow.h"
-#include "stylesheetdialog.h"
-#include "ui_mainwindow.h"
-
-static QMap<QString, QString> defaultStyleSheets = {
- {"Upside down", "body { -webkit-transform: rotate(180deg); }"}
-};
-
-MainWindow::MainWindow(const QUrl &url) :
- QMainWindow(),
- ui(new Ui::MainWindow)
-{
- ui->setupUi(this);
-
- connect(ui->urlBar, &QLineEdit::returnPressed, this, &MainWindow::urlEntered);
- connect(ui->webEngineView, &QWebEngineView::urlChanged, this, &MainWindow::urlChanged);
- connect(ui->settingsButton, &QPushButton::clicked, this, &MainWindow::showStyleSheetsDialog);
- connect(ui->reloadButton, &QPushButton::clicked, this, &MainWindow::reloadRequested);
-
- QSettings settings;
- settings.beginGroup("styleSheets");
- QStringList styleSheets = settings.allKeys();
- if (styleSheets.empty()) {
- // Add back default style sheets if the user cleared them out
- loadDefaultStyleSheets();
- } else {
- for (auto name : std::as_const(styleSheets)) {
- StyleSheet styleSheet = settings.value(name).value<StyleSheet>();
- if (styleSheet.second)
- insertStyleSheet(name, styleSheet.first, false);
- }
- }
- settings.endGroup();
-
- ui->webEngineView->setUrl(url);
-}
-
-MainWindow::~MainWindow()
-{
- delete ui;
-}
-
-void MainWindow::insertStyleSheet(const QString &name, const QString &source, bool immediately)
-{
- QWebEngineScript script;
- QString s = QString::fromLatin1("(function() {"\
- " css = document.createElement('style');"\
- " css.type = 'text/css';"\
- " css.id = '%1';"\
- " document.head.appendChild(css);"\
- " css.innerText = '%2';"\
- "})()").arg(name).arg(source.simplified());
- if (immediately)
- ui->webEngineView->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
-
- script.setName(name);
- script.setSourceCode(s);
- script.setInjectionPoint(QWebEngineScript::DocumentReady);
- script.setRunsOnSubFrames(true);
- script.setWorldId(QWebEngineScript::ApplicationWorld);
- ui->webEngineView->page()->scripts().insert(script);
-}
-
-void MainWindow::removeStyleSheet(const QString &name, bool immediately)
-{
- QString s = QString::fromLatin1("(function() {"\
- " var element = document.getElementById('%1');"\
- " element.outerHTML = '';"\
- " delete element;"\
- "})()").arg(name);
- if (immediately)
- ui->webEngineView->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
-
- const QList<QWebEngineScript> scripts = ui->webEngineView->page()->scripts().find(name);
- if (!scripts.isEmpty())
- ui->webEngineView->page()->scripts().remove(scripts.first());
-}
-
-bool MainWindow::hasStyleSheet(const QString &name)
-{
- const QList<QWebEngineScript> scripts = ui->webEngineView->page()->scripts().find(name);
- return !scripts.isEmpty();
-}
-
-void MainWindow::loadDefaultStyleSheets()
-{
- QSettings settings;
- settings.beginGroup("styleSheets");
-
- auto it = defaultStyleSheets.constBegin();
- while (it != defaultStyleSheets.constEnd()) {
- settings.setValue(it.key(), QVariant::fromValue(qMakePair(it.value(), true)));
- insertStyleSheet(it.key(), it.value(), false);
- ++it;
- }
-
- settings.endGroup();
-}
-
-void MainWindow::urlEntered()
-{
- ui->webEngineView->setUrl(QUrl::fromUserInput(ui->urlBar->text()));
-}
-
-void MainWindow::urlChanged(const QUrl &url)
-{
- ui->urlBar->setText(url.toString());
-}
-
-void MainWindow::showStyleSheetsDialog()
-{
- StylesheetDialog *dialog = new StylesheetDialog(this);
- dialog->show();
-}
-
-void MainWindow::reloadRequested()
-{
- ui->webEngineView->reload();
-}
diff --git a/examples/webenginewidgets/stylesheetbrowser/mainwindow.ui b/examples/webenginewidgets/stylesheetbrowser/mainwindow.ui
deleted file mode 100644
index bc68c16bb..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/mainwindow.ui
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>713</width>
- <height>455</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>StyleSheet Browser</string>
- </property>
- <property name="unifiedTitleAndToolBarOnMac">
- <bool>false</bool>
- </property>
- <widget class="QWidget" name="centralWidget">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QWidget" name="webContentsWidget" native="true">
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QWidget" name="urlBarWidget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QLineEdit" name="urlBar"/>
- </item>
- <item>
- <widget class="QPushButton" name="reloadButton">
- <property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="stylesheetbrowser.qrc">
- <normaloff>:/view-refresh.png</normaloff>:/view-refresh.png</iconset>
- </property>
- <property name="shortcut">
- <string>Ctrl+R</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="settingsButton">
- <property name="text">
- <string>Settings</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QWebEngineView" name="webEngineView" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="url" stdset="0">
- <url>
- <string>about:blank</string>
- </url>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QMenuBar" name="menuBar">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>713</width>
- <height>20</height>
- </rect>
- </property>
- </widget>
- </widget>
- <layoutdefault spacing="6" margin="11"/>
- <customwidgets>
- <customwidget>
- <class>QWebEngineView</class>
- <extends>QWidget</extends>
- <header location="global">QtWebEngineWidgets/QWebEngineView</header>
- </customwidget>
- </customwidgets>
- <resources>
- <include location="stylesheetbrowser.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.qrc b/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.qrc
deleted file mode 100644
index a1cebd6a7..000000000
--- a/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="view-refresh.png">3rdparty/view-refresh.png</file>
- </qresource>
-</RCC>
diff --git a/examples/webenginewidgets/webenginewidgets.pro b/examples/webenginewidgets/webenginewidgets.pro
index aedd2cc62..2a24686cf 100644
--- a/examples/webenginewidgets/webenginewidgets.pro
+++ b/examples/webenginewidgets/webenginewidgets.pro
@@ -7,12 +7,11 @@ SUBDIRS += \
cookiebrowser \
notifications \
simplebrowser \
- stylesheetbrowser \
push-notifications \
videoplayer
qtConfig(webengine-geolocation): SUBDIRS += maps
-qtConfig(webengine-webchannel): SUBDIRS += markdowneditor
+qtConfig(webengine-webchannel): SUBDIRS += recipebrowser
qtConfig(webengine-printing-and-pdf) {
SUBDIRS += printme html2pdf