57 requires ::aria::ListSourceOf<L, T>
59 std::shared_ptr<runtime::IDispatcher> dispatcher = {})
60 : state_(
std::make_shared<State>(source.snapshot(),
std::move(sink),
61 std::move(dispatcher))) {
62 sub_ = source.observe([weak = std::weak_ptr<State>(state_)](
const ListChange<T>& change) {
63 if (
auto state = weak.lock()) enqueue_(state, change);
75 std::lock_guard lock(state_->mutex);
76 return state_->rows.size();
81 [[nodiscard]] std::shared_ptr<T>
at(std::size_t position)
const {
82 std::lock_guard lock(state_->mutex);
83 return position < state_->rows.size() ? state_->rows[position] :
nullptr;
91 enqueue_(state, std::nullopt);
95 using Rows = std::vector<std::shared_ptr<T>>;
97 using Event = std::optional<ListChange<T>>;
99 State(Rows initial,
NotifySink callback, std::shared_ptr<runtime::IDispatcher> owner)
101 dispatcher(
std::move(owner)) {}
104 bool scheduled =
false;
106 std::shared_ptr<NotifySink> sink;
107 std::shared_ptr<runtime::IDispatcher> dispatcher;
108 std::deque<Event> pending;
111 static void close_(
const std::shared_ptr<State>& state)
noexcept {
113 std::shared_ptr<NotifySink> retired_sink;
114 std::deque<Event> retired_events;
116 std::lock_guard lock(state->mutex);
117 state->active =
false;
118 retired_rows.swap(state->rows);
119 retired_sink.swap(state->sink);
120 retired_events.swap(state->pending);
125 static void enqueue_(
const std::shared_ptr<State>& state, Event event) {
126 std::shared_ptr<runtime::IDispatcher> dispatcher;
128 std::lock_guard lock(state->mutex);
129 if (!state->active)
return;
130 state->pending.push_back(std::move(event));
131 if (state->scheduled)
return;
132 state->scheduled =
true;
133 dispatcher = state->dispatcher;
135 if (dispatcher && !dispatcher->is_main_thread()) {
137 dispatcher->post([weak = std::weak_ptr<State>(state)] {
138 if (
auto owner = weak.lock()) drain_(owner);
141 std::deque<Event> retired;
143 std::lock_guard lock(state->mutex);
144 state->scheduled =
false;
145 retired.swap(state->pending);
154 static void drain_(
const std::shared_ptr<State>& state)
noexcept {
158 std::lock_guard lock(state->mutex);
159 if (!state->active || state->pending.empty()) {
160 state->scheduled =
false;
163 event = std::move(state->pending.front());
164 state->pending.pop_front();
167 apply_(state, event);
174 static void apply_(
const std::shared_ptr<State>& state,
const Event& event) {
177 if (event && event->kind == K::Reset) {
178 if (!event->snapshot)
throw std::invalid_argument(
"JniListSource: Reset needs a snapshot");
179 reset_rows = *
event->snapshot;
181 std::shared_ptr<T> retired_item;
182 std::shared_ptr<NotifySink> sink;
183 RecyclerNotification notification;
185 std::lock_guard lock(state->mutex);
186 if (!state->active)
return;
187 auto& rows = state->rows;
189 const auto& change = *event;
190 const auto index = change.
index;
191 switch (change.
kind) {
193 if (index > rows.size())
throw std::out_of_range(
"JniListSource: insert index");
194 rows.insert(rows.begin() +
static_cast<std::ptrdiff_t
>(index), change.
item);
198 if (index >= rows.size())
return;
199 retired_item = std::move(rows[index]);
200 rows.erase(rows.begin() +
static_cast<std::ptrdiff_t
>(index));
205 if (index >= rows.size())
return;
206 retired_item = std::move(rows[index]);
207 rows[index] = change.
item;
212 if (from >= rows.size() || index >= rows.size() || from == index)
return;
213 auto moved = std::move(rows[from]);
214 rows.erase(rows.begin() +
static_cast<std::ptrdiff_t
>(from));
215 rows.insert(rows.begin() +
static_cast<std::ptrdiff_t
>(index), std::move(moved));
220 rows.swap(reset_rows);
228 if (sink && *sink) (*sink)(notification);
231 std::shared_ptr<State> state_;
requires ::aria::ListSourceOf< L, T > JniListSource(L &source, NotifySink sink, std::shared_ptr< runtime::IDispatcher > dispatcher={})
Construct on the source's graph thread.
Definition JniListSource.hpp:58