瀏覽代碼

Implement toast notifications in the editor

Gilles Roudière 3 年之前
父節點
當前提交
0587e5e018
共有 38 個文件被更改,包括 928 次插入93 次删除
  1. 1 1
      core/debugger/engine_debugger.h
  2. 1 1
      core/debugger/local_debugger.cpp
  3. 1 1
      core/debugger/local_debugger.h
  4. 3 3
      core/debugger/remote_debugger.cpp
  5. 2 2
      core/debugger/remote_debugger.h
  6. 2 2
      core/debugger/script_debugger.cpp
  7. 1 1
      core/debugger/script_debugger.h
  8. 20 16
      core/error/error_macros.cpp
  9. 219 32
      core/error/error_macros.h
  10. 3 3
      core/extension/gdnative_interface.cpp
  11. 7 3
      core/io/logger.cpp
  12. 2 2
      core/io/logger.h
  13. 2 2
      core/os/os.cpp
  14. 1 1
      core/os/os.h
  15. 1 1
      drivers/unix/os_unix.cpp
  16. 1 1
      drivers/unix/os_unix.h
  17. 5 1
      editor/editor_log.cpp
  18. 1 1
      editor/editor_log.h
  19. 4 0
      editor/editor_node.cpp
  20. 2 0
      editor/editor_node.h
  21. 1 0
      editor/editor_settings.cpp
  22. 511 0
      editor/editor_toaster.cpp
  23. 116 0
      editor/editor_toaster.h
  24. 1 0
      editor/icons/Notification.svg
  25. 1 0
      editor/icons/NotificationDisabled.svg
  26. 1 1
      editor/plugins/tiles/tile_set_atlas_source_editor.cpp
  27. 1 1
      editor/rename_dialog.cpp
  28. 1 1
      editor/rename_dialog.h
  29. 3 3
      modules/gdnative/gdnative/gdnative.cpp
  30. 4 4
      modules/gdscript/gdscript.cpp
  31. 1 1
      modules/gdscript/gdscript_vm.cpp
  32. 1 1
      modules/gdscript/tests/gdscript_test_runner.cpp
  33. 1 1
      modules/gdscript/tests/gdscript_test_runner.h
  34. 2 2
      modules/gltf/gltf_document.cpp
  35. 1 1
      modules/visual_script/visual_script.cpp
  36. 1 1
      platform/osx/os_osx.mm
  37. 1 1
      servers/rendering/renderer_rd/shader_compiler_rd.cpp
  38. 1 1
      tests/test_tools.h

+ 1 - 1
core/debugger/engine_debugger.h

@@ -128,7 +128,7 @@ public:
 
 
 	virtual void poll_events(bool p_is_idle) {}
 	virtual void poll_events(bool p_is_idle) {}
 	virtual void send_message(const String &p_msg, const Array &p_data) = 0;
 	virtual void send_message(const String &p_msg, const Array &p_data) = 0;
-	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) = 0;
+	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) = 0;
 	virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
 	virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
 
 
 	virtual ~EngineDebugger();
 	virtual ~EngineDebugger();

+ 1 - 1
core/debugger/local_debugger.cpp

@@ -358,7 +358,7 @@ void LocalDebugger::send_message(const String &p_message, const Array &p_args) {
 	// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
 	// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
 }
 }
 
 
-void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
+void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) {
 	print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'");
 	print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'");
 }
 }
 
 

+ 1 - 1
core/debugger/local_debugger.h

@@ -50,7 +50,7 @@ private:
 public:
 public:
 	void debug(bool p_can_continue, bool p_is_error_breakpoint);
 	void debug(bool p_can_continue, bool p_is_error_breakpoint);
 	void send_message(const String &p_message, const Array &p_args);
 	void send_message(const String &p_message, const Array &p_args);
-	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
+	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type);
 
 
 	LocalDebugger();
 	LocalDebugger();
 	~LocalDebugger();
 	~LocalDebugger();

+ 3 - 3
core/debugger/remote_debugger.cpp

@@ -455,7 +455,7 @@ Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
 	return err;
 	return err;
 }
 }
 
 
-void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
+void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type) {
 	if (p_type == ERR_HANDLER_SCRIPT) {
 	if (p_type == ERR_HANDLER_SCRIPT) {
 		return; //ignore script errors, those go through debugger
 		return; //ignore script errors, those go through debugger
 	}
 	}
@@ -475,7 +475,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *
 	}
 	}
 
 
 	// send_error will lock internally.
 	// send_error will lock internally.
-	rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+	rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type, si);
 }
 }
 
 
 void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {
 void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {
@@ -605,7 +605,7 @@ void RemoteDebugger::send_message(const String &p_message, const Array &p_args)
 	}
 	}
 }
 }
 
 
-void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
+void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) {
 	ErrorMessage oe;
 	ErrorMessage oe;
 	oe.error = p_err;
 	oe.error = p_err;
 	oe.error_descr = p_descr;
 	oe.error_descr = p_descr;

+ 2 - 2
core/debugger/remote_debugger.h

@@ -89,7 +89,7 @@ private:
 	PrintHandlerList phl;
 	PrintHandlerList phl;
 	static void _print_handler(void *p_this, const String &p_string, bool p_error);
 	static void _print_handler(void *p_this, const String &p_string, bool p_error);
 	ErrorHandlerList eh;
 	ErrorHandlerList eh;
-	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type);
+	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type);
 
 
 	ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr);
 	ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr);
 	Error _put_msg(String p_message, Array p_data);
 	Error _put_msg(String p_message, Array p_data);
@@ -111,7 +111,7 @@ public:
 	// Overrides
 	// Overrides
 	void poll_events(bool p_is_idle);
 	void poll_events(bool p_is_idle);
 	void send_message(const String &p_message, const Array &p_args);
 	void send_message(const String &p_message, const Array &p_args);
-	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
+	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type);
 	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);
 	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);
 
 
 	RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer);
 	RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer);

+ 2 - 2
core/debugger/script_debugger.cpp

@@ -100,10 +100,10 @@ void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_i
 	break_lang = prev;
 	break_lang = prev;
 }
 }
 
 
-void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) {
+void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) {
 	// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
 	// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
 	error_stack_info.append_array(p_stack_info);
 	error_stack_info.append_array(p_stack_info);
-	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_type);
+	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type);
 	error_stack_info.resize(0);
 	error_stack_info.resize(0);
 }
 }
 
 

+ 1 - 1
core/debugger/script_debugger.h

@@ -71,7 +71,7 @@ public:
 	void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);
 	void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);
 	ScriptLanguage *get_break_language() const;
 	ScriptLanguage *get_break_language() const;
 
 
-	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info);
+	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info);
 	Vector<StackInfo> get_error_stack_info() const;
 	Vector<StackInfo> get_error_stack_info() const;
 	ScriptDebugger() {}
 	ScriptDebugger() {}
 };
 };

+ 20 - 16
core/error/error_macros.cpp

@@ -65,45 +65,49 @@ void remove_error_handler(ErrorHandlerList *p_handler) {
 	_global_unlock();
 	_global_unlock();
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) {
-	_err_print_error(p_function, p_file, p_line, p_error, "", p_type);
+// Errors without messages.
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify, ErrorHandlerType p_type) {
+	_err_print_error(p_function, p_file, p_line, p_error, "", p_editor_notify, p_type);
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type) {
-	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_type);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify, ErrorHandlerType p_type) {
+	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_editor_notify, p_type);
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type) {
+// Main error printing function.
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
 	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type);
 	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type);
 
 
 	_global_lock();
 	_global_lock();
 	ErrorHandlerList *l = error_handler_list;
 	ErrorHandlerList *l = error_handler_list;
 	while (l) {
 	while (l) {
-		l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type);
+		l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_editor_notify, p_type);
 		l = l->next;
 		l = l->next;
 	}
 	}
 
 
 	_global_unlock();
 	_global_unlock();
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type) {
-	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_type);
+// Errors with message. (All combinations of p_error and p_message as String or char*.)
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
+	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type);
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type) {
-	_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_type);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) {
+	_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_editor_notify, p_type);
 }
 }
 
 
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type) {
-	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_type);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) {
+	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_editor_notify, p_type);
 }
 }
 
 
-void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool fatal) {
-	String fstr(fatal ? "FATAL: " : "");
+// Index errors. (All combinations of p_message as String or char*.)
+void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool p_editor_notify, bool p_fatal) {
+	String fstr(p_fatal ? "FATAL: " : "");
 	String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ").");
 	String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ").");
 	_err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message);
 	_err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message);
 }
 }
 
 
-void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal) {
-	_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), fatal);
+void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify, bool p_fatal) {
+	_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), p_fatal);
 }
 }

+ 219 - 32
core/error/error_macros.h

@@ -46,7 +46,7 @@ enum ErrorHandlerType {
 
 
 // Pointer to the error handler printing function. Reassign to any function to have errors printed.
 // Pointer to the error handler printing function. Reassign to any function to have errors printed.
 // Parameters: userdata, function, file, line, error, explanation, type.
 // Parameters: userdata, function, file, line, error, explanation, type.
-typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
+typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, bool p_editor_notify, ErrorHandlerType p_type);
 
 
 struct ErrorHandlerList {
 struct ErrorHandlerList {
 	ErrorHandlerFunc errfunc = nullptr;
 	ErrorHandlerFunc errfunc = nullptr;
@@ -61,14 +61,14 @@ void add_error_handler(ErrorHandlerList *p_handler);
 void remove_error_handler(ErrorHandlerList *p_handler);
 void remove_error_handler(ErrorHandlerList *p_handler);
 
 
 // Functions used by the error macros.
 // Functions used by the error macros.
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
-void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false);
-void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false);
+void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
 
 
 #ifdef __GNUC__
 #ifdef __GNUC__
 //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
 //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
@@ -135,6 +135,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                             \
 	} else                                                                                                             \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, prints `m_msg`, notifies in the editor, and the current function returns.
+ */
+#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg)                                                                         \
+	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \
+		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+		return;                                                                                                              \
+	} else                                                                                                                   \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_INDEX_V_MSG`.
  * Try using `ERR_FAIL_INDEX_V_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -160,6 +171,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                             \
 	} else                                                                                                             \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                             \
+	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \
+		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+		return m_retval;                                                                                                     \
+	} else                                                                                                                   \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`.
  * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`.
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
@@ -215,6 +237,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 		return;                                                                                                        \
 		return;                                                                                                        \
 	} else                                                                                                             \
 	} else                                                                                                             \
 		((void)0)
 		((void)0)
+/**
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, prints `m_msg`, notifies in the editor, and the current function returns.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg)                                                                \
+	if (unlikely((m_index) >= (m_size))) {                                                                                   \
+		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+		return;                                                                                                              \
+	} else                                                                                                                   \
+		((void)0)
 
 
 /**
 /**
  * Try using `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
  * Try using `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
@@ -241,6 +273,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                             \
 	} else                                                                                                             \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                    \
+	if (unlikely((m_index) >= (m_size))) {                                                                                   \
+		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+		return m_retval;                                                                                                     \
+	} else                                                                                                                   \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
  * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
@@ -297,6 +340,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                     \
 	} else                                                                                                     \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, prints `m_msg`, notifies in the editor, and the current function returns.
+ */
+#define ERR_FAIL_NULL_EDMSG(m_param, m_msg)                                                                          \
+	if (unlikely(m_param == nullptr)) {                                                                              \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
+		return;                                                                                                      \
+	} else                                                                                                           \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_NULL_V_MSG`.
  * Try using `ERR_FAIL_NULL_V_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -322,6 +376,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                     \
 	} else                                                                                                     \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg)                                                              \
+	if (unlikely(m_param == nullptr)) {                                                                              \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
+		return m_retval;                                                                                             \
+	} else                                                                                                           \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_COND_MSG`.
  * Try using `ERR_FAIL_COND_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -352,6 +417,20 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                    \
 	} else                                                                                                    \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns.
+ *
+ * If checking for null use ERR_FAIL_NULL_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
+ */
+#define ERR_FAIL_COND_EDMSG(m_cond, m_msg)                                                                          \
+	if (unlikely(m_cond)) {                                                                                         \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \
+		return;                                                                                                     \
+	} else                                                                                                          \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_COND_V_MSG`.
  * Try using `ERR_FAIL_COND_V_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -382,6 +461,20 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                                               \
 	} else                                                                                                                               \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`.
+ *
+ * If checking for null use ERR_FAIL_NULL_V_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
+ */
+#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg)                                                                                         \
+	if (unlikely(m_cond)) {                                                                                                                    \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \
+		return m_retval;                                                                                                                       \
+	} else                                                                                                                                     \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_CONTINUE_MSG`.
  * Try using `ERR_CONTINUE_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -407,6 +500,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                                \
 	} else                                                                                                                \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop continues.
+ */
+#define ERR_CONTINUE_EDMSG(m_cond, m_msg)                                                                                       \
+	if (unlikely(m_cond)) {                                                                                                     \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \
+		continue;                                                                                                               \
+	} else                                                                                                                      \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_BREAK_MSG`.
  * Try using `ERR_BREAK_MSG`.
  * Only use this macro if there is no sensible error message.
  * Only use this macro if there is no sensible error message.
@@ -432,6 +536,17 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                              \
 	} else                                                                                                              \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop breaks.
+ */
+#define ERR_BREAK_EDMSG(m_cond, m_msg)                                                                                        \
+	if (unlikely(m_cond)) {                                                                                                   \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \
+		break;                                                                                                                \
+	} else                                                                                                                    \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`.
  * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`.
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
@@ -490,6 +605,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                    \
 	} else                                                                                    \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Try using `ERR_FAIL_COND_MSG`.
+ * Only use this macro if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg`, notifies in the editor, and the current function returns.
+ */
+#define ERR_FAIL_EDMSG(m_msg)                                                                       \
+	if (true) {                                                                                     \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \
+		return;                                                                                     \
+	} else                                                                                          \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_COND_V_MSG` or `ERR_FAIL_V_MSG`.
  * Try using `ERR_FAIL_COND_V_MSG` or `ERR_FAIL_V_MSG`.
  * Only use this macro if more complex error detection or recovery is required, and
  * Only use this macro if more complex error detection or recovery is required, and
@@ -517,6 +645,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                                                                               \
 	} else                                                                                                               \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Try using `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg`, notifies in the editor, and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_V_EDMSG(m_retval, m_msg)                                                                                      \
+	if (true) {                                                                                                                \
+		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \
+		return m_retval;                                                                                                       \
+	} else                                                                                                                     \
+		((void)0)
+
 /**
 /**
  * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG.
  * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG.
  * Only use this macro at the start of a function that has not been implemented yet, or
  * Only use this macro at the start of a function that has not been implemented yet, or
@@ -527,6 +668,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 #define ERR_PRINT(m_msg) \
 #define ERR_PRINT(m_msg) \
 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg)
 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg)
 
 
+/**
+ * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG.
+ * Only use this macro at the start of a function that has not been implemented yet, or
+ * if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg` and notifies the editor.
+ */
+#define ERR_PRINT_ED(m_msg) \
+	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, )
+
 /**
 /**
  * Prints `m_msg` once during the application lifetime.
  * Prints `m_msg` once during the application lifetime.
  */
  */
@@ -540,6 +691,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 	} else                                                             \
 	} else                                                             \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Prints `m_msg` and notifies the editor once during the application lifetime.
+ */
+#define ERR_PRINT_ONCE_ED(m_msg)                                             \
+	if (true) {                                                              \
+		static bool first_print = true;                                      \
+		if (first_print) {                                                   \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \
+			first_print = false;                                             \
+		}                                                                    \
+	} else                                                                   \
+		((void)0)
+
 // Print warning message macros.
 // Print warning message macros.
 
 
 /**
 /**
@@ -548,51 +712,74 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  */
  */
 #define WARN_PRINT(m_msg) \
 #define WARN_PRINT(m_msg) \
-	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING)
+	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING)
+
+/**
+ * Prints `m_msg` and notifies the editor.
+ *
+ * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
+ */
+#define WARN_PRINT_ED(m_msg) \
+	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING)
 
 
 /**
 /**
  * Prints `m_msg` once during the application lifetime.
  * Prints `m_msg` once during the application lifetime.
  *
  *
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  */
  */
-#define WARN_PRINT_ONCE(m_msg)                                                              \
-	if (true) {                                                                             \
-		static bool first_print = true;                                                     \
-		if (first_print) {                                                                  \
-			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \
-			first_print = false;                                                            \
-		}                                                                                   \
-	} else                                                                                  \
+#define WARN_PRINT_ONCE(m_msg)                                                                     \
+	if (true) {                                                                                    \
+		static bool first_print = true;                                                            \
+		if (first_print) {                                                                         \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \
+			first_print = false;                                                                   \
+		}                                                                                          \
+	} else                                                                                         \
 		((void)0)
 		((void)0)
 
 
-// Print deprecated warning message macros.
-
 /**
 /**
- * Warns that the current function is deprecated.
+ * Prints `m_msg` and notifies the editor once during the application lifetime.
+ *
+ * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  */
  */
-#define WARN_DEPRECATED                                                                                                                                    \
-	if (true) {                                                                                                                                            \
-		static SafeFlag warning_shown;                                                                                                                     \
-		if (!warning_shown.is_set()) {                                                                                                                     \
-			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \
-			warning_shown.set();                                                                                                                           \
-		}                                                                                                                                                  \
-	} else                                                                                                                                                 \
+#define WARN_PRINT_ONCE_ED(m_msg)                                                                 \
+	if (true) {                                                                                   \
+		static bool first_print = true;                                                           \
+		if (first_print) {                                                                        \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \
+			first_print = false;                                                                  \
+		}                                                                                         \
+	} else                                                                                        \
 		((void)0)
 		((void)0)
 
 
+// Print deprecated warning message macros.
+
 /**
 /**
- * Warns that the current function is deprecated and prints `m_msg`.
+ * Warns that the current function is deprecated.
  */
  */
-#define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                \
+#define WARN_DEPRECATED                                                                                                                                           \
 	if (true) {                                                                                                                                                   \
 	if (true) {                                                                                                                                                   \
 		static SafeFlag warning_shown;                                                                                                                            \
 		static SafeFlag warning_shown;                                                                                                                            \
 		if (!warning_shown.is_set()) {                                                                                                                            \
 		if (!warning_shown.is_set()) {                                                                                                                            \
-			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, ERR_HANDLER_WARNING); \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \
 			warning_shown.set();                                                                                                                                  \
 			warning_shown.set();                                                                                                                                  \
 		}                                                                                                                                                         \
 		}                                                                                                                                                         \
 	} else                                                                                                                                                        \
 	} else                                                                                                                                                        \
 		((void)0)
 		((void)0)
 
 
+/**
+ * Warns that the current function is deprecated and prints `m_msg`.
+ */
+#define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                       \
+	if (true) {                                                                                                                                                          \
+		static SafeFlag warning_shown;                                                                                                                                   \
+		if (!warning_shown.is_set()) {                                                                                                                                   \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \
+			warning_shown.set();                                                                                                                                         \
+		}                                                                                                                                                                \
+	} else                                                                                                                                                               \
+		((void)0)
+
 /**
 /**
  * Do not use.
  * Do not use.
  * If the application should never reach this point use CRASH_NOW_MSG(m_msg) to explain why.
  * If the application should never reach this point use CRASH_NOW_MSG(m_msg) to explain why.

+ 3 - 3
core/extension/gdnative_interface.cpp

@@ -51,13 +51,13 @@ static void gdnative_free(void *p_mem) {
 
 
 // Helper print functions.
 // Helper print functions.
 static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
 static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR);
 }
 }
 static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
 static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING);
 }
 }
 static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
 static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);
 }
 }
 
 
 // Variant functions
 // Variant functions

+ 7 - 3
core/io/logger.cpp

@@ -50,7 +50,7 @@ void Logger::set_flush_stdout_on_print(bool value) {
 	_flush_stdout_on_print = value;
 	_flush_stdout_on_print = value;
 }
 }
 
 
-void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
 	if (!should_log(true)) {
 	if (!should_log(true)) {
 		return;
 		return;
 	}
 	}
@@ -81,7 +81,11 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
 		err_details = p_code;
 		err_details = p_code;
 	}
 	}
 
 
-	logf_error("%s: %s\n", err_type, err_details);
+	if (p_editor_notify) {
+		logf_error("%s: %s\n", err_type, err_details);
+	} else {
+		logf_error("USER %s: %s\n", err_type, err_details);
+	}
 	logf_error("   at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
 	logf_error("   at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
 }
 }
 
 
@@ -256,7 +260,7 @@ void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) {
 	}
 	}
 }
 }
 
 
-void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
 	if (!should_log(true)) {
 	if (!should_log(true)) {
 		return;
 		return;
 	}
 	}

+ 2 - 2
core/io/logger.h

@@ -54,7 +54,7 @@ public:
 	static void set_flush_stdout_on_print(bool value);
 	static void set_flush_stdout_on_print(bool value);
 
 
 	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
 	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
-	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR);
 
 
 	void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -103,7 +103,7 @@ public:
 	CompositeLogger(Vector<Logger *> p_loggers);
 	CompositeLogger(Vector<Logger *> p_loggers);
 
 
 	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
 	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
-	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR);
 
 
 	void add_logger(Logger *p_logger);
 	void add_logger(Logger *p_logger);
 
 

+ 2 - 2
core/os/os.cpp

@@ -75,12 +75,12 @@ void OS::add_logger(Logger *p_logger) {
 	}
 	}
 }
 }
 
 
-void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) {
+void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type) {
 	if (!_stderr_enabled) {
 	if (!_stderr_enabled) {
 		return;
 		return;
 	}
 	}
 
 
-	_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
+	_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type);
 }
 }
 
 
 void OS::print(const char *p_format, ...) {
 void OS::print(const char *p_format, ...) {

+ 1 - 1
core/os/os.h

@@ -110,7 +110,7 @@ public:
 
 
 	static OS *get_singleton();
 	static OS *get_singleton();
 
 
-	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
+	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR);
 	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 
 

+ 1 - 1
drivers/unix/os_unix.cpp

@@ -528,7 +528,7 @@ String OS_Unix::get_executable_path() const {
 #endif
 #endif
 }
 }
 
 
-void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
 	if (!should_log(true)) {
 	if (!should_log(true)) {
 		return;
 		return;
 	}
 	}

+ 1 - 1
drivers/unix/os_unix.h

@@ -102,7 +102,7 @@ public:
 
 
 class UnixTerminalLogger : public StdLogger {
 class UnixTerminalLogger : public StdLogger {
 public:
 public:
-	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR);
 	virtual ~UnixTerminalLogger();
 	virtual ~UnixTerminalLogger();
 };
 };
 
 

+ 5 - 1
editor/editor_log.cpp

@@ -37,7 +37,7 @@
 #include "scene/gui/center_container.h"
 #include "scene/gui/center_container.h"
 #include "scene/resources/font.h"
 #include "scene/resources/font.h"
 
 
-void EditorLog::_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) {
+void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
 	EditorLog *self = (EditorLog *)p_self;
 	EditorLog *self = (EditorLog *)p_self;
 	if (self->current != Thread::get_caller_id()) {
 	if (self->current != Thread::get_caller_id()) {
 		return;
 		return;
@@ -50,6 +50,10 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
 		err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
 		err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
 	}
 	}
 
 
+	if (p_editor_notify) {
+		err_str += " (User)";
+	}
+
 	if (p_type == ERR_HANDLER_WARNING) {
 	if (p_type == ERR_HANDLER_WARNING) {
 		self->add_message(err_str, MSG_TYPE_WARNING);
 		self->add_message(err_str, MSG_TYPE_WARNING);
 	} else {
 	} else {

+ 1 - 1
editor/editor_log.h

@@ -136,7 +136,7 @@ private:
 	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).
 	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;
 	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);
+	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, bool p_editor_notify, ErrorHandlerType p_type);
 
 
 	ErrorHandlerList eh;
 	ErrorHandlerList eh;
 
 

+ 4 - 0
editor/editor_node.cpp

@@ -95,6 +95,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_spin_slider.h"
 #include "editor/editor_spin_slider.h"
 #include "editor/editor_themes.h"
 #include "editor/editor_themes.h"
+#include "editor/editor_toaster.h"
 #include "editor/editor_translation_parser.h"
 #include "editor/editor_translation_parser.h"
 #include "editor/export_template_manager.h"
 #include "editor/export_template_manager.h"
 #include "editor/filesystem_dock.h"
 #include "editor/filesystem_dock.h"
@@ -6747,6 +6748,9 @@ EditorNode::EditorNode() {
 	bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	bottom_panel_hb->add_child(bottom_panel_hb_editors);
 	bottom_panel_hb->add_child(bottom_panel_hb_editors);
 
 
+	editor_toaster = memnew(EditorToaster);
+	bottom_panel_hb->add_child(editor_toaster);
+
 	VBoxContainer *version_info_vbc = memnew(VBoxContainer);
 	VBoxContainer *version_info_vbc = memnew(VBoxContainer);
 	bottom_panel_hb->add_child(version_info_vbc);
 	bottom_panel_hb->add_child(version_info_vbc);
 
 

+ 2 - 0
editor/editor_node.h

@@ -37,6 +37,7 @@
 #include "editor/editor_folding.h"
 #include "editor/editor_folding.h"
 #include "editor/editor_native_shader_source_visualizer.h"
 #include "editor/editor_native_shader_source_visualizer.h"
 #include "editor/editor_run.h"
 #include "editor/editor_run.h"
+#include "editor/editor_toaster.h"
 #include "editor/inspector_dock.h"
 #include "editor/inspector_dock.h"
 #include "editor/property_editor.h"
 #include "editor/property_editor.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
@@ -438,6 +439,7 @@ private:
 	HBoxContainer *bottom_panel_hb;
 	HBoxContainer *bottom_panel_hb;
 	HBoxContainer *bottom_panel_hb_editors;
 	HBoxContainer *bottom_panel_hb_editors;
 	VBoxContainer *bottom_panel_vb;
 	VBoxContainer *bottom_panel_vb;
+	EditorToaster *editor_toaster;
 	LinkButton *version_btn;
 	LinkButton *version_btn;
 	Button *bottom_panel_raise;
 	Button *bottom_panel_raise;
 
 

+ 1 - 0
editor/editor_settings.cpp

@@ -437,6 +437,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("interface/editor/hide_console_window", false);
 	_initial_set("interface/editor/hide_console_window", false);
 	_initial_set("interface/editor/mouse_extra_buttons_navigate_history", true);
 	_initial_set("interface/editor/mouse_extra_buttons_navigate_history", true);
 	_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
 	_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
+	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_internal_errors_in_toast_notifications", 0, "Auto,Enabled,Disabled")
 
 
 	// Inspector
 	// Inspector
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")

+ 511 - 0
editor/editor_toaster.cpp

@@ -0,0 +1,511 @@
+/*************************************************************************/
+/*  editor_toaster.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "scene/gui/label.h"
+#include "scene/gui/panel_container.h"
+
+#include "editor_toaster.h"
+
+EditorToaster *EditorToaster::singleton;
+
+void EditorToaster::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_INTERNAL_PROCESS: {
+			double delta = get_process_delta_time();
+
+			// Check if one element is hovered, if so, don't elapse time.
+			bool hovered = false;
+			for (const KeyValue<Control *, Toast> &element : toasts) {
+				if (Rect2(Vector2(), element.key->get_size()).has_point(element.key->get_local_mouse_position())) {
+					hovered = true;
+					break;
+				}
+			}
+
+			// Elapses the time and remove toasts if needed.
+			if (!hovered) {
+				for (const KeyValue<Control *, Toast> &element : toasts) {
+					if (!element.value.popped || element.value.duration <= 0) {
+						continue;
+					}
+					toasts[element.key].remaining_time -= delta;
+					if (toasts[element.key].remaining_time < 0) {
+						close(element.key);
+					}
+					element.key->update();
+				}
+			} else {
+				// Reset the timers when hovered.
+				for (const KeyValue<Control *, Toast> &element : toasts) {
+					if (!element.value.popped || element.value.duration <= 0) {
+						continue;
+					}
+					toasts[element.key].remaining_time = element.value.duration;
+					element.key->update();
+				}
+			}
+
+			// Change alpha over time.
+			bool needs_update = false;
+			for (const KeyValue<Control *, Toast> &element : toasts) {
+				Color modulate = element.key->get_modulate();
+
+				// Change alpha over time.
+				if (element.value.popped && modulate.a < 1.0) {
+					modulate.a += delta * 3;
+					element.key->set_modulate(modulate);
+				} else if (!element.value.popped && modulate.a > 0.0) {
+					modulate.a -= delta * 2;
+					element.key->set_modulate(modulate);
+				}
+
+				// Hide element if it is not visible anymore.
+				if (modulate.a <= 0) {
+					element.key->hide();
+					needs_update = true;
+				}
+			}
+
+			if (needs_update) {
+				_update_vbox_position();
+				_update_disable_notifications_button();
+				main_button->update();
+			}
+		} break;
+		case NOTIFICATION_ENTER_TREE:
+		case NOTIFICATION_THEME_CHANGED: {
+			if (vbox_container->is_visible()) {
+				main_button->set_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));
+			} else {
+				main_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+			}
+			disable_notifications_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+
+			// Styleboxes background.
+			info_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+
+			warning_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+			warning_panel_style_background->set_border_color(get_theme_color("warning_color", "Editor"));
+
+			error_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+			error_panel_style_background->set_border_color(get_theme_color("error_color", "Editor"));
+
+			// Styleboxes progress.
+			info_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+
+			warning_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+			warning_panel_style_progress->set_border_color(get_theme_color("warning_color", "Editor"));
+
+			error_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+			error_panel_style_progress->set_border_color(get_theme_color("error_color", "Editor"));
+
+			main_button->update();
+			disable_notifications_button->update();
+		} break;
+		case NOTIFICATION_TRANSFORM_CHANGED: {
+			_update_vbox_position();
+			_update_disable_notifications_button();
+		} break;
+		default:
+			break;
+	}
+}
+
+void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
+	if (!EditorToaster::get_singleton()) {
+		return;
+	}
+
+#ifdef DEV_ENABLED
+	bool in_dev = true;
+#else
+	bool in_dev = false;
+#endif
+
+	int show_all_setting = EDITOR_GET("interface/editor/show_internal_errors_in_toast_notifications");
+
+	if (p_editor_notify || (show_all_setting == 0 && in_dev) || show_all_setting == 1) {
+		String err_str;
+		if (p_errorexp && p_errorexp[0]) {
+			err_str = p_errorexp;
+		} else {
+			err_str = String(p_error);
+		}
+		String tooltip_str = String(p_file) + ":" + itos(p_line);
+
+		if (!p_editor_notify) {
+			if (p_type == ERR_HANDLER_WARNING) {
+				err_str = "INTERNAL WARNING: " + err_str;
+			} else {
+				err_str = "INTERNAL ERROR: " + err_str;
+			}
+		}
+
+		if (p_type == ERR_HANDLER_WARNING) {
+			EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_WARNING, tooltip_str);
+		} else {
+			EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_ERROR, tooltip_str);
+		}
+	}
+}
+
+void EditorToaster::_update_vbox_position() {
+	// This is kind of a workaround because it's hard to keep the VBox anchroed to the bottom.
+	vbox_container->set_size(Vector2());
+	vbox_container->set_position(get_global_position() - vbox_container->get_size() + Vector2(get_size().x, -5 * EDSCALE));
+}
+
+void EditorToaster::_update_disable_notifications_button() {
+	bool any_visible = false;
+	for (KeyValue<Control *, Toast> element : toasts) {
+		if (element.key->is_visible()) {
+			any_visible = true;
+			break;
+		}
+	}
+
+	if (!any_visible || !vbox_container->is_visible()) {
+		disable_notifications_panel->hide();
+	} else {
+		disable_notifications_panel->show();
+		disable_notifications_panel->set_position(get_global_position() + Vector2(5 * EDSCALE, -disable_notifications_panel->get_minimum_size().y) + Vector2(get_size().x, -5 * EDSCALE));
+	}
+}
+
+void EditorToaster::_auto_hide_or_free_toasts() {
+	// Hide or free old temporary items.
+	int visible_temporary = 0;
+	int temporary = 0;
+	LocalVector<Control *> to_delete;
+	for (int i = vbox_container->get_child_count() - 1; i >= 0; i--) {
+		Control *control = Object::cast_to<Control>(vbox_container->get_child(i));
+		if (toasts[control].duration <= 0) {
+			continue; // Ignore non-temporary toasts.
+		}
+
+		temporary++;
+		if (control->is_visible()) {
+			visible_temporary++;
+		}
+
+		// Hide
+		if (visible_temporary > max_temporary_count) {
+			close(control);
+		}
+
+		// Free
+		if (temporary > max_temporary_count * 2) {
+			to_delete.push_back(control);
+		}
+	}
+
+	// Delete the control right away (removed as child) as it might cause issues otherwise when iterative over the vbox_container children.
+	for (unsigned int i = 0; i < to_delete.size(); i++) {
+		vbox_container->remove_child(to_delete[i]);
+		to_delete[i]->queue_delete();
+		toasts.erase(to_delete[i]);
+	}
+}
+
+void EditorToaster::_draw_button() {
+	bool has_one = false;
+	Severity highest_severity = SEVERITY_INFO;
+	for (const KeyValue<Control *, Toast> &element : toasts) {
+		if (!element.key->is_visible()) {
+			continue;
+		}
+		has_one = true;
+		if (element.value.severity > highest_severity) {
+			highest_severity = element.value.severity;
+		}
+	}
+
+	if (!has_one) {
+		return;
+	}
+
+	Color color;
+	real_t button_radius = main_button->get_size().x / 8;
+	switch (highest_severity) {
+		case SEVERITY_INFO:
+			color = get_theme_color("accent_color", "Editor");
+			break;
+		case SEVERITY_WARNING:
+			color = get_theme_color("warning_color", "Editor");
+			break;
+		case SEVERITY_ERROR:
+			color = get_theme_color("error_color", "Editor");
+			break;
+		default:
+			break;
+	}
+	main_button->draw_circle(Vector2(button_radius * 2, button_radius * 2), button_radius, color);
+}
+
+void EditorToaster::_draw_progress(Control *panel) {
+	if (toasts.has(panel) && toasts[panel].remaining_time > 0 && toasts[panel].duration > 0) {
+		Size2 size = panel->get_size();
+		size.x *= MIN(1, Math::range_lerp(toasts[panel].remaining_time, 0, toasts[panel].duration, 0, 2));
+
+		Ref<StyleBoxFlat> stylebox;
+		switch (toasts[panel].severity) {
+			case SEVERITY_INFO:
+				stylebox = info_panel_style_progress;
+				break;
+			case SEVERITY_WARNING:
+				stylebox = warning_panel_style_progress;
+				break;
+			case SEVERITY_ERROR:
+				stylebox = error_panel_style_progress;
+				break;
+			default:
+				break;
+		}
+		panel->draw_style_box(stylebox, Rect2(Vector2(), size));
+	}
+}
+
+void EditorToaster::_set_notifications_enabled(bool p_enabled) {
+	vbox_container->set_visible(p_enabled);
+	if (p_enabled) {
+		main_button->set_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));
+	} else {
+		main_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+	}
+	_update_disable_notifications_button();
+}
+
+void EditorToaster::_repop_old() {
+	// Repop olds, up to max_temporary_count
+	bool needs_update = false;
+	int visible = 0;
+	for (int i = vbox_container->get_child_count() - 1; i >= 0; i--) {
+		Control *control = Object::cast_to<Control>(vbox_container->get_child(i));
+		if (!control->is_visible()) {
+			control->show();
+			toasts[control].remaining_time = toasts[control].duration;
+			toasts[control].popped = true;
+			needs_update = true;
+		}
+		visible++;
+		if (visible >= max_temporary_count) {
+			break;
+		}
+	}
+	if (needs_update) {
+		_update_vbox_position();
+		_update_disable_notifications_button();
+		main_button->update();
+	}
+}
+
+Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_time, String p_tooltip) {
+	// Create the panel according to the severity.
+	PanelContainer *panel = memnew(PanelContainer);
+	panel->set_tooltip(p_tooltip);
+	switch (p_severity) {
+		case SEVERITY_INFO:
+			panel->add_theme_style_override("panel", info_panel_style_background);
+			break;
+		case SEVERITY_WARNING:
+			panel->add_theme_style_override("panel", warning_panel_style_background);
+			break;
+		case SEVERITY_ERROR:
+			panel->add_theme_style_override("panel", error_panel_style_background);
+			break;
+		default:
+			break;
+	}
+	panel->set_modulate(Color(1, 1, 1, 0));
+	panel->connect("draw", callable_bind(callable_mp(this, &EditorToaster::_draw_progress), panel));
+
+	// Horizontal container.
+	HBoxContainer *hbox_container = memnew(HBoxContainer);
+	hbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
+	panel->add_child(hbox_container);
+
+	// Content control.
+	p_control->set_h_size_flags(SIZE_EXPAND_FILL);
+	hbox_container->add_child(p_control);
+
+	// Close button.
+	if (p_time > 0.0) {
+		Button *close_button = memnew(Button);
+		close_button->set_flat(true);
+		close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
+		close_button->connect("pressed", callable_bind(callable_mp(this, &EditorToaster::close), panel));
+		hbox_container->add_child(close_button);
+	}
+
+	toasts[panel].severity = p_severity;
+	if (p_time > 0.0) {
+		toasts[panel].duration = p_time;
+		toasts[panel].remaining_time = p_time;
+	} else {
+		toasts[panel].duration = -1.0;
+	}
+	toasts[panel].popped = true;
+	vbox_container->add_child(panel);
+	_auto_hide_or_free_toasts();
+	_update_vbox_position();
+	_update_disable_notifications_button();
+	main_button->update();
+
+	return panel;
+}
+
+void EditorToaster::popup_str(String p_message, Severity p_severity, String p_tooltip) {
+	// Check if we already have a popup with the given message.
+	Control *control = nullptr;
+	for (KeyValue<Control *, Toast> element : toasts) {
+		if (element.value.message == p_message && element.value.severity == p_severity && element.value.tooltip == p_tooltip) {
+			control = element.key;
+			break;
+		}
+	}
+
+	// Create a new message if needed.
+	if (control == nullptr) {
+		Label *label = memnew(Label);
+
+		control = popup(label, p_severity, default_message_duration, p_tooltip);
+		toasts[control].message = p_message;
+		toasts[control].tooltip = p_tooltip;
+		toasts[control].count = 1;
+	} else {
+		if (toasts[control].popped) {
+			toasts[control].count += 1;
+		} else {
+			toasts[control].count = 1;
+		}
+		toasts[control].remaining_time = toasts[control].duration;
+		toasts[control].popped = true;
+		control->show();
+		vbox_container->move_child(control, vbox_container->get_child_count());
+		_auto_hide_or_free_toasts();
+		_update_vbox_position();
+		_update_disable_notifications_button();
+		main_button->update();
+	}
+
+	// Retrieve the label back then update the text.
+	Label *label = Object::cast_to<Label>(control->get_child(0)->get_child(0));
+	ERR_FAIL_COND(!label);
+	if (toasts[control].count == 1) {
+		label->set_text(p_message);
+	} else {
+		label->set_text(vformat("%s (%d)", p_message, toasts[control].count));
+	}
+}
+
+void EditorToaster::close(Control *p_control) {
+	ERR_FAIL_COND(!toasts.has(p_control));
+	toasts[p_control].remaining_time = -1.0;
+	toasts[p_control].popped = false;
+}
+
+EditorToaster *EditorToaster::get_singleton() {
+	return singleton;
+}
+
+EditorToaster::EditorToaster() {
+	set_notify_transform(true);
+	set_process_internal(true);
+
+	// VBox.
+	vbox_container = memnew(VBoxContainer);
+	vbox_container->set_as_top_level(true);
+	vbox_container->connect("resized", callable_mp(this, &EditorToaster::_update_vbox_position));
+	add_child(vbox_container);
+
+	// Theming (background).
+	info_panel_style_background.instantiate();
+	info_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	warning_panel_style_background.instantiate();
+	warning_panel_style_background->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+	warning_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	error_panel_style_background.instantiate();
+	error_panel_style_background->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+	error_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	Ref<StyleBoxFlat> boxes[] = { info_panel_style_background, warning_panel_style_background, error_panel_style_background };
+	for (int i = 0; i < 3; i++) {
+		boxes[i]->set_default_margin(SIDE_LEFT, int(stylebox_radius * 2.5));
+		boxes[i]->set_default_margin(SIDE_RIGHT, int(stylebox_radius * 2.5));
+		boxes[i]->set_default_margin(SIDE_TOP, 3);
+		boxes[i]->set_default_margin(SIDE_BOTTOM, 3);
+	}
+
+	// Theming (progress).
+	info_panel_style_progress.instantiate();
+	info_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	warning_panel_style_progress.instantiate();
+	warning_panel_style_progress->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+	warning_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	error_panel_style_progress.instantiate();
+	error_panel_style_progress->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+	error_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+	// Main button.
+	main_button = memnew(Button);
+	main_button->set_flat(true);
+	main_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(true));
+	main_button->connect("pressed", callable_mp(this, &EditorToaster::_repop_old));
+	main_button->connect("draw", callable_mp(this, &EditorToaster::_draw_button));
+	add_child(main_button);
+
+	// Disable notification button.
+	disable_notifications_panel = memnew(PanelContainer);
+	disable_notifications_panel->set_as_top_level(true);
+	disable_notifications_panel->add_theme_style_override("panel", info_panel_style_background);
+	add_child(disable_notifications_panel);
+
+	disable_notifications_button = memnew(Button);
+	disable_notifications_button->set_flat(true);
+	disable_notifications_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(false));
+	disable_notifications_panel->add_child(disable_notifications_button);
+
+	// Other
+	singleton = this;
+
+	eh.errfunc = _error_handler;
+	add_error_handler(&eh);
+};
+
+EditorToaster::~EditorToaster() {
+	singleton = nullptr;
+	remove_error_handler(&eh);
+}

+ 116 - 0
editor/editor_toaster.h

@@ -0,0 +1,116 @@
+/*************************************************************************/
+/*  editor_toaster.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef EDITOR_TOASTER_H
+#define EDITOR_TOASTER_H
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/popup.h"
+
+#include "core/string/ustring.h"
+#include "core/templates/local_vector.h"
+
+class EditorToaster : public HBoxContainer {
+	GDCLASS(EditorToaster, HBoxContainer);
+
+public:
+	enum Severity {
+		SEVERITY_INFO = 0,
+		SEVERITY_WARNING,
+		SEVERITY_ERROR,
+	};
+
+private:
+	ErrorHandlerList eh;
+
+	const int stylebox_radius = 3;
+
+	Ref<StyleBoxFlat> info_panel_style_background;
+	Ref<StyleBoxFlat> warning_panel_style_background;
+	Ref<StyleBoxFlat> error_panel_style_background;
+
+	Ref<StyleBoxFlat> info_panel_style_progress;
+	Ref<StyleBoxFlat> warning_panel_style_progress;
+	Ref<StyleBoxFlat> error_panel_style_progress;
+
+	Button *main_button;
+	PanelContainer *disable_notifications_panel;
+	Button *disable_notifications_button;
+
+	VBoxContainer *vbox_container;
+	const int max_temporary_count = 5;
+	struct Toast {
+		Severity severity = SEVERITY_INFO;
+
+		// Timing.
+		real_t duration = -1.0;
+		real_t remaining_time = 0.0;
+		bool popped = false;
+
+		// Messages
+		String message;
+		String tooltip;
+		int count = 0;
+	};
+	Map<Control *, Toast> toasts;
+
+	const double default_message_duration = 5.0;
+
+	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, bool p_editor_notify, ErrorHandlerType p_type);
+	void _update_vbox_position();
+	void _update_disable_notifications_button();
+	void _auto_hide_or_free_toasts();
+
+	void _draw_button();
+	void _draw_progress(Control *panel);
+
+	void _set_notifications_enabled(bool p_enabled);
+	void _repop_old();
+
+protected:
+	static EditorToaster *singleton;
+
+	void _notification(int p_what);
+
+public:
+	static EditorToaster *get_singleton();
+
+	Control *popup(Control *p_control, Severity p_severity = SEVERITY_INFO, double p_time = 0.0, String p_tooltip = String());
+	void popup_str(String p_message, Severity p_severity = SEVERITY_INFO, String p_tooltip = String());
+	void close(Control *p_control);
+
+	EditorToaster();
+	~EditorToaster();
+};
+
+VARIANT_ENUM_CAST(EditorToaster::Severity);
+
+#endif // EDITOR_TOASTER_H

+ 1 - 0
editor/icons/Notification.svg

@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12.089506 1.2353795c-.498141-.2384823-1.095292-.027987-1.333775.4701537-.01372.028981-.02341.059557-.03428.089693-.01467.023016-.02777.046925-.04071.071459-.04899-.00527-.09728-.00862-.146087-.011473-1.4730257-.6255101-3.1777024.0153376-3.8738627 1.4563251l-1.7272425 3.6078572s-.3364181.7034345-.8079671 1.1268133c-.00105.0009371-.00239.00174-.00344.00268-.2721193.1337295-.5707545.185826-.8605632.0470816-.4981411-.2384824-1.0952924-.0279876-1.3337749.4701537-.01605.033526-.029907.066894-.041944.1011828-.018769.030371-.036749.06319-.052515.096122-.2384825.4981412-.027988 1.0952923.4701537 1.3337751l9.0196437 4.318106c.498141.238482 1.095292.02799 1.333775-.470154.01577-.03293.0301-.0675.04191-.1012.0192-.03086.0365-.06257.05255-.0961.238483-.498141.02799-1.095292-.470153-1.333775-.901965-.43181-.03834-2.235739-.03834-2.235739l1.727237-3.6078618c.715447-1.4944233.08396-3.2858776-1.410461-4.0013247.238482-.4981411.02799-1.0952926-.470154-1.333775zm-5.5145786 11.3714015c-.322341.673306-.037829 1.480435.6354753 1.802775.6733031.32234 1.4804334.03783 1.8027749-.635476z" fill="#e6e6e6"/></svg>

+ 1 - 0
editor/icons/NotificationDisabled.svg

@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11.705078 1.1386719c-.389281-.0180576-.770356.1928007-.949219.5664062-.01372.028981-.024286.0597078-.035156.0898438-.01467.023016-.026122.0477316-.039062.0722656-.04899-.00527-.097678-.0088657-.146485-.0117187-1.4730253-.6255102-3.1788394.0160437-3.8749998 1.4570312l-1.7265624 3.6074219s-.3370448.7035743-.8085938 1.1269531l-.0019531.0019531c-.2721193.1337295-.5715196.1856194-.8613281.046875-.4981413-.2384824-1.0955019-.0274382-1.3339844.4707031-.01605.0335262-.0289787.0672737-.0410156.1015626-.0187691.0303709-.0369684.0627711-.0527344.0957031-.2384825.4981412-.0293917 1.0955019.46875 1.3339841l.3398437.16211 10.8984379-6.9394535c-.263272-.3070418-.592225-.5660832-.980469-.7519531.238482-.4981411.027441-1.0935489-.470703-1.3320313-.124536-.0596206-.255006-.091637-.384766-.0976562zm2.435547 2.8652343a.94188849 1 0 0 0 -.566406.1386719l-12.1171878 7.7148439a.94188849 1 0 0 0 -.3222656 1.373047.94188849 1 0 0 0 1.2910156.341797l12.1171878-7.7148441a.94188849 1 0 0 0 .322265-1.3710938.94188849 1 0 0 0 -.724609-.4824219zm-.509766 3.2753907-7.3867184 4.7050781 5.0781254 2.431641c.498141.238482 1.095501.027442 1.333984-.470704.01577-.03293.031159-.067862.042969-.101562.0192-.03086.036684-.062173.052734-.095703.238483-.498141.02744-1.095501-.470703-1.333985-.901965-.431809-.039063-2.236328-.039062-2.236328zm-7.0566402 5.3281251c-.322341.673306-.0365856 1.480394.6367187 1.802734.6733031.32234 1.4803929.036588 1.8027344-.636718z" fill="#e6e6e6"/></svg>

+ 1 - 1
editor/plugins/tiles/tile_set_atlas_source_editor.cpp

@@ -157,7 +157,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
 			} else if (p_name == "size_in_atlas") {
 			} else if (p_name == "size_in_atlas") {
 				Vector2i as_vector2i = Vector2i(p_value);
 				Vector2i as_vector2i = Vector2i(p_value);
 				bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
 				bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
-				ERR_FAIL_COND_V(!has_room_for_tile, false);
+				ERR_FAIL_COND_V_EDMSG(!has_room_for_tile, false, "Invalid size or not enough room in the atlas for the tile.");
 				tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
 				tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
 				emit_signal(SNAME("changed"), "size_in_atlas");
 				emit_signal(SNAME("changed"), "size_in_atlas");
 				return true;
 				return true;

+ 1 - 1
editor/rename_dialog.cpp

@@ -459,7 +459,7 @@ String RenameDialog::_substitute(const String &subject, const Node *node, int co
 	return result;
 	return result;
 }
 }
 
 
-void RenameDialog::_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) {
+void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
 	RenameDialog *self = (RenameDialog *)p_self;
 	RenameDialog *self = (RenameDialog *)p_self;
 	String source_file(p_file);
 	String source_file(p_file);
 
 

+ 1 - 1
editor/rename_dialog.h

@@ -63,7 +63,7 @@ class RenameDialog : public ConfirmationDialog {
 	String _postprocess(const String &subject);
 	String _postprocess(const String &subject);
 	void _update_preview(String new_text = "");
 	void _update_preview(String new_text = "");
 	void _update_preview_int(int new_value = 0);
 	void _update_preview_int(int new_value = 0);
-	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);
+	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, bool p_editor_notify, ErrorHandlerType p_type);
 
 
 	SceneTreeEditor *scene_tree_editor;
 	SceneTreeEditor *scene_tree_editor;
 	UndoRedo *undo_redo;
 	UndoRedo *undo_redo;

+ 3 - 3
modules/gdnative/gdnative/gdnative.cpp

@@ -129,13 +129,13 @@ void GDAPI godot_free(void *p_ptr) {
 
 
 // Helper print functions.
 // Helper print functions.
 void GDAPI godot_print_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
 void GDAPI godot_print_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR);
 }
 }
 void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line) {
 void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING);
 }
 }
 void GDAPI godot_print_script_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
 void GDAPI godot_print_script_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
-	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT);
+	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);
 }
 }
 
 
 void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have) {
 void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have) {

+ 4 - 4
modules/gdscript/gdscript.cpp

@@ -835,7 +835,7 @@ Error GDScript::reload(bool p_keep_state) {
 			GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
 			GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
 		}
 		}
 		// TODO: Show all error messages.
 		// TODO: Show all error messages.
-		_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+		_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 		ERR_FAIL_V(ERR_PARSE_ERROR);
 		ERR_FAIL_V(ERR_PARSE_ERROR);
 	}
 	}
 
 
@@ -849,7 +849,7 @@ Error GDScript::reload(bool p_keep_state) {
 
 
 		const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
 		const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
 		while (e != nullptr) {
 		while (e != nullptr) {
-			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 			e = e->next();
 			e = e->next();
 		}
 		}
 		ERR_FAIL_V(ERR_PARSE_ERROR);
 		ERR_FAIL_V(ERR_PARSE_ERROR);
@@ -869,7 +869,7 @@ Error GDScript::reload(bool p_keep_state) {
 			if (EngineDebugger::is_active()) {
 			if (EngineDebugger::is_active()) {
 				GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
 				GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
 			}
 			}
-			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
+			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 			ERR_FAIL_V(ERR_COMPILATION_FAILED);
 			ERR_FAIL_V(ERR_COMPILATION_FAILED);
 		} else {
 		} else {
 			return err;
 			return err;
@@ -879,7 +879,7 @@ Error GDScript::reload(bool p_keep_state) {
 	for (const GDScriptWarning &warning : parser.get_warnings()) {
 	for (const GDScriptWarning &warning : parser.get_warnings()) {
 		if (EngineDebugger::is_active()) {
 		if (EngineDebugger::is_active()) {
 			Vector<ScriptLanguage::StackInfo> si;
 			Vector<ScriptLanguage::StackInfo> si;
-			EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+			EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si);
 		}
 		}
 	}
 	}
 #endif
 #endif

+ 1 - 1
modules/gdscript/gdscript_vm.cpp

@@ -3313,7 +3313,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 		if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
 		if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
 			// debugger break did not happen
 			// debugger break did not happen
 
 
-			_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
+			_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 		}
 		}
 
 
 #endif
 #endif

+ 1 - 1
modules/gdscript/tests/gdscript_test_runner.cpp

@@ -334,7 +334,7 @@ void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_e
 	result->output += p_message + "\n";
 	result->output += p_message + "\n";
 }
 }
 
 
-void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type) {
+void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type) {
 	ErrorHandlerData *data = (ErrorHandlerData *)p_this;
 	ErrorHandlerData *data = (ErrorHandlerData *)p_this;
 	GDScriptTest *self = data->self;
 	GDScriptTest *self = data->self;
 	TestResult *result = data->result;
 	TestResult *result = data->result;

+ 1 - 1
modules/gdscript/tests/gdscript_test_runner.h

@@ -87,7 +87,7 @@ private:
 
 
 public:
 public:
 	static void print_handler(void *p_this, const String &p_message, bool p_error);
 	static void print_handler(void *p_this, const String &p_message, bool p_error);
-	static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type);
+	static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type);
 	TestResult run_test();
 	TestResult run_test();
 	bool generate_output();
 	bool generate_output();
 
 

+ 2 - 2
modules/gltf/gltf_document.cpp

@@ -247,7 +247,7 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
 	JSON json;
 	JSON json;
 	err = json.parse(text);
 	err = json.parse(text);
 	if (err != OK) {
 	if (err != OK) {
-		_err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT);
+		_err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 		return err;
 		return err;
 	}
 	}
 	state->json = json.get_data();
 	state->json = json.get_data();
@@ -282,7 +282,7 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
 	JSON json;
 	JSON json;
 	err = json.parse(text);
 	err = json.parse(text);
 	if (err != OK) {
 	if (err != OK) {
-		_err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT);
+		_err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 		return err;
 		return err;
 	}
 	}
 
 

+ 1 - 1
modules/visual_script/visual_script.cpp

@@ -1688,7 +1688,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
 		// debugger break did not happen
 		// debugger break did not happen
 
 
 		if (!VisualScriptLanguage::singleton->debug_break(error_str, false)) {
 		if (!VisualScriptLanguage::singleton->debug_break(error_str, false)) {
-			_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), ERR_HANDLER_SCRIPT);
+			_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
 		}
 		}
 
 
 		//}
 		//}

+ 1 - 1
platform/osx/os_osx.mm

@@ -178,7 +178,7 @@
 
 
 class OSXTerminalLogger : public StdLogger {
 class OSXTerminalLogger : public StdLogger {
 public:
 public:
-	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR) {
 		if (!should_log(true)) {
 		if (!should_log(true)) {
 			return;
 			return;
 		}
 		}

+ 1 - 1
servers/rendering/renderer_rd/shader_compiler_rd.cpp

@@ -1421,7 +1421,7 @@ Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, Ide
 			}
 			}
 		}
 		}
 
 
-		_err_print_error(nullptr, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), ERR_HANDLER_SHADER);
+		_err_print_error(nullptr, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER);
 		return err;
 		return err;
 	}
 	}
 
 

+ 1 - 1
tests/test_tools.h

@@ -49,7 +49,7 @@ struct ErrorDetector {
 		has_error = false;
 		has_error = false;
 	}
 	}
 
 
-	static void _detect_error(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) {
+	static void _detect_error(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
 		ErrorDetector *self = (ErrorDetector *)p_self;
 		ErrorDetector *self = (ErrorDetector *)p_self;
 		self->has_error = true;
 		self->has_error = true;
 	}
 	}