diff options
35 files changed, 337 insertions, 69 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 25dd408dce..0d699cdacb 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1104,6 +1104,8 @@ ProjectSettings::ProjectSettings() {  	}  	extensions.push_back("shader"); +	GLOBAL_DEF("editor/run/main_run_args", ""); +  	GLOBAL_DEF("editor/script/search_in_file_extensions", extensions);  	custom_prop_info["editor/script/search_in_file_extensions"] = PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"); diff --git a/core/object/script_language.h b/core/object/script_language.h index f9898ccd0c..bb46c718b2 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -303,6 +303,7 @@ public:  	void get_core_type_words(List<String> *p_core_type_words) const;  	virtual void get_reserved_words(List<String> *p_words) const = 0; +	virtual bool is_control_flow_keyword(String p_string) const = 0;  	virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;  	virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;  	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml index 5aa2d699a8..0b31352611 100644 --- a/doc/classes/ButtonGroup.xml +++ b/doc/classes/ButtonGroup.xml @@ -28,6 +28,15 @@  	<members>  		<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" override="true" default="true" />  	</members> +	<signals> +		<signal name="pressed"> +			<argument index="0" name="button" type="Object"> +			</argument> +			<description> +				Emitted when one of the buttons of the group is pressed. +			</description> +		</signal> +	</signals>  	<constants>  	</constants>  </class> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 8c90108aef..d997073849 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -143,7 +143,7 @@  				Returns the command-line arguments passed to the engine.  				Command-line arguments can be written in any form, including both [code]--key value[/code] and [code]--key=value[/code] forms so they can be properly parsed, as long as custom command-line arguments do not conflict with engine arguments.  				You can also incorporate environment variables using the [method get_environment] method. -				You can set [code]editor/main_run_args[/code] in the Project Settings to define command-line arguments to be passed by the editor when running the project. +				You can set [member ProjectSettings.editor/run/main_run_args] to define command-line arguments to be passed by the editor when running the project.  				Here's a minimal example on how to parse command-line arguments into a dictionary using the [code]--key=value[/code] form for arguments:  				[codeblocks]  				[gdscript] diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 005873c2ff..3200f789b8 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -542,6 +542,14 @@  		<member name="editor/node_naming/name_num_separator" type="int" setter="" getter="" default="0">  			What to use to separate node name from number. This is mostly an editor setting.  		</member> +		<member name="editor/run/main_run_args" type="String" setter="" getter="" default=""""> +			The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself. +			It is possible to make another executable run Godot by using the [code]%command%[/code] placeholder. The placeholder will be replaced with Godot's own command line. Program-specific arguments should be placed [i]before[/i] the placeholder, whereas Godot-specific arguments should be placed [i]after[/i] the placeholder. +			For example, this can be used to force the project to run on the dedicated GPU in a NVIDIA Optimus system on Linux: +			[codeblock] +			prime-run %command% +			[/codeblock] +		</member>  		<member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray( "gd", "shader" )">  			Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.  		</member> diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 9949fd8199..c0d5716c4e 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -730,10 +730,6 @@ void ActionMapEditor::_add_action_pressed() {  }  void ActionMapEditor::_add_action(const String &p_name) { -	if (!allow_editing_actions) { -		return; -	} -  	if (p_name == "" || !_is_action_name_valid(p_name)) {  		show_message(TTR("Invalid action name. it cannot be.is_empty()() nor contain '/', ':', '=', '\\' or '\"'"));  		return; @@ -744,10 +740,6 @@ void ActionMapEditor::_add_action(const String &p_name) {  }  void ActionMapEditor::_action_edited() { -	if (!allow_editing_actions) { -		return; -	} -  	TreeItem *ti = action_tree->get_edited();  	if (!ti) {  		return; @@ -812,10 +804,6 @@ void ActionMapEditor::_tree_button_pressed(Object *p_item, int p_column, int p_i  		} break;  		case ActionMapEditor::BUTTON_REMOVE_ACTION: { -			if (!allow_editing_actions) { -				break; -			} -  			// Send removed action name  			String name = item->get_meta("__name");  			emit_signal("action_removed", name); @@ -848,11 +836,11 @@ void ActionMapEditor::_tree_item_activated() {  	_tree_button_pressed(item, 2, BUTTON_EDIT_EVENT);  } -void ActionMapEditor::set_show_uneditable(bool p_show) { -	show_uneditable = p_show; -	show_uneditable_actions_checkbox->set_pressed(p_show); +void ActionMapEditor::set_show_builtin_actions(bool p_show) { +	show_builtin_actions = p_show; +	show_builtin_actions_checkbutton->set_pressed(p_show); -	// Prevent unnecessary updates of action list when cache is.is_empty()(). +	// Prevent unnecessary updates of action list when cache is empty.  	if (!actions_cache.is_empty()) {  		update_action_list();  	} @@ -1022,7 +1010,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info  			continue;  		} -		if (!action_info.editable && !show_uneditable) { +		if (!action_info.editable && !show_builtin_actions) {  			continue;  		} @@ -1080,15 +1068,6 @@ void ActionMapEditor::show_message(const String &p_message) {  	message->popup_centered(Size2(300, 100) * EDSCALE);  } -void ActionMapEditor::set_allow_editing_actions(bool p_allow) { -	allow_editing_actions = p_allow; -	add_hbox->set_visible(p_allow); -} - -void ActionMapEditor::set_toggle_editable_label(const String &p_label) { -	show_uneditable_actions_checkbox->set_text(p_label); -} -  void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {  	memdelete(action_list_search);  	action_list_search = p_searchbox; @@ -1096,8 +1075,7 @@ void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {  }  ActionMapEditor::ActionMapEditor() { -	allow_editing_actions = true; -	show_uneditable = true; +	show_builtin_actions = false;  	// Main Vbox Container  	VBoxContainer *main_vbox = memnew(VBoxContainer); @@ -1114,11 +1092,11 @@ ActionMapEditor::ActionMapEditor() {  	action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated));  	top_hbox->add_child(action_list_search); -	show_uneditable_actions_checkbox = memnew(CheckBox); -	show_uneditable_actions_checkbox->set_pressed(false); -	show_uneditable_actions_checkbox->set_text(TTR("Show Uneditable Actions")); -	show_uneditable_actions_checkbox->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_uneditable)); -	top_hbox->add_child(show_uneditable_actions_checkbox); +	show_builtin_actions_checkbutton = memnew(CheckButton); +	show_builtin_actions_checkbutton->set_pressed(false); +	show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions")); +	show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions)); +	top_hbox->add_child(show_builtin_actions_checkbutton);  	// Adding Action line edit + button  	add_hbox = memnew(HBoxContainer); @@ -1132,7 +1110,7 @@ ActionMapEditor::ActionMapEditor() {  	add_hbox->add_child(add_edit);  	Button *add_button = memnew(Button); -	add_button->set_text("Add"); +	add_button->set_text(TTR("Add"));  	add_button->connect("pressed", callable_mp(this, &ActionMapEditor::_add_action_pressed));  	add_hbox->add_child(add_button); diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h index f1f7bffef4..fb097ddfdd 100644 --- a/editor/action_map_editor.h +++ b/editor/action_map_editor.h @@ -156,11 +156,10 @@ private:  	// Filtering and Adding actions -	bool show_uneditable; -	CheckBox *show_uneditable_actions_checkbox; +	bool show_builtin_actions; +	CheckButton *show_builtin_actions_checkbutton;  	LineEdit *action_list_search; -	bool allow_editing_actions;  	HBoxContainer *add_hbox;  	LineEdit *add_edit; @@ -190,10 +189,7 @@ public:  	void update_action_list(const Vector<ActionInfo> &p_action_infos = Vector<ActionInfo>());  	void show_message(const String &p_message); -	void set_show_uneditable(bool p_show); -	void set_allow_editing_actions(bool p_allow); - -	void set_toggle_editable_label(const String &p_label); +	void set_show_builtin_actions(bool p_show);  	void use_external_search_box(LineEdit *p_searchbox); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 602035634a..7c485d53c5 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -75,6 +75,8 @@ void EditorLog::_notification(int p_what) {  		collapse_button->set_icon(get_theme_icon("CombineLines", "EditorIcons"));  		show_search_button->set_icon(get_theme_icon("Search", "EditorIcons")); +		_load_state(); +  	} else if (p_what == NOTIFICATION_THEME_CHANGED) {  		Ref<Font> df_output_code = get_theme_font("output_source", "EditorFonts");  		if (df_output_code.is_valid()) { @@ -89,9 +91,56 @@ void EditorLog::_notification(int p_what) {  void EditorLog::_set_collapse(bool p_collapse) {  	collapse = p_collapse; +	_start_state_save_timer();  	_rebuild_log();  } +void EditorLog::_start_state_save_timer() { +	if (!is_loading_state) { +		save_state_timer->start(); +	} +} + +void EditorLog::_save_state() { +	Ref<ConfigFile> config; +	config.instance(); +	// Load and amend existing config if it exists. +	config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); + +	const String section = "editor_log"; +	for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { +		config->set_value(section, "log_filter_" + itos(E->key()), E->get()->is_active()); +	} + +	config->set_value(section, "collapse", collapse); +	config->set_value(section, "show_search", search_box->is_visible()); + +	config->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); +} + +void EditorLog::_load_state() { +	is_loading_state = true; + +	Ref<ConfigFile> config; +	config.instance(); +	Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); + +	if (err == OK) { +		const String section = "editor_log"; +		for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { +			E->get()->set_active(config->get_value(section, "log_filter_" + itos(E->key()), false)); +		} + +		collapse = config->get_value(section, "collapse", false); +		collapse_button->set_pressed(collapse); +		bool show_search = config->get_value(section, "show_search", true); +		search_box->set_visible(show_search); +		show_search_button->set_pressed(show_search); +	} + +	is_loading_state = false; +} +  void EditorLog::_clear_request() {  	log->clear();  	messages.clear(); @@ -175,7 +224,7 @@ void EditorLog::_rebuild_log() {  void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {  	// Only add the message to the log if it passes the filters. -	bool filter_active = type_filter_map[p_message.type]->active; +	bool filter_active = type_filter_map[p_message.type]->is_active();  	String search_text = search_box->get_text();  	bool search_match = search_text == String() || p_message.text.findn(search_text) > -1; @@ -230,7 +279,8 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {  }  void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) { -	type_filter_map[p_message_type]->active = p_active; +	type_filter_map[p_message_type]->set_active(p_active); +	_start_state_save_timer();  	_rebuild_log();  } @@ -239,6 +289,7 @@ void EditorLog::_set_search_visible(bool p_visible) {  	if (p_visible) {  		search_box->grab_focus();  	} +	_start_state_save_timer();  }  void EditorLog::_search_changed(const String &p_text) { @@ -257,6 +308,12 @@ void EditorLog::_bind_methods() {  }  EditorLog::EditorLog() { +	save_state_timer = memnew(Timer); +	save_state_timer->set_wait_time(2); +	save_state_timer->set_one_shot(true); +	save_state_timer->connect("timeout", callable_mp(this, &EditorLog::_save_state)); +	add_child(save_state_timer); +  	HBoxContainer *hb = this;  	VBoxContainer *vb_left = memnew(VBoxContainer); diff --git a/editor/editor_log.h b/editor/editor_log.h index 89d00d0fa0..3b6476634a 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -72,11 +72,11 @@ private:  	private:  		// Force usage of set method since it has functionality built-in.  		int message_count = 0; +		bool active = true;  	public:  		MessageType type;  		Button *toggle_button = nullptr; -		bool active = true;  		void initialize_button(const String &p_tooltip, Callable p_toggled_callback) {  			toggle_button = memnew(Button); @@ -100,6 +100,15 @@ private:  			toggle_button->set_text(itos(message_count));  		} +		bool is_active() { +			return active; +		} + +		void set_active(bool p_active) { +			toggle_button->set_pressed(p_active); +			active = p_active; +		} +  		LogFilter(MessageType p_type) :  				type(p_type) {  		} @@ -124,6 +133,9 @@ private:  	// Warnings or Errors are encounetered.  	Button *tool_button; +	bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try trigger a save, which happens during loading). +	Timer *save_state_timer; +  	static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);  	ErrorHandlerList eh; @@ -147,6 +159,10 @@ private:  	void _set_collapse(bool p_collapse); +	void _start_state_save_timer(); +	void _save_state(); +	void _load_state(); +  protected:  	static void _bind_methods();  	void _notification(int p_what); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ed670b1296..6390755656 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4362,6 +4362,8 @@ void EditorNode::_save_docks() {  	}  	Ref<ConfigFile> config;  	config.instance(); +	// Load and amend existing config if it exists. +	config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));  	_save_docks_to_config(config, "docks");  	_save_open_scenes_to_config(config, "EditorNode"); @@ -5877,8 +5879,6 @@ EditorNode::EditorNode() {  	register_exporters(); -	GLOBAL_DEF("editor/run/main_run_args", ""); -  	ClassDB::set_class_enabled("RootMotionView", true);  	//defs here, use EDITOR_GET in logic diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index e46f4eb65a..5e6d2ab69c 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -183,15 +183,50 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L  		args.push_back(p_scene);  	} +	String exec = OS::get_singleton()->get_executable_path(); +  	if (p_custom_args != "") { -		Vector<String> cargs = p_custom_args.split(" ", false); -		for (int i = 0; i < cargs.size(); i++) { -			args.push_back(cargs[i].replace(" ", "%20")); +		// Allow the user to specify a command to run, similar to Steam's launch options. +		// In this case, Godot will no longer be run directly; it's up to the underlying command +		// to run it. For instance, this can be used on Linux to force a running project +		// to use Optimus using `prime-run` or similar. +		// Example: `prime-run %command% --time-scale 0.5` +		const int placeholder_pos = p_custom_args.find("%command%"); + +		Vector<String> custom_args; + +		if (placeholder_pos != -1) { +			// Prepend executable-specific custom arguments. +			// If nothing is placed before `%command%`, behave as if no placeholder was specified. +			Vector<String> exec_args = p_custom_args.substr(0, placeholder_pos).split(" ", false); +			if (exec_args.size() >= 1) { +				exec = exec_args[0]; +				exec_args.remove(0); + +				// Append the Godot executable name before we append executable arguments +				// (since the order is reversed when using `push_front()`). +				args.push_front(OS::get_singleton()->get_executable_path()); +			} + +			for (int i = exec_args.size() - 1; i >= 0; i--) { +				// Iterate backwards as we're pushing items in the reverse order. +				args.push_front(exec_args[i].replace(" ", "%20")); +			} + +			// Append Godot-specific custom arguments. +			custom_args = p_custom_args.substr(placeholder_pos + String("%command%").size()).split(" ", false); +			for (int i = 0; i < custom_args.size(); i++) { +				args.push_back(custom_args[i].replace(" ", "%20")); +			} +		} else { +			// Append Godot-specific custom arguments. +			custom_args = p_custom_args.split(" ", false); +			for (int i = 0; i < custom_args.size(); i++) { +				args.push_back(custom_args[i].replace(" ", "%20")); +			}  		}  	} -	String exec = OS::get_singleton()->get_executable_path(); -  	printf("Running: %s", exec.utf8().get_data());  	for (List<String>::Element *E = args.front(); E; E = E->next()) {  		printf(" %s", E->get().utf8().get_data()); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 7e450ed5fc..1bb40c50b8 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -787,6 +787,7 @@ void EditorSettings::_load_default_text_editor_theme() {  	_initial_set("text_editor/highlighting/symbol_color", Color(0.73, 0.87, 1.0));  	_initial_set("text_editor/highlighting/keyword_color", Color(1.0, 1.0, 0.7)); +	_initial_set("text_editor/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));  	_initial_set("text_editor/highlighting/base_type_color", Color(0.64, 1.0, 0.83));  	_initial_set("text_editor/highlighting/engine_type_color", Color(0.51, 0.83, 1.0));  	_initial_set("text_editor/highlighting/user_type_color", Color(0.42, 0.67, 0.93)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 44b4a9f688..d5ad638436 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1322,6 +1322,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {  	const Color symbol_color = Color(0.34, 0.57, 1.0).lerp(mono_color, dark_theme ? 0.5 : 0.3);  	const Color keyword_color = Color(1.0, 0.44, 0.52); +	const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.8, 0.4, 0.6);  	const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0.0, 0.76, 0.38);  	const Color type_color = basetype_color.lerp(mono_color, dark_theme ? 0.4 : 0.3);  	const Color usertype_color = basetype_color.lerp(mono_color, dark_theme ? 0.7 : 0.5); @@ -1360,6 +1361,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {  	if (text_editor_color_theme == "Adaptive") {  		setting->set_initial_value("text_editor/highlighting/symbol_color", symbol_color, true);  		setting->set_initial_value("text_editor/highlighting/keyword_color", keyword_color, true); +		setting->set_initial_value("text_editor/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);  		setting->set_initial_value("text_editor/highlighting/base_type_color", basetype_color, true);  		setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true);  		setting->set_initial_value("text_editor/highlighting/user_type_color", usertype_color, true); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index a319a595c7..18cc5d43fb 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -487,10 +487,15 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  	List<String> kwors;  	scr->get_language()->get_reserved_words(&kwors); +	Set<String> control_flow_keywords;  	Set<String> keywords;  	for (List<String>::Element *E = kwors.front(); E; E = E->next()) { -		keywords.insert(E->get()); +		if (scr->get_language()->is_control_flow_keyword(E->get())) { +			control_flow_keywords.insert(E->get()); +		} else { +			keywords.insert(E->get()); +		}  	}  	int line = 0; @@ -502,6 +507,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  	Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color");  	Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color"); +	Color control_flow_keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/control_flow_keyword_color");  	Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");  	Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");  	Color comment_color = EditorSettings::get_singleton()->get("text_editor/highlighting/comment_color"); @@ -523,6 +529,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  	col = x0;  	bool prev_is_text = false; +	bool in_control_flow_keyword = false;  	bool in_keyword = false;  	bool in_comment = false;  	for (int i = 0; i < code.length(); i++) { @@ -541,6 +548,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  					if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) {  						//make symbol a little visible  						color = symbol_color; +						in_control_flow_keyword = false;  						in_keyword = false;  					} else if (!prev_is_text && _is_text_char(c)) {  						int pos = i; @@ -549,7 +557,9 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  							pos++;  						}  						String word = code.substr(i, pos - i); -						if (keywords.has(word)) { +						if (control_flow_keywords.has(word)) { +							in_control_flow_keyword = true; +						} else if (keywords.has(word)) {  							in_keyword = true;  						} @@ -557,11 +567,12 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  						in_keyword = false;  					} -					if (in_keyword) { +					if (in_control_flow_keyword) { +						color = control_flow_keyword_color; +					} else if (in_keyword) {  						color = keyword_color;  					}  				} -  				Color ul = color;  				ul.a *= 0.5;  				img->set_pixel(col, y0 + line * 2, bg_color.blend(ul)); @@ -572,6 +583,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size  			col++;  		} else {  			prev_is_text = false; +			in_control_flow_keyword = false;  			in_keyword = false;  			if (c == '\n') { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index bc33d03d8b..adfeead33b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -140,10 +140,15 @@ void EditorStandardSyntaxHighlighter::_update_cache() {  		/* Reserved words. */  		const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); +		const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");  		List<String> keywords;  		script->get_language()->get_reserved_words(&keywords);  		for (List<String>::Element *E = keywords.front(); E; E = E->next()) { -			highlighter->add_keyword_color(E->get(), keyword_color); +			if (script->get_language()->is_control_flow_keyword(E->get())) { +				highlighter->add_keyword_color(E->get(), control_flow_keyword_color); +			} else { +				highlighter->add_keyword_color(E->get(), keyword_color); +			}  		}  		/* Member types. */ diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index ed3b746678..3cdba9cf16 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -141,9 +141,14 @@ void ShaderTextEditor::_load_theme_settings() {  	List<String> keywords;  	ShaderLanguage::get_keyword_list(&keywords);  	const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); +	const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");  	for (List<String>::Element *E = keywords.front(); E; E = E->next()) { -		syntax_highlighter->add_keyword_color(E->get(), keyword_color); +		if (ShaderLanguage::is_control_flow_keyword(E->get())) { +			syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color); +		} else { +			syntax_highlighter->add_keyword_color(E->get(), keyword_color); +		}  	}  	// Colorize built-ins like `COLOR` differently to make them easier diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 7d5e72ff8e..1d4c23fee6 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -736,6 +736,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {  		Color background_color = EDITOR_GET("text_editor/highlighting/background_color");  		Color text_color = EDITOR_GET("text_editor/highlighting/text_color");  		Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); +		Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");  		Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");  		Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");  		Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); @@ -746,7 +747,11 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {  		expression_box->add_theme_color_override("background_color", background_color);  		for (List<String>::Element *E = VisualShaderEditor::get_singleton()->keyword_list.front(); E; E = E->next()) { -			expression_syntax_highlighter->add_keyword_color(E->get(), keyword_color); +			if (ShaderLanguage::is_control_flow_keyword(E->get())) { +				expression_syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color); +			} else { +				expression_syntax_highlighter->add_keyword_color(E->get(), keyword_color); +			}  		}  		expression_box->add_theme_font_override("font", VisualShaderEditor::get_singleton()->get_theme_font("expression", "EditorFonts")); @@ -2792,6 +2797,7 @@ void VisualShaderEditor::_notification(int p_what) {  			Color background_color = EDITOR_GET("text_editor/highlighting/background_color");  			Color text_color = EDITOR_GET("text_editor/highlighting/text_color");  			Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); +			Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");  			Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");  			Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");  			Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); @@ -2801,7 +2807,11 @@ void VisualShaderEditor::_notification(int p_what) {  			preview_text->add_theme_color_override("background_color", background_color);  			for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) { -				syntax_highlighter->add_keyword_color(E->get(), keyword_color); +				if (ShaderLanguage::is_control_flow_keyword(E->get())) { +					syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color); +				} else { +					syntax_highlighter->add_keyword_color(E->get(), keyword_color); +				}  			}  			preview_text->add_theme_font_override("font", get_theme_font("expression", "EditorFonts")); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index faec3355ac..76290b4b62 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -649,8 +649,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {  	action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));  	action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));  	action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered)); -	action_map->set_toggle_editable_label(TTR("Show Built-in Actions")); -	action_map->set_show_uneditable(false);  	tab_container->add_child(action_map);  	localization_editor = memnew(LocalizationEditor); diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 3283f28de5..46af70f73c 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -1289,6 +1289,10 @@ void NativeScriptLanguage::finish() {  void NativeScriptLanguage::get_reserved_words(List<String> *p_words) const {  } +bool NativeScriptLanguage::is_control_flow_keyword(String p_keyword) const { +	return false; +} +  void NativeScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {  } diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 4bd54f9c46..ca5e76e43e 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -336,6 +336,7 @@ public:  	virtual Error execute_file(const String &p_path);  	virtual void finish();  	virtual void get_reserved_words(List<String> *p_words) const; +	virtual bool is_control_flow_keyword(String p_keyword) const;  	virtual void get_comment_delimiters(List<String> *p_delimiters) const;  	virtual void get_string_delimiters(List<String> *p_delimiters) const;  	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index 3ed1dcaca9..1360cf0299 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -77,6 +77,10 @@ void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const {  	}  } +bool PluginScriptLanguage::is_control_flow_keyword(String p_keyword) const { +	return false; +} +  void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {  	if (_desc.comment_delimiters) {  		const char **w = _desc.comment_delimiters; diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index 226b039265..957bf355ca 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -71,6 +71,7 @@ public:  	/* EDITOR FUNCTIONS */  	virtual void get_reserved_words(List<String> *p_words) const; +	virtual bool is_control_flow_keyword(String p_keyword) const;  	virtual void get_comment_delimiters(List<String> *p_delimiters) const;  	virtual void get_string_delimiters(List<String> *p_delimiters) const;  	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index ccc942d86b..ca646dff15 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -485,10 +485,15 @@ void GDScriptSyntaxHighlighter::_update_cache() {  	/* Reserved words. */  	const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); +	const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");  	List<String> keyword_list;  	gdscript->get_reserved_words(&keyword_list);  	for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) { -		keywords[E->get()] = keyword_color; +		if (gdscript->is_control_flow_keyword(E->get())) { +			keywords[E->get()] = control_flow_keyword_color; +		} else { +			keywords[E->get()] = keyword_color; +		}  	}  	/* Comments */ diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 5f590383d0..859c1acde9 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2135,6 +2135,19 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {  	}  } +bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const { +	return p_keyword == "break" || +		   p_keyword == "continue" || +		   p_keyword == "elif" || +		   p_keyword == "else" || +		   p_keyword == "if" || +		   p_keyword == "for" || +		   p_keyword == "match" || +		   p_keyword == "pass" || +		   p_keyword == "return" || +		   p_keyword == "while"; +} +  bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {  	return p_type == "GDScript";  } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 98da5ad4cb..6df66e876d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -461,6 +461,7 @@ public:  	/* EDITOR FUNCTIONS */  	virtual void get_reserved_words(List<String> *p_words) const; +	virtual bool is_control_flow_keyword(String p_keywords) const;  	virtual void get_comment_delimiters(List<String> *p_delimiters) const;  	virtual void get_string_delimiters(List<String> *p_delimiters) const;  	virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 09f3ea1f50..ffb04bfd37 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -304,6 +304,26 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {  	}  } +bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const { +	return p_keyword == "break" || +		   p_keyword == "case" || +		   p_keyword == "catch" || +		   p_keyword == "continue" || +		   p_keyword == "default" || +		   p_keyword == "do" || +		   p_keyword == "else" || +		   p_keyword == "finally" || +		   p_keyword == "for" || +		   p_keyword == "foreach" || +		   p_keyword == "goto" || +		   p_keyword == "if" || +		   p_keyword == "return" || +		   p_keyword == "switch" || +		   p_keyword == "throw" || +		   p_keyword == "try" || +		   p_keyword == "while"; +} +  void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {  	p_delimiters->push_back("//"); // single-line comment  	p_delimiters->push_back("/* */"); // delimited comment diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index dd93a86d7a..992c7e93c8 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -470,6 +470,7 @@ public:  	/* EDITOR FUNCTIONS */  	void get_reserved_words(List<String> *p_words) const override; +	bool is_control_flow_keyword(String p_keyword) const;  	void get_comment_delimiters(List<String> *p_delimiters) const override;  	void get_string_delimiters(List<String> *p_delimiters) const override;  	Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override; diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 765a5fe023..b863f622bd 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2336,6 +2336,10 @@ void VisualScriptLanguage::finish() {  void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {  } +bool VisualScriptLanguage::is_control_flow_keyword(String p_keyword) const { +	return false; +} +  void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {  } diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 72362e0ef4..cc78b3242d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -586,6 +586,7 @@ public:  	/* EDITOR FUNCTIONS */  	virtual void get_reserved_words(List<String> *p_words) const; +	virtual bool is_control_flow_keyword(String p_keyword) const;  	virtual void get_comment_delimiters(List<String> *p_delimiters) const;  	virtual void get_string_delimiters(List<String> *p_delimiters) const;  	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 826fd0189b..ac067aa001 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -155,6 +155,9 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {  				}  				status.pressed = !status.pressed;  				_unpress_group(); +				if (button_group.is_valid()) { +					button_group->emit_signal("pressed", this); +				}  				_toggled(status.pressed);  				_pressed();  			} @@ -218,6 +221,9 @@ void BaseButton::set_pressed(bool p_pressed) {  	if (p_pressed) {  		_unpress_group(); +		if (button_group.is_valid()) { +			button_group->emit_signal("pressed", this); +		}  	}  	_toggled(status.pressed); @@ -487,6 +493,7 @@ BaseButton *ButtonGroup::get_pressed_button() {  void ButtonGroup::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);  	ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons); +	ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));  }  ButtonGroup::ButtonGroup() { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index b78f9cad24..114abbd4da 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -189,6 +189,18 @@ void ColorPicker::set_pick_color(const Color &p_color) {  	_set_pick_color(p_color, true); //because setters can't have more arguments  } +void ColorPicker::set_old_color(const Color &p_color) { +	old_color = p_color; +} + +void ColorPicker::set_display_old_color(bool p_enabled) { +	display_old_color = p_enabled; +} + +bool ColorPicker::is_displaying_old_color() const { +	return display_old_color; +} +  void ColorPicker::set_edit_alpha(bool p_show) {  	edit_alpha = p_show;  	_update_controls(); @@ -459,17 +471,39 @@ void ColorPicker::_update_text_value() {  }  void ColorPicker::_sample_draw() { -	const Rect2 r = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95)); +	// Covers the right half of the sample if the old color is being displayed, +	// or the whole sample if it's not being displayed. +	Rect2 rect_new; + +	if (display_old_color) { +		rect_new = Rect2(Point2(sample->get_size().width * 0.5, 0), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); + +		// Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton). +		const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); + +		if (display_old_color && old_color.a < 1.0) { +			sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_old, true); +		} + +		sample->draw_rect(rect_old, old_color); + +		if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) { +			// Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview. +			sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2()); +		} +	} else { +		rect_new = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95)); +	}  	if (color.a < 1.0) { -		sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), r, true); +		sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_new, true);  	} -	sample->draw_rect(r, color); +	sample->draw_rect(rect_new, color);  	if (color.r > 1 || color.g > 1 || color.b > 1) { -		// Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview -		sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2()); +		// Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview. +		sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2(uv_edit->get_size().width * 0.5, 0));  	}  } @@ -1174,6 +1208,11 @@ ColorPicker::ColorPicker() :  ///////////////// +void ColorPickerButton::_about_to_popup() { +	set_pressed(true); +	picker->set_old_color(color); +} +  void ColorPickerButton::_color_changed(const Color &p_color) {  	color = p_color;  	update(); @@ -1286,10 +1325,11 @@ void ColorPickerButton::_update_picker() {  		popup->add_child(picker);  		add_child(popup);  		picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed)); -		popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true)); +		popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));  		popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));  		picker->set_pick_color(color);  		picker->set_edit_alpha(edit_alpha); +		picker->set_display_old_color(true);  		emit_signal("picker_created");  	}  } @@ -1301,6 +1341,7 @@ void ColorPickerButton::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);  	ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);  	ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha); +	ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup);  	ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));  	ADD_SIGNAL(MethodInfo("popup_closed")); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index a0d2aa95ca..13fe5fd60e 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -86,6 +86,8 @@ private:  	PickerShapeType picker_type = SHAPE_HSV_WHEEL;  	Color color; +	Color old_color; +	bool display_old_color = false;  	bool raw_mode_enabled = false;  	bool hsv_mode_enabled = false;  	bool deferred_mode_enabled = false; @@ -131,6 +133,10 @@ public:  	void _set_pick_color(const Color &p_color, bool p_update_sliders);  	void set_pick_color(const Color &p_color);  	Color get_pick_color() const; +	void set_old_color(const Color &p_color); + +	void set_display_old_color(bool p_enabled); +	bool is_displaying_old_color() const;  	void set_picker_shape(PickerShapeType p_picker_type);  	PickerShapeType get_picker_shape() const; @@ -171,6 +177,7 @@ class ColorPickerButton : public Button {  	Color color;  	bool edit_alpha = true; +	void _about_to_popup();  	void _color_changed(const Color &p_color);  	void _modal_closed(); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index c30bd7927d..ff682a40f4 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -663,6 +663,8 @@ void SurfaceTool::deindex() {  }  void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) { +	ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::_create_list() must be a valid object of type Mesh"); +  	Array arr = p_existing->surface_get_arrays(p_surface);  	ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX);  	_create_list_from_arrays(arr, r_vertex, r_index, lformat); @@ -824,6 +826,8 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) {  }  void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { +	ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from() must be a valid object of type Mesh"); +  	clear();  	primitive = p_existing->surface_get_primitive_type(p_surface);  	_create_list(p_existing, p_surface, &vertex_array, &index_array, format); @@ -831,6 +835,8 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {  }  void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) { +	ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from_blend_shape() must be a valid object of type Mesh"); +  	clear();  	primitive = p_existing->surface_get_primitive_type(p_surface);  	Array arr = p_existing->surface_get_blend_shape_arrays(p_surface); @@ -851,6 +857,8 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur  }  void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) { +	ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::append_from() must be a valid object of type Mesh"); +  	if (vertex_array.size() == 0) {  		primitive = p_existing->surface_get_primitive_type(p_surface);  		format = 0; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 0d6d3f5e13..a81306b97d 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -2969,6 +2969,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {  	}  } +bool ShaderLanguage::is_control_flow_keyword(String p_keyword) { +	return p_keyword == "break" || +		   p_keyword == "case" || +		   p_keyword == "continue" || +		   p_keyword == "default" || +		   p_keyword == "do" || +		   p_keyword == "else" || +		   p_keyword == "for" || +		   p_keyword == "if" || +		   p_keyword == "return" || +		   p_keyword == "switch" || +		   p_keyword == "while"; +} +  void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) {  	Set<String> kws; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 470f3d38d5..e00f4dce19 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -748,6 +748,7 @@ public:  	static uint32_t get_type_size(DataType p_type);  	static void get_keyword_list(List<String> *r_keywords); +	static bool is_control_flow_keyword(String p_keyword);  	static void get_builtin_funcs(List<String> *r_keywords);  	struct BuiltInInfo {  |