Skip to content

Commit

Permalink
Add tooltips (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hop311 committed Aug 26, 2024
1 parent ba180ae commit 2924ffb
Show file tree
Hide file tree
Showing 16 changed files with 409 additions and 44 deletions.
12 changes: 11 additions & 1 deletion extension/src/openvic-extension/classes/GUIButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,14 @@
using namespace godot;
using namespace OpenVic;

void GUIButton::_bind_methods() {}
GUI_TOOLTIP_IMPLEMENTATIONS(GUIButton)

void GUIButton::_bind_methods() {
GUI_TOOLTIP_BIND_METHODS(GUIButton)
}

void GUIButton::_notification(int what) {
_tooltip_notification(what);
}

GUIButton::GUIButton() : tooltip_active { false } {}
9 changes: 9 additions & 0 deletions extension/src/openvic-extension/classes/GUIButton.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

#include <godot_cpp/classes/button.hpp>

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

namespace OpenVic {
class GUIButton : public godot::Button {
GDCLASS(GUIButton, godot::Button)

GUI_TOOLTIP_DEFINITIONS

protected:
static void _bind_methods();

void _notification(int what);

public:
GUIButton();
};
}
117 changes: 117 additions & 0 deletions extension/src/openvic-extension/classes/GUIHasTooltip.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/variant/string.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/variant/vector2.hpp>

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

#include "openvic-extension/singletons/MenuSingleton.hpp"
#include "openvic-extension/utility/ClassBindings.hpp"
#include "openvic-extension/utility/Utilities.hpp"

/* To add tooltip functionality to a class:
* - the class must be derived from Control.
* - add GUI_TOOLTIP_DEFINITIONS to the class definition, bearing in mind that it leaves visibility as private.
* - add GUI_TOOLTIP_IMPLEMENTATIONS(CLASS) to the class' source file.
* - add GUI_TOOLTIP_BIND_METHODS(CLASS) to the class' _bind_methods implementation.
* - call _tooltip_notification from the class' _notification method.
* - initialise tooltip_active to false in the class' constructor. */

#define GUI_TOOLTIP_DEFINITIONS \
public: \
void set_tooltip_string_and_substitution_dict( \
godot::String const& new_tooltip_string, godot::Dictionary const& new_tooltip_substitution_dict \
); \
void set_tooltip_string(godot::String const& new_tooltip_string); \
void set_tooltip_substitution_dict(godot::Dictionary const& new_tooltip_substitution_dict); \
void clear_tooltip(); \
private: \
godot::String PROPERTY(tooltip_string); \
godot::Dictionary PROPERTY(tooltip_substitution_dict); \
bool PROPERTY_CUSTOM_PREFIX(tooltip_active, is); \
void _tooltip_notification(int what); \
void _set_tooltip_visibility(bool visible);

#define GUI_TOOLTIP_IMPLEMENTATIONS(CLASS) \
void CLASS::set_tooltip_string_and_substitution_dict( \
String const& new_tooltip_string, Dictionary const& new_tooltip_substitution_dict \
) { \
if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \
UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \
} \
if (tooltip_string != new_tooltip_string || tooltip_substitution_dict != new_tooltip_substitution_dict) { \
tooltip_string = new_tooltip_string; \
tooltip_substitution_dict = new_tooltip_substitution_dict; \
if (tooltip_active) { \
_set_tooltip_visibility(!tooltip_string.is_empty()); \
} \
} \
} \
void CLASS::set_tooltip_string(String const& new_tooltip_string) { \
if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \
UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \
} \
if (tooltip_string != new_tooltip_string) { \
tooltip_string = new_tooltip_string; \
if (tooltip_active) { \
_set_tooltip_visibility(!tooltip_string.is_empty()); \
} \
} \
} \
void CLASS::set_tooltip_substitution_dict(Dictionary const& new_tooltip_substitution_dict) { \
if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \
UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \
} \
if (tooltip_substitution_dict != new_tooltip_substitution_dict) { \
tooltip_substitution_dict = new_tooltip_substitution_dict; \
if (tooltip_active) { \
_set_tooltip_visibility(!tooltip_string.is_empty()); \
} \
} \
} \
void CLASS::clear_tooltip() { \
set_tooltip_string_and_substitution_dict({}, {}); \
} \
void CLASS::_tooltip_notification(int what) { \
if (what == NOTIFICATION_MOUSE_ENTER || what == NOTIFICATION_MOUSE_EXIT) { \
if (tooltip_active != (what == NOTIFICATION_MOUSE_ENTER)) { \
tooltip_active = !tooltip_active; \
if (!tooltip_string.is_empty()) { \
_set_tooltip_visibility(tooltip_active); \
} \
} \
} \
} \
void CLASS::_set_tooltip_visibility(bool visible) { \
MenuSingleton* menu_singleton = MenuSingleton::get_singleton(); \
ERR_FAIL_NULL(menu_singleton); \
if (visible) { \
using namespace OpenVic::Utilities::literals; \
static const Vector2 offset { 0.0_real, 64.0_real }; \
menu_singleton->show_tooltip(tooltip_string, tooltip_substitution_dict, get_global_position() + offset); \
} else { \
menu_singleton->hide_tooltip(); \
} \
}

#define GUI_TOOLTIP_BIND_METHODS(CLASS) \
OV_BIND_METHOD(CLASS::get_tooltip_string); \
OV_BIND_METHOD(CLASS::set_tooltip_string, { "new_tooltip_string" }); \
OV_BIND_METHOD(CLASS::get_tooltip_substitution_dict); \
OV_BIND_METHOD(CLASS::set_tooltip_substitution_dict, { "new_tooltip_substitution_dict" }); \
OV_BIND_METHOD( \
CLASS::set_tooltip_string_and_substitution_dict, { "new_tooltip_string", "new_tooltip_substitution_dict" } \
); \
OV_BIND_METHOD(CLASS::clear_tooltip); \
OV_BIND_METHOD(CLASS::is_tooltip_active); \
ADD_PROPERTY( \
PropertyInfo(Variant::STRING, "tooltip_string", PROPERTY_HINT_MULTILINE_TEXT), \
"set_tooltip_string", "get_tooltip_string" \
); \
ADD_PROPERTY( \
PropertyInfo(Variant::DICTIONARY, "tooltip_substitution_dict"), \
"set_tooltip_substitution_dict", "get_tooltip_substitution_dict" \
); \
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tooltip_active"), "", "is_tooltip_active");
9 changes: 8 additions & 1 deletion extension/src/openvic-extension/classes/GUILabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ using namespace OpenVic::Utilities::literals;

static constexpr int32_t DEFAULT_FONT_SIZE = 16;

GUI_TOOLTIP_IMPLEMENTATIONS(GUILabel)

void GUILabel::_bind_methods() {
GUI_TOOLTIP_BIND_METHODS(GUILabel)

OV_BIND_METHOD(GUILabel::clear);
OV_BIND_METHOD(GUILabel::get_gui_text_name);

Expand Down Expand Up @@ -80,6 +84,8 @@ void GUILabel::_bind_methods() {
}

void GUILabel::_notification(int what) {
_tooltip_notification(what);

switch (what) {
case NOTIFICATION_RESIZED:
case NOTIFICATION_TRANSLATION_CHANGED: {
Expand Down Expand Up @@ -146,7 +152,8 @@ void GUILabel::_notification(int what) {
}

GUILabel::GUILabel()
: gui_text { nullptr },
: tooltip_active { false },
gui_text { nullptr },
text {},
substitution_dict {},
horizontal_alignment { HORIZONTAL_ALIGNMENT_LEFT },
Expand Down
3 changes: 3 additions & 0 deletions extension/src/openvic-extension/classes/GUILabel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
#include <openvic-simulation/interface/GUI.hpp>

#include "openvic-extension/classes/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GUIHasTooltip.hpp"

namespace OpenVic {
class GUILabel : public godot::Control {
GDCLASS(GUILabel, godot::Control)

GUI_TOOLTIP_DEFINITIONS

using colour_instructions_t = std::vector<std::pair<int64_t, char>>;

GUI::Text const* PROPERTY(gui_text);
Expand Down
12 changes: 11 additions & 1 deletion extension/src/openvic-extension/classes/GUITextureRect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,14 @@
using namespace godot;
using namespace OpenVic;

void GUITextureRect::_bind_methods() {}
GUI_TOOLTIP_IMPLEMENTATIONS(GUITextureRect)

void GUITextureRect::_bind_methods() {
GUI_TOOLTIP_BIND_METHODS(GUITextureRect)
}

void GUITextureRect::_notification(int what) {
_tooltip_notification(what);
}

GUITextureRect::GUITextureRect() : tooltip_active { false } {}
9 changes: 9 additions & 0 deletions extension/src/openvic-extension/classes/GUITextureRect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

#include <godot_cpp/classes/texture_rect.hpp>

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

namespace OpenVic {
class GUITextureRect : public godot::TextureRect {
GDCLASS(GUITextureRect, godot::TextureRect)

GUI_TOOLTIP_DEFINITIONS

protected:
static void _bind_methods();

void _notification(int what);

public:
GUITextureRect();
};
}
23 changes: 23 additions & 0 deletions extension/src/openvic-extension/singletons/MenuSingleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ StringName const& MenuSingleton::_signal_search_cache_changed() {
static const StringName signal_search_cache_changed = "search_cache_changed";
return signal_search_cache_changed;
}
StringName const& MenuSingleton::_signal_update_tooltip() {
static const StringName signal_update_tooltip = "update_tooltip";
return signal_update_tooltip;
}

String MenuSingleton::get_state_name(State const& state) const {
StateSet const& state_set = state.get_state_set();
Expand Down Expand Up @@ -103,6 +107,15 @@ String MenuSingleton::get_country_adjective(CountryInstance const& country) cons
}

void MenuSingleton::_bind_methods() {
/* TOOLTIP */
OV_BIND_METHOD(MenuSingleton::show_tooltip, { "text", "substitution_dict", "position" });
OV_BIND_METHOD(MenuSingleton::hide_tooltip);

ADD_SIGNAL(MethodInfo(
_signal_update_tooltip(), PropertyInfo(Variant::STRING, "text"),
PropertyInfo(Variant::DICTIONARY, "substitution_dict"), PropertyInfo(Variant::VECTOR2, "position")
));

/* PROVINCE OVERVIEW PANEL */
OV_BIND_METHOD(MenuSingleton::get_province_info_from_index, { "index" });
OV_BIND_METHOD(MenuSingleton::get_province_building_count);
Expand Down Expand Up @@ -205,6 +218,16 @@ MenuSingleton::~MenuSingleton() {
singleton = nullptr;
}

/* TOOLTIP */

void MenuSingleton::show_tooltip(String const& text, Dictionary const& substitution_dict, Vector2 const& position) {
emit_signal(_signal_update_tooltip(), text, substitution_dict, position);
}

void MenuSingleton::hide_tooltip() {
show_tooltip({}, {}, {});
}

/* PROVINCE OVERVIEW PANEL */

static TypedArray<Dictionary> _make_buildings_dict_array(
Expand Down
10 changes: 10 additions & 0 deletions extension/src/openvic-extension/singletons/MenuSingleton.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ namespace OpenVic {
static godot::StringName const& _signal_population_menu_pops_changed();
/* Emitted when the collection of possible search results changes. */
static godot::StringName const& _signal_search_cache_changed();
/* Emitted when the current tooltip changes. Arguments: text (godot::String), substitution_dict (godot::Dictionary),
* position (godot::Vector2). If text is empty then the tooltip will be hidden, otherwise the text will be shown at
* the given position. */
static godot::StringName const& _signal_update_tooltip();

godot::String get_state_name(State const& state) const;
godot::String get_country_name(CountryInstance const& country) const;
Expand All @@ -110,6 +114,12 @@ namespace OpenVic {
MenuSingleton();
~MenuSingleton();

/* TOOLTIP */
void show_tooltip(
godot::String const& text, godot::Dictionary const& substitution_dict, godot::Vector2 const& position
);
void hide_tooltip();

/* PROVINCE OVERVIEW PANEL */
/* Get info to display in Province Overview Panel, packaged in a Dictionary using StringName constants as keys. */
godot::Dictionary get_province_info_from_index(int32_t index) const;
Expand Down
1 change: 1 addition & 0 deletions extension/src/openvic-extension/utility/UITools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ static bool generate_icon(generate_gui_args_t&& args) {

args.result = godot_progress_bar;
} else if (icon.get_sprite()->is_type<GFX::PieChart>()) {
// TODO - add custom node supporting slice-based tooltip
GUITextureRect* gui_texture_rect = nullptr;
ret &= new_control(gui_texture_rect, icon, args.name);
ERR_FAIL_NULL_V_MSG(
Expand Down
8 changes: 7 additions & 1 deletion game/src/Game/GameSession/GameSession.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=20 format=3 uid="uid://bgnupcshe1m7r"]
[gd_scene load_steps=21 format=3 uid="uid://bgnupcshe1m7r"]

[ext_resource type="Script" path="res://src/Game/GameSession/GameSession.gd" id="1_eklvp"]
[ext_resource type="PackedScene" uid="uid://cvl76duuym1wq" path="res://src/Game/MusicConductor/MusicPlayer.tscn" id="2_kt6aa"]
Expand All @@ -19,6 +19,7 @@
[ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/TradeMenu.gd" id="10_mv1r6"]
[ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/DiplomacyMenu.gd" id="11_fu7ys"]
[ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/MilitaryMenu.gd" id="12_6h6nc"]
[ext_resource type="Script" path="res://src/Game/GameSession/Tooltip.gd" id="20_3306e"]

[node name="GameSession" type="Control" node_paths=PackedStringArray("_model_manager", "_game_session_menu")]
editor_description = "SS-102, UI-546"
Expand Down Expand Up @@ -156,6 +157,11 @@ offset_left = -150.0
offset_right = 0.0
grow_horizontal = 0

[node name="Tooltip" type="GUINode" parent="UICanvasLayer/UI"]
layout_mode = 1
anchors_preset = 15
script = ExtResource("20_3306e")

[connection signal="detailed_view_changed" from="MapView" to="ModelManager" method="set_visible"]
[connection signal="map_view_camera_changed" from="MapView" to="UICanvasLayer/UI/MapControlPanel" method="_on_map_view_camera_changed"]
[connection signal="game_session_menu_button_pressed" from="UICanvasLayer/UI/MapControlPanel" to="." method="_on_game_session_menu_button_pressed"]
Expand Down
Loading

0 comments on commit 2924ffb

Please sign in to comment.