aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/interface
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2023-11-07 23:18:08 +0100
committer hop311 <hop3114@gmail.com>2023-11-12 17:55:39 +0100
commitfd686eadf81e85bd4993a483adcefd6a153d259f (patch)
treee59e8a1531d96a37872373f6262eff8ed45da1ff /src/openvic-simulation/interface
parentc8983f5cda0b396b76c9d1491cf4c8ff5997d420 (diff)
GUI and GFX file loading
Diffstat (limited to 'src/openvic-simulation/interface')
-rw-r--r--src/openvic-simulation/interface/GFX.cpp98
-rw-r--r--src/openvic-simulation/interface/GFX.hpp138
-rw-r--r--src/openvic-simulation/interface/GUI.cpp182
-rw-r--r--src/openvic-simulation/interface/GUI.hpp235
-rw-r--r--src/openvic-simulation/interface/LoadBase.hpp82
-rw-r--r--src/openvic-simulation/interface/UI.cpp92
-rw-r--r--src/openvic-simulation/interface/UI.hpp27
7 files changed, 854 insertions, 0 deletions
diff --git a/src/openvic-simulation/interface/GFX.cpp b/src/openvic-simulation/interface/GFX.cpp
new file mode 100644
index 0000000..24f4b21
--- /dev/null
+++ b/src/openvic-simulation/interface/GFX.cpp
@@ -0,0 +1,98 @@
+#include "GFX.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::GFX;
+using namespace OpenVic::NodeTools;
+
+Font::Font(std::string_view new_identifier, colour_t new_colour, std::string_view new_fontname)
+ : HasIdentifierAndColour { new_identifier, new_colour, false, true }, fontname { new_fontname } {}
+
+node_callback_t Sprite::expect_sprite(callback_t<std::unique_ptr<Sprite>&&> callback) {
+ return expect_dictionary_keys(
+ "spriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback),
+ "progressbartype", ZERO_OR_MORE, _expect_instance<Sprite, ProgressBar>(callback),
+ "PieChartType", ZERO_OR_MORE, _expect_instance<Sprite, PieChart>(callback),
+ "LineChartType", ZERO_OR_MORE, _expect_instance<Sprite, LineChart>(callback),
+ "textSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback),
+ "maskedShieldType", ZERO_OR_MORE, _expect_instance<Sprite, MaskedFlag>(callback),
+ "corneredTileSpriteType", ZERO_OR_MORE, success_callback,
+ "tileSpriteType", ZERO_OR_MORE, success_callback,
+ "BarChartType", ZERO_OR_MORE, success_callback,
+ "scrollingSprite", ZERO_OR_MORE, success_callback
+ );
+}
+
+TextureSprite::TextureSprite() : texture_file {}, no_of_frames { NO_FRAMES } {}
+
+bool TextureSprite::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "texturefile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)),
+ "textureFile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)),
+ "noOfFrames", ZERO_OR_ONE, expect_uint(assign_variable_callback(no_of_frames)),
+
+ "norefcount", ZERO_OR_ONE, success_callback,
+ "effectFile", ZERO_OR_ONE, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "transparencecheck", ZERO_OR_ONE, success_callback,
+ "loadType", ZERO_OR_ONE, success_callback,
+ "clicksound", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+ProgressBar::ProgressBar()
+: back_colour {}, back_texture_file {},
+ progress_colour {}, progress_texture_file {},
+ size {} {}
+
+bool ProgressBar::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "color", ONE_EXACTLY, expect_colour(assign_variable_callback(back_colour)),
+ "colortwo", ONE_EXACTLY, expect_colour(assign_variable_callback(progress_colour)),
+ "textureFile1", ZERO_OR_ONE, expect_string(assign_variable_callback_string(back_texture_file)),
+ "textureFile2", ZERO_OR_ONE, expect_string(assign_variable_callback_string(progress_texture_file)),
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+
+ "effectFile", ONE_EXACTLY, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "loadType", ZERO_OR_ONE, success_callback,
+ "horizontal", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+PieChart::PieChart() : size {} {}
+
+bool PieChart::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map, "size", ONE_EXACTLY, expect_uint(assign_variable_callback(size)));
+ return ret;
+}
+
+LineChart::LineChart() : size {}, linewidth {} {}
+
+bool LineChart::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "linewidth", ONE_EXACTLY, expect_uint(assign_variable_callback(linewidth)),
+ "allwaystransparent", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+MaskedFlag::MaskedFlag() : texture_file {}, mask_file {} {}
+
+bool MaskedFlag::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "textureFile1", ONE_EXACTLY, expect_string(assign_variable_callback_string(texture_file)),
+ "textureFile2", ONE_EXACTLY, expect_string(assign_variable_callback_string(mask_file)),
+ "effectFile", ONE_EXACTLY, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "flipv", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
diff --git a/src/openvic-simulation/interface/GFX.hpp b/src/openvic-simulation/interface/GFX.hpp
new file mode 100644
index 0000000..e155486
--- /dev/null
+++ b/src/openvic-simulation/interface/GFX.hpp
@@ -0,0 +1,138 @@
+#pragma once
+
+#include "openvic-simulation/interface/LoadBase.hpp"
+
+namespace OpenVic {
+ class UIManager;
+}
+
+namespace OpenVic::GFX {
+
+ struct Font : HasIdentifierAndColour {
+ friend class OpenVic::UIManager;
+
+ private:
+ const std::string PROPERTY(fontname);
+
+ // TODO - colorcodes, effect
+
+ Font(std::string_view new_identifier, colour_t new_colour, std::string_view new_fontname);
+
+ public:
+ Font(Font&&) = default;
+ };
+
+ using frame_t = int32_t;
+ static constexpr frame_t NO_FRAMES = 0;
+
+ class Sprite : public Named<> {
+ protected:
+ Sprite() = default;
+
+ public:
+ Sprite(Sprite&&) = default;
+ virtual ~Sprite() = default;
+
+ OV_DETAIL_GET_BASE_TYPE(Sprite)
+ OV_DETAIL_GET_TYPE
+
+ static NodeTools::node_callback_t expect_sprite(NodeTools::callback_t<std::unique_ptr<Sprite>&&> callback);
+ };
+
+ class TextureSprite final : public Sprite {
+ friend std::unique_ptr<TextureSprite> std::make_unique<TextureSprite>();
+
+ std::string PROPERTY(texture_file);
+ frame_t PROPERTY(no_of_frames);
+
+ // TODO - norefcount, effectFile, allwaystransparent
+
+ protected:
+ TextureSprite();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ TextureSprite(TextureSprite&&) = default;
+ virtual ~TextureSprite() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class ProgressBar final : public Sprite {
+ friend std::unique_ptr<ProgressBar> std::make_unique<ProgressBar>();
+
+ colour_t PROPERTY(back_colour);
+ std::string PROPERTY(back_texture_file);
+ colour_t PROPERTY(progress_colour);
+ std::string PROPERTY(progress_texture_file);
+ ivec2_t PROPERTY(size);
+
+ // TODO - effectFile
+
+ protected:
+ ProgressBar();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ ProgressBar(ProgressBar&&) = default;
+ virtual ~ProgressBar() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class PieChart final : public Sprite {
+ friend std::unique_ptr<PieChart> std::make_unique<PieChart>();
+
+ uint32_t PROPERTY(size);
+
+ protected:
+ PieChart();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ PieChart(PieChart&&) = default;
+ virtual ~PieChart() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class LineChart final : public Sprite {
+ friend std::unique_ptr<LineChart> std::make_unique<LineChart>();
+
+ ivec2_t PROPERTY(size);
+ uint32_t PROPERTY(linewidth);
+
+ protected:
+ LineChart();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ LineChart(LineChart&&) = default;
+ virtual ~LineChart() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+
+ class MaskedFlag final : public Sprite {
+ friend std::unique_ptr<MaskedFlag> std::make_unique<MaskedFlag>();
+
+ std::string PROPERTY(texture_file)
+ std::string PROPERTY(mask_file);
+
+ protected:
+ MaskedFlag();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ MaskedFlag(MaskedFlag&&) = default;
+ virtual ~MaskedFlag() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+}
diff --git a/src/openvic-simulation/interface/GUI.cpp b/src/openvic-simulation/interface/GUI.cpp
new file mode 100644
index 0000000..534f552
--- /dev/null
+++ b/src/openvic-simulation/interface/GUI.cpp
@@ -0,0 +1,182 @@
+#include "GUI.hpp"
+
+#include "openvic-simulation/interface/UI.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::GUI;
+using namespace OpenVic::NodeTools;
+
+Element::Element() : position {}, orientation { orientation_t::UPPER_LEFT } {}
+
+bool Element::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Named::_fill_key_map(key_map, ui_manager);
+ using enum orientation_t;
+ static const string_map_t<orientation_t> orientation_map = {
+ { "UPPER_LEFT", UPPER_LEFT }, { "LOWER_LEFT", LOWER_LEFT },
+ { "LOWER_RIGHT", LOWER_RIGHT }, { "UPPER_RIGHT", UPPER_RIGHT },
+ { "CENTER", CENTER }
+ };
+ ret &= add_key_map_entries(key_map,
+ "position", ONE_EXACTLY, expect_ivec2(assign_variable_callback(position)),
+ "orientation", ZERO_OR_ONE, expect_string(expect_mapped_string(orientation_map, assign_variable_callback(orientation))),
+ "Orientation", ZERO_OR_ONE, expect_string(expect_mapped_string(orientation_map, assign_variable_callback(orientation)))
+ );
+ return ret;
+}
+
+bool Element::_fill_elements_key_map(
+ NodeTools::key_map_t& key_map, callback_t<std::unique_ptr<Element>&&> callback, UIManager const& ui_manager
+) {
+ bool ret = true;
+ ret &= add_key_map_entries(key_map,
+ "iconType", ZERO_OR_MORE, _expect_instance<Element, Icon>(callback, ui_manager),
+ "guiButtonType", ZERO_OR_MORE, _expect_instance<Element, Button>(callback, ui_manager),
+ "checkboxType", ZERO_OR_MORE, _expect_instance<Element, Checkbox>(callback, ui_manager),
+ "textBoxType", ZERO_OR_MORE, _expect_instance<Element, Text>(callback, ui_manager),
+ "instantTextBoxType", ZERO_OR_MORE, _expect_instance<Element, Text>(callback, ui_manager),
+ "OverlappingElementsBoxType", ZERO_OR_MORE, _expect_instance<Element, OverlappingElementsBox>(callback, ui_manager),
+ "listboxType", ZERO_OR_MORE, _expect_instance<Element, ListBox>(callback, ui_manager),
+ "windowType", ZERO_OR_MORE, _expect_instance<Element, Window>(callback, ui_manager),
+ "positionType", ZERO_OR_MORE, success_callback
+ );
+ return ret;
+}
+
+Scene::Scene() : elements { "scene elements" } {}
+
+bool Scene::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ return Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool {
+ return elements.add_item(std::move(element));
+ }, ui_manager);
+}
+
+node_callback_t Scene::expect_scene(
+ std::string_view scene_name, callback_t<std::unique_ptr<Scene>&&> callback, UIManager const& ui_manager
+) {
+ return _expect_instance<Scene, Scene>([scene_name, callback](std::unique_ptr<Scene>&& scene) -> bool {
+ scene->_set_name(scene_name);
+ return callback(std::move(scene));
+ }, ui_manager);
+}
+
+Window::Window() : elements { "window elements" }, size {}, moveable { false }, fullscreen { false } {}
+
+bool Window::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool {
+ return elements.add_item(std::move(element));
+ }, ui_manager);
+ ret &= Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "backGround", ZERO_OR_ONE, success_callback,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "moveable", ONE_EXACTLY, expect_int_bool(assign_variable_callback(moveable)),
+ "dontRender", ZERO_OR_ONE, success_callback,
+ "horizontalBorder", ZERO_OR_ONE, success_callback,
+ "verticalBorder", ZERO_OR_ONE, success_callback,
+ "fullScreen", ZERO_OR_ONE, expect_bool(assign_variable_callback(fullscreen))
+ );
+ return ret;
+}
+
+Icon::Icon() : sprite { nullptr }, frame { GFX::NO_FRAMES } {}
+
+bool Icon::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "spriteType", ONE_EXACTLY, expect_string(ui_manager.expect_sprite_str(assign_variable_callback_pointer(sprite))),
+ "frame", ZERO_OR_ONE, expect_uint(assign_variable_callback(frame))
+ );
+ return ret;
+}
+
+BaseButton::BaseButton() : sprite { nullptr } {}
+
+bool BaseButton::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ // look up sprite registry for texture sprite with name...
+ ret &= add_key_map_entries(key_map,
+ "quadTextureSprite", ONE_EXACTLY,
+ expect_string(ui_manager.expect_sprite_str(assign_variable_callback_pointer(sprite))),
+ "shortcut", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+Button::Button() : text {}, font { nullptr} {}
+
+bool Button::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = BaseButton::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "buttonText", ZERO_OR_ONE, expect_string(assign_variable_callback_string(text), true),
+ "buttonFont", ZERO_OR_ONE, expect_string(ui_manager.expect_font_str(assign_variable_callback_pointer(font))),
+ "clicksound", ZERO_OR_ONE, success_callback,
+ /* These are always empty in the base defines */
+ "tooltip", ZERO_OR_ONE, success_callback,
+ "tooltipText", ZERO_OR_ONE, success_callback,
+ "delayedTooltipText", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+bool Checkbox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = BaseButton::_fill_key_map(key_map, ui_manager);
+ return ret;
+}
+
+AlignedElement::AlignedElement() : format { format_t::left } {}
+
+bool AlignedElement::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ using enum format_t;
+ static const string_map_t<format_t> format_map = {
+ { "left", left }, { "right", right }, { "centre", centre }, { "center", centre }
+ };
+ ret &= add_key_map_entries(key_map,
+ "format", ZERO_OR_ONE, expect_identifier(expect_mapped_string(format_map, assign_variable_callback(format))
+ ));
+ return ret;
+}
+
+Text::Text() : text {}, font { nullptr } {}
+
+bool Text::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = AlignedElement::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "text", ZERO_OR_ONE, expect_string(assign_variable_callback_string(text), true),
+ "font", ONE_EXACTLY, expect_string(ui_manager.expect_font_str(assign_variable_callback_pointer(font))),
+ "maxWidth", ONE_EXACTLY, expect_uint(assign_variable_callback(max_size.x)),
+ "maxHeight", ONE_EXACTLY, expect_uint(assign_variable_callback(max_size.y)),
+
+ "borderSize", ZERO_OR_ONE, success_callback,
+ "fixedsize", ZERO_OR_ONE, success_callback,
+
+ // Add warning about redundant key?
+ "textureFile", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+OverlappingElementsBox::OverlappingElementsBox() : size {} {}
+
+bool OverlappingElementsBox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = AlignedElement::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "spacing", ONE_EXACTLY, success_callback
+ );
+ return ret;
+}
+
+ListBox::ListBox() : size {} {}
+
+bool ListBox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "backGround", ZERO_OR_ONE, success_callback,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "spacing", ZERO_OR_ONE, success_callback,
+ "scrollbartype", ZERO_OR_ONE, success_callback,
+ "borderSize", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
diff --git a/src/openvic-simulation/interface/GUI.hpp b/src/openvic-simulation/interface/GUI.hpp
new file mode 100644
index 0000000..1a76ca0
--- /dev/null
+++ b/src/openvic-simulation/interface/GUI.hpp
@@ -0,0 +1,235 @@
+#pragma once
+
+#include "openvic-simulation/interface/GFX.hpp"
+
+namespace OpenVic {
+ class UIManager;
+}
+namespace OpenVic::GUI {
+ class Scene;
+
+ class Element : public Named<UIManager const&> {
+ friend class Scene;
+
+ public:
+ enum class orientation_t {
+ UPPER_LEFT, LOWER_LEFT, LOWER_RIGHT, UPPER_RIGHT, CENTER
+ };
+
+ private:
+ ivec2_t PROPERTY(position);
+ orientation_t PROPERTY(orientation);
+
+ protected:
+ Element();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+ static bool _fill_elements_key_map(
+ NodeTools::key_map_t& key_map, NodeTools::callback_t<std::unique_ptr<Element>&&> callback,
+ UIManager const& ui_manager
+ );
+
+ public:
+ Element(Element&&) = default;
+ virtual ~Element() = default;
+
+ OV_DETAIL_GET_BASE_TYPE(Element)
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Scene : public Named<UIManager const&> {
+ friend std::unique_ptr<Scene> std::make_unique<Scene>();
+
+ NamedInstanceRegistry<Element, UIManager const&> elements;
+
+ protected:
+ Scene();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Scene(Scene&&) = default;
+ virtual ~Scene() = default;
+
+ OV_DETAIL_GET_TYPE
+
+ static NodeTools::node_callback_t expect_scene(
+ std::string_view scene_name, NodeTools::callback_t<std::unique_ptr<Scene>&&> callback, UIManager const& ui_manager
+ );
+
+ IDENTIFIER_REGISTRY_ACCESSORS(element)
+ };
+
+ class Window final : public Element {
+ friend std::unique_ptr<Window> std::make_unique<Window>();
+
+ NamedInstanceRegistry<Element, UIManager const&> elements;
+
+ ivec2_t PROPERTY(size);
+ bool PROPERTY(moveable);
+ bool PROPERTY(fullscreen);
+ // TODO - background, dontRender, horizontalBorder, verticalBorder
+
+ protected:
+ Window();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Window(Window&&) = default;
+ virtual ~Window() = default;
+
+ OV_DETAIL_GET_TYPE
+
+ IDENTIFIER_REGISTRY_ACCESSORS(element)
+ };
+
+ class Icon final : public Element {
+ friend std::unique_ptr<Icon> std::make_unique<Icon>();
+
+ GFX::Sprite const* PROPERTY(sprite);
+ GFX::frame_t PROPERTY(frame);
+
+ protected:
+ Icon();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Icon(Icon&&) = default;
+ virtual ~Icon() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class BaseButton : public Element {
+ GFX::Sprite const* PROPERTY(sprite);
+ // TODO - shortcut
+
+ protected:
+ BaseButton();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ BaseButton(BaseButton&&) = default;
+ virtual ~BaseButton() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Button final : public BaseButton {
+ friend std::unique_ptr<Button> std::make_unique<Button>();
+
+ std::string PROPERTY(text);
+ GFX::Font const* PROPERTY(font);
+
+ // TODO - clicksound
+
+ protected:
+ Button() ;
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Button(Button&&) = default;
+ virtual ~Button() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Checkbox final : public BaseButton {
+ friend std::unique_ptr<Checkbox> std::make_unique<Checkbox>();
+
+ protected:
+ Checkbox() = default;
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Checkbox(Checkbox&&) = default;
+ virtual ~Checkbox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class AlignedElement : public Element {
+ public:
+ enum class format_t {
+ left, centre, right
+ };
+
+ private:
+ format_t PROPERTY(format);
+
+ protected:
+ AlignedElement();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ AlignedElement(AlignedElement&&) = default;
+ virtual ~AlignedElement() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Text final : public AlignedElement {
+ friend std::unique_ptr<Text> std::make_unique<Text>();
+
+ std::string PROPERTY(text);
+ GFX::Font const* PROPERTY(font);
+ ivec2_t PROPERTY(max_size); // maxWidth, maxHeight
+
+ // TODO - borderSize, fixedsize, textureFile
+
+ protected:
+ Text();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Text(Text&&) = default;
+ virtual ~Text() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class OverlappingElementsBox final : public AlignedElement {
+ friend std::unique_ptr<OverlappingElementsBox> std::make_unique<OverlappingElementsBox>();
+
+ ivec2_t PROPERTY(size);
+
+ // TODO - spacing
+
+ protected:
+ OverlappingElementsBox();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ OverlappingElementsBox(OverlappingElementsBox&&) = default;
+ virtual ~OverlappingElementsBox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class ListBox final : public Element {
+ friend std::unique_ptr<ListBox> std::make_unique<ListBox>();
+
+ ivec2_t PROPERTY(size);
+
+ // TODO - backGround, spacing, scrollbartype, borderSize
+
+ protected:
+ ListBox();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ ListBox(ListBox&&) = default;
+ virtual ~ListBox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+}
diff --git a/src/openvic-simulation/interface/LoadBase.hpp b/src/openvic-simulation/interface/LoadBase.hpp
new file mode 100644
index 0000000..3ee7c83
--- /dev/null
+++ b/src/openvic-simulation/interface/LoadBase.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
+
+namespace OpenVic {
+
+ template<typename... _Context>
+ class LoadBase {
+ protected:
+ LoadBase() = default;
+
+ virtual bool _fill_key_map(NodeTools::key_map_t&, _Context...) = 0;
+
+ public:
+ LoadBase(LoadBase&&) = default;
+ virtual ~LoadBase() = default;
+
+ bool load(ast::NodeCPtr node, _Context... context) {
+ NodeTools::key_map_t key_map;
+ bool ret = _fill_key_map(key_map, context...);
+ ret &= NodeTools::expect_dictionary_key_map(std::move(key_map))(node);
+ return ret;
+ }
+
+ template<std::derived_from<LoadBase<_Context...>> T, std::derived_from<T> U>
+ static NodeTools::node_callback_t _expect_instance(
+ NodeTools::callback_t<std::unique_ptr<T>&&> callback, _Context... context
+ ) {
+ return [callback, &context...](ast::NodeCPtr node) -> bool {
+ std::unique_ptr<T> instance { std::make_unique<U>() };
+ bool ret = instance->load(node, context...);
+ ret &= callback(std::move(instance));
+ return ret;
+ };
+ }
+
+ OV_DETAIL_GET_TYPE_BASE_CLASS(LoadBase)
+ };
+
+ template<typename... _Context>
+ class Named : public LoadBase<_Context...> {
+ std::string PROPERTY(name);
+
+ protected:
+ Named() = default;
+
+ virtual bool _fill_key_map(NodeTools::key_map_t& key_map, _Context...) override {
+ using namespace OpenVic::NodeTools;
+ return add_key_map_entries(key_map, "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(name)));
+ }
+
+ void _set_name(std::string_view new_name) {
+ if (!name.empty()) {
+ Logger::warning("Overriding scene name ", name, " with ", new_name);
+ }
+ name = new_name;
+ }
+
+ public:
+ Named(Named&&) = default;
+ virtual ~Named() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ struct _get_name {
+ constexpr std::string_view operator()(T const* item) const {
+ return item->get_name();
+ }
+ };
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ using NamedRegistry = ValueRegistry<T, _get_name<T, _Context...>>;
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ using NamedInstanceRegistry = InstanceRegistry<T, _get_name<T, _Context...>>;
+}
diff --git a/src/openvic-simulation/interface/UI.cpp b/src/openvic-simulation/interface/UI.cpp
new file mode 100644
index 0000000..4653e5b
--- /dev/null
+++ b/src/openvic-simulation/interface/UI.cpp
@@ -0,0 +1,92 @@
+#include "UI.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+using namespace OpenVic::GFX;
+using namespace OpenVic::GUI;
+
+UIManager::UIManager() : sprites { "sprites" }, scenes { "scenes" }, fonts { "fonts" } {}
+
+bool UIManager::add_font(std::string_view identifier, colour_t colour, std::string_view fontname) {
+ if (identifier.empty()) {
+ Logger::error("Invalid font identifier - empty!");
+ return false;
+ }
+ if (fontname.empty()) {
+ Logger::error("Invalid culture colour for ", identifier, ": ", colour_to_hex_string(colour));
+ return false;
+ }
+ return fonts.add_item({ identifier, colour, fontname }, duplicate_warning_callback);
+}
+
+bool UIManager::_load_font(ast::NodeCPtr node) {
+ std::string_view identifier, fontname;
+ colour_t colour = NULL_COLOUR;
+ bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(identifier)),
+ "fontname", ONE_EXACTLY, expect_string(assign_variable_callback(fontname)),
+ "color", ONE_EXACTLY, expect_colour_hex(assign_variable_callback(colour)),
+ "colorcodes", ZERO_OR_ONE, success_callback,
+ "effect", ZERO_OR_ONE, success_callback
+ )(node);
+ ret &= add_font(identifier, colour, fontname);
+ return ret;
+}
+
+bool UIManager::load_gfx_file(ast::NodeCPtr root) {
+ return expect_dictionary_keys(
+ "spriteTypes", ZERO_OR_ONE, Sprite::expect_sprite(
+ [this](std::unique_ptr<Sprite>&& sprite) -> bool {
+ /* TODO - more checks on duplicates (the false here reduces them from
+ * errors to warnings). The repeats in vanilla are identical (simple
+ * texture sprites with the same name referring to the same asset),
+ * maybe check if duplicates are equal? (this would require some kind
+ * of duplicate handling callback for `add_item`)
+ *
+ * It's also possible that some of the vanilla duplicates are intentional,
+ * after all I'm assuming all GFX files are loaded into a global registry
+ * rather than each GUI file using only the contnts of a few specific,
+ * maybe even hardcoded GFX files. This can't, however, explain all of the
+ * vanilla duplicates, as GFX_frontend_tab_button is repeated in the same
+ * file, albeit in a section marked "OLD STUFF BELOW..".
+ *
+ * Vanilla Duplicates:
+ * - GFX_bp_open_manager and GFX_bp_toggle_visible
+ * - simple texture sprites appearing identically in menubar.gfx and battleplansinterface.gfx
+ * - GFX_frontend_tab_button
+ * - texture sprite appearing twice identically in the same file (temp_frontend.gfx),
+ * both below a comment saying "OLD STUFF BELOW.."
+ */
+ return sprites.add_item(std::move(sprite), duplicate_warning_callback);
+ }
+ ),
+ "bitmapfonts", ZERO_OR_ONE, expect_dictionary(
+ [this](std::string_view key, ast::NodeCPtr node) -> bool {
+ if (key != "bitmapfont") {
+ Logger::error("Invalid bitmapfonts key: ", key);
+ return false;
+ }
+ return _load_font(node);
+ }
+ ),
+ "objectTypes", ZERO_OR_ONE, success_callback,
+ "lightTypes", ZERO_OR_ONE, success_callback,
+ "fonts", ZERO_OR_ONE, success_callback
+ )(root);
+}
+
+bool UIManager::load_gui_file(std::string_view scene_name, ast::NodeCPtr root) {
+ if (!sprites_are_locked()) {
+ Logger::error("Cannot load GUI files until GFX files (i.e. Sprites) are locked!");
+ return false;
+ }
+ return expect_dictionary_keys(
+ "guiTypes", ZERO_OR_ONE, Scene::expect_scene(
+ scene_name,
+ [this](std::unique_ptr<Scene>&& scene) -> bool {
+ return scenes.add_item(std::move(scene));
+ },
+ *this
+ )
+ )(root);
+}
diff --git a/src/openvic-simulation/interface/UI.hpp b/src/openvic-simulation/interface/UI.hpp
new file mode 100644
index 0000000..045766b
--- /dev/null
+++ b/src/openvic-simulation/interface/UI.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "openvic-simulation/interface/GUI.hpp"
+
+namespace OpenVic {
+
+ class UIManager {
+
+ NamedInstanceRegistry<GFX::Sprite> sprites;
+ NamedInstanceRegistry<GUI::Scene, UIManager const&> scenes;
+ IdentifierRegistry<GFX::Font> fonts;
+
+ bool _load_font(ast::NodeCPtr node);
+
+ public:
+ UIManager();
+
+ IDENTIFIER_REGISTRY_ACCESSORS(sprite)
+ IDENTIFIER_REGISTRY_ACCESSORS(scene)
+ IDENTIFIER_REGISTRY_ACCESSORS(font)
+
+ bool add_font(std::string_view identifier, colour_t colour, std::string_view fontname);
+
+ bool load_gfx_file(ast::NodeCPtr root);
+ bool load_gui_file(std::string_view scene_name, ast::NodeCPtr root);
+ };
+}