Skip to content

Commit

Permalink
Merge pull request #9 from NickvisionApps/shell-notify
Browse files Browse the repository at this point in the history
`ShellNotification` namespace + `NotifyIcon` class for Windows
  • Loading branch information
nlogozzo authored Jan 2, 2024
2 parents bb6715e + f6aa7c9 commit 54fdfda
Show file tree
Hide file tree
Showing 13 changed files with 996 additions and 6 deletions.
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if (POLICY CMP0141)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()

project ("libaura" LANGUAGES CXX VERSION 2023.12.0 DESCRIPTION "A cross-platform base for native Nickvision applications.")
project ("libaura" LANGUAGES CXX VERSION 2024.1.0 DESCRIPTION "A cross-platform base for native Nickvision applications.")
include(GNUInstallDirs)

#libaura Library Setup
Expand All @@ -35,6 +35,9 @@ add_library (${PROJECT_NAME}
src/network/networkmonitor.cpp
src/network/networkstatechangedeventargs.cpp
src/notifications/notificationsenteventargs.cpp
src/notifications/notifyicon.cpp
src/notifications/notifyiconmenu.cpp
src/notifications/shellnotification.cpp
src/notifications/shellnotificationsenteventargs.cpp
src/taskbar/taskbaritem.cpp
src/update/updater.cpp
Expand Down Expand Up @@ -94,6 +97,7 @@ if(NOT SKIP_TESTS)
tests/keyringtests.cpp
tests/main.cpp
tests/networktests.cpp
tests/notifyicontests.cpp
tests/passwordtests.cpp
tests/storetests.cpp
tests/stringtests.cpp
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ Visual Studio 2022 with C++ Desktop workload is required to be installed.
1. Open a terminal and navigate to the repo's root directory.
1. Run `conan install conanfile-windows.txt --profile:host=conanprofile-windows.txt --profile:build=conanprofile-windows.txt -s compiler.cppstd=20 --build=missing`.
1. Once that command finishes, cd into the `build` folder.
1. From the `build` folder, run `cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="generators/conan_toolchain.cmake"` and `cmake --build . --config Release`.
1. From the `build` folder, run `cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="generators/conan_toolchain.cmake"`.
- To skip building libaura's test suite, add `-DSKIP_TESTS="true"` to the end of the command.
1. From the `build` folder, run ``cmake --build . --config Release`.
1. After these commands, libaura will be successfully built and its binaries can be found in the `Release` folder of the `build` folder.
1. To install libaura to the system, run `cmake --install . --prefix "PATH_TO_INSTALL_DIR"`.
1. To install libaura to the system, from the `build` folder, run `cmake --install . --prefix "PATH_TO_INSTALL_DIR"`.
- Replace `PATH_TO_INSTALL_DIR` with the path to a folder to install libaura to. This is usually a dependencies folder set up by the programmer, added to the PATH variable, to allow linking to said dependencies.
1. If contributing to upstream, remove `conanfile-windows.txt` and `conanprofile-windows.txt` from the repo's root directory as to not accidentally add and commit them.

Expand All @@ -41,9 +43,11 @@ Visual Studio 2022 with C++ Desktop workload is required to be installed.
1. Run `conan config install settings_user.yml`.
1. Run `conan install conanfile-linux.txt --profile:host=conanprofile-linux.txt --profile:build=conanprofile-linux.txt -s compiler.cppstd=20 --build=missing`.
1. Once that command finished, cd into the `build` folder.
1. From the `build` folder, run `cmake .. -DCMAKE_TOOLCHAIN_FILE="Release/generators/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release` and `cmake --build`.
1. From the `build` folder, run `cmake .. -DCMAKE_TOOLCHAIN_FILE="Release/generators/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release`.
- To skip building libaura's test suite, add `-DSKIP_TESTS="true"` to the end of the command.
1. From the `build` folder, run `cmake --build`.
1. After these commands, libaura will be successfully built and its binaries can be found in the `Release` folder of the `build` folder.
1. To install libaura to the system, run `cmake --install . --prefix "PATH_TO_INSTALL_DIR"`.
1. To install libaura to the system, from the `build` folder, run `cmake --install . --prefix "PATH_TO_INSTALL_DIR"`.
- Replace `PATH_TO_INSTALL_DIR` with the path to a folder to install libaura to. This is usually a dependencies folder set up by the programmer, added to the PATH variable, to allow linking to said dependencies.
- On linux, `PATH_TO_INSTALL_DIR` would usually be `/usr`.
1. If contributing to upstream, remove `conanfile-windows.txt` and `conanprofile-windows.txt` from the repo's root directory as to not accidentally add and commit them.
165 changes: 165 additions & 0 deletions docs/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ This module contains objects for sending notifications within Nickvision applica
## Table of Contents
- [NotificationSentEventArgs](#notificationsenteventargs)
- [NotificationSeverity](#notificationseverity)
- [NotifyIcon](#notifyicon)
- [NotifyIconMenu](#notifyiconmenu)
- [ShellNotification](#shellnotification)
- [ShellNotificationSentEventArgs](#shellnotificationsenteventargs)

## NotificationSentEventArgs
Expand Down Expand Up @@ -68,6 +71,168 @@ Path: `Nickvision::Aura::Notifications::NotificationSeverity`
```
- Represents an error notification

## NotifyIcon
Description: An icon for the system tray.

Note: This API is only available on the Windows platform.

Interface: [notifyicon.h](/include/notifications/notifyicon.h)

Type: `class`

Path: `Nickvision::Aura::Notifications::NotifyIcon`

### Methods
- ```cpp
NotifyIcon(HWND hwnd, const NotifyIconMenu& contextMenu = { }, bool hidden = false);
```
- Constructs a NotifyIcon.
- Accepts: The `HWND` handle for the main application window, hwnd, the context menu model for the icon, contextMenu, and whether or not to hide the icon by default, hidden.
- Throws: std::logic_error if Aura::init() was not called yet.
- Throws: std::runtime_error if unable to create the NotifyIcon.
- ```cpp
~NotifyIcon()
```
- Destructs a NotifyIcon.
- ```cpp
bool hide()
```
- Returns: `true` if the icon was hidden.
- Returns: `false` if the icon was not hidden.
- ```cpp
bool show()
```
- Returns: `true` if the icon was shown.
- Returns: `false` if the icon was not shown.
- ```cpp
const std::string& getTooltip() const
```
- Returns: The tooltip text of the icon.
- ```cpp
bool setTooltip(const std::string& tooltip)
```
- Accepts: The tooltip text to show on the icon, tooltip.
- Returns: `true` if the tooltip was updated.
- Returns: `false` if the tooltip was not updated.
- ```cpp
bool notify(const ShellNotificationSentEventArgs& e)
```
- Accepts: The `ShellNotificationSentEventArgs` containing the information to show on the NotifyIcon, e.
- Returns: `true` if the notification was shown from the NotifyIcon.
- Returns: `false` if the notification was not shown from the NotifyIcon.
- Note: Supports the action "open" with action param being a path of a file or folder to open.

## NotifyIconMenu
Description: A menu for a NotifyIcon.

Note: This API is only available on the Windows platform.

Interface: [notifyiconmenu.h](/include/notifications/notifyiconmenu.h)

Type: `class`

Path: `Nickvision::Aura::Notifications::NotifyIconMenu`

Note: There are other classes, notably: `NotifyIconMenuItem`, `NotifyIconSeparatorMenuItem`, `NotifyIconActionMenuItem`, that are meant for use by `NotifyIcon`'s implementation of the context menu itself. Users are free to use these objects, however they will not be covered in this documentation.

### Methods
- ```cpp
size_t size() const
```
- Returns: The number of items stored in the menu.
- ```cpp
bool empty() const
```
- Returns: `true` if the menu is empty.
- Returns: `false` if the menu is not empty.
- ```cpp
size_t addSeparator()
```
- Returns: The index at which the new separator was added.
- ```cpp
bool insertSeparator(size_t index)
```
- Accepts: The index at which to insert a new separator, index.
- Returns: `true` if the separator was inserted.
- Returns: `false` if the separator was not inserted.
- ```cpp
bool removeSeparator(size_t index)
```
- Accepts: The index at which to remove a separator, index.
- Returns: `true` if the separator was removed.
- Returns: `false` if the separator was not removed. This could mean that the index was invalid or that the index pointed a menu item that was not a separator.
- ```cpp
size_t addAction(const std::string& label, const std::function<void()>& action)
```
- Accepts: The label of the new action item, label, and the callback function of the action, action.
- Returns: The index at which the new action item was added.
- ```cpp
bool insertAction(size_t index, const std::string& label, const std::function<void()>& action)
```
- Accepts: The index at which to insert a new action item, index, the label of the new action, label, and the callback function of the action, action.
- Returns: `true` if the action item was inserted.
- Returns: `false` if the action item was not inserted.
- ```cpp
bool removeAction(size_t index)
```
- Accepts: The index at which to remove an action item, index.
- Returns: `true` if the action item was removed.
- Returns: `false` if the action item was not removed. This could mean that the index was invalid or that the index pointed a menu item that was not an action item.
### Creating a Context Menu
The `NotifyMenuItem` provides an easy API for creating context menus for a `NotifyIcon` to use within your app.
Assume we want to create the following context menu for a `NotifyIcon`:
```
---------------
| Show Window |
| ----------- |
| Exit |
---------------
```
Here is the code we could use to accomplish this:
```cpp
...
bool m_running = true;
NotifyIconMenu contextMenu;
contextMenu.addAction("Show Window", [&]()
{
showWindow();
});
contextMenu.addSeparator();
contextMenu.addAction("Exit", [&m_running]()
{
m_running = false;
});
NotifyIcon icon{ GetConsoleWindow(), contextMenu };
...
```
## ShellNotification
Description: Functions for working with shell (desktop) notifications.

Interface: [shellnotification.h](/include/notifications/shellnotification.h)

Type: `namespace`

Path: `Nickvision::Aura::Notifications::ShellNotification`

### Functions
- ```cpp
void send(const ShellNotificationSentEventArgs& e, HWND hwnd)
```
- Accepts: The `ShellNotificationSentEventArgs` containing the information to show, e, and the `HWND` handle for the main application window, hwnd. The hwnd parameter is only used once on the initial creation of the static `NotifyIcon` and then is ignored on future calls.
- Note: This function is only available on the Windows platform. It uses `Nickvision::Aura::Notifications::NotifyIcon`.
- Note: This function supports the action "open" with action param being a path of a file or folder to open.
- Throws: std::logic_error if Aura::init() was not called yet
- ```cpp
void send(const ShellNotificationSentEventArgs& e)
```
- Accepts: The `ShellNotificationSentEventArgs` containing the information to show, e.
- Note: This function is only available on the Linux platform.
- Note: This function supports the action "open" with action param being a path of a file or folder to open. The app must define an "app.open" action to handle this event.
- Throws: std::logic_error if Aura::init() was not called yet

## ShellNotificationSentEventArgs
Description: Event args for when a shell notification is sent.

Expand Down
103 changes: 103 additions & 0 deletions include/notifications/notifyicon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#ifdef _WIN32
#ifndef NOTIFYICON_H
#define NOTIFYICON_H

#define _CRT_SECURE_NO_WARNINGS
#define WM_NOTIFYICON_EVENT (WM_APP + 100)
#define IDM_CONTEXT_MENU (WM_APP + 200)

#include <filesystem>
#include <map>
#include <windows.h>
#include <shellapi.h>
#include "notifyiconmenu.h"
#include "shellnotificationsenteventargs.h"

namespace Nickvision::Aura::Notifications
{
/**
* @brief An icon for the system tray.
* @brief This API is only available on Windows.
*/
class NotifyIcon
{
public:
/**
* @brief Constructs a NotifyIcon.
* @param hwnd The HWND handle of the main application window
* @param menu The model for the context menu of the NotifyIcon
* @param hidden Whether or not the NotifyIcon should be hidden by default
* @throw std::logic_error Thrown if Aura::init() was not called yet
* @throw std::runtime_error Thrown if unable to create the NotifyIcon
*/
NotifyIcon(HWND hwnd, const NotifyIconMenu& contextMenu = { }, bool hidden = false);
/**
* @brief Destructs a NotifyIcon.
*/
~NotifyIcon() noexcept;
/**
* @brief Hides the icon.
* @return True if icon was hidden, else false
*/
bool hide() noexcept;
/**
* @brief Shows the icon.
* @return True if icon was shown, else false
*/
bool show() noexcept;
/**
* @brief Gets the tooltip text of the icon.
* @return The tooltip text
*/
const std::string& getTooltip() const noexcept;
/**
* @brief Sets the tooltip text of the icon.
* @param tooltip The toolip text
* @return True if the tooltip was updated, else false
*/
bool setTooltip(const std::string& tooltip) noexcept;
/**
* @brief Shows a notification from the icon.
* @brief Supports the action "open" with action param being a path of a file or folder to open.
* @param e ShellNotificationSentEventArgs
* @return True if notification was shown from the icon
*/
bool notify(const ShellNotificationSentEventArgs& e) noexcept;

private:
/**
* @brief Gets a basic NOTIFYICONDATAA struct for this NotifyIcon.
* @return NOTIFYICONDATAA
*/
NOTIFYICONDATAA getBaseNotifyIconData() noexcept;
/**
* @brief Handles a WM_NOTIFYICON_EVENT message.
* @param wParam WPARAM
* @param lParam LPARAM
* @return LRESULT
*/
LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
std::string m_className;
bool m_isHidden;
std::string m_tooltip;
NotifyIconMenu m_contextMenu;
HWND m_hwnd;
GUID m_guid;
HMENU m_hmenu;
std::filesystem::path m_openPath;

private:
static std::map<HWND, NotifyIcon*> m_icons;
/**
* @brief The window procedure for NotifyIcons
* @param hwnd HWND
* @param uMsg UINT
* @param wParam WPARAM
* @param lParam LPARAM
*/
static LRESULT notifyIconWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
};
}

#endif //NOTIFYICON_H
#endif
Loading

0 comments on commit 54fdfda

Please sign in to comment.