关于Action快捷键和小键盘的问题

在使用全尺寸键盘的时候 键盘右边都有一排小键盘

但是这个小键盘的数字键值和普通键盘的数字键值是不一样的

在ANSI码里 标准数字键值是$30..$39, 而小键盘的键值是$60..$69

这样问题就来了 , 我们在属性编辑器里设置Action的ShortCut的时候, 使用的都是文字表示, 而VCL架构里, ShortCut的文字表示是不区分大小键盘数字键的

我们先看一下文字是真么转换成ShortCut的:

在Menus单元: TextToShortCut(Text: string): TShortCut; 函数

function TextToShortCut(Text: string): TShortCut;

  { If the front of Text is equal to Front then remove the matching piece
    from Text and return True, otherwise return False }

  function CompareFront(var Text: string; const Front: string): Boolean;
  begin
    Result := False;
    if (Length(Text) >= Length(Front)) and
      (AnsiStrLIComp(PChar(Text), PChar(Front), Length(Front)) = 0) then
    begin
      Result := True;
      Delete(Text, 1, Length(Front));
    end;
  end;

var
  Key: TShortCut;
  Shift: TShortCut;
begin
  Result := 0;
  Shift := 0;
  while True do
  begin
    if CompareFront(Text, MenuKeyCaps[mkcShift]) then Shift := Shift or scShift
    else if CompareFront(Text, '^') then Shift := Shift or scCtrl
    else if CompareFront(Text, MenuKeyCaps[mkcCtrl]) then Shift := Shift or scCtrl
    else if CompareFront(Text, MenuKeyCaps[mkcAlt]) then Shift := Shift or scAlt
    else Break;
  end;
  if Text = '' then Exit;
  for Key := $08 to $255 do { Copy range from table in ShortCutToText }
    if AnsiCompareText(Text, ShortCutToText(Key)) = 0 then
    begin
      Result := Key or Shift;
      Exit;
    end;
end;

注意加亮的那几行, 实际上就是吧$08~$255的所有字符和传入的做个比较, 那么我们的焦点就关注到ShortCutToText上了:

function ShortCutToText(ShortCut: TShortCut): string;
var
  Name: string;
begin
  case WordRec(ShortCut).Lo of
    $08, $09:
      Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + WordRec(ShortCut).Lo - $08)];
    $0D: Name := MenuKeyCaps[mkcEnter];
    $1B: Name := MenuKeyCaps[mkcEsc];
    $20..$28:
      Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + WordRec(ShortCut).Lo - $20)];
    $2D..$2E:
      Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + WordRec(ShortCut).Lo - $2D)];
    $30..$39: Name := Chr(WordRec(ShortCut).Lo - $30 + Ord('0'));
    $41..$5A: Name := Chr(WordRec(ShortCut).Lo - $41 + Ord('A'));
    $60..$69: Name := Chr(WordRec(ShortCut).Lo - $60 + Ord('0'));
    $70..$87: Name := 'F' + IntToStr(WordRec(ShortCut).Lo - $6F);
  else
    Name := GetSpecialName(ShortCut);
  end;
  if Name <> '' then
  begin
    Result := '';
    if ShortCut and scShift <> 0 then Result := Result + MenuKeyCaps[mkcShift];
    if ShortCut and scCtrl <> 0 then Result := Result + MenuKeyCaps[mkcCtrl];
    if ShortCut and scAlt <> 0 then Result := Result + MenuKeyCaps[mkcAlt];
    Result := Result + Name;
  end
  else Result := '';
end;

很明显, 这里面把标准数字键和小键盘数字键全部映射成0~9了, 那么自然在TextToShortCut的时候就只会认出标准键盘了

所以, 一般情况下, 是没办法在属性编辑器里设置小键盘热键的, 只能通过代码解决:

在代码模式下, Action的ShortCut属性是一个TShortCut类型: TShortCut = Low(Word)..High(Word); 也就是说, 是Ansi字符

那么我们就可以直接使用键值来设置ShortCut, 而绕过Text转换的步骤, 从而能够达到使用小键盘快捷键的目的:

  Action1.ShortCut := Menus.ShortCut(VK_NUMPAD0, [ssCtrl]);

这样就很简单的设置了一个ctrl+小键盘0的快捷键

那么, 如果要同时设置标准键0和小键盘0都能激活这个Action怎么做呢

这就要使用另一个属性: SecondaryShortCuts, 这个实际上是一个TStringList

  property SecondaryShortCuts: TShortCutList

  TShortCutList = class(TStringList)
  private
    function GetShortCuts(Index: Integer): TShortCut;
  public
    function Add(const S: String): Integer; override;
    function IndexOfShortCut(const Shortcut: TShortCut): Integer;
    property ShortCuts[Index: Integer]: TShortCut read GetShortCuts;
  end;

OK, 现在出现了另一个问题, 我们向SecondaryShortCuts里写入多个快捷键, 是使用的Add, 可惜....Add的是一个文本, 这样我们又回到开始的问题了, 文本没办法区分大小键盘数字键, 咋办...-_-

有人会说, 吧小键盘设置到ShortCut上, 大键盘用SecondaryShortCuts, OK, 这当然没问题....不过始终不是正统的解决办法(程序员的偏执)

研究代码吧:

function TShortCutList.Add(const S: String): Integer;
begin
  Result := inherited Add(S);
  Objects[Result] := TObject(TextToShortCut(S));
end;

function TShortCutList.GetShortCuts(Index: Integer): TShortCut;
begin
  Result := TShortCut(Objects[Index]);
end;

似乎有办法解决, add的时候实际上是吧ShortCut强制转换为TObject类型保存进Objects里了(其实是个取巧的办法, 我自己也总这么干...^_^)

而读取ShortCut的时候根本没读String, 直接读的就是Objects, 那么我们如果直接写Objects能否可行? 试试看:

  Action1.SecondaryShortCuts.AddObject('Ctrl+Num0', TObject(Menus.ShortCut(VK_NUMPAD0, [ssCtrl])));

OK, 测试完全可行......问题完美解决了

原文地址:https://www.cnblogs.com/lzl_17948876/p/3911936.html