diff options
-rw-r--r-- | core/debugger/local_debugger.cpp | 5 | ||||
-rw-r--r-- | core/input/input.cpp | 61 | ||||
-rw-r--r-- | core/input/input.h | 15 | ||||
-rw-r--r-- | doc/classes/Input.xml | 6 | ||||
-rw-r--r-- | doc/classes/ProjectSettings.xml | 5 | ||||
-rw-r--r-- | editor/icons/AudioStreamPolyphonic.svg | 1 | ||||
-rw-r--r-- | editor/project_converter_3_to_4.cpp | 15 | ||||
-rw-r--r-- | scene/gui/container.cpp | 2 | ||||
-rw-r--r-- | scene/gui/control.cpp | 60 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 16 | ||||
-rw-r--r-- | scene/gui/line_edit.h | 1 | ||||
-rw-r--r-- | scene/gui/spin_box.cpp | 2 |
12 files changed, 107 insertions, 82 deletions
diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index 623b1eb0ce..dc46ffc307 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -138,7 +138,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { // Cache options String variable_prefix = options["variable_prefix"]; - if (line.is_empty()) { + if (line.is_empty() && !feof(stdin)) { print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'"); print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'"); print_line("Enter \"help\" for assistance."); @@ -267,7 +267,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { print_line("Added breakpoint at " + source + ":" + itos(linenr)); } - } else if (line == "q" || line == "quit") { + } else if (line == "q" || line == "quit" || + (line.is_empty() && feof(stdin))) { // Do not stop again on quit script_debugger->clear_breakpoints(); script_debugger->set_depth(-1); diff --git a/core/input/input.cpp b/core/input/input.cpp index ca348c0c3c..b4945b41a8 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -297,10 +297,13 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con return false; } + // Backward compatibility for legacy behavior, only return true if currently pressed. + bool pressed_requirement = legacy_just_pressed_behavior ? E->value.pressed : true; + if (Engine::get_singleton()->is_in_physics_frame()) { - return E->value.pressed && E->value.physics_frame == Engine::get_singleton()->get_physics_frames(); + return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames(); } else { - return E->value.pressed && E->value.process_frame == Engine::get_singleton()->get_process_frames(); + return pressed_requirement && E->value.pressed_process_frame == Engine::get_singleton()->get_process_frames(); } } @@ -315,10 +318,13 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co return false; } + // Backward compatibility for legacy behavior, only return true if currently released. + bool released_requirement = legacy_just_pressed_behavior ? !E->value.pressed : true; + if (Engine::get_singleton()->is_in_physics_frame()) { - return !E->value.pressed && E->value.physics_frame == Engine::get_singleton()->get_physics_frames(); + return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames(); } else { - return !E->value.pressed && E->value.process_frame == Engine::get_singleton()->get_process_frames(); + return released_requirement && E->value.released_process_frame == Engine::get_singleton()->get_process_frames(); } } @@ -686,19 +692,24 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) { if (InputMap::get_singleton()->event_is_action(p_event, E.key)) { + Action &action = action_state[E.key]; // If not echo and action pressed state has changed if (!p_event->is_echo() && is_action_pressed(E.key, false) != p_event->is_action_pressed(E.key)) { - Action action; - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.process_frame = Engine::get_singleton()->get_process_frames(); - action.pressed = p_event->is_action_pressed(E.key); + if (p_event->is_action_pressed(E.key)) { + action.pressed = true; + action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } else { + action.pressed = false; + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); + } action.strength = 0.0f; action.raw_strength = 0.0f; action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); - action_state[E.key] = action; } - action_state[E.key].strength = p_event->get_action_strength(E.key); - action_state[E.key].raw_strength = p_event->get_action_raw_strength(E.key); + action.strength = p_event->get_action_strength(E.key); + action.raw_strength = p_event->get_action_raw_strength(E.key); } } @@ -813,29 +824,27 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con } void Input::action_press(const StringName &p_action, float p_strength) { - Action action; + // Create or retrieve existing action. + Action &action = action_state[p_action]; - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.process_frame = Engine::get_singleton()->get_process_frames(); + action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = true; action.strength = p_strength; action.raw_strength = p_strength; action.exact = true; - - action_state[p_action] = action; } void Input::action_release(const StringName &p_action) { - Action action; + // Create or retrieve existing action. + Action &action = action_state[p_action]; - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.process_frame = Engine::get_singleton()->get_process_frames(); + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = false; - action.strength = 0.f; - action.raw_strength = 0.f; + action.strength = 0.0f; + action.raw_strength = 0.0f; action.exact = true; - - action_state[p_action] = action; } void Input::set_emulate_touch_from_mouse(bool p_emulate) { @@ -1532,6 +1541,12 @@ Input::Input() { parse_mapping(entries[i]); } } + + legacy_just_pressed_behavior = GLOBAL_DEF("input_devices/compatibility/legacy_just_pressed_behavior", false); + if (Engine::get_singleton()->is_editor_hint()) { + // Always use standard behaviour in the editor. + legacy_just_pressed_behavior = false; + } } Input::~Input() { diff --git a/core/input/input.h b/core/input/input.h index c254650ef8..9cc596ee90 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -96,14 +96,17 @@ private: Vector3 gyroscope; Vector2 mouse_pos; int64_t mouse_window = 0; + bool legacy_just_pressed_behavior = false; struct Action { - uint64_t physics_frame; - uint64_t process_frame; - bool pressed; - bool exact; - float strength; - float raw_strength; + uint64_t pressed_physics_frame = UINT64_MAX; + uint64_t pressed_process_frame = UINT64_MAX; + uint64_t released_physics_frame = UINT64_MAX; + uint64_t released_process_frame = UINT64_MAX; + bool pressed = false; + bool exact = true; + float strength = 0.0f; + float raw_strength = 0.0f; }; HashMap<StringName, Action> action_state; diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a5c7925d17..fab4d2385b 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -179,9 +179,10 @@ <param index="0" name="action" type="StringName" /> <param index="1" name="exact_match" type="bool" default="false" /> <description> - Returns [code]true[/code] when the user starts pressing the action event, meaning it's [code]true[/code] only on the frame that the user pressed down the button. + Returns [code]true[/code] when the user has [i]started[/i] pressing the action event in the current frame or physics tick. It will only return [code]true[/code] on the frame or tick that the user pressed down the button. This is useful for code that needs to run only once when an action is pressed, instead of every frame while it's pressed. If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. + [b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] pressed. An action can be pressed and released again rapidly, and [code]true[/code] will still be returned so as not to miss input. [b]Note:[/b] Due to keyboard ghosting, [method is_action_just_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information. </description> </method> @@ -190,7 +191,8 @@ <param index="0" name="action" type="StringName" /> <param index="1" name="exact_match" type="bool" default="false" /> <description> - Returns [code]true[/code] when the user stops pressing the action event, meaning it's [code]true[/code] only on the frame that the user released the button. + Returns [code]true[/code] when the user [i]stops[/i] pressing the action event in the current frame or physics tick. It will only return [code]true[/code] on the frame or tick that the user releases the button. + [b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] not pressed. An action can be released and pressed again rapidly, and [code]true[/code] will still be returned so as not to miss input. If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 1a79146f96..31cc3d9386 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1265,6 +1265,11 @@ Enabling this can greatly improve the responsiveness to input, specially in devices that need to run multiple physics frames per visible (process) frame, because they can't run at the target frame rate. [b]Note:[/b] Currently implemented only on Android. </member> + <member name="input_devices/compatibility/legacy_just_pressed_behavior" type="bool" setter="" getter="" default="false"> + If [code]true[/code], [method Input.is_action_just_pressed] and [method Input.is_action_just_released] will only return [code]true[/code] if the action is still in the respective state, i.e. an action that is pressed [i]and[/i] released on the same frame will be missed. + If [code]false[/code], no input will be lost. + [b]Note:[/b] You should in nearly all cases prefer the [code]false[/code] setting. The legacy behavior is to enable supporting old projects that rely on the old logic, without changes to script. + </member> <member name="input_devices/pen_tablet/driver" type="String" setter="" getter=""> Specifies the tablet driver to use. If left empty, the default driver will be used. [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. diff --git a/editor/icons/AudioStreamPolyphonic.svg b/editor/icons/AudioStreamPolyphonic.svg new file mode 100644 index 0000000000..3dd80d6373 --- /dev/null +++ b/editor/icons/AudioStreamPolyphonic.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M12 1 4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1V2a1 1 0 0 0-1-1zm-2 7h2v2h2v2h-2v2h-2v-2H8v-2h2z" fill="url(#a)"/></svg> diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 4455cb8f93..ed0c373ade 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -946,10 +946,6 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { valid = valid && test_conversion_gdscript_builtin("func _init(a: int).(d,e,f) -> void:", "func _init(a: int) -> void:\n\tsuper(d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("assert(speed < 20, str(randi()%10))", "assert(speed < 20) #,str(randi()%10))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("assert(speed < 2)", "assert(speed < 2)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("assert(false, \"Missing type --\" + str(argument.type) + \"--, needs to be added to project\")", "assert(false) #,\"Missing type --\" + str(argument.type) + \"--, needs to be added to project\")", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("create_from_image(aa, bb)", "create_from_image(aa) #,bb", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("q_ImageTexture.create_from_image(variable1, variable2)", "q_ImageTexture.create_from_image(variable1) #,variable2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -2037,17 +2033,6 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai } } - // assert(speed < 20, str(randi()%10)) -> assert(speed < 20) #,str(randi()%10)) GDScript - GDScript bug constant message - if (line.contains("assert(")) { - int start = line.find("assert("); - int end = get_end_parenthesis(line.substr(start)) + 1; - if (end > -1) { - Vector<String> parts = parse_arguments(line.substr(start, end)); - if (parts.size() == 2) { - line = line.substr(0, start) + "assert(" + parts[0] + ") " + line.substr(end + start) + "#," + parts[1] + ")"; - } - } - } // create_from_image(aa, bb) -> create_from_image(aa) #, bb ImageTexture if (line.contains("create_from_image(")) { int start = line.find("create_from_image("); diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 2abd8cdf03..4e23db4cae 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -34,8 +34,6 @@ #include "scene/scene_string_names.h" void Container::_child_minsize_changed() { - //Size2 ms = get_combined_minimum_size(); - //if (ms.width > get_size().width || ms.height > get_size().height) { update_minimum_size(); queue_sort(); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 108d8dd09e..78862364d5 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1579,12 +1579,28 @@ Vector2 Control::get_pivot_offset() const { void Control::_update_minimum_size() { if (!is_inside_tree()) { + data.updating_last_minimum_size = false; return; } - Control *invalidate = this; + Size2 minsize = get_combined_minimum_size(); + data.updating_last_minimum_size = false; + + if (minsize != data.last_minimum_size) { + data.last_minimum_size = minsize; + _size_changed(); + emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); + } +} + +void Control::update_minimum_size() { + ERR_MAIN_THREAD_GUARD; + if (!is_inside_tree() || data.block_minimum_size_adjust) { + return; + } // Invalidate cache upwards. + Control *invalidate = this; while (invalidate && invalidate->data.minimum_size_valid) { invalidate->data.minimum_size_valid = false; if (invalidate->is_set_as_top_level()) { @@ -1604,28 +1620,12 @@ void Control::_update_minimum_size() { return; } - Size2 minsize = get_combined_minimum_size(); - data.updating_last_minimum_size = false; - if (minsize != data.last_minimum_size) { - data.last_minimum_size = minsize; - _size_changed(); - emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); - } -} - -void Control::update_minimum_size() { - ERR_MAIN_THREAD_GUARD; - if (!is_inside_tree() || data.block_minimum_size_adjust) { - return; - } - if (data.updating_last_minimum_size) { return; } - data.updating_last_minimum_size = true; - MessageQueue::get_singleton()->push_call(this, "_update_minimum_size"); + MessageQueue::get_singleton()->push_callable(callable_mp(this, &Control::_update_minimum_size)); } void Control::set_block_minimum_size_adjust(bool p_block) { @@ -1665,17 +1665,8 @@ void Control::_update_minimum_size_cache() { minsize.x = MAX(minsize.x, data.custom_minimum_size.x); minsize.y = MAX(minsize.y, data.custom_minimum_size.y); - bool size_changed = false; - if (data.minimum_size_cache != minsize) { - size_changed = true; - } - data.minimum_size_cache = minsize; data.minimum_size_valid = true; - - if (size_changed) { - update_minimum_size(); - } } Size2 Control::get_combined_minimum_size() const { @@ -3128,6 +3119,7 @@ void Control::_notification(int p_notification) { case NOTIFICATION_POST_ENTER_TREE: { data.is_rtl_dirty = true; + update_minimum_size(); _size_changed(); } break; @@ -3243,10 +3235,13 @@ void Control::_notification(int p_notification) { case NOTIFICATION_THEME_CHANGED: { emit_signal(SceneStringNames::get_singleton()->theme_changed); + _invalidate_theme_cache(); _update_theme_item_cache(); - update_minimum_size(); queue_redraw(); + + update_minimum_size(); + _size_changed(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -3255,7 +3250,8 @@ void Control::_notification(int p_notification) { get_viewport()->_gui_hide_control(this); } } else { - _update_minimum_size(); + update_minimum_size(); + _size_changed(); } } break; @@ -3263,8 +3259,12 @@ void Control::_notification(int p_notification) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { if (is_inside_tree()) { data.is_rtl_dirty = true; + _invalidate_theme_cache(); _update_theme_item_cache(); + queue_redraw(); + + update_minimum_size(); _size_changed(); } } break; @@ -3272,8 +3272,6 @@ void Control::_notification(int p_notification) { } void Control::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_minimum_size"), &Control::_update_minimum_size); - ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event); ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size); ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index cf2bbcb0a1..a73e952eca 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1527,6 +1527,22 @@ void LineEdit::set_text(String p_text) { scroll_offset = 0.0; } +void LineEdit::set_text_with_selection(const String &p_text) { + Selection selection_copy = selection; + + clear_internal(); + insert_text_at_caret(p_text); + _create_undo_state(); + + int tlen = text.length(); + selection = selection_copy; + selection.begin = MIN(selection.begin, tlen); + selection.end = MIN(selection.end, tlen); + selection.start_column = MIN(selection.start_column, tlen); + + queue_redraw(); +} + void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index c7ea16b4c8..8acb4896c5 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -283,6 +283,7 @@ public: void set_text(String p_text); String get_text() const; + void set_text_with_selection(const String &p_text); // Set text, while preserving selection. void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 4f4754add5..7cb54f24ea 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -54,7 +54,7 @@ void SpinBox::_update_text() { } } - line_edit->set_text(value); + line_edit->set_text_with_selection(value); } void SpinBox::_text_submitted(const String &p_string) { |