diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/CMakeLists.txt | 16 | ||||
| -rw-r--r-- | lib/src/turns-init.c (renamed from lib/src/turns-init.cpp) | 6 | ||||
| -rw-r--r-- | lib/src/turns-participant.c | 264 | ||||
| -rw-r--r-- | lib/src/turns-participant.cpp | 272 | ||||
| -rw-r--r-- | lib/src/turns-turn-order.c | 227 | ||||
| -rw-r--r-- | lib/src/turns-turn-order.cpp | 268 | ||||
| -rw-r--r-- | lib/src/turns-turn-order.h | 69 |
7 files changed, 539 insertions, 583 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 61e35b0..d00e804 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,6 +11,7 @@ project("libturns" include("FetchContent") include("GlibMkenums") +include("GlibIntrospection") find_package("PkgConfig" REQUIRED) @@ -56,9 +57,9 @@ set(HEADERS ) set(SOURCES - "src/turns-init.cpp" - "src/turns-participant.cpp" - "src/turns-turn-order.cpp" + "src/turns-init.c" + "src/turns-participant.c" + "src/turns-turn-order.c" ) add_library("lib" ${SOURCES}) @@ -108,6 +109,15 @@ target_add_glib_enums("lib" HEADERS ${HEADERS} ) +target_glib_generate_gir("lib" + NAMESPACE "Turns" + VERSION "${PROJECT_VERSION_MAJOR}.0" + DEPENDENCIES + "GLib-2.0" + "Gio-2.0" + "GObject-2.0" +) + enable_coverage("lib") install(TARGETS "lib" FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/turns") diff --git a/lib/src/turns-init.cpp b/lib/src/turns-init.c index 13a42cc..0cf5a97 100644 --- a/lib/src/turns-init.cpp +++ b/lib/src/turns-init.c @@ -11,14 +11,10 @@ #include <glib-object.h> #include <glib.h> -G_BEGIN_DECLS - -auto turns_init() -> void +void turns_init(void) { g_type_ensure(TURNS_TYPE_PARTICIPANT); g_type_ensure(TURNS_TYPE_PARTICIPANT_DISPOSITION); g_type_ensure(TURNS_TYPE_TURN_ORDER); g_type_ensure(TURNS_TYPE_TURN_ORDER_SORT_MODE); } - -G_END_DECLS
\ No newline at end of file diff --git a/lib/src/turns-participant.c b/lib/src/turns-participant.c new file mode 100644 index 0000000..18785db --- /dev/null +++ b/lib/src/turns-participant.c @@ -0,0 +1,264 @@ +/* + * SPDX-FileCopyrightText: 2025 Felix Morgner <felix.morgner@gmail.com> + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include "turns-participant.h" + +#include <glib-object.h> +#include <glib.h> +#include <math.h> + +struct _TurnsParticipant +{ + GObject parent_instance; + + gboolean active; + gboolean defeated; + TurnsParticipantDisposition disposition; + gchar * id; + gchar * name; + gfloat priority; +}; + +G_DEFINE_TYPE(TurnsParticipant, turns_participant, G_TYPE_OBJECT) + +enum PROPERTIES +{ + PROP_ACTIVE = 1, + PROP_DEFEATED, + PROP_DISPOSITION, + PROP_NAME, + PROP_PRIORITY, + N_PROPERTIES, +}; + +static GParamSpec * properties[N_PROPERTIES] = { + nullptr, +}; + +static void turns_participant_finalize(GObject * self) +{ + auto participant = TURNS_PARTICIPANT(self); + + g_free(participant->id); + g_free(participant->name); + + G_OBJECT_CLASS(turns_participant_parent_class)->finalize(self); +} + +static void turns_participant_get_property(GObject * self, guint id, GValue * value, GParamSpec * specification) +{ + auto participant = TURNS_PARTICIPANT(self); + + switch ((enum PROPERTIES)id) + { + case PROP_ACTIVE: + g_value_set_boolean(value, turns_participant_get_active(participant)); + return; + case PROP_DEFEATED: + g_value_set_boolean(value, turns_participant_get_defeated(participant)); + return; + case PROP_NAME: + g_value_set_string(value, turns_participant_get_name(participant)); + return; + case PROP_PRIORITY: + g_value_set_float(value, turns_participant_get_priority(participant)); + return; + case PROP_DISPOSITION: + g_value_set_enum(value, turns_participant_get_disposition(participant)); + return; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); + }; +} + +static void turns_participant_set_property(GObject * self, guint id, GValue const * value, GParamSpec * specification) +{ + auto participant = TURNS_PARTICIPANT(self); + + switch ((enum PROPERTIES)id) + { + case PROP_ACTIVE: + turns_participant_set_active(participant, g_value_get_boolean(value)); + return; + case PROP_DEFEATED: + turns_participant_set_defeated(participant, g_value_get_boolean(value)); + return; + case PROP_NAME: + turns_participant_set_name(participant, g_value_get_string(value)); + return; + case PROP_PRIORITY: + turns_participant_set_priority(participant, g_value_get_float(value)); + return; + case PROP_DISPOSITION: + turns_participant_set_disposition(participant, (TurnsParticipantDisposition)g_value_get_enum(value)); + return; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); + } +} + +static void turns_participant_class_init(TurnsParticipantClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + + (void)object_class; + + object_class->get_property = turns_participant_get_property; + object_class->set_property = turns_participant_set_property; + object_class->finalize = turns_participant_finalize; + + properties[PROP_ACTIVE] = g_param_spec_boolean("active", + "Active", + "Whether or not the participant is currently active (taking their turn)", + false, + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_DEFEATED] = g_param_spec_boolean("defeated", + "Defeated", + "Whether or not the participant has been defeated", + false, + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_NAME] = g_param_spec_string("name", + "Name", + "The Name of the participant", + "", + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_PRIORITY] = g_param_spec_float("priority", + "Priority", + "The turn priority of the participant", + -INFINITY, + +INFINITY, + 0.0f, + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_DISPOSITION] = g_param_spec_enum("disposition", + "Disposition", + "Disposition of the participant toward the players", + turns_participant_disposition_get_type(), + TURNS_PARTICIPANT_DISPOSITION_NEUTRAL, + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties(object_class, N_PROPERTIES, properties); +} + +static void turns_participant_init(TurnsParticipant * self) +{ + self->id = g_uuid_string_random(); +} + +TurnsParticipant * turns_participant_new() +{ + return TURNS_PARTICIPANT(g_object_new(TURNS_TYPE_PARTICIPANT, nullptr)); +} + +TurnsParticipant * turns_participant_new_with(gchar const * name, gfloat priority, TurnsParticipantDisposition disposition) +{ + g_return_val_if_fail(name != nullptr, nullptr); + + return TURNS_PARTICIPANT(g_object_new(TURNS_TYPE_PARTICIPANT, "name", name, "priority", priority, "disposition", disposition, nullptr)); +} + +gboolean turns_participant_get_active(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), false); + return self->active; +} + +gboolean turns_participant_get_defeated(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), false); + return self->defeated; +} + +TurnsParticipantDisposition turns_participant_get_disposition(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), TURNS_PARTICIPANT_DISPOSITION_NEUTRAL); + return self->disposition; +} + +gchar const * turns_participant_get_id(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), nullptr); + return self->id; +} + +gchar const * turns_participant_get_name(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), nullptr); + return self->name; +} + +gfloat turns_participant_get_priority(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT((TurnsParticipant *)self), 0.0f); + return self->priority; +} + +void turns_participant_set_active(TurnsParticipant * self, gboolean value) +{ + g_return_if_fail(TURNS_IS_PARTICIPANT(self)); + + if (value == self->active) + { + return; + } + + self->active = value; + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_ACTIVE]); +} + +void turns_participant_set_defeated(TurnsParticipant * self, gboolean value) +{ + g_return_if_fail(TURNS_IS_PARTICIPANT(self)); + + if (value == self->defeated) + { + return; + } + + self->defeated = value; + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_DEFEATED]); +} + +void turns_participant_set_disposition(TurnsParticipant * self, TurnsParticipantDisposition value) +{ + g_return_if_fail(TURNS_IS_PARTICIPANT(self)); + + if (value == self->disposition) + { + return; + } + + self->disposition = value; + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_DISPOSITION]); +} + +void turns_participant_set_name(TurnsParticipant * self, gchar const * value) +{ + g_return_if_fail(TURNS_IS_PARTICIPANT(self)); + g_return_if_fail(value != nullptr); + + if (!g_set_str(&self->name, value)) + { + return; + } + + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_NAME]); +} + +void turns_participant_set_priority(TurnsParticipant * self, gfloat value) +{ + g_return_if_fail(TURNS_IS_PARTICIPANT(self)); + + if (value == self->priority) + { + return; + } + + self->priority = value; + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PRIORITY]); +} diff --git a/lib/src/turns-participant.cpp b/lib/src/turns-participant.cpp deleted file mode 100644 index e69de6e..0000000 --- a/lib/src/turns-participant.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Felix Morgner <felix.morgner@gmail.com> - * SPDX-License-Identifier: LGPL-2.1-only - */ - -#include "turns-participant.h" - -#include <glib-object.h> -#include <glib.h> - -#include <array> -#include <cstddef> -#include <limits> - -G_BEGIN_DECLS - -struct _TurnsParticipant -{ - GObject parent_instance; - - gboolean active; - gboolean defeated; - TurnsParticipantDisposition disposition; - gchar * id; - gchar * name; - gfloat priority; -}; - -G_DEFINE_TYPE(TurnsParticipant, turns_participant, G_TYPE_OBJECT) - -G_END_DECLS - -namespace -{ - enum struct property - { - Active = 1, - Defeated, - Disposition, - Name, - Priority, - N_PROPERTIES, - }; - - auto static constinit properties = std::array<GParamSpec *, static_cast<std::size_t>(property::N_PROPERTIES)>{}; - - auto finalize(GObject * self) - { - auto participant = TURNS_PARTICIPANT(self); - - g_free(participant->id); - g_free(participant->name); - - G_OBJECT_CLASS(turns_participant_parent_class)->finalize(self); - } - - auto get_property(GObject * self, guint id, GValue * value, GParamSpec * specification) -> void - { - auto participant = TURNS_PARTICIPANT(self); - - switch (static_cast<property>(id)) - { - case property::Active: - return g_value_set_boolean(value, turns_participant_get_active(participant)); - case property::Defeated: - return g_value_set_boolean(value, turns_participant_get_defeated(participant)); - case property::Name: - return g_value_set_string(value, turns_participant_get_name(participant)); - case property::Priority: - return g_value_set_float(value, turns_participant_get_priority(participant)); - case property::Disposition: - return g_value_set_enum(value, turns_participant_get_disposition(participant)); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); - }; - } - - auto set_property(GObject * self, guint id, GValue const * value, GParamSpec * specification) -> void - { - auto participant = TURNS_PARTICIPANT(self); - - switch (static_cast<property>(id)) - { - case property::Active: - return turns_participant_set_active(participant, g_value_get_boolean(value)); - case property::Defeated: - return turns_participant_set_defeated(participant, g_value_get_boolean(value)); - case property::Name: - return turns_participant_set_name(participant, g_value_get_string(value)); - case property::Priority: - return turns_participant_set_priority(participant, g_value_get_float(value)); - case property::Disposition: - return turns_participant_set_disposition(participant, static_cast<TurnsParticipantDisposition>(g_value_get_enum(value))); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); - } - } -} // namespace - -G_BEGIN_DECLS - -static void turns_participant_class_init(TurnsParticipantClass * klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - - (void)object_class; - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - - properties[static_cast<std::size_t>(property::Active)] = - g_param_spec_boolean("active", - "Active", - "Whether or not the participant is currently active (taking their turn)", - false, - static_cast<GParamFlags>(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - properties[static_cast<std::size_t>(property::Defeated)] = - g_param_spec_boolean("defeated", - "Defeated", - "Whether or not the participant has been defeated", - false, - static_cast<GParamFlags>(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - properties[static_cast<std::size_t>(property::Name)] = - g_param_spec_string("name", - "Name", - "The Name of the participant", - "", - static_cast<GParamFlags>(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - properties[static_cast<std::size_t>(property::Priority)] = - g_param_spec_float("priority", - "Priority", - "The turn priority of the participant", - -std::numeric_limits<float>::infinity(), - +std::numeric_limits<float>::infinity(), - 0.0f, - static_cast<GParamFlags>(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - properties[static_cast<std::size_t>(property::Disposition)] = - g_param_spec_enum("disposition", - "Disposition", - "Disposition of the participant toward the players", - turns_participant_disposition_get_type(), - TURNS_PARTICIPANT_DISPOSITION_NEUTRAL, - static_cast<GParamFlags>(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_properties(object_class, static_cast<guint>(property::N_PROPERTIES), properties.data()); -} - -static void turns_participant_init(TurnsParticipant * self) -{ - self->id = g_uuid_string_random(); -} - -TurnsParticipant * turns_participant_new() -{ - return static_cast<TurnsParticipant *>(g_object_new(TURNS_TYPE_PARTICIPANT, nullptr)); -} - -TurnsParticipant * turns_participant_new_with(gchar const * name, gfloat priority, TurnsParticipantDisposition disposition) -{ - g_return_val_if_fail(name != nullptr, nullptr); - - return static_cast<TurnsParticipant *>( - g_object_new(TURNS_TYPE_PARTICIPANT, "name", name, "priority", priority, "disposition", static_cast<gint>(disposition), nullptr)); -} - -gboolean turns_participant_get_active(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), false); - return self->active; -} - -gboolean turns_participant_get_defeated(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), false); - return self->defeated; -} - -TurnsParticipantDisposition turns_participant_get_disposition(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), TurnsParticipantDisposition::TURNS_PARTICIPANT_DISPOSITION_NEUTRAL); - return self->disposition; -} - -gchar const * turns_participant_get_id(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), nullptr); - return self->id; -} - -gchar const * turns_participant_get_name(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), nullptr); - return self->name; -} - -gfloat turns_participant_get_priority(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast<TurnsParticipant *>(self)), 0.0f); - return self->priority; -} - -void turns_participant_set_active(TurnsParticipant * self, gboolean value) -{ - g_return_if_fail(TURNS_IS_PARTICIPANT(self)); - - if (value == self->active) - { - return; - } - - self->active = value; - g_object_notify_by_pspec(G_OBJECT(self), properties[static_cast<std::size_t>(property::Active)]); -} - -void turns_participant_set_defeated(TurnsParticipant * self, gboolean value) -{ - g_return_if_fail(TURNS_IS_PARTICIPANT(self)); - - if (value == self->defeated) - { - return; - } - - self->defeated = value; - g_object_notify_by_pspec(G_OBJECT(self), properties[static_cast<std::size_t>(property::Defeated)]); -} - -void turns_participant_set_disposition(TurnsParticipant * self, TurnsParticipantDisposition value) -{ - g_return_if_fail(TURNS_IS_PARTICIPANT(self)); - - if (value == self->disposition) - { - return; - } - - self->disposition = value; - g_object_notify_by_pspec(G_OBJECT(self), properties[static_cast<std::size_t>(property::Disposition)]); -} - -void turns_participant_set_name(TurnsParticipant * self, gchar const * value) -{ - g_return_if_fail(TURNS_IS_PARTICIPANT(self)); - g_return_if_fail(value != nullptr); - - if (!g_set_str(&self->name, value)) - { - return; - } - - g_object_notify_by_pspec(G_OBJECT(self), properties[static_cast<std::size_t>(property::Name)]); -} - -void turns_participant_set_priority(TurnsParticipant * self, gfloat value) -{ - g_return_if_fail(TURNS_IS_PARTICIPANT(self)); - - if (value == self->priority) - { - return; - } - - self->priority = value; - g_object_notify_by_pspec(G_OBJECT(self), properties[static_cast<std::size_t>(property::Priority)]); -} - -G_END_DECLS diff --git a/lib/src/turns-turn-order.c b/lib/src/turns-turn-order.c new file mode 100644 index 0000000..071e4c5 --- /dev/null +++ b/lib/src/turns-turn-order.c @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: 2025 Felix Morgner <felix.morgner@gmail.com> + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include "turns-turn-order.h" + +#include "turns-participant.h" + +#include <gio/gio.h> +#include <glib-object.h> +#include <glib.h> + +enum +{ + PROP_RUNNING = 1, + PROP_SORT_MODE, + N_PROPERTIES, +}; + +struct _TurnsTurnOrder +{ + GObject parent_instance; + + GSList * participants; + gboolean running; + TurnsTurnOrderSortMode sort_mode; +}; + +static GParamSpec * properties[N_PROPERTIES] = { + nullptr, +}; + +static void turns_turn_order_list_model_init(GListModelInterface * iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE(TurnsTurnOrder, + turns_turn_order, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, turns_turn_order_list_model_init)) + +static gint compare_participant(void const * lhs, void const * rhs, void * sort_mode) +{ + auto left_priority = turns_participant_get_priority(lhs); + auto right_priority = turns_participant_get_priority(rhs); + + auto result = 0; + + if (left_priority < right_priority) + { + result = -1; + } + else if (left_priority > right_priority) + { + result = 1; + } + + if ((TurnsTurnOrderSortMode)(long long)sort_mode == TURNS_TURN_ORDER_SORT_MODE_DESCENDING) + { + return result * -1; + } + + return result; +} + +static void sort_participants(TurnsTurnOrder * self) +{ + auto const sort_mode = turns_turn_order_get_sort_mode(self); + + self->participants = g_slist_sort_with_data(self->participants, &compare_participant, (void *)sort_mode); + + auto const participant_count = turns_turn_order_get_participant_count(self); + + g_list_model_items_changed(G_LIST_MODEL(self), 0, participant_count, participant_count); +} + +static void handle_participant_property_changed(TurnsParticipant const *, GParamSpec *, TurnsTurnOrder * self) +{ + sort_participants(self); +} + +static void turns_turn_order_get_property(GObject * self, guint id, GValue * value, GParamSpec * specification) +{ + auto instance = TURNS_TURN_ORDER(self); + + switch (id) + { + case PROP_RUNNING: + g_value_set_boolean(value, turns_turn_order_get_running(instance)); + return; + case PROP_SORT_MODE: + g_value_set_enum(value, turns_turn_order_get_sort_mode(instance)); + return; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); + } +} + +static void turns_turn_order_set_property(GObject * self, guint id, GValue const * value, GParamSpec * specification) +{ + auto instance = TURNS_TURN_ORDER(self); + + switch (id) + { + case PROP_SORT_MODE: + turns_turn_order_set_sort_mode(instance, g_value_get_enum(value)); + return; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); + } +} + +static void turns_turn_order_init(TurnsTurnOrder * self) +{ + self->participants = nullptr; + self->running = false; + self->sort_mode = TURNS_TURN_ORDER_SORT_MODE_DESCENDING; +} + +static void turns_turn_order_finalize(GObject * self) +{ + auto instance = TURNS_TURN_ORDER(self); + + g_slist_free_full(instance->participants, g_object_unref); + + G_OBJECT_CLASS(turns_turn_order_parent_class)->finalize(self); +} + +static void turns_turn_order_class_init(TurnsTurnOrderClass * klass) +{ + auto object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = turns_turn_order_finalize; + object_class->get_property = turns_turn_order_get_property; + object_class->set_property = turns_turn_order_set_property; + + properties[PROP_RUNNING] = g_param_spec_boolean("running", + "Running", + "Whether or not the turn order is running (e.g. has been started)", + false, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_SORT_MODE] = g_param_spec_enum("sort-mode", + "Sort Mode", + "The sort mode applied to the turn order", + TURNS_TYPE_TURN_ORDER_SORT_MODE, + TURNS_TURN_ORDER_SORT_MODE_DESCENDING, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties(object_class, N_PROPERTIES, properties); +} + +static gpointer turns_turn_order_list_model_get_item(GListModel * self, guint position) +{ + g_return_val_if_fail(position < turns_turn_order_get_participant_count(TURNS_TURN_ORDER(self)), nullptr); + return g_object_ref(g_slist_nth_data(TURNS_TURN_ORDER(self)->participants, position)); +} + +static guint turns_turn_order_list_model_get_n_items(GListModel * self) +{ + return turns_turn_order_get_participant_count(TURNS_TURN_ORDER(self)); +} + +static GType turns_turn_order_list_model_get_item_type(GListModel * self) +{ + (void)self; + return TURNS_TYPE_PARTICIPANT; +} + +static void turns_turn_order_list_model_init(GListModelInterface * iface) +{ + iface->get_item = turns_turn_order_list_model_get_item; + iface->get_item_type = turns_turn_order_list_model_get_item_type; + iface->get_n_items = turns_turn_order_list_model_get_n_items; +} + +TurnsTurnOrder * turns_turn_order_new() +{ + return TURNS_TURN_ORDER(g_object_new(TURNS_TYPE_TURN_ORDER, nullptr)); +} + +void turns_turn_order_add(TurnsTurnOrder * self, TurnsParticipant * participant) +{ + g_return_if_fail(participant != nullptr); + + g_signal_connect(participant, "notify::priority", (GCallback)&handle_participant_property_changed, self); + + auto sort_mode = turns_turn_order_get_sort_mode(self); + + self->participants = g_slist_insert_sorted_with_data(self->participants, g_object_ref(participant), compare_participant, (void *)sort_mode); + + auto position = g_slist_index(self->participants, participant); + + g_list_model_items_changed(G_LIST_MODEL(self), position, 0, 1); +} + +gsize turns_turn_order_get_participant_count(TurnsTurnOrder const * self) +{ + g_return_val_if_fail(TURNS_IS_TURN_ORDER((TurnsTurnOrder *)self), 0); + return g_slist_length(self->participants); +} + +gboolean turns_turn_order_get_running(TurnsTurnOrder const * self) +{ + g_return_val_if_fail(TURNS_IS_TURN_ORDER((TurnsTurnOrder *)self), false); + return self->running; +} + +TurnsTurnOrderSortMode turns_turn_order_get_sort_mode(TurnsTurnOrder const * self) +{ + g_return_val_if_fail(TURNS_IS_TURN_ORDER((TurnsTurnOrder *)self), TURNS_TURN_ORDER_SORT_MODE_ASCENDING); + return self->sort_mode; +} + +void turns_turn_order_set_sort_mode(TurnsTurnOrder * self, TurnsTurnOrderSortMode sort_mode) +{ + g_return_if_fail(TURNS_IS_TURN_ORDER(self)); + + if (sort_mode == self->sort_mode) + { + return; + } + + self->sort_mode = sort_mode; + g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_SORT_MODE]); + + sort_participants(self); +} diff --git a/lib/src/turns-turn-order.cpp b/lib/src/turns-turn-order.cpp deleted file mode 100644 index 7c8b5cf..0000000 --- a/lib/src/turns-turn-order.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Felix Morgner <felix.morgner@gmail.com> - * SPDX-License-Identifier: LGPL-2.1-only - */ - -#include "turns-turn-order.h" - -#include "turns-participant.h" - -#include <gio/gio.h> -#include <glib-object.h> -#include <glib.h> - -#include <array> -#include <bit> -#include <cstddef> - -G_BEGIN_DECLS - -struct _TurnsTurnOrder -{ - GObject parent_instance; - - GSList * participants; - gboolean running; - TurnsTurnOrderSortMode sort_mode; -}; - -auto static turns_turn_order_list_model_init(GListModelInterface * iface) -> void; - -G_DEFINE_FINAL_TYPE_WITH_CODE(TurnsTurnOrder, - turns_turn_order, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, turns_turn_order_list_model_init)); - -G_END_DECLS - -namespace -{ - enum struct property - { - Running = 1, - SortMode, - N_PROPERTIES, - }; - - auto static constinit properties = std::array<GParamSpec *, static_cast<std::size_t>(property::N_PROPERTIES)>{}; - - /* Private Functions */ - - auto static inline compare_participant(TurnsParticipant const * lhs, TurnsParticipant const * rhs, TurnsTurnOrderSortMode sort_mode) - { - auto left_priority = turns_participant_get_priority(lhs); - auto right_priority = turns_participant_get_priority(rhs); - - auto ascending_result = [&] { - if (left_priority < right_priority) - { - return -1; - } - else if (left_priority > right_priority) - { - return 1; - } - else - { - return 0; - } - }(); - - if (sort_mode == TURNS_TURN_ORDER_SORT_MODE_DESCENDING) - { - return ascending_result * -1; - } - - return ascending_result; - } - - auto static inline sort_participants(TurnsTurnOrder * self) - { - auto const sort_mode = turns_turn_order_get_sort_mode(self); - auto const sort_data = std::bit_cast<void *>(static_cast<guintptr>(sort_mode)); - auto const sort_function = std::bit_cast<GCompareDataFunc>(&compare_participant); - - self->participants = g_slist_sort_with_data(self->participants, sort_function, sort_data); - - auto const participant_count = turns_turn_order_get_participant_count(self); - - g_list_model_items_changed(G_LIST_MODEL(self), 0, participant_count, participant_count); - } - - /* Signal Handlers */ - - auto handle_participant_property_changed(TurnsParticipant const *, GParamSpec *, TurnsTurnOrder * self) - { - sort_participants(self); - } - -} // namespace - -G_BEGIN_DECLS - -/* GObject Construction / Destruction */ - -auto static turns_turn_order_init(TurnsTurnOrder * self) -> void -{ - self->participants = nullptr; - self->running = false; - self->sort_mode = TURNS_TURN_ORDER_SORT_MODE_DESCENDING; -} - -auto static turns_turn_order_finalize(GObject * self) -> void -{ - auto instance = TURNS_TURN_ORDER(self); - - g_slist_free_full(instance->participants, g_object_unref); - - G_OBJECT_CLASS(turns_turn_order_parent_class)->finalize(self); -} - -/* GObject Property Accessors */ - -auto static turns_turn_order_get_property(GObject * self, guint id, GValue * value, GParamSpec * specification) -> void -{ - auto instance = TURNS_TURN_ORDER(self); - - switch (static_cast<property>(id)) - { - case property::Running: - return g_value_set_boolean(value, turns_turn_order_get_running(instance)); - case property::SortMode: - return g_value_set_enum(value, turns_turn_order_get_sort_mode(instance)); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); - } -} - -auto static turns_turn_order_set_property(GObject * self, guint id, GValue const * value, GParamSpec * specification) -> void -{ - auto instance = TURNS_TURN_ORDER(self); - - switch (static_cast<property>(id)) - { - case property::SortMode: - return turns_turn_order_set_sort_mode(instance, static_cast<TurnsTurnOrderSortMode>(g_value_get_enum(value))); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); - } -} - -/* GType Class Initialization */ - -static void turns_turn_order_class_init(TurnsTurnOrderClass * klass) -{ - auto object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = turns_turn_order_finalize; - object_class->get_property = turns_turn_order_get_property; - object_class->set_property = turns_turn_order_set_property; - - properties[static_cast<std::size_t>(property::Running)] = - g_param_spec_boolean("running", - "Running", - "Whether or not the turn order is running (e.g. has been started)", - false, - static_cast<GParamFlags>(G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY)); - - properties[static_cast<std::size_t>(property::SortMode)] = - g_param_spec_enum("sort-mode", - "Sort Mode", - "The sort mode applied to the turn order", - TURNS_TYPE_TURN_ORDER_SORT_MODE, - TURNS_TURN_ORDER_SORT_MODE_DESCENDING, - static_cast<GParamFlags>(G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_properties(object_class, properties.size(), properties.data()); -} - -/* GListModel Implementation */ - -auto static turns_turn_order_list_model_get_item(TurnsTurnOrder * self, guint position) -> gpointer -{ - g_return_val_if_fail(position < turns_turn_order_get_participant_count(self), nullptr); - return g_object_ref(g_slist_nth_data(self->participants, position)); -} - -auto static turns_turn_order_list_model_get_n_items(TurnsTurnOrder * self) -> guint -{ - return turns_turn_order_get_participant_count(self); -} - -auto static turns_turn_order_list_model_get_item_type(TurnsTurnOrder * self) -> GType -{ |
