Skip to content

Commit 7967d65

Browse files
committed
feat: six new methods on FigureManagerMac: mpl_connect, mpl_disconnect, _window_resize_event, _window_move_event, _focus_in_event,
_focus_out_event. src/_macosx.m @interface Window — added - (PyObject*)pyManager; declaration. @interface View — added declarations for windowDidMove:, windowDidBecomeKey:, windowDidResignKey:. @implementation Window — added pyManager accessor (lines 1261–1264) between closeButtonPressed and close. windowDidResize: — augmented with a second PyObject_CallMethod on [window pyManager] calling _window_resize_event with (width, height), reusing the already-acquired GIL. Three new delegate methods added after windowDidResize:: - windowDidMove: — calls _window_move_event(x, y) with the window's new Cocoa origin - windowDidBecomeKey: — calls _focus_in_event() via gil_call_method - windowDidResignKey: — calls _focus_out_event() via gil_call_method backend_macosx.py FigureManagerMac.__init__ — initialises self._window_event_callbacks = cbook.CallbackRegistry(). Six new methods on FigureManagerMac: mpl_connect, mpl_disconnect, _window_resize_event, _window_move_event, _focus_in_event, _focus_out_event.
1 parent b162944 commit 7967d65

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

lib/matplotlib/backends/backend_macosx.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class FigureManagerMac(_macosx.FigureManager, FigureManagerBase):
150150

151151
def __init__(self, canvas, num, *, x=None, y=None):
152152
self._shown = False
153+
self._window_event_callbacks = cbook.CallbackRegistry()
153154
kwargs = {}
154155
if x is not None:
155156
kwargs['x'] = x
@@ -170,6 +171,45 @@ def _close_button_pressed(self):
170171
Gcf.destroy(self)
171172
self.canvas.flush_events()
172173

174+
def mpl_connect(self, event_name, callback):
175+
"""Register *callback* to be called on a window event.
176+
177+
Parameters
178+
----------
179+
event_name : str
180+
One of ``'window_resize_event'``, ``'window_move_event'``,
181+
``'focus_in_event'``, ``'focus_out_event'``.
182+
callback : callable
183+
- ``'window_resize_event'``: called with ``(width, height)``
184+
in logical pixels.
185+
- ``'window_move_event'``: called with ``(x, y)`` in Cocoa
186+
screen coordinates (origin at bottom-left of primary screen).
187+
- ``'focus_in_event'``, ``'focus_out_event'``: called with
188+
no arguments.
189+
190+
Returns
191+
-------
192+
int
193+
A callback id that can be passed to `mpl_disconnect`.
194+
"""
195+
return self._window_event_callbacks.connect(event_name, callback)
196+
197+
def mpl_disconnect(self, cid):
198+
"""Remove a callback previously registered with `mpl_connect`."""
199+
self._window_event_callbacks.disconnect(cid)
200+
201+
def _window_resize_event(self, width, height):
202+
self._window_event_callbacks.process('window_resize_event', width, height)
203+
204+
def _window_move_event(self, x, y):
205+
self._window_event_callbacks.process('window_move_event', x, y)
206+
207+
def _focus_in_event(self):
208+
self._window_event_callbacks.process('focus_in_event')
209+
210+
def _focus_out_event(self):
211+
self._window_event_callbacks.process('focus_out_event')
212+
173213
def destroy(self):
174214
# We need to clear any pending timers that never fired, otherwise
175215
# we get a memory leak from the timer callbacks holding a reference

src/_macosx.m

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ @interface Window : NSWindow
133133
- (Window*)initWithContentRect:(NSRect)rect styleMask:(unsigned int)mask backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation withManager: (PyObject*)theManager;
134134
- (NSRect)constrainFrameRect:(NSRect)rect toScreen:(NSScreen*)screen;
135135
- (BOOL)closeButtonPressed;
136+
- (PyObject*)pyManager;
136137
@end
137138

138139
@interface View : NSView <NSWindowDelegate>
@@ -145,6 +146,9 @@ - (void)drawRect:(NSRect)rect;
145146
- (void)updateDevicePixelRatio:(double)scale;
146147
- (void)windowDidChangeBackingProperties:(NSNotification*)notification;
147148
- (void)windowDidResize:(NSNotification*)notification;
149+
- (void)windowDidMove:(NSNotification*)notification;
150+
- (void)windowDidBecomeKey:(NSNotification*)notification;
151+
- (void)windowDidResignKey:(NSNotification*)notification;
148152
- (View*)initWithFrame:(NSRect)rect;
149153
- (void)setCanvas: (PyObject*)newCanvas;
150154
- (void)windowWillClose:(NSNotification*)notification;
@@ -1254,6 +1258,11 @@ - (BOOL)closeButtonPressed
12541258
return YES;
12551259
}
12561260

1261+
- (PyObject*)pyManager
1262+
{
1263+
return manager;
1264+
}
1265+
12571266
- (void)close
12581267
{
12591268
[super close];
@@ -1452,10 +1461,41 @@ - (void)windowDidResize: (NSNotification*)notification
14521461
Py_DECREF(result);
14531462
else
14541463
PyErr_Print();
1464+
result = PyObject_CallMethod(
1465+
[window pyManager], "_window_resize_event", "ii", width, height);
1466+
if (result)
1467+
Py_DECREF(result);
1468+
else
1469+
PyErr_Print();
14551470
PyGILState_Release(gstate);
14561471
[self setNeedsDisplay: YES];
14571472
}
14581473

1474+
- (void)windowDidMove:(NSNotification*)notification
1475+
{
1476+
Window* window = (Window*)[self window];
1477+
NSRect frame = [window frame];
1478+
PyGILState_STATE gstate = PyGILState_Ensure();
1479+
PyObject* result = PyObject_CallMethod(
1480+
[window pyManager], "_window_move_event", "dd",
1481+
frame.origin.x, frame.origin.y);
1482+
if (result)
1483+
Py_DECREF(result);
1484+
else
1485+
PyErr_Print();
1486+
PyGILState_Release(gstate);
1487+
}
1488+
1489+
- (void)windowDidBecomeKey:(NSNotification*)notification
1490+
{
1491+
gil_call_method([(Window*)[self window] pyManager], "_focus_in_event");
1492+
}
1493+
1494+
- (void)windowDidResignKey:(NSNotification*)notification
1495+
{
1496+
gil_call_method([(Window*)[self window] pyManager], "_focus_out_event");
1497+
}
1498+
14591499
- (void)windowWillClose:(NSNotification*)notification
14601500
{
14611501
process_event(

0 commit comments

Comments
 (0)