diff options
29 files changed, 662 insertions, 136 deletions
diff --git a/core/array.cpp b/core/array.cpp index 171c11776c..b7d4ae413a 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -265,6 +265,49 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { return *this; } +template <typename Less> +_FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) { + + int lo = 0; + int hi = p_array.size(); + if (p_before) { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (p_less(p_array.get(mid), p_value)) { + lo = mid + 1; + } else { + hi = mid; + } + } + } else { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (p_less(p_value, p_array.get(mid))) { + hi = mid; + } else { + lo = mid + 1; + } + } + } + return lo; +} + +int Array::bsearch(const Variant &p_value, bool p_before) { + + return bisect(_p->array, p_value, p_before, _ArrayVariantSort()); +} + +int Array::bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before) { + + ERR_FAIL_NULL_V(p_obj, 0); + + _ArrayVariantSortCustom less; + less.obj = p_obj; + less.func = p_function; + + return bisect(_p->array, p_value, p_before, less); +} + Array &Array::invert() { _p->array.invert(); diff --git a/core/array.h b/core/array.h index 2c29103108..3d70a31d2f 100644 --- a/core/array.h +++ b/core/array.h @@ -70,6 +70,8 @@ public: Array &sort(); Array &sort_custom(Object *p_obj, const StringName &p_function); + int bsearch(const Variant &p_value, bool p_before = true); + int bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before = true); Array &invert(); int find(const Variant &p_value, int p_from = 0) const; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 6b43f2c63b..103a8e82c2 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -177,6 +177,14 @@ bool InputEventWithModifiers::get_command() const { return command; } +void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModifiers *event) { + + set_alt(event->get_alt()); + set_shift(event->get_shift()); + set_control(event->get_control()); + set_metakey(event->get_metakey()); +} + void InputEventWithModifiers::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alt", "enable"), &InputEventWithModifiers::set_alt); @@ -436,10 +444,7 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co mb->set_id(get_id()); mb->set_device(get_device()); - mb->set_alt(get_alt()); - mb->set_shift(get_shift()); - mb->set_control(get_control()); - mb->set_metakey(get_metakey()); + mb->set_modifiers_from_event(this); mb->set_position(l); mb->set_global_position(g); @@ -555,10 +560,7 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co mm->set_id(get_id()); mm->set_device(get_device()); - mm->set_alt(get_alt()); - mm->set_shift(get_shift()); - mm->set_control(get_control()); - mm->set_metakey(get_metakey()); + mm->set_modifiers_from_event(this); mm->set_position(l); mm->set_global_position(g); @@ -930,3 +932,75 @@ void InputEventAction::_bind_methods() { InputEventAction::InputEventAction() { pressed = false; } +///////////////////////////// + +void InputEventGesture::set_position(const Vector2 &p_pos) { + + pos = p_pos; +} + +Vector2 InputEventGesture::get_position() const { + + return pos; +} +///////////////////////////// + +void InputEventMagnifyGesture::set_factor(real_t p_factor) { + + factor = p_factor; +} + +real_t InputEventMagnifyGesture::get_factor() const { + + return factor; +} + +Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { + + Ref<InputEventMagnifyGesture> ev; + ev.instance(); + + ev->set_id(get_id()); + ev->set_device(get_device()); + ev->set_modifiers_from_event(this); + + ev->set_position(p_xform.xform(get_position() + p_local_ofs)); + ev->set_factor(get_factor()); + + return ev; +} + +InputEventMagnifyGesture::InputEventMagnifyGesture() { + + factor = 1.0; +} +///////////////////////////// + +void InputEventPanGesture::set_delta(const Vector2 &p_delta) { + + delta = p_delta; +} + +Vector2 InputEventPanGesture::get_delta() const { + return delta; +} + +Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { + + Ref<InputEventPanGesture> ev; + ev.instance(); + + ev->set_id(get_id()); + ev->set_device(get_device()); + ev->set_modifiers_from_event(this); + + ev->set_position(p_xform.xform(get_position() + p_local_ofs)); + ev->set_delta(get_delta()); + + return ev; +} + +InputEventPanGesture::InputEventPanGesture() { + + delta = Vector2(0, 0); +} diff --git a/core/os/input_event.h b/core/os/input_event.h index de3c0232ff..2cba60bede 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -213,6 +213,8 @@ public: void set_command(bool p_enabled); bool get_command() const; + void set_modifiers_from_event(const InputEventWithModifiers *event); + InputEventWithModifiers(); }; @@ -468,4 +470,42 @@ public: InputEventAction(); }; +class InputEventGesture : public InputEventWithModifiers { + + GDCLASS(InputEventGesture, InputEventWithModifiers) + + Vector2 pos; + +public: + void set_position(const Vector2 &p_pos); + Vector2 get_position() const; +}; + +class InputEventMagnifyGesture : public InputEventGesture { + + GDCLASS(InputEventMagnifyGesture, InputEventGesture) + real_t factor; + +public: + void set_factor(real_t p_factor); + real_t get_factor() const; + + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; + + InputEventMagnifyGesture(); +}; + +class InputEventPanGesture : public InputEventGesture { + + GDCLASS(InputEventPanGesture, InputEventGesture) + Vector2 delta; + +public: + void set_delta(const Vector2 &p_delta); + Vector2 get_delta() const; + + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; + + InputEventPanGesture(); +}; #endif diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 9e6aaeb911..da6e602ccb 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -483,6 +483,8 @@ struct _VariantCall { VCALL_LOCALMEM1(Array, erase); VCALL_LOCALMEM0(Array, sort); VCALL_LOCALMEM2(Array, sort_custom); + VCALL_LOCALMEM2R(Array, bsearch); + VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM0R(Array, duplicate); VCALL_LOCALMEM0(Array, invert); @@ -1625,6 +1627,8 @@ void register_variant_methods() { ADDFUNC0RNC(ARRAY, NIL, Array, pop_front, varray()); ADDFUNC0NC(ARRAY, NIL, Array, sort, varray()); ADDFUNC2NC(ARRAY, NIL, Array, sort_custom, OBJECT, "obj", STRING, "func", varray()); + ADDFUNC2R(ARRAY, INT, Array, bsearch, NIL, "value", BOOL, "before", varray(true)); + ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); ADDFUNC0RNC(ARRAY, ARRAY, Array, duplicate, varray()); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 203c60e644..3bb40755a6 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -260,6 +260,32 @@ Sort the array using a custom method and return reference to the array. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return true if the first argument is less than the second, and return false otherwise. Note: you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior. </description> </method> + <method name="bsearch"> + <return type="int"> + </return> + <argument index="0" name="value" type="var"> + </argument> + <argument index="1" name="before" type="bool" default="true"> + </argument> + <description> + Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. Note that calling bsearch on an unsorted array results in unexpected behavior. + </description> + </method> + <method name="bsearch_custom"> + <return type="int"> + </return> + <argument index="0" name="value" type="var"> + </argument> + <argument index="1" name="obj" type="Object"> + </argument> + <argument index="2" name="func" type="String"> + </argument> + <argument index="3" name="before" type="bool" default="true"> + </argument> + <description> + Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return true if the first argument is less than the second, and return false otherwise. Note that calling bsearch on an unsorted array results in unexpected behavior. + </description> + </method> </methods> <constants> </constants> diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp index 7d877bdb8c..ae304ed0bc 100644 --- a/editor/animation_editor.cpp +++ b/editor/animation_editor.cpp @@ -2889,6 +2889,18 @@ void AnimationKeyEditor::_track_editor_gui_input(const Ref<InputEvent> &p_input) } } } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_input; + if (magnify_gesture.is_valid()) { + zoom->set_value(zoom->get_value() * magnify_gesture->get_factor()); + } + + Ref<InputEventPanGesture> pan_gesture = p_input; + if (pan_gesture.is_valid()) { + + h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * pan_gesture->get_delta().x / 8); + v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } } void AnimationKeyEditor::_notification(int p_what) { diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index c7012a0c14..216f2027fb 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -979,6 +979,23 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) { } } + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + + Ref<DynamicFont> font = text_editor->get_font("font"); + + if (font.is_valid()) { + if (font->get_size() != (int)font_size) { + font_size = font->get_size(); + } + + font_size *= powf(magnify_gesture->get_factor(), 0.25); + + _add_font_size((int)font_size - font->get_size()); + } + return; + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { @@ -999,14 +1016,15 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) { void CodeTextEditor::_zoom_in() { font_resize_val += EDSCALE; - - if (font_resize_timer->get_time_left() == 0) - font_resize_timer->start(); + _zoom_changed(); } void CodeTextEditor::_zoom_out() { font_resize_val -= EDSCALE; + _zoom_changed(); +} +void CodeTextEditor::_zoom_changed() { if (font_resize_timer->get_time_left() == 0) font_resize_timer->start(); } @@ -1067,16 +1085,25 @@ void CodeTextEditor::_complete_request() { void CodeTextEditor::_font_resize_timeout() { + if (_add_font_size(font_resize_val)) { + font_resize_val = 0; + } +} + +bool CodeTextEditor::_add_font_size(int p_delta) { + Ref<DynamicFont> font = text_editor->get_font("font"); if (font.is_valid()) { - int new_size = CLAMP(font->get_size() + font_resize_val, 8 * EDSCALE, 96 * EDSCALE); + int new_size = CLAMP(font->get_size() + p_delta, 8 * EDSCALE, 96 * EDSCALE); if (new_size != font->get_size()) { EditorSettings::get_singleton()->set("interface/editor/source_font_size", new_size / EDSCALE); font->set_size(new_size); } - font_resize_val = 0; + return true; + } else { + return false; } } @@ -1285,6 +1312,7 @@ CodeTextEditor::CodeTextEditor() { code_complete_timer->connect("timeout", this, "_code_complete_timer_timeout"); font_resize_val = 0; + font_size = -1; font_resize_timer = memnew(Timer); add_child(font_resize_timer); font_resize_timer->set_one_shot(true); diff --git a/editor/code_editor.h b/editor/code_editor.h index 410dd99878..656ea4b47b 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -204,6 +204,7 @@ class CodeTextEditor : public VBoxContainer { Timer *font_resize_timer; int font_resize_val; + real_t font_size; Label *error; @@ -212,10 +213,12 @@ class CodeTextEditor : public VBoxContainer { void _update_font(); void _complete_request(); void _font_resize_timeout(); + bool _add_font_size(int p_delta); void _text_editor_gui_input(const Ref<InputEvent> &p_event); void _zoom_in(); void _zoom_out(); + void _zoom_changed(); void _reset_zoom(); CodeTextEditorCodeCompleteFunc code_complete_func; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 6cb4171f5a..3940dd9044 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1442,6 +1442,22 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { } } + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + + _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); + return; + } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta(); + h_scroll->set_value(h_scroll->get_value() + delta.x); + v_scroll->set_value(v_scroll->get_value() + delta.y); + return; + } + Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { // Button event diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index a525983c75..ebb5f57e99 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -339,6 +339,19 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_edit_draw->update(); } } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_input; + if (magnify_gesture.is_valid()) { + + uv_zoom->set_value(uv_zoom->get_value() * magnify_gesture->get_factor()); + } + + Ref<InputEventPanGesture> pan_gesture = p_input; + if (pan_gesture.is_valid()) { + + uv_hscroll->set_value(uv_hscroll->get_value() + uv_hscroll->get_page() * pan_gesture->get_delta().x / 8); + uv_vscroll->set_value(uv_vscroll->get_value() + uv_vscroll->get_page() * pan_gesture->get_delta().y / 8); + } } void Polygon2DEditor::_uv_scroll_changed(float) { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 448daab226..921ba529a2 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -1694,92 +1694,78 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { switch (nav_mode) { case NAVIGATION_PAN: { + _nav_pan(m, _get_warped_mouse_motion(m)); - real_t pan_speed = 1 / 150.0; - int pan_speed_modifier = 10; - if (nav_scheme == NAVIGATION_MAYA && m->get_shift()) - pan_speed *= pan_speed_modifier; + } break; - Point2i relative = _get_warped_mouse_motion(m); + case NAVIGATION_ZOOM: { + _nav_zoom(m, m->get_relative()); - Transform camera_transform; + } break; - camera_transform.translate(cursor.pos); - camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); - camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); - Vector3 translation(-relative.x * pan_speed, relative.y * pan_speed, 0); - translation *= cursor.distance / DISTANCE_DEFAULT; - camera_transform.translate(translation); - cursor.pos = camera_transform.origin; + case NAVIGATION_ORBIT: { + _nav_orbit(m, _get_warped_mouse_motion(m)); + + } break; + + case NAVIGATION_LOOK: { + _nav_look(m, _get_warped_mouse_motion(m)); + + } break; + + default: {} + } + } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + + if (is_freelook_active()) + scale_freelook_speed(magnify_gesture->get_factor()); + else + scale_cursor_distance(1.0 / magnify_gesture->get_factor()); + } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + NavigationMode nav_mode = NAVIGATION_NONE; + + if (nav_scheme == NAVIGATION_GODOT) { + + int mod = _get_key_modifier(pan_gesture); + + if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) + nav_mode = NAVIGATION_PAN; + else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) + nav_mode = NAVIGATION_ZOOM; + else if (mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) + nav_mode = NAVIGATION_ORBIT; + + } else if (nav_scheme == NAVIGATION_MAYA) { + if (pan_gesture->get_alt()) + nav_mode = NAVIGATION_PAN; + } + + switch (nav_mode) { + case NAVIGATION_PAN: { + _nav_pan(m, pan_gesture->get_delta()); } break; case NAVIGATION_ZOOM: { - real_t zoom_speed = 1 / 80.0; - int zoom_speed_modifier = 10; - if (nav_scheme == NAVIGATION_MAYA && m->get_shift()) - zoom_speed *= zoom_speed_modifier; - - NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int(); - if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) { - if (m->get_relative().x > 0) - scale_cursor_distance(1 - m->get_relative().x * zoom_speed); - else if (m->get_relative().x < 0) - scale_cursor_distance(1.0 / (1 + m->get_relative().x * zoom_speed)); - } else { - if (m->get_relative().y > 0) - scale_cursor_distance(1 + m->get_relative().y * zoom_speed); - else if (m->get_relative().y < 0) - scale_cursor_distance(1.0 / (1 - m->get_relative().y * zoom_speed)); - } + _nav_zoom(m, pan_gesture->get_delta()); } break; case NAVIGATION_ORBIT: { - Point2i relative = _get_warped_mouse_motion(m); - - real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - - cursor.x_rot += relative.y * radians_per_pixel; - cursor.y_rot += relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; - name = ""; - _update_name(); + _nav_orbit(m, pan_gesture->get_delta()); + } break; case NAVIGATION_LOOK: { - // Freelook only works properly in perspective. - // It technically works too in ortho, but it's awful for a user due to fov being near zero - if (!orthogonal) { - Point2i relative = _get_warped_mouse_motion(m); - - real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - - // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". - Transform prev_camera_transform = to_camera_transform(cursor); - - cursor.x_rot += relative.y * radians_per_pixel; - cursor.y_rot += relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; - - // Look is like the opposite of Orbit: the focus point rotates around the camera - Transform camera_transform = to_camera_transform(cursor); - Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); - Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); - Vector3 diff = prev_pos - pos; - cursor.pos += diff; - - name = ""; - _update_name(); - } + _nav_look(m, pan_gesture->get_delta()); } break; @@ -1885,6 +1871,94 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { accept_event(); } +void SpatialEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { + + const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + + real_t pan_speed = 1 / 150.0; + int pan_speed_modifier = 10; + if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) + pan_speed *= pan_speed_modifier; + + Transform camera_transform; + + camera_transform.translate(cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); + Vector3 translation(-p_relative.x * pan_speed, p_relative.y * pan_speed, 0); + translation *= cursor.distance / DISTANCE_DEFAULT; + camera_transform.translate(translation); + cursor.pos = camera_transform.origin; +} + +void SpatialEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { + + const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + + real_t zoom_speed = 1 / 80.0; + int zoom_speed_modifier = 10; + if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) + zoom_speed *= zoom_speed_modifier; + + NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int(); + if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) { + if (p_relative.x > 0) + scale_cursor_distance(1 - p_relative.x * zoom_speed); + else if (p_relative.x < 0) + scale_cursor_distance(1.0 / (1 + p_relative.x * zoom_speed)); + } else { + if (p_relative.y > 0) + scale_cursor_distance(1 + p_relative.y * zoom_speed); + else if (p_relative.y < 0) + scale_cursor_distance(1.0 / (1 - p_relative.y * zoom_speed)); + } +} + +void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { + + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); + real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + + cursor.x_rot += p_relative.y * radians_per_pixel; + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + name = ""; + _update_name(); +} + +void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { + + // Freelook only works properly in perspective. + // It technically works too in ortho, but it's awful for a user due to fov being near zero + if (!orthogonal) { + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); + real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + + // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". + Transform prev_camera_transform = to_camera_transform(cursor); + + cursor.x_rot += p_relative.y * radians_per_pixel; + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + + // Look is like the opposite of Orbit: the focus point rotates around the camera + Transform camera_transform = to_camera_transform(cursor); + Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; + cursor.pos += diff; + + name = ""; + _update_name(); + } +} + void SpatialEditorViewport::set_freelook_active(bool active_now) { if (!freelook_active && active_now) { diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index ad11ec33df..14558fc878 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -170,6 +170,11 @@ private: void _select_region(); bool _gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); + void _nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); + void _nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); + void _nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); + void _nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); + float get_znear() const; float get_zfar() const; float get_fov() const; diff --git a/main/input_default.cpp b/main/input_default.cpp index 2940f432d5..7cc7521686 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -319,6 +319,15 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { set_joy_axis(jm->get_device(), jm->get_axis(), jm->get_axis_value()); } + Ref<InputEventGesture> ge = p_event; + + if (ge.is_valid()) { + + if (main_loop) { + main_loop->input_event(ge); + } + } + if (!p_event->is_echo()) { for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index e0d9514985..8351c43574 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -302,6 +302,17 @@ void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, con self->sort_custom((Object *)p_obj, *func); } +godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before) { + Array *self = (Array *)p_self; + return self->bsearch((const Variant *)p_value, p_before); +} + +godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before) { + Array *self = (Array *)p_self; + const String *func = (const String *)p_func; + return self->bsearch_custom((const Variant *)p_value, (Object *)p_obj, *func, p_before); +} + void GDAPI godot_array_destroy(godot_array *p_self) { ((Array *)p_self)->~Array(); } diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 007c7955d4..0438a196cf 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -2680,6 +2680,26 @@ ] }, { + "name": "godot_array_bsearch", + "return_type": "godot_int", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"], + ["const godot_bool", "p_before"] + ] + }, + { + "name": "godot_array_bsearch_custom", + "return_type": "godot_int", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"], + ["godot_object *", "p_obj"], + ["const godot_string *", "p_func"], + ["const godot_bool", "p_before"] + ] + }, + { "name": "godot_array_destroy", "return_type": "void", "arguments": [ diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 01ae61e280..484ffd10ba 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -124,6 +124,10 @@ void GDAPI godot_array_sort(godot_array *p_self); void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, const godot_string *p_func); +godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before); + +godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before); + void GDAPI godot_array_destroy(godot_array *p_self); #ifdef __cplusplus diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 0a884e6106..6e69d43469 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -53,7 +53,7 @@ extern "C" { // This is for libraries *using* the header, NOT GODOT EXPOSING STUFF!! #ifdef _WIN32 -#define GDN_EXPORT +#define GDN_EXPORT __declspec(dllexport) #else #define GDN_EXPORT #endif diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 491adb31ee..3a5d0fd3fc 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -623,6 +623,16 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return do_input_action(p_camera, mm->get_position(), false); } + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + if (pan_gesture->get_command() || pan_gesture->get_shift()) { + const real_t delta = pan_gesture->get_delta().y; + floor->set_value(floor->get_value() + SGN(delta)); + return true; + } + } + return false; } diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 110cb776ee..cb6fff7855 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -85,6 +85,15 @@ static int prev_mouse_y = 0; static int button_mask = 0; static bool mouse_down_control = false; +static Vector2 get_mouse_pos(NSEvent *event) { + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSPoint p = [event locationInWindow]; + mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + return Vector2(mouse_x, mouse_y); +} + @interface GodotApplication : NSApplication @end @@ -508,12 +517,9 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { mm->set_button_mask(button_mask); prev_mouse_x = mouse_x; prev_mouse_y = mouse_y; - const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - const NSPoint p = [event locationInWindow]; - mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - mm->set_position(Vector2(mouse_x, mouse_y)); - mm->set_global_position(Vector2(mouse_x, mouse_y)); + const Vector2 pos = get_mouse_pos(event); + mm->set_position(pos); + mm->set_global_position(pos); Vector2 relativeMotion = Vector2(); relativeMotion.x = [event deltaX] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); relativeMotion.y = [event deltaY] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); @@ -575,6 +581,15 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { OS_OSX::singleton->input->set_mouse_in_window(true); } +- (void)magnifyWithEvent:(NSEvent *)event { + Ref<InputEventMagnifyGesture> ev; + ev.instance(); + get_key_modifier_state([event modifierFlags], ev); + ev->set_position(get_mouse_pos(event)); + ev->set_factor([event magnification] + 1.0); + OS_OSX::singleton->push_input(ev); +} + - (void)viewDidChangeBackingProperties { // nothing left to do here } @@ -838,6 +853,18 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) { OS_OSX::singleton->push_input(sc); } +inline void sendPanEvent(double dx, double dy, int modifierFlags) { + + Ref<InputEventPanGesture> pg; + pg.instance(); + + get_key_modifier_state(modifierFlags, pg); + Vector2 mouse_pos = Vector2(mouse_x, mouse_y); + pg->set_position(mouse_pos); + pg->set_delta(Vector2(-dx, -dy)); + OS_OSX::singleton->push_input(pg); +} + - (void)scrollWheel:(NSEvent *)event { double deltaX, deltaY; @@ -856,11 +883,16 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) { deltaX = [event deltaX]; deltaY = [event deltaY]; } - if (fabs(deltaX)) { - sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); - } - if (fabs(deltaY)) { - sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); + + if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) { + sendPanEvent(deltaX, deltaY, [event modifierFlags]); + } else { + if (fabs(deltaX)) { + sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); + } + if (fabs(deltaY)) { + sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); + } } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 946a8c47a3..da52fb39e0 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -964,6 +964,19 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { emit_signal("delete_nodes_request"); accept_event(); } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_ev; + if (magnify_gesture.is_valid()) { + + set_zoom_custom(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); + } + + Ref<InputEventPanGesture> pan_gesture = p_ev; + if (pan_gesture.is_valid()) { + + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } } void GraphEdit::clear_connections() { @@ -975,6 +988,11 @@ void GraphEdit::clear_connections() { void GraphEdit::set_zoom(float p_zoom) { + set_zoom_custom(p_zoom, get_size() / 2); +} + +void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { + p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); if (zoom == p_zoom) return; @@ -982,7 +1000,7 @@ void GraphEdit::set_zoom(float p_zoom) { zoom_minus->set_disabled(zoom == MIN_ZOOM); zoom_plus->set_disabled(zoom == MAX_ZOOM); - Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + get_size() / 2) / zoom; + Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + p_center) / zoom; zoom = p_zoom; top_layer->update(); @@ -992,7 +1010,7 @@ void GraphEdit::set_zoom(float p_zoom) { if (is_visible_in_tree()) { - Vector2 ofs = sbofs * zoom - get_size() / 2; + Vector2 ofs = sbofs * zoom - p_center; h_scroll->set_value(ofs.x); v_scroll->set_value(ofs.y); } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 4656b50133..e8e530848d 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -179,6 +179,7 @@ public: bool is_valid_connection_type(int p_type, int p_with_type) const; void set_zoom(float p_zoom); + void set_zoom_custom(float p_zoom, const Vector2 &p_center); float get_zoom() const; GraphEditFilter *get_top_layer() const { return top_layer; } diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 5e4dedcb48..51ab49e643 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -713,6 +713,12 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { } } } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8); + } } void ItemList::ensure_current_is_visible() { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 71e02cb2f7..124c268c8a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -817,6 +817,16 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + if (scroll_active) + + vscroll->set_value(vscroll->get_value() + vscroll->get_page() * pan_gesture->get_delta().y * 0.5 / 8); + + return; + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 9022d67a4a..a71a1c5f92 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -180,6 +180,17 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { time_since_motion = 0; } } + + Ref<InputEventPanGesture> pan_gesture = p_gui_input; + if (pan_gesture.is_valid()) { + + if (h_scroll->is_visible_in_tree()) { + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + } + if (v_scroll->is_visible_in_tree()) { + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } + } } void ScrollContainer::_update_scrollbar_position() { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1c937a6784..69dc7b21fe 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1777,46 +1777,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->is_pressed()) { if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { - float scroll_factor = 3 * mb->get_factor(); - if (scrolling) { - target_v_scroll = (target_v_scroll - scroll_factor); - } else { - target_v_scroll = (v_scroll->get_value() - scroll_factor); - } - - if (smooth_scroll_enabled) { - if (target_v_scroll <= 0) { - target_v_scroll = 0; - } - scrolling = true; - set_physics_process(true); - } else { - v_scroll->set_value(target_v_scroll); - } + _scroll_up(3 * mb->get_factor()); } if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { - float scroll_factor = 3 * mb->get_factor(); - if (scrolling) { - target_v_scroll = (target_v_scroll + scroll_factor); - } else { - target_v_scroll = (v_scroll->get_value() + scroll_factor); - } - - if (smooth_scroll_enabled) { - int max_v_scroll = get_total_unhidden_rows(); - if (!scroll_past_end_of_file_enabled) { - max_v_scroll -= get_visible_rows(); - max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); - } - - if (target_v_scroll > max_v_scroll) { - target_v_scroll = max_v_scroll; - } - scrolling = true; - set_physics_process(true); - } else { - v_scroll->set_value(target_v_scroll); - } + _scroll_down(3 * mb->get_factor()); } if (mb->get_button_index() == BUTTON_WHEEL_LEFT) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); @@ -1973,6 +1937,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + const Ref<InputEventPanGesture> pan_gesture = p_gui_input; + if (pan_gesture.is_valid()) { + + const real_t delta = pan_gesture->get_delta().y; + if (delta < 0) { + _scroll_up(-delta); + } else { + _scroll_down(delta); + } + h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100); + return; + } + Ref<InputEventMouseMotion> mm = p_gui_input; if (mm.is_valid()) { @@ -3066,6 +3043,50 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } +void TextEdit::_scroll_up(real_t p_delta) { + + if (scrolling) { + target_v_scroll = (target_v_scroll - p_delta); + } else { + target_v_scroll = (v_scroll->get_value() - p_delta); + } + + if (smooth_scroll_enabled) { + if (target_v_scroll <= 0) { + target_v_scroll = 0; + } + scrolling = true; + set_physics_process(true); + } else { + v_scroll->set_value(target_v_scroll); + } +} + +void TextEdit::_scroll_down(real_t p_delta) { + + if (scrolling) { + target_v_scroll = (target_v_scroll + p_delta); + } else { + target_v_scroll = (v_scroll->get_value() + p_delta); + } + + if (smooth_scroll_enabled) { + int max_v_scroll = get_total_unhidden_rows(); + if (!scroll_past_end_of_file_enabled) { + max_v_scroll -= get_visible_rows(); + max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); + } + + if (target_v_scroll > max_v_scroll) { + target_v_scroll = max_v_scroll; + } + scrolling = true; + set_physics_process(true); + } else { + v_scroll->set_value(target_v_scroll); + } +} + void TextEdit::_pre_shift_selection() { if (!selection.active || selection.selecting_mode == Selection::MODE_NONE) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index b1c7b14e58..bb9ca87d06 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -328,6 +328,9 @@ class TextEdit : public Control { void _update_selection_mode_word(); void _update_selection_mode_line(); + void _scroll_up(real_t p_delta); + void _scroll_down(real_t p_delta); + void _pre_shift_selection(); void _post_shift_selection(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 749ff51f57..9213296c55 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2612,6 +2612,12 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } break; } } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } } bool Tree::edit_selected() { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 0a02f471c1..1f539041fd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2013,6 +2013,30 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } + Ref<InputEventGesture> gesture_event = p_event; + if (gesture_event.is_valid()) { + + Size2 pos = gesture_event->get_position(); + + Control *over = _gui_find_control(pos); + if (over) { + + if (over->can_process()) { + + gesture_event = gesture_event->xformed_by(Transform2D()); //make a copy + if (over == gui.mouse_focus) { + pos = gui.focus_inv_xform.xform(pos); + } else { + pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); + } + gesture_event->set_position(pos); + _gui_call_input(over, gesture_event); + } + get_tree()->set_input_as_handled(); + return; + } + } + Ref<InputEventScreenDrag> drag_event = p_event; if (drag_event.is_valid()) { |