From b6596412f664e3a359110b53b7e9943afcfe16ad Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 30 Mar 2020 20:58:46 -0600 Subject: [PATCH 1/5] Update how tracking for macosx mouse enter/exit is handled Instead of manually adding tracking rectangles and updating for resize (older API), use a "newer" API (added OSX 10.5) for tracking areas. With the right flags, these areas are automatically updated for resize. --- src/_macosx.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index 637ab2bfb315..d59df5b0e956 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -337,6 +337,13 @@ static CGFloat _get_device_scale(CGContextRef cr) NSRect rect = NSMakeRect(0.0, 0.0, width, height); self->view = [self->view initWithFrame: rect]; + int opts = (NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect); + [self->view addTrackingArea: [ + [NSTrackingArea alloc] initWithRect: rect + options: opts + owner: self->view + userInfo: nil]]; [self->view setCanvas: (PyObject*)self]; return 0; } @@ -1565,7 +1572,6 @@ - (View*)initWithFrame:(NSRect)rect self = [super initWithFrame: rect]; rubberband = NSZeroRect; inside = false; - tracking = 0; device_scale = 1; return self; } @@ -1574,7 +1580,6 @@ - (void)dealloc { FigureCanvas* fc = (FigureCanvas*)canvas; if (fc) fc->view = NULL; - [self removeTrackingRect: tracking]; [super dealloc]; } @@ -1713,11 +1718,6 @@ - (void)windowDidResize: (NSNotification*)notification else PyErr_Print(); PyGILState_Release(gstate); - if (tracking) [self removeTrackingRect: tracking]; - tracking = [self addTrackingRect: [self bounds] - owner: self - userData: nil - assumeInside: NO]; [self setNeedsDisplay: YES]; } From a3f0999ced85a8b60b08785e8eced6b67d1869f3 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 30 Mar 2020 20:14:08 -0600 Subject: [PATCH 2/5] Simplify resizing a bit on macosx Just set some resizing flags on the view. This allows us to avoid manually resizing the view. --- src/_macosx.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index d59df5b0e956..9892c5be6a56 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -337,6 +337,7 @@ static CGFloat _get_device_scale(CGContextRef cr) NSRect rect = NSMakeRect(0.0, 0.0, width, height); self->view = [self->view initWithFrame: rect]; + self->view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect); [self->view addTrackingArea: [ @@ -1708,8 +1709,6 @@ - (void)windowDidResize: (NSNotification*)notification width = size.width; height = size.height; - [self setFrameSize: size]; - PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* result = PyObject_CallMethod( canvas, "resize", "ii", width, height); From 53bd4b87a3bb2e9341becff5439b6e4ad76eaee0 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 30 Mar 2020 20:51:41 -0600 Subject: [PATCH 3/5] Simplify macosx handling of mousemove events We can just request these events from the tracking area rather than manually enabling/disabling mouse move/checking inside. --- src/_macosx.m | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index 9892c5be6a56..1fe38b96fff1 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -204,7 +204,6 @@ - (void)close; @interface View : NSView { PyObject* canvas; NSRect rubberband; - BOOL inside; NSTrackingRectTag tracking; @public double device_scale; } @@ -338,7 +337,7 @@ static CGFloat _get_device_scale(CGContextRef cr) NSRect rect = NSMakeRect(0.0, 0.0, width, height); self->view = [self->view initWithFrame: rect]; self->view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - int opts = (NSTrackingMouseEnteredAndExited | + int opts = (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect); [self->view addTrackingArea: [ [NSTrackingArea alloc] initWithRect: rect @@ -712,7 +711,6 @@ static CGFloat _get_device_scale(CGContextRef cr) [window setTitle: [NSString stringWithCString: title encoding: NSASCIIStringEncoding]]; - [window setAcceptsMouseMovedEvents: YES]; [window setDelegate: view]; [window makeFirstResponder: view]; [[window contentView] addSubview: view]; @@ -1572,7 +1570,6 @@ - (View*)initWithFrame:(NSRect)rect { self = [super initWithFrame: rect]; rubberband = NSZeroRect; - inside = false; device_scale = 1; return self; } @@ -1759,8 +1756,6 @@ - (void)mouseEntered:(NSEvent *)event { PyGILState_STATE gstate; PyObject* result; - NSWindow* window = [self window]; - if ([window isKeyWindow]==false) return; int x, y; NSPoint location = [event locationInWindow]; @@ -1777,19 +1772,13 @@ - (void)mouseEntered:(NSEvent *)event else PyErr_Print(); PyGILState_Release(gstate); - - [window setAcceptsMouseMovedEvents: YES]; - inside = true; } - (void)mouseExited:(NSEvent *)event { PyGILState_STATE gstate; PyObject* result; - NSWindow* window = [self window]; - if ([window isKeyWindow]==false) return; - if (inside==false) return; gstate = PyGILState_Ensure(); result = PyObject_CallMethod(canvas, "leave_notify_event", ""); if(result) @@ -1797,9 +1786,6 @@ - (void)mouseExited:(NSEvent *)event else PyErr_Print(); PyGILState_Release(gstate); - - [[self window] setAcceptsMouseMovedEvents: NO]; - inside = false; } - (void)mouseDown:(NSEvent *)event From 40a588b70bed9dd6354ac4ab987be46c5b9d3e77 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 30 Mar 2020 23:01:09 -0600 Subject: [PATCH 4/5] Fix DPI scaling on macosx Rename _device_scale to _dpi_ratio, as expected by Figure.set_size_inches(). Also, make sure the value of _dpi_ratio on instances of FigureCanvasMac are updated *before* setting figure DPI so that the proper value is available for downstream code. This is part of the fix needed to make set_size_inches work on macosx. --- lib/matplotlib/backends/backend_macosx.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index bb75aeab7dc2..9e117cb4f072 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -46,12 +46,14 @@ def __init__(self, figure): FigureCanvasBase.__init__(self, figure) width, height = self.get_width_height() _macosx.FigureCanvas.__init__(self, width, height) - self._device_scale = 1.0 + self._dpi_ratio = 1.0 def _set_device_scale(self, value): - if self._device_scale != value: - self.figure.dpi = self.figure.dpi / self._device_scale * value - self._device_scale = value + if self._dpi_ratio != value: + # Need the new value in place before setting figure.dpi, which + # will trigger a resize + self._dpi_ratio, old_value = value, self._dpi_ratio + self.figure.dpi = self.figure.dpi / old_value * self._dpi_ratio def _draw(self): renderer = self.get_renderer(cleared=self.figure.stale) @@ -77,8 +79,8 @@ def resize(self, width, height): dpi = self.figure.dpi width /= dpi height /= dpi - self.figure.set_size_inches(width * self._device_scale, - height * self._device_scale, + self.figure.set_size_inches(width * self._dpi_ratio, + height * self._dpi_ratio, forward=False) FigureCanvasBase.resize_event(self) self.draw_idle() From 706f7f1e2f961467ee0c39d7b4a67496745992a0 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 30 Mar 2020 23:07:25 -0600 Subject: [PATCH 5/5] Implement macosx FigureManager.resize This allows Figure.set_size_inches to work. --- src/_macosx.m | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/_macosx.m b/src/_macosx.m index 1fe38b96fff1..b5b4bd8042eb 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -810,6 +810,22 @@ static CGFloat _get_device_scale(CGContextRef cr) } } +static PyObject* +FigureManager_resize(FigureManager* self, PyObject *args, PyObject *kwds) +{ + int width, height; + if (!PyArg_ParseTuple(args, "ii", &width, &height)) { + return NULL; + } + Window* window = self->window; + if(window) + { + // 36 comes from hard-coded size of toolbar later in code + [window setContentSize: NSMakeSize(width, height + 36.)]; + } + Py_RETURN_NONE; +} + static PyMethodDef FigureManager_methods[] = { {"show", (PyCFunction)FigureManager_show, @@ -831,6 +847,11 @@ static CGFloat _get_device_scale(CGContextRef cr) METH_NOARGS, "Returns the title of the window associated with the figure manager." }, + {"resize", + (PyCFunction)FigureManager_resize, + METH_VARARGS, + "Resize the window (in pixels)." + }, {NULL} /* Sentinel */ };