From 9d81245efff881b431cb429bfd24b09ac7bdb723 Mon Sep 17 00:00:00 2001 From: Dima Buzdyk <46728448+buzzhuzz@users.noreply.github.com> Date: Thu, 4 Apr 2024 18:46:14 +0500 Subject: [PATCH] Touchpad-friently 3d navigation (#4598) * gui: camera navigation style option Add camera navigation style config option as a preparation for introduction of touchpad style navigation * gui: touchpa-friently 3d navigation Implement FreeCAD-inspired touchpad-friendly camera operation mode: - Shift+move: panning - Alt+move: rotation Set "Camera mode" to "Touchpad" in settings menu to activate this mode. * cache current navigation style in class member variable Cache current navigation style on canvas focus, do not request settings value each mouse event. * force focuse 3d preview on preferences window close Explicitly force focus 3D preview pane on closing preferences window. This allows preferences be updated only once without checking current value each input event. Another benefit is to have 3D view in focus is that user could immediatelly use 3d-view shortcuts like Ctrl+<1..7> without clicking to the 3D view first. --- src/libslic3r/AppConfig.cpp | 3 +++ src/slic3r/GUI/GLCanvas3D.cpp | 34 +++++++++++++++++++++++++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 6 ++++++ src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/MainFrame.cpp | 3 +++ src/slic3r/GUI/Preferences.cpp | 4 ++++ 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index e474ca642a..8e82e21862 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -173,6 +173,9 @@ void AppConfig::set_defaults() if (get("use_free_camera").empty()) set_bool("use_free_camera", false); + if (get("camera_navigation_style").empty()) + set("camera_navigation_style", "0"); + #ifdef SUPPORT_REVERSE_MOUSE_ZOOM if (get("reverse_mouse_wheel_zoom").empty()) set_bool("reverse_mouse_wheel_zoom", false); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 227c93d15a..5bf629ccde 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1137,6 +1137,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_multisample_allowed(false) , m_moving(false) , m_tab_down(false) + , m_camera_movement(false) , m_cursor_type(Standard) , m_color_by("volume") , m_reload_delayed(false) @@ -3853,7 +3854,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.ignore_left_up = true; m_tooltip.set_in_imgui(false); if (imgui->update_mouse_data(evt)) { - if (evt.LeftDown() && m_canvas != nullptr) + if ((evt.LeftDown() || (evt.Moving() && (evt.AltDown() || evt.ShiftDown()))) && m_canvas != nullptr) m_canvas->SetFocus(); m_mouse.position = evt.Leaving() ? Vec2d(-1.0, -1.0) : pos.cast(); m_tooltip.set_in_imgui(true); @@ -4172,7 +4173,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } - else if (evt.Dragging()) { + else if (evt.Dragging() || is_camera_rotate(evt) || is_camera_pan(evt)) { m_mouse.dragging = true; if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) { @@ -4182,7 +4183,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } // do not process the dragging if the left mouse was set down in another canvas - else if (evt.LeftIsDown()) { + else if (is_camera_rotate(evt)) { // Orca: Sphere rotation for painting view // if dragging over blank area with left button, rotate if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { @@ -4244,9 +4245,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } + m_camera_movement = true; m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); } - else if (evt.MiddleIsDown() || evt.RightIsDown()) { + else if (is_camera_pan(evt)) { // If dragging over blank area with right button, pan. if (m_mouse.is_start_position_2D_defined()) { // get point in model space at Z = 0 @@ -4268,10 +4270,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.ignore_right_up = true; } + m_camera_movement = true; m_mouse.drag.start_position_2D = pos; } } - else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { + else if ((evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) || + (m_camera_movement && !is_camera_rotate(evt) && !is_camera_pan(evt))) { m_mouse.position = pos.cast(); if (evt.LeftUp()) { @@ -4450,6 +4454,25 @@ void GLCanvas3D::on_set_focus(wxFocusEvent& evt) } _refresh_if_shown_on_screen(); m_tooltip_enabled = true; + m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style"); +} + +bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt) const +{ + if (m_is_touchpad_navigation) { + return evt.Moving() && evt.AltDown() && !evt.ShiftDown(); + } else { + return evt.Dragging() && evt.LeftIsDown(); + } +} + +bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt) const +{ + if (m_is_touchpad_navigation) { + return evt.Moving() && evt.ShiftDown() && !evt.AltDown(); + } else { + return evt.Dragging() && (evt.MiddleIsDown() || evt.RightIsDown()); + } } Size GLCanvas3D::get_canvas_size() const @@ -5052,6 +5075,7 @@ void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const void GLCanvas3D::mouse_up_cleanup() { m_moving = false; + m_camera_movement = false; m_mouse.drag.move_volume_idx = -1; m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6f9de7c9ad..bed5ac3ae1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -551,6 +551,8 @@ private: std::array m_old_size{ 0, 0 }; + bool m_is_touchpad_navigation{ false }; + // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; bool m_initialized; @@ -564,6 +566,7 @@ private: bool m_multisample_allowed; bool m_moving; bool m_tab_down; + bool m_camera_movement; //BBS: add toolpath outside bool m_toolpath_outside{ false }; ECursorType m_cursor_type; @@ -963,6 +966,9 @@ public: void on_set_focus(wxFocusEvent& evt); void force_set_focus(); + bool is_camera_rotate(const wxMouseEvent& evt) const; + bool is_camera_pan(const wxMouseEvent& evt) const; + Size get_canvas_size() const; Vec2d get_local_mouse_position() const; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1894e1d35c..5284595e0b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -5275,6 +5275,7 @@ void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_ // so we put it into an inner scope PreferencesDialog dlg(mainframe, open_on_tab, highlight_option); dlg.ShowModal(); + this->plater_->get_current_canvas3D()->force_set_focus(); // BBS //app_layout_changed = dlg.settings_layout_changed(); #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 7e06f6e17d..c25b14d7e1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -589,6 +589,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ { PreferencesDialog dlg(this); dlg.ShowModal(); + plater()->get_current_canvas3D()->force_set_focus(); #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed()) #else @@ -2703,6 +2704,7 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent &) { PreferencesDialog dlg(this); dlg.ShowModal(); + plater()->get_current_canvas3D()->force_set_focus(); #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed()) #else @@ -2729,6 +2731,7 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent &) { PreferencesDialog dlg(this); dlg.ShowModal(); + plater()->get_current_canvas3D()->force_set_focus(); #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed()) #else diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index d5dd87da15..f54fe37cda 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1019,6 +1019,9 @@ wxWindow* PreferencesDialog::create_general_page() std::vector DefaultPage = {_L("Home"), _L("Prepare")}; auto item_default_page = create_item_combobox(_L("Default Page"), page, _L("Set the page opened on startup."), "default_page", DefaultPage); + std::vector CameraNavStyle = {_L("Default"), _L("Touchpad")}; + auto item_camera_navigation_style = create_item_combobox(_L("Camera style"), page, _L("Select camera navigation style.\nDefault: LMB+move for rotation, RMB/MMB+move for panning.\nTouchpad: Alt+move for rotation, Shift+move for panning."), "camera_navigation_style", CameraNavStyle); + auto item_mouse_zoom_settings = create_item_checkbox(_L("Zoom to mouse position"), page, _L("Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center."), 50, "zoom_to_mouse"); auto item_use_free_camera_settings = create_item_checkbox(_L("Use free camera"), page, _L("If enabled, use free camera. If not enabled, use constrained camera."), 50, "use_free_camera"); @@ -1083,6 +1086,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_region, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_currency, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_default_page, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_camera_navigation_style, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_use_free_camera_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3));