Как отобразить НОМЕРНУЮ КЛАВИАТУРУ для ярлыка пункта меню, назначенного с помощью `VK_NUMPAD0`?

В приложении Win32 VCL Delphi 10.4.2 в Windows10 X64 (немецкий язык) я программно установил ярлыки для некоторых пунктов меню:

mRasterizedDoubleSize.Shortcut := VK_ADD;
mRasterizedHalveSize.Shortcut := VK_SUBTRACT;
mRasterizedResetToOriginalSVGSize.Shortcut := VK_NUMPAD0;

Это приводит к следующему меню во время выполнения:

введите описание изображения здесь

(ZEHNERTASTATUR на немецком языке означает ЦИФРОВАЯ КЛАВИАТУРА)

Почему Zehnertastatur (цифровая клавиатура) не отображается для третьего пункта меню?

Как я могу отобразить ZEHNERTASTATUR (ЦИФРОВУЮ КЛАВИАТУРУ) для ярлыка пункта меню, назначенного с помощью VK_NUMPAD0?

Я отправил отчет о качестве для этой ошибки в Vcl.Menus: https://quality.embarcadero.com/browse/RSP-33296 Проголосуйте за него!

РЕДАКТИРОВАТЬ: я попробовал совет Андреаса, но он работает только программно во время выполнения, а не во время разработки в инспекторе объектов:

mRasterizedResetToOriginalSVGSize.Caption := mRasterizedResetToOriginalSVGSize.Caption + #9 + '0 (NUMPAD)  ';

введите описание изображения здесь

Нет ли функции, которая переводит слово NUMPAD на текущий системный язык во время выполнения?

EDIT2: я пробовал это, чтобы получить слово для ярлыка VK_NUMPAD0, но он возвращает только тот же 0 без суффикса NUMPAD:

var s: TShortCut;
s := ShortCut(VK_NUMPAD0, []);
CodeSite.Send('TformMain.FormCreate: ShortCutToText(s)', ShortCutToText(s));

введите описание изображения здесь

РЕДАКТИРОВАТЬ 3: теперь я отлаживал Vcl.Menus: ошибка, похоже, находится в Vcl.Menus.ShortCutToText: хотя VK_ADD (6 млрд долларов) правильно переведен GetSpecialName(ShortCut), VK_NUMPAD0 (60 долларов) НЕ переводится GetSpecialName(ShortCut)!

EDIT4: я нашел решение:

function MyGetSpecialName(ShortCut: TShortCut): string;
var
  ScanCode: Integer;
  KeyName: array[0..255] of Char;
begin
  Result := '';
  ScanCode := Winapi.Windows.MapVirtualKey(LoByte(Word(ShortCut)), 0) shl 16;
  if ScanCode <> 0 then
  begin
    if Winapi.Windows.GetKeyNameText(ScanCode, KeyName, Length(KeyName)) <> 0 then
      Result := KeyName;
  end;
end;

var s: System.Classes.TShortCut;
s := ShortCut(VK_NUMPAD0, []);
CodeSite.Send('ShortCutToText', MyGetSpecialName(s));

введите описание изображения здесь


person user1580348    schedule 10.03.2021    source источник
comment
Можно ли использовать TActionList и подключать к нему пункты меню? Или, по крайней мере, реализовать ярлык каким-либо другим способом, кроме свойства TMenuItem.ShortCut? Потому что в любом случае можно сделать mRasterizedResetToOriginalSVGSize.Caption := 'Original SVG size'#9'Numpad 0'.   -  person Andreas Rejbrand    schedule 10.03.2021
comment
(Причина, по которой TActionList может помочь, заключается в том, что вы можете использовать вторичное средство быстрого доступа.)   -  person Andreas Rejbrand    schedule 10.03.2021
comment
К сожалению, actNumpad0Dummy.Shortcut := VK_NUMPAD0; (конечно, для свойства Ation этого пункта меню установлено значение actNumpad0Dummy) не работает. Он показывает тот же результат, что и раньше.   -  person user1580348    schedule 10.03.2021
comment
Я считаю, что вы используете TActionList с TAction с именем aResetZoom с Caption = 'Original SVG size'#9'Numpad 0' и NO Shortcut. Это вы разместите в главном меню. Затем, чтобы заставить работать фактическое сокращение клавиатуры, вы можете либо использовать aResetZoom.SecondaryShortcuts, либо создать фиктивное действие с Shortcut = VK_NUMPAD0 и тем же OnExecute (которое вы не помещаете в главное меню).   -  person Andreas Rejbrand    schedule 10.03.2021
comment
Пожалуйста, посмотрите на мой вопрос EDIT: нет ли функции, которая переводит слово NUMPAD на текущий системный язык во время выполнения?   -  person user1580348    schedule 10.03.2021
comment
Это хорошо работает и во время разработки. Я часто использую инспектор объектов, чтобы добавлять такие специальные подписи с вкладками. Мне просто нужно вставить их в OI или использовать текстовое представление формы. См. privat.rejbrand.se/menutab.mp4.   -  person Andreas Rejbrand    schedule 10.03.2021
comment
EDIT4: Я нашел решение - это решение следует опубликовать как ответ, а не как редактирование.   -  person Remy Lebeau    schedule 10.03.2021
comment
@RemyLebeau: Я до сих пор не понимаю, чем EDIT4 отличается от нижней части моего A!   -  person Andreas Rejbrand    schedule 10.03.2021


Ответы (1)


Один из подходов такой:

Используйте TActionList. В целом это хорошая практика. Определите свои действия в этом списке, а затем просто сопоставьте их с пунктами меню, кнопками, флажками и т. Д. Средство списка действий - одна из самых лучших частей VCL IMHO.

Теперь создайте действие с именем aResetZoom с Caption = 'Reset zoom'#9'Numpad 0' и NO ShortCut. Поместите это в строку меню.

Затем создайте новое действие с именем aResetZoomShortcut с тем же OnExecute (и, возможно, тем же OnUpdate) и ярлыком Num 0 (установленным во время разработки или программно во время выполнения). Не помещайте это в главное меню.

Результат:

Снимок экрана главной формы с показанным пунктом меню Просмотр / Сброс масштабирования. Справа от пункта меню находится текст Numpad 0.

и действие запускается, когда я нажимаю цифровую клавиатуру 0 (но не альфа 0).

У этого подхода много вариантов. Может быть, вы сможете заставить его работать с одним действием без ShortCut, но с Num 0 в его SecondaryShortCuts списке. Или вы можете использовать свойства формы KeyPreview и OnKeyPress вместо фиктивного действия.

Вариантов много. Выберите тот, который лучше всего подходит для вашего конкретного сценария.

Бонусные замечания

Обратите внимание, что вполне возможно установить подписи с вкладками во время разработки с помощью инспектора объектов. См. пример видео.

Вероятно, вы можете выполнить локализацию с помощью Win32 GetKeyNameText < / a> функция. Следующий код адаптирован из VCL:

var
  name: array[0..128] of Char;
begin
  FillChar(name, SizeOf(name), 0);
  GetKeyNameText(MapVirtualKey(VK_NUMPAD0, 0) shl 16, @name[0], Length(name));
  // string(name) now is 'NUM 0' on my system

При этом лично я не возражаю, если описания ярлыков нелокализованы или локализованы вручную, как и остальная часть приложения.

Обновлять

Уточнение, как использовать код локализации:

procedure TForm5.FormCreate(Sender: TObject);
var
  name: array[0..128] of Char;
  NameAsANormalString: string;
begin
  FillChar(name, SizeOf(name), 0);
  GetKeyNameText(MapVirtualKey(VK_NUMPAD0, 0) shl 16, @name[0], Length(name));
  NameAsANormalString := name;
  ShowMessage(name);
end;

производит

Окно сообщения с текстом NUM 0

в моей системе.

person Andreas Rejbrand    schedule 10.03.2021
comment
Спасибо за хороший пример. Однако при использовании Caption = 'Reset zoom'#9'Numpad 0' он работает только на английском языке. (Мне пришлось бы сделать перевод на другие языки), а функция TMenuItem.Shortcut автоматически переводит это на текущий системный язык. Нет ли функции, которая переводит слово NUMPAD на текущий системный язык во время выполнения? - person user1580348; 10.03.2021
comment
Интересная идея. Однако у него есть один недостаток: ярлык в меню не будет переведен при работе в Windows с другой локалью. - person dummzeuch; 10.03.2021
comment
@dummzeuch: Я точно не знаю, как это работает. Если я посмотрю на исходный код VCL, я могу увидеть, что VCL обычно добавляет сокращенный текст в Vcl.Menus.TMenuItem.AppendTo: Caption := Caption + #9 + ShortCutToText(FShortCut); и что ShortCutToText использует SmkcCtrl = 'Ctrl+'; строку ресурсов в Vcl.Const. Следовательно, мне кажется, что эти ярлыки не автоматически переводятся. Если у вас есть немецкое приложение VCL с немецкими ярлыками и я запускаю его в своей шведской системе, не увижу ли я Strg+A вместо Ctrl+A? - person Andreas Rejbrand; 10.03.2021
comment
Ах, локализованы только специальные имена. И они локализованы с использованием Win32 GetKeyNameText. - person Andreas Rejbrand; 10.03.2021
comment
@AndreasRejbrand Посмотрите на мой EDIT4 в вопросе. - person user1580348; 10.03.2021
comment
@ user1580348: Разве это не то, что я сказал полчаса назад? :) - person Andreas Rejbrand; 10.03.2021
comment
@AndreasRejbrand Разница в том, что мое решение EDIT4 обеспечивает правильный перевод на системный язык, в то время как в вашем (все еще хорошем) решении вы используете постоянную строку "Numpad 0". Итак, я получаю желаемый результат с mRasterizedResetToOriginalSVGSize.Caption := mRasterizedResetToOriginalSVGSize.Caption + #9 + GetSpecialShortcutName(VK_NUMPAD0) + ' '; - person user1580348; 10.03.2021
comment
Посмотрите на снимок экрана: i.imgur.com/ThhQ0j0.png - person user1580348; 10.03.2021
comment
Я отправил отчет о качестве этой ошибки в Vcl.Menus: quality.embarcadero.com/browse/ RSP-33296 Проголосуйте за! - person user1580348; 10.03.2021
comment
@ user1580348: Вы видели часть "Вы, наверное, можете выполнить локализацию ..." внизу моего ответа? :) - person Andreas Rejbrand; 10.03.2021
comment
@AndreasRejbrand То, что вы описываете как «вероятно», я получаю как 0 (ZEHNERTASTATUR) с моим решением EDIT4 в моей немецкой Windows 10. Однако я принимаю этот ответ, потому что он дает исчерпывающий ответ на мой вопрос. - person user1580348; 10.03.2021
comment
@ user1580348: Спасибо. Замечательно. Возможно, я просто не очень хорошо говорю по-английски, но для ясности, обратите внимание, что ваш EDIT4 на самом деле делает то же самое, что и нижняя часть моего A: privat.rejbrand.se/edit4.png. В частности, и мой фрагмент, и ваш EDIT 4 делают GetKeyNameText из MapVirtualKey из VK_NUMPAD0, и оба производят 0 (ZEHNERTASTATUR) в немецкой системе и NUM 0 в английской системе! :) - person Andreas Rejbrand; 10.03.2021
comment
@AndreasRejbrand Когда я использую: CodeSite.Send('TformMain.FormCreate: GetKeyNameText', GetKeyNameText(MapVirtualKey(VK_NUMPAD0, 0) shl 16, @name[0], Length(name)));, я получаю в результате: 18 !!! Итак, ясно, что GetKeyNameText не работает по назначению. - person user1580348; 10.03.2021
comment
Кстати, также странно, что var name: array[0..128] of Char; не может быть объявлен как INLINE VARIABLE! Он должен быть объявлен в разделе var. - person user1580348; 10.03.2021
comment
@ user1580348: Вы неправильно используете функцию. GetKeyNameText принимает указатель на строковый буфер и заполняет его. Возвращает количество заполненных символов. 18 - длина строки 0 (ZEHNERTASTATUR). Итак, после того, как функция GetKeyNameText вернулась, then string(name) (да, приведение к строке) имеет значение 0 (ZEHNERTASTATUR). Если вы посмотрите на свой EDIT 4, вы увидите, что он тоже не считает возвращаемое значение GetKeyNameText желаемой строкой, а вместо этого смотрит на значение своего второго параметра после. - person Andreas Rejbrand; 10.03.2021
comment
Вы знаете, что Winapi.Windows.GetKeyNameText из вашего EDIT4 - это точно такая же функция, как GetKeyNameText в моем фрагменте? :) - person Andreas Rejbrand; 10.03.2021
comment
@ user1580348: Что касается встроенных переменных, это потому, что array[..] of особенный. По той же причине вы не можете использовать такую ​​конструкцию в качестве возвращаемого значения: function Test: array[0..3] of Integer. - person Andreas Rejbrand; 10.03.2021
comment
Я ДЕЙСТВИТЕЛЬНО не заметил. Моя кратковременная память, должно быть, ухудшилась. - person user1580348; 10.03.2021
comment
@ user1580348: Такое случается со всеми нами! Просто рад, что теперь мы на одной волне! :) - person Andreas Rejbrand; 10.03.2021
comment
Я всегда использую префиксы единиц: Winapi.Windows.GetKeyNameText. С этой привычкой не может быть недопонимания. - person user1580348; 10.03.2021
comment
@AndreasRejbrand Спасибо за обновление! Я ОБОЖАЮ ваши объяснения: они настолько ясны, что даже такой дурак, как я, может их понять! - person user1580348; 10.03.2021
comment
Спасибо за ваши добрые слова! :) Я всегда стараюсь быть максимально понятным. - person Andreas Rejbrand; 10.03.2021
comment
@AndreasRejbrand А как включить ShiftState, например CTROL, ALT, SHIFT, если я не хочу использовать буквальную строку? - person user1580348; 24.03.2021