aboutsummaryrefslogtreecommitdiff
path: root/extension/src/openvic-extension/UIAdapter.cpp
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2023-11-08 23:24:21 +0100
committer hop311 <hop3114@gmail.com>2023-11-16 00:24:28 +0100
commitbc0b3c61ae0b742da304cada451fba1df72bb0ad (patch)
tree047968e7ea8189ad8391dcabbd09fee1d8cdc30c /extension/src/openvic-extension/UIAdapter.cpp
parent72d893d55d26ae9dc6739a853d1773b3cb286123 (diff)
GUI elements -> Godot UI nodes generator
Diffstat (limited to 'extension/src/openvic-extension/UIAdapter.cpp')
-rw-r--r--extension/src/openvic-extension/UIAdapter.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/extension/src/openvic-extension/UIAdapter.cpp b/extension/src/openvic-extension/UIAdapter.cpp
new file mode 100644
index 0000000..5b889d6
--- /dev/null
+++ b/extension/src/openvic-extension/UIAdapter.cpp
@@ -0,0 +1,404 @@
+#include "UIAdapter.hpp"
+
+#include <godot_cpp/classes/button.hpp>
+#include <godot_cpp/classes/check_box.hpp>
+#include <godot_cpp/classes/color_rect.hpp>
+#include <godot_cpp/classes/label.hpp>
+#include <godot_cpp/classes/panel.hpp>
+#include <godot_cpp/classes/style_box_texture.hpp>
+#include <godot_cpp/classes/texture_progress_bar.hpp>
+#include <godot_cpp/classes/texture_rect.hpp>
+#include <godot_cpp/classes/theme.hpp>
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/classes/GFXIconTexture.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace godot;
+using namespace OpenVic;
+
+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, 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&, 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 }
+ };
+ const decltype(type_map)::const_iterator it = type_map.find(element->get_type());
+ if (it != type_map.end()) {
+ return it->second(*element, 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>
+static T* new_control(GUI::Element const& element) {
+ T* node = memnew(T);
+ ERR_FAIL_NULL_V(node, nullptr);
+
+ using enum GUI::Element::orientation_t;
+ using enum Control::LayoutPreset;
+ static const std::map<GUI::Element::orientation_t, Control::LayoutPreset> orientation_map {
+ { UPPER_LEFT, PRESET_TOP_LEFT }, { LOWER_LEFT, PRESET_BOTTOM_LEFT },
+ { LOWER_RIGHT, PRESET_BOTTOM_RIGHT }, { UPPER_RIGHT, PRESET_TOP_RIGHT },
+ { CENTER, PRESET_CENTER }
+ };
+
+ node->set_name(std_view_to_godot_string(element.get_name()));
+ const decltype(orientation_map)::const_iterator it = orientation_map.find(element.get_orientation());
+ if (it != orientation_map.end()) {
+ node->set_anchors_and_offsets_preset(it->second);
+ } else {
+ UtilityFunctions::push_error("Invalid orientation for GUI element ",
+ std_view_to_godot_string(element.get_name()));
+ }
+ node->set_position(Utilities::to_godot_ivec2(element.get_position()));
+ node->set_focus_mode(Control::FOCUS_NONE);
+
+ return node;
+}
+
+bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::Icon const& icon = static_cast<GUI::Icon const&>(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);
+ if (godot_texture_rect == nullptr) {
+ UtilityFunctions::push_error("Failed to create TextureRect for GUI icon ", icon_name);
+ return false;
+ }
+
+ GFX::TextureSprite const* texture_sprite = icon.get_sprite()->cast_to<GFX::TextureSprite>();
+ Ref<GFXIconTexture> texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, icon.get_frame());
+ if (texture.is_valid()) {
+ godot_texture_rect->set_texture(texture);
+ } else {
+ UtilityFunctions::push_error("Failed to make GFXIconTexture for GUI icon ", icon_name);
+ ret = false;
+ }
+
+ result = godot_texture_rect;
+ } else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) {
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon);
+ if (godot_texture_rect == nullptr) {
+ UtilityFunctions::push_error("Failed to create TextureRect for GUI icon ", icon_name);
+ return false;
+ }
+
+ const StringName texture_file =
+ std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::MaskedFlag>()->get_texture_file());
+ const Ref<ImageTexture> texture = asset_manager.get_texture(texture_file);
+ if (texture.is_valid()) {
+ godot_texture_rect->set_texture(texture);
+ } else {
+ UtilityFunctions::push_error("Failed to load masked flag sprite ", texture_file, " for GUI icon ", icon_name);
+ ret = false;
+ }
+
+ result = godot_texture_rect;
+ } else if (icon.get_sprite()->is_type<GFX::ProgressBar>()) {
+ TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon);
+ if (godot_progress_bar == nullptr) {
+ UtilityFunctions::push_error("Failed to create TextureProgressBar for GUI icon ", icon_name);
+ return false;
+ }
+
+ 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);
+ if (back_texture.is_valid()) {
+ godot_progress_bar->set_under_texture(back_texture);
+ } else {
+ UtilityFunctions::push_error("Failed to load progress bar base sprite ", back_texture_file, " for GUI icon ", icon_name);
+ ret = false;
+ }
+
+ 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);
+ if (progress_texture.is_valid()) {
+ godot_progress_bar->set_progress_texture(progress_texture);
+ } else {
+ UtilityFunctions::push_error(
+ "Failed to load progress bar base sprite ", progress_texture_file, " for GUI icon ", icon_name
+ );
+ ret = false;
+ }
+
+ result = godot_progress_bar;
+ } else if (icon.get_sprite()->is_type<GFX::PieChart>()) {
+
+ } else if (icon.get_sprite()->is_type<GFX::LineChart>()) {
+
+ } else {
+ UtilityFunctions::push_error("Invalid sprite type ", std_view_to_godot_string(icon.get_sprite()->get_type()),
+ " for GUI icon ", icon_name);
+ ret = false;
+ }
+ } else {
+ UtilityFunctions::push_error("Null sprite for GUI icon ", icon_name);
+ ret = false;
+ }
+ return ret;
+}
+
+bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::Button const& button = static_cast<GUI::Button const&>(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);
+ if (godot_button == nullptr) {
+ UtilityFunctions::push_error("Failed to create Button for GUI button ", button_name);
+ return false;
+ }
+
+ godot_button->set_text(std_view_to_godot_string(button.get_text()));
+ //godot_button->set_flat(true);
+
+ bool ret = true;
+ if (button.get_sprite() != nullptr) {
+ Ref<Texture2D> texture;
+ if (button.get_sprite()->is_type<GFX::TextureSprite>()) {
+ GFX::TextureSprite const* texture_sprite = button.get_sprite()->cast_to<GFX::TextureSprite>();
+ texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite);
+ if (texture.is_null()) {
+ UtilityFunctions::push_error("Failed to make GFXIconTexture for GUI button ", button_name);
+ ret = false;
+ }
+ } else if (button.get_sprite()->is_type<GFX::MaskedFlag>()) {
+ texture = asset_manager.get_texture(std_view_to_godot_string_name(
+ button.get_sprite()->cast_to<GFX::MaskedFlag>()->get_texture_file()));
+ if (texture.is_null()) {
+ UtilityFunctions::push_error("Failed to load masked flag sprite for GUI button ", button_name);
+ ret = false;
+ }
+ } else {
+ UtilityFunctions::push_error("Invalid sprite type ", std_view_to_godot_string(button.get_sprite()->get_type()),
+ " for GUI button ", button_name);
+ ret = false;
+ }
+
+ if (texture.is_valid()) {
+ godot_button->set_size(texture->get_size());
+ Ref<StyleBoxTexture> stylebox;
+ stylebox.instantiate();
+ if (stylebox.is_valid()) {
+ stylebox->set_texture(texture);
+ godot_button->add_theme_stylebox_override("normal", stylebox);
+ } else {
+ UtilityFunctions::push_error("Failed to load instantiate texture stylebox for GUI button ", button_name);
+ ret = false;
+ }
+ }
+ } else {
+ UtilityFunctions::push_error("Null sprite for GUI button ", button_name);
+ ret = false;
+ }
+
+ 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);
+ if (font.is_valid()) {
+ godot_button->add_theme_font_override("font", font);
+ } else {
+ UtilityFunctions::push_error("Failed to load font for GUI button ", button_name);
+ ret = false;
+ }
+ const Color colour = Utilities::to_godot_color(button.get_font()->get_colour());
+ godot_button->add_theme_color_override("font_color", colour);
+ }
+
+ result = godot_button;
+ return ret;
+}
+
+bool GodotGUIBuilder::generate_checkbox(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(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);
+ if (godot_checkbox == nullptr) {
+ UtilityFunctions::push_error("Failed to create CheckBox for GUI checkbox ", checkbox_name);
+ return false;
+ }
+
+ bool ret = true;
+ if (checkbox.get_sprite() != nullptr) {
+ GFX::TextureSprite const* texture_sprite = checkbox.get_sprite()->cast_to<GFX::TextureSprite>();
+ 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->add_theme_icon_override("unchecked", icon_texture);
+ } else {
+ UtilityFunctions::push_error("Failed to make unchecked GFXIconTexture for GUI checkbox ", checkbox_name);
+ ret = false;
+ }
+ icon_texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, 2);
+ if (icon_texture.is_valid()) {
+ godot_checkbox->add_theme_icon_override("checked", icon_texture);
+ } else {
+ UtilityFunctions::push_error("Failed to make checked GFXIconTexture for GUI checkbox ", checkbox_name);
+ ret = false;
+ }
+ } else {
+ UtilityFunctions::push_error("Invalid sprite type ", std_view_to_godot_string(checkbox.get_sprite()->get_type()),
+ " for GUI checkbox ", checkbox_name);
+ ret = false;
+ }
+ } else {
+ UtilityFunctions::push_error("Null sprite for GUI checkbox ", checkbox_name);
+ ret = false;
+ }
+
+ result = godot_checkbox;
+ return ret;
+}
+
+bool GodotGUIBuilder::generate_text(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::Text const& text = static_cast<GUI::Text const&>(element);
+
+ result = nullptr;
+ const String text_name = std_view_to_godot_string(text.get_name());
+
+ Label* godot_label = new_control<Label>(text);
+ if (godot_label == nullptr) {
+ UtilityFunctions::push_error("Failed to create Label for GUI text ", text_name);
+ return false;
+ }
+
+ godot_label->set_text(std_view_to_godot_string(text.get_text()));
+ godot_label->set_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 },
+ { right, HORIZONTAL_ALIGNMENT_RIGHT }
+ };
+
+ const decltype(format_map)::const_iterator it = format_map.find(text.get_format());
+ if (it != format_map.end()) {
+ godot_label->set_horizontal_alignment(it->second);
+ } else {
+ UtilityFunctions::push_error("Invalid text format (horizontal alignment) for GUI text ", text_name);
+ }
+
+ 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);
+ if (font.is_valid()) {
+ godot_label->add_theme_font_override("font", font);
+ } else {
+ UtilityFunctions::push_error("Failed to load font for GUI text ", text_name);
+ ret = false;
+ }
+ const Color colour = Utilities::to_godot_color(text.get_font()->get_colour());
+ godot_label->add_theme_color_override("font_color", colour);
+ }
+
+ result = godot_label;
+ return ret;
+}
+
+bool GodotGUIBuilder::generate_overlapping_elements(
+ GUI::Element const& element, AssetManager& asset_manager, Control*& result
+) {
+ GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(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);
+ if (godot_rect == nullptr) {
+ UtilityFunctions::push_error("Failed to create ColorRect for GUI overlapping elements ",
+ overlapping_elements_name);
+ return false;
+ }
+
+ godot_rect->set_size(Utilities::to_godot_ivec2(overlapping_elements.get_size()));
+ godot_rect->set_color({ 0.0f, 0.5f, 1.0f, 0.2f });
+
+ result = godot_rect;
+ return true;
+}
+
+bool GodotGUIBuilder::generate_listbox(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(element);
+
+ result = nullptr;
+ const String listbox_name = std_view_to_godot_string(listbox.get_name());
+
+ ColorRect* godot_rect = new_control<ColorRect>(listbox);
+ if (godot_rect == nullptr) {
+ UtilityFunctions::push_error("Failed to create ColorRect for GUI listbox ", listbox_name);
+ return false;
+ }
+
+ godot_rect->set_size(Utilities::to_godot_ivec2(listbox.get_size()));
+ godot_rect->set_color({ 1.0f, 0.5f, 0.0f, 0.2f });
+
+ result = godot_rect;
+ return true;
+}
+
+bool GodotGUIBuilder::generate_window(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+ GUI::Window const& window = static_cast<GUI::Window const&>(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);
+ if (godot_panel == nullptr) {
+ UtilityFunctions::push_error("Failed to create Panel for GUI window ", window_name);
+ return false;
+ }
+
+ godot_panel->set_size(Utilities::to_godot_ivec2(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_elements()) {
+ Control* node = nullptr;
+ const bool element_ret = generate_element(element.get(), asset_manager, node);
+ if (element_ret) {
+ if (node != nullptr) {
+ godot_panel->add_child(node);
+ }
+ } else {
+ UtilityFunctions::push_error("Failed to generate GUI element ", std_view_to_godot_string(element->get_name()));
+ ret = false;
+ }
+ }
+
+ result = godot_panel;
+ return ret;
+}