Merge remote-tracking branch 'sunset/process-enter'
Fix wxTE_PROCESS_ENTER logic for wxMSW and wxGTK too. See https://github.com/wxWidgets/wxWidgets/pull/1415 Closes #18273.
This commit is contained in:
@@ -114,6 +114,10 @@ protected:
|
|||||||
// currently.
|
// currently.
|
||||||
virtual void EnableTextChangedEvents(bool enable) wxOVERRIDE;
|
virtual void EnableTextChangedEvents(bool enable) wxOVERRIDE;
|
||||||
|
|
||||||
|
// Helper for wxTE_PROCESS_ENTER handling: activates the default button in
|
||||||
|
// the dialog containing this control if any.
|
||||||
|
bool ClickDefaultButtonIfPossible();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// implement this to return the associated GtkEntry or another widget
|
// implement this to return the associated GtkEntry or another widget
|
||||||
// implementing GtkEditable
|
// implementing GtkEditable
|
||||||
|
@@ -81,6 +81,10 @@ protected:
|
|||||||
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE;
|
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE;
|
||||||
#endif // wxUSE_OLE
|
#endif // wxUSE_OLE
|
||||||
|
|
||||||
|
// Helper for wxTE_PROCESS_ENTER handling: activates the default button in
|
||||||
|
// the dialog containing this control if any.
|
||||||
|
bool ClickDefaultButtonIfPossible();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// implement this to return the HWND of the EDIT control
|
// implement this to return the HWND of the EDIT control
|
||||||
virtual WXHWND GetEditHWND() const = 0;
|
virtual WXHWND GetEditHWND() const = 0;
|
||||||
|
@@ -41,9 +41,13 @@
|
|||||||
Sorts the entries in the list alphabetically. Notice that this style
|
Sorts the entries in the list alphabetically. Notice that this style
|
||||||
is not currently implemented in wxOSX.
|
is not currently implemented in wxOSX.
|
||||||
@style{wxTE_PROCESS_ENTER}
|
@style{wxTE_PROCESS_ENTER}
|
||||||
The control will generate the event @c wxEVT_TEXT_ENTER
|
The control will generate the event @c wxEVT_TEXT_ENTER that can be
|
||||||
(otherwise pressing Enter key is either processed internally by the
|
handled by the program. Otherwise, i.e. either if this style not
|
||||||
control or used for navigation between dialog controls).
|
specified at all, or it is used, but there is no event handler for
|
||||||
|
this event or the event handler called wxEvent::Skip() to avoid
|
||||||
|
overriding the default handling, pressing Enter key is either
|
||||||
|
processed internally by the control or used to activate the default
|
||||||
|
button of the dialog, if any.
|
||||||
@endStyleTable
|
@endStyleTable
|
||||||
|
|
||||||
@beginEventEmissionTable{wxCommandEvent}
|
@beginEventEmissionTable{wxCommandEvent}
|
||||||
|
@@ -992,9 +992,13 @@ public:
|
|||||||
|
|
||||||
@beginStyleTable
|
@beginStyleTable
|
||||||
@style{wxTE_PROCESS_ENTER}
|
@style{wxTE_PROCESS_ENTER}
|
||||||
The control will generate the event @c wxEVT_TEXT_ENTER
|
The control will generate the event @c wxEVT_TEXT_ENTER that can be
|
||||||
(otherwise pressing Enter key is either processed internally by the
|
handled by the program. Otherwise, i.e. either if this style not
|
||||||
control or used to activate the default button of the dialog, if any).
|
specified at all, or it is used, but there is no event handler for
|
||||||
|
this event or the event handler called wxEvent::Skip() to avoid
|
||||||
|
overriding the default handling, pressing Enter key is either
|
||||||
|
processed internally by the control or used to activate the default
|
||||||
|
button of the dialog, if any.
|
||||||
@style{wxTE_PROCESS_TAB}
|
@style{wxTE_PROCESS_TAB}
|
||||||
Normally, TAB key is used for keyboard navigation and pressing it in
|
Normally, TAB key is used for keyboard navigation and pressing it in
|
||||||
a control switches focus to the next one. With this style, this
|
a control switches focus to the next one. With this style, this
|
||||||
|
@@ -231,6 +231,12 @@ void wxComboBox::OnChar( wxKeyEvent &event )
|
|||||||
// down list upon RETURN.
|
// down list upon RETURN.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We disable built-in default button activation when
|
||||||
|
// wxTE_PROCESS_ENTER is used, but we still should activate it
|
||||||
|
// if the event wasn't handled, so do it from here.
|
||||||
|
if ( ClickDefaultButtonIfPossible() )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -1728,6 +1728,12 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event )
|
|||||||
event.SetString(GetValue());
|
event.SetString(GetValue());
|
||||||
if ( HandleWindowEvent(event) )
|
if ( HandleWindowEvent(event) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// We disable built-in default button activation when
|
||||||
|
// wxTE_PROCESS_ENTER is used, but we still should activate it
|
||||||
|
// if the event wasn't handled, so do it from here.
|
||||||
|
if ( ClickDefaultButtonIfPossible() )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1139,4 +1139,35 @@ wxString wxTextEntry::GetHint() const
|
|||||||
}
|
}
|
||||||
#endif // __WXGTK3__
|
#endif // __WXGTK3__
|
||||||
|
|
||||||
|
bool wxTextEntry::ClickDefaultButtonIfPossible()
|
||||||
|
{
|
||||||
|
GtkWidget* const widget = GTK_WIDGET(GetEntry());
|
||||||
|
|
||||||
|
// This does the same thing as gtk_entry_real_activate() in GTK itself.
|
||||||
|
//
|
||||||
|
// Note: in GTK 4 we should probably just use gtk_widget_activate_default().
|
||||||
|
GtkWidget* const toplevel = gtk_widget_get_toplevel(widget);
|
||||||
|
if ( GTK_IS_WINDOW (toplevel) )
|
||||||
|
{
|
||||||
|
GtkWindow* const window = GTK_WINDOW(toplevel);
|
||||||
|
|
||||||
|
if ( window )
|
||||||
|
{
|
||||||
|
GtkWidget* const default_widget = gtk_window_get_default_widget(window);
|
||||||
|
GtkWidget* const focus_widget = gtk_window_get_focus(window);
|
||||||
|
|
||||||
|
if ( widget != default_widget &&
|
||||||
|
!(widget == focus_widget &&
|
||||||
|
(!default_widget ||
|
||||||
|
!gtk_widget_get_sensitive(default_widget))) )
|
||||||
|
{
|
||||||
|
if ( gtk_window_activate_default(window) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
||||||
|
@@ -243,6 +243,9 @@ bool wxComboBox::MSWProcessEditSpecialKey(WXWPARAM vkey)
|
|||||||
// beep if it gets it
|
// beep if it gets it
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ClickDefaultButtonIfPossible() )
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@@ -2199,29 +2199,26 @@ wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
|||||||
{
|
{
|
||||||
bool processed = wxTextCtrlBase::MSWHandleMessage(rc, nMsg, wParam, lParam);
|
bool processed = wxTextCtrlBase::MSWHandleMessage(rc, nMsg, wParam, lParam);
|
||||||
|
|
||||||
// Handle the special case of "Enter" key: the user code needs to specify
|
|
||||||
// wxTE_PROCESS_ENTER style to get it in the first place, but if this flag
|
|
||||||
// is used, then even if the wxEVT_TEXT_ENTER handler skips the event, the
|
|
||||||
// normal action of this key is not performed because IsDialogMessage() is
|
|
||||||
// not called and, also, an annoying beep is generated by EDIT default
|
|
||||||
// WndProc.
|
|
||||||
//
|
|
||||||
// Fix these problems by explicitly performing the default function of this
|
|
||||||
// key (which would be done by MSWProcessMessage() if we didn't have
|
|
||||||
// wxTE_PROCESS_ENTER) and preventing the default WndProc from getting it.
|
|
||||||
if ( nMsg == WM_CHAR &&
|
|
||||||
!processed &&
|
|
||||||
HasFlag(wxTE_PROCESS_ENTER) &&
|
|
||||||
wParam == VK_RETURN &&
|
|
||||||
!wxIsAnyModifierDown() )
|
|
||||||
{
|
|
||||||
MSWClickButtonIfPossible(MSWGetDefaultButtonFor(this));
|
|
||||||
|
|
||||||
processed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( nMsg )
|
switch ( nMsg )
|
||||||
{
|
{
|
||||||
|
case WM_CHAR:
|
||||||
|
// Handle the special case of "Enter" key: the user code needs to specify
|
||||||
|
// wxTE_PROCESS_ENTER style to get it in the first place, but if this flag
|
||||||
|
// is used, then even if the wxEVT_TEXT_ENTER handler skips the event, the
|
||||||
|
// normal action of this key is not performed because IsDialogMessage() is
|
||||||
|
// not called and, also, an annoying beep is generated by EDIT default
|
||||||
|
// WndProc.
|
||||||
|
//
|
||||||
|
// Fix these problems by explicitly performing the default function of this
|
||||||
|
// key (which would be done by MSWProcessMessage() if we didn't have
|
||||||
|
// wxTE_PROCESS_ENTER) and preventing the default WndProc from getting it.
|
||||||
|
if ( !processed && wParam == VK_RETURN )
|
||||||
|
{
|
||||||
|
if ( ClickDefaultButtonIfPossible() )
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_GETDLGCODE:
|
case WM_GETDLGCODE:
|
||||||
{
|
{
|
||||||
// Ensure that the result value is initialized even if the base
|
// Ensure that the result value is initialized even if the base
|
||||||
|
@@ -1036,4 +1036,15 @@ wxPoint wxTextEntry::DoGetMargins() const
|
|||||||
return wxPoint(left, top);
|
return wxPoint(left, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// input handling
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool wxTextEntry::ClickDefaultButtonIfPossible()
|
||||||
|
{
|
||||||
|
return !wxIsAnyModifierDown() &&
|
||||||
|
wxWindow::MSWClickButtonIfPossible(
|
||||||
|
wxWindow::MSWGetDefaultButtonFor(GetEditableWindow()));
|
||||||
|
}
|
||||||
|
|
||||||
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
||||||
|
@@ -231,4 +231,22 @@ void ComboBoxTestCase::IsEmpty()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("wxComboBox::ProcessEnter", "[wxComboBox][enter]")
|
||||||
|
{
|
||||||
|
struct ComboBoxCreator
|
||||||
|
{
|
||||||
|
static wxControl* Do(wxWindow* parent, int style)
|
||||||
|
{
|
||||||
|
const wxString choices[] = { "foo", "bar", "baz" };
|
||||||
|
|
||||||
|
return new wxComboBox(parent, wxID_ANY, wxString(),
|
||||||
|
wxDefaultPosition, wxDefaultSize,
|
||||||
|
WXSIZEOF(choices), choices,
|
||||||
|
style);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProcessEnter(&ComboBoxCreator::Do);
|
||||||
|
}
|
||||||
|
|
||||||
#endif //wxUSE_COMBOBOX
|
#endif //wxUSE_COMBOBOX
|
||||||
|
@@ -1269,4 +1269,19 @@ void TextCtrlTestCase::XYToPositionSingleLine()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("wxTextCtrl::ProcessEnter", "[wxTextCtrl][enter]")
|
||||||
|
{
|
||||||
|
struct TextCtrlCreator
|
||||||
|
{
|
||||||
|
static wxControl* Do(wxWindow* parent, int style)
|
||||||
|
{
|
||||||
|
return new wxTextCtrl(parent, wxID_ANY, wxString(),
|
||||||
|
wxDefaultPosition, wxDefaultSize,
|
||||||
|
style);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProcessEnter(&TextCtrlCreator::Do);
|
||||||
|
}
|
||||||
|
|
||||||
#endif //wxUSE_TEXTCTRL
|
#endif //wxUSE_TEXTCTRL
|
||||||
|
@@ -10,8 +10,12 @@
|
|||||||
|
|
||||||
#ifndef WX_PRECOMP
|
#ifndef WX_PRECOMP
|
||||||
#include "wx/app.h"
|
#include "wx/app.h"
|
||||||
|
#include "wx/dialog.h"
|
||||||
#include "wx/event.h"
|
#include "wx/event.h"
|
||||||
|
#include "wx/sizer.h"
|
||||||
|
#include "wx/textctrl.h"
|
||||||
#include "wx/textentry.h"
|
#include "wx/textentry.h"
|
||||||
|
#include "wx/timer.h"
|
||||||
#include "wx/window.h"
|
#include "wx/window.h"
|
||||||
#endif // WX_PRECOMP
|
#endif // WX_PRECOMP
|
||||||
|
|
||||||
@@ -364,3 +368,122 @@ void TextEntryTestCase::UndoRedo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if wxUSE_UIACTIONSIMULATOR
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
enum ProcessEnter
|
||||||
|
{
|
||||||
|
ProcessEnter_No,
|
||||||
|
ProcessEnter_ButSkip,
|
||||||
|
ProcessEnter_WithoutSkipping
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestDialog : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TestDialog(TextLikeControlCreator controlCreator,
|
||||||
|
ProcessEnter processEnter)
|
||||||
|
: wxDialog(wxTheApp->GetTopWindow(), wxID_ANY, "Test dialog"),
|
||||||
|
m_control((*controlCreator)(this,
|
||||||
|
processEnter == ProcessEnter_No
|
||||||
|
? 0
|
||||||
|
: wxTE_PROCESS_ENTER)),
|
||||||
|
m_processEnter(processEnter),
|
||||||
|
m_gotEnter(false)
|
||||||
|
{
|
||||||
|
// We can't always bind this handler because wx will helpfully
|
||||||
|
// complain if we bind it without using wxTE_PROCESS_ENTER.
|
||||||
|
if ( processEnter != ProcessEnter_No )
|
||||||
|
m_control->Bind(wxEVT_TEXT_ENTER, &TestDialog::OnTextEnter, this);
|
||||||
|
|
||||||
|
wxSizer* const sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
sizer->Add(m_control, wxSizerFlags().Expand());
|
||||||
|
sizer->Add(CreateStdDialogButtonSizer(wxOK));
|
||||||
|
SetSizerAndFit(sizer);
|
||||||
|
|
||||||
|
CallAfter(&TestDialog::SimulateEnter);
|
||||||
|
|
||||||
|
m_timer.Bind(wxEVT_TIMER, &TestDialog::OnTimeOut, this);
|
||||||
|
m_timer.StartOnce(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GotEnter() const { return m_gotEnter; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnTextEnter(wxCommandEvent& e)
|
||||||
|
{
|
||||||
|
m_gotEnter = true;
|
||||||
|
|
||||||
|
switch ( m_processEnter )
|
||||||
|
{
|
||||||
|
case ProcessEnter_No:
|
||||||
|
FAIL("Shouldn't be getting wxEVT_TEXT_ENTER at all");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ProcessEnter_ButSkip:
|
||||||
|
e.Skip();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ProcessEnter_WithoutSkipping:
|
||||||
|
// Close the dialog with a different exit code than what
|
||||||
|
// pressing the OK button would have generated.
|
||||||
|
EndModal(wxID_APPLY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTimeOut(wxTimerEvent&)
|
||||||
|
{
|
||||||
|
EndModal(wxID_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulateEnter()
|
||||||
|
{
|
||||||
|
wxUIActionSimulator sim;
|
||||||
|
m_control->SetFocus();
|
||||||
|
sim.Char(WXK_RETURN);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxControl* const m_control;
|
||||||
|
const ProcessEnter m_processEnter;
|
||||||
|
wxTimer m_timer;
|
||||||
|
bool m_gotEnter;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void TestProcessEnter(TextLikeControlCreator controlCreator)
|
||||||
|
{
|
||||||
|
SECTION("Without wxTE_PROCESS_ENTER")
|
||||||
|
{
|
||||||
|
TestDialog dlg(controlCreator, ProcessEnter_No);
|
||||||
|
REQUIRE( dlg.ShowModal() == wxID_OK );
|
||||||
|
CHECK( !dlg.GotEnter() );
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("With wxTE_PROCESS_ENTER but skipping")
|
||||||
|
{
|
||||||
|
TestDialog dlgProcessEnter(controlCreator, ProcessEnter_ButSkip);
|
||||||
|
REQUIRE( dlgProcessEnter.ShowModal() == wxID_OK );
|
||||||
|
CHECK( dlgProcessEnter.GotEnter() );
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("With wxTE_PROCESS_ENTER without skipping")
|
||||||
|
{
|
||||||
|
TestDialog dlgProcessEnter(controlCreator, ProcessEnter_WithoutSkipping);
|
||||||
|
REQUIRE( dlgProcessEnter.ShowModal() == wxID_APPLY );
|
||||||
|
CHECK( dlgProcessEnter.GotEnter() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !wxUSE_UIACTIONSIMULATOR
|
||||||
|
|
||||||
|
void TestProcessEnter(TextLikeControlCreator WXUNUSED(controlCreator))
|
||||||
|
{
|
||||||
|
WARN("Skipping wxTE_PROCESS_ENTER tests: wxUIActionSimulator not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // wxUSE_UIACTIONSIMULATOR/!wxUSE_UIACTIONSIMULATOR
|
||||||
|
@@ -75,4 +75,11 @@ private:
|
|||||||
wxDECLARE_NO_COPY_CLASS(TextEntryTestCase);
|
wxDECLARE_NO_COPY_CLASS(TextEntryTestCase);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function to call to test that wxTE_PROCESS_ENTER is handled correctly for
|
||||||
|
// the controls of the type created by the given creator function when they're
|
||||||
|
// placed in a dialog with a default button.
|
||||||
|
typedef wxControl* (*TextLikeControlCreator)(wxWindow* parent, int style);
|
||||||
|
|
||||||
|
void TestProcessEnter(TextLikeControlCreator controlCreator);
|
||||||
|
|
||||||
#endif // _WX_TESTS_CONTROLS_TEXTENTRYTEST_H_
|
#endif // _WX_TESTS_CONTROLS_TEXTENTRYTEST_H_
|
||||||
|
Reference in New Issue
Block a user