#include "turns-turn-order.h" #include "turns-enums.h" #include "turns-participant.h" #include #include #include #include #include #include #include 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(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(static_cast(sort_mode)); auto const sort_function = std::bit_cast(&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(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(id)) { case property::SortMode: return turns_turn_order_set_sort_mode(instance, static_cast(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(property::Running)] = g_param_spec_boolean("running", "Running", "Whether or not the turn order is running (e.g. has been started)", false, static_cast(G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY)); properties[static_cast(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(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 { static_cast(self); return TURNS_TYPE_PARTICIPANT; } auto static turns_turn_order_list_model_init(GListModelInterface * iface) -> void { iface->get_item = std::bit_castget_item)>(&turns_turn_order_list_model_get_item); iface->get_item_type = std::bit_castget_item_type)>(&turns_turn_order_list_model_get_item_type); iface->get_n_items = std::bit_castget_n_items)>(&turns_turn_order_list_model_get_n_items); } /* Constructors */ auto turns_turn_order_new() -> TurnsTurnOrder * { return TURNS_TURN_ORDER(g_object_new(TURNS_TYPE_TURN_ORDER, nullptr)); } /* Functions */ auto turns_turn_order_add(TurnsTurnOrder * self, TurnsParticipant * participant) -> void { g_return_if_fail(participant != nullptr); g_signal_connect(participant, "notify::priority", std::bit_cast(&handle_participant_property_changed), self); auto sort_mode = turns_turn_order_get_sort_mode(self); auto sort_data = std::bit_cast(static_cast(sort_mode)); auto sort_function = std::bit_cast(&compare_participant); self->participants = g_slist_insert_sorted_with_data(self->participants, g_object_ref(participant), sort_function, sort_data); auto position = g_slist_index(self->participants, participant); g_list_model_items_changed(G_LIST_MODEL(self), position, 0, 1); } /* Getters */ auto turns_turn_order_get_participant_count(TurnsTurnOrder const * self) -> gsize { g_return_val_if_fail(TURNS_IS_TURN_ORDER(const_cast(self)), 0); return g_slist_length(self->participants); } auto turns_turn_order_get_running(TurnsTurnOrder const * self) -> gboolean { g_return_val_if_fail(TURNS_IS_TURN_ORDER(const_cast(self)), false); return self->running; } auto turns_turn_order_get_sort_mode(TurnsTurnOrder const * self) -> TurnsTurnOrderSortMode { g_return_val_if_fail(TURNS_IS_TURN_ORDER(const_cast(self)), TURNS_TURN_ORDER_SORT_MODE_ASCENDING); return self->sort_mode; } /* Setters */ auto turns_turn_order_set_sort_mode(TurnsTurnOrder * self, TurnsTurnOrderSortMode sort_mode) -> void { 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[static_cast(property::SortMode)]); sort_participants(self); } G_END_DECLS