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

Add support for inline flags in GUILabel #361

Merged
merged 1 commit into from
Jan 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ Error GFXMaskedFlagTexture::set_flag_country(CountryInstance const* new_flag_cou
GovernmentType const* government_type = new_flag_country->get_flag_government_type();

const StringName new_flag_type = government_type != nullptr
? Utilities::std_to_godot_string(government_type->get_flag_type()) : String {};
? StringName { Utilities::std_to_godot_string(government_type->get_flag_type()) }
: StringName {};

return set_flag_country_and_type(new_flag_country->get_country_definition(), new_flag_type);
}
Expand Down
196 changes: 151 additions & 45 deletions extension/src/openvic-extension/classes/GUILabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <godot_cpp/variant/utility_functions.hpp>

#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
#include "openvic-extension/utility/Utilities.hpp"
#include "openvic-simulation/types/TextFormat.hpp"
Expand All @@ -13,7 +14,15 @@ using namespace OpenVic;
using namespace godot;
using namespace OpenVic::Utilities::literals;

GUILabel::string_segment_t::string_segment_t(String&& new_text, Color const& new_colour, real_t new_width)
: text { std::move(new_text) }, colour { new_colour }, width { new_width } {}

GUILabel::string_segment_t::string_segment_t(String const& new_text, Color const& new_colour, real_t new_width)
: text { new_text }, colour { new_colour }, width { new_width } {}

static constexpr int32_t DEFAULT_FONT_SIZE = 16;
static const Vector2 FLAG_DRAW_DIMS { 23.0_real, 11.0_real };
static const real_t FLAG_SEGMENT_WIDTH = FLAG_DRAW_DIMS.width + 1.0_real;

GUI_TOOLTIP_IMPLEMENTATIONS(GUILabel)

Expand Down Expand Up @@ -136,6 +145,8 @@ void GUILabel::_notification(int what) {
const Vector2 offset = base_offset + adjusted_rect.position + border_size;
Vector2 position = offset;

const float ascent = font->get_ascent(font_size), descent = font->get_descent(font_size);

for (line_t const& line : lines) {
position.x = offset.x;
switch (horizontal_alignment) {
Expand All @@ -150,30 +161,34 @@ void GUILabel::_notification(int what) {
break;
}

position.y += font->get_ascent(font_size);
position.y += ascent;

for (segment_t const& segment : line.segments) {
string_segment_t const* string_segment = std::get_if<string_segment_t>(&segment);

if (string_segment == nullptr) {
if (currency_texture.is_valid()) {
currency_texture->draw(
ci, position - Vector2 {
1.0_real, static_cast<real_t>(currency_texture->get_height()) * 0.75_real
}
);
position.x += currency_texture->get_width();
}
} else {
if (string_segment_t const* string_segment = std::get_if<string_segment_t>(&segment)) {
Hop311 marked this conversation as resolved.
Show resolved Hide resolved
font->draw_string(
ci, position, string_segment->text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size,
string_segment->colour
);

position.x += string_segment->width;
} else if (flag_segment_t const* flag_segment = std::get_if<flag_segment_t>(&segment)) {
(*flag_segment)->draw_rect(ci, Rect2 {
position - Vector2 { 1.0_real, ascent - 4.0_real }, FLAG_DRAW_DIMS
}, false);

position.x += FLAG_SEGMENT_WIDTH;
} else if (currency_texture.is_valid()) {
currency_texture->draw(
ci, position - Vector2 {
1.0_real, static_cast<real_t>(currency_texture->get_height()) * 0.75_real
}
);

position.x += currency_texture->get_width();
}
}

position.y += font->get_descent(font_size);
position.y += descent;
}

} break;
Expand Down Expand Up @@ -248,34 +263,30 @@ Error GUILabel::set_gui_text(GUI::Text const* new_gui_text, GFX::Font::colour_co

Error err = OK;

AssetManager* asset_manager = AssetManager::get_singleton();
if (asset_manager != nullptr) {
const StringName font_filepath = Utilities::std_to_godot_string(gui_text->get_font()->get_fontname());
Ref<FontFile> font_file = asset_manager->get_font(font_filepath);
if (font_file.is_valid()) {
if (set_font_file(font_file) != OK) {
err = FAILED;
}
} else {
UtilityFunctions::push_error("Failed to load font \"", font_filepath, "\" for GUILabel");
err = FAILED;
}
const StringName font_filepath = Utilities::std_to_godot_string(gui_text->get_font()->get_fontname());

if (!gui_text->get_texture_file().empty()) {
const StringName texture_path = Utilities::std_to_godot_string(gui_text->get_texture_file());
Ref<ImageTexture> texture = asset_manager->get_texture(texture_path);
if (texture.is_valid()) {
set_background_texture(texture);
} else {
UtilityFunctions::push_error("Failed to load texture \"", texture_path, "\" for GUILabel ", get_name());
err = FAILED;
}
AssetManager& asset_manager = *AssetManager::get_singleton();
Ref<FontFile> font_file = asset_manager.get_font(font_filepath);
if (font_file.is_valid()) {
if (set_font_file(font_file) != OK) {
err = FAILED;
}
} else {
UtilityFunctions::push_error("Failed to get AssetManager singleton for GUILabel");
UtilityFunctions::push_error("Failed to load font \"", font_filepath, "\" for GUILabel");
err = FAILED;
}

if (!gui_text->get_texture_file().empty()) {
const StringName texture_path = Utilities::std_to_godot_string(gui_text->get_texture_file());
Ref<ImageTexture> texture = asset_manager.get_texture(texture_path);
if (texture.is_valid()) {
set_background_texture(texture);
} else {
UtilityFunctions::push_error("Failed to load texture \"", texture_path, "\" for GUILabel ", get_name());
err = FAILED;
}
}

_queue_line_update();

return err;
Expand Down Expand Up @@ -382,10 +393,8 @@ Error GUILabel::set_font_size(int32_t new_font_size) {

_queue_line_update();

AssetManager* asset_manager = AssetManager::get_singleton();
ERR_FAIL_NULL_V_MSG(asset_manager, FAILED, "Failed to get AssetManager singleton for GUILabel");

currency_texture = asset_manager->get_currency_texture(font_size);
AssetManager& asset_manager = *AssetManager::get_singleton();
currency_texture = asset_manager.get_currency_texture(font_size);
ERR_FAIL_NULL_V(currency_texture, FAILED);

return OK;
Expand Down Expand Up @@ -443,6 +452,8 @@ real_t GUILabel::get_string_width(String const& string) const {
real_t GUILabel::get_segment_width(segment_t const& segment) const {
if (string_segment_t const* string_segment = std::get_if<string_segment_t>(&segment)) {
return string_segment->width;
} else if (std::get_if<flag_segment_t>(&segment) != nullptr) {
return FLAG_SEGMENT_WIDTH;
} else if (currency_texture.is_valid()) {
return currency_texture->get_width();
} else {
Expand Down Expand Up @@ -628,6 +639,87 @@ void GUILabel::separate_lines(

void GUILabel::separate_currency_segments(
String const& string, Color const& colour, line_t& line
) const {
int64_t start_pos = 0;
int64_t marker_pos;

const real_t currency_width = currency_texture.is_valid() ? currency_texture->get_width() : 0.0_real;

while ((marker_pos = string.find(get_currency_marker(), start_pos)) != -1) {
if (start_pos < marker_pos) {
separate_flag_segments(string.substr(start_pos, marker_pos - start_pos), colour, line);
}

line.segments.push_back(currency_segment_t {});
line.width += currency_width;

start_pos = marker_pos + get_currency_marker().length();
}

if (start_pos < string.length()) {
separate_flag_segments(string.substr(start_pos), colour, line);
}
}

GUILabel::flag_segment_t GUILabel::make_flag_segment(String const& identifier) {
GameSingleton const& game_singleton = *GameSingleton::get_singleton();

int32_t country_index = -1;
StringName flag_type;

InstanceManager const* instance_manager = game_singleton.get_instance_manager();

if (instance_manager != nullptr) {
CountryInstance const* country_instance =
instance_manager->get_country_instance_manager().get_country_instance_by_identifier(
Utilities::godot_to_std_string(identifier)
);

if (country_instance != nullptr) {
country_index = country_instance->get_country_definition()->get_index();

GovernmentType const* government_type = country_instance->get_flag_government_type();
if (government_type == nullptr) {
government_type = country_instance->get_government_type();
}
if (government_type != nullptr) {
flag_type = Utilities::std_to_godot_string(government_type->get_flag_type());
}
}
} else {
CountryDefinition const* country_definition =
game_singleton.get_definition_manager().get_country_definition_manager().get_country_definition_by_identifier(
Utilities::godot_to_std_string(identifier)
);

if (country_definition != nullptr) {
country_index = country_definition->get_index();
}
}

// If no country with the given identifier can be found, fallback to country index 0 (usually REB) and empty flag type
if (country_index < 0) {
UtilityFunctions::push_warning(
"Failed to find country with identifier \"", identifier, "\" for GUILabel flag segment, falling back to index 0"
);
country_index = 0;
}

const Rect2 flag_image_rect = game_singleton.get_flag_sheet_rect(country_index, flag_type);
ERR_FAIL_COND_V(!flag_image_rect.has_area(), {});

flag_segment_t flag_segment;
flag_segment.instantiate();
ERR_FAIL_NULL_V(flag_segment, {});

flag_segment->set_region(flag_image_rect);
flag_segment->set_atlas(game_singleton.get_flag_sheet_texture());

return flag_segment;
}

void GUILabel::separate_flag_segments(
String const& string, Color const& colour, line_t& line
) const {
const auto push_string_segment = [this, &string, &colour, &line](int64_t start, int64_t end) -> void {
String substring = string.substr(start, end - start);
Expand All @@ -639,17 +731,31 @@ void GUILabel::separate_currency_segments(
int64_t start_pos = 0;
int64_t marker_pos;

const real_t currency_width = currency_texture.is_valid() ? currency_texture->get_width() : 0.0_real;
static const int64_t FLAG_IDENTIFIER_LENGTH = 3;

while ((marker_pos = string.find(get_currency_marker(), start_pos)) != -1) {
while ((marker_pos = string.find(get_flag_marker(), start_pos)) != -1) {
if (start_pos < marker_pos) {
push_string_segment(start_pos, marker_pos);
}

line.segments.push_back(currency_segment_t {});
line.width += currency_width;
marker_pos += get_flag_marker().length();

start_pos = marker_pos + get_currency_marker().length();
start_pos = marker_pos + FLAG_IDENTIFIER_LENGTH;

if (start_pos >= string.length()) {
UtilityFunctions::push_warning(
"Incomplete flag identifier \"", string.substr(marker_pos), "\" at end of line \"", string,
"\" in GUILabel text - discarding!"
);
return;
}

const flag_segment_t flag_segment = make_flag_segment(string.substr(marker_pos, FLAG_IDENTIFIER_LENGTH));

if (flag_segment.is_valid()) {
line.segments.push_back(flag_segment);
line.width += FLAG_SEGMENT_WIDTH;
}
}

if (start_pos < string.length()) {
Expand Down
12 changes: 11 additions & 1 deletion extension/src/openvic-extension/classes/GUILabel.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <godot_cpp/classes/atlas_texture.hpp>
#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/font.hpp>
#include <godot_cpp/classes/font_file.hpp>
Expand Down Expand Up @@ -40,9 +41,14 @@ namespace OpenVic {
godot::String text;
godot::Color colour;
real_t width;

string_segment_t(godot::String&& new_text, godot::Color const& new_colour, real_t new_width);
string_segment_t(godot::String const& new_text, godot::Color const& new_colour, real_t new_width);
string_segment_t(string_segment_t&&) = default;
};
using currency_segment_t = std::monostate;
using segment_t = std::variant<string_segment_t, currency_segment_t>;
using flag_segment_t = godot::Ref<godot::AtlasTexture>;
using segment_t = std::variant<string_segment_t, currency_segment_t, flag_segment_t>;
struct line_t {
std::vector<segment_t> segments;
real_t width {};
Expand Down Expand Up @@ -121,6 +127,10 @@ namespace OpenVic {
void separate_currency_segments(
godot::String const& string, godot::Color const& colour, line_t& line
) const;
static flag_segment_t make_flag_segment(godot::String const& identifier);
void separate_flag_segments(
godot::String const& string, godot::Color const& colour, line_t& line
) const;
std::vector<line_t> wrap_lines(std::vector<line_t>& unwrapped_lines) const;
void adjust_to_content_size();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ Dictionary MenuSingleton::get_topbar_info() const {
}
);
for (auto const& [country_identifier, country_name, power] : industrial_power_from_investments) {
industrial_power_tooltip += "\n" + GUILabel::get_flag_marker() + country_identifier + country_name + ": "
industrial_power_tooltip += "\n" + GUILabel::get_flag_marker() + country_identifier + " " + country_name + ": "
+ GUILabel::get_colour_marker() + "Y" + GUINode::float_to_string_dp(power, 3) + GUILabel::get_colour_marker()
+ "!";
}
Expand Down
Loading