首页 > 代码库 > 关于Action快捷键和小键盘的问题

关于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, 测试完全可行......问题完美解决了