1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 2020  Warzone 2100 Project
4 
5 	Warzone 2100 is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Warzone 2100 is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Warzone 2100; if not, write to the Free Software
17 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /** @file
20  *  Interface to the initialisation routines.
21  */
22 
23 #ifndef __INCLUDED_SRC_NOTIFICATIONS_H__
24 #define __INCLUDED_SRC_NOTIFICATIONS_H__
25 
26 #include <list>
27 #include <memory>
28 #include <functional>
29 #include <vector>
30 
31 class WZ_Notification_Display_Options
32 {
33 public:
34 	// The default WZ_Notification_Display_Options is useful for notifications
35 	// that should _always_ be displayed.
WZ_Notification_Display_Options()36 	WZ_Notification_Display_Options() {}
37 
38 public:
39 	// Sets a specific notification identifier to "display once".
40 	//
41 	// Once a notification with this identifier is seen once, future calls to `addNotification` with a
42 	// matching "uniqueNotificationIdentifier" will be ignored.
43 	// The "seen" status is persisted across application runs.
44 	//
45 	// Specify a uniqueNotificationIdentifier that uniquely identifies this notification, and is used to
46 	// match against future notifications.
makeOneTime(const std::string & uniqueNotificationIdentifier)47 	static WZ_Notification_Display_Options makeOneTime(const std::string& uniqueNotificationIdentifier)
48 	{
49 		WZ_Notification_Display_Options options;
50 		options._uniqueNotificationIdentifier = uniqueNotificationIdentifier;
51 		options._isOneTimeNotification = true;
52 		return options;
53 	}
54 
55 	// Sets a specific notification identifier as "ignorable".
56 	//
57 	// This will cause a "Do Not Show Again" checkbox / option to be displayed after this
58 	// uniquely identified notification is displayed `numTimesSeenBeforeDoNotShowAgainOption`
59 	// times. (Prior instances of this notification will offer a "Dismiss" or "Not Now" option.)
60 	//
61 	// If the user selects / checks the "Do Not Show Again" option, future calls to
62 	// `addNotification` with a matching "uniqueNotificationIdentifier" will be ignored.
63 	// The display count / ignore state is persisted across application runs.
64 	//
65 	// Specify a uniqueNotificationIdentifier that uniquely identifies this notification, and is used to
66 	// match against future notifications.
67 	static WZ_Notification_Display_Options makeIgnorable(const std::string& uniqueNotificationIdentifier, uint8_t numTimesSeenBeforeDoNotShowAgainOption = 0)
68 	{
69 		WZ_Notification_Display_Options options;
70 		options._uniqueNotificationIdentifier = uniqueNotificationIdentifier;
71 		options._numTimesSeenBeforeDoNotShowAgainOption = numTimesSeenBeforeDoNotShowAgainOption;
72 		return options;
73 	}
74 public:
uniqueNotificationIdentifier()75 	const std::string& uniqueNotificationIdentifier() const { return _uniqueNotificationIdentifier; }
numTimesSeenBeforeDoNotShowAgainOption()76 	uint8_t numTimesSeenBeforeDoNotShowAgainOption() const { return _numTimesSeenBeforeDoNotShowAgainOption; }
isOneTimeNotification()77 	bool isOneTimeNotification() const { return _isOneTimeNotification; }
78 
79 private:
80 	// A string that uniquely identifies the notification
81 	// (to match against future notifications)
82 	std::string _uniqueNotificationIdentifier;
83 
84 	// The number of times this identified notification can be displayed
85 	// before the "Do Not Show Again" option is offered
86 	uint8_t _numTimesSeenBeforeDoNotShowAgainOption = 0;
87 
88 	bool _isOneTimeNotification = false;
89 };
90 
91 class WZ_Notification_Image
92 {
93 public:
94 	enum class ImageType {
95 		PNG
96 	};
97 public:
WZ_Notification_Image()98 	WZ_Notification_Image()
99 	: _type(ImageType::PNG)
100 	{ }
101 
102 	explicit WZ_Notification_Image(const char* imagePath, const ImageType& imageType = ImageType::PNG)
_imagePath(imagePath)103 	: _imagePath(imagePath)
104 	, _type(imageType)
105 	{ }
106 	explicit WZ_Notification_Image(const std::string& imagePath, const ImageType& imageType = ImageType::PNG)
_imagePath(imagePath)107 	: _imagePath(imagePath)
108 	, _type(imageType)
109 	{ }
110 	explicit WZ_Notification_Image(const std::vector<unsigned char>& memoryBuffer, const ImageType& imageType = ImageType::PNG)
_memoryBuffer(memoryBuffer)111 	: _memoryBuffer(memoryBuffer)
112 	, _type(imageType)
113 	{ }
114 public:
empty()115 	bool empty() const
116 	{
117 		return _imagePath.empty() && _memoryBuffer.empty();
118 	}
imagePath()119 	const std::string& imagePath() const { return _imagePath; }
memoryBuffer()120 	const std::vector<unsigned char>& memoryBuffer() const { return _memoryBuffer; }
imageType()121 	ImageType imageType() const { return _type; }
122 private:
123 	std::string _imagePath;
124 	std::vector<unsigned char> _memoryBuffer;
125 	ImageType _type;
126 };
127 
128 class WZ_Notification; // forward-declare
129 
130 class WZ_Notification_Action
131 {
132 public:
WZ_Notification_Action()133 	WZ_Notification_Action() {}
WZ_Notification_Action(std::string actionTitle,const std::function<void (const WZ_Notification &)> & onAction)134 	WZ_Notification_Action(std::string actionTitle, const std::function<void (const WZ_Notification&)>& onAction)
135 	: title(actionTitle)
136 	, onAction(onAction)
137 	{ }
138 public:
139 	std::string title;
140 	std::function<void (WZ_Notification&)> onAction;
141 };
142 
143 enum class WZ_Notification_Dismissal_Reason {
144 	// the user pressed the "Dismiss" button (or dragged the notification upwards)
145 	USER_DISMISSED,
146 	// the notification was dismissed because the "Action" button was pressed
147 	ACTION_BUTTON_CLICK,
148 	// the notification was cancelled or dismissed by one of the `cancelOrDismissNotification*()` functions (or similar)
149 	PROGRAMMATIC
150 };
151 
152 class WZ_Notification
153 {
154 public:
155 	// [Required properties]:
156 
157 	// A title for the top of the notification - REQUIRED
158 	std::string contentTitle;
159 	// Content text for the notification - REQUIRED
160 	std::string contentText;
161 	// A duration, in game ticks (see: GAME_TICKS_PER_SEC),
162 	// for the notification to be displayed before it auto-dismisses.
163 	//
164 	// Set to 0 for "until dismissed by user".
165 	//
166 	// Do not set this interval too short or users may not be able to
167 	// read the text before the notification disappears!
168 	uint32_t duration = 0;
169 
170 	// [Optional properties]:
171 
172 	// A PNG to be displayed on the right side of the notification.
173 	// (Suggestion: Make sure the PNG is *at least* 72x72 pixels to ensure sharper display on higher resolution screens.)
174 	WZ_Notification_Image largeIcon;
175 
176 	// Displays an Action button on the notification which calls the handler when clicked.
177 	WZ_Notification_Action action;
178 	// See: WZ_Notification_Display_Options
179 	WZ_Notification_Display_Options displayOptions;
180 	// An optional string tag that can be filtered on to cancel / dismiss existing notifications
181 	// Multiple notifications can share the same tag, if they ought to be "grouped" for this purpose
182 	std::string tag;
183 	// Called when the notification is initially (fully) displayed
184 	std::function<void (const WZ_Notification&)> onDisplay;
185 	// Called when the notification is dismissed / closed
186 	// see: `WZ_Notification_Dismissal_Reason`
187 	std::function<void (const WZ_Notification&, WZ_Notification_Dismissal_Reason reason)> onDismissed;
188 	// Called if an ignorable notification is ignored / not displayed
189 	std::function<void (const WZ_Notification&)> onIgnored;
190 public:
isIgnorable()191 	bool isIgnorable() const { return !displayOptions.uniqueNotificationIdentifier().empty(); }
192 };
193 
194 class WZ_Notification_Trigger
195 {
196 public:
197 	// A time interval, in game ticks (see: GAME_TICKS_PER_SEC),
198 	// from the time the notification is submitted before it is displayed.
WZ_Notification_Trigger(uint32_t timeInterval)199 	WZ_Notification_Trigger(uint32_t timeInterval)
200 	: timeInterval(timeInterval)
201 	{ }
202 
203 	// Can be displayed immediately.
Immediate()204 	static WZ_Notification_Trigger Immediate()
205 	{
206 		return WZ_Notification_Trigger(0);
207 	}
208 public:
209 	uint32_t timeInterval = 0;
210 };
211 
212 // Add a notification
213 // - must be called from the main thread
214 void addNotification(const WZ_Notification& notification, const WZ_Notification_Trigger& trigger);
215 
216 // Remove notification preferences by unique notification identifier
217 // - must be called from the main thread
218 bool removeNotificationPreferencesIf(const std::function<bool (const std::string& uniqueNotificationIdentifier)>& matchIdentifierFunc);
219 
220 enum class NotificationScope {
221 	DISPLAYED_ONLY,
222 	QUEUED_ONLY,
223 	DISPLAYED_AND_QUEUED
224 };
225 
226 // Whether one or more notifications with the specified tag (exact match) are currently-displayed or queued
227 // If `scope` is `DISPLAYED_ONLY`, only currently-displayed notifications will be processed
228 // If `scope` is `QUEUED_ONLY`, only queued notifications will be processed
229 bool hasNotificationsWithTag(const std::string& tag, NotificationScope scope = NotificationScope::DISPLAYED_AND_QUEUED);
230 
231 // Cancel or dismiss existing notifications by tag (exact match)
232 // If `scope` is `DISPLAYED_ONLY`, only currently-displayed notifications will be processed
233 // If `scope` is `QUEUED_ONLY`, only queued notifications will be processed
234 //
235 // Returns: `true` if one or more notifications were cancelled or dismissed
236 bool cancelOrDismissNotificationsWithTag(const std::string& tag, NotificationScope scope = NotificationScope::DISPLAYED_AND_QUEUED);
237 
238 // Cancel or dismiss existing notifications by tag
239 // Accepts a `matchTagFunc` that receives each (queued / currently-displayed) notification's tag,
240 // and returns "true" if it should be cancelled or dismissed (if queued / currently-displayed)
241 // If `scope` is `DISPLAYED_ONLY`, only currently-displayed notifications will be processed
242 // If `scope` is `QUEUED_ONLY`, only queued notifications will be processed
243 //
244 // Returns: `true` if one or more notifications were cancelled or dismissed
245 bool cancelOrDismissNotificationIfTag(const std::function<bool (const std::string& tag)>& matchTagFunc, NotificationScope scope = NotificationScope::DISPLAYED_AND_QUEUED);
246 
247 // In-Game Notifications System
248 bool notificationsInitialize();
249 void notificationsShutDown();
250 void runNotifications();
251 bool isDraggingInGameNotification();
252 
253 #endif // __INCLUDED_SRC_NOTIFICATIONS_H__
254