Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET: Add a warning in the inspector when properties might be out of sync #85869

Merged

Conversation

paulloz
Copy link
Member

@paulloz paulloz commented Dec 6, 2023

Add a small warning in the inspector when C# scripts are out of sync with data we gathered during compilation. The warning is displayed if any C# script in the class hierarchy was modified since the last compilation, and disappears as soon as possible.

Sadly, the inspector is not refreshed ATM when the window is refocused (e.g. if you tab back from your external code editor). From my tests, this behaviour is not language-dependent, so it'd probably warrant a fix higher up (therefore, I didn't look how to do so in this PR).

I gladly take any suggestion for UI and wording. E.g. someone suggested to directly add a button/link to build directly from the warning. But I'm not sure how I feel about that.

godot windows editor dev x86_64 mono_ElbJvjZ2Lp

@paulloz paulloz requested a review from a team as a code owner December 6, 2023 22:23
@paulloz paulloz changed the title Inspector ⚠️ when C# props might be out of date .NET: Add a warning in the inspector when properties might be out of sync Dec 6, 2023
@raulsntos raulsntos added this to the 4.3 milestone Dec 7, 2023
Copy link
Member

@raulsntos raulsntos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for working on this.

modules/mono/.editorconfig Outdated Show resolved Hide resolved
@paulloz paulloz force-pushed the dotnet-warning-out-of-date-properties branch from 74176e6 to 39e1fec Compare December 17, 2023 09:30
@raulsntos
Copy link
Member

raulsntos commented Dec 17, 2023

Sadly, the inspector is not refreshed ATM when the window is refocused

That's really unfortunate. When modifying a GDScript in an external editor the inspector updates when refocusing the Godot editor, but I couldn't find any calls to InspectorDock::get_inspector_singleton()->update_tree(); in the GDScript module.

It's probably triggered somewhere in GDScript::_update_exports, maybe we could do the same in CSharpScript::_update_exports.

@paulloz
Copy link
Member Author

paulloz commented Dec 17, 2023

When modifying a GDScript in an external editor the inspector updates when refocusing the Godot editor

Uuh, you're right. I'm positive I tried it out and didn't see it refresh 🤔 Looking at it now, it comes from GDScript::update_exports() called by ScriptEditor::_update_modified_scripts_for_external_editor().

This does the trick, but I'm not entirely sure of all the potential implications. The idea being: when we're not sure something changed or not, consider it might have, and call notify_property_list_change() (which, in turn, queue an update).

diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 8e1587997b..933d0a0f84 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2246,6 +2246,13 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
                        } else {
                                p_instance_to_update->update(propnames, values);
                        }
+               } else if (placeholders.size()) {
+                       for (PlaceHolderScriptInstance *instance : placeholders) {
+                               Object *owner = instance->get_owner();
+                               if (owner->get_script_instance() == instance) {
+                                       owner->notify_property_list_changed();
+                               }
+                       }
                }
        }
 #endif

@raulsntos
Copy link
Member

It'd be nice to notify only if the file last modified date is newer than the last valid build time, so maybe something like this:

diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 8e1587997b..9759db5a0a 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2246,6 +2246,18 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
 			} else {
 				p_instance_to_update->update(propnames, values);
 			}
+		} else if (placeholders.size()) {
+			uint64_t script_modified_time = FileAccess::get_modified_time(get_path());
+			uint64_t last_valid_build_time = FileAccess::get_modified_time(GodotSharpDirs::get_res_temp_assemblies_dir().path_join(path::get_csharp_project_name() + ".dll"));
+
+			if (script_modified_time > last_valid_build_time) {
+				for (PlaceHolderScriptInstance *instance : placeholders) {
+					Object *owner = instance->get_owner();
+					if (owner->get_script_instance() == instance) {
+						owner->notify_property_list_changed();
+					}
+				}
+			}
 		}
 	}
 #endif

I'm not sure if notify_property_list_change() is really the right thing to use here though. Maybe instead of iterating the placeholders, we could just call InspectorDock::get_inspector_singleton()->update_tree(); like the editor does when a GDExtension is reloaded:

void EditorNode::_gdextensions_reloaded() {
// In case the developer is inspecting an object that will be changed by the reload.
InspectorDock::get_inspector_singleton()->update_tree();
}

We also do that when reloading C# assemblies:

// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
InspectorDock::get_inspector_singleton()->update_tree();
NodeDock::get_singleton()->update_lists();
}

@paulloz
Copy link
Member Author

paulloz commented Dec 17, 2023

Yeah, I was pondering the call to update_tree() directly because I don't really know if there's a risk we end up calling that a bunch? Vs. notifying the change where it only dirties a flag, and the inspector will update only once. But if it's not a concern I 100% agree,

@YuriSizov
Copy link
Contributor

YuriSizov commented Dec 18, 2023

As a rule of thumb you should never call update_tree manually. It should be done as little as possible. Calling notify_property_list_changed() is absolutely fine.

@paulloz
Copy link
Member Author

paulloz commented Dec 19, 2023

It's probably triggered somewhere in GDScript::_update_exports, maybe we could do the same in CSharpScript::_update_exports.

Humpf, actually the issue is this is only triggered when text_editor/external/use_external_editor is set, but we use dotnet/editor/external_editor for C#.

@paulloz
Copy link
Member Author

paulloz commented Dec 21, 2023

Yeah, not sure what the best way to handle this is. Should we have our own version of what's done in ScriptEditor::_update_modified_scripts_for_external_editor to update even when this setting is unset?

@raulsntos
Copy link
Member

For me I have both settings unset and it seems to work, I guess it's because I have the C# script open in the Godot editor. But you are right that if I have text_editor/external/use_external_editor unset and dotnet/editor/external_editor set it won't update the inspector on refocusing the Godot window, every other combination seems to work.

Should we have our own version of what's done in ScriptEditor::_update_modified_scripts_for_external_editor to update even when this setting is unset?

Maybe we can just change ScriptEditor::_update_modified_scripts_for_external_editor to also consider scr->get_language()->overrides_external_editor() (like ScriptEditor::edit does).

@paulloz paulloz requested a review from a team as a code owner December 22, 2023 17:41
Copy link
Member

@raulsntos raulsntos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me and works as expected. Thanks again for contributing to the usability of the .NET module.

@paulloz paulloz force-pushed the dotnet-warning-out-of-date-properties branch from 684aa68 to 0818d01 Compare December 22, 2023 22:32
@paulloz
Copy link
Member Author

paulloz commented Dec 22, 2023

Last push is simply squashing all commits, and using TTR() on the string used in UI.

@raulsntos
Copy link
Member

raulsntos commented Dec 22, 2023

using TTR() on the string used in UI.

That shouldn't be necessary, Label auto translates the Text property1.

xl_text = atr(p_string);

Footnotes

  1. For more information, see the documentation: https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html#converting-keys-to-text

@paulloz
Copy link
Member Author

paulloz commented Dec 23, 2023

Urh, sorry, I thought it was necessary for the key to be extracted in the first place.

@raulsntos
Copy link
Member

I don't think extracting works for C# either way, so all the other strings that we have with TTR() are probably not translated either. For example, I couldn't find the string Generating solution... in Weblate.

Maybe these strings need to be added to https://github.com/godotengine/godot-editor-l10n manually or implement C# support in extract_editor.py.

@paulloz
Copy link
Member Author

paulloz commented Dec 23, 2023

So I guess having the TTR() call here is kinda future-proofing for when we add C# support to the extraction script 😅

@akien-mga akien-mga merged commit 11d1844 into godotengine:master Jan 3, 2024
15 checks passed
@akien-mga
Copy link
Member

Thanks!

@paulloz paulloz deleted the dotnet-warning-out-of-date-properties branch January 5, 2024 09:32
@molingyu
Copy link

Can an editor option be provided so that the project can automatically perform C# compilation after the file is changed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants