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.
|
||||
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:
|
||||
// implement this to return the associated GtkEntry or another widget
|
||||
// implementing GtkEditable
|
||||
|
@@ -81,6 +81,10 @@ protected:
|
||||
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE;
|
||||
#endif // wxUSE_OLE
|
||||
|
||||
// Helper for wxTE_PROCESS_ENTER handling: activates the default button in
|
||||
// the dialog containing this control if any.
|
||||
bool ClickDefaultButtonIfPossible();
|
||||
|
||||
private:
|
||||
// implement this to return the HWND of the EDIT control
|
||||
virtual WXHWND GetEditHWND() const = 0;
|
||||
|
@@ -41,9 +41,13 @@
|
||||
Sorts the entries in the list alphabetically. Notice that this style
|
||||
is not currently implemented in wxOSX.
|
||||
@style{wxTE_PROCESS_ENTER}
|
||||
The control will generate the event @c wxEVT_TEXT_ENTER
|
||||
(otherwise pressing Enter key is either processed internally by the
|
||||
control or used for navigation between dialog controls).
|
||||
The control will generate the event @c wxEVT_TEXT_ENTER that can be
|
||||
handled by the program. Otherwise, i.e. either if this style not
|
||||
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
|
||||
|
||||
@beginEventEmissionTable{wxCommandEvent}
|
||||
|
@@ -992,9 +992,13 @@ public:
|
||||
|
||||
@beginStyleTable
|
||||
@style{wxTE_PROCESS_ENTER}
|
||||
The control will generate the event @c wxEVT_TEXT_ENTER
|
||||
(otherwise pressing Enter key is either processed internally by the
|
||||
control or used to activate the default button of the dialog, if any).
|
||||
The control will generate the event @c wxEVT_TEXT_ENTER that can be
|
||||
handled by the program. Otherwise, i.e. either if this style not
|
||||
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}
|
||||
Normally, TAB key is used for keyboard navigation and pressing it in
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
@@ -1728,6 +1728,12 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event )
|
||||
event.SetString(GetValue());
|
||||
if ( HandleWindowEvent(event) )
|
||||
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__
|
||||
|
||||
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
|
||||
|
@@ -243,6 +243,9 @@ bool wxComboBox::MSWProcessEditSpecialKey(WXWPARAM vkey)
|
||||
// beep if it gets it
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ClickDefaultButtonIfPossible() )
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@@ -2199,6 +2199,9 @@ wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
||||
{
|
||||
bool processed = wxTextCtrlBase::MSWHandleMessage(rc, nMsg, wParam, lParam);
|
||||
|
||||
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
|
||||
@@ -2209,19 +2212,13 @@ wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
||||
// 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() )
|
||||
if ( !processed && wParam == VK_RETURN )
|
||||
{
|
||||
MSWClickButtonIfPossible(MSWGetDefaultButtonFor(this));
|
||||
|
||||
if ( ClickDefaultButtonIfPossible() )
|
||||
processed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
switch ( nMsg )
|
||||
{
|
||||
case WM_GETDLGCODE:
|
||||
{
|
||||
// Ensure that the result value is initialized even if the base
|
||||
|
@@ -1036,4 +1036,15 @@ wxPoint wxTextEntry::DoGetMargins() const
|
||||
return wxPoint(left, top);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// input handling
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool wxTextEntry::ClickDefaultButtonIfPossible()
|
||||
{
|
||||
return !wxIsAnyModifierDown() &&
|
||||
wxWindow::MSWClickButtonIfPossible(
|
||||
wxWindow::MSWGetDefaultButtonFor(GetEditableWindow()));
|
||||
}
|
||||
|
||||
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
||||
|
@@ -231,4 +231,22 @@ void ComboBoxTestCase::IsEmpty()
|
||||
#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
|
||||
|
@@ -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
|
||||
|
@@ -10,8 +10,12 @@
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/app.h"
|
||||
#include "wx/dialog.h"
|
||||
#include "wx/event.h"
|
||||
#include "wx/sizer.h"
|
||||
#include "wx/textctrl.h"
|
||||
#include "wx/textentry.h"
|
||||
#include "wx/timer.h"
|
||||
#include "wx/window.h"
|
||||
#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);
|
||||
};
|
||||
|
||||
// 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_
|
||||
|
Reference in New Issue
Block a user