Skip to content

Commit

Permalink
Add support for inline flags in GUILabel
Browse files Browse the repository at this point in the history
  • Loading branch information
Hop311 committed Jan 29, 2025
1 parent 7c59f61 commit 4cf7956
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 48 deletions.
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)) {
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

0 comments on commit 4cf7956

Please sign in to comment.