feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -30,41 +30,77 @@
#include "editor_bottom_panel.h"
#include "core/os/time.h"
#include "core/version.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_about.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/engine_update_label.h"
#include "editor/gui/editor_toaster.h"
#include "editor/gui/editor_version_button.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/link_button.h"
// The metadata key used to store and retrieve the version text to copy to the clipboard.
static const String META_TEXT_TO_COPY = "text_to_copy";
#include "scene/gui/scroll_container.h"
#include "scene/gui/split_container.h"
void EditorBottomPanel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
expand_button->set_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
left_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
right_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
if (is_layout_rtl()) {
bottom_hbox->move_child(left_button, button_scroll->get_index() + 1);
bottom_hbox->move_child(right_button, 0);
} else {
bottom_hbox->move_child(right_button, button_scroll->get_index() + 1);
bottom_hbox->move_child(left_button, 0);
}
} break;
}
}
void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) {
void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) {
for (int i = 0; i < items.size(); i++) {
if (items[i].control == p_control) {
_switch_to_item(p_visible, i);
_switch_to_item(p_visible, i, p_ignore_lock);
return;
}
}
}
void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
void EditorBottomPanel::_scroll(bool p_right) {
HScrollBar *h_scroll = button_scroll->get_h_scroll_bar();
if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
h_scroll->set_value(p_right ? h_scroll->get_max() : 0);
} else if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * (p_right ? 1 : -1));
} else {
h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * 0.5) * (p_right ? 1 : -1));
}
}
void EditorBottomPanel::_update_scroll_buttons() {
bool show_arrows = button_hbox->get_size().width > button_scroll->get_size().width;
left_button->set_visible(show_arrows);
right_button->set_visible(show_arrows);
if (show_arrows) {
_update_disabled_buttons();
}
}
void EditorBottomPanel::_update_disabled_buttons() {
HScrollBar *h_scroll = button_scroll->get_h_scroll_bar();
left_button->set_disabled(h_scroll->get_value() == 0);
right_button->set_disabled(h_scroll->get_value() + h_scroll->get_page() == h_scroll->get_max());
}
void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) {
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].control->is_visible() == p_visible) {
@ -75,6 +111,10 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
ERR_FAIL_NULL(center_split);
if (p_visible) {
if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) {
return;
}
for (int i = 0; i < items.size(); i++) {
items[i].button->set_pressed_no_signal(i == p_idx);
items[i].control->set_visible(i == p_idx);
@ -85,18 +125,24 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
} else {
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
center_split->set_collapsed(false);
pin_button->show();
expand_button->show();
if (expand_button->is_pressed()) {
EditorNode::get_top_split()->hide();
}
expand_button->show();
callable_mp(button_scroll, &ScrollContainer::ensure_control_visible).call_deferred(items[p_idx].button);
} else {
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
items[p_idx].button->set_pressed_no_signal(false);
items[p_idx].control->set_visible(false);
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
center_split->set_collapsed(true);
pin_button->hide();
expand_button->hide();
if (expand_button->is_pressed()) {
EditorNode::get_top_split()->show();
@ -106,17 +152,17 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
last_opened_control = items[p_idx].control;
}
void EditorBottomPanel::_pin_button_toggled(bool p_pressed) {
lock_panel_switching = p_pressed;
}
void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
EditorNode::get_top_split()->set_visible(!p_pressed);
}
void EditorBottomPanel::_version_button_pressed() {
DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
}
bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
if (!p_button->is_pressed()) {
_switch_by_control(true, p_control);
_switch_by_control(true, p_control, true);
}
return false;
}
@ -158,7 +204,7 @@ void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, c
Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) {
Button *tb = memnew(Button);
tb->set_theme_type_variation("BottomPanelButton");
tb->connect("toggled", callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item));
tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true));
tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
tb->set_text(p_text);
tb->set_shortcut(p_shortcut);
@ -205,7 +251,7 @@ void EditorBottomPanel::remove_item(Control *p_item) {
if (was_visible) {
// Open the first panel to ensure that if the removed dock was visible, the bottom
// panel will not collapse.
_switch_to_item(true, 0);
_switch_to_item(true, 0, true);
} else if (last_opened_control == p_item) {
// When a dock is removed by plugins, it might not have been visible, and it
// might have been the last_opened_control. We need to make sure to reset the last opened control.
@ -213,8 +259,8 @@ void EditorBottomPanel::remove_item(Control *p_item) {
}
}
void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible) {
_switch_by_control(p_visible, p_item);
void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible, bool p_ignore_lock) {
_switch_by_control(p_visible, p_item, p_ignore_lock);
}
void EditorBottomPanel::move_item_to_end(Control *p_item) {
@ -240,13 +286,17 @@ void EditorBottomPanel::toggle_last_opened_bottom_panel() {
// Select by control instead of index, so that the last bottom panel is opened correctly
// if it's been reordered since.
if (last_opened_control) {
_switch_by_control(!last_opened_control->is_visible(), last_opened_control);
_switch_by_control(!last_opened_control->is_visible(), last_opened_control, true);
} else {
// Open the first panel in the list if no panel was opened this session.
_switch_to_item(true, 0);
_switch_to_item(true, 0, true);
}
}
void EditorBottomPanel::set_expanded(bool p_expanded) {
expand_button->set_pressed(p_expanded);
}
EditorBottomPanel::EditorBottomPanel() {
item_vbox = memnew(VBoxContainer);
add_child(item_vbox);
@ -255,32 +305,42 @@ EditorBottomPanel::EditorBottomPanel() {
bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
item_vbox->add_child(bottom_hbox);
left_button = memnew(Button);
left_button->set_tooltip_text(TTR("Scroll Left\nHold Ctrl to scroll to the begin.\nHold Shift to scroll one page."));
left_button->set_theme_type_variation("BottomPanelButton");
left_button->set_focus_mode(Control::FOCUS_NONE);
left_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(false));
bottom_hbox->add_child(left_button);
left_button->hide();
button_scroll = memnew(ScrollContainer);
button_scroll->set_h_size_flags(Control::SIZE_EXPAND_FILL);
button_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER);
button_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
button_scroll->get_h_scroll_bar()->connect(CoreStringName(changed), callable_mp(this, &EditorBottomPanel::_update_scroll_buttons), CONNECT_DEFERRED);
button_scroll->get_h_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(this, &EditorBottomPanel::_update_disabled_buttons).unbind(1), CONNECT_DEFERRED);
bottom_hbox->add_child(button_scroll);
right_button = memnew(Button);
right_button->set_tooltip_text(TTR("Scroll Right\nHold Ctrl to scroll to the end.\nHold Shift to scroll one page."));
right_button->set_theme_type_variation("BottomPanelButton");
right_button->set_focus_mode(Control::FOCUS_NONE);
right_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(true));
bottom_hbox->add_child(right_button);
right_button->hide();
callable_mp(this, &EditorBottomPanel::_update_scroll_buttons).call_deferred();
button_hbox = memnew(HBoxContainer);
button_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
bottom_hbox->add_child(button_hbox);
button_hbox->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_BEGIN);
button_scroll->add_child(button_hbox);
editor_toaster = memnew(EditorToaster);
bottom_hbox->add_child(editor_toaster);
version_btn = memnew(LinkButton);
version_btn->set_text(VERSION_FULL_CONFIG);
String hash = String(VERSION_HASH);
if (hash.length() != 0) {
hash = " " + vformat("[%s]", hash.left(9));
}
// Set the text to copy in metadata as it slightly differs from the button's text.
version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
EditorVersionButton *version_btn = memnew(EditorVersionButton(EditorVersionButton::FORMAT_BASIC));
// Fade out the version label to be less prominent, but still readable.
version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
String build_date;
if (VERSION_TIMESTAMP > 0) {
build_date = Time::get_singleton()->get_datetime_string_from_unix_time(VERSION_TIMESTAMP, true) + " UTC";
} else {
build_date = TTR("(unknown)");
}
version_btn->set_tooltip_text(vformat(TTR("Git commit date: %s\nClick to copy the version information."), build_date));
version_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_version_button_pressed));
version_btn->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
bottom_hbox->add_child(version_btn);
@ -288,12 +348,19 @@ EditorBottomPanel::EditorBottomPanel() {
Control *h_spacer = memnew(Control);
bottom_hbox->add_child(h_spacer);
pin_button = memnew(Button);
bottom_hbox->add_child(pin_button);
pin_button->hide();
pin_button->set_theme_type_variation("FlatMenuButton");
pin_button->set_toggle_mode(true);
pin_button->set_tooltip_text(TTR("Pin Bottom Panel Switching"));
pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled));
expand_button = memnew(Button);
bottom_hbox->add_child(expand_button);
expand_button->hide();
expand_button->set_flat(false);
expand_button->set_theme_type_variation("FlatMenuButton");
expand_button->set_toggle_mode(true);
expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
expand_button->connect("toggled", callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTRC("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
expand_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
}

View file

@ -37,8 +37,8 @@ class Button;
class ConfigFile;
class EditorToaster;
class HBoxContainer;
class LinkButton;
class VBoxContainer;
class ScrollContainer;
class EditorBottomPanel : public PanelContainer {
GDCLASS(EditorBottomPanel, PanelContainer);
@ -50,19 +50,26 @@ class EditorBottomPanel : public PanelContainer {
};
Vector<BottomPanelItem> items;
bool lock_panel_switching = false;
VBoxContainer *item_vbox = nullptr;
HBoxContainer *bottom_hbox = nullptr;
Button *left_button = nullptr;
Button *right_button = nullptr;
ScrollContainer *button_scroll = nullptr;
HBoxContainer *button_hbox = nullptr;
EditorToaster *editor_toaster = nullptr;
LinkButton *version_btn = nullptr;
Button *pin_button = nullptr;
Button *expand_button = nullptr;
Control *last_opened_control = nullptr;
void _switch_by_control(bool p_visible, Control *p_control);
void _switch_to_item(bool p_visible, int p_idx);
void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false);
void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false);
void _pin_button_toggled(bool p_pressed);
void _expand_button_toggled(bool p_pressed);
void _version_button_pressed();
void _scroll(bool p_right);
void _update_scroll_buttons();
void _update_disabled_buttons();
bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
@ -75,10 +82,11 @@ public:
Button *add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut = nullptr, bool p_at_front = false);
void remove_item(Control *p_item);
void make_item_visible(Control *p_item, bool p_visible = true);
void make_item_visible(Control *p_item, bool p_visible = true, bool p_ignore_lock = false);
void move_item_to_end(Control *p_item);
void hide_bottom_panel();
void toggle_last_opened_bottom_panel();
void set_expanded(bool p_expanded);
EditorBottomPanel();
};

View file

@ -175,11 +175,14 @@ void EditorDirDialog::ok_pressed() {
void EditorDirDialog::_make_dir() {
TreeItem *ti = tree->get_selected();
ERR_FAIL_NULL(ti);
makedialog->config(ti->get_metadata(0));
const String &directory = ti->get_metadata(0);
makedialog->config(directory, callable_mp(this, &EditorDirDialog::_make_dir_confirm).bind(directory), DirectoryCreateDialog::MODE_DIRECTORY, "new folder");
makedialog->popup_centered();
}
void EditorDirDialog::_make_dir_confirm(const String &p_path) {
void EditorDirDialog::_make_dir_confirm(const String &p_path, const String &p_base_dir) {
FileSystemDock::get_singleton()->create_directory(p_path, p_base_dir);
// Multiple level of directories can be created at once.
String base_dir = p_path.get_base_dir();
while (true) {
@ -191,7 +194,6 @@ void EditorDirDialog::_make_dir_confirm(const String &p_path) {
}
new_dir_path = p_path + "/";
EditorFileSystem::get_singleton()->scan_changes(); // We created a dir, so rescan changes.
}
void EditorDirDialog::_bind_methods() {
@ -216,8 +218,9 @@ EditorDirDialog::EditorDirDialog() {
makedir->connect(SceneStringName(pressed), callable_mp(this, &EditorDirDialog::_make_dir));
tree = memnew(Tree);
vb->add_child(tree);
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vb->add_child(tree);
tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated));
tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED);
@ -228,5 +231,4 @@ EditorDirDialog::EditorDirDialog() {
makedialog = memnew(DirectoryCreateDialog);
add_child(makedialog);
makedialog->connect("dir_created", callable_mp(this, &EditorDirDialog::_make_dir_confirm));
}

View file

@ -56,7 +56,7 @@ class EditorDirDialog : public ConfirmationDialog {
void _update_dir(const Color &p_default_folder_color, const Dictionary &p_assigned_folder_colors, const HashMap<String, Color> &p_folder_colors, bool p_is_dark_theme, TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path = String());
void _make_dir();
void _make_dir_confirm(const String &p_path);
void _make_dir_confirm(const String &p_path, const String &p_base_dir);
void _copy_pressed();
void ok_pressed() override;

File diff suppressed because it is too large Load diff

View file

@ -32,16 +32,20 @@
#define EDITOR_FILE_DIALOG_H
#include "core/io/dir_access.h"
#include "editor/file_info.h"
#include "scene/gui/dialogs.h"
#include "scene/property_list_helper.h"
class GridContainer;
class DependencyRemoveDialog;
class GridContainer;
class HSplitContainer;
class HFlowContainer;
class ItemList;
class MenuButton;
class OptionButton;
class PopupMenu;
class TextureRect;
class VSeparator;
class EditorFileDialog : public ConfirmationDialog {
GDCLASS(EditorFileDialog, ConfirmationDialog);
@ -80,16 +84,19 @@ private:
ITEM_MENU_DELETE,
ITEM_MENU_REFRESH,
ITEM_MENU_NEW_FOLDER,
ITEM_MENU_SHOW_IN_EXPLORER
ITEM_MENU_SHOW_IN_EXPLORER,
ITEM_MENU_SHOW_BUNDLE_CONTENT,
};
ConfirmationDialog *makedialog = nullptr;
LineEdit *makedirname = nullptr;
VSeparator *makedir_sep = nullptr;
Button *makedir = nullptr;
Access access = ACCESS_RESOURCES;
GridContainer *grid_options = nullptr;
HFlowContainer *flow_checkbox_options = nullptr;
GridContainer *grid_select_options = nullptr;
VBoxContainer *vbox = nullptr;
FileMode mode = FILE_MODE_SAVE_FILE;
bool can_create_dir = false;
@ -126,6 +133,14 @@ private:
Button *refresh = nullptr;
Button *favorite = nullptr;
Button *show_hidden = nullptr;
Button *show_search_filter_button = nullptr;
String search_string;
bool show_search_filter = false;
HBoxContainer *filter_hb = nullptr;
LineEdit *filter_box = nullptr;
FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
MenuButton *file_sort_button = nullptr;
Button *fav_up = nullptr;
Button *fav_down = nullptr;
@ -138,6 +153,7 @@ private:
void _push_history();
Vector<String> filters;
Vector<String> processed_filters;
bool previews_enabled = true;
bool preview_waiting = false;
@ -156,8 +172,10 @@ private:
Ref<Texture2D> parent_folder;
Ref<Texture2D> forward_folder;
Ref<Texture2D> back_folder;
Ref<Texture2D> open_folder;
Ref<Texture2D> reload;
Ref<Texture2D> toggle_hidden;
Ref<Texture2D> toggle_filename_filter;
Ref<Texture2D> favorite;
Ref<Texture2D> mode_thumbnails;
Ref<Texture2D> mode_list;
@ -165,6 +183,9 @@ private:
Ref<Texture2D> favorites_up;
Ref<Texture2D> favorites_down;
Ref<Texture2D> filter_box;
Ref<Texture2D> file_sort_button;
Ref<Texture2D> folder;
Color folder_icon_color;
@ -192,10 +213,12 @@ private:
Vector<Option> options;
Dictionary selected_options;
bool options_dirty = false;
String full_dir;
void update_dir();
void update_file_name();
void update_file_list();
void update_search_filter_gui();
void update_filters();
void _focus_file_text();
@ -227,6 +250,11 @@ private:
void _make_dir();
void _make_dir_confirm();
void _focus_filter_box();
void _filter_changed(const String &p_text);
void _search_filter_selected();
void _file_sort_popup(int p_id);
void _delete_items();
void _delete_files_global();
@ -287,6 +315,9 @@ public:
void add_filter(const String &p_filter, const String &p_description = "");
void set_filters(const Vector<String> &p_filters);
Vector<String> get_filters() const;
void clear_search_filter();
void set_search_filter(const String &p_search_filter);
String get_search_filter() const;
void set_enable_multiple_selection(bool p_enable);
Vector<String> get_selected_files() const;
@ -327,6 +358,7 @@ public:
static void set_default_show_hidden_files(bool p_show);
static void set_default_display_mode(DisplayMode p_mode);
void set_show_hidden_files(bool p_show);
void set_show_search_filter(bool p_show);
bool is_showing_hidden_files() const;
void invalidate();

View file

@ -33,7 +33,6 @@
#include "editor/editor_data.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/multi_node_edit.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/margin_container.h"
@ -103,7 +102,6 @@ void EditorObjectSelector::_show_popup() {
sub_objects_menu->set_position(gp);
sub_objects_menu->set_size(Size2(size.width, 1));
sub_objects_menu->take_mouse_focus();
sub_objects_menu->popup();
}
@ -129,12 +127,7 @@ void EditorObjectSelector::update_path() {
continue;
}
Ref<Texture2D> obj_icon;
if (Object::cast_to<MultiNodeEdit>(obj)) {
obj_icon = EditorNode::get_singleton()->get_class_icon(Object::cast_to<MultiNodeEdit>(obj)->get_edited_class_name());
} else {
obj_icon = EditorNode::get_singleton()->get_object_icon(obj);
}
Ref<Texture2D> obj_icon = EditorNode::get_singleton()->get_object_icon(obj);
if (obj_icon.is_valid()) {
current_object_icon->set_texture(obj_icon);
@ -216,9 +209,6 @@ void EditorObjectSelector::_notification(int p_what) {
}
}
void EditorObjectSelector::_bind_methods() {
}
EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) {
history = p_history;

View file

@ -58,7 +58,6 @@ class EditorObjectSelector : public Button {
protected:
void _notification(int p_what);
static void _bind_methods();
public:
virtual Size2 get_minimum_size() const override;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,272 @@
/**************************************************************************/
/* editor_quick_open_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_QUICK_OPEN_DIALOG_H
#define EDITOR_QUICK_OPEN_DIALOG_H
#include "core/templates/oa_hash_map.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/margin_container.h"
class Button;
class CenterContainer;
class CheckButton;
class ConfigFile;
class EditorFileSystemDirectory;
class LineEdit;
class HFlowContainer;
class MarginContainer;
class PanelContainer;
class PopupMenu;
class ScrollContainer;
class StringName;
class Texture2D;
class TextureRect;
class VBoxContainer;
class FuzzySearchResult;
class QuickOpenResultItem;
enum class QuickOpenDisplayMode {
GRID,
LIST,
};
struct QuickOpenResultCandidate {
String file_path;
Ref<Texture2D> thumbnail;
const FuzzySearchResult *result = nullptr;
};
class HighlightedLabel : public Label {
GDCLASS(HighlightedLabel, Label)
Vector<Vector2i> highlights;
void draw_substr_rects(const Vector2i &p_substr, Vector2 p_offset, int p_line_limit, int line_spacing);
public:
void add_highlight(const Vector2i &p_interval);
void reset_highlights();
protected:
void _notification(int p_notification);
};
class QuickOpenResultContainer : public VBoxContainer {
GDCLASS(QuickOpenResultContainer, VBoxContainer)
enum {
FILE_SHOW_IN_FILESYSTEM,
FILE_SHOW_IN_FILE_MANAGER
};
public:
void init(const Vector<StringName> &p_base_types);
void handle_search_box_input(const Ref<InputEvent> &p_ie);
void set_query_and_update(const String &p_query);
void update_results();
bool has_nothing_selected() const;
String get_selected() const;
void save_selected_item();
void cleanup();
QuickOpenResultContainer();
protected:
void _notification(int p_what);
private:
static constexpr int SHOW_ALL_FILES_THRESHOLD = 30;
static constexpr int MAX_HISTORY_SIZE = 20;
Vector<FuzzySearchResult> search_results;
Vector<StringName> base_types;
Vector<String> filepaths;
OAHashMap<String, StringName> filetypes;
Vector<QuickOpenResultCandidate> candidates;
OAHashMap<StringName, Vector<QuickOpenResultCandidate>> selected_history;
String query;
int selection_index = -1;
int num_visible_results = 0;
int max_total_results = 0;
bool showing_history = false;
bool never_opened = true;
Ref<ConfigFile> history_file;
QuickOpenDisplayMode content_display_mode = QuickOpenDisplayMode::LIST;
Vector<QuickOpenResultItem *> result_items;
ScrollContainer *scroll_container = nullptr;
VBoxContainer *list = nullptr;
HFlowContainer *grid = nullptr;
PopupMenu *file_context_menu = nullptr;
PanelContainer *panel_container = nullptr;
CenterContainer *no_results_container = nullptr;
Label *no_results_label = nullptr;
Label *file_details_path = nullptr;
Button *display_mode_toggle = nullptr;
CheckButton *include_addons_toggle = nullptr;
CheckButton *fuzzy_search_toggle = nullptr;
OAHashMap<StringName, Ref<Texture2D>> file_type_icons;
static QuickOpenDisplayMode get_adaptive_display_mode(const Vector<StringName> &p_base_types);
void _ensure_result_vector_capacity();
void _create_initial_results();
void _find_filepaths_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons);
void _setup_candidate(QuickOpenResultCandidate &p_candidate, const String &p_filepath);
void _setup_candidate(QuickOpenResultCandidate &p_candidate, const FuzzySearchResult &p_result);
void _update_fuzzy_search_results();
void _use_default_candidates();
void _score_and_sort_candidates();
void _update_result_items(int p_new_visible_results_count, int p_new_selection_index);
void _move_selection_index(Key p_key);
void _select_item(int p_index);
void _item_input(const Ref<InputEvent> &p_ev, int p_index);
CanvasItem *_get_result_root();
void _layout_result_item(QuickOpenResultItem *p_item);
void _set_display_mode(QuickOpenDisplayMode p_display_mode);
void _toggle_display_mode();
void _toggle_include_addons(bool p_pressed);
void _toggle_fuzzy_search(bool p_pressed);
void _menu_option(int p_option);
String _get_cache_file_path() const;
static void _bind_methods();
};
class QuickOpenResultGridItem : public MarginContainer {
GDCLASS(QuickOpenResultGridItem, MarginContainer)
public:
QuickOpenResultGridItem();
void reset();
void set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight);
void highlight_item(const Color &p_color);
void remove_highlight();
private:
VBoxContainer *vbc = nullptr;
TextureRect *thumbnail = nullptr;
HighlightedLabel *name = nullptr;
};
class QuickOpenResultListItem : public MarginContainer {
GDCLASS(QuickOpenResultListItem, MarginContainer)
public:
QuickOpenResultListItem();
void reset();
void set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight);
void highlight_item(const Color &p_color);
void remove_highlight();
protected:
void _notification(int p_what);
private:
HBoxContainer *hbc = nullptr;
VBoxContainer *text_container = nullptr;
TextureRect *thumbnail = nullptr;
HighlightedLabel *name = nullptr;
HighlightedLabel *path = nullptr;
};
class QuickOpenResultItem : public HBoxContainer {
GDCLASS(QuickOpenResultItem, HBoxContainer)
public:
QuickOpenResultItem();
bool enable_highlights = true;
void reset();
void set_content(const QuickOpenResultCandidate &p_candidate);
void set_display_mode(QuickOpenDisplayMode p_display_mode);
void highlight_item(bool p_enabled);
protected:
void _notification(int p_what);
private:
QuickOpenResultListItem *list_item = nullptr;
QuickOpenResultGridItem *grid_item = nullptr;
Ref<StyleBox> selected_stylebox;
Ref<StyleBox> hovering_stylebox;
Color highlighted_font_color;
bool is_hovering = false;
bool is_selected = false;
void _set_enabled(bool p_enabled);
};
class EditorQuickOpenDialog : public AcceptDialog {
GDCLASS(EditorQuickOpenDialog, AcceptDialog);
public:
void popup_dialog(const Vector<StringName> &p_base_types, const Callable &p_item_selected_callback);
EditorQuickOpenDialog();
protected:
virtual void cancel_pressed() override;
virtual void ok_pressed() override;
private:
static String get_dialog_title(const Vector<StringName> &p_base_types);
LineEdit *search_box = nullptr;
QuickOpenResultContainer *container = nullptr;
Callable item_selected_callback;
void _search_box_text_changed(const String &p_query);
};
#endif // EDITOR_QUICK_OPEN_DIALOG_H

View file

@ -32,16 +32,25 @@
#include "core/config/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_quick_open.h"
#include "editor/editor_run_native.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_quick_open_dialog.h"
#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#ifndef _3D_DISABLED
#include "servers/xr_server.h"
#endif // _3D_DISABLED
EditorRunBar *EditorRunBar::singleton = nullptr;
void EditorRunBar::_notification(int p_what) {
@ -50,10 +59,39 @@ void EditorRunBar::_notification(int p_what) {
_reset_play_buttons();
} break;
case NOTIFICATION_READY: {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
recovery_mode_show_dialog();
}
} break;
case NOTIFICATION_THEME_CHANGED: {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadRecoveryMode"), EditorStringName(EditorStyles)));
recovery_mode_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("RecoveryModeButton"), EditorStringName(EditorStyles)));
recovery_mode_button->add_theme_style_override("hover", get_theme_stylebox(SNAME("RecoveryModeButton"), EditorStringName(EditorStyles)));
recovery_mode_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
recovery_mode_reload_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
recovery_mode_button->begin_bulk_theme_override();
recovery_mode_button->add_theme_color_override("icon_normal_color", Color(0.3, 0.3, 0.3, 1));
recovery_mode_button->add_theme_color_override("icon_pressed_color", Color(0.4, 0.4, 0.4, 1));
recovery_mode_button->add_theme_color_override("icon_hover_color", Color(0.6, 0.6, 0.6, 1));
Color dark_color = get_theme_color("recovery_mode_text_color", EditorStringName(Editor));
recovery_mode_button->add_theme_color_override(SceneStringName(font_color), dark_color);
recovery_mode_button->add_theme_color_override("font_pressed_color", dark_color.lightened(0.2));
recovery_mode_button->add_theme_color_override("font_hover_color", dark_color.lightened(0.4));
recovery_mode_button->add_theme_color_override("font_hover_pressed_color", dark_color.lightened(0.2));
recovery_mode_button->end_bulk_theme_override();
return;
}
_update_play_buttons();
pause_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
stop_button->set_icon(get_editor_theme_icon(SNAME("Stop")));
profiler_autostart_indicator->set_button_icon(get_editor_theme_icon(SNAME("ProfilerAutostartWarning")));
pause_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
if (is_movie_maker_enabled()) {
main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadMovieMode"), EditorStringName(EditorStyles)));
@ -63,7 +101,7 @@ void EditorRunBar::_notification(int p_what) {
write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonNormal"), EditorStringName(EditorStyles)));
}
write_movie_button->set_icon(get_editor_theme_icon(SNAME("MainMovieWrite")));
write_movie_button->set_button_icon(get_editor_theme_icon(SNAME("MainMovieWrite")));
// This button behaves differently, so color it as such.
write_movie_button->begin_bulk_theme_override();
write_movie_button->add_theme_color_override("icon_normal_color", get_theme_color(SNAME("movie_writer_icon_normal"), EditorStringName(EditorStyles)));
@ -76,20 +114,28 @@ void EditorRunBar::_notification(int p_what) {
}
void EditorRunBar::_reset_play_buttons() {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
play_button->set_pressed(false);
play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
play_button->set_tooltip_text(TTR("Play the project."));
play_scene_button->set_pressed(false);
play_scene_button->set_icon(get_editor_theme_icon(SNAME("PlayScene")));
play_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayScene")));
play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
play_custom_scene_button->set_pressed(false);
play_custom_scene_button->set_icon(get_editor_theme_icon(SNAME("PlayCustom")));
play_custom_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayCustom")));
play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
}
void EditorRunBar::_update_play_buttons() {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
_reset_play_buttons();
if (!is_playing()) {
return;
@ -106,7 +152,7 @@ void EditorRunBar::_update_play_buttons() {
if (active_button) {
active_button->set_pressed(true);
active_button->set_icon(get_editor_theme_icon(SNAME("Reload")));
active_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
active_button->set_tooltip_text(TTR("Reload the played scene."));
}
}
@ -121,34 +167,51 @@ void EditorRunBar::_write_movie_toggled(bool p_enabled) {
}
}
void EditorRunBar::_quick_run_selected() {
play_custom_scene(quick_run->get_selected());
Vector<String> EditorRunBar::_get_xr_mode_play_args(int p_xr_mode_id) {
Vector<String> play_args;
if (p_xr_mode_id == 0) {
// Play in regular mode, xr mode off.
play_args.push_back("--xr-mode");
play_args.push_back("off");
} else if (p_xr_mode_id == 1) {
// Play in xr mode.
play_args.push_back("--xr-mode");
play_args.push_back("on");
}
return play_args;
}
void EditorRunBar::_play_custom_pressed() {
void EditorRunBar::_quick_run_selected(const String &p_file_path, int p_id) {
play_custom_scene(p_file_path, _get_xr_mode_play_args(p_id));
}
void EditorRunBar::_play_custom_pressed(int p_id) {
if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CUSTOM) {
stop_playing();
quick_run->popup_dialog("PackedScene", true);
quick_run->set_title(TTR("Quick Run Scene..."));
EditorNode::get_singleton()->get_quick_open_dialog()->popup_dialog({ "PackedScene" }, callable_mp(this, &EditorRunBar::_quick_run_selected).bind(p_id));
play_custom_scene_button->set_pressed(false);
} else {
Vector<String> play_args = _get_xr_mode_play_args(p_id);
// Reload if already running a custom scene.
String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
play_custom_scene(last_custom_scene);
play_custom_scene(last_custom_scene, play_args);
}
}
void EditorRunBar::_play_current_pressed() {
void EditorRunBar::_play_current_pressed(int p_id) {
Vector<String> play_args = _get_xr_mode_play_args(p_id);
if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CURRENT) {
play_current_scene();
play_current_scene(false, play_args);
} else {
// Reload if already running the current scene.
play_current_scene(true);
play_current_scene(true, play_args);
}
}
void EditorRunBar::_run_scene(const String &p_scene_path) {
void EditorRunBar::_run_scene(const String &p_scene_path, const Vector<String> &p_run_args) {
ERR_FAIL_COND_MSG(current_mode == RUN_CUSTOM && p_scene_path.is_empty(), "Attempting to run a custom scene with an empty path.");
if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
@ -233,7 +296,7 @@ void EditorRunBar::_run_scene(const String &p_scene_path) {
}
EditorDebuggerNode::get_singleton()->start();
Error error = editor_run.run(run_filename, write_movie_file);
Error error = editor_run.run(run_filename, write_movie_file, p_run_args);
if (error != OK) {
EditorDebuggerNode::get_singleton()->stop();
EditorNode::get_singleton()->show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
@ -262,7 +325,34 @@ void EditorRunBar::_run_native(const Ref<EditorExportPreset> &p_preset) {
}
}
void EditorRunBar::_profiler_autostart_indicator_pressed() {
// Switch to the first profiler tab in the bottom panel.
EditorNode::get_singleton()->get_bottom_panel()->make_item_visible(EditorDebuggerNode::get_singleton(), true);
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(3);
} else if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(4);
} else {
// Switch to the network profiler tab.
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(8);
}
}
void EditorRunBar::recovery_mode_show_dialog() {
recovery_mode_popup->popup_centered();
}
void EditorRunBar::recovery_mode_reload_project() {
EditorNode::get_singleton()->trigger_menu_option(EditorNode::PROJECT_RELOAD_CURRENT_PROJECT, false);
}
void EditorRunBar::play_main_scene(bool p_from_native) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
return;
}
if (p_from_native) {
run_native->resume_run_native();
} else {
@ -273,7 +363,12 @@ void EditorRunBar::play_main_scene(bool p_from_native) {
}
}
void EditorRunBar::play_current_scene(bool p_reload) {
void EditorRunBar::play_current_scene(bool p_reload, const Vector<String> &p_play_args) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
return;
}
String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
EditorNode::get_singleton()->save_default_environment();
@ -281,17 +376,22 @@ void EditorRunBar::play_current_scene(bool p_reload) {
current_mode = RunMode::RUN_CURRENT;
if (p_reload) {
_run_scene(last_current_scene);
_run_scene(last_current_scene, p_play_args);
} else {
_run_scene();
_run_scene("", p_play_args);
}
}
void EditorRunBar::play_custom_scene(const String &p_custom) {
void EditorRunBar::play_custom_scene(const String &p_custom, const Vector<String> &p_play_args) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
return;
}
stop_playing();
current_mode = RunMode::RUN_CUSTOM;
_run_scene(p_custom);
_run_scene(p_custom, p_play_args);
}
void EditorRunBar::stop_playing() {
@ -345,6 +445,10 @@ void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
}
}
OS::ProcessID EditorRunBar::get_current_process() const {
return editor_run.get_current_process();
}
void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
write_movie_button->set_pressed(p_enabled);
}
@ -353,6 +457,29 @@ bool EditorRunBar::is_movie_maker_enabled() const {
return write_movie_button->is_pressed();
}
void EditorRunBar::update_profiler_autostart_indicator() {
bool profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false);
bool visual_profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false);
bool network_profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false);
bool any_profiler_active = profiler_active | visual_profiler_active | network_profiler_active;
any_profiler_active &= !Engine::get_singleton()->is_recovery_mode_hint();
profiler_autostart_indicator->set_visible(any_profiler_active);
if (any_profiler_active) {
String tooltip = TTR("Autostart is enabled for the following profilers, which can have a performance impact:");
if (profiler_active) {
tooltip += "\n- " + TTR("Profiler");
}
if (visual_profiler_active) {
tooltip += "\n- " + TTR("Visual Profiler");
}
if (network_profiler_active) {
tooltip += "\n- " + TTR("Network Profiler");
}
tooltip += "\n\n" + TTR("Click to open the first profiler for which autostart is enabled.");
profiler_autostart_indicator->set_tooltip_text(tooltip);
}
}
HBoxContainer *EditorRunBar::get_buttons_container() {
return main_hbox;
}
@ -365,21 +492,69 @@ void EditorRunBar::_bind_methods() {
EditorRunBar::EditorRunBar() {
singleton = this;
outer_hbox = memnew(HBoxContainer);
add_child(outer_hbox);
// Use a button for the indicator since it comes with a background panel and pixel perfect centering of an icon.
profiler_autostart_indicator = memnew(Button);
profiler_autostart_indicator->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
profiler_autostart_indicator->set_focus_mode(FOCUS_NONE);
profiler_autostart_indicator->set_theme_type_variation("ProfilerAutostartIndicator");
profiler_autostart_indicator->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_profiler_autostart_indicator_pressed));
outer_hbox->add_child(profiler_autostart_indicator);
update_profiler_autostart_indicator();
main_panel = memnew(PanelContainer);
add_child(main_panel);
outer_hbox->add_child(main_panel);
main_hbox = memnew(HBoxContainer);
main_panel->add_child(main_hbox);
if (Engine::get_singleton()->is_recovery_mode_hint()) {
recovery_mode_popup = memnew(AcceptDialog);
recovery_mode_popup->set_min_size(Size2(550, 70) * EDSCALE);
recovery_mode_popup->set_title(TTR("Recovery Mode"));
recovery_mode_popup->set_text(
TTR("Godot opened the project in Recovery Mode, which is a special mode that can help recover projects that crash the engine upon initialization. The following features have been temporarily disabled:") +
String::utf8("\n\n") + TTR("Tool scripts") +
String::utf8("\n") + TTR("Editor plugins") +
String::utf8("\n") + TTR("GDExtension addons") +
String::utf8("\n") + TTR("Automatic scene restoring") +
String::utf8("\n\n") + TTR("If the project cannot be opened outside of this mode, then it's very likely any of these components is preventing this project from launching. This mode is intended only for basic editing to troubleshoot such issues, and therefore it is not possible to run a project in this mode.") +
String::utf8("\n\n") + TTR("To disable Recovery Mode, reload the project by pressing the Reload button next to the Recovery Mode banner, or by reopening the project normally."));
recovery_mode_popup->set_autowrap(true);
add_child(recovery_mode_popup);
recovery_mode_reload_button = memnew(Button);
main_hbox->add_child(recovery_mode_reload_button);
recovery_mode_reload_button->set_theme_type_variation("RunBarButton");
recovery_mode_reload_button->set_focus_mode(Control::FOCUS_NONE);
recovery_mode_reload_button->set_tooltip_text(TTR("Disable recovery mode and reload the project."));
recovery_mode_reload_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::recovery_mode_reload_project));
recovery_mode_panel = memnew(PanelContainer);
main_hbox->add_child(recovery_mode_panel);
recovery_mode_button = memnew(Button);
recovery_mode_panel->add_child(recovery_mode_button);
recovery_mode_button->set_theme_type_variation("RunBarButton");
recovery_mode_button->set_focus_mode(Control::FOCUS_NONE);
recovery_mode_button->set_text(TTR("Recovery Mode"));
recovery_mode_button->set_tooltip_text(TTR("Recovery Mode is enabled. Click for more details."));
recovery_mode_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::recovery_mode_show_dialog));
return;
}
play_button = memnew(Button);
main_hbox->add_child(play_button);
play_button->set_theme_type_variation("RunBarButton");
play_button->set_toggle_mode(true);
play_button->set_focus_mode(Control::FOCUS_NONE);
play_button->set_tooltip_text(TTR("Run the project's default scene."));
play_button->set_tooltip_text(TTRC("Run the project's default scene."));
play_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::play_main_scene).bind(false));
ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
ED_SHORTCUT_AND_COMMAND("editor/run_project", TTRC("Run Project"), Key::F5);
ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
@ -388,10 +563,10 @@ EditorRunBar::EditorRunBar() {
pause_button->set_theme_type_variation("RunBarButton");
pause_button->set_toggle_mode(true);
pause_button->set_focus_mode(Control::FOCUS_NONE);
pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
pause_button->set_tooltip_text(TTRC("Pause the running project's execution for debugging."));
pause_button->set_disabled(true);
ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
ED_SHORTCUT("editor/pause_running_project", TTRC("Pause Running Project"), Key::F7);
ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
@ -399,11 +574,11 @@ EditorRunBar::EditorRunBar() {
main_hbox->add_child(stop_button);
stop_button->set_theme_type_variation("RunBarButton");
stop_button->set_focus_mode(Control::FOCUS_NONE);
stop_button->set_tooltip_text(TTR("Stop the currently running project."));
stop_button->set_tooltip_text(TTRC("Stop the currently running project."));
stop_button->set_disabled(true);
stop_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::stop_playing));
ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
ED_SHORTCUT("editor/stop_running_project", TTRC("Stop Running Project"), Key::F8);
ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
@ -411,27 +586,57 @@ EditorRunBar::EditorRunBar() {
main_hbox->add_child(run_native);
run_native->connect("native_run", callable_mp(this, &EditorRunBar::_run_native));
play_scene_button = memnew(Button);
bool add_play_xr_mode_options = false;
#ifndef _3D_DISABLED
if (OS::get_singleton()->has_feature("xr_editor") &&
(XRServer::get_xr_mode() == XRServer::XRMODE_ON ||
(XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT && GLOBAL_GET("xr/openxr/enabled")))) {
// If this is the XR editor and openxr is enabled, we turn the `play_scene_button` and
// `play_custom_scene_button` into MenuButtons to provide the option to start a scene in
// either regular mode or XR mode.
add_play_xr_mode_options = true;
}
#endif // _3D_DISABLED
if (add_play_xr_mode_options) {
MenuButton *menu_button = memnew(MenuButton);
PopupMenu *popup = menu_button->get_popup();
popup->add_item(TTRC("Run Scene in Regular Mode"), 0);
popup->add_item(TTRC("Run Scene in XR Mode"), 1);
popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorRunBar::_play_current_pressed));
play_scene_button = menu_button;
} else {
play_scene_button = memnew(Button);
play_scene_button->set_toggle_mode(true);
play_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_current_pressed).bind(-1));
}
main_hbox->add_child(play_scene_button);
play_scene_button->set_theme_type_variation("RunBarButton");
play_scene_button->set_toggle_mode(true);
play_scene_button->set_focus_mode(Control::FOCUS_NONE);
play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
play_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_current_pressed));
play_scene_button->set_tooltip_text(TTRC("Run the currently edited scene."));
ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTRC("Run Current Scene"), Key::F6);
ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
play_custom_scene_button = memnew(Button);
if (add_play_xr_mode_options) {
MenuButton *menu_button = memnew(MenuButton);
PopupMenu *popup = menu_button->get_popup();
popup->add_item(TTRC("Run in Regular Mode"), 0);
popup->add_item(TTRC("Run in XR Mode"), 1);
popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorRunBar::_play_custom_pressed));
play_custom_scene_button = menu_button;
} else {
play_custom_scene_button = memnew(Button);
play_custom_scene_button->set_toggle_mode(true);
play_custom_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_custom_pressed).bind(-1));
}
main_hbox->add_child(play_custom_scene_button);
play_custom_scene_button->set_theme_type_variation("RunBarButton");
play_custom_scene_button->set_toggle_mode(true);
play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
play_custom_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_custom_pressed));
play_custom_scene_button->set_tooltip_text(TTRC("Run a specific scene."));
ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTRC("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
@ -445,9 +650,5 @@ EditorRunBar::EditorRunBar() {
write_movie_button->set_pressed(false);
write_movie_button->set_focus_mode(Control::FOCUS_NONE);
write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
write_movie_button->connect("toggled", callable_mp(this, &EditorRunBar::_write_movie_toggled));
quick_run = memnew(EditorQuickOpen);
add_child(quick_run);
quick_run->connect("quick_open", callable_mp(this, &EditorRunBar::_quick_run_selected));
write_movie_button->connect(SceneStringName(toggled), callable_mp(this, &EditorRunBar::_write_movie_toggled));
}

View file

@ -37,9 +37,9 @@
class Button;
class EditorRunNative;
class EditorQuickOpen;
class PanelContainer;
class HBoxContainer;
class AcceptDialog;
class EditorRunBar : public MarginContainer {
GDCLASS(EditorRunBar, MarginContainer);
@ -55,6 +55,14 @@ class EditorRunBar : public MarginContainer {
PanelContainer *main_panel = nullptr;
HBoxContainer *main_hbox = nullptr;
HBoxContainer *outer_hbox = nullptr;
Button *profiler_autostart_indicator = nullptr;
PanelContainer *recovery_mode_panel = nullptr;
Button *recovery_mode_button = nullptr;
Button *recovery_mode_reload_button = nullptr;
AcceptDialog *recovery_mode_popup = nullptr;
Button *play_button = nullptr;
Button *pause_button = nullptr;
@ -68,8 +76,6 @@ class EditorRunBar : public MarginContainer {
PanelContainer *write_movie_panel = nullptr;
Button *write_movie_button = nullptr;
EditorQuickOpen *quick_run = nullptr;
RunMode current_mode = RunMode::STOPPED;
String run_custom_filename;
String run_current_filename;
@ -78,14 +84,19 @@ class EditorRunBar : public MarginContainer {
void _update_play_buttons();
void _write_movie_toggled(bool p_enabled);
void _quick_run_selected();
void _quick_run_selected(const String &p_file_path, int p_id = -1);
void _play_current_pressed();
void _play_custom_pressed();
void _play_current_pressed(int p_id = -1);
void _play_custom_pressed(int p_id = -1);
void _run_scene(const String &p_scene_path = "");
void _run_scene(const String &p_scene_path = "", const Vector<String> &p_run_args = Vector<String>());
void _run_native(const Ref<EditorExportPreset> &p_preset);
void _profiler_autostart_indicator_pressed();
private:
static Vector<String> _get_xr_mode_play_args(int p_xr_mode_id);
protected:
void _notification(int p_what);
static void _bind_methods();
@ -93,9 +104,12 @@ protected:
public:
static EditorRunBar *get_singleton() { return singleton; }
void recovery_mode_show_dialog();
void recovery_mode_reload_project();
void play_main_scene(bool p_from_native = false);
void play_current_scene(bool p_reload = false);
void play_custom_scene(const String &p_custom);
void play_current_scene(bool p_reload = false, const Vector<String> &p_play_args = Vector<String>());
void play_custom_scene(const String &p_custom, const Vector<String> &p_play_args = Vector<String>());
void stop_playing();
bool is_playing() const;
@ -105,10 +119,13 @@ public:
OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
void stop_child_process(OS::ProcessID p_pid);
OS::ProcessID get_current_process() const;
void set_movie_maker_enabled(bool p_enabled);
bool is_movie_maker_enabled() const;
void update_profiler_autostart_indicator();
Button *get_pause_button() { return pause_button; }
HBoxContainer *get_buttons_container();

View file

@ -30,12 +30,15 @@
#include "editor_scene_tabs.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/editor_context_menu_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -45,15 +48,13 @@
#include "scene/gui/tab_bar.h"
#include "scene/gui/texture_rect.h"
EditorSceneTabs *EditorSceneTabs::singleton = nullptr;
void EditorSceneTabs::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
tabbar_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer")));
scene_tabs->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
scene_tab_add->set_icon(get_editor_theme_icon(SNAME("Add")));
scene_tab_add->set_button_icon(get_editor_theme_icon(SNAME("Add")));
scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f));
scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
@ -90,6 +91,14 @@ void EditorSceneTabs::_scene_tab_hovered(int p_tab) {
if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) {
return;
}
// Currently the tab previews are displayed under the running game process when embed.
// Right now, the easiest technique to fix that is to prevent displaying the tab preview
// when the user is in the Game View.
if (EditorNode::get_singleton()->get_editor_main_screen()->get_selected_index() == EditorMainScreen::EDITOR_GAME && EditorRunBar::get_singleton()->is_playing()) {
return;
}
int current_tab = scene_tabs->get_current_tab();
if (p_tab == current_tab || p_tab < 0) {
@ -153,6 +162,11 @@ void EditorSceneTabs::_reposition_active_tab(int p_to_index) {
}
void EditorSceneTabs::_update_context_menu() {
#define DISABLE_LAST_OPTION_IF(m_condition) \
if (m_condition) { \
scene_tabs_context_menu->set_item_disabled(-1, true); \
}
scene_tabs_context_menu->clear();
scene_tabs_context_menu->reset_size();
@ -162,12 +176,11 @@ void EditorSceneTabs::_update_context_menu() {
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), EditorNode::FILE_NEW_SCENE);
if (tab_id >= 0) {
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), EditorNode::FILE_SAVE_SCENE);
_disable_menu_option_if(EditorNode::FILE_SAVE_SCENE, no_root_node);
DISABLE_LAST_OPTION_IF(no_root_node);
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), EditorNode::FILE_SAVE_AS_SCENE);
_disable_menu_option_if(EditorNode::FILE_SAVE_AS_SCENE, no_root_node);
DISABLE_LAST_OPTION_IF(no_root_node);
}
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), EditorNode::FILE_SAVE_ALL_SCENES);
bool can_save_all_scenes = false;
for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) {
if (!EditorNode::get_editor_data().get_scene_path(i).is_empty() && EditorNode::get_editor_data().get_edited_scene_root(i)) {
@ -175,32 +188,41 @@ void EditorSceneTabs::_update_context_menu() {
break;
}
}
_disable_menu_option_if(EditorNode::FILE_SAVE_ALL_SCENES, !can_save_all_scenes);
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), EditorNode::FILE_SAVE_ALL_SCENES);
DISABLE_LAST_OPTION_IF(!can_save_all_scenes);
if (tab_id >= 0) {
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), EditorNode::FILE_SHOW_IN_FILESYSTEM);
_disable_menu_option_if(EditorNode::FILE_SHOW_IN_FILESYSTEM, !ResourceLoader::exists(EditorNode::get_editor_data().get_scene_path(tab_id)));
scene_tabs_context_menu->add_item(TTR("Play This Scene"), EditorNode::FILE_RUN_SCENE);
_disable_menu_option_if(EditorNode::FILE_RUN_SCENE, no_root_node);
scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), SCENE_SHOW_IN_FILESYSTEM);
DISABLE_LAST_OPTION_IF(!ResourceLoader::exists(EditorNode::get_editor_data().get_scene_path(tab_id)));
scene_tabs_context_menu->add_item(TTR("Play This Scene"), SCENE_RUN);
DISABLE_LAST_OPTION_IF(no_root_node);
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), EditorNode::FILE_CLOSE);
scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_CLOSE), TTR("Close Tab"));
scene_tabs_context_menu->set_item_text(-1, TTR("Close Tab"));
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene"), EditorNode::FILE_OPEN_PREV);
scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_OPEN_PREV), TTR("Undo Close Tab"));
_disable_menu_option_if(EditorNode::FILE_OPEN_PREV, !EditorNode::get_singleton()->has_previous_scenes());
scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), EditorNode::FILE_CLOSE_OTHERS);
_disable_menu_option_if(EditorNode::FILE_CLOSE_OTHERS, EditorNode::get_editor_data().get_edited_scene_count() <= 1);
scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), EditorNode::FILE_CLOSE_RIGHT);
_disable_menu_option_if(EditorNode::FILE_CLOSE_RIGHT, EditorNode::get_editor_data().get_edited_scene_count() == tab_id + 1);
scene_tabs_context_menu->add_item(TTR("Close All Tabs"), EditorNode::FILE_CLOSE_ALL);
scene_tabs_context_menu->set_item_text(-1, TTR("Undo Close Tab"));
DISABLE_LAST_OPTION_IF(!EditorNode::get_singleton()->has_previous_scenes());
scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), SCENE_CLOSE_OTHERS);
DISABLE_LAST_OPTION_IF(EditorNode::get_editor_data().get_edited_scene_count() <= 1);
scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), SCENE_CLOSE_RIGHT);
DISABLE_LAST_OPTION_IF(EditorNode::get_editor_data().get_edited_scene_count() == tab_id + 1);
scene_tabs_context_menu->add_item(TTR("Close All Tabs"), SCENE_CLOSE_ALL);
const PackedStringArray paths = { EditorNode::get_editor_data().get_scene_path(tab_id) };
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(scene_tabs_context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, paths);
} else {
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(scene_tabs_context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, {});
}
#undef DISABLE_LAST_OPTION_IF
last_hovered_tab = tab_id;
}
void EditorSceneTabs::_disable_menu_option_if(int p_option, bool p_condition) {
if (p_condition) {
scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(p_option), true);
void EditorSceneTabs::_custom_menu_option(int p_option) {
if (p_option >= EditorContextMenuPlugin::BASE_ID) {
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, p_option, last_hovered_tab >= 0 ? EditorNode::get_editor_data().get_scene_path(last_hovered_tab) : String());
}
}
@ -410,6 +432,7 @@ EditorSceneTabs::EditorSceneTabs() {
scene_tabs_context_menu = memnew(PopupMenu);
tabbar_container->add_child(scene_tabs_context_menu);
scene_tabs_context_menu->connect(SceneStringName(id_pressed), callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(false));
scene_tabs_context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorSceneTabs::_custom_menu_option));
scene_tab_add = memnew(Button);
scene_tab_add->set_flat(true);

View file

@ -44,8 +44,18 @@ class TextureRect;
class EditorSceneTabs : public MarginContainer {
GDCLASS(EditorSceneTabs, MarginContainer);
static EditorSceneTabs *singleton;
inline static EditorSceneTabs *singleton = nullptr;
public:
enum {
SCENE_SHOW_IN_FILESYSTEM = 3000, // Prevents conflicts with EditorNode options.
SCENE_RUN,
SCENE_CLOSE_OTHERS,
SCENE_CLOSE_RIGHT,
SCENE_CLOSE_ALL,
};
private:
PanelContainer *tabbar_panel = nullptr;
HBoxContainer *tabbar_container = nullptr;
@ -57,6 +67,8 @@ class EditorSceneTabs : public MarginContainer {
Panel *tab_preview_panel = nullptr;
TextureRect *tab_preview = nullptr;
int last_hovered_tab = -1;
void _scene_tab_changed(int p_tab);
void _scene_tab_script_edited(int p_tab);
void _scene_tab_closed(int p_tab);
@ -68,7 +80,7 @@ class EditorSceneTabs : public MarginContainer {
void _update_tab_titles();
void _reposition_active_tab(int p_to_index);
void _update_context_menu();
void _disable_menu_option_if(int p_option, bool p_condition);
void _custom_menu_option(int p_option);
void _tab_preview_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata);

View file

@ -35,10 +35,7 @@
#include "core/os/keyboard.h"
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
bool EditorSpinSlider::is_text_field() const {
return true;
}
#include "scene/theme/theme_db.h"
String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
if (!read_only && grabber->is_visible()) {
@ -70,6 +67,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
} else {
set_value(get_value() - get_step());
}
emit_signal("updown_pressed");
return;
}
_grab_start();
@ -184,10 +182,6 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (is_read_only()) {
return;
}
if (grabbing_grabber) {
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::WHEEL_UP) {
@ -240,7 +234,7 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && !is_read_only()) {
if (k.is_valid() && k->is_pressed() && !read_only) {
Key code = k->get_keycode();
switch (code) {
@ -314,7 +308,7 @@ void EditorSpinSlider::_draw_spin_slider() {
bool rtl = is_layout_rtl();
Vector2 size = get_size();
Ref<StyleBox> sb = get_theme_stylebox(is_read_only() ? SNAME("read_only") : CoreStringName(normal), SNAME("LineEdit"));
Ref<StyleBox> sb = get_theme_stylebox(read_only ? SNAME("read_only") : CoreStringName(normal), SNAME("LineEdit"));
if (!flat) {
draw_style_box(sb, Rect2(Vector2(), size));
}
@ -326,14 +320,14 @@ void EditorSpinSlider::_draw_spin_slider() {
int label_width = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
int number_width = size.width - sb->get_minimum_size().width - label_width - sep;
Ref<Texture2D> updown = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
Ref<Texture2D> updown = get_theme_icon(read_only ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
String numstr = get_text_value();
int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size);
Color fc = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit"));
Color lc = get_theme_color(is_read_only() ? SNAME("read_only_label_color") : SNAME("label_color"));
Color fc = get_theme_color(read_only ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit"));
Color lc = get_theme_color(read_only ? SNAME("read_only_label_color") : SNAME("label_color"));
if (flat && !label.is_empty()) {
Ref<StyleBox> label_bg = get_theme_stylebox(SNAME("label_bg"), SNAME("EditorSpinSlider"));
@ -372,7 +366,7 @@ void EditorSpinSlider::_draw_spin_slider() {
}
if (glyphs[i].font_rid != RID()) {
TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
} else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
} else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
TS->draw_hex_code_box(ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
}
}
@ -382,8 +376,8 @@ void EditorSpinSlider::_draw_spin_slider() {
TS->free_rid(num_rid);
if (!hide_slider) {
if (get_step() == 1) {
Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
if (editing_integer) {
Ref<Texture2D> updown2 = read_only ? theme_cache.updown_disabled_icon : theme_cache.updown_icon;
int updown_vofs = (size.height - updown2->get_height()) / 2;
if (rtl) {
updown_offset = sb->get_margin(SIDE_LEFT);
@ -444,7 +438,7 @@ void EditorSpinSlider::_draw_spin_slider() {
Vector2 scale = get_global_transform_with_canvas().get_scale();
grabber->set_scale(scale);
grabber->reset_size();
grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
grabber->set_position((grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
if (mousewheel_over_grabber) {
Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size);
@ -536,6 +530,19 @@ bool EditorSpinSlider::is_hiding_slider() const {
return hide_slider;
}
void EditorSpinSlider::set_editing_integer(bool p_editing_integer) {
if (p_editing_integer == editing_integer) {
return;
}
editing_integer = p_editing_integer;
queue_redraw();
}
bool EditorSpinSlider::is_editing_integer() const {
return editing_integer;
}
void EditorSpinSlider::set_label(const String &p_label) {
label = p_label;
queue_redraw();
@ -603,7 +610,7 @@ void EditorSpinSlider::_value_focus_exited() {
return;
}
if (is_read_only()) {
if (read_only) {
// Spin slider has become read only while it was being edited.
return;
}
@ -614,13 +621,13 @@ void EditorSpinSlider::_value_focus_exited() {
// -> TAB was pressed
// -> modal_close was not called
// -> need to close/hide manually
if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
if (!is_visible_in_tree() || value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
// Hidden or something else took focus.
if (value_input_popup) {
value_input_popup->hide();
}
//tab was pressed
} else {
//enter, click, esc
// Enter or Esc was pressed.
grab_focus();
}
@ -639,7 +646,7 @@ void EditorSpinSlider::_grabber_mouse_exited() {
void EditorSpinSlider::set_read_only(bool p_enable) {
read_only = p_enable;
if (read_only && value_input) {
if (read_only && value_input && value_input->is_inside_tree()) {
value_input->release_focus();
}
@ -664,6 +671,10 @@ bool EditorSpinSlider::is_grabbing() const {
}
void EditorSpinSlider::_focus_entered() {
if (read_only) {
return;
}
_ensure_input_popup();
value_input->set_text(get_text_value());
value_input_popup->set_size(get_size());
@ -691,16 +702,24 @@ void EditorSpinSlider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_slider", "hide_slider"), &EditorSpinSlider::set_hide_slider);
ClassDB::bind_method(D_METHOD("is_hiding_slider"), &EditorSpinSlider::is_hiding_slider);
ClassDB::bind_method(D_METHOD("set_editing_integer", "editing_integer"), &EditorSpinSlider::set_editing_integer);
ClassDB::bind_method(D_METHOD("is_editing_integer"), &EditorSpinSlider::is_editing_integer);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_slider"), "set_hide_slider", "is_hiding_slider");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editing_integer"), "set_editing_integer", "is_editing_integer");
ADD_SIGNAL(MethodInfo("grabbed"));
ADD_SIGNAL(MethodInfo("ungrabbed"));
ADD_SIGNAL(MethodInfo("updown_pressed"));
ADD_SIGNAL(MethodInfo("value_focus_entered"));
ADD_SIGNAL(MethodInfo("value_focus_exited"));
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, EditorSpinSlider, updown_icon, "updown");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, EditorSpinSlider, updown_disabled_icon, "updown_disabled");
}
void EditorSpinSlider::_ensure_input_popup() {
@ -713,11 +732,12 @@ void EditorSpinSlider::_ensure_input_popup() {
add_child(value_input_popup);
value_input = memnew(LineEdit);
value_input->set_emoji_menu_enabled(false);
value_input->set_focus_mode(FOCUS_CLICK);
value_input_popup->add_child(value_input);
value_input->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
value_input_popup->connect(SceneStringName(hidden), callable_mp(this, &EditorSpinSlider::_value_input_closed));
value_input->connect("text_submitted", callable_mp(this, &EditorSpinSlider::_value_input_submitted));
value_input->connect(SceneStringName(text_submitted), callable_mp(this, &EditorSpinSlider::_value_input_submitted));
value_input->connect(SceneStringName(focus_exited), callable_mp(this, &EditorSpinSlider::_value_focus_exited));
value_input->connect(SceneStringName(gui_input), callable_mp(this, &EditorSpinSlider::_value_input_gui_input));
@ -731,7 +751,7 @@ EditorSpinSlider::EditorSpinSlider() {
grabber = memnew(TextureRect);
add_child(grabber);
grabber->hide();
grabber->set_as_top_level(true);
grabber->set_z_index(1);
grabber->set_mouse_filter(MOUSE_FILTER_STOP);
grabber->connect(SceneStringName(mouse_entered), callable_mp(this, &EditorSpinSlider::_grabber_mouse_entered));
grabber->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSpinSlider::_grabber_mouse_exited));

View file

@ -71,6 +71,7 @@ class EditorSpinSlider : public Range {
bool hide_slider = false;
bool flat = false;
bool editing_integer = false;
void _grab_start();
void _grab_end();
@ -87,6 +88,11 @@ class EditorSpinSlider : public Range {
void _ensure_input_popup();
void _draw_spin_slider();
struct ThemeCache {
Ref<Texture2D> updown_icon;
Ref<Texture2D> updown_disabled_icon;
} theme_cache;
protected:
void _notification(int p_what);
virtual void gui_input(const Ref<InputEvent> &p_event) override;
@ -96,8 +102,6 @@ protected:
void _focus_entered();
public:
virtual bool is_text_field() const override;
String get_tooltip(const Point2 &p_pos) const override;
String get_text_value() const;
@ -110,6 +114,9 @@ public:
void set_hide_slider(bool p_hide);
bool is_hiding_slider() const;
void set_editing_integer(bool p_editing_integer);
bool is_editing_integer() const;
void set_read_only(bool p_enable);
bool is_read_only() const;

View file

@ -54,8 +54,12 @@ void EditorTitleBar::gui_input(const Ref<InputEvent> &p_event) {
if (w) {
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
click_pos = DisplayServer::get_singleton()->mouse_get_position() - w->get_position();
moving = true;
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_DRAG)) {
DisplayServer::get_singleton()->window_start_drag(w->get_window_id());
} else {
click_pos = DisplayServer::get_singleton()->mouse_get_position() - w->get_position();
moving = true;
}
} else {
moving = false;
}

View file

@ -43,7 +43,7 @@ class EditorTitleBar : public HBoxContainer {
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods(){};
static void _bind_methods() {}
public:
void set_can_move_window(bool p_enabled);

View file

@ -108,14 +108,13 @@ void EditorToaster::_notification(int p_what) {
}
} break;
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
if (vbox_container->is_visible()) {
main_button->set_icon(get_editor_theme_icon(SNAME("Notification")));
main_button->set_button_icon(get_editor_theme_icon(SNAME("Notification")));
} else {
main_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
main_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
}
disable_notifications_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
disable_notifications_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
// Styleboxes background.
info_panel_style_background->set_bg_color(get_theme_color(SNAME("base_color"), EditorStringName(Editor)));
@ -134,9 +133,6 @@ void EditorToaster::_notification(int p_what) {
error_panel_style_progress->set_bg_color(get_theme_color(SNAME("base_color"), EditorStringName(Editor)).lightened(0.03));
error_panel_style_progress->set_border_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
main_button->queue_redraw();
disable_notifications_button->queue_redraw();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@ -243,6 +239,7 @@ void EditorToaster::_auto_hide_or_free_toasts() {
main_button->set_tooltip_text(TTR("No notifications."));
main_button->set_modulate(Color(0.5, 0.5, 0.5));
main_button->set_disabled(true);
set_process_internal(false);
} else {
main_button->set_tooltip_text(TTR("Show notifications."));
main_button->set_modulate(Color(1, 1, 1));
@ -311,9 +308,9 @@ void EditorToaster::_draw_progress(Control *panel) {
void EditorToaster::_set_notifications_enabled(bool p_enabled) {
vbox_container->set_visible(p_enabled);
if (p_enabled) {
main_button->set_icon(get_editor_theme_icon(SNAME("Notification")));
main_button->set_button_icon(get_editor_theme_icon(SNAME("Notification")));
} else {
main_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
main_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled")));
}
_update_disable_notifications_button();
}
@ -361,6 +358,9 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_
}
panel->set_modulate(Color(1, 1, 1, 0));
panel->connect(SceneStringName(draw), callable_mp(this, &EditorToaster::_draw_progress).bind(panel));
panel->connect(SceneStringName(theme_changed), callable_mp(this, &EditorToaster::_toast_theme_changed).bind(panel));
Toast &toast = toasts[panel];
// Horizontal container.
HBoxContainer *hbox_container = memnew(HBoxContainer);
@ -371,24 +371,30 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_
p_control->set_h_size_flags(SIZE_EXPAND_FILL);
hbox_container->add_child(p_control);
// Close button.
// Add buttons.
if (p_time > 0.0) {
Button *copy_button = memnew(Button);
copy_button->set_flat(true);
copy_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::copy).bind(panel));
hbox_container->add_child(copy_button);
Button *close_button = memnew(Button);
close_button->set_flat(true);
close_button->set_icon(get_editor_theme_icon(SNAME("Close")));
close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::close).bind(panel));
close_button->connect(SceneStringName(theme_changed), callable_mp(this, &EditorToaster::_close_button_theme_changed).bind(close_button));
close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::instant_close).bind(panel));
hbox_container->add_child(close_button);
toast.copy_button = copy_button;
toast.close_button = close_button;
}
toasts[panel].severity = p_severity;
toast.severity = p_severity;
if (p_time > 0.0) {
toasts[panel].duration = p_time;
toasts[panel].remaining_time = p_time;
toast.duration = p_time;
toast.remaining_time = p_time;
} else {
toasts[panel].duration = -1.0;
toast.duration = -1.0;
}
toasts[panel].popped = true;
toast.popped = true;
vbox_container->add_child(panel);
_auto_hide_or_free_toasts();
_update_vbox_position();
@ -406,7 +412,7 @@ void EditorToaster::popup_str(const String &p_message, Severity p_severity, cons
// Since "_popup_str" adds nodes to the tree, and since the "add_child" method is not
// thread-safe, it's better to defer the call to the next cycle to be thread-safe.
is_processing_error = true;
MessageQueue::get_main_singleton()->push_callable(callable_mp(this, &EditorToaster::_popup_str).bind(p_message, p_severity, p_tooltip));
MessageQueue::get_main_singleton()->push_callable(callable_mp(this, &EditorToaster::_popup_str), p_message, p_severity, p_tooltip);
is_processing_error = false;
}
@ -433,19 +439,22 @@ void EditorToaster::_popup_str(const String &p_message, Severity p_severity, con
hb->add_child(count_label);
control = popup(hb, p_severity, default_message_duration, p_tooltip);
toasts[control].message = p_message;
toasts[control].tooltip = p_tooltip;
toasts[control].count = 1;
toasts[control].message_label = label;
toasts[control].message_count_label = count_label;
Toast &toast = toasts[control];
toast.message = p_message;
toast.tooltip = p_tooltip;
toast.count = 1;
toast.message_label = label;
toast.message_count_label = count_label;
} else {
if (toasts[control].popped) {
toasts[control].count += 1;
Toast &toast = toasts[control];
if (toast.popped) {
toast.count += 1;
} else {
toasts[control].count = 1;
toast.count = 1;
}
toasts[control].remaining_time = toasts[control].duration;
toasts[control].popped = true;
toast.remaining_time = toast.duration;
toast.popped = true;
control->show();
vbox_container->move_child(control, vbox_container->get_child_count());
_auto_hide_or_free_toasts();
@ -480,6 +489,19 @@ void EditorToaster::_popup_str(const String &p_message, Severity p_severity, con
vbox_container->reset_size();
is_processing_error = false;
set_process_internal(true);
}
void EditorToaster::_toast_theme_changed(Control *p_control) {
ERR_FAIL_COND(!toasts.has(p_control));
Toast &toast = toasts[p_control];
if (toast.close_button) {
toast.close_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));
}
if (toast.copy_button) {
toast.copy_button->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));
}
}
void EditorToaster::close(Control *p_control) {
@ -488,11 +510,22 @@ void EditorToaster::close(Control *p_control) {
toasts[p_control].popped = false;
}
void EditorToaster::_close_button_theme_changed(Control *p_close_button) {
Button *close_button = Object::cast_to<Button>(p_close_button);
if (close_button) {
close_button->set_icon(get_editor_theme_icon(SNAME("Close")));
}
void EditorToaster::instant_close(Control *p_control) {
close(p_control);
p_control->set_modulate(Color(1, 1, 1, 0));
}
void EditorToaster::copy(Control *p_control) {
ERR_FAIL_COND(!toasts.has(p_control));
DisplayServer::get_singleton()->clipboard_set(toasts[p_control].message);
}
void EditorToaster::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_toast", "message", "severity", "tooltip"), &EditorToaster::_popup_str, DEFVAL(EditorToaster::SEVERITY_INFO), DEFVAL(String()));
BIND_ENUM_CONSTANT(SEVERITY_INFO);
BIND_ENUM_CONSTANT(SEVERITY_WARNING);
BIND_ENUM_CONSTANT(SEVERITY_ERROR);
}
EditorToaster *EditorToaster::get_singleton() {
@ -501,7 +534,6 @@ EditorToaster *EditorToaster::get_singleton() {
EditorToaster::EditorToaster() {
set_notify_transform(true);
set_process_internal(true);
// VBox.
vbox_container = memnew(VBoxContainer);
@ -566,7 +598,7 @@ EditorToaster::EditorToaster() {
eh.errfunc = _error_handler;
add_error_handler(&eh);
};
}
EditorToaster::~EditorToaster() {
singleton = nullptr;

View file

@ -31,8 +31,6 @@
#ifndef EDITOR_TOASTER_H
#define EDITOR_TOASTER_H
#include "core/string/ustring.h"
#include "core/templates/local_vector.h"
#include "scene/gui/box_container.h"
class Button;
@ -76,6 +74,10 @@ private:
real_t remaining_time = 0.0;
bool popped = false;
// Buttons
Button *copy_button = nullptr;
Button *close_button = nullptr;
// Messages
String message;
String tooltip;
@ -101,9 +103,10 @@ private:
void _set_notifications_enabled(bool p_enabled);
void _repop_old();
void _popup_str(const String &p_message, Severity p_severity, const String &p_tooltip);
void _close_button_theme_changed(Control *p_close_button);
void _toast_theme_changed(Control *p_control);
protected:
static void _bind_methods();
static EditorToaster *singleton;
void _notification(int p_what);
@ -114,6 +117,8 @@ public:
Control *popup(Control *p_control, Severity p_severity = SEVERITY_INFO, double p_time = 0.0, const String &p_tooltip = String());
void popup_str(const String &p_message, Severity p_severity = SEVERITY_INFO, const String &p_tooltip = String());
void close(Control *p_control);
void instant_close(Control *p_control);
void copy(Control *p_control);
EditorToaster();
~EditorToaster();

View file

@ -0,0 +1,85 @@
/**************************************************************************/
/* editor_version_button.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_version_button.h"
#include "core/os/time.h"
#include "core/version.h"
String _get_version_string(EditorVersionButton::VersionFormat p_format) {
String main;
switch (p_format) {
case EditorVersionButton::FORMAT_BASIC: {
return VERSION_FULL_CONFIG;
} break;
case EditorVersionButton::FORMAT_WITH_BUILD: {
main = "v" VERSION_FULL_BUILD;
} break;
case EditorVersionButton::FORMAT_WITH_NAME_AND_BUILD: {
main = VERSION_FULL_NAME;
} break;
default: {
ERR_FAIL_V_MSG(VERSION_FULL_NAME, "Unexpected format: " + itos(p_format));
} break;
}
String hash = VERSION_HASH;
if (!hash.is_empty()) {
hash = vformat(" [%s]", hash.left(9));
}
return main + hash;
}
void EditorVersionButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
// This can't be done in the constructor because theme cache is not ready yet.
set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
set_text(_get_version_string(format));
} break;
}
}
void EditorVersionButton::pressed() {
DisplayServer::get_singleton()->clipboard_set(_get_version_string(FORMAT_WITH_BUILD));
}
EditorVersionButton::EditorVersionButton(VersionFormat p_format) {
format = p_format;
set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
String build_date;
if (VERSION_TIMESTAMP > 0) {
build_date = Time::get_singleton()->get_datetime_string_from_unix_time(VERSION_TIMESTAMP, true) + " UTC";
} else {
build_date = TTR("(unknown)");
}
set_tooltip_text(vformat(TTR("Git commit date: %s\nClick to copy the version information."), build_date));
}

View file

@ -0,0 +1,61 @@
/**************************************************************************/
/* editor_version_button.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_VERSION_BUTTON_H
#define EDITOR_VERSION_BUTTON_H
#include "scene/gui/link_button.h"
class EditorVersionButton : public LinkButton {
GDCLASS(EditorVersionButton, LinkButton);
public:
enum VersionFormat {
// 4.3.2.stable
FORMAT_BASIC,
// v4.3.2.stable.mono [HASH]
FORMAT_WITH_BUILD,
// Godot Engine v4.3.2.stable.mono.official [HASH]
FORMAT_WITH_NAME_AND_BUILD,
};
private:
VersionFormat format = FORMAT_WITH_NAME_AND_BUILD;
protected:
void _notification(int p_what);
virtual void pressed() override;
public:
EditorVersionButton(VersionFormat p_format);
};
#endif // EDITOR_VERSION_BUTTON_H

View file

@ -141,22 +141,15 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte
}
}
} else {
// Base increment factor defined as the twelveth root of two.
// This allows for a smooth geometric evolution of the zoom, with the advantage of
// visiting all integer power of two scale factors.
// Note: this is analogous to the 'semitone' interval in the music world
// In order to avoid numerical imprecisions, we compute and edit a zoom index
// with the following relation: zoom = 2 ^ (index / 12)
if (zoom < CMP_EPSILON || p_increment_count == 0) {
return;
}
// zoom = 2**(index/12) => log2(zoom) = index/12
float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
float new_zoom_index = closest_zoom_index + p_increment_count;
float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
// Zoom is calculated as pow(zoom_factor, zoom_step).
// This ensures the zoom will always equal 100% when zoom_step is 0.
float zoom_factor = EDITOR_GET("editors/2d/zoom_speed_factor");
float current_zoom_step = Math::round(Math::log(zoom_noscale) / Math::log(zoom_factor));
float new_zoom = Math::pow(zoom_factor, current_zoom_step + p_increment_count);
// Restore Editor scale transformation.
new_zoom *= MAX(1, EDSCALE);
@ -169,8 +162,8 @@ void EditorZoomWidget::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
zoom_minus->set_icon(get_editor_theme_icon(SNAME("ZoomLess")));
zoom_plus->set_icon(get_editor_theme_icon(SNAME("ZoomMore")));
zoom_minus->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess")));
zoom_plus->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore")));
} break;
}
}
@ -195,7 +188,7 @@ EditorZoomWidget::EditorZoomWidget() {
// Zoom buttons
zoom_minus = memnew(Button);
zoom_minus->set_flat(true);
zoom_minus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_minus", TTR("Zoom Out"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::MINUS), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_SUBTRACT) }));
zoom_minus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_minus", TTRC("Zoom Out"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::MINUS), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_SUBTRACT) }));
zoom_minus->set_shortcut_context(this);
zoom_minus->set_focus_mode(FOCUS_NONE);
add_child(zoom_minus);
@ -206,7 +199,7 @@ EditorZoomWidget::EditorZoomWidget() {
Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty);
zoom_reset->add_theme_style_override(CoreStringName(normal), empty_stylebox);
zoom_reset->add_theme_style_override("hover", empty_stylebox);
zoom_reset->add_theme_style_override(SceneStringName(hover), empty_stylebox);
zoom_reset->add_theme_style_override("focus", empty_stylebox);
zoom_reset->add_theme_style_override(SceneStringName(pressed), empty_stylebox);
zoom_reset->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE));
@ -224,7 +217,7 @@ EditorZoomWidget::EditorZoomWidget() {
zoom_plus = memnew(Button);
zoom_plus->set_flat(true);
zoom_plus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_plus", TTR("Zoom In"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::EQUAL), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_ADD) }));
zoom_plus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_plus", TTRC("Zoom In"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::EQUAL), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_ADD) }));
zoom_plus->set_shortcut_context(this);
zoom_plus->set_focus_mode(FOCUS_NONE);
add_child(zoom_plus);

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,7 @@
#ifndef SCENE_TREE_EDITOR_H
#define SCENE_TREE_EDITOR_H
#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
@ -57,6 +58,58 @@ class SceneTreeEditor : public Control {
BUTTON_UNIQUE = 9,
};
struct CachedNode {
Node *node = nullptr;
TreeItem *item = nullptr;
int index = -1;
bool dirty = true;
bool has_moved_children = false;
bool removed = false;
// Store the iterator for faster removal. This is safe as
// HashMap never moves elements.
HashMap<Node *, CachedNode>::Iterator cache_iterator;
// This is safe because it gets compared to a uint8_t.
uint16_t delete_serial = UINT16_MAX;
// To know whether to update children or not.
bool can_process = false;
CachedNode() = delete; // Always an error.
CachedNode(Node *p_node, TreeItem *p_item) :
node(p_node), item(p_item) {}
};
struct NodeCache {
~NodeCache() {
clear();
}
NodeCache(SceneTreeEditor *p_editor) :
editor(p_editor) {}
HashMap<Node *, CachedNode>::Iterator add(Node *p_node, TreeItem *p_item);
HashMap<Node *, CachedNode>::Iterator get(Node *p_node, bool p_deleted_ok = true);
bool has(Node *p_node);
void remove(Node *p_node, bool p_recursive = false);
void mark_dirty(Node *p_node, bool p_parents = true);
void mark_children_dirty(Node *p_node, bool p_recursive = false);
void delete_pending();
void clear();
SceneTreeEditor *editor;
HashMap<Node *, CachedNode> cache;
HashSet<CachedNode *> to_delete;
Node *current_scene_node = nullptr;
Node *current_pinned_node = nullptr;
bool current_has_pin = false;
bool force_update = false;
uint8_t delete_serial = 0;
};
NodeCache node_cache;
Tree *tree = nullptr;
Node *selected = nullptr;
ObjectID instance_node;
@ -68,20 +121,40 @@ class SceneTreeEditor : public Control {
AcceptDialog *error = nullptr;
AcceptDialog *warning = nullptr;
ConfirmationDialog *revoke_dialog = nullptr;
Label *revoke_dialog_label = nullptr;
CheckBox *ask_before_revoke_checkbox = nullptr;
Node *revoke_node = nullptr;
bool auto_expand_selected = true;
bool hide_filtered_out_parents = false;
bool connect_to_script_mode = false;
bool connecting_signal = false;
bool update_when_invisible = true;
int blocked;
void _compute_hash(Node *p_node, uint64_t &hash);
void _reset();
PackedStringArray _get_node_configuration_warnings(Node *p_node);
void _update_node_path(Node *p_node, bool p_recursive = true);
void _update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force = false);
void _update_node(Node *p_node, TreeItem *p_item, bool p_part_of_subscene);
void _update_if_clean();
void _add_nodes(Node *p_node, TreeItem *p_parent);
void _test_update_tree();
bool _update_filter(TreeItem *p_parent = nullptr, bool p_scroll_to_selected = false);
bool _item_matches_all_terms(TreeItem *p_item, const PackedStringArray &p_terms);
void _tree_changed();
void _tree_process_mode_changed();
void _move_node_children(HashMap<Node *, CachedNode>::Iterator &p_I);
void _move_node_item(TreeItem *p_parent, HashMap<Node *, CachedNode>::Iterator &p_I);
void _node_child_order_changed(Node *p_node);
void _node_editor_state_changed(Node *p_node);
void _node_added(Node *p_node);
void _node_removed(Node *p_node);
void _node_renamed(Node *p_node);
@ -136,6 +209,7 @@ class SceneTreeEditor : public Control {
void _rmb_select(const Vector2 &p_pos, MouseButton p_button = MouseButton::RIGHT);
void _warning_changed(Node *p_for_node);
void _update_marking_list(const HashSet<Node *> &p_marked);
Timer *update_timer = nullptr;
@ -144,6 +218,9 @@ class SceneTreeEditor : public Control {
Vector<StringName> valid_types;
void _update_ask_before_revoking_unique_name();
void _revoke_unique_name();
public:
// Public for use with callable_mp.
void _update_tree(bool p_scroll_to_selected = false);
@ -168,11 +245,13 @@ public:
void set_show_enabled_subscene(bool p_show) { show_enabled_subscene = p_show; }
void set_valid_types(const Vector<StringName> &p_valid);
void update_tree() { _update_tree(); }
inline void update_tree() { _update_tree(); }
void set_auto_expand_selected(bool p_auto, bool p_update_settings);
void set_hide_filtered_out_parents(bool p_hide, bool p_update_settings);
void set_connect_to_script_mode(bool p_enable);
void set_connecting_signal(bool p_enable);
void set_update_when_invisible(bool p_enable);
Tree *get_scene_tree() { return tree; }
@ -190,14 +269,17 @@ class SceneTreeDialog : public ConfirmationDialog {
LineEdit *filter = nullptr;
CheckButton *show_all_nodes = nullptr;
LocalVector<TextureRect *> valid_type_icons;
HBoxContainer *allowed_types_hbox = nullptr;
void _select();
void _cancel();
void _selected_changed();
void _filter_changed(const String &p_filter);
void _on_filter_gui_input(const Ref<InputEvent> &p_event);
void _show_all_nodes_changed(bool p_button_pressed);
protected:
void _update_valid_type_icons();
void _notification(int p_what);
static void _bind_methods();