@@ -102,6 +102,12 @@ def str_to_op(op_name: str) -> Callable[[Any, Any], bool]:
102
102
raise ValueError (f'invalid operation { op_name !r} ' )
103
103
104
104
105
+ def dpi_scale (x : int , dpi : int ) -> int :
106
+ """Scale a number according to a DPI"""
107
+ # DPI / standard DPI = scaling factor, eg: 144 / 96 = 1.5 = 150% in windows settings
108
+ return int (x / (dpi / 96 ))
109
+
110
+
105
111
class JSONFile :
106
112
def __init__ (self , file : str , * a , ** kw ):
107
113
self ._log = logging .getLogger (__name__ ).getChild (self .__class__ .__name__ + '.' + str (id (self )))
@@ -444,27 +450,32 @@ def set_pos(self, rect: Rect, placement: Optional[Placement] = None):
444
450
try :
445
451
# adjust the offset for the monitor that the window is going to end up on, since it might change
446
452
# if that monitor's DPI is different
447
- offset = int (
448
- self .get_border_and_shadow_thickness ()
449
- # get scaling factor
450
- / (
451
- # DPI / standard DPI = scaling factor, eg: 144 / 96 = 1.5 = 150% in windows settings
452
- GetDpiForMonitor (win32api .MonitorFromPoint (rect [:2 ], win32con .MONITOR_DEFAULTTONEAREST ).handle )[0 ]
453
- / 96
454
- )
453
+ target_display_dpi = GetDpiForMonitor (
454
+ win32api .MonitorFromPoint (rect [:2 ], win32con .MONITOR_DEFAULTTONEAREST ).handle # type: ignore
455
455
)
456
+ offset = dpi_scale (self .get_border_and_shadow_thickness (), target_display_dpi )
457
+
456
458
# check if Window will fit on the Display it's being moved to. If not, adjust the rect to fit
457
- rect = self .rebound (rect , to_rect = self .get_closest_display_rect (rect [:2 ]), offset = offset )
459
+ target_display_rect = self .get_closest_display_rect (rect [:2 ])
460
+ rect = self .rebound (rect , to_rect = target_display_rect , offset = offset )
458
461
459
462
resizable = self .is_resizable ()
460
463
# if the window is not resizeable, make sure we don't resize it by preserving the w + h
461
464
# includes 95 era system dialogs and the Outlook reminder window
462
465
w , h = size_from_rect (rect ) if resizable else self .get_size ()
463
466
# remake rect with the bounds adjusted coords
464
467
rect = (* rect [:2 ], rect [0 ] + w , rect [1 ] + h )
465
- if placement and not resizable :
466
- # override the placement for non-resizable windows to avoid setting wrong size for unminimised state
467
- placement = (* placement [:- 1 ], (* rect [:2 ], rect [0 ] + w , rect [1 ] + h ))
468
+ if placement :
469
+ if not resizable :
470
+ # override the placement for non-resizable windows to avoid setting wrong size for unminimised state
471
+ placement = (* placement [:- 1 ], (* rect [:2 ], rect [0 ] + w , rect [1 ] + h ))
472
+ elif placement [1 ] == win32con .SW_SHOWMAXIMIZED :
473
+ # rebound placement rect so that when user drags window away from maximised position it doesn't
474
+ # suddenly expand to some silly size
475
+ np_rect = self .rebound (placement [4 ], to_rect = target_display_rect , offset = offset )
476
+ # DPI scale it. From experimentation this worked best but I don't have any docs to back it up
477
+ np_rect = Rect (dpi_scale (i , target_display_dpi ) for i in np_rect )
478
+ placement = (* placement [:- 1 ], np_rect )
468
479
469
480
if placement :
470
481
win32gui .SetWindowPlacement (self .id , placement )
@@ -474,12 +485,12 @@ def set_pos(self, rect: Rect, placement: Optional[Placement] = None):
474
485
log .error ('err moving window %s : %s' % (win32gui .GetWindowText (self .id ), e ))
475
486
476
487
def get_border_and_shadow_thickness (self ):
477
- '''
488
+ """
478
489
Get the size of the window's resizable border and drop shadow in pixels.
479
490
480
491
Unlike `WindowType.get_border_and_shadow_thickness`, this function is based on the actual
481
492
shadow size of the window subject to the DPI of the monitor the window is on.
482
- '''
493
+ """
483
494
# DWMWA_EXTENDED_FRAME_BOUNDS = 9 says every StackOverflow answer, and it's the 9th item in this enum:
484
495
# https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
485
496
efb = DwmGetWindowAttribute (self .id , 9 )
0 commit comments