Use Ctrl+/ as shortcut in Delphi program
Asked Answered
G

2

6

Is this even possible? Notepad++ for example does it, but simply trying to assign it to components like actions or menu items doesn't work. The event it is assigned to simply doesn't trigger.

So, I took my question to Google. Nada. Then, I tried stepping through the various shortcut functions, in this case TextToShortCut and ShortCutToText.

The first one, TextToShortCut, converts stuff like 'Ctrl+A' (a string) to the following 16bit value:

(uint)A | (uint)Ctrl

Works fine, mostly. However, I observed the following weirdness:

// Try converting back and forward...
TextToShortCut('Ctrl+/') = 16495

// That's incorrect. It should be:
Ord('/') or scCtrl = 16431

// Incorrect too
ShortCutToText(16495) = 'Ctrl+/'

// This is the shortcut the first line actually creates (Ctrl+o)
Ord('o') or scCtrl = 16495 // wut?

// Which is invalid, cause only caps are used
ShortCutToText(16431) = ''

What's going on here? For now, I believe the error lies in the final part of TextToShortCut: after handling the part before the + sign ("Ctrl" in this case), it will try to find a shortcut for the remaining part ("/"). However, in its current form the part after + must be a valid shortcut on its own too.

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;

So, because:

ShortCutToText('/') = 0 (failure)
MapVirtualKey('/',MAPVK_VK_TO_VSC) = 0 (failure)

... the loop fails to detect '/' as a valid shortcut.

Is this some VCL bug or am I missing something?

Here's a proof of concept (yes, I'm taking screenshots of code, but mashing this together with the Component Palette is faster than using this code directly):

Edit 1:

enter image description here

Edit 2:

Manually assigning 16431 to the menu item does not work.

Greatcoat answered 10/9, 2013 at 16:18 Comment(2)
Works fine here. Although only on the / key that is part of the numpad. But then that's what npp does. Its restore zoom shortcut is mapped to Ctrl+Num /Precipitation
Notepad++ uses the regular one (left of Shift on US International) for stepping to the beginning of the word at the caret too.Greatcoat
G
12

If you assign an OnShortCut event handler to your menu's parent TForm, you will see that pressing Ctrl+/ results in a TShortCut value of 16575 from Menus.ShortCutFromMessage(), which is the function that the VCL uses internally when dispatching keystrokes for shortcut handling.

Both ShortCutToText(16495) and ShortCutToText(16575) return 'Ctrl+/', so lets break it down:

scCtrl = $4000
16495 = $406F
16575 = $40BF

Both shortcuts have the scCtrl flag present.

ShortCutToText($6F) and ShortCutToText($BF) both return '/'. This is because MapVirtualKey() returns the same scan code ($350000) for both virtual keys $6F (VK_DIVIDE) and $BF (VK_OEM_2 - the /? key for US keyboards).

When dispatching a shortcut, the VCL does exact comparisons of TShortCut values. So when you have 16495 assigned as your shortcut, it will not trigger because the system is reporting a shortcut of 16575 instead, even though they both map to Ctrl+/.

When I assign 16575 to the ShortCut property of a TMenuItem or TAction, pressing Ctrl+/ triggers the item, as expected.

So the trick is that the system is reporting a shortcut that uses the VK_OEM_2 virtual key for /, but you are expecting it to use the VK_DIVIDE virtual key instead.

Gordon answered 10/9, 2013 at 17:23 Comment(0)
L
7

A simple investigation of Key from an OnKeyDown event handler shows two different kind of forward slashes:

  • / on the numerical keypad = 111 (combined with CTRL = 16575)
  • / next to the shift key = 191 (combined with CTRL = 16495)

It is not possible to create a single shortcut which responds to both.

But instead of setting the shortcut on the memu item, you could attach an action which has a SecondaryShortCuts property. Then use:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Action1.ShortCut := 16575;
  Action1.SecondaryShortCuts.Add('Ctrl+/');
end;
Lubbi answered 10/9, 2013 at 17:40 Comment(5)
There's always the SecondaryShortCuts propertyPrecipitation
I'm not trying to bind to both, I'm just trying to bind to the 191 one. Thanks for answering though.Greatcoat
I'm just trying to bind to the 191 one. Why doesn't the question state that? Are we meant to guess?Precipitation
@DavidHeffernan: I completely forgot about the existence of the Numpad version of / when I wrote it. Sorry about that.Greatcoat
That's understandable. But your comment doesn't sound very grateful. Thanks for answering though. But what you did not like about the answer was really a result of that omission. I guess that's why I reacted. Sticking up for my fellow answerer!!Precipitation

© 2022 - 2024 — McMap. All rights reserved.