Implement close function on keypress in c++ builder - c++

How do I implement Close (or exit) function when the ESC key is pressed in a form application in C++ builder?
Also, note that the form will have a number of components; it can't be only an empty form.
I tried to use this code but it doesn't work every time I press ESC.
void __fastcall TForm1::FormKeyPress(TObject *Sender, System::WideChar &Key) {
if (Key == VK_ESCAPE) {
this->Close();
}}
The code above doesn't work because focus is not always on the form and, if you have more components like EditBox, you have to disable VK_ESCAPE on every event and reference the desired function (which is, of course, a weak solution).

Using the TForm::KeyPreview property and TForm::OnKeyPress event is the best approach, but an alternativve would be to put a hidden TButton on the form and set its Cancel property to true, then you can call Close() in its OnClick event.

Set the KeyPreview property of the Form to true. This way, keyboard events occur on the form, before the active control.

Related

How to override keyPressEvent of QTextEdit?

I override keyPressEven() of widget QTextEdit:
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_0)
{
qDebug() << "Ok";
}
}
Button 0 works - show "Ok", but does not write in field of QTextEdit. Why? Thanks advance.
You need to call the base class implementation if you want to keep the default behaviour:
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_0)
{
qDebug() << "Ok";
}
QTextEdit::keyPressEvent(e);
}
See the docs for keyPressEvent.
In case someone using PySide2 is having trouble overriding QTextEdit's built-in keybindings, I post my solution here. Hopefully this is also useful for C++.
Scenario:
We are using a QTextEdit in an application and want to distribute a series of keybindings, but the text editor has already several hardcoded bindings. We want the editor to ignore them and hand them over to its parent, so they can be eventually handled by our code.
Problem:
While the docs say that whenever an event is ignored (e.g. by returning True in the installed eventFilter method) it automatically gets passed on to the parent, the truth is that when doing that for predefined keybindings QTextEdit did not hand them over: the event got ignored AND absorbed. So any textedit built-in keybindings filtered this way will be effectively globally disabled.
Direct event passing via sendEvent inside the editor's eventFilter had an interesting effect:
When calling sendEvent and returning super().sendEvent, the keybinding got executed by the editor AND the event passed to the receiver.
When calling sendEvent and returning True, the keybinding didn't get executed, and the event didn't get passed to the receiver.
When calling sendEvent and returning False, the keybinding didn't get executed, and the event passed to the receiver twice.
Furthermore:
Using event.ignore() didn't have any effect: the editor executed the built-in anyway.
Trying to discriminate via event.spontaneous() caused a segfault due to a missing pointer. Probably something got GCed but didn't try to debug that.
Trying to replace the event with a "dummy event" and call super also didn't work. Magically, the text editor kept executing the built-ins.
Maybe I missed something. Anyway, below I detail the approach that worked for me.
Solution:
The plan is to completely block the event, but broadcast it via signals, and then connect to them wherever we want. In your text editor instance, define the signal e.g. as follows:
eventCatched = QtCore.Signal(QtCore.QEvent)
Then, e.g. the following event filter will prevent execution of a few keybindings, and emit them once via eventCatched:
def eventFilter(self, obj, evt):
"""
Remember to install via self.installEventFilter(self)
"""
catch_control_keys = {QtCore.Qt.Key_Left, QtCore.Qt.Key_Right}
catch = False
# documentation for keys and modifiers:
# https://doc.qt.io/qtforpython-5/PySide2/QtCore/Qt.html
if evt.type() == QtCore.QEvent.KeyPress:
modifiers = evt.modifiers()
ctrl = bool(modifiers & QtCore.Qt.ControlModifier)
shift = bool(modifiers & QtCore.Qt.ShiftModifier)
alt = bool(modifiers & QtCore.Qt.AltModifier)
key = evt.key()
# catch all undo/redo builtins
if ((ctrl and shift and key == QtCore.Qt.Key_Z) or
evt.matches(QtGui.QKeySequence.Undo) or
evt.matches(QtGui.QKeySequence.Redo)):
catch = True
# catch specified control-keys
if ctrl and not shift and not alt:
if key in catch_control_keys:
catch = True
#
if catch:
# block event but send it as signal
self.eventCatched.emit(evt)
return True
else:
return super().eventFilter(obj, evt)
Then, we are free to connect the signal wherever we want to, we just need a method that handles events. In my case, I just wanted to pass them to the main window, which can be done with the following one-liner in the constructor:
text_editor.eventCatched.connect(lambda evt: QtCore.QCoreApplication.sendEvent(self, evt))
This way, whenever we catch an event in the text editor, it will be ignored and won't be propagated the standard way. Instead, a signal will be emitted, and we can subscribe to that signal to e.g. restart the propagation tree at a different point, as shown here via sendEvent.
Hope this helps!
Cheers,
Andres

Setting text input focus within a QWebView

I'm trying to display a Google login dialog in a QWebView, and as I recall, Google likes to set your keyboard focus to the first input field on the page (in this case, the e-mail field).
Unfortunately, the QWebView widget doesn't actually respect this behaviour, and therefore loads the page with keyboard focus on nothing at all:
So I decided dig about a little, and inserted this code snippet into my class logic:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
ui->webView->setFocus();
ui->webView->page()->mainFrame()->setFocus();
QWebElement el = ui->webView->page()->mainFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
el.evaluateJavaScript("this.focus()");
el.evaluateJavaScript("this.click()");
}
}
}
And the following declaration in my header file:
...
private slots:
void pageLoaded(bool);
Back in the class code, I connected the appropriate signal from the QWebView to my slot:
connect(ui->webView, SIGNAL(loadFinished(bool)), this, SLOT(pageLoaded(bool)));
Yes, I am throwing every possible thing I can think of at it to redirect keyboard focus to the first input box.
Unfortunately, the code did not seem to work, as while it did focus the right input box, I could not type anything inside of it until I clicked it myself, or pressed Tab:
Next I bound the function to my Control key, and proceeded to produce strange results.
If I placed focus into the password field manually, and pressed the Control key, I noticed that I would continue to have keyboard focus in the password field, but have 'visual' focus in the e-mail field:
Also, when I typed something in this 'state', occasionally a letter might 'leak' into the e-mail field before the visual and keyboard focus would 'reset' to the password field:
Is there a proper way of redirecting keyboard focus to an input field of my choosing?
I managed to redirect the keyboard input focus by simulating tab focusing via QKeyEvent:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
//Gets the first input element
QWebElement el = ui->webView->page()->currentFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
}
// Simulate a forward tab, then back.
QKeyEvent *forwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QKeyEvent *backwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier);
QCoreApplication::postEvent(ui->webView, forwardTab);
QCoreApplication::postEvent(ui->webView, backwardTab);
}
}
In my opinion, this does seem like a 'hack-ish' solution, so if there is a 'right' way to do this, I'm all ears.
pgh is right. Just set focus on the QWebView object during your app's initialization.
m_webView->setFocus();

How to prevent MFC dialog closing on Enter and Escape keys?

I know one method of preventing an MFC dialog from closing when the Enter or Esc keys are pressed, but I'd like to know more details of the process and all the common alternative methods for doing so.
Thanks in advance for any help.
When the user presses Enter key in a dialog two things can happen:
The dialog has a default control (see CDialog::SetDefID()). Then a WM_COMMAND with the ID of this control is sent to the dialog.
The dialog does not have a default control. Then WM_COMMAND with ID = IDOK is sent to the dialog.
With the first option, it may happen that the default control has a ID equal to IDOK. Then the results will be the same that in the second option.
By default, class CDialog has a handler for the WM_COMMAND(IDOK) that is to call to CDialog::OnOk(), that is a virtual function, and by default it calls EndDialog(IDOK) that closes the dialog.
So, if you want to avoid the dialog being closed, do one of the following.
Set the default control to other than IDOK.
Set a handler to the WM_COMMAND(IDOK) that does not call EndDialog().
Override CDialog::OnOk() and do not call the base implementation.
About IDCANCEL, it is similar but there is not equivalent SetDefID() and the ESC key is hardcoded. So to avoid the dialog being closed:
Set a handler to the WM_COMMAND(IDCANCEL) that does not call EndDialog().
Override CDialog::OnCancel() and do not call the base implementation.
There is an alternative to the previous answer, which is useful if you wish to still have an OK / Close button. If you override the PreTranslateMessage function, you can catch the use of VK_ESCAPE / VK_RETURN like so:
BOOL MyCtrl::PreTranslateMessage(MSG* pMsg)
{
if( pMsg->message == WM_KEYDOWN )
{
if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
return TRUE; // Do not process further
}
}
return CWnd::PreTranslateMessage(pMsg);
}
The answer of #the-forest-and-the-trees is quite good. Except one situation which was addressed by #oneworld. You need to filter messages which are not for dialog window:
BOOL CDialogDemoDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->hwnd == this->m_hWnd && pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
return TRUE; // Do not process further
}
}
return CWnd::PreTranslateMessage(pMsg);
}
Remember to add virtual in the header file.
When dealing with Dialog style MFC applications, the framework automatically hard codes a few items that must be overridden to prevent the application from quitting when the Esc or Enter keys are pressed. But there is a very simple way that doesn't require anything special such as implementing PreTranslateMessage() which is very much not recommend.
There are three functions that need to be in place:
The OnCancel() function to override the base class version and not to call it. This prevents the Esc key from closing the app.
The OnOK() function to override the base class version and not to call the base class. This prevents the Enter key from closing the app.
Because you've now prevented the dialog window from being closed you must now implement the OnClose() event handler. This function handler will handle when the Windows "X" button or the system command Close Alt+F4 are clicked. Now in order to close the application, you then call the base class version of one of the other functions OnOK(), OnCancel() if desired, to actually close the app. At this point you now have full control of how the app is closed.
Step 1
In the header, add the three function prototypes.
You can use the Class Wizard if you like to add the WM_CLOSE event handler but it's super simple to just type it in.
// DefaultDialogAppDlg.h
//
class CDefaultDialogAppDlg : public CDialogEx
{
// ... other code
protected:
virtual void OnCancel(){} // inline empty function
virtual void OnOK(){} // inline empty function
public:
afx_msg void OnClose(); // message handler for WM_CLOSE
// ...other code
};
Step 2
In the .cpp file, add the ON_WM_CLOSE() entry to the message map and the definitions for the three functions. Since OnCancel() and OnOK() are generally going to be empty, you could just inline them in the header if you want (see what I did in Step 1?).
The .cpp file will have something like this:
// DefaultDialogAppDlg.cpp
// ... other code
BEGIN_MESSAGE_MAP(CDefaultDialogAppDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE() // WM_CLOSE messages are handled here.
END_MESSAGE_MAP()
// ... other code
void CDefaultDialogAppDlg::OnClose()
{
// TODO: Add exit handling code here
// NOTE: to actually allow the program to end, call the base class
// version of either the OnOK() or OnCancel() function.
//CDialogEx::OnOK(); // returns 1 to theApp object
CDialogEx::OnCancel(); // returns 2 to theApp object
}
I simply override the OnOk event and instead of passing the message to the parent dialog, do nothing.
So it's basically simple as doing so:
void OnOk() override { /*CDialog::OnOK();*/ }
This should prevent the dialog from closing when pressing the return/enter key.
Make sure you don't #define CUSTOM_ID 2 because 2 is already defined for escape and I think 1 is defined for enter? Correct me if i'm wrong.

CPropertyPage derived dialog doesn't close on Esc when showing as a standalone dialog

I have a dialog which I need to show both inside a CPropertySheet and as a standalone dialog. I've chosen not to have 2 separate classes to avoid code redundancy (I make changes a lot in those dialogs, and having to sync 2 classes constantly would be hell), instead when I want to show it as a standalone dialog, I just call CPropertyPage::DoModal. This causes some problems, but I've fixed most of them.
However, some still remain, namely enter and esc don't work. Also pressing tab doesn't change the focus. This makes me think that CPropertyPage eats up all keyboard input, or maybe it tries to pass them to its parent.
Any ideas how I can override that behaviour in the standalone mode?
I believe this would work for you. I don't have a dialog that I can test this with so I am doing this all from memory but I believe you could add a bool that you set when you call DoModal or expose it as a property that you set before the call to DoModal to indicate it is running as a stand alone dialog, then override PreTranslateMessage like this:
CMyPropertyPage::PreTranslateMessage(MSG* pMsg)
{
if (m_runningAsStandalone && pMsg->message == WM_KEYDOWN)
{
UINT key = pMsg->wParam;
switch(pMsg->wParam)
{
case VK_RETURN:
OnOK();
return TRUE;
case VK_ESCAPE:
OnClose();
return TRUE;
}
}
return CPropertyPage::PreTranslateMessage(pMsg);
}
You may also find this link helpful http://support.microsoft.com/kb/125645

Qt - Esc should not close the dialog

How to make Esc key to minimize a dialog? By default it closes. Should I process KeyEvent or there is a better way?
I think you may use this:
void MyDialog::keyPressEvent(QKeyEvent *e) {
if(e->key() != Qt::Key_Escape)
QDialog::keyPressEvent(e);
else {/* minimize */}
}
Also have a look at Events and Event Filters docs.
Escape calls reject(). I override this function (in my case not to minimize the dialog but to prompt to save)
void MyDialog::reject() {if(cleanupIsOK()) done(0);}
Al_
Renaming the reject is correct. But be careful because if you want to close the dialog in other way you cannot call close.
MyDialog::reject(){
if(some_closing_condition)
{
QDialog::reject() //calls the default close.
}
else
{
//skip reject operation
}
}
I think that to do this, you would basically have to avoid inheriting from QDialog. The documentation for QDialog says:
Escape Key
If the user presses the Esc key in a
dialog, QDialog::reject() will be
called. This will cause the window to
close: The close event cannot be
ignored.
Interestingly Qt docs state ESC calls reject()
Escape Key
If the user presses the Esc key in a dialog, QDialog::reject() will be
called. This will cause the window to close: The close event cannot be
ignored
yet QDialog::reject() documentation says hides. i.e closeEvent() is not called, which I have confirmed to be the case.
void QDialog::reject()
Hides the modal dialog and sets the result code to Rejected