mirror of https://github.com/godotengine/godot
Improve Project Manager UI navigation
Provides a focus stylebox for navigable controls. The focused project is automatically selected. This resolves the conflict in the handling of `Key::UP`/`Key::DOWN` in `ProjectManager::shortcut_input()` and `Viewport::_gui_input_event()`.
This commit is contained in:
parent
7b1ed520bd
commit
6f7411a8b6
|
|
@ -804,9 +804,7 @@ void ProjectManager::_on_project_created(const String &dir, bool edit) {
|
||||||
search_box->clear();
|
search_box->clear();
|
||||||
|
|
||||||
int i = project_list->refresh_project(dir);
|
int i = project_list->refresh_project(dir);
|
||||||
project_list->select_project(i);
|
|
||||||
project_list->ensure_project_visible(i);
|
project_list->ensure_project_visible(i);
|
||||||
_update_project_buttons();
|
|
||||||
_update_list_placeholder();
|
_update_list_placeholder();
|
||||||
|
|
||||||
if (edit) {
|
if (edit) {
|
||||||
|
|
@ -1059,42 +1057,13 @@ void ProjectManager::shortcut_input(const Ref<InputEvent> &p_ev) {
|
||||||
} break;
|
} break;
|
||||||
case Key::HOME: {
|
case Key::HOME: {
|
||||||
if (project_list->get_project_count() > 0) {
|
if (project_list->get_project_count() > 0) {
|
||||||
project_list->select_project(0);
|
project_list->ensure_project_visible(0);
|
||||||
_update_project_buttons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case Key::END: {
|
case Key::END: {
|
||||||
if (project_list->get_project_count() > 0) {
|
if (project_list->get_project_count() > 0) {
|
||||||
project_list->select_project(project_list->get_project_count() - 1);
|
project_list->ensure_project_visible(project_list->get_project_count() - 1);
|
||||||
_update_project_buttons();
|
|
||||||
}
|
|
||||||
|
|
||||||
} break;
|
|
||||||
case Key::UP: {
|
|
||||||
if (k->is_shift_pressed()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = project_list->get_single_selected_index();
|
|
||||||
if (index > 0) {
|
|
||||||
project_list->select_project(index - 1);
|
|
||||||
project_list->ensure_project_visible(index - 1);
|
|
||||||
_update_project_buttons();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Key::DOWN: {
|
|
||||||
if (k->is_shift_pressed()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = project_list->get_single_selected_index();
|
|
||||||
if (index + 1 < project_list->get_project_count()) {
|
|
||||||
project_list->select_project(index + 1);
|
|
||||||
project_list->ensure_project_visible(index + 1);
|
|
||||||
_update_project_buttons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,14 @@ void ProjectListItemControl::_notification(int p_what) {
|
||||||
project_path->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), SNAME("Tree")));
|
project_path->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), SNAME("Tree")));
|
||||||
project_unsupported_features->set_texture(get_editor_theme_icon(SNAME("NodeWarning")));
|
project_unsupported_features->set_texture(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||||
|
|
||||||
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites")));
|
favorite_focus_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||||
|
_update_favorite_button_focus_color();
|
||||||
|
if (is_favourite) {
|
||||||
|
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites")));
|
||||||
|
} else {
|
||||||
|
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Unfavorite")));
|
||||||
|
}
|
||||||
|
|
||||||
if (project_is_missing) {
|
if (project_is_missing) {
|
||||||
explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -92,12 +99,23 @@ void ProjectListItemControl::_notification(int p_what) {
|
||||||
if (is_hovering) {
|
if (is_hovering) {
|
||||||
draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||||
}
|
}
|
||||||
|
if (has_focus()) {
|
||||||
|
draw_style_box(get_theme_stylebox(SNAME("focus"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||||
|
}
|
||||||
|
|
||||||
draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
|
draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectListItemControl::_update_favorite_button_focus_color() {
|
||||||
|
if (favorite_button->has_focus()) {
|
||||||
|
favorite_button->set_self_modulate(favorite_focus_color);
|
||||||
|
} else {
|
||||||
|
favorite_button->set_self_modulate(Color(1.0, 1.0, 1.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectListItemControl::_favorite_button_pressed() {
|
void ProjectListItemControl::_favorite_button_pressed() {
|
||||||
emit_signal(SNAME("favorite_pressed"));
|
emit_signal(SNAME("favorite_pressed"));
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +204,12 @@ void ProjectListItemControl::set_selected(bool p_selected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectListItemControl::set_is_favorite(bool p_favorite) {
|
void ProjectListItemControl::set_is_favorite(bool p_favorite) {
|
||||||
favorite_button->set_modulate(p_favorite ? Color(1, 1, 1, 1) : Color(1, 1, 1, 0.2));
|
is_favourite = p_favorite;
|
||||||
|
if (p_favorite) {
|
||||||
|
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites")));
|
||||||
|
} else {
|
||||||
|
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Unfavorite")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectListItemControl::set_is_missing(bool p_missing) {
|
void ProjectListItemControl::set_is_missing(bool p_missing) {
|
||||||
|
|
@ -242,6 +265,8 @@ ProjectListItemControl::ProjectListItemControl() {
|
||||||
favorite_button->set_mouse_filter(MOUSE_FILTER_PASS);
|
favorite_button->set_mouse_filter(MOUSE_FILTER_PASS);
|
||||||
favorite_box->add_child(favorite_button);
|
favorite_box->add_child(favorite_button);
|
||||||
favorite_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectListItemControl::_favorite_button_pressed));
|
favorite_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectListItemControl::_favorite_button_pressed));
|
||||||
|
favorite_button->connect(SceneStringName(focus_entered), callable_mp(this, &ProjectListItemControl::_update_favorite_button_focus_color));
|
||||||
|
favorite_button->connect(SceneStringName(focus_exited), callable_mp(this, &ProjectListItemControl::_update_favorite_button_focus_color));
|
||||||
|
|
||||||
project_icon = memnew(TextureRect);
|
project_icon = memnew(TextureRect);
|
||||||
project_icon->set_name("ProjectIcon");
|
project_icon->set_name("ProjectIcon");
|
||||||
|
|
@ -833,7 +858,6 @@ int ProjectList::refresh_project(const String &dir_path) {
|
||||||
for (int i = 0; i < _projects.size(); ++i) {
|
for (int i = 0; i < _projects.size(); ++i) {
|
||||||
if (_projects[i].path == dir_path) {
|
if (_projects[i].path == dir_path) {
|
||||||
if (was_selected) {
|
if (was_selected) {
|
||||||
select_project(i);
|
|
||||||
ensure_project_visible(i);
|
ensure_project_visible(i);
|
||||||
}
|
}
|
||||||
_load_project_icon(i);
|
_load_project_icon(i);
|
||||||
|
|
@ -849,7 +873,21 @@ int ProjectList::refresh_project(const String &dir_path) {
|
||||||
|
|
||||||
void ProjectList::ensure_project_visible(int p_index) {
|
void ProjectList::ensure_project_visible(int p_index) {
|
||||||
const Item &item = _projects[p_index];
|
const Item &item = _projects[p_index];
|
||||||
ensure_control_visible(item.control);
|
// Since follow focus is enabled.
|
||||||
|
item.control->grab_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectList::_on_child_focus_entered(ProjectListItemControl *p_hb) {
|
||||||
|
if (!Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
|
||||||
|
_clear_project_selection();
|
||||||
|
|
||||||
|
int idx = p_hb->get_index();
|
||||||
|
Item &item = _projects.write[idx];
|
||||||
|
_selected_project_paths.insert(item.path);
|
||||||
|
p_hb->set_selected(true);
|
||||||
|
|
||||||
|
emit_signal(SNAME(SIGNAL_SELECTION_CHANGED));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectList::_create_project_item_control(int p_index) {
|
void ProjectList::_create_project_item_control(int p_index) {
|
||||||
|
|
@ -876,6 +914,7 @@ void ProjectList::_create_project_item_control(int p_index) {
|
||||||
|
|
||||||
hb->connect(SceneStringName(gui_input), callable_mp(this, &ProjectList::_list_item_input).bind(hb));
|
hb->connect(SceneStringName(gui_input), callable_mp(this, &ProjectList::_list_item_input).bind(hb));
|
||||||
hb->connect("favorite_pressed", callable_mp(this, &ProjectList::_on_favorite_pressed).bind(hb));
|
hb->connect("favorite_pressed", callable_mp(this, &ProjectList::_on_favorite_pressed).bind(hb));
|
||||||
|
hb->connect(SceneStringName(focus_entered), callable_mp(this, &ProjectList::_on_child_focus_entered).bind(hb));
|
||||||
|
|
||||||
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
|
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
|
||||||
hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path));
|
hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path));
|
||||||
|
|
@ -971,12 +1010,8 @@ void ProjectList::_on_favorite_pressed(Node *p_hb) {
|
||||||
sort_projects();
|
sort_projects();
|
||||||
|
|
||||||
if (item.favorite) {
|
if (item.favorite) {
|
||||||
for (int i = 0; i < _projects.size(); ++i) {
|
// Because controls are sorted, the call is delayed in case follow focus does not take effect.
|
||||||
if (_projects[i].path == item.path) {
|
callable_mp((ScrollContainer *)this, &ScrollContainer::ensure_control_visible).call_deferred(control);
|
||||||
ensure_project_visible(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_dock_menu();
|
update_dock_menu();
|
||||||
|
|
@ -1239,6 +1274,8 @@ void ProjectList::_bind_methods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectList::ProjectList() {
|
ProjectList::ProjectList() {
|
||||||
|
set_follow_focus(true);
|
||||||
|
|
||||||
project_list_vbox = memnew(VBoxContainer);
|
project_list_vbox = memnew(VBoxContainer);
|
||||||
project_list_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
project_list_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
add_child(project_list_vbox);
|
add_child(project_list_vbox);
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,15 @@ class ProjectListItemControl : public HBoxContainer {
|
||||||
TextureRect *project_unsupported_features = nullptr;
|
TextureRect *project_unsupported_features = nullptr;
|
||||||
HBoxContainer *tag_container = nullptr;
|
HBoxContainer *tag_container = nullptr;
|
||||||
|
|
||||||
|
Color favorite_focus_color;
|
||||||
|
|
||||||
bool project_is_missing = false;
|
bool project_is_missing = false;
|
||||||
bool icon_needs_reload = true;
|
bool icon_needs_reload = true;
|
||||||
bool is_selected = false;
|
bool is_selected = false;
|
||||||
bool is_hovering = false;
|
bool is_hovering = false;
|
||||||
|
bool is_favourite = false;
|
||||||
|
|
||||||
|
void _update_favorite_button_focus_color();
|
||||||
void _favorite_button_pressed();
|
void _favorite_button_pressed();
|
||||||
void _explore_button_pressed();
|
void _explore_button_pressed();
|
||||||
|
|
||||||
|
|
@ -207,7 +211,7 @@ private:
|
||||||
static void _scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active);
|
static void _scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active);
|
||||||
|
|
||||||
// Project list items.
|
// Project list items.
|
||||||
|
void _on_child_focus_entered(ProjectListItemControl *p_hb);
|
||||||
void _create_project_item_control(int p_index);
|
void _create_project_item_control(int p_index);
|
||||||
void _toggle_project(int p_index);
|
void _toggle_project(int p_index);
|
||||||
void _remove_project(int p_index, bool p_update_settings);
|
void _remove_project(int p_index, bool p_update_settings);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue