92 :
public detail::ListSignalMixin<GroupedList<T, Key, Source>,
94 friend detail::ListSignalMixin<GroupedList<T, Key, Source>,
Group<T, Key>>;
100 using Signal = detail::ListSignal<Group<T, Key>>;
103 KeyOf key_of = default_key_of_())
104 : source_(
std::move(source)),
106 state_(
std::make_shared<SharedState>())
108 state_->key_of = std::move(key_of);
111 std::weak_ptr<SharedState> weak_state = state_;
112 std::weak_ptr<Signal> weak_signal = signal_;
113 std::weak_ptr<Source> weak_source{source_};
114 source_sub_ = source_->observe(
115 [weak_state, weak_signal, weak_source](
const ListChange<T>& ch) {
116 auto st = weak_state.lock();
117 auto sig = weak_signal.lock();
118 auto src = weak_source.lock();
119 if (!st || !sig || !src)
return;
120 handle_source_change_(*st, *sig, ch);
130 [[nodiscard]] std::size_t
size()
const {
131 std::shared_lock lk(state_->m);
132 return state_->groups.size();
135 [[nodiscard]]
bool empty()
const {
return size() == 0; }
137 [[nodiscard]] std::shared_ptr<Group<T, Key>>
at(std::size_t idx)
const {
138 std::shared_lock lk(state_->m);
139 return state_->groups.at(idx);
142 [[nodiscard]] std::vector<std::shared_ptr<Group<T, Key>>>
snapshot()
const {
143 std::shared_lock lk(state_->m);
144 return state_->groups;
150 [[nodiscard]] std::shared_ptr<Group<T, Key>>
find(
const Key& k)
const {
151 std::shared_lock lk(state_->m);
152 auto it = state_->by_key.find(k);
153 if (it == state_->by_key.end())
return nullptr;
154 if (it->second >= state_->groups.size())
return nullptr;
155 return state_->groups[it->second];
160 std::shared_ptr<T> item;
168 std::optional<SourceRow> row;
169 std::vector<SourceRow> reset;
173 mutable std::shared_mutex m;
175 std::vector<std::shared_ptr<Group<T, Key>>> groups;
176 std::unordered_map<Key, std::size_t> by_key;
179 std::vector<SourceRow> rows;
182 std::shared_ptr<Source> source_;
183 std::shared_ptr<Signal> signal_;
184 std::shared_ptr<SharedState> state_;
185 Subscription source_sub_;
187 static KeyOf default_key_of_() {
188 return [](
const T& v) -> Key {
189 static_assert(std::is_same_v<Key, T>,
190 "GroupedList: provide a key extractor when Key differs from T.");
195 static std::vector<SourceRow> source_rows_(SharedState& st, Source& src) {
196 std::vector<SourceRow> rows;
197 for (
auto& item : src.snapshot()) {
198 rows.push_back(SourceRow{item, st.key_of(*item)});
203 static void rebuild_(SharedState& st, std::vector<SourceRow> rows) {
204 std::vector<std::shared_ptr<Group<T, Key>>> groups;
205 std::unordered_map<Key, std::size_t> by_key;
206 for (
const auto& row : rows) {
207 auto [it, inserted] = by_key.emplace(row.key, groups.size());
209 groups.push_back(std::make_shared<Group<T, Key>>(
210 Group<T, Key>{row.key, std::make_shared<ObservableList<T>>()}));
213 groups[it->second]->items->push_back(row.item);
215 std::unique_lock lk(st.m);
216 st.rows = std::move(rows);
217 st.groups = std::move(groups);
218 st.by_key = std::move(by_key);
221 void rebuild_initial_() {
222 rebuild_(*state_, source_rows_(*state_, *source_));
225 static void handle_source_change_(SharedState& st,
Signal& sig,
226 const ListChange<T>& ch) {
227 InputChange
event{ch.kind, ch.index, ch.from_index, {}, {}};
232 event.row.emplace(SourceRow{item, st.key_of(*item)});
234 for (
const auto& item : *ch.snapshot)
event.reset.push_back(SourceRow{item, st.key_of(*item)});
236 apply_(st, sig, std::move(event));
241 static std::size_t inner_position_(
const SharedState& st,
242 const Key& key, std::size_t before) {
243 std::size_t count = 0;
244 for (std::size_t i = 0; i < before; ++i) {
245 if (st.rows[i].key == key) ++count;
250 static void insert_(SharedState& st,
Signal& sig,
251 std::size_t index, SourceRow row) {
252 std::shared_ptr<Group<T, Key>> group;
253 std::size_t inner = 0;
254 std::size_t outer = 0;
255 bool created =
false;
257 std::unique_lock lk(st.m);
258 const bool append = index == st.rows.size();
259 auto found = st.by_key.find(row.key);
260 if (found == st.by_key.end()) {
261 outer = st.groups.size();
263 std::unordered_map<Key, bool> preceding;
264 for (std::size_t i = 0; i < index; ++i) {
265 preceding.emplace(st.rows[i].key,
true);
267 outer = preceding.size();
269 group = std::make_shared<Group<T, Key>>(
270 Group<T, Key>{row.key, std::make_shared<ObservableList<T>>()});
272 for (
auto& [key, position] : st.by_key) {
273 if (position >= outer) ++position;
276 st.by_key.emplace(row.key, outer);
277 st.groups.insert(st.groups.begin() +
static_cast<std::ptrdiff_t
>(outer), group);
280 group = st.groups[found->second];
284 inner = append ? group->items->size()
285 : inner_position_(st, row.key, index);
287 st.rows.insert(st.rows.begin() +
static_cast<std::ptrdiff_t
>(index), row);
289 group->items->insert(inner, std::move(row.item));
295 static void remove_(SharedState& st,
Signal& sig, std::size_t index) {
296 std::shared_ptr<Group<T, Key>> group;
299 std::unique_lock lk(st.m);
300 const auto& row = st.rows.at(index);
301 inner = inner_position_(st, row.key, index);
302 group = st.groups[st.by_key.at(row.key)];
303 st.rows.erase(st.rows.begin() +
static_cast<std::ptrdiff_t
>(index));
307 group->items->remove_at(inner);
308 if (group->items->empty()) {
311 std::unique_lock lk(st.m);
312 outer = st.by_key.at(group->key);
313 st.by_key.erase(group->key);
314 st.groups.erase(st.groups.begin() +
static_cast<std::ptrdiff_t
>(outer));
315 for (
auto& [key, position] : st.by_key) {
316 if (position > outer) --position;
323 static void apply_(SharedState& st,
Signal& sig, InputChange&& event) {
325 std::vector<std::size_t> occurrences;
327 std::shared_lock lk(st.m);
328 for (std::size_t i = 0; i < st.rows.size(); ++i) {
329 if (st.rows[i].item == event.row->item && st.rows[i].key != event.row->key) {
330 occurrences.push_back(i);
334 for (
const auto index : occurrences) {
335 remove_(st, sig, index);
336 insert_(st, sig, index, *event.row);
340 switch (event.kind) {
342 insert_(st, sig, event.index, std::move(*event.row));
345 remove_(st, sig, event.index);
349 std::shared_ptr<Group<T, Key>> group;
350 std::size_t inner = 0;
352 std::unique_lock lk(st.m);
353 auto& old = st.rows.at(event.index);
354 if (old.key == event.row->key) {
355 inner = inner_position_(st, old.key, event.index);
356 group = st.groups[st.by_key.at(old.key)];
362 group->items->replace_at(inner, std::move(event.row->item));
367 remove_(st, sig, event.index);
368 insert_(st, sig, event.index, std::move(*event.row));
373 std::shared_ptr<Group<T, Key>> group;
377 std::unique_lock lk(st.m);
378 auto row = st.rows.at(event.from);
379 group = st.groups[st.by_key.at(row.key)];
380 from = inner_position_(st, row.key, event.from);
381 st.rows.erase(st.rows.begin() +
static_cast<std::ptrdiff_t
>(event.from));
382 to = inner_position_(st, row.key, event.index);
383 st.rows.insert(st.rows.begin() +
static_cast<std::ptrdiff_t
>(event.index), std::move(row));
385 group->items->move(from, to);
389 rebuild_(st, std::move(event.reset));
391 std::shared_lock lock(st.m);
394 sig.emit(ListChange<Group<T, Key>>::reset(std::move(
snapshot)));