diff options
Diffstat (limited to 'examples/webenginewidgets/notifications')
-rw-r--r-- | examples/webenginewidgets/notifications/CMakeLists.txt | 52 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/data/data.qrc | 6 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/data/icon.png | bin | 0 -> 2252 bytes | |||
-rw-r--r-- | examples/webenginewidgets/notifications/data/index.html | 90 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/doc/images/notifications-example.png | bin | 0 -> 14732 bytes | |||
-rw-r--r-- | examples/webenginewidgets/notifications/doc/src/notifications.qdoc | 133 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/main.cpp | 53 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/notificationpopup.h | 90 | ||||
-rw-r--r-- | examples/webenginewidgets/notifications/notifications.pro | 10 |
9 files changed, 434 insertions, 0 deletions
diff --git a/examples/webenginewidgets/notifications/CMakeLists.txt b/examples/webenginewidgets/notifications/CMakeLists.txt new file mode 100644 index 000000000..8cca0ab31 --- /dev/null +++ b/examples/webenginewidgets/notifications/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(notifications LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/notifications") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui WebEngineWidgets) + +qt_add_executable(notifications + main.cpp + notificationpopup.h +) + +set_target_properties(notifications PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(notifications PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineWidgets +) + +# Resources: +set(data_resource_files + "data/icon.png" + "data/index.html" +) + +qt_add_resources(notifications "data" + PREFIX + "/" + BASE + "data" + FILES + ${data_resource_files} +) + +install(TARGETS notifications + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/webenginewidgets/notifications/data/data.qrc b/examples/webenginewidgets/notifications/data/data.qrc new file mode 100644 index 000000000..bc252b89c --- /dev/null +++ b/examples/webenginewidgets/notifications/data/data.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>index.html</file> + <file>icon.png</file> + </qresource> +</RCC> diff --git a/examples/webenginewidgets/notifications/data/icon.png b/examples/webenginewidgets/notifications/data/icon.png Binary files differnew file mode 100644 index 000000000..4c3870c06 --- /dev/null +++ b/examples/webenginewidgets/notifications/data/icon.png diff --git a/examples/webenginewidgets/notifications/data/index.html b/examples/webenginewidgets/notifications/data/index.html new file mode 100644 index 000000000..ebb73b573 --- /dev/null +++ b/examples/webenginewidgets/notifications/data/index.html @@ -0,0 +1,90 @@ +<!doctype html> +<html> +<head> +<title>Web Notifications Example</title> +<script> + var notificationsCreated = 0 + + function getPermission() { return document.Notification } + function resetPermission(permission = 'default') { + document.Notification = permission + document.getElementById('state').value = getPermission() + } + + function createNotification() { + let title = 'Notification #' + ++notificationsCreated + let options = { body: 'Visit doc.qt.io for more info!', icon: 'icon.png', } + + let notification = new Notification(title, options) + document.notification = notification + + notification.onerror = function(error) { + document.getElementById('act').value += ' with error' + document.notification = null + } + notification.onshow = function() { + document.getElementById('act').value += ', shown' + document.getElementById('close').style.display = 'inline' + } + notification.onclick = function() { + document.getElementById('act').value += ', clicked' + } + notification.onclose = function() { + if (document.notification && notification == document.notification) { + document.getElementById('act').value += ' and closed' + document.getElementById('close').style.display = 'none' + document.notification = null + } + } + + console.log('...notification created [Title: ' + title + ']') + document.getElementById('act').value = 'Notification was created' + } + + function onMakeNotification() { + if (getPermission() == 'granted') { + createNotification() + } else if (getPermission() == 'denied') { + setTimeout(function() { + if (window.confirm('Notifications are disabled!\n' + + 'Permission needs to be granted by user. Reset?')) + resetPermission() + }, 1) + } else { + Notification.requestPermission().then(function (permission) { + console.info('notifications request: ' + permission) + resetPermission(permission) + if (permission == 'granted') + createNotification() + }) + } + } + + function closeNotification() { if (document.notification) document.notification.close() } + + document.addEventListener('DOMContentLoaded', function() { resetPermission(Notification.permission) }) +</script> +</head> +<body style='text-align:center;'> + <h3>Click the button to send a notification</h3> + + <button onclick='onMakeNotification()'>Notify!</button> + + <p> + <output id='act'></output> + <button id='close' style='display: none;' onclick='closeNotification()'>Close</button> + </p><br> + + <p> + <label for='state'>Permission:</label> + <output id='state'></output> + <button onclick='resetPermission()'>Reset</button> + </p><br> + + <h4>More info can be found on:</h4> + <ul style='list-style-type: none;'> + <li>W3 <a href='https://www.w3.org/TR/notifications'>Web Notifications</a> standard</li> + <li>Documentation for <a href='https://doc.qt.io'>Qt WebEngine</a> module</li> + </ul> +</body> +</html> diff --git a/examples/webenginewidgets/notifications/doc/images/notifications-example.png b/examples/webenginewidgets/notifications/doc/images/notifications-example.png Binary files differnew file mode 100644 index 000000000..671cd1703 --- /dev/null +++ b/examples/webenginewidgets/notifications/doc/images/notifications-example.png diff --git a/examples/webenginewidgets/notifications/doc/src/notifications.qdoc b/examples/webenginewidgets/notifications/doc/src/notifications.qdoc new file mode 100644 index 000000000..f4fe1818f --- /dev/null +++ b/examples/webenginewidgets/notifications/doc/src/notifications.qdoc @@ -0,0 +1,133 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example webenginewidgets/notifications + \examplecategory {Web Technologies} + \title WebEngine Notifications Example + \ingroup webengine-widgetexamples + \brief Demonstrates how to pass HTML5 web notifications to users. + + \image notifications-example.png + + \e {\WebEngine Notifications} demonstrates how to use the + \l QWebEngineProfile::setNotificationPresenter() method and + \l QWebEngineNotification class to show an HTML5 web + notification to the user. + + \include examples-run.qdocinc + + \section1 HTML Page + + In this example, we create an internal HTML page that is added through + a resource collection file (.qrc). The page displays buttons for requesting + permissions and contains necessary JavaScript code to trigger this request: + + \quotefromfile webenginewidgets/notifications/data/index.html + \skipto Notification.requestPermission + \printline requestPermission + \skipuntil resetPermission + \printuntil /\}\)$/ + + Also page contains a button for creating a notification. The following + JavaScript constructions are executed on the press event: + + \quotefromfile webenginewidgets/notifications/data/index.html + \skipto createNotification() + \printuntil new Notification + \skipuntil Notification was created + \printline } + + \section1 Main Function + + In the \c main function, we instantiate a QWebEngineView, load our internal + HTML page, and set up the required callbacks for notifications handling. + + \section2 Requesting Feature Permissions + + We then use the \l QWebEnginePage::featurePermissionRequested() call to + request the user's permission to show notifications on their device. + + \quotefromfile webenginewidgets/notifications/main.cpp + \skipto featurePermissionRequested + \printuntil }); + + \section2 Handling New Notifications + + We then construct a \c NotificationPopup that encapsulates + the data of the HTML web notification. We also use the + \l QWebEngineProfile::setNotificationPresenter() call to set + our handler, which we use in conjunction with our \c popup + to handle all new notifications. + + \skipto popup + \printuntil }); + + \section1 Presenting Notifications to Users + + The \c NotificationPopup class in this example is a simple + QWidget-based class that uses multiple QLabel instances + for displaying the notification's title, message, and icon. + + \quotefromfile webenginewidgets/notifications/notificationpopup.h + \skipto class NotificationPopup + \printto public: + + \section2 Presenting Notifications + + Inside the \c present method, we first close and release the previous + notification if we have one and then take ownership of a new notification + by calling the \c std::unique_ptr::swap method on our internal notification + instance. + + \skipto present + \printto m_title + + Then we query the notification instance for a title, a message, + and an icon by calling \l QWebEngineNotification::title(), + \l QWebEngineNotification::message(), \l QWebEngineNotification::icon() + and set up the appropriate labels in our popup. + + \printuntil m_icon + + After that we are ready to display our notification to the user + by calling the \l QWidget::show() method. On this step we also call the + \l QWebEngineNotification::show() method to notify \c JavaScript code + about our \e show event. + + \printuntil notification->show + + Finally, we set up a callback to handle the \e close event from the + \c JavaScript side by connecting to the \l QWebEngineNotification::closed() + signal. We also schedule a timer event to close our active notification + automatically. + + \skipto QWebEngineNotification::closed + \printuntil QTimer + \skipto /\}/ + \printline /\}/ + + \section2 Closing Active Notification + + We execute the \e close step for the currently active notification either by + timeout or by handling the \c JavaScript event. First, we hide the popup + widget itself by calling \l QWidget::hide(). Then, we notify the \c JavaScript + code by calling the \l QWebEngineNotification::close() method. Finally, we + destroy the notification object through the \c std::unique_ptr::reset() method. + + \skipto onClosed + \printuntil } + + \section2 Implementing User Interaction + + To implement the \e click step for a notification, we handle mouse interaction + through \l QWidget::mouseReleaseEvent(). On this event, the \c JavaScript code + is notified by calling the \l QWebEngineNotification::click() method. + Then we automatically perform the \e close step as a notification is + considered fully handled and no longer needed, and therefore can be destroyed. + + \skipto mouseReleaseEvent + \printuntil onClosed + \printline /\}$/ + \printline /\}$/ +*/ diff --git a/examples/webenginewidgets/notifications/main.cpp b/examples/webenginewidgets/notifications/main.cpp new file mode 100644 index 000000000..c754aff3f --- /dev/null +++ b/examples/webenginewidgets/notifications/main.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "notificationpopup.h" + +#include <QApplication> +#include <QDesktopServices> +#include <QWebEnginePage> +#include <QWebEngineProfile> +#include <QWebEngineView> + +class WebEnginePage : public QWebEnginePage +{ +public: + WebEnginePage(QWidget *parent) : QWebEnginePage(parent) { } + + bool acceptNavigationRequest(const QUrl &url, NavigationType, bool) override + { + if (url.scheme() != "https") + return true; + QDesktopServices::openUrl(url); + return false; + } +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QApplication app(argc, argv); + + QWebEngineView view; + + // set custom page to open all page's links for https scheme in system browser + view.setPage(new WebEnginePage(&view)); + + QObject::connect(view.page(), &QWebEnginePage::featurePermissionRequested, + [&] (const QUrl &origin, QWebEnginePage::Feature feature) { + if (feature != QWebEnginePage::Notifications) + return; + view.page()->setFeaturePermission(origin, feature, QWebEnginePage::PermissionGrantedByUser); + }); + + auto profile = view.page()->profile(); + auto popup = new NotificationPopup(&view); + profile->setNotificationPresenter([&] (std::unique_ptr<QWebEngineNotification> notification) + { popup->present(notification); }); + + view.resize(640, 480); + view.show(); + view.setUrl(QStringLiteral("qrc:/index.html")); + return app.exec(); +} + diff --git a/examples/webenginewidgets/notifications/notificationpopup.h b/examples/webenginewidgets/notifications/notificationpopup.h new file mode 100644 index 000000000..57912f204 --- /dev/null +++ b/examples/webenginewidgets/notifications/notificationpopup.h @@ -0,0 +1,90 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#pragma once + +#include <QHBoxLayout> +#include <QLabel> +#include <QMouseEvent> +#include <QPushButton> +#include <QSpacerItem> +#include <QTimer> +#include <QVBoxLayout> +#include <QWebEngineNotification> + +#include <memory> + +class NotificationPopup : public QWidget +{ + Q_OBJECT + + QLabel m_icon, m_title, m_message; + std::unique_ptr<QWebEngineNotification> notification; + +public: + NotificationPopup(QWidget *parent) : QWidget(parent) + { + setWindowFlags(Qt::ToolTip); + auto rootLayout = new QHBoxLayout(this); + + rootLayout->addWidget(&m_icon); + + auto bodyLayout = new QVBoxLayout; + rootLayout->addLayout(bodyLayout); + + auto titleLayout = new QHBoxLayout; + bodyLayout->addLayout(titleLayout); + + titleLayout->addWidget(&m_title); + titleLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); + + auto close = new QPushButton(tr("Close")); + titleLayout->addWidget(close); + connect(close, &QPushButton::clicked, this, &NotificationPopup::onClosed); + + bodyLayout->addWidget(&m_message); + adjustSize(); + } + + void present(std::unique_ptr<QWebEngineNotification> &newNotification) + { + if (notification) { + notification->close(); + notification.reset(); + } + + notification.swap(newNotification); + + m_title.setText("<b>" + notification->title() + "</b>"); + m_message.setText(notification->message()); + m_icon.setPixmap(QPixmap::fromImage(notification->icon()).scaledToHeight(m_icon.height())); + + show(); + notification->show(); + + connect(notification.get(), &QWebEngineNotification::closed, this, &NotificationPopup::onClosed); + QTimer::singleShot(10000, notification.get(), [&] () { onClosed(); }); + + // position our popup in the right corner of its parent widget + move(parentWidget()->mapToGlobal(parentWidget()->rect().bottomRight() - QPoint(width() + 10, height() + 10))); + } + +protected slots: + void onClosed() + { + hide(); + notification->close(); + notification.reset(); + } + +protected: + void mouseReleaseEvent(QMouseEvent *event) override + { + QWidget::mouseReleaseEvent(event); + if (notification && event->button() == Qt::LeftButton) { + notification->click(); + onClosed(); + } + } +}; + diff --git a/examples/webenginewidgets/notifications/notifications.pro b/examples/webenginewidgets/notifications/notifications.pro new file mode 100644 index 000000000..6e276d405 --- /dev/null +++ b/examples/webenginewidgets/notifications/notifications.pro @@ -0,0 +1,10 @@ +QT += webenginewidgets + +HEADERS = notificationpopup.h + +SOURCES = main.cpp + +RESOURCES = data/data.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/notifications +INSTALLS += target |