[win] Use native desktop notifications for prettier notifications

This commit is contained in:
Nyall Dawson 2018-08-07 13:05:36 +10:00
parent bbbc5b2562
commit 8876270c9f
11 changed files with 1546 additions and 6 deletions

21
external/wintoast/LICENSE.txt vendored Executable file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

236
external/wintoast/README.md vendored Executable file
View File

@ -0,0 +1,236 @@
![releases](https://img.shields.io/github/tag/mohabouje/WinToast.svg)
![issues](https://img.shields.io/github/issues/mohabouje/WinToast.svg)
![license](https://img.shields.io/github/license/mohabouje/WinToast.svg)
![built](https://img.shields.io/badge/built%20with-MVSC-6f62ba.svg)
[![GitHub forks](https://img.shields.io/github/forks/mohabouje/WinToast.svg?style=social&label=Fork)]()
[![GitHub stars](https://img.shields.io/github/stars/mohabouje/WinToast.svg?style=social&label=Star)]()
[![GitHub watchers](https://img.shields.io/github/watchers/mohabouje/WinToast.svg?style=social&label=Watch)]()
***
WinToast
===================
WinToast is a lightly library written in C++ which brings a complete integration of the modern **toast notifications** of **Windows 8** & **Windows 10**.
Toast notifications allows your app to inform the users about relevant information and timely events that they should see and take action upon inside your app, such as a new instant message, a new friend request, breaking news, or a calendar event.
1. [Toast Templates](#id1)
2. [Event Handler](#id3)
3. [Expiration Time](#id4)
4. [Additional features available on Windows 10](#id5)
5. [Error Handling](#id2)
6. [Example of usage](#id6)
7. [Toast configuration on Windows 10](#id7)
8. [Projects using WinToast](#id8)
<div id='id1' />
## Toast Templates
WinToast integrates all standard templates available in the [ToastTemplateType enumeration](https://msdn.microsoft.com/en-us/library/windows/apps/br208660.aspx).
| Template | Description | Example |
| :------- | ----: | :---: |
| `ImageAndText01` | A large image and a single string wrapped across three lines of text. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601606.png) |
| `ImageAndText02` | A large image, one string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![12](https://i-msdn.sec.s-msft.com/dynimg/IC601607.png) |
| `ImageAndText03` | A large image, one string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601608.png) |
| `ImageAndText04` | A large image, one string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601609.png) |
| `Text01` | Single string wrapped across three lines of text. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601602.png)|
| `Text02` | One string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601603.png) |
| `Text03` | One string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601604.png)|
| `Text04` | One string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601605.png) |
Example of a `ImageAndText02` template:
```cpp
WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02);
templ.setTextField(L"title", WinToastTemplate::FirstLine);
templ.setTextField(L"subtitle", WinToastTemplate::SecondLine);
templ.setImagePath(L"C:/example.png");
```
**Note:** The user can use the default system sound or specify a sound to play when a toast notification is displayed. Same behavior for the toast notification image, by default Windows try to use the app icon.*
<div id='id3' />
## Event Handler
WinToast handle different events:
- **Activated**: Occurs when user activates a toast notification through a click or touch. Apps that are running subscribe to this event
- **Dismissed**: Occurs when a toast notification leaves the screen, either by expiring or being explicitly dismissed by the user.
* Application Hidden: The application hid the toast using ToastNotifier.hide.
* User Canceled: The user dismissed the toast.
* Timed Out: The toast has expired
- **Failed**: Occurs when an error is caused when Windows attempts to raise a toast notification.
Create your custom handler to interact with the user actions by subclassing the interface `IWinToastHandler`:
```cpp
class WinToastHandlerExample : public IWinToastHandler {
public:
WinToastHandlerExample();
// Public interfaces
void toastActivated() const override;
void toastDismissed(WinToastDismissalReason state) const override;
void toastFailed() const override;
};
```
<div id='id4' />
## Expiration Time
Set the time after which a toast notification is no longer considered current or valid and should not be displayed. Windows attempts to raise toast notifications immediately after you call Show, so this property is rarely used.
> For Windows 8.x app, this property also causes the toast notification to be removed from the
> Action Center once the specified data and time is reached.
**Note:** Default Windows behavior is to hide notification automatically after time set in Windows Ease of Access Settings.
If you need to preserve notification in Windows Action Center for longer period of time, you have to call `WinToastTemplate::setExpiration` method.
<div id='id5' />
## Additional features available on Windows 10
If your system supports the new modern features (Version > Windows 8.1) available in Windows 10, you can add some interesting fields as:
- **Actions**: you can add your own actions, this fact allow you to interact with user in a different way:
```cpp
WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text02);
templ.setTextField(L"Do you think this feature is cool?", WinToastTemplate::FirstLine);
templ.setTextField(L"Ofc,it is!", WinToastTemplate::SecondLine);
std::vector<std::wstring> actions;
actions.push_back(L"Yes");
actions.push_back(L"No");
for (auto const &action : actions)
templ.addAction(action);
WinToast::instance()->showToast(templ, handler)
```
!["Toast with some actions"](https://lh3.googleusercontent.com/uJE_H0aBisOZ-9GynEWgA7Hha8tHEI-i0aHrFuOFDBsPSD-IJ-qEN0Y7XY4VI5hp_5MQ9xjWbFcm)
- **Attribution text**: you can add/remove the attribution text, by default is empty. Use `WinToastTemplate::setAttributionText` to modify it.
- **Duration**: The amount of time the toast should display. This attribute can have one of the following values:
- *System*: default system configuration.
- *Short*: default system short time configuration.
- *Long*: default system long time configuration.
- **Audio Properties**: you can modify the different behaviors of the sound:
- *Default*: plays the audio file just one time.
- *Silent*: turn off the sound.
- *Loop*: plays the given sound in a loop during the toast existence.
> WinToast allows the modification of the default audio file. Add
> the given file in to your projects resources (*must be ms-appx:// or
> ms-appdata:// path*) and define it by calling: `WinToastTemplate::setAudioPath`
***By default, WinToast checks if your systems support the features, ignoring the not supported ones.***
<div id='id2' />
## Error Handling
There are several reasons WinToast can fail that's why the library notifies caller about fail reason. Those are the code for each failure:
| WinToastError | Error Code | Error message |
|--|--|--|
| `NoError` | 0x00 | No error. The process was executed correctly |
| `NotInitialized` | 0x01 | The library has not been initialized |
| `SystemNotSupported` | 0x02 | The OS does not support WinToast |
| `ShellLinkNotCreated` | 0x03 | The library was not able to create a Shell Link for the app |
| `InvalidAppUserModelID` | 0x04 | The AUMI is not a valid one |
| `InvalidParameters` | 0x05 | The parameters used to configure the library are not valid normally because an invalid AUMI or App Name |
| `NotDisplayed` | 0x06 | The toast was created correctly but WinToast was not able to display the toast |
| `UnknownError` | 0x07 | Unknown error |
A common example of usage is to check while initializing the library or showing a toast notification the possible failure code:
```cpp
WinToast::WinToastError error;
const bool succedded = WinToast::instance()->initialize(&error);
if (!succedded) {
std::wcout << L"Error, could not initialize the lib. Error number: "
<< error << std::endl;
}
...
// Configure the template
...
const bool launched = WinToast::instance()->showToast(templ, handler, &error);
if (!launched) {
std::wcout << L"Error: Could not launch your toast notification. Error: "
<< error << std::endl;
}
```
<div id='id6' />
## Example of Usage
*For an easy usage, you can just use the available singleton instance.*
First step, Import the header file wintoastlib.h to your project. You should check if your Windows Version is supported by the library.
```cpp
using namespace WinToastLib;
....
if (!WinToast::isCompatible()) {
std::wcout << L"Error, your system in not supported!" << std::endl;
}
```
Configure your [App User Model Id](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459%28v=vs.85%29.aspx), this can be done by using the existing helper:
```cpp
WinToast::instance()->setAppName(L"WinToastExample");
const auto aumi = WinToast::configureAUMI(L"mohabouje", L"wintoast", L"wintoastexample", L"20161006");
WinToast::instance()->setAppUserModelId(aumi);
```
Initialize all the dependencies and check if WinToast has been initialized successfully in your system:
```cpp
if (!WinToast::instance()->initialize()) {
std::wcout << L"Error, could not initialize the lib!" << std::endl;
}
```
Implement your own action handler by subclassing the interface `IWinToastHandler` and custom your template:
```cpp
WinToastHandlerExample* handler = new WinToastHandlerExample;
WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02);
templ.setImagePath(L"C:/example.png");
templ.setTextField(L"title", WinToastTemplate::FirstLine);
templ.setTextField(L"subtitle", WinToastTemplate::SecondLine);
```
Finally show the results:
```cpp
if (!WinToast::instance()->showToast(templ, handler)) {
std::wcout << L"Error: Could not launch your toast notification!" << std::endl;
}
```
<div id='id7' />
## Toast configuration on Windows 10
Windows allows the configuration of the default behavior of a toast notification. This can be done in the *Ease of Access* configuration by modifying the *Other options* tab.
The system configuration helps you to define how long you want notifications to appear for (5 seconds to 5 minutes) as turning on visual notifications for sound.
![Ease of Access configuration](https://camo.githubusercontent.com/56c8edd1a7a4a43be07ba211d9d828478fdbad39/68747470733a2f2f7777772e686f77746f6765656b2e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30332f656173655f6f665f6163636573732e706e67)
<div id='id8' />
## Projects using WinToast
- [Git for Windows](https://github.com/git-for-windows/git): A fork of Git containing Windows-specific patches.
- [nheko](https://github.com/mujx/nheko): Desktop client for the Matrix protocol.
- [EDPathFinder](https://github.com/neotron/EDPathFinder): A program that creates an optimal route that passes through two or more systems in Elite.
- [AntiExploit](https://github.com/Empier/Anti-Exploit): antiexploit utility for Windows.
- [Zroya](https://github.com/malja/zroya): Python extension for creating native Windows notifications..
- [PidginWinToastNotifications](https://github.com/ChristianGalla/PidginWinToastNotifications): Windows Toast Notification Plugin for Pidgin.
- [Dnai-Editor](https://github.com/Nicolas-Constanty/Dnai.Editor): Visual Scripting, node editor.

1035
external/wintoast/src/wintoastlib.cpp vendored Executable file

File diff suppressed because it is too large Load Diff

163
external/wintoast/src/wintoastlib.h vendored Executable file
View File

@ -0,0 +1,163 @@
#ifndef WINTOASTLIB_H
#define WINTOASTLIB_H
#include <Windows.h>
#include <sdkddkver.h>
#include <WinUser.h>
#include <ShObjIdl.h>
#include <wrl/implements.h>
#include <wrl/event.h>
#include <windows.ui.notifications.h>
#include <strsafe.h>
#include <Psapi.h>
#include <ShlObj.h>
#include <roapi.h>
#include <propvarutil.h>
#include <functiondiscoverykeys.h>
#include <iostream>
#include <winstring.h>
#include <string.h>
#include <vector>
#include <map>
using namespace Microsoft::WRL;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::UI::Notifications;
using namespace Windows::Foundation;
#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\"
#define DEFAULT_LINK_FORMAT L".lnk"
namespace WinToastLib {
class IWinToastHandler {
public:
enum WinToastDismissalReason {
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
};
virtual void toastActivated() const = 0;
virtual void toastActivated(int actionIndex) const = 0;
virtual void toastDismissed(WinToastDismissalReason state) const = 0;
virtual void toastFailed() const = 0;
};
class WinToastTemplate {
public:
enum Duration { System, Short, Long };
enum AudioOption { Default = 0, Silent = 1, Loop = 2 };
enum TextField { FirstLine = 0, SecondLine, ThirdLine };
enum WinToastTemplateType {
ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01,
ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02,
ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03,
ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04,
Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
Text04 = ToastTemplateType::ToastTemplateType_ToastText04,
WinToastTemplateTypeCount
};
WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02);
~WinToastTemplate();
void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
void setImagePath(_In_ const std::wstring& imgPath);
void setAudioPath(_In_ const std::wstring& audioPath);
void setAttributionText(_In_ const std::wstring & attributionText);
void addAction(_In_ const std::wstring& label);
void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
void setDuration(_In_ Duration duration);
void setExpiration(_In_ INT64 millisecondsFromNow);
std::size_t textFieldsCount() const;
std::size_t actionsCount() const;
bool hasImage() const;
const std::vector<std::wstring>& textFields() const;
const std::wstring& textField(_In_ TextField pos) const;
const std::wstring& actionLabel(_In_ int pos) const;
const std::wstring& imagePath() const;
const std::wstring& audioPath() const;
const std::wstring& attributionText() const;
INT64 expiration() const;
WinToastTemplateType type() const;
WinToastTemplate::AudioOption audioOption() const;
Duration duration() const;
private:
std::vector<std::wstring> _textFields;
std::vector<std::wstring> _actions;
std::wstring _imagePath = L"";
std::wstring _audioPath = L"";
std::wstring _attributionText = L"";
INT64 _expiration = 0;
AudioOption _audioOption = WinToastTemplate::AudioOption::Default;
WinToastTemplateType _type = WinToastTemplateType::Text01;
Duration _duration = Duration::System;
};
class WinToast {
public:
enum WinToastError {
NoError = 0,
NotInitialized,
SystemNotSupported,
ShellLinkNotCreated,
InvalidAppUserModelID,
InvalidParameters,
InvalidHandler,
NotDisplayed,
UnknownError
};
enum ShortcutResult {
SHORTCUT_UNCHANGED = 0,
SHORTCUT_WAS_CHANGED = 1,
SHORTCUT_WAS_CREATED = 2,
SHORTCUT_MISSING_PARAMETERS = -1,
SHORTCUT_INCOMPATIBLE_OS = -2,
SHORTCUT_COM_INIT_FAILURE = -3,
SHORTCUT_CREATE_FAILED = -4
};
WinToast(void);
virtual ~WinToast();
static WinToast* instance();
static bool isCompatible();
static bool isSupportingModernFeatures();
static std::wstring configureAUMI(_In_ const std::wstring& companyName,
_In_ const std::wstring& productName,
_In_ const std::wstring& subProduct = std::wstring(),
_In_ const std::wstring& versionInformation = std::wstring()
);
virtual bool initialize(_Out_ WinToastError* error = nullptr);
virtual bool isInitialized() const;
virtual bool hideToast(_In_ INT64 id);
virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error = nullptr);
virtual void clear();
virtual enum ShortcutResult createShortcut();
const std::wstring& appName() const;
const std::wstring& appUserModelId() const;
void setAppUserModelId(_In_ const std::wstring& appName);
void setAppName(_In_ const std::wstring& appName);
protected:
bool _isInitialized;
bool _hasCoInitialized;
std::wstring _appName;
std::wstring _aumi;
std::map<INT64, ComPtr<IToastNotification>> _buffer;
HRESULT validateShellLinkHelper(_Out_ bool& wasChanged);
HRESULT createShellLinkHelper();
HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path);
HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ int pos);
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
void setError(_Out_ WinToastError* error, _In_ WinToastError value);
};
}
#endif // WINTOASTLIB_H

View File

@ -85,7 +85,7 @@ astyleit() {
for f in "$@"; do
case "$f" in
src/plugins/grass/qtermwidget/*|external/o2/*|external/astyle/*|external/kdbush/*|python/ext-libs/*|ui_*.py|*.astyle|tests/testdata/*|editors/*)
src/plugins/grass/qtermwidget/*|external/o2/*|external/astyle/*|external/kdbush/*|external/wintoast/*|python/ext-libs/*|ui_*.py|*.astyle|tests/testdata/*|editors/*)
echo -ne "$f skipped $elcr"
continue
;;

View File

@ -1302,7 +1302,11 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
connect( QgsApplication::taskManager(), &QgsTaskManager::statusChanged, this, &QgisApp::onTaskCompleteShowNotify );
QgsGui::instance()->nativePlatformInterface()->initializeMainWindow( windowHandle() );
QgsGui::instance()->nativePlatformInterface()->initializeMainWindow( windowHandle(),
QgsApplication::applicationName(),
QgsApplication::organizationName(),
Qgis::QGIS_VERSION
);
// setup application progress reports from task manager
connect( QgsApplication::taskManager(), &QgsTaskManager::taskAdded, this, []
@ -13849,6 +13853,7 @@ void QgisApp::showSystemNotification( const QString &title, const QString &messa
if ( replaceExisting )
settings.messageId = sLastMessageId;
settings.svgAppIconPath = QgsApplication::iconsPath() + QStringLiteral( "qgis_icon.svg" );
settings.pngAppIconPath = QgsApplication::appIconPath();
QgsNative::NotificationResult result = QgsGui::instance()->nativePlatformInterface()->showDesktopNotification( title, message, settings );

7
src/native/CMakeLists.txt Normal file → Executable file
View File

@ -47,6 +47,7 @@ ENDIF(APPLE)
IF(WIN32)
SET(QGIS_APP_WIN32_SRCS
../../external/wintoast/src/wintoastlib.cpp
win/qgswinnative.cpp
)
SET(QGIS_NATIVE_SRCS ${QGIS_NATIVE_SRCS}
@ -93,6 +94,12 @@ INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_BINARY_DIR}
)
IF(WIN32)
INCLUDE_DIRECTORIES(SYSTEM
../../external/wintoast/src
)
ENDIF(WIN32)
#############################################################
# qgis_native library

View File

@ -26,7 +26,10 @@ QgsNative::Capabilities QgsNative::capabilities() const
return nullptr;
}
void QgsNative::initializeMainWindow( QWindow * )
void QgsNative::initializeMainWindow( QWindow *,
const QString &,
const QString &,
const QString & )
{
}

View File

@ -54,11 +54,17 @@ class NATIVE_EXPORT QgsNative
/**
* Initializes the native interface, using the specified \a window.
*
* The \a applicationName, \a organizationName and \a version information
* are used to initialize application-wide settings, depending on the platform.
*
* The default implementation does nothing.
*
* \since QGIS 3.4
*/
virtual void initializeMainWindow( QWindow *window );
virtual void initializeMainWindow( QWindow *window,
const QString &applicationName,
const QString &organizationName,
const QString &version );
/**
* Brings the QGIS app to front. The default implementation does nothing.
@ -129,6 +135,9 @@ class NATIVE_EXPORT QgsNative
//! Path to application icon in SVG format
QString svgAppIconPath;
//! Path to application icon in png format
QString pngAppIconPath;
//! Message ID, used to replace existing messages
QVariant messageId;
};

View File

@ -17,6 +17,7 @@
#include "qgswinnative.h"
#include <QCoreApplication>
#include <QDebug>
#include <QString>
#include <QDir>
#include <QWindow>
@ -25,8 +26,17 @@
#include <QtWinExtras/QWinJumpList>
#include <QtWinExtras/QWinJumpListItem>
#include <QtWinExtras/QWinJumpListCategory>
#include "wintoastlib.h"
void QgsWinNative::initializeMainWindow( QWindow *window )
QgsNative::Capabilities QgsWinNative::capabilities() const
{
return mCapabilities;
}
void QgsWinNative::initializeMainWindow( QWindow *window,
const QString &applicationName,
const QString &organizationName,
const QString &version )
{
if ( mTaskButton )
return; // already initialized!
@ -35,6 +45,17 @@ void QgsWinNative::initializeMainWindow( QWindow *window )
mTaskButton->setWindow( window );
mTaskProgress = mTaskButton->progress();
mTaskProgress->setVisible( false );
WinToastLib::WinToast::instance()->setAppName( applicationName.toStdWString() );
WinToastLib::WinToast::instance()->setAppUserModelId(
WinToastLib::WinToast::configureAUMI( organizationName.toStdWString(),
applicationName.toStdWString(),
applicationName.toStdWString(),
version.toStdWString() ) );
if ( WinToastLib::WinToast::instance()->initialize() )
{
mCapabilities = mCapabilities | NativeDesktopNotifications;
}
}
void QgsWinNative::openFileExplorerAndSelectFile( const QString &path )
@ -80,3 +101,37 @@ void QgsWinNative::onRecentProjectsChanged( const std::vector<QgsNative::RecentP
jumplist.recent()->addItem( newProject );
}
}
class NotificationHandler : public WinToastLib::IWinToastHandler
{
public:
void toastActivated() const override {}
void toastActivated( int ) const override {}
void toastFailed() const
{
qWarning() << "Error showing notification";
}
void toastDismissed( WinToastDismissalReason ) const override {}
};
QgsNative::NotificationResult QgsWinNative::showDesktopNotification( const QString &summary, const QString &body, const QgsNative::NotificationSettings &settings )
{
WinToastLib::WinToastTemplate templ = WinToastLib::WinToastTemplate( WinToastLib::WinToastTemplate::ImageAndText02 );
templ.setImagePath( settings.pngAppIconPath.toStdWString() );
templ.setTextField( summary.toStdWString(), WinToastLib::WinToastTemplate::FirstLine );
templ.setTextField( body.toStdWString(), WinToastLib::WinToastTemplate::SecondLine );
templ.setDuration( WinToastLib::WinToastTemplate::Short );
NotificationResult result;
if ( WinToastLib::WinToast::instance()->showToast( templ, new NotificationHandler ) < 0 )
result.successful = false;
else
result.successful = true;
return result;
}

View File

@ -30,15 +30,21 @@ class QWindow;
class NATIVE_EXPORT QgsWinNative : public QgsNative
{
public:
void initializeMainWindow( QWindow *window ) override;
Capabilities capabilities() const override;
void initializeMainWindow( QWindow *window,
const QString &applicationName,
const QString &organizationName,
const QString &version ) override;
void openFileExplorerAndSelectFile( const QString &path ) override;
void showUndefinedApplicationProgress() override;
void setApplicationProgress( double progress ) override;
void hideApplicationProgress() override;
void onRecentProjectsChanged( const std::vector< RecentProjectProperties > &recentProjects ) override;
NotificationResult showDesktopNotification( const QString &summary, const QString &body, const NotificationSettings &settings = NotificationSettings() ) override;
private:
Capabilities mCapabilities = nullptr;
QWinTaskbarButton *mTaskButton = nullptr;
QWinTaskbarProgress *mTaskProgress = nullptr;
};