@@ -133,6 +133,7 @@ void InitMainMenu(Menu mainMenu, IExportProvider exportProvider)
133133 Tag = entry . Metadata ? . MenuID ,
134134 Header = headerText ?? entry . Metadata ? . Header
135135 } ;
136+ ApplyGesture ( menuItem , entry . Metadata ? . InputGestureText , cmd ) ;
136137 if ( ! string . IsNullOrEmpty ( entry . Metadata ? . MenuIcon ) )
137138 {
138139 try
@@ -556,6 +557,11 @@ static MenuItem CreateToolPaneMenuItem(ICSharpCode.ILSpy.ViewModels.ToolPaneMode
556557 Tag = toolPane . ContentId ,
557558 Command = toolPane . AssociatedCommand ?? new ICSharpCode . ILSpy . Commands . ToolPaneCommand ( toolPane . ContentId , dockWorkspace )
558559 } ;
560+ if ( toolPane . ShortcutKey != null )
561+ {
562+ menuItem . HotKey = toolPane . ShortcutKey ;
563+ menuItem . InputGesture = toolPane . ShortcutKey ;
564+ }
559565
560566 // Add icon if available
561567 if ( ! string . IsNullOrEmpty ( toolPane . Icon ) )
@@ -920,6 +926,41 @@ void UpdateIcon()
920926 }
921927 }
922928
929+ static void ApplyGesture ( MenuItem menuItem , string ? gestureText , ICommand cmd )
930+ {
931+ // Prefer explicit metadata text; otherwise fall back to routed command gestures.
932+ var resolvedCommand = ICSharpCode . ILSpy . CommandWrapper . Unwrap ( cmd ) ;
933+ if ( string . IsNullOrWhiteSpace ( gestureText ) && resolvedCommand is System . Windows . Input . RoutedCommand routed )
934+ {
935+ var routedGesture = routed . InputGestures ? . OfType < KeyGesture > ( ) . FirstOrDefault ( ) ;
936+ gestureText = routedGesture ? . ToString ( ) ;
937+ }
938+ else if ( string . IsNullOrWhiteSpace ( gestureText ) && resolvedCommand is System . Windows . Input . RoutedUICommand routedUi )
939+ {
940+ var routedGesture = routedUi . InputGestures ? . OfType < KeyGesture > ( ) . FirstOrDefault ( ) ;
941+ gestureText = routedGesture ? . ToString ( ) ;
942+ }
943+
944+ if ( string . IsNullOrWhiteSpace ( gestureText ) )
945+ return ;
946+
947+ try
948+ {
949+ // HotKey drives visual hint rendering in Avalonia menus
950+ var parsed = KeyGesture . Parse ( gestureText ) ;
951+ menuItem . HotKey = parsed ;
952+ menuItem . InputGesture = parsed ; // ensures hint is rendered even if HotKey isn't
953+ }
954+ catch
955+ {
956+ // Fallback: append gesture text so it is still visible
957+ if ( menuItem . Header is string headerText )
958+ {
959+ menuItem . Header = $ "{ headerText } \t { gestureText } ";
960+ }
961+ }
962+ }
963+
923964 void BuildNativeMenu ( Menu mainMenu )
924965 {
925966 if ( ! OperatingSystem . IsMacOS ( ) )
@@ -1056,6 +1097,24 @@ NativeMenuItem Convert(MenuItem m)
10561097
10571098 log . Debug ( "BuildNativeMenu: Converted {ItemCount} menu items total" , itemCount ) ;
10581099
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 )
1114+ {
1115+ log . Warning ( ex , "BuildNativeMenu: Failed to insert application menu" ) ;
1116+ }
1117+
10591118 // Always set a fresh native menu; avoid reusing existing items to prevent parent conflicts.
10601119 // Some native backends may throw when attempting to update menus that are still
10611120 // referenced by the platform. Always clear existing menu first before setting new one.
0 commit comments