Editor: Display deprecated/experimental messages in tooltips

This commit is contained in:
Danil Alexeev 2024-04-11 11:21:44 +03:00
parent 3b1806182a
commit a714cb9f65
No known key found for this signature in database
GPG Key ID: 124453E157DA8DC7
17 changed files with 819 additions and 380 deletions

View File

@ -47,6 +47,7 @@
#include "scene/gui/check_box.h" #include "scene/gui/check_box.h"
#include "scene/gui/label.h" #include "scene/gui/label.h"
#include "scene/gui/line_edit.h" #include "scene/gui/line_edit.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h" #include "scene/gui/option_button.h"
#include "scene/gui/popup_menu.h" #include "scene/gui/popup_menu.h"
#include "scene/gui/spin_box.h" #include "scene/gui/spin_box.h"
@ -872,7 +873,13 @@ ConnectDialog::~ConnectDialog() {
Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
// If it's not a doc tooltip, fallback to the default one. // If it's not a doc tooltip, fallback to the default one.
return p_text.contains("::") ? nullptr : memnew(EditorHelpTooltip(p_text)); if (p_text.contains("::")) {
return nullptr;
}
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ConnectionsDockTree *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
struct _ConnectionsDockMethodInfoSort { struct _ConnectionsDockMethodInfoSort {
@ -1458,8 +1465,8 @@ void ConnectionsDock::update_tree() {
section_item = tree->create_item(root); section_item = tree->create_item(root);
section_item->set_text(0, class_name); section_item->set_text(0, class_name);
// `|` separators used in `EditorHelpTooltip` for formatting. // `|` separators used in `EditorHelpBit`.
section_item->set_tooltip_text(0, "class|" + doc_class_name + "||"); section_item->set_tooltip_text(0, "class|" + doc_class_name + "|");
section_item->set_icon(0, class_icon); section_item->set_icon(0, class_icon);
section_item->set_selectable(0, false); section_item->set_selectable(0, false);
section_item->set_editable(0, false); section_item->set_editable(0, false);
@ -1490,8 +1497,8 @@ void ConnectionsDock::update_tree() {
sinfo["args"] = argnames; sinfo["args"] = argnames;
signal_item->set_metadata(0, sinfo); signal_item->set_metadata(0, sinfo);
signal_item->set_icon(0, get_editor_theme_icon(SNAME("Signal"))); signal_item->set_icon(0, get_editor_theme_icon(SNAME("Signal")));
// `|` separators used in `EditorHelpTooltip` for formatting. // `|` separators used in `EditorHelpBit`.
signal_item->set_tooltip_text(0, "signal|" + doc_class_name + "|" + String(signal_name) + "|" + signame.trim_prefix(mi.name)); signal_item->set_tooltip_text(0, "signal|" + doc_class_name + "|" + String(signal_name));
// List existing connections. // List existing connections.
List<Object::Connection> existing_connections; List<Object::Connection> existing_connections;

View File

@ -191,7 +191,7 @@ public:
////////////////////////////////////////// //////////////////////////////////////////
// Custom `Tree` needed to use `EditorHelpTooltip` to display signal documentation. // Custom `Tree` needed to use `EditorHelpBit` to display signal documentation.
class ConnectionsDockTree : public Tree { class ConnectionsDockTree : public Tree {
virtual Control *make_custom_tooltip(const String &p_text) const; virtual Control *make_custom_tooltip(const String &p_text) const;
}; };

View File

@ -202,8 +202,7 @@ void CreateDialog::_update_search() {
select_type(_top_result(candidates, search_text)); select_type(_top_result(candidates, search_text));
} else { } else {
favorite->set_disabled(true); favorite->set_disabled(true);
help_bit->set_text(vformat(TTR("No results for \"%s\"."), search_text)); help_bit->set_custom_text(String(), String(), vformat(TTR("No results for \"%s\"."), search_text.replace("[", "[lb]")));
help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5));
get_ok_button()->set_disabled(true); get_ok_button()->set_disabled(true);
search_options->deselect_all(); search_options->deselect_all();
} }
@ -502,17 +501,7 @@ void CreateDialog::select_type(const String &p_type, bool p_center_on_item) {
to_select->select(0); to_select->select(0);
search_options->scroll_to_item(to_select, p_center_on_item); search_options->scroll_to_item(to_select, p_center_on_item);
String text = help_bit->get_class_description(p_type); help_bit->parse_symbol("class|" + p_type + "|");
if (!text.is_empty()) {
// Display both class name and description, since the help bit may be displayed
// far away from the location (especially if the dialog was resized to be taller).
help_bit->set_text(vformat("[b]%s[/b]: %s", p_type, text));
help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
} else {
// Use nested `vformat()` as translators shouldn't interfere with BBCode tags.
help_bit->set_text(vformat(TTR("No description available for %s."), vformat("[b]%s[/b]", p_type)));
help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5));
}
favorite->set_disabled(false); favorite->set_disabled(false);
favorite->set_pressed(favorite_list.has(p_type)); favorite->set_pressed(favorite_list.has(p_type));
@ -837,6 +826,7 @@ CreateDialog::CreateDialog() {
vbc->add_margin_child(TTR("Matches:"), search_options, true); vbc->add_margin_child(TTR("Matches:"), search_options, true);
help_bit = memnew(EditorHelpBit); help_bit = memnew(EditorHelpBit);
help_bit->set_content_height_limits(64 * EDSCALE, 64 * EDSCALE);
help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested)); help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested));
vbc->add_margin_child(TTR("Description:"), help_bit); vbc->add_margin_child(TTR("Description:"), help_bit);

View File

@ -593,6 +593,10 @@ void EditorBuildProfileManager::_action_confirm() {
} }
} }
void EditorBuildProfileManager::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
void EditorBuildProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) { void EditorBuildProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
TreeItem *class_item = class_list->create_item(p_parent); TreeItem *class_item = class_list->create_item(p_parent);
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
@ -646,21 +650,10 @@ void EditorBuildProfileManager::_class_list_item_selected() {
Variant md = item->get_metadata(0); Variant md = item->get_metadata(0);
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) { if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
String text = description_bit->get_class_description(md); description_bit->parse_symbol("class|" + md.operator String() + "|");
if (!text.is_empty()) {
// Display both class name and description, since the help bit may be displayed
// far away from the location (especially if the dialog was resized to be taller).
description_bit->set_text(vformat("[b]%s[/b]: %s", md, text));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
} else {
// Use nested `vformat()` as translators shouldn't interfere with BBCode tags.
description_bit->set_text(vformat(TTR("No description available for %s."), vformat("[b]%s[/b]", md)));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5));
}
} else if (md.get_type() == Variant::INT) { } else if (md.get_type() == Variant::INT) {
String build_option_description = EditorBuildProfile::get_build_option_description(EditorBuildProfile::BuildOption((int)md)); String build_option_description = EditorBuildProfile::get_build_option_description(EditorBuildProfile::BuildOption((int)md));
description_bit->set_text(vformat("[b]%s[/b]: %s", TTR(item->get_text(0)), TTRGET(build_option_description))); description_bit->set_custom_text(TTR(item->get_text(0)), String(), TTRGET(build_option_description));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
} }
} }
@ -864,7 +857,8 @@ EditorBuildProfileManager::EditorBuildProfileManager() {
main_vbc->add_margin_child(TTR("Configure Engine Compilation Profile:"), class_list, true); main_vbc->add_margin_child(TTR("Configure Engine Compilation Profile:"), class_list, true);
description_bit = memnew(EditorHelpBit); description_bit = memnew(EditorHelpBit);
description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE); description_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
description_bit->connect("request_hide", callable_mp(this, &EditorBuildProfileManager::_hide_requested));
main_vbc->add_margin_child(TTR("Description:"), description_bit, false); main_vbc->add_margin_child(TTR("Description:"), description_bit, false);
confirm_dialog = memnew(ConfirmationDialog); confirm_dialog = memnew(ConfirmationDialog);

View File

@ -150,6 +150,7 @@ class EditorBuildProfileManager : public AcceptDialog {
void _profile_action(int p_action); void _profile_action(int p_action);
void _action_confirm(); void _action_confirm();
void _hide_requested();
void _update_edited_profile(); void _update_edited_profile();
void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected); void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected);

View File

@ -493,6 +493,10 @@ void EditorFeatureProfileManager::_profile_selected(int p_what) {
_update_selected_profile(); _update_selected_profile();
} }
void EditorFeatureProfileManager::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) { void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
TreeItem *class_item = class_list->create_item(p_parent); TreeItem *class_item = class_list->create_item(p_parent);
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
@ -555,22 +559,10 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
Variant md = item->get_metadata(0); Variant md = item->get_metadata(0);
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) { if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
String text = description_bit->get_class_description(md); description_bit->parse_symbol("class|" + md.operator String() + "|");
if (!text.is_empty()) {
// Display both class name and description, since the help bit may be displayed
// far away from the location (especially if the dialog was resized to be taller).
description_bit->set_text(vformat("[b]%s[/b]: %s", md, text));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
} else {
// Use nested `vformat()` as translators shouldn't interfere with BBCode tags.
description_bit->set_text(vformat(TTR("No description available for %s."), vformat("[b]%s[/b]", md)));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5));
}
} else if (md.get_type() == Variant::INT) { } else if (md.get_type() == Variant::INT) {
String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature((int)md)); String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature((int)md));
description_bit->set_text(vformat("[b]%s[/b]: %s", TTR(item->get_text(0)), TTRGET(feature_description))); description_bit->set_custom_text(TTR(item->get_text(0)), String(), TTRGET(feature_description));
description_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
return; return;
} else { } else {
return; return;
@ -991,8 +983,9 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
property_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); property_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
description_bit = memnew(EditorHelpBit); description_bit = memnew(EditorHelpBit);
description_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
description_bit->connect("request_hide", callable_mp(this, &EditorFeatureProfileManager::_hide_requested));
property_list_vbc->add_margin_child(TTR("Description:"), description_bit, false); property_list_vbc->add_margin_child(TTR("Description:"), description_bit, false);
description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
property_list = memnew(Tree); property_list = memnew(Tree);
property_list_vbc->add_margin_child(TTR("Extra Options:"), property_list, true); property_list_vbc->add_margin_child(TTR("Extra Options:"), property_list, true);

View File

@ -142,6 +142,7 @@ class EditorFeatureProfileManager : public AcceptDialog {
void _profile_action(int p_action); void _profile_action(int p_action);
void _profile_selected(int p_what); void _profile_selected(int p_what);
void _hide_requested();
String current_profile; String current_profile;
void _update_profile_list(const String &p_select_profile = String()); void _update_profile_list(const String &p_select_profile = String());

File diff suppressed because it is too large Load Diff

View File

@ -35,9 +35,9 @@
#include "editor/code_editor.h" #include "editor/code_editor.h"
#include "editor/doc_tools.h" #include "editor/doc_tools.h"
#include "editor/editor_plugin.h" #include "editor/editor_plugin.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h" #include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h" #include "scene/gui/panel_container.h"
#include "scene/gui/popup.h"
#include "scene/gui/rich_text_label.h" #include "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h" #include "scene/gui/split_container.h"
#include "scene/gui/tab_container.h" #include "scene/gui/tab_container.h"
@ -251,53 +251,91 @@ public:
~EditorHelp(); ~EditorHelp();
}; };
class EditorHelpBit : public MarginContainer { class EditorHelpBit : public VBoxContainer {
GDCLASS(EditorHelpBit, MarginContainer); GDCLASS(EditorHelpBit, VBoxContainer);
inline static HashMap<StringName, String> doc_class_cache; struct DocType {
inline static HashMap<StringName, HashMap<StringName, String>> doc_property_cache; String type;
inline static HashMap<StringName, HashMap<StringName, String>> doc_method_cache; String enumeration;
inline static HashMap<StringName, HashMap<StringName, String>> doc_signal_cache; bool is_bitfield = false;
inline static HashMap<StringName, HashMap<StringName, String>> doc_theme_item_cache; };
RichTextLabel *rich_text = nullptr; struct ArgumentData {
String name;
DocType doc_type;
String default_value;
};
struct HelpData {
String description;
String deprecated_message;
String experimental_message;
DocType doc_type; // For method return type.
Vector<ArgumentData> arguments; // For methods and signals.
};
inline static HashMap<StringName, HelpData> doc_class_cache;
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_property_cache;
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_method_cache;
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_signal_cache;
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_theme_item_cache;
RichTextLabel *title = nullptr;
RichTextLabel *content = nullptr;
String symbol_class_name;
String symbol_type;
String symbol_visible_type;
String symbol_name;
HelpData help_data;
float content_min_height = 0.0;
float content_max_height = 0.0;
static HelpData _get_class_help_data(const StringName &p_class_name);
static HelpData _get_property_help_data(const StringName &p_class_name, const StringName &p_property_name);
static HelpData _get_method_help_data(const StringName &p_class_name, const StringName &p_method_name);
static HelpData _get_signal_help_data(const StringName &p_class_name, const StringName &p_signal_name);
static HelpData _get_theme_item_help_data(const StringName &p_class_name, const StringName &p_theme_item_name);
void _add_type_to_title(const DocType &p_doc_type);
void _update_labels();
void _go_to_help(const String &p_what); void _go_to_help(const String &p_what);
void _meta_clicked(const String &p_select); void _meta_clicked(const String &p_select);
String text;
protected: protected:
String doc_class_name;
String custom_description;
static void _bind_methods(); static void _bind_methods();
void _notification(int p_what); void _notification(int p_what);
public: public:
String get_class_description(const StringName &p_class_name) const; void parse_symbol(const String &p_symbol);
String get_property_description(const StringName &p_class_name, const StringName &p_property_name) const; void set_custom_text(const String &p_type, const String &p_name, const String &p_description);
String get_method_description(const StringName &p_class_name, const StringName &p_method_name) const; void prepend_description(const String &p_text);
String get_signal_description(const StringName &p_class_name, const StringName &p_signal_name) const;
String get_theme_item_description(const StringName &p_class_name, const StringName &p_theme_item_name) const;
RichTextLabel *get_rich_text() { return rich_text; } void set_content_height_limits(float p_min, float p_max);
void set_text(const String &p_text); void update_content_height();
EditorHelpBit(); EditorHelpBit(const String &p_symbol = String());
}; };
class EditorHelpTooltip : public EditorHelpBit { // Standard tooltips do not allow you to hover over them.
GDCLASS(EditorHelpTooltip, EditorHelpBit); // This class is intended as a temporary workaround.
class EditorHelpBitTooltip : public PopupPanel {
GDCLASS(EditorHelpBitTooltip, PopupPanel);
String tooltip_text; Timer *timer = nullptr;
protected: protected:
void _notification(int p_what); void _notification(int p_what);
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
public: public:
void parse_tooltip(const String &p_text); static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target);
EditorHelpTooltip(const String &p_text = String(), const String &p_custom_description = String()); void popup_under_cursor();
EditorHelpBitTooltip(Control *p_target);
}; };
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED) #if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)

View File

@ -44,6 +44,7 @@
#include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h" #include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h" #include "editor/themes/editor_theme_manager.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/spin_box.h" #include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h" #include "scene/gui/texture_rect.h"
#include "scene/property_utils.h" #include "scene/property_utils.h"
@ -916,34 +917,35 @@ void EditorProperty::_update_pin_flags() {
} }
Control *EditorProperty::make_custom_tooltip(const String &p_text) const { Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
EditorHelpBit *tooltip = nullptr; String custom_warning;
if (object->has_method("_get_property_warning")) {
custom_warning = object->call("_get_property_warning", property);
}
if (has_doc_tooltip || !custom_warning.is_empty()) {
EditorHelpBit *help_bit = memnew(EditorHelpBit);
if (has_doc_tooltip) { if (has_doc_tooltip) {
String custom_description; help_bit->parse_symbol(p_text);
const EditorInspector *inspector = get_parent_inspector(); const EditorInspector *inspector = get_parent_inspector();
if (inspector) { if (inspector) {
custom_description = inspector->get_custom_property_description(p_text); const String custom_description = inspector->get_custom_property_description(p_text);
if (!custom_description.is_empty()) {
help_bit->prepend_description(custom_description);
} }
tooltip = memnew(EditorHelpTooltip(p_text, custom_description));
}
if (object->has_method("_get_property_warning")) {
String warn = object->call("_get_property_warning", property);
if (!warn.is_empty()) {
String prev_text;
if (tooltip == nullptr) {
tooltip = memnew(EditorHelpBit());
tooltip->set_text(p_text);
tooltip->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 0));
} else {
prev_text = tooltip->get_rich_text()->get_text() + "\n";
}
tooltip->set_text(prev_text + "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + warn + "[/color][/b]");
} }
} }
return tooltip; if (!custom_warning.is_empty()) {
help_bit->prepend_description("[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]");
}
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorProperty *>(this));
return memnew(Control); // Make the standard tooltip invisible.
}
return nullptr;
} }
void EditorProperty::menu_option(int p_option) { void EditorProperty::menu_option(int p_option) {
@ -1178,7 +1180,14 @@ void EditorInspectorCategory::_notification(int p_what) {
} }
Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const { Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const {
return doc_class_name.is_empty() ? nullptr : memnew(EditorHelpTooltip(p_text)); // If it's not a doc tooltip, fallback to the default one.
if (doc_class_name.is_empty()) {
return nullptr;
}
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorInspectorCategory *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
Size2 EditorInspectorCategory::get_minimum_size() const { Size2 EditorInspectorCategory::get_minimum_size() const {
@ -2887,8 +2896,8 @@ void EditorInspector::update_tree() {
category->doc_class_name = doc_name; category->doc_class_name = doc_name;
if (use_doc_hints) { if (use_doc_hints) {
// `|` separator used in `EditorHelpTooltip` for formatting. // `|` separators used in `EditorHelpBit`.
category->set_tooltip_text("class|" + doc_name + "||"); category->set_tooltip_text("class|" + doc_name + "|");
} }
} }
@ -3368,15 +3377,15 @@ void EditorInspector::update_tree() {
ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), CONNECT_DEFERRED); ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), CONNECT_DEFERRED);
if (use_doc_hints) { if (use_doc_hints) {
// `|` separator used in `EditorHelpTooltip` for formatting. // `|` separators used in `EditorHelpBit`.
if (theme_item_name.is_empty()) { if (theme_item_name.is_empty()) {
if (p.usage & PROPERTY_USAGE_INTERNAL) { if (p.usage & PROPERTY_USAGE_INTERNAL) {
ep->set_tooltip_text("internal_property|" + classname + "|" + property_prefix + p.name + "|"); ep->set_tooltip_text("internal_property|" + classname + "|" + property_prefix + p.name);
} else { } else {
ep->set_tooltip_text("property|" + classname + "|" + property_prefix + p.name + "|"); ep->set_tooltip_text("property|" + classname + "|" + property_prefix + p.name);
} }
} else { } else {
ep->set_tooltip_text("theme_item|" + classname + "|" + theme_item_name + "|"); ep->set_tooltip_text("theme_item|" + classname + "|" + theme_item_name);
} }
ep->has_doc_tooltip = true; ep->has_doc_tooltip = true;
} }

View File

@ -41,6 +41,7 @@ class ConfirmationDialog;
class EditorInspector; class EditorInspector;
class EditorValidationPanel; class EditorValidationPanel;
class LineEdit; class LineEdit;
class MarginContainer;
class OptionButton; class OptionButton;
class PanelContainer; class PanelContainer;
class PopupMenu; class PopupMenu;

View File

@ -40,6 +40,7 @@
#include "editor/inspector_dock.h" #include "editor/inspector_dock.h"
#include "editor/themes/editor_scale.h" #include "editor/themes/editor_scale.h"
#include "scene/gui/button.h" #include "scene/gui/button.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) { bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) {

View File

@ -37,6 +37,7 @@
class Button; class Button;
class EditorSpinSlider; class EditorSpinSlider;
class MarginContainer;
class EditorPropertyArrayObject : public RefCounted { class EditorPropertyArrayObject : public RefCounted {
GDCLASS(EditorPropertyArrayObject, RefCounted); GDCLASS(EditorPropertyArrayObject, RefCounted);

View File

@ -2267,7 +2267,9 @@ ThemeTypeDialog::ThemeTypeDialog() {
/////////////////////// ///////////////////////
Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const { Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const {
return memnew(EditorHelpTooltip(p_text)); EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ThemeItemLabel *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
@ -2436,8 +2438,8 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_
item_name->set_h_size_flags(SIZE_EXPAND_FILL); item_name->set_h_size_flags(SIZE_EXPAND_FILL);
item_name->set_clip_text(true); item_name->set_clip_text(true);
item_name->set_text(p_item_name); item_name->set_text(p_item_name);
// `|` separators used in `EditorHelpTooltip` for formatting. // `|` separators used in `EditorHelpBit`.
item_name->set_tooltip_text("theme_item|" + edited_type + "|" + p_item_name + "|"); item_name->set_tooltip_text("theme_item|" + edited_type + "|" + p_item_name);
item_name->set_mouse_filter(Control::MOUSE_FILTER_STOP); item_name->set_mouse_filter(Control::MOUSE_FILTER_STOP);
item_name_container->add_child(item_name); item_name_container->add_child(item_name);

View File

@ -321,7 +321,7 @@ public:
ThemeTypeDialog(); ThemeTypeDialog();
}; };
// Custom `Label` needed to use `EditorHelpTooltip` to display theme item documentation. // Custom `Label` needed to use `EditorHelpBit` to display theme item documentation.
class ThemeItemLabel : public Label { class ThemeItemLabel : public Label {
virtual Control *make_custom_tooltip(const String &p_text) const; virtual Control *make_custom_tooltip(const String &p_text) const;
}; };

View File

@ -87,7 +87,7 @@ void PropertySelector::_update_search() {
} }
search_options->clear(); search_options->clear();
help_bit->set_text(""); help_bit->set_custom_text(String(), String(), String());
TreeItem *root = search_options->create_item(); TreeItem *root = search_options->create_item();
@ -353,7 +353,7 @@ void PropertySelector::_confirmed() {
} }
void PropertySelector::_item_selected() { void PropertySelector::_item_selected() {
help_bit->set_text(""); help_bit->set_custom_text(String(), String(), String());
TreeItem *item = search_options->get_selected(); TreeItem *item = search_options->get_selected();
if (!item) { if (!item) {
@ -372,25 +372,21 @@ void PropertySelector::_item_selected() {
String text; String text;
while (!class_type.is_empty()) { while (!class_type.is_empty()) {
text = properties ? help_bit->get_property_description(class_type, name) : help_bit->get_method_description(class_type, name); if (properties) {
if (!text.is_empty()) { if (ClassDB::has_property(class_type, name, true)) {
help_bit->parse_symbol("property|" + class_type + "|" + name);
break; break;
} }
} else {
if (ClassDB::has_method(class_type, name, true)) {
help_bit->parse_symbol("method|" + class_type + "|" + name);
break;
}
}
// It may be from a parent class, keep looking. // It may be from a parent class, keep looking.
class_type = ClassDB::get_parent_class(class_type); class_type = ClassDB::get_parent_class(class_type);
} }
if (!text.is_empty()) {
// Display both property name and description, since the help bit may be displayed
// far away from the location (especially if the dialog was resized to be taller).
help_bit->set_text(vformat("[b]%s[/b]: %s", name, text));
help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1));
} else {
// Use nested `vformat()` as translators shouldn't interfere with BBCode tags.
help_bit->set_text(vformat(TTR("No description available for %s."), vformat("[b]%s[/b]", name)));
help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5));
}
} }
void PropertySelector::_hide_requested() { void PropertySelector::_hide_requested() {
@ -569,8 +565,7 @@ PropertySelector::PropertySelector() {
search_options->set_hide_folding(true); search_options->set_hide_folding(true);
help_bit = memnew(EditorHelpBit); help_bit = memnew(EditorHelpBit);
vbc->add_margin_child(TTR("Description:"), help_bit); help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
help_bit->get_rich_text()->set_fit_content(false);
help_bit->get_rich_text()->set_custom_minimum_size(Size2(help_bit->get_rich_text()->get_minimum_size().x, 135 * EDSCALE));
help_bit->connect("request_hide", callable_mp(this, &PropertySelector::_hide_requested)); help_bit->connect("request_hide", callable_mp(this, &PropertySelector::_hide_requested));
vbc->add_margin_child(TTR("Description:"), help_bit);
} }

View File

@ -2158,6 +2158,28 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
p_theme->set_constant("text_highlight_v_padding", "EditorHelp", 2 * EDSCALE); p_theme->set_constant("text_highlight_v_padding", "EditorHelp", 2 * EDSCALE);
} }
// EditorHelpBitTitle.
{
Ref<StyleBoxFlat> style = p_config.tree_panel_style->duplicate();
style->set_bg_color(p_config.dark_theme ? style->get_bg_color().lightened(0.04) : style->get_bg_color().darkened(0.04));
style->set_border_color(p_config.dark_theme ? style->get_border_color().lightened(0.04) : style->get_border_color().darkened(0.04));
style->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
style->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
p_theme->set_type_variation("EditorHelpBitTitle", "RichTextLabel");
p_theme->set_stylebox("normal", "EditorHelpBitTitle", style);
}
// EditorHelpBitContent.
{
Ref<StyleBoxFlat> style = p_config.tree_panel_style->duplicate();
style->set_corner_radius(CORNER_TOP_LEFT, 0);
style->set_corner_radius(CORNER_TOP_RIGHT, 0);
p_theme->set_type_variation("EditorHelpBitContent", "RichTextLabel");
p_theme->set_stylebox("normal", "EditorHelpBitContent", style);
}
// Asset Library. // Asset Library.
p_theme->set_stylebox("bg", "AssetLib", p_config.base_empty_style); p_theme->set_stylebox("bg", "AssetLib", p_config.base_empty_style);
p_theme->set_stylebox("panel", "AssetLib", p_config.content_panel_style); p_theme->set_stylebox("panel", "AssetLib", p_config.content_panel_style);