This question already has an answer here:
How can my form detect KeyDown events when another control has the focus?
(1 answer)
Closed 2 years ago.
so I've made a simple little calculator and I want to add a little more functionality to it by allowing the user to use the Numpad on their keyboard instead of using a mouse to interact with the GUI.
How can I receive input from the keyboard?
Thanks in advance!
PrimeBeat
I have done that. I mean a simple calculator application.
To have keyboard user input, I put a TEdit on the form. This is the only component on the form getting keyboard input. Then at runtime I move the edit out of view:
procedure TCalculatorForm.FormResize(Sender: TObject);
begin
EntryEdit.Left := ClientWidth + 10;
end;
This makes the TEdit invisible but it stay active. When the user type on the keyboard, this TEdit receive the characters. I use the OnKeyPress event to get hand on it and act in the calculation like the user had clicked in the buttons.
Related
The component I use is a descendant of TStringGrid, called TDataGrid (which can be found on Torry). Unfortunately, it has a small bug which doesn't seem to be present in the original TStringGrid component. I have the options goEditing and goAlwaysShowEditor both enabled, so I'm expecting the text of a cell to be selected when the control receives input focus. But in the case of TDataGrid this doesn't happen if I press TAB to move from one control into the grid control. It receives input focus, but nothing is selected, and the caret isn't even visible. Obviously, this is very misleading for a user.
The original TStringGrid component has similar inconsistencies, where if you have goAlwaysShowEditor enabled, there is always one cell with its text "exposed" (focused, sort of, even if the grid control itself doesn't have focus), so if you left-click in that cell, it also won't select the text, just enable a caret. I have been able to get around this however, by simply disabling goAlwaysShowEditor when the grid control doesn't have focus and subsequently enabling it when the grid receives focus.
So does a TStringGrid have any way of selecting the text of a cell? I know how to set the focus to a cell, but I haven't figured out a way to actually select the text. Really would appreciate any ideas to get around this!
FWIW, I'm using Delphi 10.3
I've found one solution that actually seems to work, but it only handles the very specific scenario when you TAB into the grid control and there may be other cases that need to be handled..
So, controlling the selection of the text within a cell IS possible, but it will not work if you try to control it during the OnEnter event. So instead, I've had to resort to checking for TAB in the OnKeyUp event, where it will work.
You need to expose protected members of a TStringGrid to access the appropriate methods. It's not the prettiest solution, but it works at least...
type TStringGridHelper = class helper for TStringGrid
procedure HHideEditor;
procedure HShowEditor;
end;
procedure TDataGridHelper.HHideEditor;
begin
HideEditor;
end;
procedure TDataGridHelper.HShowEditor;
begin
ShowEditor;
end;
And in the OnKeyUp event handler...
if Key = VK_TAB then begin
StringGrid.HHideEditor;
StringGrid.HShowEditor;
end;
I have a fairly large program, and I need a way to suppress all the Windows beeps you get on pressing enter. I found a function that allows me to suppress all beep sounds, but I need the error sounds to be fired when something goes wrong, so that is not an option. I saw you could suppress the sound for a single textbox by setting the Key to 0, but this is not an option as there are a LOT of keypress events in my program.
You can suppress these beeps that are produced when an edit control has the focus and you press ESCAPE and ENTER by setting TForm.KeyPreview to True and then adding the following OnKeyPress event handler for your form:
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#13) or (Key=#27) then
Key := #0;
end;
If you have some controls for which you wish to accept ENTER or ESCAPE, for instance multi-line edit controls, then you could leave KeyPreview as False, and handle OnKeyPress for each single-line edit control:
procedure TForm1.EditKeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#13) or (Key=#27) then
Key := #0;
end;
Or you could leave KeyPreview as True and then have a form-wide OnKeyPress handler that discriminated based on the control that has the focus. For example, a rather crude example:
function IsSingleLineEdit(Edit: TCustomEdit): Boolean;
var
Style: DWORD;
begin
Style := GetWindowLongPtr(Edit.Handle, GWL_STYLE);
Result := Style and ES_MULTILINE = 0;
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if (ActiveControl is TCustomEdit)
and IsSingleLineEdit(TCustomEdit(ActiveControl)) then
if (Key=#13) or (Key=#27) then
Key := #0;
end;
However, I would suggest that you do none of these things. I suggest that you leave the behaviour as it currently is. If neither the form, nor the focused control is going to response to these key presses, then beeping is the most appropriate response. The user presses ESCAPE when she wants to cancel the current dialog. If you are not going to respond to that, then the system beeps to indicate that. Likewise, the user presses ENTER when she wants to accept the current dialog. Again, since your dialog doesn't respond, a beep is appropriate.
I saw you could surpress the sound for a single textbox by setting the Key to 0, but this is not an option as there are a LOT of keypress events in my program. Is there any solution to this?
This is actually the correct approach to do this provided that specific EditBox does have proper code to execute certain action upon pressing the Enter key.
If you don't have any specific code to process some action on pressing the Enter key when in certain field then the Ding sound should be heard to tell the user he is doing something wrong.
Same would go if you are limited specific EditBoxes to only numbers. So that every time when the user presses any letter key the Ding sound is played and the user knows that letters are not allowed. Othervise the user might thing that there is something wrong with his keyboard.
You wouldn't want to be a guy who made a software whose usage resulted in lots of keyboards getting busted. Or would you? :-)
So I'm afraid you would have to do lots of code editing for a lot of KeyPress events. I must admit that I kinda feel sorry for you. Been in similar position once.
As for David Heffernan suggestion about filtering the Enter and Escape keys using forms KeyPreview.
Don't! Realy! DON'T! Why?
Becouse by doing so you could interfeere with normal functionality of some other components.
For instance using that code with combination of ComboBox prevents you from using Escape key to colapse the expanded combo box.
I've built a custom control that I'm trying to send input to. It will accept mouse input and report MouseDown, MouseMove and MouseUp correctly, but for whatever reason, it won't accept keyboard input. When I click on it, it doesn't receive focus, and any keys I press get interpreted by whatever control had the focus already.
This is probably something really simple. The first place I thought to look was in the ControlStyle property, but the only thing I can see in the helpfile about keyboard input is csNoStdEvents, which disables it, and my control doesn't have that. So what do I need to do to make it so my control can receive input focus?
A few things to try:
On MouseDown, call Windows.SetFocus(Handle). In my experience, the WinAPI function SetFocus often works better than the VCL's SetFocus method.
In response to the WM_GETDLGCODE message, reply with Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS or DLGC_WANTTAB or DLGC_WANTALLKEYS;
Could it be as simple as calling SetFocus on mouse down?
procedure TYourCustomControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
begin
inherited;
if CanFocus then
SetFocus;
end;
Do you have WS_TABSTOP set? You don't have input focus without that, I believe. But this is based on a recollection from nearly 10 years ago, when I was writing my own syntax-highlighting code editor, for which I have long since lost the source.
{TWinControl.}TabStop := True; ought to do. A quick test app with a do-nothing component derived from TWinControl and displaying a dialog for key events seems to show that it makes all the difference.
I've checked the code for my control and I can't see anything that might stop this working. Are you calling "inherited" in the Create procedure?
I do handle the following, but nothing special:
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
Is the keystroke available at form level? That is, is KeyPreview turned on, and can you see the keystroke in the form's OnKeypress event? You can follow it from there in the debugger. Is the control (as Dan indicated) suitable for keyboard input? For instance, a TLabel, although it displays text, is a graphical control.
I encountered some behavior in FMX that is different in VCL. This relates to how main menu shortcuts are processed. It seems that an FMX application will intercept all shortcut keystrokes in the main form such that any other active forms do not see these keystrokes.
This means for example if you have a TMemo on a second form and the main form uses the Ctrl-V main menu short cut, it's not possible to paste text into the memo using Ctrl-V. This is unique to FMX, VCL works as expected where a second form receives all keystrokes irrespective of shortcuts on a main form.
An answer in this question How to intercept Menu shortcut event in Firemonkey explains how to intercept shortcuts in the main form.
The question here is how to get these intercepted keystrokes from the main form to the currently active form, so that controls such as TMemo or TEdit on the second form work as expected?
Based on the answer in How to intercept Menu shortcut event in Firemonkey, one way to pass on the mainmenu shortcut keystrokes, eg Ctrl-A, to the currently active form is to use this code in the main form:
TMenuItem = class(FMX.Menus.TMenuItem)
protected
procedure DialogKey(var Key: Word; Shift: TShiftState); override;
end;
procedure TMenuItem.DialogKey(var Key: Word; Shift: TShiftState);
var ch : char;
begin
if (ssCtrl in Shift) and (Key = 65){A} then
begin
ch := #0;
Screen.ActiveForm.KeyDown(Key, ch, Shift);
exit;
end;
inherited;
end;
An alternative answer to the first one, it's works but not everyone might like it, plus it has limitations. For simple cases is should work.
Before showing the second form, remove the shortcuts from the mainform, then restore the shortcuts when the form returns control to the mainform. Works ok if the second form is shown using showmodal. Pity there isn't a form OnShortCut event as we have in VCL. Eg rough example:
(MainMenu.Items[0] as TMenuItem).Items[0].ShortCut := TextToShortCut('');
FormTwo.ShowModal;
(MainMenu.Items[0] as TMenuItem).Items[0].ShortCut := TextToShortCut('Ctrl+V');
In Keyboard Extension, in UIInputViewController, I can get notified through textDidChange(textInput: UITextInput) of any change, and use self.textDocumentProxy.documentContextBefore/AfterInput to get current text.
Problem arise when user 'select text'. The 'before' and 'after' "sees" only the part before and after selection.
Is there any way to know if user touched any of the Copy-Cut-Select in a textField (given - we don't have access to that field from Keyboard Extension)?
Something like:
if(self.textDocumentProxy.someProperty == UIDocumentProxyTextCut)
Or any other way to know which of the UITextField action (Copy/Cut/Select) did the user took?
I think we can not find out if user touched on Copy/Cut/Paste menu
Because a custom keyboard can draw only within the primary view of its
UIInputViewController object, it cannot select text. Text selection is
under the control of the app that is using the keyboard. If that app
provides an editing menu interface (such as for Cut, Copy, and Paste),
the keyboard has no access to it. A custom keyboard cannot offer
inline autocorrection controls near the insertion point.
Source: https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/Keyboard.html
P/s:
I saw self.textDocumentProxy.documentContextAfterInput is always NIL. Who know why?
How can we know where the cursor is to provide suggestion for user?