aboutsummaryrefslogtreecommitdiff
path: root/extension/src
diff options
context:
space:
mode:
author Hop311 <Hop3114@gmail.com>2024-03-04 23:49:37 +0100
committer GitHub <noreply@github.com>2024-03-04 23:49:37 +0100
commit521f7d3d156f42535fb7574fb36f2726e9d13885 (patch)
tree8c319b540020aaf382592c9b513a27e9fcaaa603 /extension/src
parent9e305db5e5090a1a24979c480d64eebfe2de65da (diff)
parent9ee1940ac3d15aa4c0a87b84d1c4ab8958184f63 (diff)
Merge pull request #210 from OpenVicProject/gui-listbox
Add GUIListBox + UI improvements
Diffstat (limited to 'extension/src')
-rw-r--r--extension/src/openvic-extension/classes/GUIListBox.cpp226
-rw-r--r--extension/src/openvic-extension/classes/GUIListBox.hpp58
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp9
-rw-r--r--extension/src/openvic-extension/classes/GUINode.hpp4
-rw-r--r--extension/src/openvic-extension/classes/GUIScrollbar.cpp68
-rw-r--r--extension/src/openvic-extension/classes/GUIScrollbar.hpp8
-rw-r--r--extension/src/openvic-extension/register_types.cpp2
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.cpp17
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.hpp6
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.cpp8
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.hpp9
-rw-r--r--extension/src/openvic-extension/utility/UITools.cpp48
12 files changed, 406 insertions, 57 deletions
diff --git a/extension/src/openvic-extension/classes/GUIListBox.cpp b/extension/src/openvic-extension/classes/GUIListBox.cpp
new file mode 100644
index 0000000..f7b8d88
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUIListBox.cpp
@@ -0,0 +1,226 @@
+#include "GUIListBox.hpp"
+
+#include <godot_cpp/classes/input_event_mouse_button.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 GUIListBox::_calculate_child_arrangement() {
+ ERR_FAIL_NULL_V(gui_listbox, FAILED);
+
+ const int32_t child_count = get_child_count();
+ const real_t max_height = get_size().height;
+
+ real_t height = 0.0f, height_under_max_scroll_index = 0.0f;
+
+ children_data.clear();
+ max_scroll_index = 0;
+
+ for (int32_t index = 0; index < child_count; ++index) {
+ Control* child = Object::cast_to<Control>(get_child(index));
+ if (child != nullptr && child != scrollbar && child->is_visible()) {
+ const real_t child_height = child->get_size().height; /* Spacing is ignored */
+ children_data.push_back({ child, height, child_height });
+
+ height += child_height;
+ height_under_max_scroll_index += child_height;
+
+ while (height_under_max_scroll_index > max_height && max_scroll_index + 1 < children_data.size()) {
+ height_under_max_scroll_index -= children_data[max_scroll_index++].height;
+ }
+ }
+ }
+
+ ERR_FAIL_NULL_V(scrollbar, FAILED);
+
+ scrollbar->set_limits(0, max_scroll_index, false);
+ scrollbar->emit_value_changed();
+ scrollbar->set_visible(max_scroll_index > 0);
+
+ return OK;
+}
+
+Error GUIListBox::_update_child_positions() {
+ ERR_FAIL_NULL_V(gui_listbox, FAILED);
+
+ if (children_data.empty()) {
+ return OK;
+ }
+
+ const real_t max_height = get_size().height;
+ const real_t scroll_pos = children_data[scroll_index].start_pos;
+
+ for (int32_t index = 0; index < children_data.size(); ++index) {
+ child_data_t const& data = children_data[index];
+ if (index < scroll_index || data.start_pos + data.height > scroll_pos + max_height) {
+ data.child->set_position({ 0.0f, max_height + 10.0f });
+ } else {
+ data.child->set_position({ 0.0f, data.start_pos - scroll_pos });
+ }
+ }
+
+ return OK;
+}
+
+void GUIListBox::_bind_methods() {
+ OV_BIND_METHOD(GUIListBox::clear);
+ OV_BIND_METHOD(GUIListBox::clear_children);
+
+ OV_BIND_METHOD(GUIListBox::get_scroll_index);
+ OV_BIND_METHOD(GUIListBox::set_scroll_index, { "new_scroll_index" });
+ OV_BIND_METHOD(GUIListBox::get_max_scroll_index);
+
+ OV_BIND_METHOD(GUIListBox::get_gui_listbox_name);
+ OV_BIND_METHOD(GUIListBox::get_scrollbar);
+}
+
+void GUIListBox::_notification(int what) {
+ switch (what) {
+ case NOTIFICATION_SORT_CHILDREN: {
+ _calculate_child_arrangement();
+ } break;
+ }
+}
+
+GUIListBox::GUIListBox()
+ : gui_listbox { nullptr }, scrollbar { nullptr }, children_data {}, scroll_index { 0 }, max_scroll_index { 0 } {
+ set_clip_contents(true);
+}
+
+Vector2 GUIListBox::_get_minimum_size() const {
+ if (gui_listbox != nullptr) {
+ Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size());
+
+ if (scrollbar != nullptr) {
+ size.width += scrollbar->get_minimum_size().width;
+ }
+
+ return size;
+ } else {
+ return {};
+ }
+}
+
+void GUIListBox::_gui_input(godot::Ref<godot::InputEvent> const& event) {
+ ERR_FAIL_NULL(event);
+
+ Ref<InputEventMouseButton> mb = event;
+
+ if (mb.is_valid()) {
+ if (mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_UP) {
+ set_scroll_index(scroll_index - 1);
+ } else if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_DOWN) {
+ set_scroll_index(scroll_index + 1);
+ } else {
+ return;
+ }
+ accept_event();
+ }
+ }
+}
+
+void GUIListBox::clear() {
+ gui_listbox = nullptr;
+ children_data.clear();
+ scroll_index = 0;
+ max_scroll_index = 0;
+ clear_children();
+ if (scrollbar != nullptr) {
+ remove_child(scrollbar);
+ scrollbar = nullptr;
+ }
+}
+
+void GUIListBox::clear_children() {
+ int32_t child_count = get_child_count();
+ while (child_count > 0) {
+ Node* child = get_child(--child_count);
+ if (child != scrollbar) {
+ remove_child(child);
+ }
+ }
+ if (scrollbar != nullptr) {
+ scrollbar->set_value(0);
+ }
+}
+
+void GUIListBox::set_scroll_index(int32_t new_scroll_index) {
+ scroll_index = std::clamp(new_scroll_index, 0, max_scroll_index);
+ if (scrollbar != nullptr && scrollbar->get_value() != scroll_index) {
+ scrollbar->set_value(scroll_index, false);
+ }
+ _update_child_positions();
+}
+
+Error GUIListBox::set_gui_listbox(GUI::ListBox const* new_gui_listbox) {
+ if (gui_listbox == new_gui_listbox) {
+ return OK;
+ }
+
+ if (new_gui_listbox == nullptr) {
+ clear();
+ return OK;
+ }
+
+ gui_listbox = new_gui_listbox;
+
+ const String scrollbar_name = std_view_to_godot_string(gui_listbox->get_scrollbar_name());
+
+ Error err = OK;
+
+ if (scrollbar_name.is_empty()) {
+ UtilityFunctions::push_error("GUIListBox ", get_name(), " has no scrollbar name!");
+ err = FAILED;
+ scrollbar = nullptr;
+ } else {
+ static const String scrollbar_scene = "core";
+
+ Control* scrollbar_control = nullptr;
+ if (!UITools::generate_gui_element(scrollbar_scene, scrollbar_name, "", scrollbar_control)) {
+ UtilityFunctions::push_error("Error generating scrollbar ", scrollbar_name, " for GUIListBox ", get_name());
+ err = FAILED;
+ }
+
+ if (scrollbar_control != nullptr) {
+ scrollbar = Object::cast_to<GUIScrollbar>(scrollbar_control);
+ if (scrollbar != nullptr) {
+ add_child(scrollbar);
+
+ const Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size());
+ scrollbar->set_position({ size.width, 0.0f });
+ scrollbar->set_length_override(size.height);
+
+ static const StringName set_scroll_index_func_name = "set_scroll_index";
+
+ scrollbar->connect(
+ GUIScrollbar::signal_value_changed(), Callable { this, set_scroll_index_func_name }, CONNECT_PERSIST
+ );
+ } else {
+ UtilityFunctions::push_error(
+ "Element ", scrollbar_name, " for GUIListBox ", get_name(), " is not a GUIScrollbar"
+ );
+ err = FAILED;
+ }
+ } else {
+ scrollbar = nullptr;
+ }
+ }
+
+ return err;
+}
+
+String GUIListBox::get_gui_listbox_name() const {
+ return gui_listbox != nullptr ? std_view_to_godot_string(gui_listbox->get_name()) : String {};
+}
+
+GUIScrollbar* GUIListBox::get_scrollbar() const {
+ return scrollbar;
+}
diff --git a/extension/src/openvic-extension/classes/GUIListBox.hpp b/extension/src/openvic-extension/classes/GUIListBox.hpp
new file mode 100644
index 0000000..9d49840
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUIListBox.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <godot_cpp/classes/container.hpp>
+
+#include <openvic-simulation/interface/GUI.hpp>
+
+#include "openvic-extension/classes/GUIScrollbar.hpp"
+
+namespace OpenVic {
+ class GUIListBox : public godot::Container {
+ GDCLASS(GUIListBox, godot::Container)
+
+ GUI::ListBox const* PROPERTY(gui_listbox);
+
+ GUIScrollbar* scrollbar;
+
+ struct child_data_t {
+ Control* child;
+ real_t start_pos, height;
+ };
+
+ std::vector<child_data_t> children_data;
+
+ /* The children_data index of the topmost visible child element. */
+ int32_t PROPERTY(scroll_index);
+ int32_t PROPERTY(max_scroll_index);
+
+ godot::Error _calculate_child_arrangement();
+ godot::Error _update_child_positions();
+
+ protected:
+ static void _bind_methods();
+
+ void _notification(int what);
+
+ public:
+ GUIListBox();
+
+ godot::Vector2 _get_minimum_size() const override;
+ void _gui_input(godot::Ref<godot::InputEvent> const& event) override;
+
+ /* Reset gui_listbox to nullptr, and remove all child elements. */
+ void clear();
+
+ /* Remove all child elements except for the scrollbar. */
+ void clear_children();
+
+ void set_scroll_index(int32_t new_scroll_index);
+
+ /* Set the GUI::ListBox. This does not affect any existing child elements. */
+ godot::Error set_gui_listbox(GUI::ListBox const* new_gui_listbox);
+
+ /* Return the name of the GUI::ListBox, or an empty String if it's null. */
+ godot::String get_gui_listbox_name() const;
+
+ GUIScrollbar* get_scrollbar() const;
+ };
+}
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
index 5296ad7..73ebb0c 100644
--- a/extension/src/openvic-extension/classes/GUINode.cpp
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -18,7 +18,8 @@ using namespace OpenVic;
F(TextureProgressBar, progress_bar) \
F(TextureRect, texture_rect) \
F(GUIOverlappingElementsBox, gui_overlapping_elements_box) \
- F(GUIScrollbar, gui_scrollbar)
+ F(GUIScrollbar, gui_scrollbar) \
+ F(GUIListBox, gui_listbox)
#define APPLY_TO_TEXTURE_TYPES(F) \
F(GFXSpriteTexture, gfx_sprite_texture) \
@@ -48,6 +49,7 @@ void GUINode::_bind_methods() {
OV_BIND_SMETHOD(int_to_formatted_string, { "val" });
OV_BIND_SMETHOD(float_to_formatted_string, { "val", "decimal_places" });
+ OV_BIND_SMETHOD(format_province_name, { "province_identifier" });
}
GUINode::GUINode() {
@@ -189,3 +191,8 @@ String GUINode::int_to_formatted_string(int64_t val) {
String GUINode::float_to_formatted_string(float val, int32_t decimal_places) {
return Utilities::float_to_formatted_string(val, decimal_places);
}
+
+String GUINode::format_province_name(String const& province_identifier) {
+ static const String province_prefix = "PROV";
+ return province_prefix + province_identifier;
+}
diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp
index f935683..3dbe403 100644
--- a/extension/src/openvic-extension/classes/GUINode.hpp
+++ b/extension/src/openvic-extension/classes/GUINode.hpp
@@ -12,6 +12,7 @@
#include "openvic-extension/classes/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
@@ -43,6 +44,7 @@ namespace OpenVic {
static godot::TextureRect* get_texture_rect_from_node(godot::Node* node);
static GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_node(godot::Node* node);
static GUIScrollbar* get_gui_scrollbar_from_node(godot::Node* node);
+ static GUIListBox* get_gui_listbox_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;
@@ -52,6 +54,7 @@ namespace OpenVic {
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;
GUIScrollbar* get_gui_scrollbar_from_nodepath(godot::NodePath const& path) const;
+ GUIListBox* get_gui_listbox_from_nodepath(godot::NodePath const& path) const;
/* Helper functions to get textures from TextureRects and Buttons. */
static godot::Ref<godot::Texture2D> get_texture_from_node(godot::Node* node);
@@ -69,5 +72,6 @@ namespace OpenVic {
static godot::String int_to_formatted_string(int64_t val);
static godot::String float_to_formatted_string(float val, int32_t decimal_places);
+ static godot::String format_province_name(godot::String const& province_identifier);
};
}
diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.cpp b/extension/src/openvic-extension/classes/GUIScrollbar.cpp
index 0f3cde1..93eb00b 100644
--- a/extension/src/openvic-extension/classes/GUIScrollbar.cpp
+++ b/extension/src/openvic-extension/classes/GUIScrollbar.cpp
@@ -16,7 +16,7 @@ using OpenVic::Utilities::std_view_to_godot_string;
/* StringNames cannot be constructed until Godot has called StringName::setup(),
* so we must use wrapper functions to delay their initialisation. */
-StringName const& GUIScrollbar::_signal_value_changed() {
+StringName const& GUIScrollbar::signal_value_changed() {
static const StringName signal_value_changed = "value_changed";
return signal_value_changed;
}
@@ -44,7 +44,10 @@ void GUIScrollbar::_bind_methods() {
OV_BIND_METHOD(GUIScrollbar::set_range_limits, { "new_range_limit_min", "new_range_limit_max", "signal" }, DEFVAL(true));
OV_BIND_METHOD(GUIScrollbar::set_limits, { "new_min_value", "new_max_value", "signal" }, DEFVAL(true));
- ADD_SIGNAL(MethodInfo(_signal_value_changed(), PropertyInfo(Variant::INT, "value")));
+ OV_BIND_METHOD(GUIScrollbar::get_length_override);
+ OV_BIND_METHOD(GUIScrollbar::set_length_override, { "new_length_override" });
+
+ ADD_SIGNAL(MethodInfo(signal_value_changed(), PropertyInfo(Variant::INT, "value")));
}
GUIScrollbar::GUIScrollbar() {
@@ -220,24 +223,30 @@ void GUIScrollbar::_constrain_value() {
/* _constrain_value() should be called sometime after this. */
Error GUIScrollbar::_constrain_range_limits() {
- range_limit_min = std::clamp(range_limit_min, min_value, max_value);
- range_limit_max = std::clamp(range_limit_max, min_value, max_value);
-
- Error err = OK;
- if (range_limit_min > range_limit_max) {
- UtilityFunctions::push_error(
- "GUIScrollbar range max ", range_limit_max, " is less than range min ", range_limit_min, " - swapping values."
- );
- std::swap(range_limit_min, range_limit_max);
- err = FAILED;
- }
+ if (range_limited) {
+ range_limit_min = std::clamp(range_limit_min, min_value, max_value);
+ range_limit_max = std::clamp(range_limit_max, min_value, max_value);
+
+ Error err = OK;
+ if (range_limit_min > range_limit_max) {
+ UtilityFunctions::push_error(
+ "GUIScrollbar range max ", range_limit_max, " is less than range min ", range_limit_min, " - swapping values."
+ );
+ std::swap(range_limit_min, range_limit_max);
+ err = FAILED;
+ }
- const int axis = orientation == HORIZONTAL ? 0 : 1;
- range_limit_min_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_min);
- range_limit_max_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_max)
- + slider_rect.size[axis] / 2.0f;
+ const int axis = orientation == HORIZONTAL ? 0 : 1;
+ range_limit_min_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_min);
+ range_limit_max_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_max)
+ + slider_rect.size[axis] / 2.0f;
- return err;
+ return err;
+ } else {
+ range_limit_min = min_value;
+ range_limit_max = max_value;
+ return OK;
+ }
}
/* _constrain_range_limits() should be called sometime after this. */
@@ -265,6 +274,10 @@ Vector2 GUIScrollbar::_get_minimum_size() const {
size[axis] = std::max(size[axis], more_texture->get_size()[axis]);
}
+ if (length_override > 0.0f) {
+ size[1 - axis] = length_override;
+ }
+
return size;
} else {
return {};
@@ -272,7 +285,7 @@ Vector2 GUIScrollbar::_get_minimum_size() const {
}
void GUIScrollbar::emit_value_changed() {
- emit_signal(_signal_value_changed(), value);
+ emit_signal(signal_value_changed(), value);
}
Error GUIScrollbar::reset() {
@@ -291,7 +304,7 @@ Error GUIScrollbar::reset() {
pressed_less = false;
pressed_more = false;
- value = (max_value - min_value) / 2;
+ value = min_value;
range_limit_min = min_value;
range_limit_max = max_value;
@@ -321,6 +334,7 @@ void GUIScrollbar::clear() {
range_limit_max_rect = {};
orientation = HORIZONTAL;
+ length_override = 0.0f;
min_value = 0;
max_value = 100;
range_limited = false;
@@ -347,6 +361,7 @@ Error GUIScrollbar::set_gui_scrollbar(GUI::Scrollbar const* new_gui_scrollbar) {
const String gui_scrollbar_name = std_view_to_godot_string(gui_scrollbar->get_name());
orientation = gui_scrollbar->is_horizontal() ? HORIZONTAL : VERTICAL;
+ length_override = 0.0f;
range_limited = gui_scrollbar->is_range_limited();
/* _Element is either GUI::Button or GUI::Icon, both of which have their own
@@ -477,6 +492,19 @@ Error GUIScrollbar::set_limits(int32_t new_min_value, int32_t new_max_value, boo
return ERR(ret);
}
+void GUIScrollbar::set_length_override(real_t new_length_override) {
+ ERR_FAIL_COND_MSG(
+ length_override < 0, vformat("Invalid GUIScrollbar length override: %f - cannot be negative!", length_override)
+ );
+
+ length_override = new_length_override;
+
+ _calculate_rects();
+ _constrain_limits();
+ _constrain_range_limits();
+ _constrain_value();
+}
+
void GUIScrollbar::_gui_input(Ref<InputEvent> const& event) {
ERR_FAIL_NULL(event);
diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.hpp b/extension/src/openvic-extension/classes/GUIScrollbar.hpp
index 7af7b57..c5b476a 100644
--- a/extension/src/openvic-extension/classes/GUIScrollbar.hpp
+++ b/extension/src/openvic-extension/classes/GUIScrollbar.hpp
@@ -11,8 +11,6 @@ namespace OpenVic {
class GUIScrollbar : public godot::Control {
GDCLASS(GUIScrollbar, godot::Control)
- static godot::StringName const& _signal_value_changed();
-
GUI::Scrollbar const* PROPERTY(gui_scrollbar);
godot::Ref<GFXSpriteTexture> slider_texture;
@@ -33,6 +31,7 @@ namespace OpenVic {
godot::Rect2 range_limit_max_rect;
godot::Orientation PROPERTY(orientation);
+ real_t PROPERTY(length_override);
int32_t PROPERTY(value);
int32_t PROPERTY(min_value);
@@ -78,6 +77,8 @@ namespace OpenVic {
void _notification(int what);
public:
+ static godot::StringName const& signal_value_changed();
+
GUIScrollbar();
godot::Vector2 _get_minimum_size() const override;
@@ -98,5 +99,8 @@ namespace OpenVic {
godot::Error set_range_limits(int32_t new_range_limit_min, int32_t new_range_limit_max, bool signal = true);
godot::Error set_limits(int32_t new_min_value, int32_t new_max_value, bool signal = true);
+
+ /* Override the main dimension of gui_scollbar's size with the specified length. */
+ void set_length_override(real_t new_length_override);
};
}
diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp
index 1d503d7..750cc53 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/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUINode.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
@@ -56,6 +57,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
ClassDB::register_class<GFXMaskedFlagTexture>();
ClassDB::register_class<GFXPieChartTexture>();
+ ClassDB::register_class<GUIListBox>();
ClassDB::register_class<GUINode>();
ClassDB::register_class<GUIOverlappingElementsBox>();
ClassDB::register_class<GUIScrollbar>();
diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp
index 546dc9d..083d934 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.cpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.cpp
@@ -13,8 +13,8 @@ using OpenVic::Utilities::godot_to_std_string;
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_METHOD(AssetManager::get_image, { "path", "cache", "flip_y" }, DEFVAL(true), DEFVAL(false));
+ OV_BIND_METHOD(AssetManager::get_texture, { "path", "flip_y" }, DEFVAL(false));
OV_BIND_METHOD(AssetManager::get_font, { "name" });
}
@@ -45,19 +45,22 @@ Ref<Image> AssetManager::_load_image(StringName const& path) {
return image;
}
-AssetManager::image_asset_t* AssetManager::_get_image_asset(StringName const& path) {
+AssetManager::image_asset_t* AssetManager::_get_image_asset(StringName const& path, bool flip_y) {
image_asset_map_t::iterator it = image_assets.find(path);
if (it != image_assets.end()) {
return &it.value();
}
const Ref<Image> image = _load_image(path);
ERR_FAIL_NULL_V(image, nullptr);
+ if (flip_y) {
+ image->flip_y();
+ }
return &image_assets.emplace(std::move(path), AssetManager::image_asset_t { image, nullptr }).first.value();
}
-Ref<Image> AssetManager::get_image(StringName const& path, bool cache) {
+Ref<Image> AssetManager::get_image(StringName const& path, bool cache, bool flip_y) {
if (cache) {
- image_asset_t const* asset = _get_image_asset(path);
+ image_asset_t const* asset = _get_image_asset(path, flip_y);
ERR_FAIL_NULL_V(asset, nullptr);
return asset->image;
} else {
@@ -65,8 +68,8 @@ Ref<Image> AssetManager::get_image(StringName const& path, bool cache) {
}
}
-Ref<ImageTexture> AssetManager::get_texture(StringName const& path) {
- image_asset_t* asset = _get_image_asset(path);
+Ref<ImageTexture> AssetManager::get_texture(StringName const& path, bool flip_y) {
+ image_asset_t* asset = _get_image_asset(path, flip_y);
ERR_FAIL_NULL_V(asset, nullptr);
if (asset->texture.is_null()) {
asset->texture = ImageTexture::create_from_image(asset->image);
diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp
index 7f73e4c..0416e5b 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.hpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.hpp
@@ -25,7 +25,7 @@ namespace OpenVic {
font_map_t fonts;
static godot::Ref<godot::Image> _load_image(godot::StringName const& path);
- image_asset_t* _get_image_asset(godot::StringName const& path);
+ image_asset_t* _get_image_asset(godot::StringName const& path, bool flip_y);
protected:
static void _bind_methods();
@@ -38,12 +38,12 @@ namespace OpenVic {
/* Search for and load an image at the specified path relative to the game defines, first checking the AssetManager's
* image cache (if cache is true) in case it has already been loaded, and returning nullptr if image loading fails. */
- godot::Ref<godot::Image> get_image(godot::StringName const& path, bool cache = true);
+ godot::Ref<godot::Image> get_image(godot::StringName const& path, bool cache = true, bool flip_y = false);
/* Create a texture from an image found at the specified path relative to the game defines, fist checking
* AssetManager's texture cache in case it has already been loaded, and returning nullptr if image loading
* or texture creation fails. */
- godot::Ref<godot::ImageTexture> get_texture(godot::StringName const& path);
+ godot::Ref<godot::ImageTexture> get_texture(godot::StringName const& path, bool flip_y = false);
/* 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. */
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp
index 9b8abce..8893f75 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp
@@ -129,14 +129,6 @@ void GameSingleton::setup_logger() {
});
}
-GameManager const& GameSingleton::get_game_manager() const {
- return game_manager;
-}
-
-Dataloader const& GameSingleton::get_dataloader() const {
- return dataloader;
-}
-
Error GameSingleton::setup_game(int32_t bookmark_index) {
Bookmark const* bookmark = game_manager.get_history_manager().get_bookmark_manager().get_bookmark_by_index(bookmark_index);
ERR_FAIL_NULL_V_MSG(bookmark, FAILED, vformat("Failed to get bookmark with index: %d", bookmark_index));
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp
index 35f7437..a2b15cd 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/texture2d_array.hpp>
@@ -14,8 +13,8 @@ namespace OpenVic {
static inline GameSingleton* singleton = nullptr;
- GameManager game_manager;
- Dataloader dataloader;
+ GameManager PROPERTY(game_manager);
+ Dataloader PROPERTY(dataloader);
godot::Vector2i image_subdivisions;
godot::Ref<godot::Texture2DArray> province_shape_texture;
@@ -29,7 +28,6 @@ namespace OpenVic {
static godot::StringName const& _signal_province_selected();
static godot::StringName const& _signal_clock_state_changed();
- godot::Error _generate_terrain_texture_array();
godot::Error _load_map_images();
godot::Error _load_terrain_variants();
godot::Error _load_flag_images();
@@ -50,9 +48,6 @@ namespace OpenVic {
static void setup_logger();
- GameManager const& get_game_manager() const;
- Dataloader const& get_dataloader() const;
-
/* Load the game's defines in compatiblity mode from the filepath
* pointing to the defines folder. */
godot::Error load_defines_compatibility_mode(godot::PackedStringArray const& file_paths);
diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp
index 9cd5db0..c00b64d 100644
--- a/extension/src/openvic-extension/utility/UITools.cpp
+++ b/extension/src/openvic-extension/utility/UITools.cpp
@@ -15,6 +15,7 @@
#include "openvic-extension/classes/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
#include "openvic-extension/singletons/AssetManager.hpp"
@@ -117,6 +118,11 @@ static bool add_theme_stylebox(Control* control, StringName const& theme_name, R
stylebox.instantiate();
ERR_FAIL_NULL_V(stylebox, false);
stylebox->set_texture(texture);
+
+ static const StringName changed_signal = "changed";
+ static const StringName emit_changed_func = "emit_changed";
+ texture->connect(changed_signal, Callable { *stylebox, emit_changed_func }, Object::CONNECT_PERSIST);
+
control->add_theme_stylebox_override(theme_name, stylebox);
return true;
};
@@ -145,6 +151,9 @@ static bool generate_icon(generate_gui_args_t&& args) {
ret = false;
}
+ const float scale = icon.get_scale();
+ godot_texture_rect->set_scale({ scale, scale });
+
args.result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) {
TextureRect* godot_texture_rect = nullptr;
@@ -168,12 +177,15 @@ static bool generate_icon(generate_gui_args_t&& args) {
godot_progress_bar, false, vformat("Failed to create TextureProgressBar for GUI icon %s", icon_name)
);
+ godot_progress_bar->set_nine_patch_stretch(true);
+ godot_progress_bar->set_max(1.0);
+
GFX::ProgressBar const* progress_bar = icon.get_sprite()->cast_to<GFX::ProgressBar>();
Ref<ImageTexture> back_texture;
if (!progress_bar->get_back_texture_file().empty()) {
const StringName back_texture_file = std_view_to_godot_string_name(progress_bar->get_back_texture_file());
- back_texture = args.asset_manager.get_texture(back_texture_file);
+ back_texture = args.asset_manager.get_texture(back_texture_file, true);
if (back_texture.is_null()) {
UtilityFunctions::push_error(
"Failed to load progress bar sprite back texture ", back_texture_file, " for GUI icon ", icon_name
@@ -205,7 +217,7 @@ static bool generate_icon(generate_gui_args_t&& args) {
Ref<ImageTexture> progress_texture;
if (!progress_bar->get_progress_texture_file().empty()) {
const StringName progress_texture_file = std_view_to_godot_string_name(progress_bar->get_progress_texture_file());
- progress_texture = args.asset_manager.get_texture(progress_texture_file);
+ progress_texture = args.asset_manager.get_texture(progress_texture_file, true);
if (progress_texture.is_null()) {
UtilityFunctions::push_error(
"Failed to load progress bar sprite progress texture ", progress_texture_file, " for GUI icon ", icon_name
@@ -266,9 +278,13 @@ static bool generate_icon(generate_gui_args_t&& args) {
}
if (args.result != nullptr) {
- const float scale = icon.get_scale();
- args.result->set_scale({ scale, scale });
- // TODO - rotation (may have to translate as godot rotates around the top left corner)
+ const float rotation = icon.get_rotation();
+ if (rotation != 0.0f) {
+ args.result->set_position(
+ args.result->get_position() - args.result->get_custom_minimum_size().height * Vector2 { sin(rotation), cos(rotation) - 1.0f }
+ );
+ args.result->set_rotation(-rotation);
+ }
}
} else {
UtilityFunctions::push_error("Null sprite for GUI icon ", icon_name);
@@ -470,6 +486,24 @@ static bool generate_overlapping_elements(generate_gui_args_t&& args) {
return ret;
}
+static bool generate_listbox(generate_gui_args_t&& args) {
+ GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(args.element);
+
+ const String listbox_name = std_view_to_godot_string(listbox.get_name());
+
+ GUIListBox* gui_listbox = nullptr;
+ bool ret = new_control(gui_listbox, listbox, args.name);
+ ERR_FAIL_NULL_V_MSG(gui_listbox, false, vformat("Failed to create GUIListBox for GUI listbox %s", listbox_name));
+
+ if (gui_listbox->set_gui_listbox(&listbox) != OK) {
+ UtilityFunctions::push_error("Error initialising GUIListBox for GUI listbox ", listbox_name);
+ ret = false;
+ }
+
+ args.result = gui_listbox;
+ return ret;
+}
+
template<std::derived_from<GUI::Element> T>
requires requires(T const& element) {
{ element.get_size() } -> std::same_as<fvec2_t>;
@@ -498,10 +532,6 @@ static bool generate_placeholder(generate_gui_args_t&& args, Color colour) {
return ret;
}
-static bool generate_listbox(generate_gui_args_t&& args) {
- return generate_placeholder<GUI::ListBox>(std::move(args), { 0.0f, 0.0f, 1.0f, 0.3f });
-}
-
static bool generate_texteditbox(generate_gui_args_t&& args) {
return generate_placeholder<GUI::TextEditBox>(std::move(args), { 0.0f, 1.0f, 0.0f, 0.3f });
}