mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 08:11:14 +00:00
UV editor: keep the mouse capture balanced
The canvas captured the mouse on every button press without checking whether it already held one, released it in a single place, and handled no capture loss at all. Two sequences leaked a capture: pressing a second button during a drag nested a second one that the single release on button-up could not undo, and a modal R/S skipped that release entirely while waiting for a confirming click that may never come. A leaked capture is not a local problem on macOS, where wxEVT_MOUSE_CAPTURE_LOST is never sent and nothing recovers it. While any wx window holds a capture, wxOSX routes every mouse event to that window and never calls through to NSWindow, so the application stops seeing motion and enter/leave, and native tooltips stop appearing anywhere in it. Capture through grab_mouse()/drop_mouse() so there is at most one, give it back on any button-up including a modal gesture (which tracks the pointer and needs no capture), and cancel on wxEVT_MOUSE_CAPTURE_LOST: commit nothing, put back what a modal rotate or scale already applied, and do not release a capture that is already gone.
This commit is contained in:
@@ -176,6 +176,7 @@ UVEditorCanvas::UVEditorCanvas(wxWindow *parent)
|
||||
Bind(wxEVT_MIDDLE_DOWN, &UVEditorCanvas::on_mouse, this);
|
||||
Bind(wxEVT_MIDDLE_UP, &UVEditorCanvas::on_mouse, this);
|
||||
Bind(wxEVT_MOTION, &UVEditorCanvas::on_mouse, this);
|
||||
Bind(wxEVT_MOUSE_CAPTURE_LOST, &UVEditorCanvas::on_capture_lost, this);
|
||||
Bind(wxEVT_MOUSEWHEEL, &UVEditorCanvas::on_mouse, this);
|
||||
Bind(wxEVT_LEAVE_WINDOW, &UVEditorCanvas::on_leave, this);
|
||||
Bind(wxEVT_KEY_DOWN, &UVEditorCanvas::on_key, this);
|
||||
@@ -727,10 +728,47 @@ void UVEditorCanvas::end_gesture()
|
||||
m_rot_raw_deg = 0.f;
|
||||
m_rot_applied_deg = 0.f;
|
||||
m_modal_scale_accum = 1.f;
|
||||
drop_mouse();
|
||||
}
|
||||
|
||||
void UVEditorCanvas::cancel_gesture()
|
||||
{
|
||||
// Undo what the gesture already applied live, as the Esc path does, and commit nothing.
|
||||
if (m_on_island_edit) {
|
||||
if (m_gesture == Gesture::RotateIslandModal && m_rot_applied_deg != 0.f)
|
||||
m_on_island_edit(m_selected_island, Vec2f::Zero(), -m_rot_applied_deg, 1.f, false);
|
||||
if (m_gesture == Gesture::ScaleIslandModal && m_modal_scale_accum != 1.f)
|
||||
m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f / m_modal_scale_accum, false);
|
||||
}
|
||||
|
||||
m_gesture = Gesture::None;
|
||||
m_rot_raw_deg = 0.f;
|
||||
m_rot_applied_deg = 0.f;
|
||||
m_modal_scale_accum = 1.f;
|
||||
m_vertex_edit_moved = false;
|
||||
}
|
||||
|
||||
void UVEditorCanvas::grab_mouse()
|
||||
{
|
||||
if (!HasCapture())
|
||||
CaptureMouse();
|
||||
}
|
||||
|
||||
void UVEditorCanvas::drop_mouse()
|
||||
{
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
}
|
||||
|
||||
// The capture was taken from us (a dialog opened, another application grabbed the pointer). wx
|
||||
// requires this to cancel the gesture: no commit, no Skip(), and no ReleaseMouse() - the capture is
|
||||
// already gone, and releasing it again would unbalance the stack.
|
||||
void UVEditorCanvas::on_capture_lost(wxMouseCaptureLostEvent &)
|
||||
{
|
||||
cancel_gesture();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void UVEditorCanvas::on_key(wxKeyEvent &evt)
|
||||
{
|
||||
const int key = evt.GetKeyCode();
|
||||
@@ -909,7 +947,7 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
|
||||
}
|
||||
m_gesture = (m_selected_island >= 0) ? Gesture::MoveIsland : Gesture::Pan;
|
||||
}
|
||||
CaptureMouse();
|
||||
grab_mouse();
|
||||
Refresh();
|
||||
} else if (type == wxEVT_RIGHT_DOWN && m_selected_island >= 0 && m_select_mode == SelectMode::Island) {
|
||||
const Vec2f rel = screen_to_uv(pos) - island_centroid(m_selected_island);
|
||||
@@ -919,12 +957,16 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
|
||||
m_rot_base_deg = island_rotation_deg(m_selected_island);
|
||||
m_rot_display_deg = m_rot_base_deg;
|
||||
m_gesture_last_angle = std::atan2(rel.y(), rel.x());
|
||||
CaptureMouse();
|
||||
grab_mouse();
|
||||
} else if (type == wxEVT_MIDDLE_DOWN) {
|
||||
m_gesture = Gesture::Pan;
|
||||
m_drag_last_px = pos;
|
||||
CaptureMouse();
|
||||
grab_mouse();
|
||||
} else if (type == wxEVT_LEFT_UP || type == wxEVT_RIGHT_UP || type == wxEVT_MIDDLE_UP) {
|
||||
// The drag is over either way. A modal R/S keeps running until a click confirms it, but it
|
||||
// tracks the pointer over this canvas and needs no capture to do so, so the capture goes back
|
||||
// here rather than waiting for that click - which may never come.
|
||||
drop_mouse();
|
||||
if (m_gesture != Gesture::RotateIslandModal && m_gesture != Gesture::ScaleIslandModal) {
|
||||
end_gesture();
|
||||
Refresh();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// pulls in the platform's real GL/gl.h, and glad/gl.h errors out if that happens first (it wants
|
||||
// to be the one to define the standard include guards GL/gl.h itself defines).
|
||||
#include <glad/gl.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/glcanvas.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/button.h>
|
||||
@@ -194,6 +195,7 @@ private:
|
||||
void on_paint(wxPaintEvent &evt);
|
||||
void on_size(wxSizeEvent &evt);
|
||||
void on_mouse(wxMouseEvent &evt);
|
||||
void on_capture_lost(wxMouseCaptureLostEvent &evt);
|
||||
void on_key(wxKeyEvent &evt);
|
||||
void on_leave(wxMouseEvent &evt); // drops the +/- cursor hint when the pointer leaves the canvas
|
||||
void on_erase_background(wxEraseEvent &evt) {} // required to avoid flicker on MSW, deliberately a no-op
|
||||
@@ -238,6 +240,16 @@ private:
|
||||
// vertex of some *other* island, in texture-UV space. Zero if nothing is within reach (#2).
|
||||
Vec2f snap_correction(int island) const;
|
||||
void end_gesture();
|
||||
// Cancels the gesture instead of finishing it: nothing is committed, and a modal rotate/scale is
|
||||
// put back the way Esc puts it back. Used when the capture is taken away from us.
|
||||
void cancel_gesture();
|
||||
// One capture at a time, released exactly once. Pressing a second button mid-drag would otherwise
|
||||
// nest a second capture that the single release on button-up cannot undo, and macOS never sends
|
||||
// wxEVT_MOUSE_CAPTURE_LOST to recover from that. A leaked capture is not a local problem there:
|
||||
// while any wx window holds one, wxOSX routes every mouse event to it and the application stops
|
||||
// seeing enter/leave and motion entirely, which also takes its tooltips down.
|
||||
void grab_mouse();
|
||||
void drop_mouse();
|
||||
// Rebuilds the status line from the current gesture/selection and pushes it to m_on_status.
|
||||
void update_status();
|
||||
// Re-picks what a click at `pos` would grab in the current select mode, and repaints when that changed.
|
||||
|
||||
Reference in New Issue
Block a user