@@ -375,18 +375,20 @@ internal void SetupPortContextMenuPlacement(Popup popup)
375375 /// </summary>
376376 private void ConfigurePopupPlacement ( Popup popup , double zoom )
377377 {
378+ var dpiScale = GetDpiScale ( ) ;
379+
378380 popup . CustomPopupPlacementCallback = ( popupSize , targetSize , offset ) =>
379381 {
380382 double x ;
381383 double y = ( targetSize . Height - popupSize . Height ) / 2 ;
382384
383385 if ( this is InPortViewModel )
384386 {
385- x = - popupSize . Width + proxyPortContextMenuOffset * zoom ;
387+ x = - popupSize . Width + proxyPortContextMenuOffset * zoom * dpiScale ;
386388 }
387389 else
388390 {
389- x = targetSize . Width - proxyPortContextMenuOffset * zoom ;
391+ x = targetSize . Width - proxyPortContextMenuOffset * zoom * dpiScale ;
390392 }
391393
392394 return new [ ] { new CustomPopupPlacement ( new Point ( x , y ) , PopupPrimaryAxis . None ) } ;
@@ -398,6 +400,7 @@ private CustomPopupPlacement[] PlacePortContextMenu(Size popupSize, Size targetS
398400 // The actual zoom here is confusing
399401 // What matters is the zoom factor measured from the scaled : unscaled node size
400402 var zoom = node . WorkspaceViewModel . Zoom ;
403+ var dpiScale = GetDpiScale ( ) ;
401404
402405 double x ;
403406 var scaledWidth = autocompletePopupSpacing * targetSize . Width / node . ActualWidth ;
@@ -421,13 +424,31 @@ private CustomPopupPlacement[] PlacePortContextMenu(Size popupSize, Size targetS
421424 var rowOffset = PortModel . Index * PortModel . Height * zoom ;
422425 var customNodeOffset = NodeModel . CustomNodeTopBorderHeight * zoom ;
423426
424- var y = popupHeightOffset + headerHeightOffset + portHalfHeight + rowOffset + customNodeOffset ;
427+ // popupSize.Height is already DPI-scaled (in screen pixels), so we do NOT apply dpiScale to it
428+ // All other layout values are in logical units and must be multiplied by dpiScale for correct placement
429+ var y = popupHeightOffset + ( headerHeightOffset + portHalfHeight + rowOffset + customNodeOffset ) * dpiScale ;
425430
426431 var placement = new CustomPopupPlacement ( new Point ( x , y ) , PopupPrimaryAxis . None ) ;
427432
428433 return new [ ] { placement } ;
429434 }
430435
436+ private static double GetDpiScale ( )
437+ {
438+ double dpiScale = 1.0 ;
439+ try
440+ {
441+ var source = PresentationSource . FromVisual ( Application . Current . MainWindow ) ;
442+ dpiScale = source ? . CompositionTarget ? . TransformToDevice . M22 ?? 1.0 ;
443+ }
444+ catch
445+ {
446+ // fallback to default DPI scale of 1.0 if anything fails
447+ }
448+
449+ return dpiScale ;
450+ }
451+
431452 private void WorkspacePropertyChanged ( object sender , System . ComponentModel . PropertyChangedEventArgs e )
432453 {
433454 switch ( e . PropertyName )
0 commit comments