@@ -122,7 +122,7 @@ def select_loop(cls, loop: BaseLoop) -> None:
122122 def __init__ (
123123 self ,
124124 * args ,
125- size : Tuple [int , int ] = (640 , 480 ),
125+ size : Tuple [float , float ] = (640 , 480 ),
126126 title : str = "$backend" ,
127127 update_mode : UpdateModeEnum = "ondemand" ,
128128 min_fps : float = 0.0 ,
@@ -151,6 +151,8 @@ def __init__(
151151 if (self ._rc_canvas_group and self ._rc_canvas_group .get_loop ())
152152 else "no-loop" ,
153153 }
154+ self ._set_size_info ((0 , 0 ), 1.0 ) # Init self.__size_info
155+ self .__size_info ["need_event" ] = False
154156
155157 # Events and scheduler
156158 self ._events = EventEmitter ()
@@ -177,8 +179,8 @@ def __init__(
177179 def _final_canvas_init (self ):
178180 """Must be called by the subclasses at the end of their ``__init__``.
179181
180- This sets the canvas size and title, which must happen *after* the widget itself
181- is initialized. Doing this automatically can be done with a metaclass, but let's keep it simple.
182+ This sets the canvas logical size and title, which must happen *after* the widget itself
183+ is initialized. ( Doing this automatically can be done with a metaclass, but let's keep it simple.)
182184 """
183185 # Pop kwargs
184186 try :
@@ -211,7 +213,7 @@ def __del__(self):
211213
212214 def get_physical_size (self ) -> Tuple [int , int ]:
213215 """Get the physical size of the canvas in integer pixels."""
214- return self ._rc_get_physical_size ()
216+ return self .__size_info [ "physical_size" ]
215217
216218 def get_context (self , context_type : str ) -> object :
217219 """Get a context object that can be used to render to this canvas.
@@ -288,6 +290,27 @@ def get_context(self, context_type: str) -> object:
288290
289291 # %% Events
290292
293+ def _set_size_info (self , physical_size : Tuple [int , int ], pixel_ratio : float ):
294+ """Must be called by subclasses when their size changes.
295+
296+ Backends must *not* submit a "resize" event; the base class takes care of that, because
297+ it requires some more attention than the other events.
298+
299+ The subclass must call this when the actual viewport has changed. So not in ``_rc_set_logical_size()``,
300+ but e.g. when the underlying GUI layer fires a resize event, and maybe on init.
301+ """
302+ w , h = physical_size
303+
304+ psize = int (w ), int (h )
305+ pixel_ratio = float (pixel_ratio )
306+ lsize = psize [0 ] / pixel_ratio , psize [1 ] / pixel_ratio
307+ self .__size_info = {
308+ "physical_size" : psize ,
309+ "logical_size" : lsize ,
310+ "pixel_ratio" : pixel_ratio ,
311+ "need_event" : True ,
312+ }
313+
291314 def add_event_handler (
292315 self , * args : EventTypeEnum | EventHandlerFunction , order : float = 0
293316 ) -> Callable :
@@ -308,6 +331,22 @@ def submit_event(self, event: dict) -> None:
308331
309332 # %% Scheduling and drawing
310333
334+ def __maybe_emit_resize_event (self ):
335+ if self .__size_info ["need_event" ]:
336+ self .__size_info ["need_event" ] = False
337+ lsize = self .__size_info ["logical_size" ]
338+ self ._events .emit (
339+ {
340+ "event_type" : "resize" ,
341+ "width" : lsize [0 ],
342+ "height" : lsize [1 ],
343+ "pixel_ratio" : self .__size_info ["pixel_ratio" ],
344+ # Would be nice to have more details. But as it is now, PyGfx errors if we add fields it does not know, so let's do later.
345+ # "logical_size": self.__size_info["logical_size"],
346+ # "physical_size": self.__size_info["physical_size"],
347+ }
348+ )
349+
311350 def _process_events (self ):
312351 """Process events and animations.
313352
@@ -321,6 +360,9 @@ def _process_events(self):
321360 # Get events from the GUI into our event mechanism.
322361 self ._rc_gui_poll ()
323362
363+ # If the canvas changed size, send event
364+ self .__maybe_emit_resize_event ()
365+
324366 # Flush our events, so downstream code can update stuff.
325367 # Maybe that downstream code request a new draw.
326368 self ._events .flush ()
@@ -426,9 +468,16 @@ def _draw_frame_and_present(self):
426468 if self ._rc_get_closed ():
427469 return
428470
429- # Process special events
430- # Note that we must not process normal events here, since these can do stuff
431- # with the canvas (resize/close/etc) and most GUI systems don't like that.
471+ # Note: could check whether the known physical size is > 0.
472+ # But we also consider it the responsiblity of the backend to not
473+ # draw if the size is zero. GUI toolkits like Qt do this correctly.
474+ # I might get back on this once we also draw outside of the draw-event ...
475+
476+ # Make sure that the user-code is up-to-date with the current size before it draws.
477+ self .__maybe_emit_resize_event ()
478+
479+ # Emit before-draw
480+ self ._events .emit ({"event_type" : "before_draw" })
432481
433482 # Notify the scheduler
434483 if self .__scheduler is not None :
@@ -471,7 +520,7 @@ def get_logical_size(self) -> Tuple[float, float]:
471520 The logical size can be smaller than the physical size, e.g. on HiDPI
472521 monitors or when the user's system has the display-scale set to e.g. 125%.
473522 """
474- return self ._rc_get_logical_size ()
523+ return self .__size_info [ "logical_size" ]
475524
476525 def get_pixel_ratio (self ) -> float :
477526 """Get the float ratio between logical and physical pixels.
@@ -482,7 +531,7 @@ def get_pixel_ratio(self) -> float:
482531 pixel ratio >= 2.0. On MacOS (with a Retina screen) the pixel ratio is
483532 always 2.0.
484533 """
485- return self ._rc_get_pixel_ratio ()
534+ return self .__size_info [ "pixel_ratio" ]
486535
487536 def close (self ) -> None :
488537 """Close the canvas."""
@@ -611,18 +660,6 @@ def _rc_present_bitmap(self, *, data, format, **kwargs):
611660 """
612661 raise NotImplementedError ()
613662
614- def _rc_get_physical_size (self ) -> Tuple [int , int ]:
615- """Get the physical size (with, height) in integer pixels."""
616- raise NotImplementedError ()
617-
618- def _rc_get_logical_size (self ) -> Tuple [float , float ]:
619- """Get the logical size (with, height) in float pixels."""
620- raise NotImplementedError ()
621-
622- def _rc_get_pixel_ratio (self ) -> float :
623- """Get ratio between physical and logical size."""
624- raise NotImplementedError ()
625-
626663 def _rc_set_logical_size (self , width : float , height : float ):
627664 """Set the logical size. May be ignired when it makes no sense.
628665
0 commit comments