Pārlūkot izejas kodu

More robust hotkey matchting logic

Ivan Sorokin 1 nedēļu atpakaļ
vecāks
revīzija
d8b05a6a3b
2 mainītis faili ar 101 papildinājumiem un 66 dzēšanām
  1. 1 0
      packages/fv/src/drivers.inc
  2. 100 66
      packages/fv/src/menus.inc

+ 1 - 0
packages/fv/src/drivers.inc

@@ -1362,6 +1362,7 @@ begin
      begin
        // FIX: For Alt+Key combinations, build the KeyCode using the Unicode character
        // instead of relying on the scancode-based character, which is wrong for non-latin layouts.
+       // The high byte keeps the scan code, the low byte gets the actual character code.
        keycode := (keycode and $FF00) or (ord(key.UnicodeChar) and $FF);
      end;
      Event.What:=evKeyDown;

+ 100 - 66
packages/fv/src/menus.inc

@@ -654,58 +654,70 @@ BEGIN
            AND MouseInMenus Then Action := DoReturn;  { Set return action }
          End;
        evKeyDown:
-         Case CtrlToArrow(E.KeyCode) Of
-           kbUp, kbDown: If (Size.Y <> 1) Then
-             TrackKey(CtrlToArrow(E.KeyCode) = kbDown){ Track keyboard }
-             Else If (E.KeyCode = kbDown) Then        { Down arrow }
-             AutoSelect := True;                      { Select item }
-           kbLeft, kbRight: If (ParentMenu = Nil) Then
-             TrackKey(CtrlToArrow(E.KeyCode)=kbRight) { Track keyboard }
-             Else Action := DoReturn;                 { Set return action }
-           kbHome, kbEnd: If (Size.Y <> 1) Then Begin
-               Current := Menu^.Items;                { Set to first item }
-               If (E.KeyCode = kbEnd) Then            { If the 'end' key }
-                 TrackKey(False);                     { Move to last item }
-             End;
-           kbEnter: Begin
-               If Size.Y = 1 Then AutoSelect := True; { Select item }
-               Action := DoSelect;                    { Return the item }
-             End;
-           kbEsc: Begin
-               Action := DoReturn;                    { Set return action }
-               If (ParentMenu = Nil) OR
-               (ParentMenu^.Size.Y <> 1) Then         { Check parent }
-                 ClearEvent(E);                       { Kill the event }
-             End;
-         Else
-           begin
-             {$ifdef FV_UNICODE}
-             searchChar := E.UnicodeChar;
-             {$else}
-             searchChar := E.CharCode;
-             {$endif}
-             if searchChar = #0 then Target := TopMenu
-                                else Target := @Self;
-             if (E.KeyShift and kbAltShift <> 0) then Target := TopMenu;
-
-             if searchChar <> #0 then P := Target^.FindItem(searchChar)
-                                 else P := nil;
-
-             If (P = Nil) Then Begin
-               P := TopMenu^.HotKey(E.KeyCode);
-               If (P <> Nil) AND CommandEnabled(P^.Command) Then
-               Begin
-                 Res := P^.Command;
+         begin
+           Case CtrlToArrow(E.KeyCode) Of
+             kbUp, kbDown: If (Size.Y <> 1) Then
+               TrackKey(CtrlToArrow(E.KeyCode) = kbDown)
+               Else If (E.KeyCode = kbDown) Then AutoSelect := True;
+             kbLeft, kbRight: If (ParentMenu = Nil) Then
+               TrackKey(CtrlToArrow(E.KeyCode)=kbRight)
+               Else Action := DoReturn;
+             kbHome, kbEnd: If (Size.Y <> 1) Then Begin
+                 Current := Menu^.Items;
+                 If (E.KeyCode = kbEnd) Then TrackKey(False);
+               End;
+             kbEnter: Begin
+                 If Size.Y = 1 Then AutoSelect := True;
+                 Action := DoSelect;
+               End;
+             kbEsc: Begin
                  Action := DoReturn;
-               End
-             End Else If Target = @Self Then Begin
-               If Size.Y = 1 Then AutoSelect := True;   { Set auto select }
-               Action := DoSelect;                      { Select item }
-               Current := P;                            { Set current item }
-             End Else If (ParentMenu <> Target) OR (ParentMenu^.Current <> P) Then
-                Action := DoReturn;
-           end;
-         End;
+                 If (ParentMenu = Nil) OR (ParentMenu^.Size.Y <> 1) Then ClearEvent(E);
+               End;
+           Else
+             begin
+               P := nil;
+               // Only perform hotkey search (both paths) if Alt is actually pressed.
+               if (E.KeyShift and kbAltShift <> 0) then
+               begin
+                 // Path 1: Character-based search (for correct layouts like Russian)
+                 {$ifdef FV_UNICODE}
+                 searchChar := E.UnicodeChar;
+                 {$else}
+                 searchChar := E.CharCode;
+                 {$endif}
+                 if searchChar <> #0 then P := TopMenu^.FindItem(searchChar);
+
+                 // Path 2: Scancode-based search (fallback for EN hotkeys in RU layout)
+                 if P = nil then
+                 begin
+                   {$ifdef FV_UNICODE}
+                   searchChar := WideChar(GetAltChar(E.ScanCode shl 8));
+                   {$else}
+                   searchChar := GetAltChar(E.ScanCode shl 8);
+                   {$endif}
+                   if searchChar <> #0 then P := TopMenu^.FindItem(searchChar);
+                 end;
+               end;
+
+               If P = nil Then Begin
+                 P := TopMenu^.HotKey(E.KeyCode);
+                 If (P <> Nil) AND CommandEnabled(P^.Command) Then Begin
+                   Res := P^.Command;
+                   Action := DoReturn;
+                 End
+               End Else Begin // P was found by one of the paths above
+                 Target := TopMenu; // Hotkeys should always target the top menu bar
+                 If Target = @Self Then Begin
+                   If Size.Y = 1 Then AutoSelect := True;
+                   Action := DoSelect;
+                   Current := P;
+                 End Else If (ParentMenu <> Target) OR (ParentMenu^.Current <> P) Then
+                    Action := DoReturn;
+               end;
+             end;
+           End;
+         end;
        evCommand: If (E.Command = cmMenu) Then Begin  { Menu command }
            AutoSelect := False;                       { Dont select item }
            If (ParentMenu <> Nil) Then
@@ -788,6 +800,7 @@ END;
 FUNCTION TMenuView.FindItem (Ch: AnsiChar): PMenuItem;
 VAR I: SmallInt; P: PMenuItem; itemHotkey: AnsiChar;
 BEGIN
+   if Ch = #0 then begin FindItem := nil; Exit; end;
    Ch := UpCase(Ch);
    P := Menu^.Items;
    While (P <> Nil) Do Begin
@@ -812,6 +825,7 @@ END;
 FUNCTION TMenuView.FindItem (Ch: WideChar): PMenuItem;
 VAR I: SmallInt; P: PMenuItem; itemHotkey: WideChar; menuName: UnicodeString;
 BEGIN
+   if Ch = #0 then begin FindItem := nil; Exit; end;
    P := Menu^.Items;
    While (P <> Nil) Do Begin
      If (P^.Name <> Sw_PString_Empty) AND (NOT P^.Disabled)
@@ -958,22 +972,42 @@ BEGIN
      Case Event.What Of
        evMouseDown: DoSelect;                         { Select menu item }
        evKeyDown:
-         {$ifdef FV_UNICODE}
-         if (Event.UnicodeChar <> #0) and (FindItem(Event.UnicodeChar) <> Nil) then
-         {$else}
-         if (Event.CharCode <> #0) and (FindItem(Event.CharCode) <> Nil) then
-         {$endif}
-            DoSelect
-         else
          begin
-           P := HotKey(Event.KeyCode);
-           If (P <> Nil) AND (CommandEnabled(P^.Command)) Then Begin
-             Event.What := evCommand;
-             Event.Command := P^.Command;
-             Event.InfoPtr := Nil;
-             PutEvent(Event);
-             ClearEvent(Event);
-           End;
+           P := nil;
+           // Only perform hotkey search (both paths) if Alt is actually pressed.
+           if (Event.KeyShift and kbAltShift <> 0) then
+           begin
+             // Path 1: Character-based search
+             {$ifdef FV_UNICODE}
+             if Event.UnicodeChar <> #0 then P := FindItem(Event.UnicodeChar);
+             {$else}
+             if Event.CharCode <> #0 then P := FindItem(Event.CharCode);
+             {$endif}
+
+             // Path 2: Fallback to scancode-based search
+             if P = nil then
+             begin
+               {$ifdef FV_UNICODE}
+               P := FindItem(WideChar(GetAltChar(Event.ScanCode shl 8)));
+               {$else}
+               P := FindItem(GetAltChar(Event.ScanCode shl 8));
+               {$endif}
+             end;
+           end;
+
+           if P <> nil then
+             DoSelect
+           else
+           begin // Fallback for global shortcuts (F-keys)
+             P := HotKey(Event.KeyCode);
+             If (P <> Nil) AND (CommandEnabled(P^.Command)) Then Begin
+               Event.What := evCommand;
+               Event.Command := P^.Command;
+               Event.InfoPtr := Nil;
+               PutEvent(Event);
+               ClearEvent(Event);
+             End;
+           end;
          end;
        evCommand:
          If Event.Command = cmMenu Then DoSelect;     { Select menu item }