Nui
Loading...
Searching...
No Matches
selectables_registry.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <nui/concepts.hpp>
4
5#include <vector>
6#include <utility>
7#include <optional>
8#include <algorithm>
9#include <limits>
10#include <set>
11#include <iterator>
12
13namespace Nui
14{
15 namespace Detail
16 {
18 {};
19 }
20
29 template <typename T>
31 {
32 public:
34 using IdType = std::size_t;
35
40 {
43
49 std::optional<T> item;
50
51 template <typename... Args>
53 : id{id}
54 , item{T{std::forward<Args>(args)...}}
55 {}
57 : id{id}
58 , item{std::move(item)}
59 {}
60 ItemWithId(IdType id, std::optional<T> item)
61 : id{id}
62 , item{std::move(item)}
63 {}
64 ItemWithId(ItemWithId const&) = default;
65 ItemWithId(ItemWithId&&) = default;
66 ItemWithId& operator=(ItemWithId const&) = default;
68 ~ItemWithId() = default;
69
71 bool operator<(ItemWithId const& other) const
72 {
73 return id < other.id;
74 }
75 };
76
78 using ItemContainerType = std::vector<ItemWithId>;
79
81 constexpr static auto invalidId = std::numeric_limits<IdType>::max();
82
90 template <typename WrappedIterator>
92 {
93 public:
94 IteratorBase(WrappedIterator wrapped, WrappedIterator begin, WrappedIterator end)
95 : wrappedIterator_{std::move(wrapped)}
96 , beginIterator_{std::move(begin)}
97 , endIterator_{std::move(end)}
98 {
100 return;
101 // shift iterator to first valid item
104 }
105 IteratorBase(IteratorBase const&) = default;
109 ~IteratorBase() = default;
110
112 {
116 return *this;
117 }
118
120 {
121 IteratorBase tmp = *this;
122 ++*this;
123 return tmp;
124 }
125
135
137 {
138 IteratorBase tmp = *this;
139 --*this;
140 return tmp;
141 }
142
143 friend bool operator==(const IteratorBase& lhs, const IteratorBase& rhs)
144 {
145 return lhs.wrappedIterator_ == rhs.wrappedIterator_;
146 }
147
148 friend bool operator!=(const IteratorBase& lhs, const IteratorBase& rhs)
149 {
150 return !(lhs == rhs);
151 }
152
153 IteratorBase operator+(std::size_t offset) const
154 {
155 IteratorType tmp = *this;
156 for (std::size_t i = 0; i < offset && tmp != endIterator_; ++i)
157 ++tmp;
158 return tmp;
159 }
160
161 IteratorBase operator-(std::size_t offset) const
162 {
163 IteratorType tmp = *this;
164 for (std::size_t i = 0; i < offset && tmp != beginIterator_; ++i)
165 --tmp;
166 return tmp;
167 }
168
169 IteratorBase& operator+=(std::size_t offset)
170 {
171 for (std::size_t i = 0; i < offset && *this != endIterator_; ++i)
172 ++*this;
173 }
174
175 IteratorBase& operator-=(std::size_t offset)
176 {
177 for (std::size_t i = 0; i < offset && *this != beginIterator_; ++i)
178 --*this;
179 }
180
181 bool isEnd() const
182 {
184 }
185
186 protected:
187 WrappedIterator wrappedIterator_;
188 WrappedIterator beginIterator_;
189 WrappedIterator endIterator_;
190 };
191
192 template <typename WrappedIterator>
193 class ConstIterator : public IteratorBase<WrappedIterator>
194 {
195 public:
196 using IteratorBase<WrappedIterator>::IteratorBase;
197 using IteratorBase<WrappedIterator>::operator=;
198 using IteratorBase<WrappedIterator>::wrappedIterator_;
199
200 explicit ConstIterator(typename ItemContainerType::iterator iter)
201 : IteratorBase<WrappedIterator>{std::move(iter)}
202 {}
203
204 auto const& operator*() const
205 {
207 throw std::runtime_error{"Dereferencing end iterator"};
208 return wrappedIterator_->item.value();
209 }
210
211 auto const* operator->() const
212 {
214 throw std::runtime_error{"Dereferencing end iterator"};
215 return &wrappedIterator_->item.value();
216 }
217 };
218
219 template <typename WrappedIterator>
220 class Iterator : public IteratorBase<WrappedIterator>
221 {
222 public:
223 using IteratorBase<WrappedIterator>::IteratorBase;
224 using IteratorBase<WrappedIterator>::operator=;
225 using IteratorBase<WrappedIterator>::wrappedIterator_;
226
227 auto& operator*()
228 {
230 throw std::runtime_error{"Dereferencing end iterator"};
231 return wrappedIterator_->item.value();
232 }
233
235 {
237 throw std::runtime_error{"Dereferencing end iterator"};
238 return &wrappedIterator_->item.value();
239 }
240 };
241
244
245 public:
252
259 IdType append(T const& element)
260 {
261 items_.push_back(ItemWithId{id_, std::optional<T>{element}});
262 ++itemCount_;
263 const auto id = id_;
264 id_++;
265 if (id_ == invalidId - 1)
266 id_ = 0;
267 return id;
268 }
269
276 IdType append(T&& element)
277 {
278 items_.push_back(ItemWithId{id_, std::optional<T>{std::move(element)}});
279 ++itemCount_;
280 const auto id = id_;
281 id_++;
282 if (id_ == invalidId - 1)
283 id_ = 0;
284 return id;
285 }
286
294 template <typename... Args>
295 IdType emplace(Args&&... args)
296 {
297 items_.push_back(ItemWithId{id_, Detail::InplaceTag{}, std::forward<Args>(args)...});
298 ++itemCount_;
299 const auto id = id_;
300 id_++;
301 if (id_ == invalidId - 1)
302 id_ = 0;
303 return id;
304 }
305
313 {
314 const auto p = findItem(id);
315 if (p == std::end(items_))
316 return end();
317
318 p->item.reset();
319 --itemCount_;
320
321 auto result = condense();
322 return {result, items_.begin(), items_.end()};
323 }
324
331 std::optional<T> pop(IdType id)
332 {
333 const auto p = findItem(id);
334 if (p == std::end(items_))
335 return std::nullopt;
336
337 const auto result = std::move(p->item);
338 p->item.reset();
339 --itemCount_;
340
341 condense();
342 return result;
343 }
344
346 {
348 std::optional<T>* item;
349
351 bool found;
352
355 };
356
364 {
365 const auto iter = findItem(id);
366 if (iter == std::end(items_))
367 return {nullptr, false, false};
368 if (!iter->item.has_value())
369 return {nullptr, true, true};
370
371 --itemCount_;
372
373 const auto selectedIter = selected_.insert(std::move(*iter)).first;
374 iter->item.reset();
375 // having modifying access to the optional<T> does not mess with the set ordering. const casting is fine
376 // here.
377 return {
378 .item = &(const_cast<ItemWithId&>(*selectedIter).item),
379 .found = true,
380 .alreadySelected = false,
381 };
382 }
383
390 std::size_t deselectAll(std::invocable<ItemWithId const&> auto const& callback)
391 {
392 std::size_t result = 0;
393 for (auto const& selected : selected_)
394 {
395 auto const id = selected.id;
396 if (callback(selected))
397 {
398 auto entry = findItem(id);
399 if (entry != std::end(items_))
400 {
401 ++itemCount_;
402 ++result;
403 // selected is a set item, and therefore immutable, so we need to const_cast to move it.
404 // this would break the set, but its cleared afterwards anyway.
405 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
406 entry->item = std::move(const_cast<ItemWithId&>(selected).item);
407 }
408 }
409 }
410 selected_.clear();
411 condense();
412 return result;
413 }
414
423 bool deselect(IdType id, std::invocable<ItemWithId const&> auto const& callback)
424 {
425 auto const iter = selected_.find(ItemWithId{id, std::nullopt});
426 if (iter == std::end(selected_))
427 return false;
428
429 if (callback(*iter))
430 {
431 auto entry = findItem(id);
432 if (entry != std::end(items_))
433 {
434 ++itemCount_;
435 entry->item = std::move(const_cast<ItemWithId&>(*iter).item);
436 }
437 return true;
438 }
439 selected_.erase(iter);
440 if (selected_.empty())
441 condense();
442 return false;
443 }
444
445 void clear()
446 {
447 items_.clear();
448 selected_.clear();
449 itemCount_ = 0;
450 id_ = 0;
451 }
452
460 {
461 auto iter = findItem(id);
462 if (iter == std::end(items_))
463 return end();
464 return IteratorType{iter, items_.begin(), items_.end()};
465 }
466
474 {
475 auto iter = findItem(id);
476 if (iter == std::end(items_))
477 return end();
478 return ConstIteratorType{iter, items_.begin(), items_.end()};
479 }
480
487 auto const& operator[](IdType id) const
488 {
489 return *get(id);
490 }
491
499 {
500 return *get(id);
501 }
502
507 {
508 return {items_.begin(), items_.begin(), items_.end()};
509 }
510
515 {
516 return {items_.begin(), items_.begin(), items_.end()};
517 }
518
523 {
524 return {items_.cbegin(), items_.begin(), items_.end()};
525 }
526
531 {
532 return {items_.end(), items_.begin(), items_.end()};
533 }
534
539 {
540 return {items_.end(), items_.begin(), items_.end()};
541 }
542
547 {
548 return {items_.cend(), items_.begin(), items_.end()};
549 }
550
554 bool empty() const
555 {
556 return itemCount_ == 0;
557 }
558
562 std::size_t size() const
563 {
564 return itemCount_;
565 }
566
570 typename ItemContainerType::iterator rawBegin()
571 {
572 return items_.begin();
573 }
574
578 typename ItemContainerType::iterator rawEnd()
579 {
580 return items_.end();
581 }
582
586 typename ItemContainerType::const_iterator rawBegin() const
587 {
588 return items_.begin();
589 }
590
594 typename ItemContainerType::const_iterator rawConstBegin() const
595 {
596 return items_.cbegin();
597 }
598
602 typename ItemContainerType::const_iterator rawEnd() const
603 {
604 return items_.end();
605 }
606
610 typename ItemContainerType::const_iterator rawConstEnd() const
611 {
612 return items_.cend();
613 }
614
615 template <typename RegistryPtr>
617 {
618 RegistryPtr registry;
619 typename ItemContainerType::iterator begin() const
620 {
621 return registry->rawBegin();
622 }
623 typename ItemContainerType::iterator end() const
624 {
625 return registry->rawEnd();
626 }
627 typename ItemContainerType::iterator cbegin() const
628 {
629 return registry->rawConstBegin();
630 }
631 typename ItemContainerType::iterator cend() const
632 {
633 return registry->rawConstEnd();
634 }
635 };
636
643 {
644 return {this};
645 }
646
653 {
654 return {this};
655 }
656
657 private:
658 typename ItemContainerType::iterator findItem(IdType id)
659 {
660 const auto p =
661 std::lower_bound(std::begin(items_), std::end(items_), id, [](auto const& lhs, auto const& rhs) {
662 return lhs.id < rhs;
663 });
664
665 if (p == std::end(items_) || p->id != id)
666 return std::end(items_);
667 return p;
668 }
669 typename ItemContainerType::const_iterator findItem(IdType id) const
670 {
671 const auto p =
672 std::lower_bound(std::begin(items_), std::end(items_), id, [](auto const& lhs, auto const& rhs) {
673 return lhs.id < rhs;
674 });
675
676 if (p == std::end(items_) || p->id != id)
677 return std::end(items_);
678 return p;
679 }
680
681 auto condense()
682 {
683 if (selected_.empty() && itemCount_ < (items_.size() / 2))
684 {
685 return items_.erase(
686 std::remove_if(
687 std::begin(items_),
688 std::end(items_),
689 [](auto const& item) {
690 return !item.item;
691 }),
692 std::end(items_));
693 }
694 return std::end(items_);
695 }
696
697 private:
698 ItemContainerType items_{};
699 // TODO: improve performance, id link backs are costly, each one is a binary search.
700 std::set<ItemWithId> selected_{};
701 IdType itemCount_{0};
702 IdType id_{0};
703 };
704}
Definition selectables_registry.hpp:194
auto const & operator*() const
Definition selectables_registry.hpp:204
auto const * operator->() const
Definition selectables_registry.hpp:211
ConstIterator(typename ItemContainerType::iterator iter)
Definition selectables_registry.hpp:200
Iterator that ignores items that are selected.
Definition selectables_registry.hpp:92
bool isEnd() const
Definition selectables_registry.hpp:181
WrappedIterator beginIterator_
Definition selectables_registry.hpp:188
IteratorBase(IteratorBase const &)=default
IteratorBase operator+(std::size_t offset) const
Definition selectables_registry.hpp:153
friend bool operator!=(const IteratorBase &lhs, const IteratorBase &rhs)
Definition selectables_registry.hpp:148
IteratorBase(WrappedIterator wrapped, WrappedIterator begin, WrappedIterator end)
Definition selectables_registry.hpp:94
friend bool operator==(const IteratorBase &lhs, const IteratorBase &rhs)
Definition selectables_registry.hpp:143
IteratorBase & operator++()
Definition selectables_registry.hpp:111
IteratorBase & operator--()
Definition selectables_registry.hpp:126
WrappedIterator endIterator_
Definition selectables_registry.hpp:189
IteratorBase & operator=(IteratorBase const &)=default
IteratorBase & operator=(IteratorBase &&)=default
IteratorBase & operator+=(std::size_t offset)
Definition selectables_registry.hpp:169
IteratorBase operator-(std::size_t offset) const
Definition selectables_registry.hpp:161
WrappedIterator wrappedIterator_
Definition selectables_registry.hpp:187
IteratorBase(IteratorBase &&)=default
IteratorBase operator--(int)
Definition selectables_registry.hpp:136
IteratorBase operator++(int)
Definition selectables_registry.hpp:119
IteratorBase & operator-=(std::size_t offset)
Definition selectables_registry.hpp:175
Definition selectables_registry.hpp:221
auto & operator*()
Definition selectables_registry.hpp:227
auto * operator->()
Definition selectables_registry.hpp:234
This container associates items with an id and allows for individual items to be "selected",...
Definition selectables_registry.hpp:31
ConstIteratorType cend() const
Returns end iterator.
Definition selectables_registry.hpp:546
ItemContainerType::const_iterator rawConstEnd() const
Returns a const iterator to the underlying container.
Definition selectables_registry.hpp:610
auto & operator[](IdType id)
Returns item by id.
Definition selectables_registry.hpp:498
auto const & operator[](IdType id) const
Returns item by id.
Definition selectables_registry.hpp:487
ItemContainerType::const_iterator rawBegin() const
Returns a const iterator to the underlying container.
Definition selectables_registry.hpp:586
SelectablesRegistry & operator=(SelectablesRegistry &&)=default
IteratorType begin()
Returns iterator to first unselected item or end.
Definition selectables_registry.hpp:506
IteratorType erase(IdType id)
Erase/Remove an item from the container.
Definition selectables_registry.hpp:312
ConstIteratorType begin() const
Returns iterator to first unselected item or end.
Definition selectables_registry.hpp:514
IteratorType end()
Returns end iterator.
Definition selectables_registry.hpp:530
ConstIteratorType end() const
Returns end iterator.
Definition selectables_registry.hpp:538
SelectablesRegistry(SelectablesRegistry &&)=default
ItemContainerType::const_iterator rawConstBegin() const
Returns a const iterator to the underlying container.
Definition selectables_registry.hpp:594
IdType emplace(Args &&... args)
Emplace an item to the container.
Definition selectables_registry.hpp:295
std::size_t IdType
Id type used to identify items.
Definition selectables_registry.hpp:34
std::size_t deselectAll(std::invocable< ItemWithId const & > auto const &callback)
Deselects all items.
Definition selectables_registry.hpp:390
ItemContainerType::iterator rawBegin()
Returns an iterator to the underlying container.
Definition selectables_registry.hpp:570
ConstIteratorType cbegin() const
Returns iterator to first unselected item or end.
Definition selectables_registry.hpp:522
bool deselect(IdType id, std::invocable< ItemWithId const & > auto const &callback)
Deselects item with id.
Definition selectables_registry.hpp:423
SelectionResult select(IdType id)
Select an item.
Definition selectables_registry.hpp:363
SelectablesRegistry(const SelectablesRegistry &)=default
RawRangeWrap< SelectablesRegistry< T > const * > rawRange() const
Helper for range based for loops.
Definition selectables_registry.hpp:652
ItemContainerType::iterator rawEnd()
Returns an iterator to the underlying container.
Definition selectables_registry.hpp:578
IdType append(T &&element)
Append an item to the container.
Definition selectables_registry.hpp:276
RawRangeWrap< SelectablesRegistry< T > * > rawRange()
Helper for range based for loops.
Definition selectables_registry.hpp:642
std::vector< ItemWithId > ItemContainerType
Type of the container that stores the items.
Definition selectables_registry.hpp:78
static constexpr auto invalidId
Invalid id value.
Definition selectables_registry.hpp:81
bool empty() const
Returns whether the container is empty.
Definition selectables_registry.hpp:554
IteratorType get(IdType id)
Get iterator to item with id.
Definition selectables_registry.hpp:459
SelectablesRegistry & operator=(const SelectablesRegistry &)=default
ItemContainerType::const_iterator rawEnd() const
Returns a const iterator to the underlying container.
Definition selectables_registry.hpp:602
ConstIteratorType get(IdType id) const
Get iterator to item with id.
Definition selectables_registry.hpp:473
std::optional< T > pop(IdType id)
Erase/Remove an item from the container and return it.
Definition selectables_registry.hpp:331
IdType append(T const &element)
Append an item to the container.
Definition selectables_registry.hpp:259
void clear()
Definition selectables_registry.hpp:445
std::size_t size() const
Returns the amount of items in the container.
Definition selectables_registry.hpp:562
Definition file_dialog.hpp:6
Definition selectables_registry.hpp:18
Wrapper around items that associates them with an id.
Definition selectables_registry.hpp:40
ItemWithId(IdType id, std::optional< T > item)
Definition selectables_registry.hpp:60
ItemWithId & operator=(ItemWithId &&)=default
ItemWithId & operator=(ItemWithId const &)=default
std::optional< T > item
The item.
Definition selectables_registry.hpp:49
ItemWithId(IdType id, T item)
Definition selectables_registry.hpp:56
bool operator<(ItemWithId const &other) const
Compares the id of the item.
Definition selectables_registry.hpp:71
ItemWithId(IdType id, Detail::InplaceTag, Args &&... args)
Definition selectables_registry.hpp:52
ItemWithId(ItemWithId &&)=default
IdType id
Id of the item.
Definition selectables_registry.hpp:42
ItemWithId(ItemWithId const &)=default
Definition selectables_registry.hpp:617
RegistryPtr registry
Definition selectables_registry.hpp:618
ItemContainerType::iterator end() const
Definition selectables_registry.hpp:623
ItemContainerType::iterator cend() const
Definition selectables_registry.hpp:631
ItemContainerType::iterator begin() const
Definition selectables_registry.hpp:619
ItemContainerType::iterator cbegin() const
Definition selectables_registry.hpp:627
Definition selectables_registry.hpp:346
std::optional< T > * item
Pointer to the selected item (may be nullptr).
Definition selectables_registry.hpp:348
bool found
Whether the item was found.
Definition selectables_registry.hpp:351
bool alreadySelected
Whether the item was already selected.
Definition selectables_registry.hpp:354