Delphi: Can I differentiate between numpad's enter key and carriage return? - delphi

I've got a quaint little app that pops up an on screen numberpad/calculator written in Delphi. I'd like to make it so if you pressed 'enter' (on the number pad) you'd be pressing '=' and if you pressed 'return' (on the main keyboard) you'd be pressing 'OK'.
There's an OK button who is the form's default guy which responds to hitting enter or return.
There is also an onkeydown event handler which potentially might capture both Enter and return as vk_return. But its duties are usurped by the default 'OK' button.
If I could know the difference between return and enter, then I could get rid of my default property on the OK button and just do hit the OK button's click event handler on the form key down function, but alas they are both VK_RETURN.

Override the WM_KEYDOWN message handler:
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
Implement it so that it calls the ancestor for anything but what you're interested in. You can detect the difference between the return key and the enter key by the "extended" bit in the key data message field:
procedure TForm1.WMKeyDown(var Message: TWMKeyDown);
const
// Message.KeyData format:
// [0..15 repCount][16..23 scan code][24 extended bit][25..28 reserved]
// [29 context][30 previous state][31 transition state]
KD_IS_EXTENDED = 1 shl 24;
begin
if Message.CharCode <> VK_RETURN then
begin
inherited;
Exit;
end;
if (KD_IS_EXTENDED and Message.KeyData) <> 0 then
ShowMessage('Keypad Enter')
else
ShowMessage('Return');
end;

Related

WM_KEYDOWN in WndProc - doesn't trigger

I am trying to capture WM_KEYDOWN to process some keyboard events at the Form level. I know I can use KeyPreview=true and use the OnKeyDown event of TForm. However, I'd like to do it in the WndProc() to simplify event processing in a single function. The problem is, it doesn't fire in the WndProc(). Is there a particular reason why it doesn't? Aside from using BEGIN_MESSAGE_MAP to handle WM_KEYDOWN, is there another way?
void __fastcall TForm1::WndProc(TMessage &fMessage)
{
switch (fMessage.Msg)
{
default: break;
case WM_KEYDOWN: {
TWMKeyDown KDMsg = reinterpret_cast<TWMKeyDown&>(fMessage);
TShiftState KDSs = KeyDataToShiftState(KDMsg.KeyData);
if (KDMsg.CharCode == 'C' && KDSs.operator ==(TShiftState() << ssCtrl))
{
// Process CTRL+C key
fMessage.Result = 1;
return;
}
}
break;
}
TForm::WndProc(fMessage);
}
Keyboard (and mouse) messages are posted to the window that has input focus. If a TForm has a child control that has the focus, the messages will go directly to the child's WndProc, not the Form's WndProc.
That is where KeyPreview comes into play. When true, it tells child controls to forward their keyboard messages to their parent TForm for processing.
Otherwise, you can alternatively use the TApplication(Events)::OnMessage event to handle input messages before they are dispatched to their target windows.

Vaadin TextField's value not available in shortcut listener

I want to read a Vaadin Flow TextField's value when the user presses the Enter key. I can add a ShortcutListener to listen for the keypress, but the value of the TextField is null in the listener even though the TextField is not empty:
TextField textField = new TextField();
Shortcuts.addShortcutListener(UI.getCurrent(), () -> {
String text = texttextField.getValue();
// text doesn't contain the latest text
}, Key.ENTER);
Why is this happening and how can I read the value that the user has entered when the keypress listener is triggered?
The behavior boils down to browser events. While the user may have entered some text into the input element, the value of the TextField is not updated to the server until there's a ValueChange event - even the vaadin-text-field element in the browser doesn't "know" of the text (the value property has not been updated) when the enter keypress event occurs. By default, the value of the text field is only updated when the input loses focus. You can work around the issue by explicitly toggling a blur of the TextField:
// member field in class
boolean shortcutFired = false;
// ...
Shortcuts.addShortcutListener(UI.getCurrent(), () -> {
textField.blur();
shortcutFired = true;
}, Key.ENTER);
and listening to the value change event instead of the shortcut listener:
textField.addValueChangeListener(e -> {
if( shortcutFired ) {
String value = e.getValue();
// do something with the value
shortcutFired = false;
}
}
This approach doesn't work too well if you need to keep track of multiple fields; in that case, you might want to make the TextFields update their values to the server more eagerly by setting their ValueChangeMode to ValueChangeMode.EAGER. The downside of this approach is that it increases the amount of traffic between the browser and the network, as every keypress will trigger a server request.

How to Detect a TPopupMenu's OnClose (OnPopDown) Event in Delphi applications

I can write some code inside TPopupMenu's OnPopUp event. But I also need another event for OnPopDown. Is there any way to do it using Delphi 10.3.3?
There are various options you can try.
Approach 1
In the simpler case when you have a particular control whose context menu you need to track, you can handle its WM_CONTEXTMENU message manually:
protected
procedure WMContextMenu(var Message: TWMContextMenu);
message WM_CONTEXTMENU;
where (for example)
procedure TForm1.WMContextMenu(var Message: TWMContextMenu);
begin
if
Assigned(PopupMenu)
and
(ClientRect.Contains(ScreenToClient(Message.Pos)) or (Message.Pos = Point(-1, -1)))
then
begin
Windows.Beep(200, 500); // pre-popup code
if (Message.XPos = -1) and (Message.YPos = -1) then // Menu key or Shift+F10
with ClientToScreen(Point(0, 0)) do
PopupMenu.Popup(X, Y)
else
PopupMenu.Popup(Message.XPos, Message.YPos);
Windows.Beep(400, 500); // post-popup code
end
else
inherited;
end;
The test ClientRect.Contains(ScreenToClient(Message.Pos)) is necessary so that you don't "overwrite" the scrollbar's own context menu. Also, you need to consider the case when the context menu is opened using the keyboard (e.g. menu key or Shift+F10).
Approach 2
If this is not enough for you, you could create your own TPopupMenu child class and override its Popup method, which is virtual. Add a DoPopdown method and call it at the end (following the design of the DoPopup method).
To quickly test this approach, you can use an interposer class:
type
TPopupMenu = class(Vcl.Menus.TPopupMenu)
procedure Popup(X, Y: Integer); override;
end;
implemented as
{ TPopupMenu }
procedure TPopupMenu.Popup(X, Y: Integer);
begin
inherited;
Windows.Beep(400, 500); // post-popup code
end;
But of course it is nicer to create a true descendant class (TPopupMenuEx, perhaps?) that you register in the IDE. Add an FOnPopdown: TNotifyEvent private field, a DoPopdown protected function, and an OnPopdown published property. This precisely mimics the OnPopup mechanism.
Needless to say, this approach also works with a TTrayIcon's menu.

How to determine which component has the program focus in C++Builder

I am using C++Builder XE4 32bit VCL platform. I am writing for Windows OS.
I have a MainForm with a lot of components on it. When I press a keyboard arrow key and the Form's OnShortCut event is triggered, how do I determine which component has the program focus?
I have different actions which must be taken based on which component has the focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
//determine which component has the focus.
}
Use the global Screen->ActiveControl property:
Indicates which control currently has input focus on the screen.
Read ActiveControl to learn which windowed control object in the active form currently receives the input from the keyboard.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = Screen->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}
Or, you can use the Form's own ActiveControl property:
Specifies the control that has focus on the form.
Use ActiveControl to get or set the control that has focus on the form. Only one control can have focus at a given time in an application.
If the form does not have focus, ActiveControl is the control on the form that will receive focus when the form receives focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = this->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}

Two TListBoxes using the same TPopup menu?

I have one TListBox with 'movie' items and another one with 'snapshots'. I want to use one popup menu for both Listboxes. However, in the onClick event for a popups menuitem, how do I resolve which list box was used?
I tried this:
void __fastcall TMainForm::DeleteAll1Click(TObject *Sender)
{
TListBox* lb = dynamic_cast<TListBox*>(Sender);
if(lb == mMoviesLB)
{
...
where DeleteAll1 is a TMenuItem in the Popup menu. The lb is always NULL so there is something missing here..
The TPopupMenu::PopupComponent property tells you which UI control displayed the popup menu, eg:
void __fastcall TMainForm::DeleteAll1Click(TObject *Sender)
{
TListBox* lb = dynamic_cast<TListBox*>(PopupMenu1->PopupComponent);
...
}
If the TPopupMenu is displayed automatically (ie: right-clicking on a control when TPopupMenu::AutoPopup is true), the PopupComponent is populated automatically. However, if you call TPopupMenu::Popup() yourself, the PopupComponent will be NULL unless you assign it beforehand, eg:
PopupMenu1->PopupComponent = ListBox1;
PopupMenu1->Popup(X, Y);

Resources