@@ -738,6 +738,11 @@ static bool IsApiVisibilityMenuItem(MenuItem menuItem)
738738 || string . Equals ( menuItem . Tag as string , "ApiVisAll" , StringComparison . Ordinal ) ;
739739 }
740740
741+ static bool IsHelpMenuItem ( MenuItem menuItem )
742+ {
743+ return string . Equals ( menuItem . Tag as string , "_Help" , StringComparison . Ordinal ) ;
744+ }
745+
741746 static bool IsLanguageMenuItem ( MenuItem menuItem )
742747 {
743748 var tag = menuItem . Tag as string ;
@@ -980,8 +985,11 @@ void BuildNativeMenu(Menu mainMenu)
980985
981986 _nativeThemeMenuItems = new List < NativeMenuItem > ( ) ;
982987
983- var nativeRoot = new NativeMenu ( ) ;
988+ var windowMenu = new NativeMenu ( ) ;
989+ var appMenu = Application . Current != null ? NativeMenu . GetMenu ( Application . Current ) ?? new NativeMenu ( ) : null ;
990+ appMenu ? . Items . Clear ( ) ;
984991 var itemCount = 0 ;
992+ MenuItem ? helpMenu = null ;
985993
986994 NativeMenuItem Convert ( MenuItem m )
987995 {
@@ -1004,6 +1012,15 @@ NativeMenuItem Convert(MenuItem m)
10041012 native . CommandParameter = m . CommandParameter ;
10051013 }
10061014
1015+ if ( m . InputGesture != null )
1016+ {
1017+ native . Gesture = m . InputGesture ;
1018+ }
1019+ else if ( m . HotKey != null )
1020+ {
1021+ native . Gesture = m . HotKey ;
1022+ }
1023+
10071024 TryApplyNativeMenuIcon ( native , m ) ;
10081025
10091026 if ( ! suppressNativeToggle && m . ToggleType != MenuItemToggleType . None )
@@ -1091,34 +1108,51 @@ NativeMenuItem Convert(MenuItem m)
10911108 {
10921109 if ( item is MenuItem mi )
10931110 {
1094- nativeRoot . Items . Add ( Convert ( mi ) ) ;
1111+ if ( IsHelpMenuItem ( mi ) )
1112+ {
1113+ helpMenu = mi ;
1114+ continue ;
1115+ }
1116+
1117+ windowMenu . Items . Add ( Convert ( mi ) ) ;
10951118 }
10961119 }
10971120
10981121 log . Debug ( "BuildNativeMenu: Converted {ItemCount} menu items total" , itemCount ) ;
10991122
1100- // Insert an explicit application menu to avoid the platform/Avalonia
1101- // falling back to a default menu (which can show "About Avalonia").
1102- try
1103- {
1104- var appName = Application . Current ? . Name
1105- ?? Assembly . GetEntryAssembly ( ) ? . GetName ( ) . Name
1106- ?? "Project Rover" ;
1107- var appSub = new NativeMenu ( ) ;
1108- // Optionally populate appSub with About/Preferences/Quit entries.
1109- var appMenuItem = new NativeMenuItem { Header = appName , Menu = appSub } ;
1110- nativeRoot . Items . Insert ( 0 , appMenuItem ) ;
1111- log . Debug ( "BuildNativeMenu: Inserted application menu '{AppName}' to override default app menu" , appName ) ;
1112- }
1113- catch ( Exception ex )
1123+ // Fill the application menu with Help items (no extra "Project Rover" tier).
1124+ if ( appMenu != null )
11141125 {
1115- log . Warning ( ex , "BuildNativeMenu: Failed to insert application menu" ) ;
1126+ try
1127+ {
1128+ if ( helpMenu != null )
1129+ {
1130+ foreach ( var child in helpMenu . Items )
1131+ {
1132+ switch ( child )
1133+ {
1134+ case Separator :
1135+ appMenu . Items . Add ( new NativeMenuItemSeparator ( ) ) ;
1136+ break ;
1137+ case MenuItem childMi :
1138+ appMenu . Items . Add ( Convert ( childMi ) ) ;
1139+ break ;
1140+ }
1141+ }
1142+ }
1143+
1144+ log . Debug ( "BuildNativeMenu: Filled app menu with {Count} items" , appMenu . Items . Count ) ;
1145+ }
1146+ catch ( Exception ex )
1147+ {
1148+ log . Warning ( ex , "BuildNativeMenu: Failed to fill application menu" ) ;
1149+ }
11161150 }
11171151
11181152 // Always set a fresh native menu; avoid reusing existing items to prevent parent conflicts.
11191153 // Some native backends may throw when attempting to update menus that are still
11201154 // referenced by the platform. Always clear existing menu first before setting new one.
1121- void TrySetNativeMenu ( AvaloniaObject target )
1155+ void TrySetNativeMenu ( AvaloniaObject target , NativeMenu menu )
11221156 {
11231157 try
11241158 {
@@ -1137,7 +1171,7 @@ void TrySetNativeMenu(AvaloniaObject target)
11371171 }
11381172 }
11391173
1140- NativeMenu . SetMenu ( target , nativeRoot ) ;
1174+ NativeMenu . SetMenu ( target , menu ) ;
11411175 log . Debug ( "BuildNativeMenu: Successfully set native menu on target" ) ;
11421176 }
11431177 catch ( Exception ex )
@@ -1149,7 +1183,7 @@ void TrySetNativeMenu(AvaloniaObject target)
11491183 try
11501184 {
11511185 log . Debug ( "BuildNativeMenu: Trying Application.Current as fallback" ) ;
1152- NativeMenu . SetMenu ( Application . Current , nativeRoot ) ;
1186+ NativeMenu . SetMenu ( Application . Current , menu ) ;
11531187 log . Debug ( "BuildNativeMenu: Fallback succeeded" ) ;
11541188 }
11551189 catch ( Exception fallbackEx )
@@ -1160,10 +1194,12 @@ void TrySetNativeMenu(AvaloniaObject target)
11601194 }
11611195 }
11621196
1163- // Set native menu only on the window (rootObj); setting on both window and
1164- // Application.Current causes "menu does not match" errors on macOS
1197+ // Set window menu on the window and app menu on Application.Current.
11651198 if ( rootObj != null )
1166- TrySetNativeMenu ( rootObj ) ;
1199+ TrySetNativeMenu ( rootObj , windowMenu ) ;
1200+
1201+ if ( Application . Current != null && appMenu != null )
1202+ TrySetNativeMenu ( Application . Current , appMenu ) ;
11671203
11681204 log . Debug ( "BuildNativeMenu: Native menu set successfully" ) ;
11691205 MainMenuThemeHelpers . UpdateNativeThemeChecks ( _nativeThemeMenuItems , settingsService ? . SessionSettings ? . Theme ?? ThemeManager . Current . Theme ) ;
0 commit comments