diff --git a/core/input/input.cpp b/core/input/input.cpp index eba7ded267ba..064d7d495377 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1068,6 +1068,20 @@ void Input::flush_frame_parsed_events() { } #endif +void Input::clear_mouse_axes_action_state() { + for (const KeyValue &E : InputMap::get_singleton()->get_action_map()) { + const List> *inputs = InputMap::get_singleton()->action_get_events(E.key); + if (inputs) { + for (const Ref &input : *inputs) { + Ref mm = input; + if (mm.is_valid()) { + action_release(E.key); + } + } + } + } +} + void Input::flush_buffered_events() { _THREAD_SAFE_METHOD_ diff --git a/core/input/input.h b/core/input/input.h index 95dd623cc032..e979fc9c83d7 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -370,6 +370,7 @@ class Input : public Object { #ifdef DEBUG_ENABLED void flush_frame_parsed_events(); #endif + void clear_mouse_axes_action_state(); void flush_buffered_events(); bool is_agile_input_event_flushing(); void set_agile_input_event_flushing(bool p_enable); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index de3efa7a3a4f..d20ba3cc103d 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -977,6 +977,40 @@ Ref InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co return mm; } +bool InputEventMouseMotion::action_match(const Ref &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { + Ref mm = p_event; + if (mm.is_null()) { + return false; + } + + Vector2 action_rel = get_relative(); + Vector2 event_rel = mm->get_relative(); + + if (event_rel.dot(action_rel) <= 0) { + return false; + } + + Vector2 action_value = action_rel * event_rel; + float strength = action_value.x + action_value.y; + + if (r_pressed != nullptr) { + *r_pressed = strength != 0; + } + if (r_strength != nullptr) { + *r_strength = strength; + } + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; + } + + return true; +} + +bool InputEventMouseMotion::is_match(const Ref &p_event, bool p_exact_match) const { + Ref mm = p_event; + return mm.is_valid() && mm->get_relative().dot(get_relative()) > 0; +} + String InputEventMouseMotion::as_text() const { return vformat(RTR("Mouse motion at position (%s) with velocity (%s)"), String(get_position()), String(get_velocity())); } diff --git a/core/input/input_event.h b/core/input/input_event.h index 19176f748e92..c063a4695b7a 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -302,6 +302,10 @@ class InputEventMouseMotion : public InputEventMouse { Vector2 get_screen_velocity() const; virtual Ref xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; + + virtual bool action_match(const Ref &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; + virtual bool is_match(const Ref &p_event, bool p_exact_match = true) const override; + virtual String as_text() const override; virtual String to_string() override; diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index a6b30233fc20..562a0a0b5ed7 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -61,6 +61,7 @@ String EventListenerLineEdit::get_event_text(const Ref &p_event, boo String text; Ref key = p_event; + Ref mouse_motion = p_event; if (key.is_valid()) { String mods_text = key->InputEventWithModifiers::as_text(); mods_text = mods_text.is_empty() ? mods_text : mods_text + "+"; @@ -95,6 +96,17 @@ String EventListenerLineEdit::get_event_text(const Ref &p_event, boo if (text.is_empty()) { text = "(" + TTR("Unset") + ")"; } + } else if (mouse_motion.is_valid()) { + Vector2 rel = mouse_motion->get_relative(); + if (rel.x < 0) { + text = TTR("Mouse X (Left)"); + } else if (rel.x > 0) { + text = TTR("Mouse X (Right)"); + } else if (rel.y < 0) { + text = TTR("Mouse Y (Up)"); + } else { + text = TTR("Mouse Y (Down)"); + } } else { text = p_event->as_text(); } diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h index aa0cc91d4725..2009268568e7 100644 --- a/editor/event_listener_line_edit.h +++ b/editor/event_listener_line_edit.h @@ -37,13 +37,14 @@ enum InputType { INPUT_KEY = 1, INPUT_MOUSE_BUTTON = 2, INPUT_JOY_BUTTON = 4, - INPUT_JOY_MOTION = 8 + INPUT_JOY_MOTION = 8, + INPUT_MOUSE_MOTION = 16, }; class EventListenerLineEdit : public LineEdit { GDCLASS(EventListenerLineEdit, LineEdit) - int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; + int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION | INPUT_MOUSE_MOTION; bool ignore_next_event = true; bool share_keycodes = false; Ref event; diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index dc839b02f663..e4786d824963 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -56,6 +56,7 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event, c Ref k = p_event; Ref mb = p_event; + Ref mm = p_event; Ref joyb = p_event; Ref joym = p_event; Ref mod = p_event; @@ -76,6 +77,10 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event, c autoremap_command_or_control_checkbox->set_pressed(mod->is_command_or_control_autoremap()); } + if (mm.is_valid()) { + show_mods = false; + } + if (k.is_valid()) { show_key = true; Key phys_key = k->get_physical_keycode(); @@ -110,7 +115,7 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event, c device_container->set_visible(show_device); key_mode->set_visible(show_key); location_container->set_visible(show_location); - additional_options_container->show(); + additional_options_container->set_visible(show_mods or show_device or show_key or show_location); // Update mode selector based on original key event. Ref ko = p_original_event; @@ -148,7 +153,7 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event, c } // Update selected item in input list. - if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) { + if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid() || mm.is_valid())) { in_tree_update = true; TreeItem *category = input_list_tree->get_root()->get_first_child(); while (category) { @@ -170,7 +175,8 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event, c bool joyb_match = joyb.is_valid() && Variant(joyb->get_button_index()) == input_item->get_meta("__index"); bool joym_match = joym.is_valid() && Variant(joym->get_axis()) == input_item->get_meta("__axis") && joym->get_axis_value() == (float)input_item->get_meta("__value"); bool mb_match = mb.is_valid() && Variant(mb->get_button_index()) == input_item->get_meta("__index"); - if (key_match || joyb_match || joym_match || mb_match) { + bool mm_match = mm.is_valid() && mm->get_relative()[input_item->get_meta("__axis")] != 0; + if (key_match || joyb_match || joym_match || mb_match || mm_match) { category->set_collapsed(false); input_item->select(0); input_list_tree->ensure_cursor_is_visible(); @@ -319,6 +325,35 @@ void InputEventConfigurationDialog::_update_input_list() { } } + if (allowed_input_types & INPUT_MOUSE_MOTION) { + TreeItem *mouse_motion_root = input_list_tree->create_item(root); + mouse_motion_root->set_text(0, TTR("Mouse Axes")); + mouse_motion_root->set_icon(0, icon_cache.mouse); + mouse_motion_root->set_collapsed(collapse); + mouse_motion_root->set_meta("__type", INPUT_MOUSE_MOTION); + + for (int i = 0; i < 4; i++) { + int axis = i / 2; + int direction = (i & 1) ? 1 : -1; + + String desc; + if (axis == 0) { + desc = direction == 1 ? TTR("Mouse X (Right)") : TTR("Mouse X (Left)"); + } else { + desc = direction == 1 ? TTR("Mouse Y (Down)") : TTR("Mouse Y (Up)"); + } + + if (!search_term.is_empty() && desc.findn(search_term) == -1) { + continue; + } + + TreeItem *item = input_list_tree->create_item(mouse_motion_root); + item->set_text(0, desc); + item->set_meta("__axis", axis); + item->set_meta("__value", direction); + } + } + if (allowed_input_types & INPUT_JOY_BUTTON) { TreeItem *joyb_root = input_list_tree->create_item(root); joyb_root->set_text(0, TTR("Joypad Buttons")); @@ -524,6 +559,21 @@ void InputEventConfigurationDialog::_input_list_item_selected() { _set_event(mb, mb, false); } break; + case INPUT_MOUSE_MOTION: { + int axis = selected->get_meta("__axis"); + int value = selected->get_meta("__value"); + + Ref mm; + mm.instantiate(); + Vector2 rel(0, 0); + rel[axis] = value; + mm->set_relative(rel); + + // Maintain selected device. + mm->set_device(_get_current_device()); + + _set_event(mm, mm, false); + } break; case INPUT_JOY_BUTTON: { JoyButton idx = (JoyButton)(int)selected->get_meta("__index"); Ref jb = InputEventJoypadButton::create_reference(idx); @@ -635,7 +685,7 @@ void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) { } InputEventConfigurationDialog::InputEventConfigurationDialog() { - allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; + allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_MOUSE_MOTION | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; set_min_size(Size2i(550, 0) * EDSCALE); diff --git a/main/main.cpp b/main/main.cpp index f82df786bc25..f4a5b54a74af 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -4317,35 +4317,36 @@ bool Main::iteration() { } #endif - if (fixed_fps != -1) { - return exit; - } - - OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw()); + if (fixed_fps == -1) { + OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw()); #ifdef TOOLS_ENABLED - if (auto_build_solutions) { - auto_build_solutions = false; - // Only relevant when running the editor. - if (!editor) { - OS::get_singleton()->set_exit_code(EXIT_FAILURE); - ERR_FAIL_V_MSG(true, - "Command line option --build-solutions was passed, but no project is being edited. Aborting."); - } - if (!EditorNode::get_singleton()->call_build()) { - OS::get_singleton()->set_exit_code(EXIT_FAILURE); - ERR_FAIL_V_MSG(true, - "Command line option --build-solutions was passed, but the build callback failed. Aborting."); + if (auto_build_solutions) { + auto_build_solutions = false; + // Only relevant when running the editor. + if (!editor) { + OS::get_singleton()->set_exit_code(EXIT_FAILURE); + ERR_FAIL_V_MSG(true, + "Command line option --build-solutions was passed, but no project is being edited. Aborting."); + } + if (!EditorNode::get_singleton()->call_build()) { + OS::get_singleton()->set_exit_code(EXIT_FAILURE); + ERR_FAIL_V_MSG(true, + "Command line option --build-solutions was passed, but the build callback failed. Aborting."); + } } - } #endif + } #ifdef TOOLS_ENABLED if (exit && quit_after_timeout && EditorNode::get_singleton()) { EditorNode::get_singleton()->unload_editor_addons(); } #endif - + //need to find a better place to put this. + if (!Math::is_equal_approx(Input::get_singleton()->get_last_mouse_velocity().length_squared(), 0.0)) { + Input::get_singleton()->clear_mouse_axes_action_state(); + } return exit; }