Skip to content

Commit dcfeebf

Browse files
committed
implement addition signal for individual project setting change
1 parent d7382aa commit dcfeebf

File tree

3 files changed

+175
-2
lines changed

3 files changed

+175
-2
lines changed

core/config/project_settings.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ String ProjectSettings::globalize_path(const String &p_path) const {
277277
bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
278278
_THREAD_SAFE_METHOD_
279279

280+
// Capture old value before making changes
281+
Variant old_value;
282+
if (props.has(p_name)) {
283+
old_value = props[p_name].variant;
284+
}
285+
280286
if (p_value.get_type() == Variant::NIL) {
281287
props.erase(p_name);
282288
if (p_name.operator String().begins_with("autoload/")) {
@@ -298,7 +304,10 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
298304
}
299305

300306
_version++;
301-
_queue_changed();
307+
308+
if (old_value != p_value) {
309+
_queue_changed(p_name, old_value, p_value);
310+
}
302311
return true;
303312
}
304313

@@ -344,7 +353,10 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
344353
}
345354

346355
_version++;
347-
_queue_changed();
356+
357+
if (old_value != p_value) {
358+
_queue_changed(p_name, old_value, p_value);
359+
}
348360
return true;
349361
}
350362

@@ -528,12 +540,28 @@ void ProjectSettings::_queue_changed() {
528540
callable_mp(this, &ProjectSettings::_emit_changed).call_deferred();
529541
}
530542

543+
void ProjectSettings::_queue_changed(const StringName &p_name, const Variant &p_old_value, const Variant &p_new_value) {
544+
if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) {
545+
return;
546+
}
547+
is_changed = true;
548+
pending_signal_name = p_name;
549+
pending_signal_old_value = p_old_value;
550+
pending_signal_new_value = p_new_value;
551+
callable_mp(this, &ProjectSettings::_emit_changed).call_deferred();
552+
}
553+
531554
void ProjectSettings::_emit_changed() {
532555
if (!is_changed) {
533556
return;
534557
}
535558
is_changed = false;
536559
emit_signal("settings_changed");
560+
561+
if (!pending_signal_name.is_empty()) {
562+
emit_signal("setting_changed", pending_signal_name, pending_signal_old_value, pending_signal_new_value);
563+
pending_signal_name = StringName();
564+
}
537565
}
538566

539567
bool ProjectSettings::load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) {
@@ -1510,6 +1538,7 @@ void ProjectSettings::_bind_methods() {
15101538
ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
15111539

15121540
ADD_SIGNAL(MethodInfo("settings_changed"));
1541+
ADD_SIGNAL(MethodInfo("setting_changed", PropertyInfo(Variant::STRING_NAME, "name"), PropertyInfo(Variant::NIL, "old_value"), PropertyInfo(Variant::NIL, "new_value")));
15131542
}
15141543

15151544
void ProjectSettings::_add_builtin_input_map() {

core/config/project_settings.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class ProjectSettings : public Object {
4646
// and will always detect the initial project settings as a "change".
4747
uint32_t _version = 1;
4848

49+
// Pending signal data for the new setting_changed signal
50+
StringName pending_signal_name;
51+
Variant pending_signal_old_value;
52+
Variant pending_signal_new_value;
53+
4954
public:
5055
typedef HashMap<String, Variant> CustomMap;
5156
static inline const String PROJECT_DATA_DIR_NAME_SUFFIX = "godot";
@@ -119,6 +124,7 @@ class ProjectSettings : public Object {
119124
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
120125

121126
void _queue_changed();
127+
void _queue_changed(const StringName &p_name, const Variant &p_old_value, const Variant &p_new_value);
122128
void _emit_changed();
123129

124130
static inline ProjectSettings *singleton = nullptr;

tests/core/config/test_project_settings.h

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,142 @@ TEST_CASE("[ProjectSettings] localize_path") {
155155
TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path;
156156
}
157157

158+
TEST_CASE("[SceneTree][ProjectSettings] settings_changed signal") {
159+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
160+
161+
ProjectSettings::get_singleton()->set_setting("test_signal_setting", "test_value");
162+
MessageQueue::get_singleton()->flush();
163+
164+
SIGNAL_CHECK("settings_changed", { {} });
165+
166+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
167+
}
168+
169+
TEST_CASE("[SceneTree][ProjectSettings] setting_changed signal for new setting") {
170+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
171+
172+
String setting_name = "test_new_setting";
173+
String new_value = "new_value";
174+
175+
CHECK_FALSE(ProjectSettings::get_singleton()->has_setting(setting_name));
176+
177+
SIGNAL_DISCARD("setting_changed");
178+
179+
ProjectSettings::get_singleton()->set_setting(setting_name, new_value);
180+
MessageQueue::get_singleton()->flush();
181+
182+
Array expected_args;
183+
expected_args.push_back(StringName(setting_name));
184+
expected_args.push_back(Variant());
185+
expected_args.push_back(new_value);
186+
Array signal_args;
187+
signal_args.push_back(expected_args);
188+
SIGNAL_CHECK("setting_changed", signal_args);
189+
190+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
191+
}
192+
193+
TEST_CASE("[SceneTree][ProjectSettings] setting_changed signal with parameters") {
194+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
195+
196+
String setting_name = "test_old_value_signal";
197+
String old_value = "old_value";
198+
String new_value = "new_value";
199+
200+
ProjectSettings::get_singleton()->set_setting(setting_name, old_value);
201+
MessageQueue::get_singleton()->flush();
202+
203+
SIGNAL_DISCARD("setting_changed");
204+
205+
ProjectSettings::get_singleton()->set_setting(setting_name, new_value);
206+
MessageQueue::get_singleton()->flush();
207+
208+
Array expected_args;
209+
expected_args.push_back(StringName(setting_name));
210+
expected_args.push_back(old_value);
211+
expected_args.push_back(new_value);
212+
Array signal_args;
213+
signal_args.push_back(expected_args);
214+
SIGNAL_CHECK("setting_changed", signal_args);
215+
216+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
217+
}
218+
219+
TEST_CASE("[SceneTree][ProjectSettings] setting_changed signal for setting removal") {
220+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
221+
222+
String setting_name = "test_removal_setting";
223+
String initial_value = "initial_value";
224+
225+
ProjectSettings::get_singleton()->set_setting(setting_name, initial_value);
226+
MessageQueue::get_singleton()->flush();
227+
SIGNAL_DISCARD("setting_changed");
228+
229+
ProjectSettings::get_singleton()->set_setting(setting_name, Variant());
230+
MessageQueue::get_singleton()->flush();
231+
232+
Array expected_args;
233+
expected_args.push_back(StringName(setting_name));
234+
expected_args.push_back(initial_value);
235+
expected_args.push_back(Variant());
236+
Array signal_args;
237+
signal_args.push_back(expected_args);
238+
SIGNAL_CHECK("setting_changed", signal_args);
239+
240+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
241+
}
242+
243+
TEST_CASE("[SceneTree][ProjectSettings] Both signals emitted together") {
244+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
245+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
246+
247+
String setting_name = "test_both_signals";
248+
String old_value = "old_both";
249+
String new_value = "new_both";
250+
251+
ProjectSettings::get_singleton()->set_setting(setting_name, old_value);
252+
MessageQueue::get_singleton()->flush();
253+
SIGNAL_DISCARD("settings_changed");
254+
SIGNAL_DISCARD("setting_changed");
255+
256+
ProjectSettings::get_singleton()->set_setting(setting_name, new_value);
257+
MessageQueue::get_singleton()->flush();
258+
259+
SIGNAL_CHECK("settings_changed", { {} });
260+
261+
Array expected_args;
262+
expected_args.push_back(StringName(setting_name));
263+
expected_args.push_back(old_value);
264+
expected_args.push_back(new_value);
265+
Array signal_args;
266+
signal_args.push_back(expected_args);
267+
SIGNAL_CHECK("setting_changed", signal_args);
268+
269+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
270+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
271+
}
272+
273+
TEST_CASE("[SceneTree][ProjectSettings] No signals when setting same value") {
274+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
275+
SIGNAL_WATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
276+
277+
String setting_name = "test_same_value";
278+
String test_value = "same_value";
279+
280+
ProjectSettings::get_singleton()->set_setting(setting_name, test_value);
281+
MessageQueue::get_singleton()->flush();
282+
SIGNAL_DISCARD("settings_changed");
283+
SIGNAL_DISCARD("setting_changed");
284+
285+
// Set the same value again. This should not trigger any signals.
286+
ProjectSettings::get_singleton()->set_setting(setting_name, test_value);
287+
MessageQueue::get_singleton()->flush();
288+
289+
SIGNAL_CHECK_FALSE("settings_changed");
290+
SIGNAL_CHECK_FALSE("setting_changed");
291+
292+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("settings_changed"));
293+
SIGNAL_UNWATCH(ProjectSettings::get_singleton(), SNAME("setting_changed"));
294+
}
295+
158296
} // namespace TestProjectSettings

0 commit comments

Comments
 (0)