Merge pull request #49417 from Bhu1-V/gsoc-cmd-plt

Command Palette For Godot
This commit is contained in:
Rémi Verschelde 2021-08-10 18:55:22 +02:00 committed by GitHub
commit dce488d8f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 576 additions and 59 deletions

View File

@ -32,6 +32,7 @@
#include "core/input/input_map.h" #include "core/input/input_map.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "scene/gui/shortcut.h"
const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1;
const int InputEvent::DEVICE_ID_INTERNAL = -2; const int InputEvent::DEVICE_ID_INTERNAL = -2;
@ -1512,3 +1513,26 @@ void InputEventMIDI::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number");
ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value");
} }
///////////////////////////////////
void InputEventShortcut::set_shortcut(Ref<Shortcut> p_shortcut) {
shortcut = p_shortcut;
emit_changed();
}
Ref<Shortcut> InputEventShortcut::get_shortcut() {
return shortcut;
}
bool InputEventShortcut::is_pressed() const {
return true;
}
String InputEventShortcut::as_text() const {
return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text());
}
String InputEventShortcut::to_string() {
return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());
}

View File

@ -42,6 +42,8 @@
* The events are pretty obvious. * The events are pretty obvious.
*/ */
class Shortcut;
/** /**
* Input Modifier Status * Input Modifier Status
* for keyboard/mouse events. * for keyboard/mouse events.
@ -538,4 +540,18 @@ public:
InputEventMIDI() {} InputEventMIDI() {}
}; };
class InputEventShortcut : public InputEvent {
GDCLASS(InputEventShortcut, InputEvent);
Ref<Shortcut> shortcut;
public:
void set_shortcut(Ref<Shortcut> p_shortcut);
Ref<Shortcut> get_shortcut();
virtual bool is_pressed() const override;
virtual String as_text() const override;
virtual String to_string() override;
};
#endif // INPUT_EVENT_H #endif // INPUT_EVENT_H

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorCommandPalette" inherits="ConfirmationDialog" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_command">
<return type="void" />
<argument index="0" name="command_name" type="String" />
<argument index="1" name="key_name" type="String" />
<argument index="2" name="binded_callable" type="Callable" />
<argument index="3" name="shortcut_text" type="String" default="&quot;None&quot;" />
<description>
</description>
</method>
<method name="remove_command">
<return type="void" />
<argument index="0" name="key_name" type="String" />
<description>
</description>
</method>
</methods>
<members>
<member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" override="true" default="false" />
</members>
<constants>
</constants>
</class>

View File

@ -30,6 +30,11 @@
Returns the main container of Godot editor's window. For example, you can use it to retrieve the size of the container and place your controls accordingly. Returns the main container of Godot editor's window. For example, you can use it to retrieve the size of the container and place your controls accordingly.
</description> </description>
</method> </method>
<method name="get_command_palette" qualifiers="const">
<return type="EditorCommandPalette" />
<description>
</description>
</method>
<method name="get_current_path" qualifiers="const"> <method name="get_current_path" qualifiers="const">
<return type="String" /> <return type="String" />
<description> <description>

View File

@ -0,0 +1,300 @@
/*************************************************************************/
/* editor_command_palette.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor/editor_command_palette.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "scene/gui/control.h"
#include "scene/gui/tree.h"
EditorCommandPalette *EditorCommandPalette::singleton = nullptr;
float EditorCommandPalette::_score_path(const String &p_search, const String &p_path) {
float score = 0.9f + .1f * (p_search.length() / (float)p_path.length());
// Positive bias for matches close to the beginning of the file name.
int pos = p_path.findn(p_search);
if (pos != -1) {
return score * (1.0f - 0.1f * (float(pos) / p_path.length()));
}
// Positive bias for matches close to the end of the path.
pos = p_path.rfindn(p_search);
if (pos != -1) {
return score * (0.8f - 0.1f * (float(p_path.length() - pos) / p_path.length()));
}
// Remaining results belong to the same class of results.
return score * 0.69f;
}
void EditorCommandPalette::_update_command_search(const String &search_text) {
commands.get_key_list(&command_keys);
ERR_FAIL_COND(command_keys.is_empty());
const bool empty_search = search_text.is_empty();
Map<String, TreeItem *> sections;
TreeItem *first_section = nullptr;
// Filter possible candidates.
Vector<CommandEntry> entries;
for (int i = 0; i < command_keys.size(); i++) {
CommandEntry r;
r.key_name = command_keys[i];
r.display_name = commands[r.key_name].name;
r.shortcut_text = commands[r.key_name].shortcut;
if (!empty_search && search_text.is_subsequence_ofi(r.display_name)) {
r.score = _score_path(search_text, r.display_name.to_lower());
entries.push_back(r);
}
}
command_keys.clear();
TreeItem *root = search_options->get_root();
root->clear_children();
if (entries.size() > 0) {
if (!empty_search) {
SortArray<CommandEntry, CommandEntryComparator> sorter;
sorter.sort(entries.ptrw(), entries.size());
}
const int entry_limit = MIN(entries.size(), 300);
for (int i = 0; i < entry_limit; i++) {
String section_name = entries[i].key_name.get_slice("/", 0);
TreeItem *section;
if (sections.has(section_name)) {
section = sections[section_name];
} else {
section = search_options->create_item(root);
if (!first_section) {
first_section = section;
}
String item_name = section_name.capitalize();
section->set_text(0, item_name);
sections[section_name] = section;
section->set_custom_bg_color(0, search_options->get_theme_color("prop_subsection", "Editor"));
section->set_custom_bg_color(1, search_options->get_theme_color("prop_subsection", "Editor"));
}
TreeItem *ti = search_options->create_item(section);
String shortcut_text = entries[i].shortcut_text == "None" ? "" : entries[i].shortcut_text;
ti->set_text(0, entries[i].display_name);
ti->set_metadata(0, entries[i].key_name);
ti->set_text_align(1, TreeItem::TextAlign::ALIGN_RIGHT);
ti->set_text(1, shortcut_text);
Color c = Color(1, 1, 1, 0.5);
ti->set_custom_color(1, c);
}
TreeItem *to_select = first_section->get_first_child();
to_select->select(0);
to_select->set_as_cursor(0);
search_options->scroll_to_item(to_select);
get_ok_button()->set_disabled(false);
} else {
TreeItem *ti = search_options->create_item(root);
ti->set_text(0, TTR("No Matching Command"));
ti->set_metadata(0, "");
Color c = Color(0.5, 0.5, 0.5, 0.5);
ti->set_custom_color(0, c);
search_options->deselect_all();
get_ok_button()->set_disabled(true);
}
}
void EditorCommandPalette::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_command", "command_name", "key_name", "binded_callable", "shortcut_text"), &EditorCommandPalette::_add_command, DEFVAL("None"));
ClassDB::bind_method(D_METHOD("remove_command", "key_name"), &EditorCommandPalette::remove_command);
}
void EditorCommandPalette::_sbox_input(const Ref<InputEvent> &p_ie) {
Ref<InputEventKey> k = p_ie;
if (k.is_valid()) {
switch (k->get_keycode()) {
case KEY_UP:
case KEY_DOWN:
case KEY_PAGEUP:
case KEY_PAGEDOWN: {
search_options->call("_gui_input", k);
} break;
}
}
}
void EditorCommandPalette::_confirmed() {
TreeItem *selected_option = search_options->get_selected();
String command_key = selected_option != nullptr ? selected_option->get_metadata(0) : "";
if (command_key != "") {
hide();
execute_command(command_key);
}
}
void EditorCommandPalette::open_popup() {
popup_centered_clamped(Size2i(600, 440), 0.8f);
command_search_box->clear();
command_search_box->grab_focus();
}
void EditorCommandPalette::get_actions_list(List<String> *p_list) const {
commands.get_key_list(p_list);
}
void EditorCommandPalette::remove_command(String p_key_name) {
ERR_FAIL_COND_MSG(!commands.has(p_key_name), "The EditorAction '" + String(p_key_name) + "' Doesn't exists. Unable to remove it.");
commands.erase(p_key_name);
}
void EditorCommandPalette::add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, String p_shortcut_text) {
ERR_FAIL_COND_MSG(commands.has(p_key_name), "The EditorAction '" + String(p_command_name) + "' already exists. Unable to add it.");
const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * arguments.size());
for (int i = 0; i < arguments.size(); i++) {
argptrs[i] = &arguments[i];
}
Command p_command;
p_command.name = p_command_name;
p_command.callable = p_action.bind(argptrs, arguments.size());
p_command.shortcut = p_shortcut_text;
commands[p_key_name] = p_command;
}
void EditorCommandPalette::_add_command(String p_command_name, String p_key_name, Callable p_binded_action, String p_shortcut_text) {
ERR_FAIL_COND_MSG(commands.has(p_key_name), "The EditorAction '" + String(p_command_name) + "' already exists. Unable to add it.");
Command p_command;
p_command.name = p_command_name;
p_command.callable = p_binded_action;
p_command.shortcut = p_shortcut_text;
commands[p_key_name] = p_command;
}
void EditorCommandPalette::execute_command(String &p_command_key) {
ERR_FAIL_COND_MSG(!commands.has(p_command_key), p_command_key + " not found.");
commands[p_command_key].callable.call_deferred(nullptr, 0);
}
void EditorCommandPalette::register_shortcuts_as_command() {
const String *p_key = nullptr;
p_key = unregistered_shortcuts.next(p_key);
while (p_key != nullptr) {
String command_name = unregistered_shortcuts[*p_key].first;
Ref<Shortcut> p_shortcut = unregistered_shortcuts[*p_key].second;
Ref<InputEventShortcut> ev;
ev.instantiate();
ev->set_shortcut(p_shortcut);
String shortcut_text = String(p_shortcut->get_as_text());
add_command(command_name, *p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::unhandled_input), varray(ev, false), shortcut_text);
p_key = unregistered_shortcuts.next(p_key);
}
unregistered_shortcuts.clear();
}
Ref<Shortcut> EditorCommandPalette::add_shortcut_command(const String &p_command, const String &p_key, Ref<Shortcut> p_shortcut) {
if (is_inside_tree()) {
Ref<InputEventShortcut> ev;
ev.instantiate();
ev->set_shortcut(p_shortcut);
String shortcut_text = String(p_shortcut->get_as_text());
add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::unhandled_input), varray(ev, false), shortcut_text);
} else {
const String key_name = String(p_key);
const String command_name = String(p_command);
Pair p_pair = Pair(command_name, p_shortcut);
unregistered_shortcuts[key_name] = p_pair;
}
return p_shortcut;
}
void EditorCommandPalette::_theme_changed() {
command_search_box->set_right_icon(search_options->get_theme_icon("Search", "EditorIcons"));
}
EditorCommandPalette *EditorCommandPalette::get_singleton() {
if (singleton == nullptr) {
singleton = memnew(EditorCommandPalette);
}
return singleton;
}
EditorCommandPalette::EditorCommandPalette() {
VBoxContainer *vbc = memnew(VBoxContainer);
vbc->connect("theme_changed", callable_mp(this, &EditorCommandPalette::_theme_changed));
add_child(vbc);
command_search_box = memnew(LineEdit);
command_search_box->set_placeholder("search for a command");
command_search_box->set_placeholder_alpha(0.5);
command_search_box->connect("gui_input", callable_mp(this, &EditorCommandPalette::_sbox_input));
command_search_box->connect("text_changed", callable_mp(this, &EditorCommandPalette::_update_command_search));
command_search_box->connect("text_submitted", callable_mp(this, &EditorCommandPalette::_confirmed).unbind(1));
command_search_box->set_v_size_flags(Control::SIZE_EXPAND_FILL);
MarginContainer *margin_container_csb = memnew(MarginContainer);
margin_container_csb->add_child(command_search_box);
vbc->add_child(margin_container_csb);
register_text_enter(command_search_box);
search_options = memnew(Tree);
search_options->connect("item_activated", callable_mp(this, &EditorCommandPalette::_confirmed));
search_options->create_item();
search_options->set_hide_root(true);
search_options->set_hide_folding(true);
search_options->add_theme_constant_override("draw_guides", 1);
search_options->set_columns(2);
search_options->set_v_size_flags(Control::SIZE_EXPAND_FILL);
search_options->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_options->set_column_custom_minimum_width(0, int(8 * EDSCALE));
vbc->add_child(search_options, true);
set_hide_on_ok(false);
}
Ref<Shortcut> ED_SHORTCUT_AND_COMMAND(const String &p_path, const String &p_name, uint32_t p_keycode, String p_command_name) {
if (p_command_name.is_empty()) {
p_command_name = p_name;
}
Ref<Shortcut> p_shortcut = ED_SHORTCUT(p_path, p_name, p_keycode);
EditorCommandPalette::get_singleton()->add_shortcut_command(p_command_name, p_path, p_shortcut);
return p_shortcut;
}

View File

@ -0,0 +1,95 @@
/*************************************************************************/
/* editor_command_palette.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_COMMAND_PALETTE_H
#define EDITOR_COMMAND_PALETTE_H
#include "core/os/thread_safe.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/shortcut.h"
#include "scene/gui/tree.h"
class EditorCommandPalette : public ConfirmationDialog {
GDCLASS(EditorCommandPalette, ConfirmationDialog);
static EditorCommandPalette *singleton;
LineEdit *command_search_box;
Tree *search_options;
struct Command {
Callable callable;
String name;
String shortcut;
};
struct CommandEntry {
String key_name;
String display_name;
String shortcut_text;
float score;
};
struct CommandEntryComparator {
_FORCE_INLINE_ bool operator()(const CommandEntry &A, const CommandEntry &B) const {
return A.score > B.score;
}
};
HashMap<String, Command> commands;
HashMap<String, Pair<String, Ref<Shortcut>>> unregistered_shortcuts;
List<String> command_keys;
void _update_command_search(const String &search_text);
float _score_path(const String &p_search, const String &p_path);
void _sbox_input(const Ref<InputEvent> &p_ie);
void _confirmed();
void _update_command_keys();
void _add_command(String p_command_name, String p_key_name, Callable p_binded_action, String p_shortcut_text = "None");
void _theme_changed();
EditorCommandPalette();
protected:
static void _bind_methods();
public:
void open_popup();
void get_actions_list(List<String> *p_list) const;
void add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, String p_shortcut_text = "None");
void execute_command(String &p_command_name);
void register_shortcuts_as_command();
Ref<Shortcut> add_shortcut_command(const String &p_command, const String &p_key, Ref<Shortcut> p_shortcut);
void remove_command(String p_key_name);
static EditorCommandPalette *get_singleton();
};
Ref<Shortcut> ED_SHORTCUT_AND_COMMAND(const String &p_path, const String &p_name, uint32_t p_keycode = 0, String p_command = "");
#endif //EDITOR_COMMAND_PALETTE_H

View File

@ -75,6 +75,7 @@
#include "editor/dependency_editor.h" #include "editor/dependency_editor.h"
#include "editor/editor_about.h" #include "editor/editor_about.h"
#include "editor/editor_audio_buses.h" #include "editor/editor_audio_buses.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_export.h" #include "editor/editor_export.h"
#include "editor/editor_feature_profile.h" #include "editor/editor_feature_profile.h"
#include "editor/editor_file_system.h" #include "editor/editor_file_system.h"
@ -404,7 +405,7 @@ void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
Ref<InputEventKey> k = p_event; Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && !k->is_echo()) { if ((k.is_valid() && k->is_pressed() && !k->is_echo()) || Object::cast_to<InputEventShortcut>(*p_event)) {
EditorPlugin *old_editor = editor_plugin_screen; EditorPlugin *old_editor = editor_plugin_screen;
if (ED_IS_SHORTCUT("editor/next_tab", p_event)) { if (ED_IS_SHORTCUT("editor/next_tab", p_event)) {
@ -435,6 +436,9 @@ void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) {
_editor_select_next(); _editor_select_next();
} else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) { } else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) {
_editor_select_prev(); _editor_select_prev();
} else if (ED_IS_SHORTCUT("editor/command_palette", p_event)) {
_open_command_palette();
} else {
} }
if (old_editor != editor_plugin_screen) { if (old_editor != editor_plugin_screen) {
@ -572,6 +576,8 @@ void EditorNode::_notification(int p_what) {
get_tree()->set_auto_accept_quit(false); get_tree()->set_auto_accept_quit(false);
get_tree()->get_root()->connect("files_dropped", callable_mp(this, &EditorNode::_dropped_files)); get_tree()->get_root()->connect("files_dropped", callable_mp(this, &EditorNode::_dropped_files));
command_palette->register_shortcuts_as_command();
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
} break; } break;
@ -1063,6 +1069,10 @@ void EditorNode::_editor_select_next() {
_editor_select(editor); _editor_select(editor);
} }
void EditorNode::_open_command_palette() {
command_palette->open_popup();
}
void EditorNode::_editor_select_prev() { void EditorNode::_editor_select_prev() {
int editor = _get_current_main_editor(); int editor = _get_current_main_editor();
@ -2815,6 +2825,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case HELP_SEARCH: { case HELP_SEARCH: {
emit_signal(SNAME("request_help_search"), ""); emit_signal(SNAME("request_help_search"), "");
} break; } break;
case HELP_COMMAND_PALETTE: {
command_palette->open_popup();
} break;
case HELP_DOCS: { case HELP_DOCS: {
OS::get_singleton()->shell_open("https://docs.godotengine.org/"); OS::get_singleton()->shell_open("https://docs.godotengine.org/");
} break; } break;
@ -3802,6 +3815,7 @@ void EditorNode::register_editor_types() {
// FIXME: Is this stuff obsolete, or should it be ported to new APIs? // FIXME: Is this stuff obsolete, or should it be ported to new APIs?
GDREGISTER_CLASS(EditorScenePostImport); GDREGISTER_CLASS(EditorScenePostImport);
//ClassDB::register_type<EditorImportExport>(); //ClassDB::register_type<EditorImportExport>();
GDREGISTER_CLASS(EditorCommandPalette);
GDREGISTER_CLASS(EditorDebuggerPlugin); GDREGISTER_CLASS(EditorDebuggerPlugin);
NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR); NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR);
@ -6129,9 +6143,9 @@ EditorNode::EditorNode() {
distraction_free = memnew(Button); distraction_free = memnew(Button);
distraction_free->set_flat(true); distraction_free->set_flat(true);
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D)); distraction_free->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D));
#else #else
distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F11)); distraction_free->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F11));
#endif #endif
distraction_free->set_tooltip(TTR("Toggle distraction-free mode.")); distraction_free->set_tooltip(TTR("Toggle distraction-free mode."));
distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode)); distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode));
@ -6225,28 +6239,33 @@ EditorNode::EditorNode() {
ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB); ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB);
ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB); ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB);
ED_SHORTCUT("editor/filter_files", TTR("Filter Files..."), KEY_MASK_CMD + KEY_MASK_ALT + KEY_P); ED_SHORTCUT("editor/filter_files", TTR("Filter Files..."), KEY_MASK_CMD + KEY_MASK_ALT + KEY_P);
command_palette = EditorCommandPalette::get_singleton();
command_palette->set_title(TTR("Command Palette"));
gui_base->add_child(command_palette);
PopupMenu *p; PopupMenu *p;
file_menu->set_tooltip(TTR("Operations with scene files.")); file_menu->set_tooltip(TTR("Operations with scene files."));
p = file_menu->get_popup(); p = file_menu->get_popup();
p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene"), KEY_MASK_CMD + KEY_N), FILE_NEW_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_scene", TTR("New Scene"), KEY_MASK_CMD + KEY_N), FILE_NEW_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_N), FILE_NEW_INHERITED_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_inherited_scene", TTR("New Inherited Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_N), FILE_NEW_INHERITED_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_T), FILE_OPEN_PREV); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_T), FILE_OPEN_PREV);
p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT); p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_S), FILE_SAVE_AS_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_S), FILE_SAVE_AS_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_S), FILE_SAVE_ALL_SCENES); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_S), FILE_SAVE_ALL_SCENES);
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/quick_open", TTR("Quick Open..."), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_O), FILE_QUICK_OPEN); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open", TTR("Quick Open..."), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_O), FILE_QUICK_OPEN);
p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_O), FILE_QUICK_OPEN_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_scene", TTR("Quick Open Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_O), FILE_QUICK_OPEN_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script..."), KEY_MASK_CMD + KEY_MASK_ALT + KEY_O), FILE_QUICK_OPEN_SCRIPT); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_script", TTR("Quick Open Script..."), KEY_MASK_CMD + KEY_MASK_ALT + KEY_O), FILE_QUICK_OPEN_SCRIPT);
p->add_separator(); p->add_separator();
PopupMenu *pm_export = memnew(PopupMenu); PopupMenu *pm_export = memnew(PopupMenu);
@ -6261,8 +6280,8 @@ EditorNode::EditorNode() {
p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true);
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_W), FILE_CLOSE); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/close_scene", TTR("Close Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_W), FILE_CLOSE);
recent_scenes = memnew(PopupMenu); recent_scenes = memnew(PopupMenu);
recent_scenes->set_name("RecentScenes"); recent_scenes->set_name("RecentScenes");
@ -6270,7 +6289,7 @@ EditorNode::EditorNode() {
recent_scenes->connect("id_pressed", callable_mp(this, &EditorNode::_open_recent_scene)); recent_scenes->connect("id_pressed", callable_mp(this, &EditorNode::_open_recent_scene));
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/file_quit", TTR("Quit"), KEY_MASK_CMD + KEY_Q), FILE_QUIT, true); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTR("Quit"), KEY_MASK_CMD + KEY_Q), FILE_QUIT, true);
project_menu = memnew(MenuButton); project_menu = memnew(MenuButton);
project_menu->set_flat(false); project_menu->set_flat(false);
@ -6282,7 +6301,7 @@ EditorNode::EditorNode() {
p = project_menu->get_popup(); p = project_menu->get_popup();
p->add_shortcut(ED_SHORTCUT("editor/project_settings", TTR("Project Settings...")), RUN_SETTINGS); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTR("Project Settings..."), 0, TTR("Project Settings")), RUN_SETTINGS);
p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel(); vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
@ -6295,7 +6314,7 @@ EditorNode::EditorNode() {
vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN); vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN);
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export...")), FILE_EXPORT_PROJECT); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), 0, TTR("Export")), FILE_EXPORT_PROJECT);
p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE);
p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER); p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER);
@ -6313,9 +6332,9 @@ EditorNode::EditorNode() {
p->add_separator(); p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/reload_current_project", TTR("Reload Current Project")), RUN_RELOAD_CURRENT_PROJECT); p->add_shortcut(ED_SHORTCUT("editor/reload_current_project", TTR("Reload Current Project")), RUN_RELOAD_CURRENT_PROJECT);
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
p->add_shortcut(ED_SHORTCUT("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q), RUN_PROJECT_MANAGER, true); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q), RUN_PROJECT_MANAGER, true);
#else #else
p->add_shortcut(ED_SHORTCUT("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_Q), RUN_PROJECT_MANAGER, true); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_Q), RUN_PROJECT_MANAGER, true);
#endif #endif
menu_hb->add_spacer(); menu_hb->add_spacer();
@ -6342,9 +6361,9 @@ EditorNode::EditorNode() {
p = settings_menu->get_popup(); p = settings_menu->get_popup();
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings..."), KEY_MASK_CMD + KEY_COMMA), SETTINGS_PREFERENCES); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings..."), KEY_MASK_CMD + KEY_COMMA), SETTINGS_PREFERENCES);
#else #else
p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings...")), SETTINGS_PREFERENCES); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")), SETTINGS_PREFERENCES);
#endif #endif
p->add_separator(); p->add_separator();
@ -6355,15 +6374,15 @@ EditorNode::EditorNode() {
p->add_submenu_item(TTR("Editor Layout"), "Layouts"); p->add_submenu_item(TTR("Editor Layout"), "Layouts");
p->add_separator(); p->add_separator();
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
p->add_shortcut(ED_SHORTCUT("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CMD | KEY_F12), EDITOR_SCREENSHOT); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CMD | KEY_F12), EDITOR_SCREENSHOT);
#else #else
p->add_shortcut(ED_SHORTCUT("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CTRL | KEY_F12), EDITOR_SCREENSHOT); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CTRL | KEY_F12), EDITOR_SCREENSHOT);
#endif #endif
p->set_item_tooltip(p->get_item_count() - 1, TTR("Screenshots are stored in the Editor Data/Settings Folder.")); p->set_item_tooltip(p->get_item_count() - 1, TTR("Screenshots are stored in the Editor Data/Settings Folder."));
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F), SETTINGS_TOGGLE_FULLSCREEN); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F), SETTINGS_TOGGLE_FULLSCREEN);
#else #else
p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN); p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN);
#endif #endif
#if defined(WINDOWS_ENABLED) && defined(WINDOWS_SUBSYSTEM_CONSOLE) #if defined(WINDOWS_ENABLED) && defined(WINDOWS_SUBSYSTEM_CONSOLE)
// The console can only be toggled if the application was built for the console subsystem, // The console can only be toggled if the application was built for the console subsystem,
@ -6396,20 +6415,20 @@ EditorNode::EditorNode() {
p = help_menu->get_popup(); p = help_menu->get_popup();
p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE), HELP_SEARCH); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE), HELP_SEARCH);
#else #else
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F1), HELP_SEARCH); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTR("Search Help"), KEY_F1), HELP_SEARCH);
#endif #endif
p->add_separator(); p->add_separator();
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY);
p->add_separator(); p->add_separator();
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons")), ED_SHORTCUT("editor/about", TTR("About Godot")), HELP_ABOUT); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/about", TTR("About Godot")), HELP_ABOUT);
p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons")), ED_SHORTCUT("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT); p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT);
HBoxContainer *play_hb = memnew(HBoxContainer); HBoxContainer *play_hb = memnew(HBoxContainer);
menu_hb->add_child(play_hb); menu_hb->add_child(play_hb);
@ -6423,9 +6442,9 @@ EditorNode::EditorNode() {
play_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY)); play_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY));
play_button->set_tooltip(TTR("Play the project.")); play_button->set_tooltip(TTR("Play the project."));
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_MASK_CMD | KEY_B)); play_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play", TTR("Play"), KEY_MASK_CMD | KEY_B));
#else #else
play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_F5)); play_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play", TTR("Play"), KEY_F5));
#endif #endif
pause_button = memnew(Button); pause_button = memnew(Button);
@ -6469,9 +6488,9 @@ EditorNode::EditorNode() {
play_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY_SCENE)); play_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY_SCENE));
play_scene_button->set_tooltip(TTR("Play the edited scene.")); play_scene_button->set_tooltip(TTR("Play the edited scene."));
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_MASK_CMD | KEY_R)); play_scene_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play_scene", TTR("Play Scene"), KEY_MASK_CMD | KEY_R));
#else #else
play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_F6)); play_scene_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play_scene", TTR("Play Scene"), KEY_F6));
#endif #endif
play_custom_scene_button = memnew(Button); play_custom_scene_button = memnew(Button);
@ -6483,9 +6502,9 @@ EditorNode::EditorNode() {
play_custom_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY_CUSTOM_SCENE)); play_custom_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_PLAY_CUSTOM_SCENE));
play_custom_scene_button->set_tooltip(TTR("Play custom scene")); play_custom_scene_button->set_tooltip(TTR("Play custom scene"));
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R)); play_custom_scene_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R));
#else #else
play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5)); play_custom_scene_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5));
#endif #endif
HBoxContainer *right_menu_hb = memnew(HBoxContainer); HBoxContainer *right_menu_hb = memnew(HBoxContainer);
@ -7020,19 +7039,21 @@ EditorNode::EditorNode() {
ResourceLoader::set_load_callback(_resource_loaded); ResourceLoader::set_load_callback(_resource_loaded);
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1); ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1);
ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2); ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2);
ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3); ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3);
ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_ALT | KEY_4); ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_ALT | KEY_4);
ED_SHORTCUT("editor/command_palette", TTR("Open Command Palette"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_P);
#else #else
// Use the Ctrl modifier so F2 can be used to rename nodes in the scene tree dock. // Use the Ctrl modifier so F2 can be used to rename nodes in the scene tree dock.
ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_CTRL | KEY_F1); ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_CTRL | KEY_F1);
ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_CTRL | KEY_F2); ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_CTRL | KEY_F2);
ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_CTRL | KEY_F3); ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_CTRL | KEY_F3);
ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_CTRL | KEY_F4); ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_CTRL | KEY_F4);
ED_SHORTCUT("editor/command_palette", TTR("Open Command Palette"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_P);
#endif #endif
ED_SHORTCUT("editor/editor_next", TTR("Open the next Editor")); ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Open the next Editor"));
ED_SHORTCUT("editor/editor_prev", TTR("Open the previous Editor")); ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Open the previous Editor"));
screenshot_timer = memnew(Timer); screenshot_timer = memnew(Timer);
screenshot_timer->set_one_shot(true); screenshot_timer->set_one_shot(true);

View File

@ -32,6 +32,7 @@
#define EDITOR_NODE_H #define EDITOR_NODE_H
#include "core/templates/safe_refcount.h" #include "core/templates/safe_refcount.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_data.h" #include "editor/editor_data.h"
#include "editor/editor_export.h" #include "editor/editor_export.h"
#include "editor/editor_folding.h" #include "editor/editor_folding.h"
@ -55,6 +56,7 @@ class Control;
class DependencyEditor; class DependencyEditor;
class DependencyErrorDialog; class DependencyErrorDialog;
class EditorAbout; class EditorAbout;
class EditorCommandPalette;
class EditorExport; class EditorExport;
class EditorFeatureProfileManager; class EditorFeatureProfileManager;
class EditorFileServer; class EditorFileServer;
@ -192,6 +194,7 @@ private:
EDITOR_OPEN_SCREENSHOT, EDITOR_OPEN_SCREENSHOT,
HELP_SEARCH, HELP_SEARCH,
HELP_COMMAND_PALETTE,
HELP_DOCS, HELP_DOCS,
HELP_QA, HELP_QA,
HELP_REPORT_A_BUG, HELP_REPORT_A_BUG,
@ -342,6 +345,7 @@ private:
CenterContainer *tabs_center; CenterContainer *tabs_center;
EditorQuickOpen *quick_open; EditorQuickOpen *quick_open;
EditorQuickOpen *quick_run; EditorQuickOpen *quick_run;
EditorCommandPalette *command_palette;
HBoxContainer *main_editor_button_vb; HBoxContainer *main_editor_button_vb;
Vector<Button *> main_editor_buttons; Vector<Button *> main_editor_buttons;
@ -505,6 +509,7 @@ private:
void _quick_opened(); void _quick_opened();
void _quick_run(); void _quick_run();
void _open_command_palette();
void _run(bool p_current = false, const String &p_custom = ""); void _run(bool p_current = false, const String &p_custom = "");
void _run_native(const Ref<EditorExportPreset> &p_preset); void _run_native(const Ref<EditorExportPreset> &p_preset);
@ -703,6 +708,7 @@ public:
EditorInspector *get_inspector() { return inspector_dock->get_inspector(); } EditorInspector *get_inspector() { return inspector_dock->get_inspector(); }
Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); } Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); }
ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); } ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); }
EditorCommandPalette *get_editor_command_palette() { return command_palette; }
ProjectSettingsEditor *get_project_settings() { return project_settings; } ProjectSettingsEditor *get_project_settings() { return project_settings; }

View File

@ -310,6 +310,10 @@ bool EditorInterface::is_distraction_free_mode_enabled() const {
return EditorNode::get_singleton()->is_distraction_free_mode_enabled(); return EditorNode::get_singleton()->is_distraction_free_mode_enabled();
} }
EditorCommandPalette *EditorInterface::get_command_palette() const {
return EditorNode::get_singleton()->get_editor_command_palette();
}
EditorInterface *EditorInterface::singleton = nullptr; EditorInterface *EditorInterface::singleton = nullptr;
void EditorInterface::_bind_methods() { void EditorInterface::_bind_methods() {
@ -340,6 +344,7 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_path"), &EditorInterface::get_current_path); ClassDB::bind_method(D_METHOD("get_current_path"), &EditorInterface::get_current_path);
ClassDB::bind_method(D_METHOD("get_file_system_dock"), &EditorInterface::get_file_system_dock); ClassDB::bind_method(D_METHOD("get_file_system_dock"), &EditorInterface::get_file_system_dock);
ClassDB::bind_method(D_METHOD("get_editor_paths"), &EditorInterface::get_editor_paths); ClassDB::bind_method(D_METHOD("get_editor_paths"), &EditorInterface::get_editor_paths);
ClassDB::bind_method(D_METHOD("get_command_palette"), &EditorInterface::get_command_palette);
ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled); ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled);
ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled); ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled);

View File

@ -45,6 +45,7 @@
class EditorNode; class EditorNode;
class Node3D; class Node3D;
class Camera3D; class Camera3D;
class EditorCommandPalette;
class EditorSelection; class EditorSelection;
class EditorExport; class EditorExport;
class EditorSettings; class EditorSettings;
@ -87,6 +88,8 @@ public:
Array get_open_scenes() const; Array get_open_scenes() const;
ScriptEditor *get_script_editor(); ScriptEditor *get_script_editor();
EditorCommandPalette *get_command_palette() const;
void select_file(const String &p_file); void select_file(const String &p_file);
String get_selected_path() const; String get_selected_path() const;
String get_current_path() const; String get_current_path() const;

View File

@ -33,6 +33,7 @@
#include "core/math/expression.h" #include "core/math/expression.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h" #include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_scale.h" #include "editor/editor_scale.h"
#include "editor/editor_settings.h" #include "editor/editor_settings.h"
@ -1941,15 +1942,15 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I); ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I);
ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I); ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I);
ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F); ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
#ifdef OSX_ENABLED #ifdef OSX_ENABLED
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G); ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G); ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G);
ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
#else #else
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3); ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3); ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R); ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
#endif #endif
ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);

View File

@ -145,6 +145,9 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
if (status.press_attempt && status.pressing_inside) { if (status.press_attempt && status.pressing_inside) {
if (toggle_mode) { if (toggle_mode) {
if (Object::cast_to<InputEventShortcut>(*p_event)) {
action_mode = ACTION_MODE_BUTTON_PRESS; // HACK.
}
if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) { if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
if (action_mode == ACTION_MODE_BUTTON_PRESS) { if (action_mode == ACTION_MODE_BUTTON_PRESS) {
status.press_attempt = false; status.press_attempt = false;

View File

@ -44,7 +44,7 @@ void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) {
return; return;
} }
if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) { if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) {
if (!get_parent() || !is_visible_in_tree() || is_disabled()) { if (!get_parent() || !is_visible_in_tree() || is_disabled()) {
return; return;
} }

View File

@ -29,10 +29,10 @@
/*************************************************************************/ /*************************************************************************/
#include "shortcut.h" #include "shortcut.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
void Shortcut::set_event(const Ref<InputEvent> &p_event) { void Shortcut::set_event(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(Object::cast_to<InputEventShortcut>(*p_event));
event = p_event; event = p_event;
emit_changed(); emit_changed();
} }
@ -42,6 +42,12 @@ Ref<InputEvent> Shortcut::get_event() const {
} }
bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const {
Ref<InputEventShortcut> ies = p_event;
if (ies != nullptr) {
if (ies->get_shortcut().ptr() == this) {
return true;
}
}
return event.is_valid() && event->is_match(p_event, true); return event.is_valid() && event->is_match(p_event, true);
} }

View File

@ -2704,6 +2704,7 @@ void Viewport::input(const Ref<InputEvent> &p_event, bool p_local_coords) {
void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) { void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!is_inside_tree());
local_input_handled = false;
if (disable_input || !_can_consume_input_events()) { if (disable_input || !_can_consume_input_events()) {
return; return;
@ -2723,8 +2724,8 @@ void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coor
// Unhandled Input // Unhandled Input
get_tree()->_call_input_pause(unhandled_input_group, "_unhandled_input", ev, this); get_tree()->_call_input_pause(unhandled_input_group, "_unhandled_input", ev, this);
// Unhandled key Input - used for performance reasons - This is called a lot less then _unhandled_input since it ignores MouseMotion, etc // Unhandled key Input - used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, etc
if (!is_input_handled() && Object::cast_to<InputEventKey>(*ev) != nullptr) { if (!is_input_handled() && (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr)) {
get_tree()->_call_input_pause(unhandled_key_input_group, "_unhandled_key_input", ev, this); get_tree()->_call_input_pause(unhandled_key_input_group, "_unhandled_key_input", ev, this);
} }