diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2024-07-23 15:08:19 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2024-07-23 15:08:19 +0200 |
| commit | 4ec6a2ae12b6adb843c0777649ff45a741ca6cbc (patch) | |
| tree | 93dc2ba99dbcb42b2af73f0a3c4cd4f0c0f091c4 | |
| parent | 5f4249a37ce816b8deceb299bc841190fbb15983 (diff) | |
| download | turns-4ec6a2ae12b6adb843c0777649ff45a741ca6cbc.tar.xz turns-4ec6a2ae12b6adb843c0777649ff45a741ca6cbc.zip | |
domain: redesign turn_order
| -rw-r--r-- | .vscode/settings.json | 3 | ||||
| -rw-r--r-- | app/include/turns/app/widgets/turn_order_view.hpp | 1 | ||||
| -rw-r--r-- | app/include/turns/app/windows/participant_editor.hpp | 6 | ||||
| -rw-r--r-- | app/src/widgets/participant_row.cpp | 19 | ||||
| -rw-r--r-- | app/src/widgets/turn_order_view.cpp | 20 | ||||
| -rw-r--r-- | app/src/windows/participant_editor.cpp | 12 | ||||
| -rw-r--r-- | app/src/windows/tracker.cpp | 26 | ||||
| -rw-r--r-- | app/tests/windows/participant_editor.cpp | 4 | ||||
| -rw-r--r-- | domain/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | domain/include/turns/domain/participant.hpp | 46 | ||||
| -rw-r--r-- | domain/include/turns/domain/turn_order.hpp | 111 | ||||
| -rw-r--r-- | domain/src/participant.cpp | 76 | ||||
| -rw-r--r-- | domain/src/turn_order.cpp | 237 | ||||
| -rw-r--r-- | domain/tests/participant.cpp | 90 | ||||
| -rw-r--r-- | domain/tests/register_types.cpp | 13 | ||||
| -rw-r--r-- | domain/tests/turn_order.cpp | 434 | ||||
| -rw-r--r-- | test_support/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | test_support/src/glib_main.cpp | 9 |
18 files changed, 390 insertions, 720 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 3073d74..b1a65b9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,8 @@ "gobj", "refptr", "sigc", - "ustring" + "ustring", + "vfunc" ], "editor.tabSize": 2, diff --git a/app/include/turns/app/widgets/turn_order_view.hpp b/app/include/turns/app/widgets/turn_order_view.hpp index 7b7556f..15524a9 100644 --- a/app/include/turns/app/widgets/turn_order_view.hpp +++ b/app/include/turns/app/widgets/turn_order_view.hpp @@ -27,7 +27,6 @@ namespace turns::app::widgets explicit turn_order_view(Glib::RefPtr<model_type> model = {}); private: - auto handle_active_participant_changed() -> void; auto handle_create_row(Glib::RefPtr<Glib::Object> const item) -> Gtk::Widget *; Glib::RefPtr<model_type> m_model; diff --git a/app/include/turns/app/windows/participant_editor.hpp b/app/include/turns/app/windows/participant_editor.hpp index 764b9ba..23d0569 100644 --- a/app/include/turns/app/windows/participant_editor.hpp +++ b/app/include/turns/app/windows/participant_editor.hpp @@ -21,9 +21,9 @@ namespace turns::app::windows struct participant_editor : Gtk::Widget { - using signal_finished_type = sigc::signal<void(decltype(std::declval<domain::participant const>().get_name()), - decltype(std::declval<domain::participant const>().get_priority()), - decltype(std::declval<domain::participant const>().get_disposition()))>; + using signal_finished_type = sigc::signal<void(decltype(std::declval<domain::participant const>().name().get_value()), + decltype(std::declval<domain::participant const>().priority().get_value()), + decltype(std::declval<domain::participant const>().disposition().get_value()))>; participant_editor(BaseObjectType * base, Glib::RefPtr<Gtk::Builder> const builder, Glib::RefPtr<domain::participant> obj = {}); diff --git a/app/src/widgets/participant_row.cpp b/app/src/widgets/participant_row.cpp index 3494834..87cc217 100644 --- a/app/src/widgets/participant_row.cpp +++ b/app/src/widgets/participant_row.cpp @@ -73,14 +73,14 @@ namespace turns::app::widgets if (participant) { - Glib::Binding::bind_property(participant->property_name(), m_title->property_label(), Glib::Binding::Flags::SYNC_CREATE); + Glib::Binding::bind_property(participant->name(), m_title->property_label(), Glib::Binding::Flags::SYNC_CREATE); - Glib::Binding::bind_property(participant->property_priority(), + Glib::Binding::bind_property(participant->priority(), m_subtitle->property_label(), Glib::Binding::Flags::SYNC_CREATE, [](auto n) { return std::vformat(_(lang::priority_number), std::make_format_args(n)); }); - Glib::Binding::bind_property(participant->property_disposition(), + Glib::Binding::bind_property(participant->disposition(), m_toggle_defeated->property_css_classes(), Glib::Binding::Flags::SYNC_CREATE, [this](auto value) { @@ -92,6 +92,19 @@ namespace turns::app::widgets classes.push_back(css_class_for(value)); return classes; }); + + Glib::Binding::bind_property(participant->is_active(), property_css_classes(), Glib::Binding::Flags::SYNC_CREATE, [this](auto value) { + auto classes = get_css_classes(); + if (!value) + { + std::erase(classes, "active-participant"); + } + else + { + classes.push_back("active-participant"); + } + return classes; + }); } } diff --git a/app/src/widgets/turn_order_view.cpp b/app/src/widgets/turn_order_view.cpp index ccc6d25..67e0afa 100644 --- a/app/src/widgets/turn_order_view.cpp +++ b/app/src/widgets/turn_order_view.cpp @@ -25,35 +25,21 @@ namespace turns::app::widgets { if (model) { - m_view->bind_model(m_model->list_model(), sigc::mem_fun(*this, &turn_order_view::handle_create_row)); - m_model->property_active_participant().signal_changed().connect( - sigc::mem_fun(*this, &turn_order_view::handle_active_participant_changed)); + m_view->bind_model(m_model, sigc::mem_fun(*this, &turn_order_view::handle_create_row)); } } - auto turn_order_view::handle_active_participant_changed() -> void - { - std::ranges::for_each(m_view->get_children(), [](auto c) { c->remove_css_class("active-participant"); }); - - auto index = m_model->active_participant(); - if (index != std::numeric_limits<domain::turn_order::active_participant_type>::max()) - { - auto row = m_view->get_row_at_index(index); - row->add_css_class("active-participant"); - row->grab_focus(); - } - } auto turn_order_view::handle_create_row(Glib::RefPtr<Glib::Object> const item) -> Gtk::Widget * { auto participant = std::dynamic_pointer_cast<domain::participant>(item); auto row = Gtk::make_managed<widgets::participant_row>(participant); - Glib::Binding::bind_property(m_model->property_running(), + Glib::Binding::bind_property(m_model->is_running(), row->property_delete_enabled(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); - Glib::Binding::bind_property(m_model->property_running(), + Glib::Binding::bind_property(m_model->is_running(), row->property_edit_enabled(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); diff --git a/app/src/windows/participant_editor.cpp b/app/src/windows/participant_editor.cpp index 1c97442..0b35c72 100644 --- a/app/src/windows/participant_editor.cpp +++ b/app/src/windows/participant_editor.cpp @@ -42,9 +42,9 @@ namespace turns::app::windows if (m_participant) { - gtk_editable_set_text(GTK_EDITABLE(m_name), m_participant->property_name().get_value().c_str()); - adw_spin_row_set_value(m_priority, m_participant->property_priority().get_value()); - adw_combo_row_set_selected(m_disposition, static_cast<unsigned>(m_participant->get_disposition())); + gtk_editable_set_text(GTK_EDITABLE(m_name), m_participant->name().get_value().c_str()); + adw_spin_row_set_value(m_priority, m_participant->priority()); + adw_combo_row_set_selected(m_disposition, static_cast<unsigned>(m_participant->disposition().get_value())); } } @@ -66,9 +66,9 @@ namespace turns::app::windows if (m_participant) { - m_participant->set_name(name); - m_participant->set_priority(priority); - m_participant->set_disposition(disposition); + m_participant->name() = name; + m_participant->priority() = priority; + m_participant->disposition() = disposition; } m_signal_finished.emit(name, priority, disposition); diff --git a/app/src/windows/tracker.cpp b/app/src/windows/tracker.cpp index b7520dd..11f4642 100644 --- a/app/src/windows/tracker.cpp +++ b/app/src/windows/tracker.cpp @@ -51,16 +51,16 @@ namespace turns::app::windows m_stack->add(*m_turn_order_view); // clang-format off - Glib::Binding::bind_property(m_turn_order->property_empty(), + Glib::Binding::bind_property(m_turn_order->is_empty(), m_stack->property_visible_child(), Glib::Binding::Flags::SYNC_CREATE, [this](auto empty) { return empty ? m_empty : m_turn_order_view; }); - Glib::Binding::bind_property(m_turn_order->property_running(), + Glib::Binding::bind_property(m_turn_order->is_running(), m_controls->property_reveal_child(), Glib::Binding::Flags::SYNC_CREATE); - Glib::Binding::bind_property(m_turn_order->property_empty(), + Glib::Binding::bind_property(m_turn_order->is_empty(), m_subtitle, Glib::Binding::Flags::SYNC_CREATE, [](auto empty) { return empty ? _(lang::no_active_turn_order) : ""; }); @@ -84,7 +84,7 @@ namespace turns::app::windows { static_cast<void>(param); auto index = Glib::VariantBase::cast_dynamic<Glib::Variant<int>>(param); - auto participant = m_turn_order->get(index.get()); + auto participant = m_turn_order->get_typed_object<domain::participant>(index.get()); auto [lifeline, dialog] = editor_for(participant); dialog->present(this); } @@ -112,17 +112,17 @@ namespace turns::app::windows { auto action = add_action("add_participant", sigc::mem_fun(*this, &tracker::handle_add_participant)); - Glib::Binding::bind_property(m_turn_order->property_running(), + Glib::Binding::bind_property(m_turn_order->is_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); } // win.clear - // depends-on: turn_order:empty == false + // depends-on: turn_order:is_empty == false { auto action = add_action("clear", sigc::mem_fun(*m_turn_order, &domain::turn_order::clear)); - Glib::Binding::bind_property(m_turn_order->property_empty(), + Glib::Binding::bind_property(m_turn_order->is_empty(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); } @@ -132,7 +132,7 @@ namespace turns::app::windows { auto action = add_action("next", sigc::mem_fun(*m_turn_order, &domain::turn_order::next)); - Glib::Binding::bind_property(m_turn_order->property_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); + Glib::Binding::bind_property(m_turn_order->is_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); } // win.previous @@ -140,19 +140,19 @@ namespace turns::app::windows { auto action = add_action("previous", sigc::mem_fun(*m_turn_order, &domain::turn_order::previous)); - Glib::Binding::bind_property(m_turn_order->property_has_previous(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); + Glib::Binding::bind_property(m_turn_order->has_previous(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); } // win.start - // depends-on: turn_order:empty == false + // depends-on: turn_order:is_empty == false { auto action = add_action("start", sigc::mem_fun(*m_turn_order, &domain::turn_order::start)); - Glib::Binding::bind_property(m_turn_order->property_empty(), + Glib::Binding::bind_property(m_turn_order->is_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); - Glib::Binding::bind_property(m_turn_order->property_running(), + Glib::Binding::bind_property(m_turn_order->is_running(), m_start->property_visible(), Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN); } @@ -162,7 +162,7 @@ namespace turns::app::windows { auto action = add_action("stop", sigc::mem_fun(*this, &tracker::handle_stop)); - Glib::Binding::bind_property(m_turn_order->property_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); + Glib::Binding::bind_property(m_turn_order->is_running(), action->property_enabled(), Glib::Binding::Flags::SYNC_CREATE); } // win.delete diff --git a/app/tests/windows/participant_editor.cpp b/app/tests/windows/participant_editor.cpp index 5808740..9f73861 100644 --- a/app/tests/windows/participant_editor.cpp +++ b/app/tests/windows/participant_editor.cpp @@ -95,13 +95,13 @@ namespace turns::app::windows::tests SECTION("has its name field set according to its participant") { auto widget = GTK_EDITABLE(builder->get_widget<Gtk::ListBoxRow>("name")->gobj()); - REQUIRE(gtk_editable_get_text(widget) == participant->get_name()); + REQUIRE(gtk_editable_get_text(widget) == participant->name().get_value()); } SECTION("has its priority field set according to its participant") { auto widget = ADW_SPIN_ROW(builder->get_widget<Gtk::ListBoxRow>("priority")->gobj()); - REQUIRE(adw_spin_row_get_value(widget) == participant->get_priority()); + REQUIRE(adw_spin_row_get_value(widget) == participant->priority()); } SECTION("allows binding to the finished signal") diff --git a/domain/CMakeLists.txt b/domain/CMakeLists.txt index 58a7900..ec9eb62 100644 --- a/domain/CMakeLists.txt +++ b/domain/CMakeLists.txt @@ -37,6 +37,8 @@ target_link_options("domain" PRIVATE # Tests add_executable("domain-tests" + "tests/register_types.cpp" + "tests/disposition.cpp" "tests/participant.cpp" "tests/turn_order.cpp" diff --git a/domain/include/turns/domain/participant.hpp b/domain/include/turns/domain/participant.hpp index d845c77..b51425d 100644 --- a/domain/include/turns/domain/participant.hpp +++ b/domain/include/turns/domain/participant.hpp @@ -13,34 +13,44 @@ namespace turns::domain { - struct participant : Glib::Object { auto static create(Glib::ustring name, float priority, disposition disposition) -> Glib::RefPtr<participant>; + participant(); participant(Glib::ustring name, float priority, disposition disposition); auto operator<=>(participant const & other) const noexcept -> std::partial_ordering; - auto property_disposition() -> Glib::PropertyProxy<disposition>; - auto property_disposition() const -> Glib::PropertyProxy_ReadOnly<disposition>; - auto get_disposition() const noexcept -> disposition; - auto set_disposition(disposition value) -> void; - - auto property_name() -> Glib::PropertyProxy<Glib::ustring>; - auto property_name() const -> Glib::PropertyProxy_ReadOnly<Glib::ustring>; - auto get_name() const -> Glib::ustring; - auto set_name(Glib::ustring value) -> void; - - auto property_priority() -> Glib::PropertyProxy<float>; - auto property_priority() const -> Glib::PropertyProxy_ReadOnly<float>; - auto get_priority() const noexcept -> float; - auto set_priority(float value) -> void; + template<typename Self> + auto disposition(this Self && self) + { + return self.m_disposition.get_proxy(); + } + + template<typename Self> + auto is_active(this Self && self) + { + return self.m_is_active.get_proxy(); + } + + template<typename Self> + auto name(this Self && self) + { + return self.m_name.get_proxy(); + } + + template<typename Self> + auto priority(this Self && self) + { + return self.m_priority.get_proxy(); + } private: - Glib::Property<disposition> m_disposition; - Glib::Property<Glib::ustring> m_name; - Glib::Property<float> m_priority; + Glib::Property<domain::disposition> m_disposition{*this, "disposition", domain::disposition::neutral}; + Glib::Property<bool> m_is_active{*this, "active", false}; + Glib::Property<Glib::ustring> m_name{*this, "name", ""}; + Glib::Property<float> m_priority{*this, "priority", 0.0f}; }; } // namespace turns::domain diff --git a/domain/include/turns/domain/turn_order.hpp b/domain/include/turns/domain/turn_order.hpp index 3b42562..ca44b62 100644 --- a/domain/include/turns/domain/turn_order.hpp +++ b/domain/include/turns/domain/turn_order.hpp @@ -4,9 +4,12 @@ #include "turns/domain/disposition.hpp" #include "turns/domain/participant.hpp" +#include <initializer_list> #include <limits> +#include <optional> +#include <vector> -#include <giomm/liststore.h> +#include <giomm/listmodel.h> #include <glibmm/property.h> #include <glibmm/refptr.h> #include <glibmm/ustring.h> @@ -14,91 +17,67 @@ namespace turns::domain { - struct turn_order : Glib::Object + struct turn_order : Gio::ListModel, + Glib::Object { + using value_type = Glib::RefPtr<participant>; + using container_type = std::vector<value_type>; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using active_participant_type = unsigned int; - using empty_type = bool; + using is_empty_type = bool; using has_next_type = bool; using has_previous_type = bool; - using running_type = bool; - using round_type = unsigned int; + using is_running_type = bool; + using round_number_type = unsigned int; auto static constexpr invalid_participant_index = std::numeric_limits<active_participant_type>::max(); + auto static constexpr invalid_round_number = std::numeric_limits<round_number_type>::max(); - auto static create() -> Glib::RefPtr<turn_order>; - + /** Life-time */ turn_order(); - /** Modifiers */ + auto static create() -> Glib::RefPtr<turn_order>; + + /** Properties */ + auto is_empty() const -> Glib::PropertyProxy_ReadOnly<is_empty_type>; + auto has_next() const -> Glib::PropertyProxy_ReadOnly<has_next_type>; + auto has_previous() const -> Glib::PropertyProxy_ReadOnly<has_previous_type>; + auto is_running() const -> Glib::PropertyProxy_ReadOnly<is_running_type>; + auto round_number() const -> Glib::PropertyProxy_ReadOnly<round_number_type>; + /** Element Modifications */ auto add(Glib::ustring const & name, float priority, disposition disposition) -> void; auto clear() -> void; + auto remove(unsigned index) -> void; + + /** Turn Modification */ auto next() -> void; auto previous() -> void; - auto remove(unsigned int index) -> void; - auto reset() -> void; auto start() -> void; auto stop() -> void; - /** Querries */ - - /** - * Get the index of the currently active participant of this turn order, if any. - * - * @returns an unsigned integer in the range [0, size()) if there is an active participant, or turn_order::invalid_participant_index otherwise. - */ - auto active_participant() const noexcept -> active_participant_type; - - /** - * Check if this turn order is empty. - */ - auto empty() const noexcept -> empty_type; - - /** - * Get the actor at the specified position in this turn order. - * - * @return a valid pointer to a participant object if the index was valid, nullptr otherwise. - */ - auto get(unsigned int index) const noexcept -> Glib::RefPtr<participant>; - - /** - * Get the underlying list model, to be used with list views. - */ - auto list_model() -> Glib::RefPtr<Gio::ListModel>; - - /** - * Get the current round. - */ - auto round() const noexcept -> round_type; - - /** - * Check if this turn order is currently running. - */ - auto running() const noexcept -> running_type; - - /** - * Get the size of this turn order - */ - auto size() const noexcept -> unsigned int; + private: + auto get_item_type_vfunc() -> GType override; + auto get_n_items_vfunc() -> unsigned override; + auto get_item_vfunc(unsigned position) -> void * override; - /** Properties */ + /** Signal handlers */ + auto handle_priority_changed(value_type entry) -> void; - auto property_active_participant() const -> Glib::PropertyProxy_ReadOnly<active_participant_type>; - auto property_empty() const -> Glib::PropertyProxy_ReadOnly<empty_type>; - auto property_has_next() const -> Glib::PropertyProxy_ReadOnly<has_next_type>; - auto property_has_previous() const -> Glib::PropertyProxy_ReadOnly<has_previous_type>; - auto property_running() const -> Glib::PropertyProxy_ReadOnly<running_type>; - auto property_round() const -> Glib::PropertyProxy_ReadOnly<round_type>; + /** Data management */ + auto find(value_type entry) const -> const_iterator; + auto insert(value_type entry) -> const_iterator; - private: - Glib::RefPtr<Gio::ListStore<participant>> m_model; - - Glib::Property<active_participant_type> m_active_participant; - Glib::Property<empty_type> m_empty; - Glib::Property<has_next_type> m_has_next; - Glib::Property<has_previous_type> m_has_previous; - Glib::Property<running_type> m_running; - Glib::Property<round_type> m_round; + container_type m_data{}; + std::optional<unsigned> m_active{}; + + Glib::Property<has_next_type> m_has_next{*this, "has-next", false}; + Glib::Property<has_previous_type> m_has_previous{*this, "has-previous", false}; + Glib::Property<is_empty_type> m_is_empty{*this, "is-empty", true}; + Glib::Property<is_running_type> m_is_running{*this, "is-running", false}; + Glib::Property<round_number_type> m_round_number{*this, "round-number", invalid_round_number}; }; } // namespace turns::domain diff --git a/domain/src/participant.cpp b/domain/src/participant.cpp index 5265eb3..6f0efb1 100644 --- a/domain/src/participant.cpp +++ b/domain/src/participant.cpp @@ -3,87 +3,33 @@ #include <typeinfo> #include <utility> +#include <glibmm/class.h> #include <glibmm/refptr.h> namespace turns::domain { - - auto participant::create(Glib::ustring name, float priority, disposition disposition) -> Glib::RefPtr<participant> + auto participant::create(Glib::ustring name, float priority, domain::disposition disposition) -> Glib::RefPtr<participant> { return Glib::make_refptr_for_instance(new participant{name, priority, disposition}); } - participant::participant(Glib::ustring name, float priority, disposition disposition) + participant::participant() : Glib::ObjectBase{typeid(participant)} - , m_disposition{*this, "disposition", disposition} - , m_name{*this, "name", name} - , m_priority{*this, "priority", priority} - { - } - - auto participant::operator<=>(participant const & other) const noexcept -> std::partial_ordering - { - return m_priority <=> other.m_priority; - } - - auto participant::get_disposition() const noexcept -> disposition - { - return m_disposition; - } - - auto participant::set_disposition(disposition value) -> void - { - m_disposition = value; - } - - auto participant::get_name() const -> Glib::ustring - { - return m_name; - } - - auto participant::set_name(Glib::ustring value) -> void - { - m_name = value; - } - - auto participant::get_priority() const noexcept -> float - { - return m_priority; - } - - auto participant::set_priority(float value) -> void + , Glib::Object{} { - m_priority = value; } - auto participant::property_disposition() -> Glib::PropertyProxy<disposition> + participant::participant(Glib::ustring name, float priority, domain::disposition disposition) + : participant() { - return m_disposition.get_proxy(); + m_name = name; + m_priority = priority; + m_disposition = disposition; } - auto participant::property_disposition() const -> Glib::PropertyProxy_ReadOnly<disposition> - { - return m_disposition.get_proxy(); - } - - auto participant::property_name() -> Glib::PropertyProxy<Glib::ustring> - { - return m_name.get_proxy(); - } - - auto participant::property_name() const -> Glib::PropertyProxy_ReadOnly<Glib::ustring> - { - return m_name.get_proxy(); - } - - auto participant::property_priority() -> Glib::PropertyProxy<float> - { - return m_priority.get_proxy(); - } - - auto participant::property_priority() const -> Glib::PropertyProxy_ReadOnly<float> + auto participant::operator<=>(participant const & other) const noexcept -> std::partial_ordering { - return m_priority.get_proxy(); + return m_priority <=> other.m_priority; } } // namespace turns::domain
\ No newline at end of file diff --git a/domain/src/turn_order.cpp b/domain/src/turn_order.cpp index 61ccdca..0f8b6e8 100644 --- a/domain/src/turn_order.cpp +++ b/domain/src/turn_order.cpp @@ -2,11 +2,11 @@ #include "turns/domain/participant.hpp" +#include <algorithm> #include <compare> #include <limits> #include <typeinfo> -#include <glibmm/binding.h> #include <glibmm/refptr.h> namespace turns::domain @@ -32,194 +32,203 @@ namespace turns::domain }; } // namespace + /** Construction */ + + turn_order::turn_order() + : Glib::ObjectBase{typeid(turn_order)} + , Gio::ListModel{} + { + } + auto turn_order::create() -> Glib::RefPtr<turn_order> { return Glib::make_refptr_for_instance(new turn_order{}); } - turn_order::turn_order() - : Glib::ObjectBase{typeid(turn_order)} - , m_model{Gio::ListStore<participant>::create()} - , m_active_participant(*this, "active_participant", invalid_participant_index) - , m_empty{*this, "empty", true} - , m_has_next{*this, "has-next", false} - , m_has_previous{*this, "has-previous", false} - , m_running{*this, "running", false} - , m_round{*this, "round", 0} + /** Queries */ + + auto turn_order::is_empty() const -> Glib::PropertyProxy_ReadOnly<is_empty_type> + { + return m_is_empty.get_proxy(); + } + + auto turn_order::has_next() const -> Glib::PropertyProxy_ReadOnly<has_next_type> + { + return m_has_next.get_proxy(); + } + + auto turn_order::has_previous() const -> Glib::PropertyProxy_ReadOnly<has_previous_type> + { + return m_has_previous.get_proxy(); + } + + auto turn_order::is_running() const -> Glib::PropertyProxy_ReadOnly<is_running_type> { - Glib::Binding::bind_property(m_model->property_n_items(), m_empty.get_proxy(), Glib::Binding::Flags::DEFAULT, [](auto n) { - return n == 0; - }); + return m_is_running.get_proxy(); + } + + auto turn_order::round_number() const -> Glib::PropertyProxy_ReadOnly<round_number_type> + { + return m_round_number.get_proxy(); } /** Modifiers */ auto turn_order::add(Glib::ustring const & name, float priority, disposition disposition) -> void { - auto participant = participant::create(name, priority, disposition); - if (auto [found, index] = m_model->find(participant, equal_comparator); !found) - { - auto position = m_model->insert_sorted(participant, comparator); - participant->property_priority().signal_changed().connect([this] { m_model->sort(comparator); }); + auto entry = participant::create(name, priority, disposition); + entry->priority().signal_changed().connect(sigc::bind(sigc::mem_fun(*this, &turn_order::handle_priority_changed), entry)); + auto position = std::distance(m_data.cbegin(), insert(entry)); + items_changed(position, 0, 1); - if (m_active_participant != invalid_participant_index && position <= m_active_participant) - { - m_active_participant = m_active_participant + 1; - } + if (get_n_items() == 1) + { + m_is_empty = false; + m_has_next = true; } } auto turn_order::clear() -> void { - m_model->remove_all(); - m_active_participant = invalid_participant_index; + m_is_running = false; + m_is_empty = true; m_has_next = false; m_has_previous = false; - m_running = false; + m_active.reset(); + m_round_number = invalid_round_number; + + auto old_size = get_n_items(); + m_data.clear(); + items_changed(0, old_size, 0); } auto turn_order::next() -> void { - m_active_participant = (m_active_participant + 1) % size(); - if (!m_active_participant) + auto old_active = *m_active; + m_active = m_active.transform([this](auto index) { return (index + 1) % get_n_items(); }); + + m_has_previous = true; + m_data[old_active]->is_active() = false; + m_data[*m_active]->is_active() = true; + + if (m_active == 0) { - m_round = round() + 1; + m_round_number = m_round_number + 1; } - m_has_previous = m_active_participant || m_round; } auto turn_order::previous() -> void { - if (!m_has_previous) + if (!(m_has_previous && m_is_running)) { return; } - if (m_active_participant) - { - m_active_participant = m_active_participant - 1; - } - else if (m_round) + auto old_active = *m_active; + m_active = m_active.transform([this](auto index) { return index ? index - 1 : get_n_items() - 1; }); + + m_has_previous = m_round_number > 0 || m_active > 0; + m_data[old_active]->is_active() = false; + m_data[*m_active]->is_active() = true; + + if (m_active == 0) { - m_round = round() - 1; - m_active_participant = size() - 1; + m_round_number = m_round_number - 1; } - - m_has_previous = m_active_participant || m_round; } - auto turn_order::remove(unsigned int index) -> void + auto turn_order::remove(unsigned index) -> void { - m_model->remove(index); - if (empty()) + if (index >= get_n_items()) { - m_active_participant = invalid_participant_index; - m_has_next = false; - m_has_previous = false; - m_running = false; + return; } - else if (m_active_participant != invalid_participant_index) + + auto position = m_data.begin() + index; + m_data.erase(position); + items_changed(index, 1, 0); + if (get_n_items() == 0) { - if (m_active_participant > size() - 1) - { - m_active_participant = size() - 1; |
