summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt16
-rw-r--r--lib/src/turns-init.c (renamed from lib/src/turns-init.cpp)6
-rw-r--r--lib/src/turns-participant.c264
-rw-r--r--lib/src/turns-participant.cpp272
-rw-r--r--lib/src/turns-turn-order.c227
-rw-r--r--lib/src/turns-turn-order.cpp268
-rw-r--r--lib/src/turns-turn-order.h69
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
-{