diff options
-rw-r--r-- | core/doc_data.h | 1 | ||||
-rw-r--r-- | core/extension/native_extension.cpp | 2 | ||||
-rw-r--r-- | core/extension/native_extension.h | 2 | ||||
-rw-r--r-- | core/extension/native_extension_manager.cpp | 19 | ||||
-rw-r--r-- | core/extension/native_extension_manager.h | 3 | ||||
-rw-r--r-- | core/io/config_file.cpp | 2 | ||||
-rw-r--r-- | core/object/class_db.cpp | 26 | ||||
-rw-r--r-- | core/object/class_db.h | 28 | ||||
-rw-r--r-- | core/register_core_types.cpp | 8 | ||||
-rw-r--r-- | doc/classes/ConfigFile.xml | 2 | ||||
-rw-r--r-- | doc/classes/NativeExtensionManager.xml | 6 | ||||
-rw-r--r-- | editor/doc_tools.cpp | 20 | ||||
-rw-r--r-- | editor/editor_export.cpp | 9 | ||||
-rw-r--r-- | editor/editor_file_system.cpp | 83 | ||||
-rw-r--r-- | editor/editor_file_system.h | 2 | ||||
-rw-r--r-- | editor/editor_help.cpp | 28 | ||||
-rw-r--r-- | editor/editor_node.cpp | 27 | ||||
-rw-r--r-- | editor/editor_node.h | 6 |
18 files changed, 265 insertions, 9 deletions
diff --git a/core/doc_data.h b/core/doc_data.h index a3011fe275..19dec71927 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -67,6 +67,7 @@ public: String qualifiers; String description; Vector<ArgumentDoc> arguments; + Vector<int> errors_returned; bool operator<(const MethodDoc &p_method) const { if (name == p_method.name) { // Must be a constructor since there is no overloading. diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 0556c9c84d..a3cd7ca14c 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -35,6 +35,8 @@ #include "core/object/method_bind.h" #include "core/os/os.h" +const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg"; + class NativeExtensionMethodBind : public MethodBind { GDNativeExtensionClassMethodCall call_func; GDNativeExtensionClassMethodPtrCall ptrcall_func; diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index a961b21cc9..b661381d64 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -60,6 +60,8 @@ protected: static void _bind_methods(); public: + static const char *EXTENSION_LIST_CONFIG_FILE; + Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp index 7be2593845..8b7a9df4f1 100644 --- a/core/extension/native_extension_manager.cpp +++ b/core/extension/native_extension_manager.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "native_extension_manager.h" +#include "core/io/file_access.h" NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) { if (native_extension_map.has(p_path)) { @@ -76,6 +77,11 @@ NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(cons native_extension_map.erase(p_path); return LOAD_STATUS_OK; } + +bool NativeExtensionManager::is_extension_loaded(const String &p_path) const { + return native_extension_map.has(p_path); +} + Vector<String> NativeExtensionManager::get_loaded_extensions() const { Vector<String> ret; for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { @@ -105,6 +111,17 @@ void NativeExtensionManager::deinitialize_extensions(NativeExtension::Initializa level = int32_t(p_level) - 1; } +void NativeExtensionManager::load_extensions() { + FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ); + while (f && !f->eof_reached()) { + String s = f->get_line().strip_edges(); + if (s != String()) { + LoadStatus err = load_extension(s); + ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s); + } + } +} + NativeExtensionManager *NativeExtensionManager::get_singleton() { return singleton; } @@ -112,6 +129,8 @@ void NativeExtensionManager::_bind_methods() { ClassDB::bind_method(D_METHOD("load_extension", "path"), &NativeExtensionManager::load_extension); ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_extension); ClassDB::bind_method(D_METHOD("unload_extension", "path"), &NativeExtensionManager::unload_extension); + ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &NativeExtensionManager::is_extension_loaded); + ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions); ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension); diff --git a/core/extension/native_extension_manager.h b/core/extension/native_extension_manager.h index 78465bd5cf..89ccd155fe 100644 --- a/core/extension/native_extension_manager.h +++ b/core/extension/native_extension_manager.h @@ -55,6 +55,7 @@ public: LoadStatus load_extension(const String &p_path); LoadStatus reload_extension(const String &p_path); LoadStatus unload_extension(const String &p_path); + bool is_extension_loaded(const String &p_path) const; Vector<String> get_loaded_extensions() const; Ref<NativeExtension> get_extension(const String &p_path); @@ -63,6 +64,8 @@ public: static NativeExtensionManager *get_singleton(); + void load_extensions(); + NativeExtensionManager(); }; diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index aeaf25f321..55793aa5a4 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -315,6 +315,8 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); + BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN); + ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index b29b2bd421..e268a8d292 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -892,6 +892,32 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ } } +void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) { + OBJTYPE_RLOCK; +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND(!type); + + type->method_error_values[p_method] = p_values; +#endif +} + +Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) { +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND_V(!type, Vector<Error>()); + + if (!type->method_error_values.has(p_method)) { + return Vector<Error>(); + } + return type->method_error_values[p_method]; +#else + return Vector<Error>(); +#endif +} + bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { OBJTYPE_RLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index 45572517be..166aa35469 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -132,6 +132,7 @@ public: List<MethodInfo> virtual_methods; Map<StringName, MethodInfo> virtual_methods_map; StringName category; + Map<StringName, Vector<Error>> method_error_values; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -385,6 +386,8 @@ public: static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); + static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values); + static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method); static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr); static StringName get_category(const StringName &p_node); @@ -415,6 +418,29 @@ public: #define BIND_ENUM_CONSTANT(m_constant) \ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) { +} + +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err) { + arr.push_back(p_err); +} + +template <class... P> +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err, P... p_args) { + arr.push_back(p_err); + errarray_add_str(arr, p_args...); +} + +template <class... P> +_FORCE_INLINE_ Vector<Error> errarray(P... p_args) { + Vector<Error> arr; + errarray_add_str(arr, p_args...); + return arr; +} + +#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) \ + ::ClassDB::set_method_error_return_values(get_class_static(), m_method, errarray(__VA_ARGS__)); + #else #define BIND_CONSTANT(m_constant) \ @@ -423,6 +449,8 @@ public: #define BIND_ENUM_CONSTANT(m_constant) \ ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); +#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) + #endif #define GDREGISTER_CLASS(m_class) \ diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 35f4532abb..6c7d9cbd89 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -308,13 +308,7 @@ void register_core_singletons() { void register_core_extensions() { // Hardcoded for now. NativeExtension::initialize_native_extensions(); - if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) { - Vector<String> paths = ProjectSettings::get_singleton()->get("native_extensions/paths"); - for (int i = 0; i < paths.size(); i++) { - NativeExtensionManager::LoadStatus status = native_extension_manager->load_extension(paths[i]); - ERR_CONTINUE_MSG(status != NativeExtensionManager::LOAD_STATUS_OK, "Error loading extension: " + paths[i]); - } - } + native_extension_manager->load_extensions(); native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE); } diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index f970be23a6..d6da4bc248 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -149,6 +149,8 @@ </method> <method name="load"> <return type="int" enum="Error" /> + <returns_error number="0"/> + <returns_error number="12"/> <argument index="0" name="path" type="String" /> <description> Loads the config file specified as a parameter. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on. diff --git a/doc/classes/NativeExtensionManager.xml b/doc/classes/NativeExtensionManager.xml index c14ce94aff..42246619f6 100644 --- a/doc/classes/NativeExtensionManager.xml +++ b/doc/classes/NativeExtensionManager.xml @@ -18,6 +18,12 @@ <description> </description> </method> + <method name="is_extension_loaded" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="path" type="String" /> + <description> + </description> + </method> <method name="load_extension"> <return type="int" enum="NativeExtensionManager.LoadStatus" /> <argument index="0" name="path" type="String" /> diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index fb5f7448c4..fee2deddda 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -434,6 +434,18 @@ void DocTools::generate(bool p_basic_types) { } } + Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); + if (errs.size()) { + if (errs.find(OK) == -1) { + errs.insert(0, OK); + } + for (int i = 0; i < errs.size(); i++) { + if (method.errors_returned.find(errs[i]) == -1) { + method.errors_returned.push_back(errs[i]); + } + } + } + c.methods.push_back(method); } @@ -874,6 +886,9 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> & if (parser->has_attribute("enum")) { method.return_enum = parser->get_attribute_value("enum"); } + } else if (name == "returns_error") { + ERR_FAIL_COND_V(!parser->has_attribute("number"), ERR_FILE_CORRUPT); + method.errors_returned.push_back(parser->get_attribute_value("number").to_int()); } else if (name == "argument") { DocData::ArgumentDoc argument; ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT); @@ -1222,6 +1237,11 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str } _write_string(f, 3, "<return type=\"" + m.return_type + "\"" + enum_text + " />"); } + if (m.errors_returned.size() > 0) { + for (int j = 0; j < m.errors_returned.size(); j++) { + _write_string(f, 3, "<returns_error number=\"" + itos(m.errors_returned[j]) + "\"/>"); + } + } for (int j = 0; j < m.arguments.size(); j++) { const DocData::ArgumentDoc &a = m.arguments[j]; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 91c3c51c4d..1240496028 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" +#include "core/extension/native_extension.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -1050,6 +1051,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } } + if (FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) { + Vector<uint8_t> array = FileAccess::get_file_as_array(NativeExtension::EXTENSION_LIST_CONFIG_FILE); + err = p_func(p_udata, NativeExtension::EXTENSION_LIST_CONFIG_FILE, array, idx, total, enc_in_filters, enc_ex_filters, key); + if (err != OK) { + return err; + } + } + // Store text server data if it is supported. if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) { bool use_data = ProjectSettings::get_singleton()->get("internationalization/locale/include_text_server_data"); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 78861eff9d..aa89a14725 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -31,6 +31,7 @@ #include "editor_file_system.h" #include "core/config/project_settings.h" +#include "core/extension/native_extension_manager.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" #include "core/io/resource_loader.h" @@ -605,6 +606,18 @@ bool EditorFileSystem::_update_scan_actions() { } } + if (_scan_extensions()) { + //needs editor restart + //extensions also may provide filetypes to be imported, so they must run before importing + if (EditorNode::immediate_confirmation_dialog(TTR("Some extensions need the editor to restart to take effect."), first_scan ? TTR("Restart") : TTR("Save&Restart"), TTR("Continue"))) { + if (!first_scan) { + EditorNode::get_singleton()->save_all_scenes(); + } + EditorNode::get_singleton()->restart_editor(); + //do not import + return true; + } + } if (reimports.size()) { reimport_files(reimports); } else { @@ -2222,6 +2235,76 @@ ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const } } +static void _scan_extensions_dir(EditorFileSystemDirectory *d, Set<String> &extensions) { + int fc = d->get_file_count(); + for (int i = 0; i < fc; i++) { + if (d->get_file_type(i) == SNAME("NativeExtension")) { + extensions.insert(d->get_file_path(i)); + } + } + int dc = d->get_subdir_count(); + for (int i = 0; i < dc; i++) { + _scan_extensions_dir(d->get_subdir(i), extensions); + } +} +bool EditorFileSystem::_scan_extensions() { + EditorFileSystemDirectory *d = get_filesystem(); + Set<String> extensions; + _scan_extensions_dir(d, extensions); + + //verify against loaded extensions + + Vector<String> extensions_added; + Vector<String> extensions_removed; + + for (const String &E : extensions) { + if (!NativeExtensionManager::get_singleton()->is_extension_loaded(E)) { + extensions_added.push_back(E); + } + } + + Vector<String> loaded_extensions = NativeExtensionManager::get_singleton()->get_loaded_extensions(); + for (int i = 0; i < loaded_extensions.size(); i++) { + if (!extensions.has(loaded_extensions[i])) { + extensions_removed.push_back(loaded_extensions[i]); + } + } + + if (extensions.size()) { + if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed + FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::WRITE); + for (const String &E : extensions) { + f->store_line(E); + } + } + } else { + if (loaded_extensions.size() || FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) { //extensions were removed + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(NativeExtension::EXTENSION_LIST_CONFIG_FILE); + } + } + + bool needs_restart = false; + for (int i = 0; i < extensions_added.size(); i++) { + NativeExtensionManager::LoadStatus st = NativeExtensionManager::get_singleton()->load_extension(extensions_added[i]); + if (st == NativeExtensionManager::LOAD_STATUS_FAILED) { + EditorNode::get_singleton()->add_io_error("Error loading extension: " + extensions_added[i]); + } else if (st == NativeExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + needs_restart = true; + } + } + for (int i = 0; i < extensions_removed.size(); i++) { + NativeExtensionManager::LoadStatus st = NativeExtensionManager::get_singleton()->unload_extension(extensions_removed[i]); + if (st == NativeExtensionManager::LOAD_STATUS_FAILED) { + EditorNode::get_singleton()->add_io_error("Error removing extension: " + extensions_added[i]); + } else if (st == NativeExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + needs_restart = true; + } + } + + return needs_restart; +} + void EditorFileSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem); ClassDB::bind_method(D_METHOD("is_scanning"), &EditorFileSystem::is_scanning); diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 9dce29d09c..b47cf5523a 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -255,6 +255,8 @@ class EditorFileSystem : public Node { static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate); + bool _scan_extensions(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 16151e36de..24b6ba1a14 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -30,6 +30,7 @@ #include "editor_help.h" +#include "core/core_constants.h" #include "core/input/input.h" #include "core/os/keyboard.h" #include "doc_data_compressed.gen.h" @@ -695,7 +696,7 @@ void EditorHelp::_update_doc() { class_desc->pop(); //cell } - if (m[i].description != "") { + if (m[i].description != "" || m[i].errors_returned.size() > 0) { method_descr = true; } @@ -1227,6 +1228,31 @@ void EditorHelp::_update_doc() { class_desc->push_color(text_color); class_desc->push_font(doc_font); class_desc->push_indent(1); + if (methods_filtered[i].errors_returned.size()) { + class_desc->append_bbcode(TTR("Error codes returned:")); + class_desc->add_newline(); + class_desc->push_list(0, RichTextLabel::LIST_DOTS, false); + for (int j = 0; j < methods_filtered[i].errors_returned.size(); j++) { + if (j > 0) { + class_desc->add_newline(); + } + int val = methods_filtered[i].errors_returned[j]; + String text = itos(val); + for (int k = 0; k < CoreConstants::get_global_constant_count(); k++) { + if (CoreConstants::get_global_constant_value(k) == val && CoreConstants::get_global_constant_enum(k) == SNAME("Error")) { + text = CoreConstants::get_global_constant_name(k); + break; + } + } + + class_desc->push_bold(); + class_desc->append_bbcode(text); + class_desc->pop(); + } + class_desc->pop(); + class_desc->add_newline(); + class_desc->add_newline(); + } if (!methods_filtered[i].description.strip_edges().is_empty()) { _add_text(DTR(methods_filtered[i].description)); } else { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 1c2b449449..2263f30931 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4800,6 +4800,32 @@ String EditorNode::get_run_playing_scene() const { return run_filename; } +void EditorNode::_immediate_dialog_confirmed() { + immediate_dialog_confirmed = true; +} +bool EditorNode::immediate_confirmation_dialog(const String &p_text, const String &p_ok_text, const String &p_cancel_text) { + ConfirmationDialog *cd = memnew(ConfirmationDialog); + cd->set_text(p_text); + cd->get_ok_button()->set_text(p_ok_text); + cd->get_cancel_button()->set_text(p_cancel_text); + cd->connect("confirmed", callable_mp(singleton, &EditorNode::_immediate_dialog_confirmed)); + singleton->gui_base->add_child(cd); + + cd->popup_centered(); + + while (true) { + OS::get_singleton()->delay_usec(1); + DisplayServer::get_singleton()->process_events(); + Main::iteration(); + if (singleton->immediate_dialog_confirmed || !cd->is_visible()) { + break; + } + } + + memdelete(cd); + return singleton->immediate_dialog_confirmed; +} + int EditorNode::get_current_tab() { return scene_tabs->get_current_tab(); } @@ -6793,7 +6819,6 @@ EditorNode::EditorNode() { preview_gen = memnew(AudioStreamPreviewGenerator); add_child(preview_gen); - //plugin stuff add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu))); add_editor_plugin(memnew(DebugAdapterServer())); diff --git a/editor/editor_node.h b/editor/editor_node.h index 5ff28f322a..bf26a6879b 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -92,6 +92,7 @@ class VSplitContainer; class Window; class SubViewport; class SceneImportSettings; +class EditorExtensionManager; class EditorNode : public Node { GDCLASS(EditorNode, Node); @@ -675,6 +676,9 @@ private: void _pick_main_scene_custom_action(const String &p_custom_action_name); + bool immediate_dialog_confirmed = false; + void _immediate_dialog_confirmed(); + protected: void _notification(int p_what); @@ -898,6 +902,8 @@ public: void run_stop(); bool is_run_playing() const; String get_run_playing_scene() const; + + static bool immediate_confirmation_dialog(const String &p_text, const String &p_ok_text = TTR("Ok"), const String &p_cancel_text = TTR("Cancel")); }; struct EditorProgress { |