diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f39761f0ec..a24c095ad6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1842,6 +1842,10 @@ void GLCanvas3D::enable_separator_toolbar(bool enable) m_separator_toolbar.set_enabled(enable); } +bool GLCanvas3D::has_mouse_capture() const { + return m_canvas != nullptr && m_canvas->HasCapture(); +} + void GLCanvas3D::zoom_to_bed() { BoundingBoxf3 box = m_bed.build_volume().bounding_volume(); @@ -2182,7 +2186,7 @@ void GLCanvas3D::render(bool only_init) // Negative coordinate means out of the window, likely because the window was deactivated. // In that case the tooltip should be hidden. - if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.) { + if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0. || has_mouse_capture()) { // ORCA continue to capture mouse pos mid drag if (tooltip.empty()) tooltip = m_layers_editing.get_tooltip(*this); @@ -4170,6 +4174,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // BBS: single snapshot Plater::SingleSnapshot single(wxGetApp().plater()); +#ifdef __WXMAC__ + // On macOS, the mouse key state is only present for mouse btn related events such as wxEVT_LEFT_DOWN. + // For other events, all buttons are reported as non-pressed, such as window leaving event. This causes + // imgui stopped responding if cursor moved out of window, such as + // https://github.com/OrcaSlicer/OrcaSlicer/pull/14999#issuecomment-5151344759 + // We solve this by correcting the state of the event from the actual mouse state querying with `wxGetMouseState()` + // so it works like on other platforms. + { + const auto state = wxGetMouseState(); + evt.SetLeftDown(state.LeftIsDown()); + evt.SetMiddleDown(state.MiddleIsDown()); + evt.SetRightDown(state.RightIsDown()); + evt.SetAux1Down(state.Aux1IsDown()); + evt.SetAux2Down(state.Aux2IsDown()); + } +#endif + #if ENABLE_RETINA_GL const float scale = m_retina_helper->get_scale_factor(); evt.SetX(evt.GetX() * scale); @@ -4183,11 +4204,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // ignore left up events coming from imgui windows and not processed by them m_mouse.ignore_left_up = true; m_tooltip.set_in_imgui(false); - if (imgui->update_mouse_data(evt)) { + + // while a non-ImGui drag is already in progress (gizmo grabber, object move, rectangle selection, layer editing), + // don't let ImGui/ImGuizmo claim the event just because the cursor is hovering something like the navigator cube + // that incorrectly suppresses the active drag's tooltip and can interrupt its processing. The active drag always takes priority. + const bool other_drag_active = m_gizmos.is_dragging() || m_mouse.dragging || m_rectangle_selection.is_dragging() || m_layers_editing.state == LayersEditing::Editing; + + if (imgui->update_mouse_data(evt) && !other_drag_active) { 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); + + // ORCA keep tracking mouse position while drag active and cursor not in window bounds + const bool imgui_dragging_active = (GImGui != nullptr && ImGui::GetIO().MouseDown[0] && GImGui->ActiveId != 0) || m_navigator_dragging; + if (!has_mouse_capture() && imgui_dragging_active) + m_canvas->CaptureMouse(); + + // release capture as soon as the button goes up + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + mouse_up_cleanup(); + render(); #ifdef SLIC3R_DEBUG_MOUSE_EVENTS printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str()); @@ -4284,6 +4321,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_main_toolbar.on_mouse(evt2, *this); } + // ORCA keep tracking mouse position while drag active and cursor not in window bounds + if (!has_mouse_capture() && evt.LeftIsDown() && m_gizmos.is_dragging()) + m_canvas->CaptureMouse(); + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); @@ -4389,6 +4430,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Start editing the layer height. m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); } else { @@ -4402,6 +4446,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) && m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation && m_gizmos.get_current_type() != GLGizmosManager::FuzzySkin) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + m_dirty = true; } } @@ -4469,6 +4517,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.start_position_3D = m_mouse.scene_position; m_sequential_print_clearance_first_displacement = true; m_moving = true; + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); } } } @@ -4477,6 +4528,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if (evt.Dragging() && evt.LeftIsDown() && m_mouse.drag.move_volume_idx != -1 && m_layers_editing.state == LayersEditing::Unknown) { if (m_canvas_type != ECanvasType::CanvasAssembleView) { + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + if (!m_mouse.drag.move_requires_threshold) { m_mouse.dragging = true; Vec3d cur_pos = m_mouse.drag.start_position_3D; @@ -4528,6 +4583,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) { //BBS not in assemble view if (m_canvas_type != ECanvasType::CanvasAssembleView) { + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + m_rectangle_selection.dragging(pos.cast()); m_dirty = true; } @@ -4537,12 +4596,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) { if (m_layers_editing.state == LayersEditing::Editing) { + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + _perform_layer_editing_action(&evt); m_mouse.position = pos.cast(); } } // do not process the dragging if the left mouse was set down in another canvas else if (is_camera_rotate(evt, button_mappings)) { + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + // Orca: Sphere rotation for painting view // if dragging over blank area with left button or other button mapped to rotate, then rotate bool middle_or_right_button_used_as_rotate = (evt.MiddleIsDown() && button_mappings[MouseButton::Middle] == MouseAction::Rotation) || @@ -4622,6 +4688,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); } else if (is_camera_pan(evt, button_mappings)) { + + if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds + m_canvas->CaptureMouse(); + // if dragging with right button or if button functions swapped and dragging with left button over blank area then pan if (m_mouse.is_start_position_2D_defined()) { // get point in model space at Z = 0 @@ -6033,9 +6103,18 @@ void GLCanvas3D::_render_3d_navigator() { if (!wxGetApp().show_3d_navigator()) { m_canvas_toolbar_pos[0] = 0; + m_navigator_dragging = false; return; } + // Fix stealing capture event from other drag events + const bool other_drag_active = !m_navigator_dragging && (m_moving || m_rectangle_selection.is_dragging() || m_gizmos.is_dragging() || m_layers_editing.state == LayersEditing::Editing); + + ImGuiIO& io = ImGui::GetIO(); + const bool saved_mouse_down0 = io.MouseDown[0]; + if (other_drag_active) + io.MouseDown[0] = false; + ImGuizmo::BeginFrame(); auto& style = ImGuizmo::GetStyle(); @@ -6060,7 +6139,6 @@ void GLCanvas3D::_render_3d_navigator() sc *= (float) dpi / (float) DPI_DEFAULT; #endif // WIN32 - const ImGuiIO& io = ImGui::GetIO(); const float viewManipulateLeft = 0; const float viewManipulateTop = io.DisplaySize.y; const float camDistance = 8.f; @@ -6084,6 +6162,10 @@ void GLCanvas3D::_render_3d_navigator() camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), ImVec2(size, size), 0x00101010); + // Restore the real mouse-down state + if (other_drag_active) + io.MouseDown[0] = saved_mouse_down0; + if (result.changed) { for (unsigned int c = 0; c < 4; ++c) { for (unsigned int r = 0; r < 4; ++r) { @@ -6115,6 +6197,8 @@ void GLCanvas3D::_render_3d_navigator() request_extra_frame(); } + + m_navigator_dragging = result.dragging; } #define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0 @@ -9226,8 +9310,12 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() //ORCA ImGui::IsWindowHovered() returns false when left_down events on buttons that causes scrollbar disappears for a short time auto win_pos = ImGui::GetWindowPos(); - bool is_win_hovered = ImGui::IsMouseHoveringRect(win_pos, win_pos + ImVec2(window_width + (show_scroll ? scrollbar_size : 0), window_height), !show_scroll); // use non clipped rectangle to reserve clickable area for scrollbar track - m_sel_plate_toolbar.is_display_scrollbar = is_win_hovered; + bool is_win_hovered = ImGui::IsMouseHoveringRect(win_pos, win_pos + ImVec2(window_width + (show_scroll ? scrollbar_size : 0), window_height), !show_scroll); + + // Also show scrollbar visible and continue to capture mouse position + const bool is_scrollbar_active_drag = GImGui != nullptr && ImGui::GetIO().MouseDown[0] && GImGui->ActiveId != 0 && GImGui->ActiveIdWindow == ImGui::GetCurrentWindow(); + + m_sel_plate_toolbar.is_display_scrollbar = is_win_hovered || is_scrollbar_active_drag; imgui.end(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 84cf311e57..7bae744098 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -589,6 +589,7 @@ private: bool m_toolpath_outside{ false }; ECursorType m_cursor_type; GLSelectionRectangle m_rectangle_selection; + bool m_navigator_dragging{ false }; //BBS:add plate related logic mutable std::vector m_hover_volume_idxs; @@ -916,6 +917,7 @@ public: void update_volumes_colors_by_extruder(); bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; } + bool has_mouse_capture() const; void render(bool only_init = false); bool is_rendering_enabled() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 7855ea92cc..1aaac99d40 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -442,7 +442,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { } } else if (m_dragging) { // when mouse cursor leave window than finish actual dragging operation - bool is_leaving = mouse_event.Leaving(); + bool is_leaving = mouse_event.Leaving() && !m_parent.has_mouse_capture(); // ORCA keep tracking mouse position while drag active and cursor not in window bounds if (mouse_event.Dragging()) { Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); auto ray = m_parent.mouse_ray(mouse_coord);