Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI polish #181

Merged
merged 1 commit into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extension/deps/openvic-simulation
106 changes: 106 additions & 0 deletions extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "GFXButtonStateTexture.hpp"

#include "openvic-extension/utility/ClassBindings.hpp"

using namespace OpenVic;
using namespace godot;

void GFXButtonStateTexture::_bind_methods() {
OV_BIND_METHOD(GFXButtonStateTexture::set_button_state, { "new_button_state" });
OV_BIND_METHOD(GFXButtonStateTexture::get_button_state);

OV_BIND_SMETHOD(get_generate_state_image_func_name);

OV_BIND_SMETHOD(button_state_to_theme_name, { "button_state" });
OV_BIND_METHOD(GFXButtonStateTexture::get_button_state_theme);

OV_BIND_METHOD(GFXButtonStateTexture::generate_state_image, { "source_image" });

BIND_ENUM_CONSTANT(HOVER);
BIND_ENUM_CONSTANT(PRESSED);
BIND_ENUM_CONSTANT(DISABLED);
}

GFXButtonStateTexture::GFXButtonStateTexture() : button_state { HOVER } {}

Ref<GFXButtonStateTexture> GFXButtonStateTexture::make_gfx_button_state_texture(
ButtonState button_state, Ref<Image> const& source_image
) {
Ref<GFXButtonStateTexture> button_state_texture;
button_state_texture.instantiate();
ERR_FAIL_NULL_V(button_state_texture, nullptr);
button_state_texture->set_button_state(button_state);
if (source_image.is_valid()) {
ERR_FAIL_COND_V(button_state_texture->generate_state_image(source_image) != OK, nullptr);
}
return button_state_texture;
}

void GFXButtonStateTexture::set_button_state(ButtonState new_button_state) {
ERR_FAIL_COND(new_button_state != HOVER && new_button_state != PRESSED && new_button_state != DISABLED);
button_state = new_button_state;
}

Error GFXButtonStateTexture::generate_state_image(Ref<Image> const& source_image) {
ERR_FAIL_COND_V(source_image.is_null() || source_image->is_empty(), FAILED);
/* Whether we've already set the ImageTexture to an image of the right dimensions and format,
* and so can update it without creating and setting a new image, or not. */
const bool can_update = state_image.is_valid() && state_image->get_size() == source_image->get_size()
&& state_image->get_format() == source_image->get_format();
if (!can_update) {
state_image = Image::create(source_image->get_width(), source_image->get_height(), false, source_image->get_format());
ERR_FAIL_NULL_V(state_image, FAILED);
}

static constexpr auto hover_colour = [](Color const& colour) -> Color {
return { std::min(colour.r + 0.1f, 1.0f), std::min(colour.g + 0.1f, 1.0f), std::min(colour.b + 0.1f, 1.0f), colour.a };
};
static constexpr auto pressed_colour = [](Color const& colour) -> Color {
return { std::max(colour.r - 0.1f, 0.0f), std::max(colour.g - 0.1f, 0.0f), std::max(colour.b - 0.1f, 0.0f), colour.a };
};
static constexpr auto disabled_colour = [](Color const& colour) -> Color {
const float luma = colour.get_luminance();
return { luma, luma, luma, colour.a };
};

const auto colour_func = button_state == HOVER ? hover_colour : button_state == PRESSED ? pressed_colour : disabled_colour;

for (Vector2i point { 0, 0 }; point.y < state_image->get_height(); ++point.y) {
for (point.x = 0; point.x < state_image->get_width(); ++point.x) {
state_image->set_pixelv(point, colour_func(source_image->get_pixelv(point)));
}
}

if (can_update) {
update(state_image);
} else {
set_image(state_image);
}
return OK;
}

StringName const& GFXButtonStateTexture::get_generate_state_image_func_name() {
static const StringName generate_state_image_func_name = "generate_state_image";
return generate_state_image_func_name;
}

StringName const& GFXButtonStateTexture::button_state_to_theme_name(ButtonState button_state) {
static const StringName theme_name_hover = "hover";
static const StringName theme_name_pressed = "pressed";
static const StringName theme_name_disabled = "disabled";
static const StringName theme_name_error = "";
switch (button_state) {
case HOVER:
return theme_name_hover;
case PRESSED:
return theme_name_pressed;
case DISABLED:
return theme_name_disabled;
default:
return theme_name_error;
}
}

StringName const& GFXButtonStateTexture::get_button_state_theme() const {
return button_state_to_theme_name(button_state);
}
46 changes: 46 additions & 0 deletions extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <godot_cpp/classes/image_texture.hpp>

#include <openvic-simulation/utility/Getters.hpp>

namespace OpenVic {
class GFXButtonStateTexture : public godot::ImageTexture {
GDCLASS(GFXButtonStateTexture, godot::ImageTexture)

public:
enum ButtonState {
HOVER,
PRESSED,
DISABLED
};

private:
ButtonState PROPERTY(button_state);
godot::Ref<godot::Image> state_image;

protected:
static void _bind_methods();

public:
GFXButtonStateTexture();

/* Create a GFXButtonStateTexture using the specified godot::Image. Returns nullptr if generate_state_image fails. */
static godot::Ref<GFXButtonStateTexture> make_gfx_button_state_texture(
ButtonState button_state, godot::Ref<godot::Image> const& source_image = nullptr
);

/* Set the ButtonState to be generated by this class (calling this does not trigger state image generation). */
void set_button_state(ButtonState new_button_state);

/* Generate a modified version of source_image and update the underlying godot::ImageTexture to use it. */
godot::Error generate_state_image(godot::Ref<godot::Image> const& source_image);

static godot::StringName const& get_generate_state_image_func_name();

static godot::StringName const& button_state_to_theme_name(ButtonState button_state);
godot::StringName const& get_button_state_theme() const;
};
}

VARIANT_ENUM_CAST(OpenVic::GFXButtonStateTexture::ButtonState);
33 changes: 31 additions & 2 deletions extension/src/openvic-extension/classes/GFXIconTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;

StringName const& GFXIconTexture::_signal_image_updated() {
static const StringName signal_image_updated = "image_updated";
return signal_image_updated;
}

void GFXIconTexture::_bind_methods() {
OV_BIND_METHOD(GFXIconTexture::clear);

Expand All @@ -26,16 +31,32 @@ void GFXIconTexture::_bind_methods() {
OV_BIND_METHOD(GFXIconTexture::get_icon_count);

ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_index"), "set_icon_index", "get_icon_index");

ADD_SIGNAL(
MethodInfo(_signal_image_updated(), PropertyInfo(Variant::OBJECT, "source_image", PROPERTY_HINT_RESOURCE_TYPE, "Image"))
);
}

GFXIconTexture::GFXIconTexture()
: gfx_texture_sprite { nullptr }, icon_index { GFX::NO_FRAMES }, icon_count { GFX::NO_FRAMES } {}

Ref<GFXIconTexture> GFXIconTexture::make_gfx_icon_texture(GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon) {
Ref<GFXIconTexture> GFXIconTexture::make_gfx_icon_texture(
GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon,
std::vector<Ref<GFXButtonStateTexture>> const& button_state_textures
) {
Ref<GFXIconTexture> icon_texture;
icon_texture.instantiate();
ERR_FAIL_NULL_V(icon_texture, nullptr);
icon_texture->set_gfx_texture_sprite(gfx_texture_sprite, icon);

for (Ref<GFXButtonStateTexture> const& button_state_texture : button_state_textures) {
icon_texture->connect(
_signal_image_updated(),
Callable { *button_state_texture, GFXButtonStateTexture::get_generate_state_image_func_name() },
CONNECT_PERSIST
);
}

ERR_FAIL_COND_V(icon_texture->set_gfx_texture_sprite(gfx_texture_sprite, icon) != OK, nullptr);
return icon_texture;
}

Expand All @@ -57,9 +78,15 @@ Error GFXIconTexture::set_gfx_texture_sprite(GFX::TextureSprite const* new_gfx_t
ERR_FAIL_NULL_V(asset_manager, FAILED);

const StringName texture_file = std_view_to_godot_string_name(new_gfx_texture_sprite->get_texture_file());

/* Needed for GFXButtonStateTexture, AssetManager::get_texture will re-use this image from its internal cache. */
const Ref<Image> image = asset_manager->get_image(texture_file);
ERR_FAIL_NULL_V_MSG(image, FAILED, vformat("Failed to load image: %s", texture_file));

const Ref<ImageTexture> texture = asset_manager->get_texture(texture_file);
ERR_FAIL_NULL_V_MSG(texture, FAILED, vformat("Failed to load texture: %s", texture_file));

sprite_image = image;
gfx_texture_sprite = new_gfx_texture_sprite;
set_atlas(texture);
icon_index = GFX::NO_FRAMES;
Expand Down Expand Up @@ -98,6 +125,7 @@ Error GFXIconTexture::set_icon_index(int32_t new_icon_index) {
}
icon_index = GFX::NO_FRAMES;
set_region({ {}, size });
emit_signal(_signal_image_updated(), sprite_image);
return OK;
}
if (GFX::NO_FRAMES < new_icon_index && new_icon_index <= icon_count) {
Expand All @@ -111,5 +139,6 @@ Error GFXIconTexture::set_icon_index(int32_t new_icon_index) {
}
}
set_region({ (icon_index - 1) * size.x / icon_count, 0, size.x / icon_count, size.y });
emit_signal(_signal_image_updated(), sprite_image->get_region(get_region()));
return OK;
}
12 changes: 11 additions & 1 deletion extension/src/openvic-extension/classes/GFXIconTexture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <openvic-simulation/interface/GFX.hpp>

#include "openvic-extension/classes/GFXButtonStateTexture.hpp"

namespace OpenVic {
class GFXIconTexture : public godot::AtlasTexture {
GDCLASS(GFXIconTexture, godot::AtlasTexture)
Expand All @@ -16,14 +18,22 @@ namespace OpenVic {
GFX::frame_t PROPERTY(icon_index);
GFX::frame_t PROPERTY(icon_count);

godot::Ref<godot::Image> sprite_image;

static godot::StringName const& _signal_image_updated();

protected:
static void _bind_methods();

public:
GFXIconTexture();

/* Create a GFXIconTexture using the specified GFX::TextureSprite and icon index. Returns nullptr if
* set_gfx_texture_sprite fails. Connects the provided GFXButtonStateTextures (if any) to the
* GFXIconTexture's image_updated signal. */
static godot::Ref<GFXIconTexture> make_gfx_icon_texture(
GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon = GFX::NO_FRAMES
GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon = GFX::NO_FRAMES,
std::vector<godot::Ref<GFXButtonStateTexture>> const& button_state_textures = {}
);

/* Discard the GFX::TextureSprite, atlas texture and icon index. */
Expand Down
31 changes: 27 additions & 4 deletions extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;

StringName const& GFXMaskedFlagTexture::_signal_image_updated() {
static const StringName signal_image_updated = "image_updated";
return signal_image_updated;
}

Error GFXMaskedFlagTexture::_generate_combined_image() {
ERR_FAIL_NULL_V(overlay_image, FAILED);
bool can_update = true;
if (combined_image.is_null() || combined_image->get_size() != overlay_image->get_size()) {
/* Whether we've already set the ImageTexture to an image of the right dimensions and format,
* and so can update it without creating and setting a new image, or not. */
const bool can_update = combined_image.is_valid() && combined_image->get_size() == overlay_image->get_size()
&& combined_image->get_format() == overlay_image->get_format();
if (!can_update) {
combined_image = Image::create(
overlay_image->get_width(), overlay_image->get_height(), false, overlay_image->get_format()
);
ERR_FAIL_NULL_V(combined_image, FAILED);
can_update = false;
}

if (mask_image.is_valid() && flag_image.is_valid()) {
Expand Down Expand Up @@ -55,6 +62,7 @@ Error GFXMaskedFlagTexture::_generate_combined_image() {
} else {
set_image(combined_image);
}
emit_signal(_signal_image_updated(), combined_image);
return OK;
}

Expand All @@ -68,14 +76,29 @@ void GFXMaskedFlagTexture::_bind_methods() {
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);

ADD_SIGNAL(
MethodInfo(_signal_image_updated(), PropertyInfo(Variant::OBJECT, "source_image", PROPERTY_HINT_RESOURCE_TYPE, "Image"))
);
}

GFXMaskedFlagTexture::GFXMaskedFlagTexture() : gfx_masked_flag { nullptr }, flag_country { nullptr } {}

Ref<GFXMaskedFlagTexture> GFXMaskedFlagTexture::make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag) {
Ref<GFXMaskedFlagTexture> GFXMaskedFlagTexture::make_gfx_masked_flag_texture(
GFX::MaskedFlag const* gfx_masked_flag, std::vector<Ref<GFXButtonStateTexture>> const& button_state_textures
) {
Ref<GFXMaskedFlagTexture> masked_flag_texture;
masked_flag_texture.instantiate();
ERR_FAIL_NULL_V(masked_flag_texture, nullptr);

for (Ref<GFXButtonStateTexture> const& button_state_texture : button_state_textures) {
masked_flag_texture->connect(
_signal_image_updated(),
Callable { *button_state_texture, GFXButtonStateTexture::get_generate_state_image_func_name() },
CONNECT_PERSIST
);
}

ERR_FAIL_COND_V(masked_flag_texture->set_gfx_masked_flag(gfx_masked_flag) != OK, nullptr);
return masked_flag_texture;
}
Expand Down
13 changes: 10 additions & 3 deletions extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <openvic-simulation/country/Country.hpp>
#include <openvic-simulation/interface/GFX.hpp>

#include "openvic-extension/classes/GFXButtonStateTexture.hpp"

namespace OpenVic {
class GFXMaskedFlagTexture : public godot::ImageTexture {
GDCLASS(GFXMaskedFlagTexture, godot::ImageTexture)
Expand All @@ -15,6 +17,8 @@ namespace OpenVic {

godot::Ref<godot::Image> overlay_image, mask_image, flag_image, combined_image;

static godot::StringName const& _signal_image_updated();

godot::Error _generate_combined_image();

protected:
Expand All @@ -23,9 +27,12 @@ namespace OpenVic {
public:
GFXMaskedFlagTexture();

/* Create a GFXMaskedFlagTexture using the specific GFX::MaskedFlag.
* Returns nullptr if setting gfx_masked_flag fails. */
static godot::Ref<GFXMaskedFlagTexture> make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag);
/* Create a GFXMaskedFlagTexture using the specified GFX::MaskedFlag. Returns nullptr if gfx_masked_flag fails.
* Connects the provided GFXButtonStateTextures (if any) to the GFXMaskedFlagTexture's image_updated signal. */
static godot::Ref<GFXMaskedFlagTexture> make_gfx_masked_flag_texture(
GFX::MaskedFlag const* gfx_masked_flag,
std::vector<godot::Ref<GFXButtonStateTexture>> const& button_state_textures = {}
);

/* Reset gfx_masked_flag, flag_country and flag_type to nullptr/an empty string, and unreference all images.
* This does not affect the godot::ImageTexture, which cannot be reset to a null or empty image. */
Expand Down
Loading