From 62b1e2413324200cf171ee9b66fba4fdb69ab3d0 Mon Sep 17 00:00:00 2001 From: kobewi Date: Fri, 17 Mar 2023 23:30:21 +0100 Subject: [PATCH 1/2] Add project tags --- core/config/project_settings.cpp | 1 + editor/project_manager.cpp | 319 +++++++++++++++++++++++++++++-- editor/project_manager.h | 40 ++++ 3 files changed, 348 insertions(+), 12 deletions(-) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index ac5499d70952..fbfc7bcd1db3 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1262,6 +1262,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("application/config/name", ""); GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), ""); + GLOBAL_DEF_INTERNAL(PropertyInfo(Variant::STRING, "application/config/tags"), PackedStringArray()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), ""); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 5bc8284a502a..bdfd707b9479 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -50,6 +50,7 @@ #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/flow_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" #include "scene/gui/panel_container.h" @@ -1015,16 +1016,16 @@ class ProjectDialog : public ConfirmationDialog { class ProjectListItemControl : public HBoxContainer { GDCLASS(ProjectListItemControl, HBoxContainer) public: - TextureButton *favorite_button; - TextureRect *icon; - bool icon_needs_reload; - bool hover; + TextureButton *favorite_button = nullptr; + TextureRect *icon = nullptr; + HBoxContainer *tag_container = nullptr; + + bool icon_needs_reload = true; + bool hover = false; ProjectListItemControl() { favorite_button = nullptr; icon = nullptr; - icon_needs_reload = true; - hover = false; set_focus_mode(FocusMode::FOCUS_ALL); } @@ -1069,6 +1070,8 @@ class ProjectList : public ScrollContainer { struct Item { String project_name; String description; + PackedStringArray tags; + String tag_sort_string; String path; String icon; String main_scene; @@ -1085,6 +1088,7 @@ class ProjectList : public ScrollContainer { Item(const String &p_name, const String &p_description, + const PackedStringArray &p_tags, const String &p_path, const String &p_icon, const String &p_main_scene, @@ -1096,6 +1100,7 @@ class ProjectList : public ScrollContainer { int p_version) { project_name = p_name; description = p_description; + tags = p_tags; path = p_path; icon = p_icon; main_scene = p_main_scene; @@ -1106,6 +1111,10 @@ class ProjectList : public ScrollContainer { missing = p_missing; version = p_version; control = nullptr; + + PackedStringArray sorted_tags = tags; + sorted_tags.sort(); + tag_sort_string = String().join(sorted_tags); } _FORCE_INLINE_ bool operator==(const Item &l) const { @@ -1125,6 +1134,7 @@ class ProjectList : public ScrollContainer { void migrate_config(); void load_projects(); void set_search_term(String p_search_term); + void add_search_tag(const String &p_tag); void set_order_option(int p_option); void sort_projects(); int get_project_count() const; @@ -1188,6 +1198,8 @@ struct ProjectListComparator { return a.path < b.path; case EDIT_DATE: return a.last_edited > b.last_edited; + case TAGS: + return a.tag_sort_string < b.tag_sort_string; default: return a.project_name < b.project_name; } @@ -1271,7 +1283,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa int config_version = 0; String project_name = TTR("Unnamed Project"); if (cf_err == OK) { - String cf_project_name = static_cast(cf->get_value("application", "config/name", "")); + String cf_project_name = cf->get_value("application", "config/name", ""); if (!cf_project_name.is_empty()) { project_name = cf_project_name.xml_unescape(); } @@ -1284,6 +1296,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa } const String description = cf->get_value("application", "config/description", ""); + const PackedStringArray tags = cf->get_value("application", "config/tags", PackedStringArray()); const String icon = cf->get_value("application", "config/icon", ""); const String main_scene = cf->get_value("application", "run/main_scene", ""); @@ -1310,7 +1323,11 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa print_line("Project is missing: " + conf); } - return Item(project_name, description, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); + for (const String &tag : tags) { + ProjectManager::get_singleton()->add_new_tag(tag); + } + + return Item(project_name, description, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); } void ProjectList::migrate_config() { @@ -1479,7 +1496,7 @@ void ProjectList::create_project_item_control(int p_index) { ec->set_mouse_filter(MOUSE_FILTER_PASS); vb->add_child(ec); - { // Top half, title and unsupported features labels. + { // Top half, title, tags and unsupported features labels. HBoxContainer *title_hb = memnew(HBoxContainer); vb->add_child(title_hb); @@ -1491,6 +1508,16 @@ void ProjectList::create_project_item_control(int p_index) { title->set_clip_text(true); title_hb->add_child(title); + HBoxContainer *tag_container = memnew(HBoxContainer); + title_hb->add_child(tag_container); + hb->tag_container = tag_container; + + for (const String &tag : item.tags) { + ProjectTag *tag_control = memnew(ProjectTag(tag, true)); + tag_container->add_child(tag_control); + tag_control->connect_button_to(callable_mp(this, &ProjectList::add_search_tag).bind(tag)); + } + String unsupported_features_str = String(", ").join(item.unsupported_features); int length = unsupported_features_str.length(); if (length > 0) { @@ -1565,13 +1592,33 @@ void ProjectList::sort_projects() { sorter.compare.order_option = _order_option; sorter.sort(_projects.ptrw(), _projects.size()); + String search_term; + PackedStringArray tags; + + if (!_search_term.is_empty()) { + PackedStringArray search_parts = _search_term.split(" "); + if (search_parts.size() > 1 || search_parts[0].begins_with("tag:")) { + PackedStringArray remaining; + for (const String &part : search_parts) { + if (part.begins_with("tag:")) { + tags.push_back(part.get_slice(":", 1)); + } else { + remaining.append(part); + } + } + search_term = String(" ").join(remaining); // Search term without tags. + } else { + search_term = _search_term; + } + } + for (int i = 0; i < _projects.size(); ++i) { Item &item = _projects.write[i]; bool item_visible = true; if (!_search_term.is_empty()) { String search_path; - if (_search_term.contains("/")) { + if (search_term.contains("/")) { // Search path will match the whole path search_path = item.path; } else { @@ -1579,8 +1626,16 @@ void ProjectList::sort_projects() { search_path = item.path.get_file(); } - // When searching, display projects whose name or path contain the search term - item_visible = item.project_name.findn(_search_term) != -1 || search_path.findn(_search_term) != -1; + bool missing_tags = false; + for (const String &tag : tags) { + if (!item.tags.has(tag)) { + missing_tags = true; + break; + } + } + + // When searching, display projects whose name or path contain the search term and whose tags match the searched tags. + item_visible = !missing_tags && (search_term.is_empty() || item.project_name.findn(search_term) != -1 || search_path.findn(search_term) != -1); } item.control->set_visible(item_visible); @@ -1968,8 +2023,13 @@ void ProjectManager::_notification(int p_what) { open_btn->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); run_btn->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); rename_btn->set_icon(get_theme_icon(SNAME("Rename"), SNAME("EditorIcons"))); + manage_tags_btn->set_icon(get_theme_icon("Script", "EditorIcons")); erase_btn->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); erase_missing_btn->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + create_tag_btn->set_icon(get_theme_icon("Add", "EditorIcons")); + + tag_error->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); + tag_edit_error->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); Engine::get_singleton()->set_editor_hint(false); } break; @@ -2078,6 +2138,7 @@ void ProjectManager::_update_project_buttons() { erase_btn->set_disabled(empty_selection); open_btn->set_disabled(empty_selection || is_missing_project_selected); rename_btn->set_disabled(empty_selection || is_missing_project_selected); + manage_tags_btn->set_disabled(empty_selection || is_missing_project_selected); run_btn->set_disabled(empty_selection || is_missing_project_selected); erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); @@ -2473,6 +2534,110 @@ void ProjectManager::_rename_project() { } } +void ProjectManager::_manage_project_tags() { + for (int i = 0; i < project_tags->get_child_count(); i++) { + project_tags->get_child(i)->queue_free(); + } + + const ProjectList::Item &item = _project_list->get_selected_projects()[0]; + current_project_tags = item.tags; + for (const String &tag : current_project_tags) { + ProjectTag *tag_control = memnew(ProjectTag(tag)); + project_tags->add_child(tag_control); + tag_control->connect_button_to(callable_mp(this, &ProjectManager::_delete_project_tag).bind(tag)); + } + + tag_edit_error->hide(); + tag_manage_dialog->popup_centered(Vector2i(500, 0) * EDSCALE); +} + +void ProjectManager::_add_project_tag(const String &p_tag) { + if (current_project_tags.has(p_tag)) { + return; + } + current_project_tags.append(p_tag); + + ProjectTag *tag_control = memnew(ProjectTag(p_tag)); + project_tags->add_child(tag_control); + tag_control->connect_button_to(callable_mp(this, &ProjectManager::_delete_project_tag).bind(p_tag)); +} + +void ProjectManager::_delete_project_tag(const String &p_tag) { + current_project_tags.erase(p_tag); + for (int i = 0; i < project_tags->get_child_count(); i++) { + ProjectTag *tag_control = Object::cast_to(project_tags->get_child(i)); + if (tag_control && tag_control->get_tag() == p_tag) { + memdelete(tag_control); + break; + } + } +} + +void ProjectManager::_apply_project_tags() { + ProjectList::Item &item = _project_list->get_selected_projects().write[0]; + + PackedStringArray tags; + for (int i = 0; i < project_tags->get_child_count(); i++) { + ProjectTag *tag_control = Object::cast_to(project_tags->get_child(i)); + if (tag_control) { + tags.append(tag_control->get_tag()); + } + } + + ConfigFile cfg; + String project_godot = item.path.path_join("project.godot"); + Error err = cfg.load(project_godot); + if (err != OK) { + tag_edit_error->set_text(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err)); + tag_edit_error->show(); + callable_mp((Window *)tag_manage_dialog, &Window::show).call_deferred(); // Make sure the dialog does not disappear. + return; + } else { + cfg.set_value("application", "config/tags", tags); + err = cfg.save(project_godot); + if (err != OK) { + tag_edit_error->set_text(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err)); + tag_edit_error->show(); + callable_mp((Window *)tag_manage_dialog, &Window::show).call_deferred(); + return; + } + } + + _on_projects_updated(); +} + +void ProjectManager::_set_new_tag_name(const String p_name) { + create_tag_dialog->get_ok_button()->set_disabled(true); + if (p_name.is_empty()) { + tag_error->set_text(TTR("Tag name can't be empty.")); + return; + } + + if (p_name.contains(" ")) { + tag_error->set_text(TTR("Tag name can't contain spaces.")); + return; + } + + for (const String &c : forbidden_tag_characters) { + if (p_name.contains(c)) { + tag_error->set_text(vformat(TTR("These characters are not allowed in tags: %s."), String(" ").join(forbidden_tag_characters))); + return; + } + } + + if (p_name.to_lower() != p_name) { + tag_error->set_text(TTR("Tag name must be lowercase.")); + return; + } + + tag_error->set_text(""); + create_tag_dialog->get_ok_button()->set_disabled(false); +} + +void ProjectManager::_create_new_tag() { + add_new_tag(new_tag_name->get_text()); +} + void ProjectManager::_erase_project_confirm() { _project_list->erase_selected_projects(false); _update_project_buttons(); @@ -2630,6 +2795,42 @@ void ProjectManager::_version_button_pressed() { DisplayServer::get_singleton()->clipboard_set(version_btn->get_text()); } +Ref ProjectManager::get_tag_stylebox() { + if (tag_stylebox.is_null()) { + Ref style; + style.instantiate(); + style->set_corner_radius_all(8); + style->set_bg_color(Color(1, 1, 1)); + tag_stylebox = style; + } + return tag_stylebox; +} + +LineEdit *ProjectManager::get_search_box() { + return search_box; +} + +void ProjectManager::add_new_tag(const String &p_tag) { + if (!tag_set.has(p_tag)) { + tag_set.insert(p_tag); + ProjectTag *tag_control = memnew(ProjectTag(p_tag)); + all_tags->add_child(tag_control); + all_tags->move_child(tag_control, -2); + tag_control->connect_button_to(callable_mp(this, &ProjectManager::_add_project_tag).bind(p_tag)); + } +} + +void ProjectList::add_search_tag(const String &p_tag) { + if (_search_term.is_empty() || _search_term.ends_with(" ")) { + _search_term += "tag:" + p_tag; + } else { + _search_term += " tag:" + p_tag; + } + ProjectManager::get_singleton()->get_search_box()->set_text(_search_term); + + sort_projects(); +} + ProjectManager::ProjectManager() { singleton = this; @@ -2756,6 +2957,7 @@ ProjectManager::ProjectManager() { sort_filter_titles.push_back(TTR("Last Edited")); sort_filter_titles.push_back(TTR("Name")); sort_filter_titles.push_back(TTR("Path")); + sort_filter_titles.push_back(TTR("Tags")); for (int i = 0; i < sort_filter_titles.size(); i++) { filter_option->add_item(sort_filter_titles[i]); @@ -2826,6 +3028,11 @@ ProjectManager::ProjectManager() { rename_btn->connect("pressed", callable_mp(this, &ProjectManager::_rename_project)); tree_vb->add_child(rename_btn); + manage_tags_btn = memnew(Button); + manage_tags_btn->set_text(TTR("Manage Tags")); + manage_tags_btn->add_theme_constant_override("h_separation", btn_h_separation); + tree_vb->add_child(manage_tags_btn); + erase_btn = memnew(Button); erase_btn->set_text(TTR("Remove")); erase_btn->add_theme_constant_override("h_separation", btn_h_separation); @@ -3020,6 +3227,71 @@ ProjectManager::ProjectManager() { _build_icon_type_cache(get_theme()); } + { + // Tag management. + tag_manage_dialog = memnew(ConfirmationDialog); + add_child(tag_manage_dialog); + tag_manage_dialog->set_title(TTR("Manage Project Tags")); + tag_manage_dialog->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_apply_project_tags)); + manage_tags_btn->connect("pressed", callable_mp(this, &ProjectManager::_manage_project_tags)); + + VBoxContainer *tag_vb = memnew(VBoxContainer); + tag_manage_dialog->add_child(tag_vb); + + Label *label = memnew(Label(TTR("Project Tags"))); + tag_vb->add_child(label); + label->set_theme_type_variation("HeaderMedium"); + label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + + label = memnew(Label(TTR("Click tag to remove it from the project."))); + tag_vb->add_child(label); + label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + + project_tags = memnew(HFlowContainer); + tag_vb->add_child(project_tags); + project_tags->set_custom_minimum_size(Vector2(0, 100) * EDSCALE); + + tag_vb->add_child(memnew(HSeparator)); + + label = memnew(Label(TTR("All Tags"))); + tag_vb->add_child(label); + label->set_theme_type_variation("HeaderMedium"); + label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + + label = memnew(Label(TTR("Click tag to add it to the project."))); + tag_vb->add_child(label); + label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + + all_tags = memnew(HFlowContainer); + tag_vb->add_child(all_tags); + all_tags->set_custom_minimum_size(Vector2(0, 100) * EDSCALE); + + tag_edit_error = memnew(Label); + tag_vb->add_child(tag_edit_error); + tag_edit_error->set_autowrap_mode(TextServer::AUTOWRAP_WORD); + + create_tag_dialog = memnew(ConfirmationDialog); + tag_manage_dialog->add_child(create_tag_dialog); + create_tag_dialog->set_title(TTR("Create New Tag")); + create_tag_dialog->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_create_new_tag)); + + tag_vb = memnew(VBoxContainer); + create_tag_dialog->add_child(tag_vb); + + new_tag_name = memnew(LineEdit); + tag_vb->add_child(new_tag_name); + new_tag_name->connect("text_changed", callable_mp(this, &ProjectManager::_set_new_tag_name)); + create_tag_dialog->connect("about_to_popup", callable_mp(new_tag_name, &LineEdit::clear)); + create_tag_dialog->connect("about_to_popup", callable_mp((Control *)new_tag_name, &Control::grab_focus), CONNECT_DEFERRED); + + tag_error = memnew(Label); + tag_vb->add_child(tag_error); + + create_tag_btn = memnew(Button); + all_tags->add_child(create_tag_btn); + create_tag_btn->connect("pressed", callable_mp((Window *)create_tag_dialog, &Window::popup_centered).bind(Vector2i(500, 0) * EDSCALE)); + } + _project_list->migrate_config(); _load_recent_projects(); @@ -3079,3 +3351,26 @@ ProjectManager::~ProjectManager() { EditorSettings::destroy(); } } + +ProjectTag::ProjectTag(const String &p_text, bool p_capitalized) { + add_theme_style_override(SNAME("panel"), ProjectManager::get_singleton()->get_tag_stylebox()); + set_v_size_flags(SIZE_SHRINK_CENTER); + + Color tag_color = Color(1, 0, 0); + tag_color.set_h(float(p_text.hash() * 10001 % UINT32_MAX) / float(UINT32_MAX)); + set_self_modulate(tag_color); + + button = memnew(Button); + add_child(button); + button->set_flat(true); + button->set_text(p_capitalized ? p_text.capitalize() : p_text); + button->add_theme_color_override(SNAME("font_color"), Color(0, 0, 0)); +} + +void ProjectTag::connect_button_to(const Callable &p_callable) { + button->connect(SNAME("pressed"), p_callable, CONNECT_DEFERRED); +} + +const String ProjectTag::get_tag() const { + return button->get_text(); +} diff --git a/editor/project_manager.h b/editor/project_manager.h index b277a35fcbb7..04b460a3f693 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -39,6 +39,7 @@ #include "scene/gui/tree.h" class CheckBox; +class HFlowContainer; class ProjectDialog; class ProjectList; @@ -46,6 +47,7 @@ enum FilterOption { EDIT_DATE, NAME, PATH, + TAGS, }; class ProjectManager : public Control { @@ -70,6 +72,7 @@ class ProjectManager : public Control { Button *open_btn = nullptr; Button *run_btn = nullptr; Button *rename_btn = nullptr; + Button *manage_tags_btn = nullptr; Button *erase_btn = nullptr; Button *erase_missing_btn = nullptr; Button *about_btn = nullptr; @@ -77,6 +80,8 @@ class ProjectManager : public Control { HBoxContainer *local_projects_hb = nullptr; EditorAssetLibrary *asset_library = nullptr; + Ref tag_stylebox; + EditorFileDialog *scan_dir = nullptr; ConfirmationDialog *language_restart_ask = nullptr; @@ -105,6 +110,18 @@ class ProjectManager : public Control { OptionButton *language_btn = nullptr; LinkButton *version_btn = nullptr; + HashSet tag_set; + PackedStringArray current_project_tags; + PackedStringArray forbidden_tag_characters{ "/", "\\", "-" }; + ConfirmationDialog *tag_manage_dialog = nullptr; + HFlowContainer *project_tags = nullptr; + HFlowContainer *all_tags = nullptr; + Label *tag_edit_error = nullptr; + Button *create_tag_btn = nullptr; + ConfirmationDialog *create_tag_dialog = nullptr; + LineEdit *new_tag_name = nullptr; + Label *tag_error = nullptr; + void _open_asset_library(); void _scan_projects(); void _run_project(); @@ -127,6 +144,13 @@ class ProjectManager : public Control { void _confirm_update_settings(); void _nonempty_confirmation_ok_pressed(); + void _manage_project_tags(); + void _add_project_tag(const String &p_tag); + void _delete_project_tag(const String &p_tag); + void _apply_project_tags(); + void _set_new_tag_name(const String p_name); + void _create_new_tag(); + void _load_recent_projects(); void _on_project_created(const String &dir); void _on_projects_updated(); @@ -155,8 +179,24 @@ class ProjectManager : public Control { public: static ProjectManager *get_singleton() { return singleton; } + Ref get_tag_stylebox(); + LineEdit *get_search_box(); + void add_new_tag(const String &p_tag); + ProjectManager(); ~ProjectManager(); }; +class ProjectTag : public PanelContainer { + GDCLASS(ProjectTag, PanelContainer); + + Button *button = nullptr; + +public: + ProjectTag(const String &p_text, bool p_capitalized = false); + + void connect_button_to(const Callable &p_callable); + const String get_tag() const; +}; + #endif // PROJECT_MANAGER_H From 9bb68cde1d0960b03b9da345022175ce2b2b1a77 Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 18 Mar 2023 22:00:00 +0100 Subject: [PATCH 2/2] Different tag look --- editor/editor_themes.cpp | 27 +++++++++++++++++++++++++++ editor/project_manager.cpp | 27 ++++++++++++--------------- editor/project_manager.h | 5 ++--- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 3e6b0ee07f35..b9181dba3730 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -894,6 +894,33 @@ Ref create_editor_theme(const Ref p_theme) { editor_log_button_pressed->set_border_color(accent_color); theme->set_stylebox("pressed", "EditorLogFilterButton", editor_log_button_pressed); + // ProjectTag + { + theme->set_type_variation("ProjectTag", "Button"); + + Ref tag = style_widget->duplicate(); + tag->set_bg_color(dark_theme ? tag->get_bg_color().lightened(0.2) : tag->get_bg_color().darkened(0.2)); + tag->set_corner_radius(CORNER_TOP_LEFT, 0); + tag->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + tag->set_corner_radius(CORNER_TOP_RIGHT, 4); + tag->set_corner_radius(CORNER_BOTTOM_RIGHT, 4); + theme->set_stylebox("normal", "ProjectTag", tag); + + tag = style_widget_hover->duplicate(); + tag->set_corner_radius(CORNER_TOP_LEFT, 0); + tag->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + tag->set_corner_radius(CORNER_TOP_RIGHT, 4); + tag->set_corner_radius(CORNER_BOTTOM_RIGHT, 4); + theme->set_stylebox("hover", "ProjectTag", tag); + + tag = style_widget_pressed->duplicate(); + tag->set_corner_radius(CORNER_TOP_LEFT, 0); + tag->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + tag->set_corner_radius(CORNER_TOP_RIGHT, 4); + tag->set_corner_radius(CORNER_BOTTOM_RIGHT, 4); + theme->set_stylebox("pressed", "ProjectTag", tag); + } + // MenuBar theme->set_stylebox("normal", "MenuBar", style_widget); theme->set_stylebox("hover", "MenuBar", style_widget_hover); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index bdfd707b9479..acfe9d5b734a 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -50,6 +50,7 @@ #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/color_rect.h" #include "scene/gui/flow_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -1510,6 +1511,7 @@ void ProjectList::create_project_item_control(int p_index) { HBoxContainer *tag_container = memnew(HBoxContainer); title_hb->add_child(tag_container); + // tag_container->add_theme_constant_override(SNAME("separation"), 4 * EDSCALE); hb->tag_container = tag_container; for (const String &tag : item.tags) { @@ -2795,17 +2797,6 @@ void ProjectManager::_version_button_pressed() { DisplayServer::get_singleton()->clipboard_set(version_btn->get_text()); } -Ref ProjectManager::get_tag_stylebox() { - if (tag_stylebox.is_null()) { - Ref style; - style.instantiate(); - style->set_corner_radius_all(8); - style->set_bg_color(Color(1, 1, 1)); - tag_stylebox = style; - } - return tag_stylebox; -} - LineEdit *ProjectManager::get_search_box() { return search_box; } @@ -3353,18 +3344,24 @@ ProjectManager::~ProjectManager() { } ProjectTag::ProjectTag(const String &p_text, bool p_capitalized) { - add_theme_style_override(SNAME("panel"), ProjectManager::get_singleton()->get_tag_stylebox()); + add_theme_constant_override(SNAME("separation"), 0); set_v_size_flags(SIZE_SHRINK_CENTER); Color tag_color = Color(1, 0, 0); - tag_color.set_h(float(p_text.hash() * 10001 % UINT32_MAX) / float(UINT32_MAX)); + tag_color.set_ok_hsl_s(0.8); + tag_color.set_ok_hsl_h(float(p_text.hash() * 10001 % UINT32_MAX) / float(UINT32_MAX)); set_self_modulate(tag_color); + ColorRect *cr = memnew(ColorRect); + add_child(cr); + cr->set_custom_minimum_size(Vector2(4, 0) * EDSCALE); + cr->set_color(tag_color); + button = memnew(Button); add_child(button); - button->set_flat(true); button->set_text(p_capitalized ? p_text.capitalize() : p_text); - button->add_theme_color_override(SNAME("font_color"), Color(0, 0, 0)); + button->set_focus_mode(FOCUS_NONE); + button->set_theme_type_variation(SNAME("ProjectTag")); } void ProjectTag::connect_button_to(const Callable &p_callable) { diff --git a/editor/project_manager.h b/editor/project_manager.h index 04b460a3f693..4220566fe832 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -179,7 +179,6 @@ class ProjectManager : public Control { public: static ProjectManager *get_singleton() { return singleton; } - Ref get_tag_stylebox(); LineEdit *get_search_box(); void add_new_tag(const String &p_tag); @@ -187,8 +186,8 @@ class ProjectManager : public Control { ~ProjectManager(); }; -class ProjectTag : public PanelContainer { - GDCLASS(ProjectTag, PanelContainer); +class ProjectTag : public HBoxContainer { + GDCLASS(ProjectTag, HBoxContainer); Button *button = nullptr;