Skip to content

Commit

Permalink
Search panel + text edit box UI generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Hop311 committed Jul 29, 2024
1 parent bf4d061 commit a13c4a2
Show file tree
Hide file tree
Showing 16 changed files with 476 additions and 47 deletions.
22 changes: 13 additions & 9 deletions extension/src/openvic-extension/classes/GUIListBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

using namespace OpenVic;
using namespace godot;
using namespace OpenVic::Utilities::literals;

using OpenVic::Utilities::std_view_to_godot_string;

Expand All @@ -24,14 +25,14 @@ Error GUIListBox::_calculate_max_scroll_index(bool signal) {
if (fixed_item_count <= 0) {
max_scroll_index = 0;
fixed_visible_items = 0;
} else if (fixed_item_height <= 0.0f) {
} else if (fixed_item_height <= 0.0_real) {
max_scroll_index = fixed_item_count - 1;
fixed_visible_items = max_scroll_index;
} else {
const real_t max_height = get_size().height;

fixed_visible_items = max_height / fixed_item_height;
max_scroll_index = fixed_item_count - std::max(fixed_visible_items, 1);
max_scroll_index = std::max(fixed_item_count - std::max(fixed_visible_items, 1), 0);
}
} else {
const int32_t child_count = get_child_count();
Expand All @@ -41,7 +42,7 @@ Error GUIListBox::_calculate_max_scroll_index(bool signal) {
} else {
const real_t max_height = get_size().height;

real_t height_under_max_scroll_index = 0.0f;
real_t height_under_max_scroll_index = 0.0_real;

max_scroll_index = child_count;

Expand Down Expand Up @@ -75,8 +76,9 @@ Error GUIListBox::_calculate_max_scroll_index(bool signal) {
Error GUIListBox::_update_child_positions() {
const int32_t child_count = get_child_count();
const real_t max_height = get_size().height;
const Vector2 offset = gui_listbox != nullptr ? Utilities::to_godot_fvec2(gui_listbox->get_items_offset()) : Vector2 {};

real_t height = 0.0f;
real_t height = 0.0_real;

const int32_t child_scroll_index = fixed ? 0 : scroll_index;

Expand All @@ -87,7 +89,7 @@ Error GUIListBox::_update_child_positions() {
if (index < child_scroll_index) {
child->hide();
} else {
child->set_position({ 0.0f, height });
child->set_position(offset + Vector2 { 0.0_real, height });

height += child->get_size().height; /* Spacing is ignored */

Expand Down Expand Up @@ -130,7 +132,7 @@ void GUIListBox::_notification(int what) {

GUIListBox::GUIListBox()
: gui_listbox { nullptr }, scrollbar { nullptr }, scroll_index { 0 }, max_scroll_index { 0 },
fixed { false }, fixed_item_count { 0 }, fixed_visible_items { 0 }, fixed_item_height { 0.0f } {}
fixed { false }, fixed_item_count { 0 }, fixed_visible_items { 0 }, fixed_item_height { 0.0_real } {}

Vector2 GUIListBox::_get_minimum_size() const {
if (gui_listbox != nullptr) {
Expand Down Expand Up @@ -177,7 +179,7 @@ void GUIListBox::clear() {
fixed = false;
fixed_item_count = 0;
fixed_visible_items = 0;
fixed_item_height = 0.0f;
fixed_item_height = 0.0_real;

clear_children();
if (scrollbar != nullptr) {
Expand Down Expand Up @@ -233,7 +235,7 @@ Error GUIListBox::unset_fixed(bool signal) {

fixed = false;
fixed_item_count = 0;
fixed_item_height = 0.0f;
fixed_item_height = 0.0_real;

return _calculate_max_scroll_index(signal);
}
Expand Down Expand Up @@ -273,7 +275,9 @@ Error GUIListBox::set_gui_listbox(GUI::ListBox const* new_gui_listbox) {
add_child(scrollbar, false, INTERNAL_MODE_FRONT);

const Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size());
scrollbar->set_position({ size.width, 0.0f });
Vector2 position = Utilities::to_godot_fvec2(gui_listbox->get_scrollbar_offset());
position.x += size.width;
scrollbar->set_position(position);
scrollbar->set_length_override(size.height);

static const StringName set_scroll_index_func_name = "set_scroll_index";
Expand Down
3 changes: 2 additions & 1 deletion extension/src/openvic-extension/classes/GUINode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ using namespace OpenVic;
F(TextureRect, texture_rect) \
F(GUIOverlappingElementsBox, gui_overlapping_elements_box) \
F(GUIScrollbar, gui_scrollbar) \
F(GUIListBox, gui_listbox)
F(GUIListBox, gui_listbox) \
F(LineEdit, line_edit)

#define APPLY_TO_TEXTURE_TYPES(F) \
F(GFXSpriteTexture, gfx_sprite_texture) \
Expand Down
3 changes: 3 additions & 0 deletions extension/src/openvic-extension/classes/GUINode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/input_event.hpp>
#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/line_edit.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/panel.hpp>
#include <godot_cpp/classes/ref.hpp>
Expand Down Expand Up @@ -58,6 +59,7 @@ namespace OpenVic {
static GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_node(godot::Node* node);
static GUIScrollbar* get_gui_scrollbar_from_node(godot::Node* node);
static GUIListBox* get_gui_listbox_from_node(godot::Node* node);
static godot::LineEdit* get_line_edit_from_node(godot::Node* node);

godot::Button* get_button_from_nodepath(godot::NodePath const& path) const;
godot::Label* get_label_from_nodepath(godot::NodePath const& path) const;
Expand All @@ -67,6 +69,7 @@ namespace OpenVic {
GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_nodepath(godot::NodePath const& path) const;
GUIScrollbar* get_gui_scrollbar_from_nodepath(godot::NodePath const& path) const;
GUIListBox* get_gui_listbox_from_nodepath(godot::NodePath const& path) const;
godot::LineEdit* get_line_edit_from_nodepath(godot::NodePath const& path) const;

/* Helper functions to get textures from TextureRects and Buttons. */
static godot::Ref<godot::Texture2D> get_texture_from_node(godot::Node* node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ float GameSingleton::get_map_aspect_ratio() const {
return static_cast<float>(get_map_width()) / static_cast<float>(get_map_height());
}

Vector2 GameSingleton::map_position_to_world_coords(fvec2_t const& position) const {
Vector2 GameSingleton::normalise_map_position(fvec2_t const& position) const {
return Utilities::to_godot_fvec2(position) / get_map_dims();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ namespace OpenVic {
int32_t get_map_height() const;
godot::Vector2i get_map_dims() const;
float get_map_aspect_ratio() const;
godot::Vector2 map_position_to_world_coords(fvec2_t const& position) const;
godot::Vector2 normalise_map_position(fvec2_t const& position) const;

/* The cosmetic terrain textures stored in a Texture2DArray. */
godot::Ref<godot::Texture2DArray> get_terrain_texture() const;
Expand Down
185 changes: 183 additions & 2 deletions extension/src/openvic-extension/singletons/MenuSingleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ StringName const& MenuSingleton::_signal_population_menu_pops_changed() {
static const StringName signal_population_menu_pops_changed = "population_menu_pops_changed";
return signal_population_menu_pops_changed;
}
StringName const& MenuSingleton::_signal_search_cache_changed() {
static const StringName signal_search_cache_changed = "search_cache_changed";
return signal_search_cache_changed;
}

String MenuSingleton::get_state_name(State const& state) const {
StateSet const& state_set = state.get_state_set();
Expand Down Expand Up @@ -59,14 +63,47 @@ String MenuSingleton::get_state_name(State const& state) const {

if (owned && split) {
// COUNTRY STATE/CAPITAL
return tr(std_view_to_godot_string(StringUtils::append_string_views(state.get_owner()->get_identifier(), "_ADJ")))
+ " " + name;
return get_country_adjective(*state.get_owner()) + " " + name;
}

// STATE/CAPITAL
return name;
}

String MenuSingleton::get_country_name(CountryInstance const& country) const {
if (country.get_government_type() != nullptr && !country.get_government_type()->get_identifier().empty()) {
const String government_name_key = std_to_godot_string(StringUtils::append_string_views(
country.get_identifier(), "_", country.get_government_type()->get_identifier()
));

String government_name = tr(government_name_key);

if (government_name != government_name_key) {
return government_name;
}
}

return tr(std_view_to_godot_string(country.get_identifier()));
}

String MenuSingleton::get_country_adjective(CountryInstance const& country) const {
static constexpr std::string_view adjective = "_ADJ";

if (country.get_government_type() != nullptr && !country.get_government_type()->get_identifier().empty()) {
const String government_adjective_key = std_to_godot_string(StringUtils::append_string_views(
country.get_identifier(), "_", country.get_government_type()->get_identifier(), adjective
));

String government_adjective = tr(government_adjective_key);

if (government_adjective != government_adjective_key) {
return government_adjective;
}
}

return tr(std_to_godot_string(StringUtils::append_string_views(country.get_identifier(), adjective)));
}

void MenuSingleton::_bind_methods() {
/* PROVINCE OVERVIEW PANEL */
OV_BIND_METHOD(MenuSingleton::get_province_info_from_index, { "index" });
Expand Down Expand Up @@ -142,6 +179,15 @@ void MenuSingleton::_bind_methods() {
BIND_ENUM_CONSTANT(SORT_REBEL_FACTION);
BIND_ENUM_CONSTANT(SORT_SIZE_CHANGE);
BIND_ENUM_CONSTANT(SORT_LITERACY);

/* Find/Search Panel */
OV_BIND_METHOD(MenuSingleton::generate_search_cache);
OV_BIND_METHOD(MenuSingleton::update_search_results, { "text" });
OV_BIND_METHOD(MenuSingleton::get_search_result_rows, { "start", "count" });
OV_BIND_METHOD(MenuSingleton::get_search_result_row_count);
OV_BIND_METHOD(MenuSingleton::get_search_result_position, { "result_index" });

ADD_SIGNAL(MethodInfo(_signal_search_cache_changed()));
}

MenuSingleton* MenuSingleton::get_singleton() {
Expand Down Expand Up @@ -442,3 +488,138 @@ String MenuSingleton::get_longform_date() const {

return Utilities::date_to_formatted_string(instance_manager->get_today());
}

Error MenuSingleton::generate_search_cache() {
GameSingleton const* game_singleton = GameSingleton::get_singleton();
ERR_FAIL_NULL_V(game_singleton, FAILED);
InstanceManager const* instance_manager = game_singleton->get_instance_manager();
ERR_FAIL_NULL_V(instance_manager, FAILED);

search_panel.entry_cache.clear();

std::vector<ProvinceInstance> const& provinces = instance_manager->get_map_instance().get_province_instances();
std::vector<StateSet> const& state_sets = instance_manager->get_map_instance().get_state_manager().get_state_sets();
std::vector<CountryInstance> const& countries = instance_manager->get_country_instance_manager().get_country_instances();

// TODO - reserve actual state count rather than state set count (maybe use a vector of pointers to all states?)
search_panel.entry_cache.reserve(provinces.size() + state_sets.size() + countries.size());

for (ProvinceInstance const& province : provinces) {
String identifier = std_view_to_godot_string(province.get_identifier());
String display_name = tr(GUINode::format_province_name(identifier));
String search_name = display_name.to_lower();

search_panel.entry_cache.push_back({
&province, std::move(display_name), std::move(search_name), identifier.to_lower()
});
}

for (StateSet const& state_set : state_sets) {
for (State const& state : state_set.get_states()) {
String display_name = get_state_name(state);
String search_name = display_name.to_lower();

search_panel.entry_cache.push_back({
// TODO - include state identifier? (region and/or split?)
&state, std::move(display_name), std::move(search_name), {}
});
}
}

for (CountryInstance const& country : countries) {
// TODO - replace with a proper "exists" check
if (country.get_capital() != nullptr) {
String display_name = get_country_name(country);
String search_name = display_name.to_lower();

search_panel.entry_cache.push_back({
&country, std::move(display_name), std::move(search_name),
std_view_to_godot_string(country.get_identifier()).to_lower()
});
}
}

std::sort(search_panel.entry_cache.begin(), search_panel.entry_cache.end(), [](auto const& a, auto const& b) -> bool {
return a.search_name < b.search_name;
});

emit_signal(_signal_search_cache_changed());

return OK;
}

void MenuSingleton::update_search_results(godot::String const& text) {
// Sanatise input
const String search_text = text.strip_edges().to_lower();

search_panel.result_indices.clear();

if (!search_text.is_empty()) {
// Search through cache
for (size_t idx = 0; idx < search_panel.entry_cache.size(); ++idx) {
search_panel_t::entry_t const& entry = search_panel.entry_cache[idx];

if (entry.search_name.begins_with(search_text) || entry.identifier == search_text) {
search_panel.result_indices.push_back(idx);
}
}
}
}

PackedStringArray MenuSingleton::get_search_result_rows(int32_t start, int32_t count) const {
if (search_panel.result_indices.empty()) {
return {};
}

ERR_FAIL_INDEX_V_MSG(
start, search_panel.result_indices.size(), {},
vformat("Invalid start for search panel result rows: %d", start)
);
ERR_FAIL_COND_V_MSG(count <= 0, {}, vformat("Invalid count for search panel result rows: %d", count));

if (start + count > search_panel.result_indices.size()) {
UtilityFunctions::push_warning(
"Requested search result rows beyond the end of the result indices (", start, " + ", count, " > ",
search_panel.result_indices.size(), "), limiting to ", search_panel.result_indices.size() - start, " rows."
);
count = search_panel.result_indices.size() - start;
}

PackedStringArray results;
results.resize(count);

for (size_t idx = 0; idx < count; ++idx) {
results[idx] = search_panel.entry_cache[search_panel.result_indices[start + idx]].display_name;
}

return results;
}

int32_t MenuSingleton::get_search_result_row_count() const {
return search_panel.result_indices.size();
}

Vector2 MenuSingleton::get_search_result_position(int32_t result_index) const {
ERR_FAIL_INDEX_V(result_index, search_panel.result_indices.size(), {});

GameSingleton const* game_singleton = GameSingleton::get_singleton();
ERR_FAIL_NULL_V(game_singleton, {});

struct entry_visitor_t {
fvec2_t operator()(ProvinceInstance const* province) {
return province->get_province_definition().get_centre();
}

fvec2_t operator()(State const* state) {
return (*this)(state->get_capital());
}

fvec2_t operator()(CountryInstance const* country) {
return (*this)(country->get_capital());
}
} entry_visitor;

return game_singleton->normalise_map_position(
std::visit(entry_visitor, search_panel.entry_cache[search_panel.result_indices[result_index]].target)
);
}
Loading

0 comments on commit a13c4a2

Please sign in to comment.