Directory: | ./ |
---|---|
File: | modules/ui/qt/notifier.cpp |
Date: | 2024-05-31 17:23:24 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 49 | 188 | 26.1% |
Branches: | 54 | 440 | 12.3% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /************************************************************************ | ||
2 | * | ||
3 | * Copyright (C) 2020-2024 IRCAD France | ||
4 | * Copyright (C) 2021 IHU Strasbourg | ||
5 | * | ||
6 | * This file is part of Sight. | ||
7 | * | ||
8 | * Sight is free software: you can redistribute it and/or modify it under | ||
9 | * the terms of the GNU Lesser General Public License as published by | ||
10 | * the Free Software Foundation, either version 3 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * Sight is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU Lesser General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public | ||
19 | * License along with Sight. If not, see <https://www.gnu.org/licenses/>. | ||
20 | * | ||
21 | ***********************************************************************/ | ||
22 | |||
23 | #include "modules/ui/qt/notifier.hpp" | ||
24 | |||
25 | #include <core/base.hpp> | ||
26 | #include <core/com/slots.hxx> | ||
27 | #include <core/runtime/path.hpp> | ||
28 | |||
29 | #include <service/macros.hpp> | ||
30 | |||
31 | #include <ui/__/registry.hpp> | ||
32 | |||
33 | #include <boost/range/iterator_range_core.hpp> | ||
34 | |||
35 | #include <QApplication> | ||
36 | |||
37 | namespace sight::module::ui::qt | ||
38 | { | ||
39 | |||
40 | static const core::com::slots::key_t POP_NOTIFICATION_SLOT = "pop"; | ||
41 | static const core::com::slots::key_t CLOSE_NOTIFICATION_SLOT = "close_notification"; | ||
42 | static const core::com::slots::key_t SET_ENUM_PARAMETER_SLOT = "set_enum_parameter"; | ||
43 | |||
44 | static const std::string POSITION_KEY("position"); | ||
45 | static const std::string DURATION_KEY("duration"); | ||
46 | static const std::string SIZE_KEY("size"); | ||
47 | static const std::string MAX_KEY("max"); | ||
48 | static const std::string CLOSABLE_KEY("closable"); | ||
49 | |||
50 | static const std::string INFINITE("infinite"); | ||
51 | |||
52 | static const std::vector<std::filesystem::path> SOUND_BOARD = { | ||
53 | std::filesystem::canonical( | ||
54 | sight::core::runtime::get_resource_file_path("sight::module::ui::qt/sounds/info_beep.wav") | ||
55 | ), | ||
56 | std::filesystem::canonical( | ||
57 | sight::core::runtime::get_resource_file_path("sight::module::ui::qt/sounds/success_beep.wav") | ||
58 | ), | ||
59 | std::filesystem::canonical( | ||
60 | sight::core::runtime::get_resource_file_path("sight::module::ui::qt/sounds/failure_beep.wav") | ||
61 | ) | ||
62 | }; | ||
63 | |||
64 | static const std::map<const std::string, const sight::ui::dialog::notification::position> POSITION_MAP = { | ||
65 | {"TOP_RIGHT", service::notification::position::top_right}, | ||
66 | {"TOP_LEFT", service::notification::position::top_left}, | ||
67 | {"CENTERED_TOP", service::notification::position::centered_top}, | ||
68 | {"CENTERED", service::notification::position::centered}, | ||
69 | {"BOTTOM_RIGHT", service::notification::position::bottom_right}, | ||
70 | {"BOTTOM_LEFT", service::notification::position::bottom_left}, | ||
71 | {"CENTERED_BOTTOM", service::notification::position::centered_bottom} | ||
72 | }; | ||
73 | |||
74 | //----------------------------------------------------------------------------- | ||
75 | |||
76 |
4/4✓ Branch 4 taken 11 times.
✓ Branch 5 taken 11 times.
✓ Branch 15 taken 77 times.
✓ Branch 16 taken 11 times.
|
99 | notifier::notifier() noexcept |
77 | { | ||
78 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | new_slot(POP_NOTIFICATION_SLOT, ¬ifier::pop, this); |
79 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | new_slot(CLOSE_NOTIFICATION_SLOT, ¬ifier::close_notification, this); |
80 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | new_slot(SET_ENUM_PARAMETER_SLOT, ¬ifier::set_enum_parameter, this); |
81 | 11 | } | |
82 | |||
83 | //----------------------------------------------------------------------------- | ||
84 | |||
85 | 11 | void notifier::configuring() | |
86 | { | ||
87 | 11 | const auto& config = this->get_config(); | |
88 | |||
89 |
2/4✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
|
11 | if(const auto& channels = config.get_child_optional("channels"); channels) |
90 | { | ||
91 |
2/2✓ Branch 2 taken 11 times.
✓ Branch 3 taken 11 times.
|
22 | for(const auto& channel : boost::make_iterator_range(channels->equal_range("channel"))) |
92 | { | ||
93 | 11 | configuration channel_config {}; | |
94 | |||
95 | // UID | ||
96 |
3/6✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
|
11 | const auto& uid = channel.second.get_optional<std::string>("<xmlattr>.uid").value_or(""); |
97 | |||
98 | // Position | ||
99 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
|
22 | if(const auto& position = channel.second.get_optional<std::string>("<xmlattr>." + POSITION_KEY); |
100 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | position) |
101 | { | ||
102 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if(POSITION_MAP.contains(*position)) |
103 | { | ||
104 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | channel_config.position = POSITION_MAP.at(*position); |
105 | } | ||
106 | else | ||
107 | { | ||
108 | ✗ | SIGHT_ERROR( | |
109 | "Position '" | ||
110 | + *position | ||
111 | + "' isn't a valid position value, accepted values are:" | ||
112 | "TOP_RIGHT, TOP_LEFT, CENTERED_TOP, CENTERED, BOTTOM_RIGHT, BOTTOM_LEFT, CENTERED_BOTTOM." | ||
113 | ) | ||
114 | } | ||
115 | } | ||
116 | |||
117 | // Duration | ||
118 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
|
22 | if(const auto& duration = channel.second.get_optional<std::string>("<xmlattr>." + DURATION_KEY); |
119 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | duration) |
120 | { | ||
121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if(*duration == INFINITE) |
122 | { | ||
123 | ✗ | channel_config.duration = std::chrono::milliseconds(0); | |
124 | } | ||
125 | else | ||
126 | { | ||
127 | 11 | try | |
128 | { | ||
129 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
|
22 | channel_config.duration = std::chrono::milliseconds(std::stoul(*duration)); |
130 | } | ||
131 | ✗ | catch(...) | |
132 | { | ||
133 | ✗ | SIGHT_ERROR( | |
134 | "Duration '" | ||
135 | + *duration | ||
136 | + "' is not valid. Accepted values are: '" | ||
137 | + INFINITE | ||
138 | + "' or a positive number of milliseconds." | ||
139 | ) | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | // Size | ||
145 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 11 times.
|
22 | if(const auto& size = channel.second.get_optional<std::string>("<xmlattr>." + SIZE_KEY); size) |
146 | { | ||
147 | ✗ | try | |
148 | { | ||
149 | ✗ | if(const auto pos = size->find_first_of('x'); pos != std::string::npos) | |
150 | { | ||
151 | ✗ | const auto width = std::stoul(size->substr(0, pos)); | |
152 | ✗ | const auto height = std::stoul(size->substr(pos + 1)); | |
153 | |||
154 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | channel_config.size = {int(width), int(height)}; |
155 | } | ||
156 | else | ||
157 | { | ||
158 | ✗ | throw std::runtime_error("No 'x' found."); | |
159 | } | ||
160 | } | ||
161 | ✗ | catch(...) | |
162 | { | ||
163 | ✗ | SIGHT_ERROR( | |
164 | "Size '" | ||
165 | + *size | ||
166 | + "' is not valid. Accepted values are: `n x n` where 'n' is a positive number." | ||
167 | ) | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // Max | ||
172 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
|
22 | if(const auto& max = channel.second.get_optional<std::string>("<xmlattr>." + MAX_KEY); max) |
173 | { | ||
174 | 11 | try | |
175 | { | ||
176 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | channel_config.max = std::stoul(*max); |
177 | } | ||
178 | ✗ | catch(...) | |
179 | { | ||
180 | ✗ | SIGHT_ERROR( | |
181 | "Maximum '" | ||
182 | + *max | ||
183 | + "' is not valid. Accepted values are positive numbers." | ||
184 | ) | ||
185 | } | ||
186 | } | ||
187 | |||
188 | // Closable | ||
189 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 11 times.
|
22 | if(const auto& closable = channel.second.get_optional<std::string>("<xmlattr>." + CLOSABLE_KEY); |
190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | closable) |
191 | { | ||
192 | ✗ | channel_config.closable = *closable == "true"; | |
193 | 11 | } | |
194 | |||
195 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | m_channels.insert_or_assign(uid, channel_config); |
196 | 11 | } | |
197 | } | ||
198 | |||
199 | // Lastly, initialize sound strutures. | ||
200 | 11 | m_sound = std::make_unique<QSoundEffect>(qApp); | |
201 | 11 | m_sound->setVolume(1.0); | |
202 | |||
203 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | m_default_message = config.get<std::string>("message", m_default_message); |
204 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | m_parent_container_id = config.get<std::string>("parent.<xmlattr>.uid", m_parent_container_id); |
205 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
11 | } |
206 | |||
207 | //----------------------------------------------------------------------------- | ||
208 | |||
209 | 11 | void notifier::starting() | |
210 | { | ||
211 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if(!m_parent_container_id.empty()) |
212 | { | ||
213 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | auto container = sight::ui::registry::get_sid_container(m_parent_container_id); |
214 | |||
215 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if(!container) |
216 | { | ||
217 |
3/8✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
22 | container = sight::ui::registry::get_wid_container(m_parent_container_id); |
218 | } | ||
219 | |||
220 | // If we have an SID/WID set the container. | ||
221 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if(container) |
222 | { | ||
223 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | m_container_where_to_display_notifs = container; |
224 | } | ||
225 | 11 | } | |
226 | 11 | } | |
227 | |||
228 | //----------------------------------------------------------------------------- | ||
229 | |||
230 | 11 | void notifier::stopping() | |
231 | { | ||
232 |
2/2✓ Branch 0 taken 77 times.
✓ Branch 1 taken 11 times.
|
88 | for(const auto& [position, stack] : m_stacks) |
233 | { | ||
234 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
|
77 | for(const auto& popup : stack.popups) |
235 | { | ||
236 | ✗ | popup->close(); | |
237 | } | ||
238 | } | ||
239 | |||
240 | 11 | m_stacks.clear(); | |
241 | 11 | } | |
242 | |||
243 | //----------------------------------------------------------------------------- | ||
244 | |||
245 | ✗ | void notifier::updating() | |
246 | { | ||
247 | } | ||
248 | |||
249 | //----------------------------------------------------------------------------- | ||
250 | |||
251 | ✗ | void notifier::set_enum_parameter(std::string _val, std::string _key) | |
252 | { | ||
253 | ✗ | try | |
254 | { | ||
255 | ✗ | if(_key == POSITION_KEY) | |
256 | { | ||
257 | ✗ | m_channels[""].position = POSITION_MAP.at(_val); | |
258 | } | ||
259 | ✗ | else if(_key == DURATION_KEY) | |
260 | { | ||
261 | ✗ | if(_val == INFINITE) | |
262 | { | ||
263 | ✗ | m_channels[""].duration = std::chrono::milliseconds(0); | |
264 | } | ||
265 | else | ||
266 | { | ||
267 | ✗ | m_channels[""].duration = std::chrono::milliseconds(std::stoul(_val)); | |
268 | } | ||
269 | } | ||
270 | ✗ | else if(_key == SIZE_KEY) | |
271 | { | ||
272 | ✗ | if(const auto pos = _val.find_first_of('x'); pos != std::string::npos) | |
273 | { | ||
274 | ✗ | const auto width = std::stoul(_val.substr(0, pos)); | |
275 | ✗ | const auto height = std::stoul(_val.substr(pos + 1)); | |
276 | |||
277 | ✗ | m_channels[""].size = {int(width), int(height)}; | |
278 | } | ||
279 | } | ||
280 | ✗ | else if(_key == MAX_KEY) | |
281 | { | ||
282 | ✗ | m_channels[""].max = std::stoul(_val); | |
283 | } | ||
284 | ✗ | else if(_key == CLOSABLE_KEY) | |
285 | { | ||
286 | ✗ | m_channels[""].closable = _val == "true"; | |
287 | } | ||
288 | } | ||
289 | ✗ | catch(...) | |
290 | { | ||
291 | ✗ | SIGHT_ERROR(std::string("Value '") + _val + "' is not handled for key " + _key); | |
292 | } | ||
293 | } | ||
294 | |||
295 | //----------------------------------------------------------------------------- | ||
296 | |||
297 | ✗ | void notifier::pop(service::notification _notification) | |
298 | { | ||
299 | ✗ | const bool channel_configured = m_channels.contains(_notification.channel); | |
300 | |||
301 | // Get channel configuration (or global configuration if there is no channel) | ||
302 | ✗ | const auto& channel_configuration = channel_configured | |
303 | ✗ | ? m_channels[_notification.channel] | |
304 | ✗ | : m_channels[""]; | |
305 | |||
306 | ✗ | const auto& default_configuration = m_channels[""]; | |
307 | |||
308 | // Get the stack configuration. First try the channel, then the default, then the notification itself | ||
309 | // If you want that services totally control the notification, associate them to an unconfigured notifier | ||
310 | ✗ | const auto& position = channel_configured && channel_configuration.position | |
311 | ✗ | ? *channel_configuration.position | |
312 | ✗ | : (channel_configured && !channel_configuration.position) || !default_configuration.position | |
313 | ? _notification.position | ||
314 | ✗ | : *default_configuration.position; | |
315 | |||
316 | ✗ | const auto& duration = channel_configured && channel_configuration.duration | |
317 | ✗ | ? *channel_configuration.duration | |
318 | ✗ | : (channel_configured && !channel_configuration.duration) || !default_configuration.duration | |
319 | ✗ | ? _notification.duration | |
320 | ✗ | : *default_configuration.duration; | |
321 | |||
322 | ✗ | const auto& size = channel_configured && channel_configuration.size | |
323 | ✗ | ? *channel_configuration.size | |
324 | ✗ | : (channel_configured && !channel_configuration.size) || !default_configuration.size | |
325 | ? _notification.size | ||
326 | ✗ | : *default_configuration.size; | |
327 | |||
328 | ✗ | const auto& max = channel_configuration.max | |
329 | ✗ | ? *channel_configuration.max | |
330 | : default_configuration.max | ||
331 | ✗ | ? *default_configuration.max | |
332 | ✗ | : 0; | |
333 | |||
334 | ✗ | const auto& closable = channel_configured && channel_configuration.closable | |
335 | ✗ | ? *channel_configuration.closable | |
336 | ✗ | : (channel_configured && !channel_configuration.closable) || !default_configuration.closable | |
337 | ✗ | ? _notification.closable | |
338 | ✗ | : *default_configuration.closable; | |
339 | |||
340 | // Get the wanted stack | ||
341 | ✗ | auto& target_stack = m_stacks[position]; | |
342 | |||
343 | // Compute harmonized max and size | ||
344 | ✗ | target_stack.max = target_stack.max | |
345 | ✗ | ? std::max(*target_stack.max, max) | |
346 | ✗ | : max; | |
347 | |||
348 | ✗ | target_stack.size = target_stack.size | |
349 | ✗ | ? std::array<int, 2> | |
350 | { | ||
351 | ✗ | std::max((*target_stack.size)[0], size[0]), | |
352 | ✗ | std::max((*target_stack.size)[1], size[1]) | |
353 | } | ||
354 | ✗ | : size; | |
355 | |||
356 | // If the maximum number of notification is reached, remove the oldest one. | ||
357 | ✗ | clean_notifications(position, *target_stack.max, *target_stack.size); | |
358 | |||
359 | // Get or create the notification | ||
360 | ✗ | const auto& popup = | |
361 | ✗ | [&] | |
362 | { | ||
363 | // If a channel is present, try to retrieve the associated dialog | ||
364 | ✗ | if(!_notification.channel.empty()) | |
365 | { | ||
366 | ✗ | for(auto& [old_position, stack] : m_stacks) | |
367 | { | ||
368 | ✗ | for(const auto& popup : stack.popups) | |
369 | { | ||
370 | ✗ | if(popup->get_channel() == _notification.channel) | |
371 | { | ||
372 | // If the position doesn't match, fix it | ||
373 | ✗ | if(old_position != position) | |
374 | { | ||
375 | // Explicit copy | ||
376 | ✗ | auto copy = popup; | |
377 | ✗ | copy->set_index(static_cast<unsigned int>(target_stack.popups.size())); | |
378 | ✗ | target_stack.popups.emplace_back(copy); | |
379 | |||
380 | // Remove the original | ||
381 | ✗ | stack.popups.remove(popup); | |
382 | |||
383 | ✗ | return copy; | |
384 | } | ||
385 | |||
386 | ✗ | return popup; | |
387 | } | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | // No channel or the dialog was not found, create a new one | ||
393 | ✗ | auto popup = std::make_shared<sight::ui::dialog::notification>(); | |
394 | ✗ | popup->set_index(static_cast<unsigned int>(target_stack.popups.size())); | |
395 | ✗ | target_stack.popups.emplace_back(popup); | |
396 | |||
397 | ✗ | return popup; | |
398 | ✗ | }(); | |
399 | |||
400 | ✗ | popup->set_container(m_container_where_to_display_notifs); | |
401 | |||
402 | ✗ | const std::string& message_to_show = _notification.message.empty() ? m_default_message : _notification.message; | |
403 | ✗ | popup->set_message(message_to_show); | |
404 | |||
405 | ✗ | popup->set_type(_notification.type); | |
406 | ✗ | popup->set_position(position); | |
407 | ✗ | popup->set_duration(duration); | |
408 | ✗ | popup->set_size(*target_stack.size); | |
409 | ✗ | popup->set_closed_callback([this, popup](auto&& ...){on_notification_closed(popup);}); | |
410 | ✗ | popup->set_channel(_notification.channel); | |
411 | ✗ | popup->set_closable(closable); | |
412 | ✗ | popup->show(); | |
413 | |||
414 | ✗ | if(_notification.sound.has_value() && _notification.sound.value()) | |
415 | { | ||
416 | ✗ | m_sound->setSource(QUrl::fromLocalFile(QString::fromStdString(SOUND_BOARD[_notification.type].string()))); | |
417 | ✗ | m_sound->play(); | |
418 | } | ||
419 | } | ||
420 | |||
421 | //------------------------------------------------------------------------------ | ||
422 | |||
423 | ✗ | void notifier::close_notification(std::string _channel) | |
424 | { | ||
425 | ✗ | bool found = false; | |
426 | |||
427 | ✗ | for(const auto& [position, stack] : m_stacks) | |
428 | { | ||
429 | ✗ | for(const auto& popup : stack.popups) | |
430 | { | ||
431 | ✗ | if(popup->get_channel() == _channel) | |
432 | { | ||
433 | ✗ | found = true; | |
434 | ✗ | popup->close(); | |
435 | } | ||
436 | } | ||
437 | } | ||
438 | |||
439 | ✗ | SIGHT_WARN_IF("Notification on channel '" << _channel << "' is already closed.", !found); | |
440 | } | ||
441 | |||
442 | //------------------------------------------------------------------------------ | ||
443 | |||
444 | ✗ | void notifier::on_notification_closed(const sight::ui::dialog::notification::sptr& _notif) | |
445 | { | ||
446 | // If the notification still exist | ||
447 | ✗ | for(auto& [position, stack] : m_stacks) | |
448 | { | ||
449 | ✗ | if(auto it = std::find(stack.popups.begin(), stack.popups.end(), _notif); it != stack.popups.end()) | |
450 | { | ||
451 | ✗ | erase_notification(position, it); | |
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | //------------------------------------------------------------------------------ | ||
457 | |||
458 | ✗ | std::list<sight::ui::dialog::notification::sptr>::iterator notifier::erase_notification( | |
459 | const enum service::notification::position& _position, | ||
460 | const std::list<sight::ui::dialog::notification::sptr>::iterator& _it | ||
461 | ) | ||
462 | { | ||
463 | // Remove the notification from the container | ||
464 | ✗ | auto& stack = m_stacks[_position]; | |
465 | ✗ | const auto next_it = stack.popups.erase(_it); | |
466 | ✗ | auto remaining_it = next_it; | |
467 | |||
468 | // Move all the remaining notifications one index lower | ||
469 | ✗ | while(remaining_it != stack.popups.end()) | |
470 | { | ||
471 | ✗ | (*remaining_it)->move_down(); | |
472 | ✗ | ++remaining_it; | |
473 | } | ||
474 | |||
475 | // Return the it pointing after the erased one | ||
476 | ✗ | return next_it; | |
477 | } | ||
478 | |||
479 | //------------------------------------------------------------------------------ | ||
480 | |||
481 | ✗ | void notifier::clean_notifications( | |
482 | const enum service::notification::position& _position, | ||
483 | std::size_t _max, | ||
484 | std::array<int, 2> _size, | ||
485 | bool _skip_permanent | ||
486 | ) | ||
487 | { | ||
488 | // Get the correct "stack" | ||
489 | ✗ | auto& stack = m_stacks[_position]; | |
490 | |||
491 | ✗ | std::size_t removable_popups = 0; | |
492 | |||
493 | // Count how many popups that can be removed there are | ||
494 | ✗ | for(const auto& popup : stack.popups) | |
495 | { | ||
496 | ✗ | if(!_skip_permanent || popup->get_duration()) | |
497 | { | ||
498 | ✗ | ++removable_popups; | |
499 | } | ||
500 | } | ||
501 | |||
502 | ✗ | for(auto it = stack.popups.begin() ; removable_popups >= _max && it != stack.popups.end() ; ) | |
503 | { | ||
504 | // If the popup is removable | ||
505 | ✗ | if(const auto& duration = (*it)->get_duration(); !_skip_permanent || (duration && duration->count() > 0)) | |
506 | { | ||
507 | // Remove it | ||
508 | ✗ | (*it)->close(); | |
509 | ✗ | it = erase_notification(_position, it); | |
510 | ✗ | --removable_popups; | |
511 | } | ||
512 | else | ||
513 | { | ||
514 | ✗ | ++it; | |
515 | } | ||
516 | } | ||
517 | |||
518 | // Adjust sizes | ||
519 | ✗ | for(const auto& popup : stack.popups) | |
520 | { | ||
521 | ✗ | popup->set_size(_size); | |
522 | } | ||
523 | } | ||
524 | |||
525 | //----------------------------------------------------------------------------- | ||
526 | |||
527 | } // namespace sight::module::ui::qt | ||
528 |