7#include <QAbstractItemModel>
23template<
typename Key,
typename KeyFromData>
24class ComboBoxSelectionBinding final :
public QObject {
26 ComboBoxSelectionBinding(Property<std::optional<Key>>& selected,
27 QComboBox& combo, KeyFromData key_from_data,
29 : selected_(&selected), combo_(&combo), model_(combo.model()),
30 key_from_data_(std::move(key_from_data)), role_(role) {}
32 void start(std::weak_ptr<ComboBoxSelectionBinding> weak) {
36 QObject::connect(combo_, &QComboBox::activated,
this,
38 if (
auto state = weak.lock()) {
39 state->at_callback_boundary([&] { state->activate(index); });
42 QObject::connect(combo_, &QObject::destroyed,
this, [weak] {
43 if (
auto state = weak.lock()) state->stop();
45 QObject::connect(model_, &QObject::destroyed,
this, [weak] {
46 if (
auto state = weak.lock()) state->stop();
49 const auto refresh = [weak] {
50 if (
auto state = weak.lock()) {
51 state->at_callback_boundary([&] { state->synchronize(
true); });
54 QObject::connect(model_, &QAbstractItemModel::rowsInserted,
this, refresh);
55 QObject::connect(model_, &QAbstractItemModel::rowsRemoved,
this, refresh);
56 QObject::connect(model_, &QAbstractItemModel::rowsMoved,
this, refresh);
57 QObject::connect(model_, &QAbstractItemModel::dataChanged,
this, refresh);
58 QObject::connect(model_, &QAbstractItemModel::modelReset,
this, refresh);
59 QObject::connect(model_, &QAbstractItemModel::layoutChanged,
this, refresh);
61 selected_sub_ = selected_->on_changed([weak](
const std::optional<Key>&) {
62 if (
auto state = weak.lock()) {
63 state->at_callback_boundary([&] { state->synchronize(
false); });
69 void stop() noexcept {
74 selected_sub_.release();
78 template<
typename Callback>
79 static void at_callback_boundary(Callback&& callback)
noexcept {
81 std::forward<Callback>(callback)();
84 std::current_exception());
88 void activate(
int index) {
89 if (!combo_ || !model_)
return;
90 std::optional<Key> next;
91 if (index >= 0 && index < combo_->count()) {
92 next = std::invoke(key_from_data_, combo_->itemData(index, role_));
95 selected_->set(std::move(next));
98 void synchronize(
bool model_changed) {
99 if (!combo_ || !model_)
return;
100 const auto key = selected_->peek();
103 for (
int i = 0; i < combo_->count(); ++i) {
104 if (std::invoke(key_from_data_, combo_->itemData(i, role_)) == *key) {
111 const bool removed = model_changed && key && resolved_key_ == key && row < 0;
112 resolved_key_ = row >= 0 ? key : std::nullopt;
114 selected_->set(std::nullopt);
120 combo_->setCurrentIndex(row);
123 Property<std::optional<Key>>* selected_;
124 QPointer<QComboBox> combo_;
125 QPointer<QAbstractItemModel> model_;
126 KeyFromData key_from_data_;
128 std::optional<Key> resolved_key_;
129 Subscription selected_sub_;
159template<
typename Key,
typename KeyFromData>
161 Property<std::optional<Key>>& selected, QComboBox& combo,
162 KeyFromData key_from_data,
int role = Qt::UserRole) {
163 Q_ASSERT(QThread::currentThread() == combo.thread());
164 Q_ASSERT(combo.model() && combo.model()->thread() == combo.thread());
169 using State = detail::ComboBoxSelectionBinding<Key, KeyFromData>;
170 auto state = std::make_shared<State>(selected, combo, std::move(key_from_data), role);
172 return Subscription{std::function<void()>{[state] { state->stop(); }}};
RAII handle to a single subscription.
Definition subscription.hpp:44
Definition property.hpp:103
Definition qt_adapter.hpp:12
Subscription bind_combo_box_selection(Property< std::optional< Key > > &selected, QComboBox &combo, KeyFromData key_from_data, int role=Qt::UserRole)
Bind a ViewModel's stable selected ID to a QComboBox's existing model.
Definition qt_combo_box_binding.hpp:160
void report_callback_failure(std::string_view category, std::exception_ptr exception, std::string_view message={}) noexcept
Report a callback failure.