aboutsummaryrefslogtreecommitdiff
path: root/extension/src/openvic-extension
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2023-12-19 00:38:54 +0100
committer hop311 <hop3114@gmail.com>2023-12-25 19:06:13 +0100
commit4e9764ee29fb7b453862835d5aa3a081b0f9a269 (patch)
treea59c5b960a706a383b8ebd1dbcfb704067a5b51b /extension/src/openvic-extension
parentd26c990d9a5596a3ef3b32ba1cb0f99950cd6d34 (diff)
Back to UI Work
- UIAdapter -> UITools with cleaner API - GUIOverlappingElementsBox (for core and modifier icons) - Improved GUINode API - Province building slots - TypeHints for files in the GameSession folder - Incorporate SIM strong colour types
Diffstat (limited to 'extension/src/openvic-extension')
-rw-r--r--extension/src/openvic-extension/UIAdapter.hpp28
-rw-r--r--extension/src/openvic-extension/classes/GFXIconTexture.cpp9
-rw-r--r--extension/src/openvic-extension/classes/GFXIconTexture.hpp4
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp31
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp11
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.cpp40
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.hpp41
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp173
-rw-r--r--extension/src/openvic-extension/classes/GUINode.hpp50
-rw-r--r--extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp177
-rw-r--r--extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp51
-rw-r--r--extension/src/openvic-extension/register_types.cpp2
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.cpp45
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.hpp11
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.cpp135
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.hpp6
-rw-r--r--extension/src/openvic-extension/utility/UITools.cpp (renamed from extension/src/openvic-extension/UIAdapter.cpp)223
-rw-r--r--extension/src/openvic-extension/utility/UITools.hpp18
-rw-r--r--extension/src/openvic-extension/utility/Utilities.cpp15
-rw-r--r--extension/src/openvic-extension/utility/Utilities.hpp20
20 files changed, 687 insertions, 403 deletions
diff --git a/extension/src/openvic-extension/UIAdapter.hpp b/extension/src/openvic-extension/UIAdapter.hpp
deleted file mode 100644
index 258b5e9..0000000
--- a/extension/src/openvic-extension/UIAdapter.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <godot_cpp/classes/control.hpp>
-
-#include <openvic-simulation/interface/GUI.hpp>
-
-#include "openvic-extension/singletons/AssetManager.hpp"
-
-namespace OpenVic::GodotGUIBuilder {
-
- bool generate_element(
- GUI::Element const* element, godot::String const& name, AssetManager& asset_manager, godot::Control*& result
- );
-
-#define GEN_GUI_ARGS \
- GUI::Element const& element, godot::String const& name, AssetManager& asset_manager, godot::Control*& result
-
- bool generate_icon(GEN_GUI_ARGS);
- bool generate_button(GEN_GUI_ARGS);
- bool generate_checkbox(GEN_GUI_ARGS);
- bool generate_text(GEN_GUI_ARGS);
- bool generate_overlapping_elements(GEN_GUI_ARGS);
- bool generate_listbox(GEN_GUI_ARGS);
- bool generate_window(GEN_GUI_ARGS);
-
-#undef GEN_GUI_ARGS
-
-}
diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.cpp b/extension/src/openvic-extension/classes/GFXIconTexture.cpp
index 57c5d50..895bf6b 100644
--- a/extension/src/openvic-extension/classes/GFXIconTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXIconTexture.cpp
@@ -5,6 +5,7 @@
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/UITools.hpp"
#include "openvic-extension/utility/Utilities.hpp"
using namespace godot;
@@ -71,12 +72,8 @@ Error GFXIconTexture::set_gfx_texture_sprite_name(String const& gfx_texture_spri
if (gfx_texture_sprite_name.is_empty()) {
return set_gfx_texture_sprite(nullptr);
}
- GameSingleton* game_singleton = GameSingleton::get_singleton();
- ERR_FAIL_NULL_V(game_singleton, FAILED);
- GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
- godot_to_std_string(gfx_texture_sprite_name)
- );
- ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_texture_sprite_name));
+ GFX::Sprite const* sprite = UITools::get_gfx_sprite(gfx_texture_sprite_name);
+ ERR_FAIL_NULL_V(sprite, FAILED);
GFX::TextureSprite const* new_texture_sprite = sprite->cast_to<GFX::TextureSprite>();
ERR_FAIL_NULL_V_MSG(
new_texture_sprite, FAILED, vformat(
diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.hpp b/extension/src/openvic-extension/classes/GFXIconTexture.hpp
index 3ed5b3e..176d855 100644
--- a/extension/src/openvic-extension/classes/GFXIconTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXIconTexture.hpp
@@ -35,12 +35,12 @@ namespace OpenVic {
);
/* Search for a GFX::TextureSprite with the specfied name and,
- * if successful, call set_gfx_texture_sprite to set it and its icon */
+ * if successful, call set_gfx_texture_sprite to set it and its icon. */
godot::Error set_gfx_texture_sprite_name(
godot::String const& gfx_texture_sprite_name, GFX::frame_t icon = GFX::NO_FRAMES
);
- /* Return the name of the GFX::TextureSprite, or an empty String if it's null */
+ /* Return the name of the GFX::TextureSprite, or an empty String if it's null. */
godot::String get_gfx_texture_sprite_name() const;
/* Set icon_index to a value between one and icon_count (inclusive), and update the AtlasTexture's region
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
index 3636855..0ed7755 100644
--- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
@@ -1,10 +1,9 @@
#include "GFXMaskedFlagTexture.hpp"
-#include <godot_cpp/variant/utility_functions.hpp>
-
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/UITools.hpp"
#include "openvic-extension/utility/Utilities.hpp"
using namespace godot;
@@ -66,6 +65,7 @@ void GFXMaskedFlagTexture::_bind_methods() {
OV_BIND_METHOD(GFXMaskedFlagTexture::get_gfx_masked_flag_name);
OV_BIND_METHOD(GFXMaskedFlagTexture::set_flag_country_name_and_type, { "new_flag_country_name", "new_flag_type" });
+ OV_BIND_METHOD(GFXMaskedFlagTexture::set_flag_country_name, { "new_flag_country_name" });
OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_country_name);
OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_type);
}
@@ -123,12 +123,8 @@ Error GFXMaskedFlagTexture::set_gfx_masked_flag_name(String const& gfx_masked_fl
if (gfx_masked_flag_name.is_empty()) {
return set_gfx_masked_flag(nullptr);
}
- GameSingleton* game_singleton = GameSingleton::get_singleton();
- ERR_FAIL_NULL_V(game_singleton, FAILED);
- GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
- godot_to_std_string(gfx_masked_flag_name)
- );
- ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_masked_flag_name));
+ GFX::Sprite const* sprite = UITools::get_gfx_sprite(gfx_masked_flag_name);
+ ERR_FAIL_NULL_V(sprite, FAILED);
GFX::MaskedFlag const* new_masked_flag = sprite->cast_to<GFX::MaskedFlag>();
ERR_FAIL_NULL_V_MSG(
new_masked_flag, FAILED, vformat(
@@ -158,6 +154,7 @@ Error GFXMaskedFlagTexture::set_flag_country_and_type(Country const* new_flag_co
flag_type = new_flag_type;
flag_image = new_flag_image;
} else {
+ // TODO - use REB flag as default/error flag
flag_country = nullptr;
flag_type = String {};
flag_image.unref();
@@ -178,6 +175,24 @@ Error GFXMaskedFlagTexture::set_flag_country_name_and_type(String const& new_fla
return set_flag_country_and_type(new_flag_country, new_flag_type);
}
+Error GFXMaskedFlagTexture::set_flag_country(Country const* new_flag_country) {
+ // TODO - get country's current flag type from the game state
+ return set_flag_country_and_type( new_flag_country, {});
+}
+
+Error GFXMaskedFlagTexture::set_flag_country_name(String const& new_flag_country_name) {
+ if (new_flag_country_name.is_empty()) {
+ return set_flag_country(nullptr);
+ }
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+ Country const* new_flag_country = game_singleton->get_game_manager().get_country_manager().get_country_by_identifier(
+ godot_to_std_string(new_flag_country_name)
+ );
+ ERR_FAIL_NULL_V_MSG(new_flag_country, FAILED, vformat("Country not found: %s", new_flag_country_name));
+ return set_flag_country(new_flag_country);
+}
+
String GFXMaskedFlagTexture::get_flag_country_name() const {
return flag_country != nullptr ? std_view_to_godot_string(flag_country->get_identifier()) : String {};
}
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
index be3b15a..f71a1d7 100644
--- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
@@ -37,7 +37,7 @@ namespace OpenVic {
/* Search for a GFX::MaskedFlag with the specfied name and, if successful, set it using set_gfx_masked_flag. */
godot::Error set_gfx_masked_flag_name(godot::String const& gfx_masked_flag_name);
- /* Return the name of the GFX::MaskedFlag, or an empty String if it's null */
+ /* Return the name of the GFX::MaskedFlag, or an empty String if it's null. */
godot::String get_gfx_masked_flag_name() const;
/* Set flag_country and flag_type and update the combined image to use that flag, or no flag if it doesn't exist. */
@@ -49,7 +49,14 @@ namespace OpenVic {
godot::String const& new_flag_country_name, godot::StringName const& new_flag_type
);
- /* Return the name of the selected flag's country, or an empty String if it's null */
+ /* Look up the specified country's current flag type, then call set_flag_country_and_type
+ * with the country and its flag type as arguments. */
+ godot::Error set_flag_country(Country const* new_flag_country);
+
+ /* Look up the country with the specified identifier, then call set_flag_country with the country its argument. */
+ godot::Error set_flag_country_name(godot::String const& new_flag_country_name);
+
+ /* Return the name of the selected flag's country, or an empty String if it's null. */
godot::String get_flag_country_name() const;
};
}
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
index 6fe2fe0..e1bffc5 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
@@ -1,11 +1,9 @@
#include "GFXPieChartTexture.hpp"
-#include <godot_cpp/variant/utility_functions.hpp>
-
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
-#include "openvic-extension/utility/Utilities.hpp"
+#include "openvic-extension/utility/UITools.hpp"
using namespace godot;
using namespace OpenVic;
@@ -14,14 +12,14 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
-#define PI std::numbers::pi_v<float>
+static constexpr float PI = std::numbers::pi_v<float>;
Error GFXPieChartTexture::_generate_pie_chart_image() {
ERR_FAIL_NULL_V(gfx_pie_chart, FAILED);
- if (gfx_pie_chart->get_size() <= 0) {
- UtilityFunctions::push_error("Invalid GFX::PieChart size for GFXPieChartTexture - ", gfx_pie_chart->get_size());
- return FAILED;
- }
+ ERR_FAIL_COND_V_MSG(
+ gfx_pie_chart->get_size() <= 0, FAILED,
+ vformat("Invalid GFX::PieChart size for GFXPieChartTexture - %d", gfx_pie_chart->get_size())
+ );
const int32_t pie_chart_size = 2 * gfx_pie_chart->get_size();
bool can_update = true;
if (
@@ -75,7 +73,7 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
return OK;
}
-Error GFXPieChartTexture::set_slices(Array const& new_slices) {
+Error GFXPieChartTexture::set_slices_array(TypedArray<Dictionary> const& new_slices) {
static const StringName colour_key = "colour";
static const StringName weight_key = "weight";
@@ -83,15 +81,11 @@ Error GFXPieChartTexture::set_slices(Array const& new_slices) {
total_weight = 0.0f;
for (int32_t i = 0; i < new_slices.size(); ++i) {
Dictionary const& slice_dict = new_slices[i];
- if (!slice_dict.has(colour_key) || !slice_dict.has(weight_key)) {
- UtilityFunctions::push_error("Invalid slice keys at index ", i, " - ", slice_dict);
- continue;
- }
+ ERR_CONTINUE_MSG(
+ !slice_dict.has(colour_key) || !slice_dict.has(weight_key), vformat("Invalid slice keys at index %d", i)
+ );
const slice_t slice = std::make_pair(slice_dict[colour_key], slice_dict[weight_key]);
- if (slice.second <= 0.0f) {
- UtilityFunctions::push_error("Invalid slice weight at index ", i, " - ", slice.second);
- continue;
- }
+ ERR_CONTINUE_MSG(slice.second <= 0.0f, vformat("Invalid slice values at index %d", i));
total_weight += slice.second;
slices.emplace_back(std::move(slice));
}
@@ -104,10 +98,10 @@ void GFXPieChartTexture::_bind_methods() {
OV_BIND_METHOD(GFXPieChartTexture::set_gfx_pie_chart_name, { "gfx_pie_chart_name" });
OV_BIND_METHOD(GFXPieChartTexture::get_gfx_pie_chart_name);
- OV_BIND_METHOD(GFXPieChartTexture::set_slices, { "new_slices" });
+ OV_BIND_METHOD(GFXPieChartTexture::set_slices_array, { "new_slices" });
}
-GFXPieChartTexture::GFXPieChartTexture() : total_weight { 0.0f } {}
+GFXPieChartTexture::GFXPieChartTexture() : gfx_pie_chart { nullptr }, total_weight { 0.0f } {}
Ref<GFXPieChartTexture> GFXPieChartTexture::make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart) {
Ref<GFXPieChartTexture> pie_chart_texture;
@@ -146,12 +140,8 @@ Error GFXPieChartTexture::set_gfx_pie_chart_name(String const& gfx_pie_chart_nam
if (gfx_pie_chart_name.is_empty()) {
return set_gfx_pie_chart(nullptr);
}
- GameSingleton* game_singleton = GameSingleton::get_singleton();
- ERR_FAIL_NULL_V(game_singleton, FAILED);
- GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
- godot_to_std_string(gfx_pie_chart_name)
- );
- ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_pie_chart_name));
+ GFX::Sprite const* sprite = UITools::get_gfx_sprite(gfx_pie_chart_name);
+ ERR_FAIL_NULL_V(sprite, FAILED);
GFX::PieChart const* new_pie_chart = sprite->cast_to<GFX::PieChart>();
ERR_FAIL_NULL_V_MSG(
new_pie_chart, FAILED, vformat(
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
index 315b00e..ad8e751 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
@@ -4,6 +4,8 @@
#include <openvic-simulation/interface/GFX.hpp>
+#include "openvic-extension/utility/Utilities.hpp"
+
namespace OpenVic {
class GFXPieChartTexture : public godot::ImageTexture {
GDCLASS(GFXPieChartTexture, godot::ImageTexture)
@@ -23,11 +25,40 @@ namespace OpenVic {
public:
GFXPieChartTexture();
- /* Set slices given new_slices, an Array of Dictionaries, each with the following keys:
+ /* Set slices given an Array of Dictionaries, each with the following key-value entries:
* - colour: Color
- * - weight: float
- */
- godot::Error set_slices(godot::Array const& new_slices);
+ * - weight: float */
+ godot::Error set_slices_array(godot::TypedArray<godot::Dictionary> const& new_slices);
+
+ /* Generate slice data from a distribution of HasIdentifierAndColour derived objects, sorted by their weight.
+ * The resulting Array of Dictionaries can be used as an argument for set_slices_array. */
+ template<std::derived_from<HasIdentifierAndColour> T>
+ static godot::TypedArray<godot::Dictionary> distribution_to_slices_array(fixed_point_map_t<T const*> const& dist) {
+ using entry_t = std::pair<T const*, fixed_point_t>;
+ std::vector<entry_t> sorted_dist;
+ sorted_dist.reserve(dist.size());
+ for (entry_t const& entry : dist) {
+ ERR_CONTINUE_MSG(
+ entry.first == nullptr, godot::vformat("Null distribution key with value %f", entry.second.to_float())
+ );
+ sorted_dist.push_back(entry);
+ }
+ std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool {
+ return lhs.second < rhs.second;
+ });
+ static const godot::StringName identifier_key = "identifier";
+ static const godot::StringName colour_key = "colour";
+ static const godot::StringName weight_key = "weight";
+ godot::TypedArray<godot::Dictionary> array;
+ for (auto const& [key, val] : sorted_dist) {
+ godot::Dictionary sub_dict;
+ sub_dict[identifier_key] = Utilities::std_view_to_godot_string(key->get_identifier());
+ sub_dict[colour_key] = Utilities::to_godot_color(key->get_colour());
+ sub_dict[weight_key] = val.to_float();
+ array.push_back(sub_dict);
+ }
+ return array;
+ }
/* Create a GFXPieChartTexture using the specific GFX::PieChart.
* Returns nullptr if setting gfx_pie_chart fails. */
@@ -43,7 +74,7 @@ namespace OpenVic {
/* Search for a GFX::PieChart with the specfied name and, if successful, set it using set_gfx_pie_chart. */
godot::Error set_gfx_pie_chart_name(godot::String const& gfx_pie_chart_name);
- /* Return the name of the GFX::PieChart, or an empty String if it's null */
+ /* Return the name of the GFX::PieChart, or an empty String if it's null. */
godot::String get_gfx_pie_chart_name() const;
};
}
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
index 1d55c54..043f65d 100644
--- a/extension/src/openvic-extension/classes/GUINode.cpp
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -3,10 +3,8 @@
#include <godot_cpp/classes/style_box_texture.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
-#include "openvic-extension/UIAdapter.hpp"
-#include "openvic-extension/singletons/AssetManager.hpp"
-#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/UITools.hpp"
#include "openvic-extension/utility/Utilities.hpp"
using namespace godot;
@@ -15,33 +13,58 @@ using namespace OpenVic;
using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
+#define APPLY_TO_CHILD_TYPES(F) \
+ F(Button, button) \
+ F(CheckBox, check_box) \
+ F(Label, label) \
+ F(Panel, panel) \
+ F(TextureProgressBar, progress_bar) \
+ F(TextureRect, texture_rect) \
+ F(GUIOverlappingElementsBox, gui_overlapping_elements_box)
+
+#define APPLY_TO_TEXTURE_TYPES(F) \
+ F(GFXIconTexture, gfx_icon_texture) \
+ F(GFXMaskedFlagTexture, gfx_masked_flag_texture) \
+ F(GFXPieChartTexture, gfx_pie_chart_texture)
+
void GUINode::_bind_methods() {
+ OV_BIND_SMETHOD(generate_gui_element, { "gui_file", "gui_element", "name" }, DEFVAL(String {}));
OV_BIND_METHOD(GUINode::add_gui_element, { "gui_file", "gui_element", "name" }, DEFVAL(String {}));
- OV_BIND_METHOD(GUINode::get_button_node, { "path" });
- OV_BIND_METHOD(GUINode::get_check_box_node, { "path" });
- OV_BIND_METHOD(GUINode::get_label_node, { "path" });
- OV_BIND_METHOD(GUINode::get_panel_node, { "path" });
- OV_BIND_METHOD(GUINode::get_progress_bar_node, { "path" });
- OV_BIND_METHOD(GUINode::get_texture_rect_node, { "path" });
+#define GET_BINDINGS(type, name) \
+ OV_BIND_SMETHOD(get_##name##_from_node, { "node" }); \
+ OV_BIND_METHOD(GUINode::get_##name##_from_nodepath, { "path" });
+
+ APPLY_TO_CHILD_TYPES(GET_BINDINGS)
+
+ OV_BIND_SMETHOD(get_texture_from_node, { "node" });
+ OV_BIND_METHOD(GUINode::get_texture_from_nodepath, { "path" });
+
+ APPLY_TO_TEXTURE_TYPES(GET_BINDINGS)
- OV_BIND_METHOD(GUINode::get_texture_from_node, { "path" });
- OV_BIND_METHOD(GUINode::get_gfx_icon_texture_from_node, { "path" });
- OV_BIND_METHOD(GUINode::get_gfx_masked_flag_texture_from_node, { "path" });
- OV_BIND_METHOD(GUINode::get_gfx_pie_chart_texture_from_node, { "path" });
+#undef GET_BINDINGS
OV_BIND_METHOD(GUINode::hide_node, { "path" });
OV_BIND_METHOD(GUINode::hide_nodes, { "paths" });
}
-Error GUINode::_add_gui_element(GUI::Element const* element, String const& name) {
- ERR_FAIL_NULL_V(element, FAILED);
- AssetManager* asset_manager = AssetManager::get_singleton();
- ERR_FAIL_NULL_V(asset_manager, FAILED);
+GUINode::GUINode() {
+ set_mouse_filter(MOUSE_FILTER_IGNORE);
+}
+
+Control* GUINode::generate_gui_element(String const& gui_file, String const& gui_element, String const& name) {
+ Control* result = nullptr;
+ if (!UITools::generate_gui_element(gui_file, gui_element, name, result)) {
+ UtilityFunctions::push_error("Error generating GUI element ", gui_element, " from GUI file ", gui_file);
+ }
+ return result;
+}
+
+Error GUINode::add_gui_element(String const& gui_file, String const& gui_element, String const& name) {
Error err = OK;
Control* result = nullptr;
- if (!GodotGUIBuilder::generate_element(element, name, *asset_manager, result)) {
- UtilityFunctions::push_error("Failed to generate GUI element ", std_view_to_godot_string(element->get_name()));
+ if (!UITools::generate_gui_element(gui_file, gui_element, name, result)) {
+ UtilityFunctions::push_error("Error generating GUI element ", gui_element, " from GUI file ", gui_file);
err = FAILED;
}
if (result != nullptr) {
@@ -50,107 +73,95 @@ Error GUINode::_add_gui_element(GUI::Element const* element, String const& name)
return err;
}
-Error GUINode::add_gui_element(String const& gui_file, String const& gui_element, String const& name) {
- GameSingleton const* game_singleton = GameSingleton::get_singleton();
- ERR_FAIL_NULL_V(game_singleton, FAILED);
- GUI::Scene const* scene =
- game_singleton->get_game_manager().get_ui_manager().get_scene_by_identifier(godot_to_std_string(gui_file));
- ERR_FAIL_NULL_V_MSG(scene, FAILED, vformat("Failed to find GUI file %s", gui_file));
- GUI::Element const* element = scene->get_scene_element_by_identifier(godot_to_std_string(gui_element));
- ERR_FAIL_NULL_V_MSG(element, FAILED, vformat("Failed to find GUI element %s in GUI file %s", gui_element, gui_file));
- return _add_gui_element(element, name);
-}
-
template<std::derived_from<godot::Node> T>
-T* GUINode::_get_cast_node(NodePath const& path) const {
- Node* node = get_node_or_null(path);
- ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to find node %s", path));
+static T* _cast_node(Node* node) {
+ ERR_FAIL_NULL_V(node, nullptr);
T* result = Object::cast_to<T>(node);
- ERR_FAIL_NULL_V_MSG(result, nullptr, vformat("Failed to cast node %s to type %s", path, T::get_class_static()));
+ ERR_FAIL_NULL_V_MSG(
+ result, nullptr,
+ vformat("Failed to cast node %s from type %s to %s", node->get_name(), node->get_class(), T::get_class_static())
+ );
return result;
}
-Button* GUINode::get_button_node(NodePath const& path) const {
- return _get_cast_node<Button>(path);
-}
-
-CheckBox* GUINode::get_check_box_node(NodePath const& path) const {
- return _get_cast_node<CheckBox>(path);
-}
-
-Label* GUINode::get_label_node(NodePath const& path) const {
- return _get_cast_node<Label>(path);
-}
-
-Panel* GUINode::get_panel_node(NodePath const& path) const {
- return _get_cast_node<Panel>(path);
-}
+#define CHILD_GET_FUNCTIONS(type, name) \
+ type* GUINode::get_##name##_from_node(Node* node) { \
+ return _cast_node<type>(node); \
+ } \
+ type* GUINode::get_##name##_from_nodepath(NodePath const& path) const { \
+ return _cast_node<type>(get_node_internal(path)); \
+ }
-TextureProgressBar* GUINode::get_progress_bar_node(NodePath const& path) const {
- return _get_cast_node<TextureProgressBar>(path);
-}
+APPLY_TO_CHILD_TYPES(CHILD_GET_FUNCTIONS)
-TextureRect* GUINode::get_texture_rect_node(NodePath const& path) const {
- return _get_cast_node<TextureRect>(path);
-}
+#undef CHILD_GET_FUNCTIONS
-Ref<Texture2D> GUINode::get_texture_from_node(NodePath const& path) const {
- Node* node = get_node_or_null(path);
- ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to find node %s", path));
+Ref<Texture2D> GUINode::get_texture_from_node(Node* node) {
+ ERR_FAIL_NULL_V(node, nullptr);
if (TextureRect const* texture_rect = Object::cast_to<TextureRect>(node); texture_rect != nullptr) {
const Ref<Texture2D> texture = texture_rect->get_texture();
- ERR_FAIL_NULL_V_MSG(texture, nullptr, vformat("Failed to get Texture2D from TextureRect %s", path));
+ ERR_FAIL_NULL_V_MSG(texture, nullptr, vformat("Failed to get Texture2D from TextureRect %s", node->get_name()));
return texture;
} else if (Button const* button = Object::cast_to<Button>(node); button != nullptr) {
static const StringName theme_name_normal = "normal";
const Ref<StyleBox> stylebox = button->get_theme_stylebox(theme_name_normal);
- ERR_FAIL_NULL_V_MSG(stylebox, nullptr, vformat("Failed to get StyleBox %s from Button %s", theme_name_normal, path));
+ ERR_FAIL_NULL_V_MSG(
+ stylebox, nullptr, vformat("Failed to get StyleBox %s from Button %s", theme_name_normal, node->get_name())
+ );
const Ref<StyleBoxTexture> stylebox_texture = stylebox;
ERR_FAIL_NULL_V_MSG(
stylebox_texture, nullptr, vformat(
- "Failed to cast StyleBox %s from Button %s to type StyleBoxTexture", theme_name_normal, path
+ "Failed to cast StyleBox %s from Button %s to type StyleBoxTexture", theme_name_normal, node->get_name()
)
);
const Ref<Texture2D> result = stylebox_texture->get_texture();
ERR_FAIL_NULL_V_MSG(
- result, nullptr, vformat("Failed to get Texture2D from StyleBoxTexture %s from Button %s", theme_name_normal, path)
+ result, nullptr,
+ vformat("Failed to get Texture2D from StyleBoxTexture %s from Button %s", theme_name_normal, node->get_name())
);
return result;
}
- ERR_FAIL_V_MSG(nullptr, vformat("Failed to cast node %s to type TextureRect or Button", path));
+ ERR_FAIL_V_MSG(
+ nullptr, vformat("Failed to cast node %s from type %s to TextureRect or Button", node->get_name(), node->get_class())
+ );
+}
+
+Ref<Texture2D> GUINode::get_texture_from_nodepath(NodePath const& path) const {
+ return get_texture_from_node(get_node_internal(path));
}
-template<std::derived_from<godot::Texture2D> T>
-Ref<T> GUINode::_get_cast_texture_from_node(NodePath const& path) const {
- const Ref<Texture2D> texture = get_texture_from_node(path);
+template<std::derived_from<Texture2D> T>
+static Ref<T> _cast_texture(Ref<Texture2D> const& texture) {
ERR_FAIL_NULL_V(texture, nullptr);
const Ref<T> result = texture;
- ERR_FAIL_NULL_V_MSG(result, nullptr, vformat("Failed to cast Texture2D from %s to type %s", path, T::get_class_static()));
+ ERR_FAIL_NULL_V_MSG(
+ result, nullptr, vformat("Failed to cast Texture2D from type %s to %s", texture->get_class(), T::get_class_static())
+ );
return result;
}
-Ref<GFXIconTexture> GUINode::get_gfx_icon_texture_from_node(NodePath const& path) const {
- return _get_cast_texture_from_node<GFXIconTexture>(path);
-}
+#define TEXTURE_GET_FUNCTIONS(type, name) \
+ Ref<type> GUINode::get_##name##_from_node(Node* node) { \
+ return _cast_texture<type>(get_texture_from_node(node)); \
+ } \
+ Ref<type> GUINode::get_##name##_from_nodepath(NodePath const& path) const { \
+ return _cast_texture<type>(get_texture_from_nodepath(path)); \
+ }
-Ref<GFXMaskedFlagTexture> GUINode::get_gfx_masked_flag_texture_from_node(NodePath const& path) const {
- return _get_cast_texture_from_node<GFXMaskedFlagTexture>(path);
-}
+APPLY_TO_TEXTURE_TYPES(TEXTURE_GET_FUNCTIONS)
-Ref<GFXPieChartTexture> GUINode::get_gfx_pie_chart_texture_from_node(NodePath const& path) const {
- return _get_cast_texture_from_node<GFXPieChartTexture>(path);
-}
+#undef TEXTURE_GET_FUNCTIONS
+
+#undef APPLY_TO_CHILD_TYPES
Error GUINode::hide_node(NodePath const& path) const {
- CanvasItem* node = _get_cast_node<CanvasItem>(path);
- if (node == nullptr) {
- return FAILED;
- }
+ CanvasItem* node = _cast_node<CanvasItem>(get_node_internal(path));
+ ERR_FAIL_NULL_V(node, FAILED);
node->hide();
return OK;
}
-Error GUINode::hide_nodes(Array const& paths) const {
+Error GUINode::hide_nodes(TypedArray<NodePath> const& paths) const {
Error ret = OK;
for (int32_t i = 0; i < paths.size(); ++i) {
if (hide_node(paths[i]) != OK) {
diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp
index 1671547..e38ed1f 100644
--- a/extension/src/openvic-extension/classes/GUINode.hpp
+++ b/extension/src/openvic-extension/classes/GUINode.hpp
@@ -12,42 +12,54 @@
#include "openvic-extension/classes/GFXIconTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
namespace OpenVic {
class GUINode : public godot::Control {
GDCLASS(GUINode, godot::Control)
- template<std::derived_from<godot::Node> T>
- T* _get_cast_node(godot::NodePath const& path) const;
-
- template<std::derived_from<godot::Texture2D> T>
- godot::Ref<T> _get_cast_texture_from_node(godot::NodePath const& path) const;
-
protected:
static void _bind_methods();
public:
- GUINode() = default;
+ GUINode();
+
+ static godot::Control* generate_gui_element(
+ godot::String const& gui_file, godot::String const& gui_element, godot::String const& name = ""
+ );
- godot::Error _add_gui_element(GUI::Element const* element, godot::String const& name);
godot::Error add_gui_element(
godot::String const& gui_file, godot::String const& gui_element, godot::String const& name = ""
);
- godot::Button* get_button_node(godot::NodePath const& path) const;
- godot::CheckBox* get_check_box_node(godot::NodePath const& path) const;
- godot::Label* get_label_node(godot::NodePath const& path) const;
- godot::Panel* get_panel_node(godot::NodePath const& path) const;
- godot::TextureProgressBar* get_progress_bar_node(godot::NodePath const& path) const;
- godot::TextureRect* get_texture_rect_node(godot::NodePath const& path) const;
+ static godot::Button* get_button_from_node(godot::Node* node);
+ static godot::CheckBox* get_check_box_from_node(godot::Node* node);
+ static godot::Label* get_label_from_node(godot::Node* node);
+ static godot::Panel* get_panel_from_node(godot::Node* node);
+ static godot::TextureProgressBar* get_progress_bar_from_node(godot::Node* node);
+ static godot::TextureRect* get_texture_rect_from_node(godot::Node* node);
+ static GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_node(godot::Node* node);
+
+ godot::Button* get_button_from_nodepath(godot::NodePath const& path) const;
+ godot::CheckBox* get_check_box_from_nodepath(godot::NodePath const& path) const;
+ godot::Label* get_label_from_nodepath(godot::NodePath const& path) const;
+ godot::Panel* get_panel_from_nodepath(godot::NodePath const& path) const;
+ godot::TextureProgressBar* get_progress_bar_from_nodepath(godot::NodePath const& path) const;
+ godot::TextureRect* get_texture_rect_from_nodepath(godot::NodePath const& path) const;
+ GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_nodepath(godot::NodePath const& path) const;
/* Helper functions to get textures from TextureRects and Buttons. */
- godot::Ref<godot::Texture2D> get_texture_from_node(godot::NodePath const& path) const;
- godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_node(godot::NodePath const& path) const;
- godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_node(godot::NodePath const& path) const;
- godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_node(godot::NodePath const& path) const;
+ static godot::Ref<godot::Texture2D> get_texture_from_node(godot::Node* node);
+ static godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_node(godot::Node* node);
+ static godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_node(godot::Node* node);
+ static godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_node(godot::Node* node);
+
+ godot::Ref<godot::Texture2D> get_texture_from_nodepath(godot::NodePath const& path) const;
+ godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_nodepath(godot::NodePath const& path) const;
+ godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_nodepath(godot::NodePath const& path) const;
+ godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_nodepath(godot::NodePath const& path) const;
godot::Error hide_node(godot::NodePath const& path) const;
- godot::Error hide_nodes(godot::Array const& paths) const;
+ godot::Error hide_nodes(godot::TypedArray<godot::NodePath> const& paths) const;
};
}
diff --git a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp
new file mode 100644
index 0000000..ff88781
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp
@@ -0,0 +1,177 @@
+#include "GUIOverlappingElementsBox.hpp"
+
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/UITools.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace OpenVic;
+using namespace godot;
+
+using OpenVic::Utilities::std_view_to_godot_string;
+
+Error GUIOverlappingElementsBox::_update_child_positions() {
+ ERR_FAIL_NULL_V(gui_overlapping_elements_box, FAILED);
+ const int32_t child_count = get_child_count();
+ if (child_count <= 0) {
+ return OK;
+ }
+
+ const auto _get_child = [this](int32_t index) -> Control* { return Object::cast_to<Control>(get_child(index)); };
+
+ const float box_width = get_size().x;
+ const float child_width = _get_child(0)->get_size().x;
+
+ const float max_spacing = box_width / (child_count + 1);
+ const float default_spacing = child_width + gui_overlapping_elements_box->get_spacing().to_float();
+
+ const float spacing = std::min(max_spacing, default_spacing);
+
+ const float total_width = spacing * (child_count - 1) + child_width;
+
+ float starting_x = 0.0f;
+ Error err = OK;
+ using enum GUI::AlignedElement::format_t;
+ switch (gui_overlapping_elements_box->get_format()) {
+ case left:
+ break;
+ case centre:
+ starting_x = (box_width - total_width) / 2;
+ break;
+ case right:
+ starting_x = box_width - total_width;
+ break;
+ default:
+ UtilityFunctions::push_error(
+ "Invalid GUIOverlappingElementsBox alignment: ",
+ static_cast<int32_t>(gui_overlapping_elements_box->get_format())
+ );
+ err = FAILED;
+ }
+
+ for (int32_t index = 0; index < child_count; ++index) {
+ _get_child(index)->set_position({ starting_x + spacing * index, 0.0f });
+ }
+
+ return err;
+}
+
+void GUIOverlappingElementsBox::_bind_methods() {
+ OV_BIND_METHOD(GUIOverlappingElementsBox::clear);
+ OV_BIND_METHOD(GUIOverlappingElementsBox::clear_children);
+ OV_BIND_METHOD(GUIOverlappingElementsBox::set_child_count, { "new_count" });
+
+ OV_BIND_METHOD(GUIOverlappingElementsBox::get_gui_overlapping_elements_box_name);
+
+ OV_BIND_METHOD(
+ GUIOverlappingElementsBox::set_gui_child_element_name, { "gui_child_element_file", "gui_child_element_name" }
+ );
+ OV_BIND_METHOD(GUIOverlappingElementsBox::get_gui_child_element_name);
+}
+
+void GUIOverlappingElementsBox::_notification(int what) {
+ if (what == NOTIFICATION_SORT_CHILDREN) {
+ _update_child_positions();
+ }
+}
+
+GUIOverlappingElementsBox::GUIOverlappingElementsBox()
+ : gui_overlapping_elements_box { nullptr }, gui_child_element { nullptr } {}
+
+void GUIOverlappingElementsBox::clear() {
+ clear_children();
+ gui_child_element = nullptr;
+ gui_overlapping_elements_box = nullptr;
+}
+
+void GUIOverlappingElementsBox::clear_children() {
+ set_child_count(0);
+}
+
+Error GUIOverlappingElementsBox::set_child_count(int32_t new_count) {
+ ERR_FAIL_COND_V_MSG(new_count < 0, FAILED, "Child count must be non-negative");
+ int32_t child_count = get_child_count();
+ if (child_count == new_count) {
+ return OK;
+ } else if (child_count > new_count) {
+ do {
+ remove_child(get_child(--child_count));
+ } while (child_count > new_count);
+ return OK;
+ } else {
+ ERR_FAIL_NULL_V_MSG(
+ gui_child_element, FAILED, vformat(
+ "GUIOverlappingElementsBox child element is null (child_count = %d, new_count = %d)", child_count, new_count
+ )
+ );
+ Error err = OK;
+ const String gui_child_element_name = std_view_to_godot_string(gui_child_element->get_name()) + "_";
+ do {
+ Control* child = nullptr;
+ const String name = gui_child_element_name + itos(child_count);
+ if (!UITools::generate_gui_element(gui_child_element, name, child)) {
+ UtilityFunctions::push_error("Error generating GUIOverlappingElementsBox child element ", name);
+ err = FAILED;
+ }
+ ERR_FAIL_NULL_V_MSG(
+ child, FAILED, vformat(
+ "Failed to generate GUIOverlappingElementsBox child element %s (child_count = %d, new_count = %d)",
+ name, child_count, new_count
+ )
+ );
+ add_child(child);
+ child_count++;
+ } while (child_count < new_count);
+ return err;
+ }
+}
+
+Error GUIOverlappingElementsBox::set_gui_overlapping_elements_box(
+ GUI::OverlappingElementsBox const* new_gui_overlapping_elements_box
+) {
+ if (gui_overlapping_elements_box == new_gui_overlapping_elements_box) {
+ return OK;
+ }
+ gui_overlapping_elements_box = new_gui_overlapping_elements_box;
+ if (gui_overlapping_elements_box == nullptr) {
+ return OK;
+ }
+
+ set_custom_minimum_size(Utilities::to_godot_fvec2(gui_overlapping_elements_box->get_size()));
+ queue_sort();
+ return OK;
+}
+
+String GUIOverlappingElementsBox::get_gui_overlapping_elements_box_name() const {
+ return gui_overlapping_elements_box != nullptr ? std_view_to_godot_string(gui_overlapping_elements_box->get_name()) : String {};
+}
+
+Error GUIOverlappingElementsBox::set_gui_child_element(GUI::Element const* new_gui_child_element) {
+ clear_children();
+ gui_child_element = new_gui_child_element;
+ return OK;
+}
+
+Error GUIOverlappingElementsBox::set_gui_child_element_name(
+ String const& gui_child_element_file, String const& gui_child_element_name
+) {
+ if (gui_child_element_file.is_empty() && gui_child_element_name.is_empty()) {
+ return set_gui_child_element(nullptr);
+ }
+ ERR_FAIL_COND_V_MSG(
+ gui_child_element_file.is_empty(), FAILED,
+ vformat("GUI child element file name is empty but element name is not: %s", gui_child_element_name)
+ );
+ ERR_FAIL_COND_V_MSG(
+ gui_child_element_name.is_empty(), FAILED,
+ vformat("GUI child element name is empty but file name is not: %s", gui_child_element_file)
+ );
+ GUI::Element const* const new_gui_child_element = UITools::get_gui_element(gui_child_element_file, gui_child_element_name);
+ ERR_FAIL_NULL_V(new_gui_child_element, FAILED);
+ return set_gui_child_element(new_gui_child_element);
+}
+
+String GUIOverlappingElementsBox::get_gui_child_element_name() const {
+ return gui_child_element != nullptr ? std_view_to_godot_string(gui_child_element->get_name()) : String {};
+}
diff --git a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp
new file mode 100644
index 0000000..5a420f2
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <godot_cpp/classes/container.hpp>
+
+#include <openvic-simulation/interface/GUI.hpp>
+
+namespace OpenVic {
+ class GUIOverlappingElementsBox : public godot::Container {
+ GDCLASS(GUIOverlappingElementsBox, godot::Container)
+
+ GUI::OverlappingElementsBox const* PROPERTY(gui_overlapping_elements_box);
+ GUI::Element const* PROPERTY(gui_child_element);
+
+ godot::Error _update_child_positions();
+
+ protected:
+ static void _bind_methods();
+
+ void _notification(int what);
+
+ public:
+ GUIOverlappingElementsBox();
+
+ /* Reset gui_overlapping_elements_box and gui_child_element to nullptr, and remove all child elements. */
+ void clear();
+
+ /* Remove all child elements. */
+ void clear_children();
+
+ /* Add or remove child elements to reach the specified count. */
+ godot::Error set_child_count(int32_t new_count);
+
+ /* Set the GUI::OverlappingElementsBox. This does not affect any existing child elements. */
+ godot::Error set_gui_overlapping_elements_box(GUI::OverlappingElementsBox const* new_gui_overlapping_elements_box);
+
+ /* Return the name of the GUI::OverlappingElementsBox, or an empty String if it's null. */
+ godot::String get_gui_overlapping_elements_box_name() const;
+
+ /* Set the child GUI::Element, removing all previous child elements (even if the child GUI::Element doesn't change). */
+ godot::Error set_gui_child_element(GUI::Element const* new_gui_child_element);
+
+ /* Search for a GUI::Element with the specfied name and, if successful,
+ * set the child element to it using set_gui_child_element. */
+ godot::Error set_gui_child_element_name(
+ godot::String const& gui_child_element_file, godot::String const& gui_child_element_name
+ );
+
+ /* Return the name of the child GUI::Element, or an empty String if it's null. */
+ godot::String get_gui_child_element_name() const;
+ };
+}
diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp
index 65bdc60..278365e 100644
--- a/extension/src/openvic-extension/register_types.cpp
+++ b/extension/src/openvic-extension/register_types.cpp
@@ -6,6 +6,7 @@
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
#include "openvic-extension/classes/GUINode.hpp"
+#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/MapMesh.hpp"
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/Checksum.hpp"
@@ -46,6 +47,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
ClassDB::register_class<GFXMaskedFlagTexture>();
ClassDB::register_class<GFXPieChartTexture>();
ClassDB::register_class<GUINode>();
+ ClassDB::register_class<GUIOverlappingElementsBox>();
}
void uninitialize_openvic_types(ModuleInitializationLevel p_level) {
diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp
index 8e9eb41..581c7fa 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.cpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.cpp
@@ -15,11 +15,6 @@ using OpenVic::Utilities::std_to_godot_string;
void AssetManager::_bind_methods() {
OV_BIND_METHOD(AssetManager::get_image, { "path" });
OV_BIND_METHOD(AssetManager::get_texture, { "path" });
-
- OV_BIND_SMETHOD(AssetManager::make_icon, { "texture", "frame", "frame_count" });
- OV_BIND_METHOD(AssetManager::get_icon, { "texture", "frame", "frame_count" });
-
- OV_BIND_METHOD(AssetManager::get_texture_or_icon, { "path", "frame", "frame_count" });
OV_BIND_METHOD(AssetManager::get_font, { "name" });
}
@@ -96,46 +91,6 @@ Ref<ImageTexture> AssetManager::get_texture(StringName path) {
}
}
-Ref<AtlasTexture> AssetManager::make_icon(Ref<Texture2D> texture, GFX::frame_t frame, GFX::frame_t frame_count) {
- ERR_FAIL_NULL_V(texture, nullptr);
-
- if (frame_count <= GFX::NO_FRAMES) {
- UtilityFunctions::push_warning("No frames!");
- frame_count = 1;
- }
- if (frame <= GFX::NO_FRAMES || frame > frame_count) {
- UtilityFunctions::push_warning("Invalid frame index ", frame, " out of count ", frame_count);
- frame = frame_count;
- }
- frame--;
- const Vector2i size = texture->get_size();
- const Rect2i region { frame * size.x / frame_count, 0, size.x / frame_count, size.y };
-
- Ref<AtlasTexture> atlas;
- atlas.instantiate();
- ERR_FAIL_NULL_V(atlas, nullptr);
- atlas->set_atlas(texture);
- atlas->set_region(region);
- return atlas;
-}
-
-Ref<AtlasTexture> AssetManager::get_icon(StringName path, GFX::frame_t frame, GFX::frame_t frame_count) {
- Ref<ImageTexture> texture = get_texture(path);
- ERR_FAIL_NULL_V(texture, nullptr);
- return make_icon(texture, frame, frame_count);
-}
-
-Ref<Texture2D> AssetManager::get_texture_or_icon(StringName path, GFX::frame_t frame, GFX::frame_t frame_count) {
- if (frame_count < 2) {
- if (frame > frame_count) {
- UtilityFunctions::push_warning("Invalid frame index ", frame, " out of count ", frame_count);
- }
- return get_texture(path);
- } else {
- return get_icon(path, frame, frame_count);
- }
-}
-
Ref<Font> AssetManager::get_font(StringName name) {
const font_map_t::const_iterator it = fonts.find(name);
if (it != fonts.end()) {
diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp
index 625944d..e6a7664 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.hpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.hpp
@@ -44,17 +44,6 @@ namespace OpenVic {
* or texture creation fails. */
godot::Ref<godot::ImageTexture> get_texture(godot::StringName path);
- /* Extract the specified frame of the texture, which is treated as a single row of frame_count frames. */
- static godot::Ref<godot::AtlasTexture> make_icon(
- godot::Ref<godot::Texture2D> texture, GFX::frame_t frame, GFX::frame_t frame_count
- );
-
- /* Load a texture as with get_texture, and extract the specified frame as with make_icon. */
- godot::Ref<godot::AtlasTexture> get_icon(godot::StringName path, GFX::frame_t frame, GFX::frame_t frame_count);
-
- /* Load a texture as with get_texture if frame_count <= 1 otherwise as with get_icon. */
- godot::Ref<godot::Texture2D> get_texture_or_icon(godot::StringName path, GFX::frame_t frame, GFX::frame_t frame_count);
-
/* Search for and load a font with the specified name from the game defines' font directory, first checking the
* AssetManager's font cache in case it has already been loaded, and returning nullptr if font loading fails. */
godot::Ref<godot::Font> get_font(godot::StringName name);
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp
index a53ba54..0e64313 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp
@@ -8,6 +8,8 @@
#include <openvic-simulation/utility/Logger.hpp>
+#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/LoadLocalisation.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
#include "openvic-extension/utility/Utilities.hpp"
@@ -48,7 +50,9 @@ void GameSingleton::_bind_methods() {
OV_BIND_METHOD(GameSingleton::get_selected_province_index);
OV_BIND_METHOD(GameSingleton::set_selected_province, { "index" });
- OV_BIND_METHOD(GameSingleton::expand_building, { "province_index", "building_type_identifier" });
+ OV_BIND_METHOD(GameSingleton::get_province_building_count);
+ OV_BIND_METHOD(GameSingleton::get_province_building_identifier, { "building_index" });
+ OV_BIND_METHOD(GameSingleton::expand_selected_province_building, { "building_index" });
OV_BIND_METHOD(GameSingleton::get_slave_pop_icon_index);
OV_BIND_METHOD(GameSingleton::get_administrative_pop_icon_index);
OV_BIND_METHOD(GameSingleton::get_rgo_owner_pop_icon_index);
@@ -135,51 +139,23 @@ int32_t GameSingleton::get_province_index_from_uv_coords(Vector2 const& coords)
return game_manager.get_map().get_province_index_at(x_mod_w, y_mod_h);
}
-template<std::derived_from<HasIdentifierAndColour> T>
-static Array _distribution_to_pie_chart_array(fixed_point_map_t<T const*> const& dist) {
- using entry_t = std::pair<T const*, fixed_point_t>;
- std::vector<entry_t> sorted_dist;
- sorted_dist.reserve(dist.size());
- for (entry_t const& entry : dist) {
- if (entry.first != nullptr) {
- sorted_dist.push_back(entry);
- } else {
- UtilityFunctions::push_error("Null distribution key with value ", entry.second.to_float());
- }
- }
- std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool {
- return lhs.second < rhs.second;
- });
- static const StringName identifier_key = "identifier";
- static const StringName colour_key = "colour";
- static const StringName weight_key = "weight";
- Array array;
- for (auto const& [key, val] : sorted_dist) {
- Dictionary sub_dict;
- sub_dict[identifier_key] = std_view_to_godot_string(key->get_identifier());
- sub_dict[colour_key] = Utilities::to_godot_color(key->get_colour());
- sub_dict[weight_key] = val.to_float();
- array.push_back(sub_dict);
- }
- return array;
-}
-
Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
static const StringName province_info_province_key = "province";
static const StringName province_info_region_key = "region";
- static const StringName province_info_controller_key = "controller";
- static const StringName province_info_life_rating_key = "life_rating";
+ static const StringName province_info_slave_status_key = "slave_status";
+ static const StringName province_info_colony_status_key = "colony_status";
static const StringName province_info_terrain_type_key = "terrain_type";
+ static const StringName province_info_life_rating_key = "life_rating";
+ static const StringName province_info_controller_key = "controller";
+ static const StringName province_info_rgo_name_key = "rgo_name";
+ static const StringName province_info_rgo_icon_key = "rgo_icon";
static const StringName province_info_crime_name_key = "crime_name";
static const StringName province_info_crime_icon_key = "crime_icon";
static const StringName province_info_total_population_key = "total_population";
static const StringName province_info_pop_types_key = "pop_types";
static const StringName province_info_pop_ideologies_key = "pop_ideologies";
static const StringName province_info_pop_cultures_key = "pop_cultures";
- static const StringName province_info_rgo_name_key = "rgo_name";
- static const StringName province_info_rgo_icon_key = "rgo_icon";
- static const StringName province_info_colony_status_key = "colony_status";
- static const StringName province_info_slave_status_key = "slave_status";
+ static const StringName province_info_cores_key = "cores";
static const StringName province_info_buildings_key = "buildings";
Province const* province = game_manager.get_map().get_province_by_index(index);
@@ -195,6 +171,17 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
ret[province_info_region_key] = std_view_to_godot_string(region->get_identifier());
}
+ ret[province_info_slave_status_key] = province->get_slave();
+
+ ret[province_info_colony_status_key] = static_cast<int32_t>(province->get_colony_status());
+
+ TerrainType const* terrain_type = province->get_terrain_type();
+ if (terrain_type != nullptr) {
+ ret[province_info_terrain_type_key] = std_view_to_godot_string(terrain_type->get_identifier());
+ }
+
+ ret[province_info_life_rating_key] = province->get_life_rating();
+
Country const* controller = province->get_controller();
if (controller != nullptr) {
ret[province_info_controller_key] = std_view_to_godot_string(controller->get_identifier());
@@ -206,37 +193,39 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
ret[province_info_rgo_icon_key] = static_cast<int32_t>(rgo->get_index());
}
- ret[province_info_life_rating_key] = province->get_life_rating();
-
- TerrainType const* terrain_type = province->get_terrain_type();
- if (terrain_type != nullptr) {
- ret[province_info_terrain_type_key] = std_view_to_godot_string(terrain_type->get_identifier());
- }
-
Crime const* crime = province->get_crime();
if (crime != nullptr) {
ret[province_info_crime_name_key] = std_view_to_godot_string(crime->get_identifier());
ret[province_info_crime_icon_key] = static_cast<int32_t>(crime->get_icon());
}
- ret[province_info_colony_status_key] = static_cast<int32_t>(province->get_colony_status());
- ret[province_info_slave_status_key] = province->get_slave();
-
ret[province_info_total_population_key] = province->get_total_population();
+
fixed_point_map_t<PopType const*> const& pop_types = province->get_pop_type_distribution();
if (!pop_types.empty()) {
- ret[province_info_pop_types_key] = _distribution_to_pie_chart_array(pop_types);
+ ret[province_info_pop_types_key] = GFXPieChartTexture::distribution_to_slices_array(pop_types);
}
+
fixed_point_map_t<Ideology const*> const& ideologies = province->get_ideology_distribution();
if (!ideologies.empty()) {
- ret[province_info_pop_ideologies_key] = _distribution_to_pie_chart_array(ideologies);
+ ret[province_info_pop_ideologies_key] = GFXPieChartTexture::distribution_to_slices_array(ideologies);
}
+
fixed_point_map_t<Culture const*> const& cultures = province->get_culture_distribution();
if (!cultures.empty()) {
- ret[province_info_pop_cultures_key] = _distribution_to_pie_chart_array(cultures);
+ ret[province_info_pop_cultures_key] = GFXPieChartTexture::distribution_to_slices_array(cultures);
+ }
+
+ std::vector<Country const*> const& cores = province->get_cores();
+ if (!cores.empty()) {
+ PackedStringArray cores_array;
+ cores_array.resize(cores.size());
+ for (size_t idx = 0; idx < cores.size(); ++idx) {
+ cores_array[idx] = std_view_to_godot_string(cores[idx]->get_identifier());
+ }
+ ret[province_info_cores_key] = cores_array;
}
- static const StringName building_info_building_key = "building";
static const StringName building_info_level_key = "level";
static const StringName building_info_expansion_state_key = "expansion_state";
static const StringName building_info_start_date_key = "start_date";
@@ -245,13 +234,14 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
std::vector<BuildingInstance> const& buildings = province->get_buildings();
if (!buildings.empty()) {
- Array buildings_array;
+ /* This system relies on the province buildings all being present in the right order. It will have to
+ * be changed if we want to support variable combinations and permutations of province buildings. */
+ TypedArray<Dictionary> buildings_array;
buildings_array.resize(buildings.size());
for (size_t idx = 0; idx < buildings.size(); ++idx) {
BuildingInstance const& building = buildings[idx];
Dictionary building_dict;
- building_dict[building_info_building_key] = std_view_to_godot_string(building.get_identifier());
building_dict[building_info_level_key] = static_cast<int32_t>(building.get_level());
building_dict[building_info_expansion_state_key] = static_cast<int32_t>(building.get_expansion_state());
building_dict[building_info_start_date_key] = std_to_godot_string(building.get_start_date().to_string());
@@ -321,14 +311,14 @@ Error GameSingleton::_update_colour_image() {
return FAILED;
}
/* We reshape the list of colours into a square, as each texture dimensions cannot exceed 16384. */
- static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * 4);
- static constexpr int32_t colour_image_width = PROVINCE_INDEX_SQRT * sizeof(Mapmode::base_stripe_t) / sizeof(colour_t);
+ static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * CHAR_BIT / 2);
+ static constexpr int32_t colour_image_width = PROVINCE_INDEX_SQRT * sizeof(Mapmode::base_stripe_t) / sizeof(colour_argb_t);
/* Province count + null province, rounded up to next multiple of PROVINCE_INDEX_SQRT.
* Rearranged from: (map.get_province_count() + 1) + (PROVINCE_INDEX_SQRT - 1) */
static const int32_t colour_image_height = (map.get_province_count() + PROVINCE_INDEX_SQRT) / PROVINCE_INDEX_SQRT;
static PackedByteArray colour_data_array;
- static const int64_t colour_data_array_size = colour_image_width * colour_image_height * sizeof(colour_t);
+ static const int64_t colour_data_array_size = colour_image_width * colour_image_height * sizeof(colour_argb_t);
colour_data_array.resize(colour_data_array_size);
Error err = OK;
@@ -386,12 +376,25 @@ void GameSingleton::set_selected_province(int32_t index) {
emit_signal("province_selected", index);
}
-Error GameSingleton::expand_building(int32_t province_index, String const& building_type_identifier) {
- if (!game_manager.expand_building(province_index, godot_to_std_string(building_type_identifier))) {
- UtilityFunctions::push_error("Failed to expand ", building_type_identifier, " at province index ", province_index);
- return FAILED;
+int32_t GameSingleton::get_province_building_count() const {
+ return game_manager.get_economy_manager().get_building_type_manager().get_province_building_types().size();
+}
+
+String GameSingleton::get_province_building_identifier(int32_t index) const {
+ std::vector<BuildingType const*> const& province_building_types =
+ game_manager.get_economy_manager().get_building_type_manager().get_province_building_types();
+ ERR_FAIL_COND_V_MSG(
+ index < 0 || index >= province_building_types.size(), {}, vformat("Invalid province building index: %d", index)
+ );
+ return std_view_to_godot_string(province_building_types[index]->get_identifier());
+}
+
+Error GameSingleton::expand_selected_province_building(int32_t building_index) {
+ const bool ret = game_manager.expand_selected_province_building(building_index);
+ if (!ret) {
+ UtilityFunctions::push_error("Failed to expand the currently selected province's building index ", building_index);
}
- return OK;
+ return ERR(ret);
}
int32_t GameSingleton::get_slave_pop_icon_index() const {
@@ -493,7 +496,7 @@ Error GameSingleton::_load_map_images(bool flip_vertical) {
Map::shape_pixel_t const* province_shape_data = game_manager.get_map().get_province_shape_image().data();
const Vector2i divided_dims = province_dims / image_subdivisions;
- Array province_shape_images;
+ TypedArray<Image> province_shape_images;
province_shape_images.resize(image_subdivisions.x * image_subdivisions.y);
for (int32_t v = 0; v < image_subdivisions.y; ++v) {
for (int32_t u = 0; u < image_subdivisions.x; ++u) {
@@ -561,12 +564,14 @@ Error GameSingleton::_load_terrain_variants() {
}
const int32_t slice_size = sheet_width / SHEET_DIMS;
- Array terrain_images;
+ TypedArray<Image> terrain_images;
{
- static constexpr colour_t TERRAIN_WATER_INDEX_COLOUR = 0xFFFFFF;
- Ref<Image> water_image = Image::create(slice_size, slice_size, false, terrain_sheet->get_format());
+ // TODO - load "map/terrain/water.dds" instead of using a solid colour (issue is that it's 512x512
+ // while the texturesheet slices are 256x256, so they can't share a Texture2DArray)
+ const Ref<Image> water_image = Utilities::make_solid_colour_image(
+ { 0.1f, 0.1f, 0.5f }, slice_size, slice_size, terrain_sheet->get_format()
+ );
ERR_FAIL_NULL_V_EDMSG(water_image, FAILED, "Failed to create water terrain image");
- water_image->fill({ 0.1f, 0.1f, 0.5f });
terrain_images.append(water_image);
}
Error err = OK;
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp
index 5559f1b..1f3905e 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp
@@ -7,8 +7,6 @@
#include <openvic-simulation/GameManager.hpp>
#include <openvic-simulation/dataloader/Dataloader.hpp>
-#include "openvic-extension/UIAdapter.hpp"
-
namespace OpenVic {
class GameSingleton : public godot::Object {
@@ -95,7 +93,9 @@ namespace OpenVic {
int32_t get_selected_province_index() const;
void set_selected_province(int32_t index);
- godot::Error expand_building(int32_t province_index, godot::String const& building_type_identifier);
+ int32_t get_province_building_count() const;
+ godot::String get_province_building_identifier(int32_t building_index) const;
+ godot::Error expand_selected_province_building(int32_t building_index);
int32_t get_slave_pop_icon_index() const;
int32_t get_administrative_pop_icon_index() const;
int32_t get_rgo_owner_pop_icon_index() const;
diff --git a/extension/src/openvic-extension/UIAdapter.cpp b/extension/src/openvic-extension/utility/UITools.cpp
index cbe898c..cba65a4 100644
--- a/extension/src/openvic-extension/UIAdapter.cpp
+++ b/extension/src/openvic-extension/utility/UITools.cpp
@@ -1,4 +1,4 @@
-#include "UIAdapter.hpp"
+#include "UITools.hpp"
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/check_box.hpp>
@@ -14,38 +14,53 @@
#include "openvic-extension/classes/GFXIconTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
+#include "openvic-extension/singletons/AssetManager.hpp"
+#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/Utilities.hpp"
using namespace godot;
using namespace OpenVic;
+using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
-bool GodotGUIBuilder::generate_element(
- GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- if (element == nullptr) {
- UtilityFunctions::push_error("Invalid element passed to GodotGUIBuilder - null!");
- return false;
- }
- static const std::map<std::string_view, bool (*)(GUI::Element const&, String const&, AssetManager&, Control*&)> type_map {
- { GUI::Icon::get_type_static(), &generate_icon },
- { GUI::Button::get_type_static(), &generate_button },
- { GUI::Checkbox::get_type_static(), &generate_checkbox },
- { GUI::Text::get_type_static(), &generate_text },
- { GUI::OverlappingElementsBox::get_type_static(), &generate_overlapping_elements },
- { GUI::ListBox::get_type_static(), &generate_listbox },
- { GUI::Window::get_type_static(), &generate_window }
+GFX::Sprite const* UITools::get_gfx_sprite(godot::String const& gfx_sprite) {
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, nullptr);
+ GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
+ godot_to_std_string(gfx_sprite)
+ );
+ ERR_FAIL_NULL_V_MSG(sprite, nullptr, vformat("GFX sprite not found: %s", gfx_sprite));
+ return sprite;
+}
+
+GUI::Element const* UITools::get_gui_element(godot::String const& gui_file, godot::String const& gui_element) {
+ GameSingleton const* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, nullptr);
+ GUI::Scene const* scene =
+ game_singleton->get_game_manager().get_ui_manager().get_scene_by_identifier(godot_to_std_string(gui_file));
+ ERR_FAIL_NULL_V_MSG(scene, nullptr, vformat("Failed to find GUI file %s", gui_file));
+ GUI::Element const* element = scene->get_scene_element_by_identifier(godot_to_std_string(gui_element));
+ ERR_FAIL_NULL_V_MSG(element, nullptr, vformat("Failed to find GUI element %s in GUI file %s", gui_element, gui_file));
+ return element;
+}
+
+/* GUI::Element tree -> godot::Control tree conversion code below: */
+
+namespace OpenVic {
+ struct generate_gui_args_t {
+ GUI::Element const& element;
+ godot::String const& name;
+ AssetManager& asset_manager;
+ godot::Control*& result;
+
+ constexpr generate_gui_args_t(
+ GUI::Element const& new_element, godot::String const& new_name, AssetManager& new_asset_manager,
+ godot::Control*& new_result
+ ) : element { new_element }, name { new_name }, asset_manager { new_asset_manager }, result { new_result } {}
};
- const decltype(type_map)::const_iterator it = type_map.find(element->get_type());
- if (it != type_map.end()) {
- return it->second(*element, name, asset_manager, result);
- } else {
- UtilityFunctions::push_error("Invalid GUI element type: ", std_view_to_godot_string(element->get_type()));
- result = nullptr;
- return false;
- }
}
template<std::derived_from<Control> T>
@@ -75,24 +90,23 @@ static T* new_control(GUI::Element const& element, String const& name) {
std_view_to_godot_string(element.get_name()));
}
node->set_position(Utilities::to_godot_fvec2(element.get_position()));
+ node->set_h_size_flags(Control::SizeFlags::SIZE_SHRINK_BEGIN);
+ node->set_v_size_flags(Control::SizeFlags::SIZE_SHRINK_BEGIN);
node->set_focus_mode(Control::FOCUS_NONE);
return node;
}
-bool GodotGUIBuilder::generate_icon(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::Icon const& icon = static_cast<GUI::Icon const&>(element);
+static bool generate_icon(generate_gui_args_t&& args) {
+ GUI::Icon const& icon = static_cast<GUI::Icon const&>(args.element);
- result = nullptr;
const String icon_name = std_view_to_godot_string(icon.get_name());
/* Change to use sprite type to choose Godot node type! */
bool ret = true;
if (icon.get_sprite() != nullptr) {
if (icon.get_sprite()->is_type<GFX::TextureSprite>()) {
- TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.name);
ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
GFX::TextureSprite const* texture_sprite = icon.get_sprite()->cast_to<GFX::TextureSprite>();
@@ -104,9 +118,9 @@ bool GodotGUIBuilder::generate_icon(
ret = false;
}
- result = godot_texture_rect;
+ args.result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) {
- TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.name);
ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
GFX::MaskedFlag const* masked_flag = icon.get_sprite()->cast_to<GFX::MaskedFlag>();
@@ -118,16 +132,16 @@ bool GodotGUIBuilder::generate_icon(
ret = false;
}
- result = godot_texture_rect;
+ args.result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::ProgressBar>()) {
- TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon, name);
+ TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon, args.name);
ERR_FAIL_NULL_V_MSG(
godot_progress_bar, false, vformat("Failed to create TextureProgressBar for GUI icon %s", icon_name)
);
const StringName back_texture_file =
std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::ProgressBar>()->get_back_texture_file());
- const Ref<ImageTexture> back_texture = asset_manager.get_texture(back_texture_file);
+ const Ref<ImageTexture> back_texture = args.asset_manager.get_texture(back_texture_file);
if (back_texture.is_valid()) {
godot_progress_bar->set_under_texture(back_texture);
} else {
@@ -137,7 +151,7 @@ bool GodotGUIBuilder::generate_icon(
const StringName progress_texture_file =
std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::ProgressBar>()->get_progress_texture_file());
- const Ref<ImageTexture> progress_texture = asset_manager.get_texture(progress_texture_file);
+ const Ref<ImageTexture> progress_texture = args.asset_manager.get_texture(progress_texture_file);
if (progress_texture.is_valid()) {
godot_progress_bar->set_progress_texture(progress_texture);
} else {
@@ -147,9 +161,9 @@ bool GodotGUIBuilder::generate_icon(
ret = false;
}
- result = godot_progress_bar;
+ args.result = godot_progress_bar;
} else if (icon.get_sprite()->is_type<GFX::PieChart>()) {
- TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.name);
ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
GFX::PieChart const* pie_chart = icon.get_sprite()->cast_to<GFX::PieChart>();
@@ -165,7 +179,7 @@ bool GodotGUIBuilder::generate_icon(
ret = false;
}
- result = godot_texture_rect;
+ args.result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::LineChart>()) {
} else {
@@ -180,16 +194,13 @@ bool GodotGUIBuilder::generate_icon(
return ret;
}
-bool GodotGUIBuilder::generate_button(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::Button const& button = static_cast<GUI::Button const&>(element);
+static bool generate_button(generate_gui_args_t&& args) {
+ GUI::Button const& button = static_cast<GUI::Button const&>(args.element);
// TODO - shortcut, sprite, text
- result = nullptr;
const String button_name = std_view_to_godot_string(button.get_name());
- Button* godot_button = new_control<Button>(button, name);
+ Button* godot_button = new_control<Button>(button, args.name);
ERR_FAIL_NULL_V_MSG(godot_button, false, vformat("Failed to create Button for GUI button %s", button_name));
if (!button.get_text().empty()) {
@@ -220,7 +231,7 @@ bool GodotGUIBuilder::generate_button(
}
if (texture.is_valid()) {
- godot_button->set_size(texture->get_size());
+ godot_button->set_custom_minimum_size(texture->get_size());
Ref<StyleBoxTexture> stylebox;
stylebox.instantiate();
if (stylebox.is_valid()) {
@@ -239,7 +250,7 @@ bool GodotGUIBuilder::generate_button(
if (button.get_font() != nullptr) {
const StringName font_file = std_view_to_godot_string_name(button.get_font()->get_fontname());
- const Ref<Font> font = asset_manager.get_font(font_file);
+ const Ref<Font> font = args.asset_manager.get_font(font_file);
if (font.is_valid()) {
godot_button->add_theme_font_override("font", font);
} else {
@@ -250,20 +261,17 @@ bool GodotGUIBuilder::generate_button(
godot_button->add_theme_color_override("font_color", colour);
}
- result = godot_button;
+ args.result = godot_button;
return ret;
}
-bool GodotGUIBuilder::generate_checkbox(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(element);
+static bool generate_checkbox(generate_gui_args_t&& args) {
+ GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(args.element);
// TODO - shortcut, sprite, text
- result = nullptr;
const String checkbox_name = std_view_to_godot_string(checkbox.get_name());
- CheckBox* godot_checkbox = new_control<CheckBox>(checkbox, name);
+ CheckBox* godot_checkbox = new_control<CheckBox>(checkbox, args.name);
ERR_FAIL_NULL_V_MSG(godot_checkbox, false, vformat("Failed to create CheckBox for GUI checkbox %s", checkbox_name));
bool ret = true;
@@ -272,7 +280,7 @@ bool GodotGUIBuilder::generate_checkbox(
if (texture_sprite != nullptr) {
Ref<GFXIconTexture> icon_texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, 1);
if (icon_texture.is_valid()) {
- godot_checkbox->set_size(icon_texture->get_size());
+ godot_checkbox->set_custom_minimum_size(icon_texture->get_size());
godot_checkbox->add_theme_icon_override("unchecked", icon_texture);
} else {
UtilityFunctions::push_error("Failed to make unchecked GFXIconTexture for GUI checkbox ", checkbox_name);
@@ -297,26 +305,22 @@ bool GodotGUIBuilder::generate_checkbox(
ret = false;
}
- result = godot_checkbox;
+ args.result = godot_checkbox;
return ret;
}
-bool GodotGUIBuilder::generate_text(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::Text const& text = static_cast<GUI::Text const&>(element);
+static bool generate_text(generate_gui_args_t&& args) {
+ GUI::Text const& text = static_cast<GUI::Text const&>(args.element);
- result = nullptr;
const String text_name = std_view_to_godot_string(text.get_name());
- Label* godot_label = new_control<Label>(text, name);
+ Label* godot_label = new_control<Label>(text, args.name);
ERR_FAIL_NULL_V_MSG(godot_label, false, vformat("Failed to create Label for GUI text %s", text_name));
godot_label->set_text(std_view_to_godot_string(text.get_text()));
- godot_label->set_size(Utilities::to_godot_fvec2(text.get_max_size()));
+ godot_label->set_custom_minimum_size(Utilities::to_godot_fvec2(text.get_max_size()));
using enum GUI::AlignedElement::format_t;
- using enum HorizontalAlignment;
static const std::map<GUI::AlignedElement::format_t, HorizontalAlignment> format_map {
{ left, HORIZONTAL_ALIGNMENT_LEFT },
{ centre, HORIZONTAL_ALIGNMENT_CENTER },
@@ -333,7 +337,7 @@ bool GodotGUIBuilder::generate_text(
bool ret = true;
if (text.get_font() != nullptr) {
const StringName font_file = std_view_to_godot_string_name(text.get_font()->get_fontname());
- const Ref<Font> font = asset_manager.get_font(font_file);
+ const Ref<Font> font = args.asset_manager.get_font(font_file);
if (font.is_valid()) {
godot_label->add_theme_font_override("font", font);
} else {
@@ -344,67 +348,59 @@ bool GodotGUIBuilder::generate_text(
godot_label->add_theme_color_override("font_color", colour);
}
- result = godot_label;
+ args.result = godot_label;
return ret;
}
-bool GodotGUIBuilder::generate_overlapping_elements(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(element);
+static bool generate_overlapping_elements(generate_gui_args_t&& args) {
+ GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(args.element);
- result = nullptr;
const String overlapping_elements_name = std_view_to_godot_string(overlapping_elements.get_name());
- ColorRect* godot_rect = new_control<ColorRect>(overlapping_elements, name);
+ GUIOverlappingElementsBox* box = new_control<GUIOverlappingElementsBox>(overlapping_elements, args.name);
ERR_FAIL_NULL_V_MSG(
- godot_rect, false, vformat("Failed to create ColorRect for GUI overlapping elements %s", overlapping_elements_name)
+ box, false,
+ vformat("Failed to create GUIOverlappingElementsBox for GUI overlapping elements %s", overlapping_elements_name)
);
-
- godot_rect->set_size(Utilities::to_godot_fvec2(overlapping_elements.get_size()));
- godot_rect->set_color({ 0.0f, 0.5f, 1.0f, 0.2f });
-
- result = godot_rect;
- return true;
+ const bool ret = box->set_gui_overlapping_elements_box(&overlapping_elements) == OK;
+ args.result = box;
+ return ret;
}
-bool GodotGUIBuilder::generate_listbox(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(element);
+static bool generate_listbox(generate_gui_args_t&& args) {
+ GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(args.element);
- result = nullptr;
const String listbox_name = std_view_to_godot_string(listbox.get_name());
- ColorRect* godot_rect = new_control<ColorRect>(listbox, name);
+ ColorRect* godot_rect = new_control<ColorRect>(listbox, args.name);
ERR_FAIL_NULL_V_MSG(godot_rect, false, vformat("Failed to create ColorRect for GUI listbox %s", listbox_name));
- godot_rect->set_size(Utilities::to_godot_fvec2(listbox.get_size()));
+ godot_rect->set_custom_minimum_size(Utilities::to_godot_fvec2(listbox.get_size()));
godot_rect->set_color({ 1.0f, 0.5f, 0.0f, 0.2f });
- result = godot_rect;
+ args.result = godot_rect;
return true;
}
-bool GodotGUIBuilder::generate_window(
- GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
-) {
- GUI::Window const& window = static_cast<GUI::Window const&>(element);
+/* Forward declaration for use in generate_window. */
+static bool generate_element(GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result);
+
+static bool generate_window(generate_gui_args_t&& args) {
+ GUI::Window const& window = static_cast<GUI::Window const&>(args.element);
// TODO - moveable, fullscreen, dontRender (disable visibility?)
- result = nullptr;
const String window_name = std_view_to_godot_string(window.get_name());
- Panel* godot_panel = new_control<Panel>(window, name);
+ Panel* godot_panel = new_control<Panel>(window, args.name);
ERR_FAIL_NULL_V_MSG(godot_panel, false, vformat("Failed to create Panel for GUI window %s", window_name));
- godot_panel->set_size(Utilities::to_godot_fvec2(window.get_size()));
+ godot_panel->set_custom_minimum_size(Utilities::to_godot_fvec2(window.get_size()));
godot_panel->set_self_modulate({ 1.0f, 1.0f, 1.0f, 0.0f });
bool ret = true;
for (std::unique_ptr<GUI::Element> const& element : window.get_window_elements()) {
Control* node = nullptr;
- const bool element_ret = generate_element(element.get(), "", asset_manager, node);
+ const bool element_ret = generate_element(element.get(), "", args.asset_manager, node);
if (node != nullptr) {
godot_panel->add_child(node);
}
@@ -414,6 +410,41 @@ bool GodotGUIBuilder::generate_window(
}
}
- result = godot_panel;
+ args.result = godot_panel;
return ret;
}
+
+static bool generate_element(GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result) {
+ ERR_FAIL_NULL_V(element, false);
+ static const std::map<std::string_view, bool (*)(generate_gui_args_t&&)> type_map {
+ { GUI::Icon::get_type_static(), &generate_icon },
+ { GUI::Button::get_type_static(), &generate_button },
+ { GUI::Checkbox::get_type_static(), &generate_checkbox },
+ { GUI::Text::get_type_static(), &generate_text },
+ { GUI::OverlappingElementsBox::get_type_static(), &generate_overlapping_elements },
+ { GUI::ListBox::get_type_static(), &generate_listbox },
+ { GUI::Window::get_type_static(), &generate_window }
+ };
+ const decltype(type_map)::const_iterator it = type_map.find(element->get_type());
+ if (it != type_map.end()) {
+ return it->second({ *element, name, asset_manager, result });
+ } else {
+ UtilityFunctions::push_error("Invalid GUI element type: ", std_view_to_godot_string(element->get_type()));
+ return false;
+ }
+}
+
+bool UITools::generate_gui_element(
+ GUI::Element const* element, String const& name, Control*& result
+) {
+ result = nullptr;
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V(asset_manager, false);
+ return generate_element(element, name, *asset_manager, result);
+}
+
+bool UITools::generate_gui_element(
+ godot::String const& gui_file, godot::String const& gui_element, godot::String const& name, godot::Control*& result
+) {
+ return generate_gui_element(get_gui_element(gui_file, gui_element), name, result);
+}
diff --git a/extension/src/openvic-extension/utility/UITools.hpp b/extension/src/openvic-extension/utility/UITools.hpp
new file mode 100644
index 0000000..65cf17a
--- /dev/null
+++ b/extension/src/openvic-extension/utility/UITools.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <godot_cpp/classes/control.hpp>
+
+#include <openvic-simulation/interface/GFX.hpp>
+#include <openvic-simulation/interface/GUI.hpp>
+
+namespace OpenVic::UITools {
+ GFX::Sprite const* get_gfx_sprite(godot::String const& gfx_sprite);
+ GUI::Element const* get_gui_element(godot::String const& gui_file, godot::String const& gui_element);
+
+ bool generate_gui_element(
+ GUI::Element const* element, godot::String const& name, godot::Control*& result
+ );
+ bool generate_gui_element(
+ godot::String const& gui_file, godot::String const& gui_element, godot::String const& name, godot::Control*& result
+ );
+}
diff --git a/extension/src/openvic-extension/utility/Utilities.cpp b/extension/src/openvic-extension/utility/Utilities.cpp
index 099b5a9..8293e70 100644
--- a/extension/src/openvic-extension/utility/Utilities.cpp
+++ b/extension/src/openvic-extension/utility/Utilities.cpp
@@ -122,3 +122,18 @@ Ref<FontFile> Utilities::load_godot_font(String const& fnt_path, Ref<Image> cons
}
return font;
}
+
+Ref<Image> Utilities::make_solid_colour_image(Color const& colour, int32_t width, int32_t height, Image::Format format) {
+ const Ref<Image> result = Image::create(width, height, false, format);
+ ERR_FAIL_NULL_V(result, nullptr);
+ result->fill(colour);
+ return result;
+}
+
+Ref<ImageTexture> Utilities::make_solid_colour_texture(Color const& colour, int32_t width, int32_t height, Image::Format format) {
+ const Ref<Image> image = make_solid_colour_image(colour, width, height, format);
+ ERR_FAIL_NULL_V(image, nullptr);
+ const Ref<ImageTexture> result = ImageTexture::create_from_image(image);
+ ERR_FAIL_NULL_V(result, nullptr);
+ return result;
+}
diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp
index 6eeb285..9bbc700 100644
--- a/extension/src/openvic-extension/utility/Utilities.hpp
+++ b/extension/src/openvic-extension/utility/Utilities.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <godot_cpp/classes/font_file.hpp>
-#include <godot_cpp/classes/image.hpp>
+#include <godot_cpp/classes/image_texture.hpp>
#include <openvic-simulation/types/Colour.hpp>
#include <openvic-simulation/types/Date.hpp>
@@ -37,12 +37,8 @@ namespace OpenVic::Utilities {
godot::String date_to_formatted_string(Date date);
- inline godot::Color to_godot_color(colour_t colour) {
- return {
- colour_byte_to_float((colour >> 16) & 0xFF),
- colour_byte_to_float((colour >> 8) & 0xFF),
- colour_byte_to_float(colour & 0xFF)
- };
+ inline godot::Color to_godot_color(IsColour auto colour) {
+ return { colour.redf(), colour.greenf(), colour.bluef(), colour.alphaf() };
}
inline godot::Vector2i to_godot_ivec2(ivec2_t vec) {
@@ -63,4 +59,14 @@ namespace OpenVic::Utilities {
/* Load a Font from anywhere on the machine, combining the ".fnt" file loaded from the given path with the
* already-loaded image file containing the actual characters. */
godot::Ref<godot::FontFile> load_godot_font(godot::String const& fnt_path, godot::Ref<godot::Image> const& image);
+
+ godot::Ref<godot::Image> make_solid_colour_image(
+ godot::Color const& colour, int32_t width, int32_t height,
+ godot::Image::Format format = godot::Image::Format::FORMAT_RGBA8
+ );
+
+ godot::Ref<godot::ImageTexture> make_solid_colour_texture(
+ godot::Color const& colour, int32_t width, int32_t height,
+ godot::Image::Format format = godot::Image::Format::FORMAT_RGBA8
+ );
}