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