From a2c73b6ba3c9ea307b0b0eb94c9e769a1f31dc00 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 12 May 2025 16:21:53 +0200 Subject: lib: move C library to new directory --- CMakeLists.txt | 13 +- app/schemas/ch.arknet.Turns.gschema.xml | 25 +++ cmake/Modules/GlibMkenums.cmake | 8 +- core/.gitignore | 2 - core/include/turns/turns-disposition.h | 18 -- core/include/turns/turns-enums.h.in | 22 -- core/include/turns/turns-participant.h | 27 --- core/src/turns-enums.c.in | 37 ---- core/src/turns-participant.cpp | 190 ----------------- lib/.gitignore | 2 + lib/CMakeLists.txt | 88 ++++++++ lib/src/turns-disposition.h | 18 ++ lib/src/turns-enums.c.in | 37 ++++ lib/src/turns-enums.h.in | 25 +++ lib/src/turns-init.cpp | 17 ++ lib/src/turns-init.h | 12 ++ lib/src/turns-participant.cpp | 202 ++++++++++++++++++ lib/src/turns-participant.h | 84 ++++++++ lib/tests/runtime_init.cpp | 21 ++ lib/tests/turns-participant.cpp | 353 ++++++++++++++++++++++++++++++++ 20 files changed, 893 insertions(+), 308 deletions(-) create mode 100644 app/schemas/ch.arknet.Turns.gschema.xml delete mode 100644 core/.gitignore delete mode 100644 core/include/turns/turns-disposition.h delete mode 100644 core/include/turns/turns-enums.h.in delete mode 100644 core/include/turns/turns-participant.h delete mode 100644 core/src/turns-enums.c.in delete mode 100644 core/src/turns-participant.cpp create mode 100644 lib/.gitignore create mode 100644 lib/CMakeLists.txt create mode 100644 lib/src/turns-disposition.h create mode 100644 lib/src/turns-enums.c.in create mode 100644 lib/src/turns-enums.h.in create mode 100644 lib/src/turns-init.cpp create mode 100644 lib/src/turns-init.h create mode 100644 lib/src/turns-participant.cpp create mode 100644 lib/src/turns-participant.h create mode 100644 lib/tests/runtime_init.cpp create mode 100644 lib/tests/turns-participant.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 84ead94..a6db0f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ FetchContent_MakeAvailable("Catch2" "nlohmann_json") pkg_check_modules("giomm" IMPORTED_TARGET REQUIRED "giomm-2.68>=${GLIB_MINIMUM_VERSION}") pkg_check_modules("glibmm" IMPORTED_TARGET REQUIRED "glibmm-2.68>=${GLIB_MINIMUM_VERSION}") pkg_check_modules("gtkmm" IMPORTED_TARGET REQUIRED "gtkmm-4.0>=${GTK4_MINIMUM_VERSION}") +pkg_check_modules("gobject" IMPORTED_TARGET REQUIRED "gobject-2.0>=${GLIB_MINIMUM_VERSION}") add_subdirectory("deps/libadwaitamm" EXCLUDE_FROM_ALL SYSTEM) @@ -74,11 +75,13 @@ include("Catch") # Targets -add_subdirectory("app") -add_subdirectory("core") -add_subdirectory("lang") -add_subdirectory("style") -add_subdirectory("ui") +add_subdirectory("lib") + +# add_subdirectory("app") +# add_subdirectory("core") +# add_subdirectory("lang") +# add_subdirectory("style") +# add_subdirectory("ui") # License diff --git a/app/schemas/ch.arknet.Turns.gschema.xml b/app/schemas/ch.arknet.Turns.gschema.xml new file mode 100644 index 0000000..d764608 --- /dev/null +++ b/app/schemas/ch.arknet.Turns.gschema.xml @@ -0,0 +1,25 @@ + + + + + '#33d17a' + Friendly Disposition Color + The color used to shade friendly participants. + + + '#e01b24' + Hostile Disposition Color + The color used to shade hostile participants. + + + '#9141ac' + Secret Disposition Color + The color used to shade secret participants. + + + false + Skip Defeated Participants + Whether or not defeated participants shall be skipped while stepping through the turn order. + + + \ No newline at end of file diff --git a/cmake/Modules/GlibMkenums.cmake b/cmake/Modules/GlibMkenums.cmake index b7caf10..58ae4ee 100644 --- a/cmake/Modules/GlibMkenums.cmake +++ b/cmake/Modules/GlibMkenums.cmake @@ -7,7 +7,6 @@ find_program(GLIB_MKENUMS_BIN function(target_add_glib_enums TARGET) set(SINGLE_VALUE_ARGS - "HEADER_OUTPUT_DIR" "HEADER_TEMPLATE" "OUTPUT_NAME" "SOURCE_TEMPLATE" @@ -25,10 +24,6 @@ function(target_add_glib_enums TARGET) message(FATAL_ERROR "Target '${TARGET}' does not exist") endif() - if(NOT _HEADER_OUTPUT_DIR) - message(FATAL_ERROR "Missing header output dir") - endif() - if(NOT _HEADERS) message(FATAL_ERROR "Missing enum headers") endif() @@ -42,7 +37,7 @@ function(target_add_glib_enums TARGET) endif() set(SOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/${_OUTPUT_NAME}.c") - set(HEADER_FILE "${CMAKE_CURRENT_SOURCE_DIR}/include/${_HEADER_OUTPUT_DIR}/${_OUTPUT_NAME}.h") + set(HEADER_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/${_OUTPUT_NAME}.h") add_custom_command(OUTPUT ${SOURCE_FILE} @@ -80,5 +75,4 @@ function(target_add_glib_enums TARGET) "${CMAKE_CURRENT_BINARY_DIR}" ) - endfunction() diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100644 index 0050be3..0000000 --- a/core/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -turns-enums.c -turns-enums.h \ No newline at end of file diff --git a/core/include/turns/turns-disposition.h b/core/include/turns/turns-disposition.h deleted file mode 100644 index c5bb174..0000000 --- a/core/include/turns/turns-disposition.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TURNS_DISPOSITION_H -#define TURNS_DISPOSITION_H - -#include - -G_BEGIN_DECLS - -typedef enum -{ - TURNS_DISPOSITION_NEUTRAL, - TURNS_DISPOSITION_FRIENDLY, - TURNS_DISPOSITION_HOSTILE, - TURNS_DISPOSITION_SECRET, -} TurnsDisposition; - -G_END_DECLS - -#endif diff --git a/core/include/turns/turns-enums.h.in b/core/include/turns/turns-enums.h.in deleted file mode 100644 index 10bb3f1..0000000 --- a/core/include/turns/turns-enums.h.in +++ /dev/null @@ -1,22 +0,0 @@ -/*** BEGIN file-header ***/ -#pragma once - -#include -#include - -G_BEGIN_DECLS -/*** END file-header ***/ - -/*** BEGIN file-production ***/ - -/* enumerations from "@basename@" */ -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GType @enum_name@_get_type (void) G_GNUC_CONST; -#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) -/*** END value-header ***/ - -/*** BEGIN file-tail ***/ -G_END_DECLS -/*** END file-tail ***/ \ No newline at end of file diff --git a/core/include/turns/turns-participant.h b/core/include/turns/turns-participant.h deleted file mode 100644 index 001de6b..0000000 --- a/core/include/turns/turns-participant.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TURNS_PARTICIPANT_H -#define TURNS_PARTICIPANT_H - -#include "turns/turns-disposition.h" - -#include -#include -#include - -G_BEGIN_DECLS - -#define TURNS_TYPE_PARTICIPANT turns_participant_get_type() -G_DECLARE_FINAL_TYPE(TurnsParticipant, turns_participant, TURNS, PARTICIPANT, GObject) - -TurnsParticipant * turns_participant_new(gchar const * name, gfloat priority, TurnsDisposition disposition) G_GNUC_WARN_UNUSED_RESULT; - -gchar const * turns_participant_get_name(TurnsParticipant const * self); -gfloat turns_participant_get_priority(TurnsParticipant const * self); -TurnsDisposition turns_participant_get_disposition(TurnsParticipant const * self); - -void turns_participant_set_name(TurnsParticipant * self, gchar const * value); -void turns_participant_set_priority(TurnsParticipant * self, gfloat value); -void turns_participant_set_disposition(TurnsParticipant * self, TurnsDisposition value); - -G_END_DECLS - -#endif \ No newline at end of file diff --git a/core/src/turns-enums.c.in b/core/src/turns-enums.c.in deleted file mode 100644 index adef39b..0000000 --- a/core/src/turns-enums.c.in +++ /dev/null @@ -1,37 +0,0 @@ -/*** BEGIN file-header ***/ -#include "turns/turns-enums.h" -/*** END file-header ***/ - -/*** BEGIN file-production ***/ -/* enumerations from "@basename@" */ -#include "turns/@basename@" -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GType -@enum_name@_get_type (void) -{ - static gsize static_g_@type@_type_id; - - if (g_once_init_enter (&static_g_@type@_type_id)) - { - static const G@Type@Value values[] = { -/*** END value-header ***/ - -/*** BEGIN value-production ***/ - { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, -/*** END value-production ***/ - -/*** BEGIN value-tail ***/ - { 0, NULL, NULL } - }; - - GType g_@type@_type_id = - g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); - - g_once_init_leave (&static_g_@type@_type_id, g_@type@_type_id); - } - return static_g_@type@_type_id; -} - -/*** END value-tail ***/ diff --git a/core/src/turns-participant.cpp b/core/src/turns-participant.cpp deleted file mode 100644 index 91f1696..0000000 --- a/core/src/turns-participant.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "turns/turns-participant.h" - -#include "turns/turns-disposition.h" -#include "turns/turns-enums.h" - -#include -#include -#include - -#include -#include -#include - -G_BEGIN_DECLS - -struct _TurnsParticipant -{ - GObject parent_instance; - - gchar * name; - gfloat priority; - TurnsDisposition disposition; -}; - -G_END_DECLS - -namespace -{ - enum struct property - { - Name = 1, - Priority, - Disposition, - N_PROPERTIES, - }; - - auto static constinit properties = std::array(property::N_PROPERTIES)>{}; - - auto get_property(GObject * self, guint id, GValue * value, GParamSpec * specification) -> void - { - auto participant = TURNS_PARTICIPANT(self); - - switch (static_cast(id)) - { - case property::Name: - g_value_set_string(value, participant->name); - return; - case property::Priority: - g_value_set_float(value, participant->priority); - return; - case property::Disposition: - g_value_set_enum(value, static_cast(participant->disposition)); - return; - 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(id)) - { - case property::Name: - g_set_str(&participant->name, g_value_get_string(value)); - return; - case property::Priority: - participant->priority = g_value_get_float(value); - return; - case property::Disposition: - participant->disposition = static_cast(g_value_get_enum(value)); - return; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(self, id, specification); - } - } -} // namespace - -G_BEGIN_DECLS - -G_DEFINE_TYPE(TurnsParticipant, turns_participant, G_TYPE_OBJECT) - -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; - - properties[static_cast(property::Name)] = - g_param_spec_string("name", - "Name", - "The Name of the participant", - "", - static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE)); - - properties[static_cast(property::Priority)] = - g_param_spec_float("priority", - "Priority", - "The turn priority of the participant", - -std::numeric_limits::infinity(), - +std::numeric_limits::infinity(), - 0.0f, - static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE)); - - properties[static_cast(property::Disposition)] = - g_param_spec_enum("disposition", - "Disposition", - "Disposition of the participant toward the players", - turns_disposition_get_type(), - TURNS_DISPOSITION_HOSTILE, - static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE)); - - g_object_class_install_properties(object_class, static_cast(property::N_PROPERTIES), properties.data()); -} - -static void turns_participant_init(TurnsParticipant * self) -{ - (void)self; -} - -TurnsParticipant * turns_participant_new(gchar const * name, gfloat priority, TurnsDisposition disposition) -{ - g_return_val_if_fail(name != nullptr, nullptr); - - return static_cast( - g_object_new(TURNS_TYPE_PARTICIPANT, "name", name, "priority", priority, "disposition", static_cast(disposition), nullptr)); -} - -gchar const * turns_participant_get_name(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), nullptr); - return self->name; -} - -gfloat turns_participant_get_priority(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), 0.0f); - return self->priority; -} - -TurnsDisposition turns_participant_get_disposition(TurnsParticipant const * self) -{ - g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), TurnsDisposition::TURNS_DISPOSITION_NEUTRAL); - return self->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(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(property::Priority)]); -} - -void turns_participant_set_disposition(TurnsParticipant * self, TurnsDisposition 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(property::Disposition)]); -} - -G_END_DECLS diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..0050be3 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,2 @@ +turns-enums.c +turns-enums.h \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..8e507ec --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,88 @@ +# Library + +set(HEADERS + "src/turns-disposition.h" + "src/turns-init.h" + "src/turns-participant.h" +) + +set(SOURCES + "src/turns-init.cpp" + "src/turns-participant.cpp" +) + +add_library("lib" + ${SOURCES} +) + +add_library("turns::lib" ALIAS "lib") + +target_sources("lib" PUBLIC + FILE_SET HEADERS + BASE_DIRS "src" + FILES + ${HEADERS} +) + +target_compile_options("lib" PUBLIC + "$<$:-Wall>" + "$<$:-Wextra>" + "$<$:-Werror>" + "$<$:-pedantic-errors>" +) + +target_include_directories("lib" PUBLIC + "src" +) + +target_link_libraries("lib" PUBLIC + "PkgConfig::gobject" +) + +target_compile_definitions("lib" PUBLIC + "$<$>:TURNS_SETTINGS_SCHEMA_DIR=\"${CMAKE_CURRENT_BINARY_DIR}\">" +) + +set_target_properties("lib" PROPERTIES + OUTPUT_NAME "turns" +) + +target_add_glib_enums("lib" + HEADER_TEMPLATE "src/turns-enums.h.in" + SOURCE_TEMPLATE "src/turns-enums.c.in" + OUTPUT_NAME "turns-enums" + HEADERS ${HEADERS} +) + +enable_coverage("lib") + +install(TARGETS "lib" + FILE_SET HEADERS +) + +# Tests + +add_executable("lib-tests" + "tests/runtime_init.cpp" + "tests/turns-participant.cpp" +) + +target_link_libraries("lib-tests" PRIVATE + "Catch2::Catch2WithMain" + + "turns::lib" +) + +target_link_options("lib-tests" PRIVATE + "$<$,$>:--coverage>" +) + +# file(GLOB_RECURSE TEST_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/tests" CONFIGURE_DEPENDS "*.trns") + +# target_add_glib_resources("core-tests" +# PREFIX "/ch/arknet/Turns/core-tests/" +# WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests" +# CSS_FILES ${TEST_FILES} +# ) + +catch_discover_tests("lib-tests") \ No newline at end of file diff --git a/lib/src/turns-disposition.h b/lib/src/turns-disposition.h new file mode 100644 index 0000000..c5bb174 --- /dev/null +++ b/lib/src/turns-disposition.h @@ -0,0 +1,18 @@ +#ifndef TURNS_DISPOSITION_H +#define TURNS_DISPOSITION_H + +#include + +G_BEGIN_DECLS + +typedef enum +{ + TURNS_DISPOSITION_NEUTRAL, + TURNS_DISPOSITION_FRIENDLY, + TURNS_DISPOSITION_HOSTILE, + TURNS_DISPOSITION_SECRET, +} TurnsDisposition; + +G_END_DECLS + +#endif diff --git a/lib/src/turns-enums.c.in b/lib/src/turns-enums.c.in new file mode 100644 index 0000000..2f75d77 --- /dev/null +++ b/lib/src/turns-enums.c.in @@ -0,0 +1,37 @@ +/*** BEGIN file-header ***/ +#include "turns-enums.h" +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@basename@" */ +#include "@basename@" +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static gsize static_g_@type@_type_id; + + if (g_once_init_enter (&static_g_@type@_type_id)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + + GType g_@type@_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + + g_once_init_leave (&static_g_@type@_type_id, g_@type@_type_id); + } + return static_g_@type@_type_id; +} + +/*** END value-tail ***/ diff --git a/lib/src/turns-enums.h.in b/lib/src/turns-enums.h.in new file mode 100644 index 0000000..de93e31 --- /dev/null +++ b/lib/src/turns-enums.h.in @@ -0,0 +1,25 @@ +/*** BEGIN file-header ***/ +#ifndef TURNS_ENUMS_H +#define TURNS_ENUMS_H + +#include +#include + +G_BEGIN_DECLS +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@basename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif +/*** END file-tail ***/ diff --git a/lib/src/turns-init.cpp b/lib/src/turns-init.cpp new file mode 100644 index 0000000..544dc14 --- /dev/null +++ b/lib/src/turns-init.cpp @@ -0,0 +1,17 @@ +#include "turns-init.h" + +#include "turns-enums.h" +#include "turns-participant.h" + +#include +#include + +G_BEGIN_DECLS + +auto turns_init() -> void +{ + g_type_ensure(TURNS_TYPE_PARTICIPANT); + g_type_ensure(TURNS_TYPE_DISPOSITION); +} + +G_END_DECLS \ No newline at end of file diff --git a/lib/src/turns-init.h b/lib/src/turns-init.h new file mode 100644 index 0000000..a6e2013 --- /dev/null +++ b/lib/src/turns-init.h @@ -0,0 +1,12 @@ +#ifndef TURNS_INIT_H +#define TURNS_INIT_H + +#include + +G_BEGIN_DECLS + +void turns_init(void); + +G_END_DECLS + +#endif diff --git a/lib/src/turns-participant.cpp b/lib/src/turns-participant.cpp new file mode 100644 index 0000000..a820f3f --- /dev/null +++ b/lib/src/turns-participant.cpp @@ -0,0 +1,202 @@ +#include "turns-participant.h" + +#include "turns-disposition.h" +#include "turns-enums.h" + +#include +#include +#include + +#include +#include +#include + +G_BEGIN_DECLS + +struct _TurnsParticipant +{ + GObject parent_instance; + + gchar * name; + gfloat priority; + TurnsDisposition disposition; +}; + +G_DEFINE_TYPE(TurnsParticipant, turns_participant, G_TYPE_OBJECT) + +G_END_DECLS + +namespace +{ + enum struct property + { + Name = 1, + Priority, + Disposition, + N_PROPERTIES, + }; + + auto static constinit properties = std::array(property::N_PROPERTIES)>{}; + + auto finalize(GObject * self) + { + auto participant = TURNS_PARTICIPANT(self); + + 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(id)) + { + case property::Name: + g_value_set_string(value, turns_participant_get_name(participant)); + return; + case property::Priority: + g_value_set_float(value, turns_participant_get_priority(participant)); + return; + case property::Disposition: + g_value_set_enum(value, turns_participant_get_disposition(participant)); + return; + 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(id)) + { + 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(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(property::Name)] = + g_param_spec_string("name", + "Name", + "The Name of the participant", + "", + static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + + properties[static_cast(property::Priority)] = + g_param_spec_float("priority", + "Priority", + "The turn priority of the participant", + -std::numeric_limits::infinity(), + +std::numeric_limits::infinity(), + 0.0f, + static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + + properties[static_cast(property::Disposition)] = + g_param_spec_enum("disposition", + "Disposition", + "Disposition of the participant toward the players", + turns_disposition_get_type(), + TURNS_DISPOSITION_NEUTRAL, + static_cast(G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_properties(object_class, static_cast(property::N_PROPERTIES), properties.data()); +} + +static void turns_participant_init(TurnsParticipant * self) +{ + (void)self; +} + +TurnsParticipant * turns_participant_new() +{ + return static_cast(g_object_new(TURNS_TYPE_PARTICIPANT, nullptr)); +} + +TurnsParticipant * turns_participant_new_with(gchar const * name, gfloat priority, TurnsDisposition disposition) +{ + g_return_val_if_fail(name != nullptr, nullptr); + + return static_cast( + g_object_new(TURNS_TYPE_PARTICIPANT, "name", name, "priority", priority, "disposition", static_cast(disposition), nullptr)); +} + +gchar const * turns_participant_get_name(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), nullptr); + return self->name; +} + +gfloat turns_participant_get_priority(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), 0.0f); + return self->priority; +} + +TurnsDisposition turns_participant_get_disposition(TurnsParticipant const * self) +{ + g_return_val_if_fail(TURNS_IS_PARTICIPANT(const_cast(self)), TurnsDisposition::TURNS_DISPOSITION_NEUTRAL); + return self->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(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(property::Priority)]); +} + +void turns_participant_set_disposition(TurnsParticipant * self, TurnsDisposition 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(property::Disposition)]); +} + +G_END_DECLS diff --git a/lib/src/turns-participant.h b/lib/src/turns-participant.h new file mode 100644 index 0000000..502de32 --- /dev/null +++ b/lib/src/turns-participant.h @@ -0,0 +1,84 @@ +#ifndef TURNS_PARTICIPANT_H +#define TURNS_PARTICIPANT_H + +#include "turns-disposition.h" + +#include +#include +#include + +G_BEGIN_DECLS + +#define TURNS_TYPE_PARTICIPANT turns_participant_get_type() +G_DECLARE_FINAL_TYPE(TurnsParticipant, turns_participant, TURNS, PARTICIPANT, GObject) + +/** + * @brief Construct a new Participant. + * + * This functions constructs a default initialized instance, meaning: + * - @p name is the empty string + * - @p priority is 0.0f + * - @p disposition is Disposition.Neutral. + */ +TurnsParticipant * turns_participant_new() G_GNUC_WARN_UNUSED_RESULT; + +/** + * @brief Construct a new Participant with the given values. + * + * @param name The name of the new instance. The value *must not* be NULL. + * @param priority The priority of the new instance. + * @param disposition The disposition of the new instance. + */ +TurnsParticipant * turns_participant_new_with(gchar const * name, gfloat priority, TurnsDisposition disposition) G_GNUC_WARN_UNUSED_RESULT; + +/** + * @brief Get the name of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @return The name of the instance. The data is owned by the instance. + */ +gchar const * turns_participant_get_name(TurnsParticipant const * self); + +/** + * @brief Get the priority of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @return The priority of the instance. + */ +gfloat turns_participant_get_priority(TurnsParticipant const * self); + +/** + * @brief Get the disposition of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @return The disposition of the instance. + */ +TurnsDisposition turns_participant_get_disposition(TurnsParticipant const * self); + +/** + * @brief Set the name of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @param value The new name. The data is owned by the caller of the method. + */ +void turns_participant_set_name(TurnsParticipant * self, gchar const * value); + +/** + * @brief Set the priority of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @param value The new priority. + */ +void turns_participant_set_priority(TurnsParticipant * self, gfloat value); + +/** + * @brief Set the disposition of a participant. + * + * @param self A Participant instance. The value *must not* be NULL. + * @param value The new disposition. + */ +void turns_participant_set_disposition(TurnsParticipant * self, TurnsDisposition value); + +G_END_DECLS + +#endif \ No newline at end of file diff --git a/lib/tests/runtime_init.cpp b/lib/tests/runtime_init.cpp new file mode 100644 index 0000000..189ef35 --- /dev/null +++ b/lib/tests/runtime_init.cpp @@ -0,0 +1,21 @@ +#include "turns-init.h" + +#include +#include + +namespace turns::core::tests +{ + + struct glib_test_init : Catch::EventListenerBase + { + using Catch::EventListenerBase::EventListenerBase; + + auto testRunStarting(Catch::TestRunInfo const &) -> void override + { + turns_init(); + } + }; + + CATCH_REGISTER_LISTENER(glib_test_init); + +} // namespace turns::core::tests \ No newline at end of file diff --git a/lib/tests/turns-participant.cpp b/lib/tests/turns-participant.cpp new file mode 100644 index 0000000..89e60cf --- /dev/null +++ b/lib/tests/turns-participant.cpp @@ -0,0 +1,353 @@ +#include "turns-participant.h" + +#include "turns-disposition.h" + +#include +#include +#include + +#include +#include + +#include + +namespace +{ + auto record_notification(TurnsParticipant const *, GParamSpec *, bool * out) -> void + { + *out = true; + } +} // namespace + +SCENARIO("Creating a participant", "[lib][object][lifetime]") +{ + GIVEN("A participant constructed using turns_participant_new()") + { + g_autoptr(TurnsParticipant) instance = turns_participant_new(); + + THEN("it's name is empty") + { + auto name = turns_participant_get_name(instance); + REQUIRE(name); + REQUIRE(std::string{name}.empty()); + } + + THEN("it's priority is 0.0f") + { + REQUIRE(turns_participant_get_priority(instance) == Catch::Approx{0.0}); + } + + THEN("it's disposition is neutral") + { + REQUIRE(turns_participant_get_disposition(instance) == TURNS_DISPOSITION_NEUTRAL); + } + } + + GIVEN("A participant constructed using turns_participant_new_with(...)") + { + auto name = "Test Participant"; + auto priority = 12.4f; + auto disposition = TURNS_DISPOSITION_FRIENDLY; + g_autoptr(TurnsParticipant) instance = turns_participant_new_with(name, priority, disposition); + + THEN("it's name is set") + { + REQUIRE(turns_participant_get_name(instance) == std::string{name}); + } + + THEN("it's priority is set") + { + REQUIRE(turns_participant_get_priority(instance) == Catch::Approx{priority}); + } + + THEN("it's disposition is") + { + REQUIRE(turns_participant_get_disposition(instance) == disposition); + } + } + + GIVEN("A participant is constructed using turns_participant_new_with(nullptr, ...)") + { + auto instance = turns_participant_new_with(nullptr, 10.0f, TURNS_DISPOSITION_HOSTILE); + + THEN("the instance is NULL") + { + REQUIRE(instance == nullptr); + } + } +} + +SCENARIO("Modifying a participant", "[lib][object][data]") +{ + GIVEN("A default participant instance") + { + g_autoptr(TurnsParticipant) instance = turns_participant_new(); + + WHEN("a new new is set") + { + auto new_value = "Test Participant"; + + CHECK(std::string{turns_participant_get_name(instance)}.empty()); + CHECK_FALSE(std::string{turns_participant_get_name(instance)} == new_value); + turns_participant_set_name(instance, new_value); + + THEN("it's name is not empty") + { + REQUIRE_FALSE(std::string{turns_participant_get_name(instance)}.empty()); + } + + THEN("it's name has the new value") + { + REQUIRE(std::string{turns_participant_get_name(instance)} == new_value); + } + } + + WHEN("a new priority is set") + { + auto new_value = GENERATE(-8.0f, 12.0f, 4.0f); + + CHECK_FALSE(turns_participant_get_priority(instance) == new_value); + turns_participant_set_priority(instance, new_value); + + THEN("it's priority has the new value") + { + REQUIRE(turns_participant_get_priority(instance) == Catch::Approx{new_value}); + } + } + + WHEN("a new disposition is set") + { + auto new_value = GENERATE(TURNS_DISPOSITION_FRIENDLY, TURNS_DISPOSITION_HOSTILE, TURNS_DISPOSITION_SECRET); + + CHECK_FALSE(turns_participant_get_disposition(instance) == new_value); + turns_participant_set_disposition(instance, new_value); + + THEN("it's disposition has the new value") + { + REQUIRE(turns_participant_get_disposition(instance) == new_value); + } + } + + AND_GIVEN("a signal handler has been subscribed to the name property") + { + auto was_notified = false; + g_signal_connect(instance, "notify::name", G_CALLBACK(&record_notification), &was_notified); + + WHEN("a new name is set using set_name") + { + auto new_value = "Test Participant"; + + CHECK_FALSE(std::string{turns_participant_get_name(instance)} == new_value); + turns_participant_set_name(instance, new_value); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same name is set using set_name") + { + auto new_value = turns_participant_get_name(instance); + + CHECK(std::string{turns_participant_get_name(instance)} == new_value); + turns_participant_set_name(instance, new_value); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + + WHEN("a new name is set using g_object_set") + { + auto new_value = "Test Participant"; + + CHECK_FALSE(std::string{turns_participant_get_name(instance)} == new_value); + g_object_set(G_OBJECT(instance), "name", new_value, nullptr); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same name is set using g_object_set") + { + auto new_value = turns_participant_get_name(instance); + + CHECK(std::string{turns_participant_get_name(instance)} == new_value); + g_object_set(G_OBJECT(instance), "name", new_value, nullptr); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + } + + AND_GIVEN("a signal handler has been subscribed to the priority property") + { + auto was_notified = false; + g_signal_connect(instance, "notify::priority", G_CALLBACK(&record_notification), &was_notified); + + WHEN("a new priority is set using set_priority") + { + auto new_value = 17.15; + + CHECK_FALSE(turns_participant_get_priority(instance) == new_value); + turns_participant_set_priority(instance, new_value); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same priority is set using set_priority") + { + auto new_value = turns_participant_get_priority(instance); + + CHECK(turns_participant_get_priority(instance) == new_value); + turns_participant_set_priority(instance, new_value); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + + WHEN("a new priority is set using g_object_set") + { + auto new_value = 17.15; + + CHECK_FALSE(turns_participant_get_priority(instance) == new_value); + g_object_set(G_OBJECT(instance), "priority", new_value, nullptr); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same priority is set using g_object_set") + { + auto new_value = turns_participant_get_priority(instance); + + CHECK(turns_participant_get_priority(instance) == new_value); + g_object_set(G_OBJECT(instance), "priority", new_value, nullptr); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + } + + AND_GIVEN("a signal handler has been subscribed to the disposition property") + { + auto was_notified = false; + g_signal_connect(instance, "notify::disposition", G_CALLBACK(&record_notification), &was_notified); + + WHEN("a new disposition is set using set_disposition") + { + auto new_value = TURNS_DISPOSITION_HOSTILE; + + CHECK_FALSE(turns_participant_get_disposition(instance) == new_value); + turns_participant_set_disposition(instance, new_value); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same disposition is set using set_disposition") + { + auto new_value = turns_participant_get_disposition(instance); + + CHECK(turns_participant_get_disposition(instance) == new_value); + turns_participant_set_disposition(instance, new_value); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + + WHEN("a new disposition is set using g_object_set") + { + auto new_value = TURNS_DISPOSITION_HOSTILE; + + CHECK_FALSE(turns_participant_get_disposition(instance) == new_value); + g_object_set(G_OBJECT(instance), "disposition", new_value, nullptr); + + THEN("a notification is issued") + { + REQUIRE(was_notified); + } + } + + WHEN("the same disposition is set using g_object_set") + { + auto new_value = turns_participant_get_disposition(instance); + + CHECK(turns_participant_get_disposition(instance) == new_value); + g_object_set(G_OBJECT(instance), "disposition", new_value, nullptr); + + THEN("no notification is issued") + { + REQUIRE_FALSE(was_notified); + } + } + } + } +} + +SCENARIO("Reading a participant", "[lib][object][data]") +{ + GIVEN("A participant with a set name, priority, and disposition") + { + auto name = "Test Participant"; + auto priority = 75.f; + auto disposition = TURNS_DISPOSITION_FRIENDLY; + + g_autoptr(TurnsParticipant) instance = turns_participant_new_with(name, priority, disposition); + + WHEN("reading the name via get_name and g_object_get") + { + auto getter_value = turns_participant_get_name(instance); + g_autofree auto property_value = decltype(turns_participant_get_name(instance)){}; + g_object_get(instance, "name", &property_value, nullptr); + + THEN("they both compare equal") + { + REQUIRE(std::string{getter_value} == property_value); + } + } + + WHEN("reading the priority via get_priority and g_object_get") + { + auto getter_value = turns_participant_get_priority(instance); + auto property_value = decltype(turns_participant_get_priority(instance)){}; + g_object_get(instance, "priority", &property_value, nullptr); + + THEN("they both compare equal") + { + REQUIRE(getter_value == property_value); + } + } + + WHEN("reading the disposition via get_disposition and g_object_get") + { + auto getter_value = turns_participant_get_disposition(instance); + auto property_value = decltype(turns_participant_get_disposition(instance)){}; + g_object_get(instance, "disposition", &property_value, nullptr); + + THEN("they both compare equal") + { + REQUIRE(getter_value == property_value); + } + } + } +} \ No newline at end of file -- cgit v1.2.3