From 65676a2882da289289c22a2810d92e56f7b9449c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 2 Dec 2012 23:49:25 +0000 Subject: [PATCH] Make wxChoice and wxComboBox behaviour same as in native controls in wxMSW. Keep the item selected from the drop down using keyboard when switching away from the control by pressing TAB: although this generates CBN_SELENDCANCEL notification, the selection is actually kept by the native controls in this case, so don't reset it ourselves -- even though it makes sense, it makes wx applications behave differently from the native ones. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73103 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/choice.h | 14 +++++++----- src/msw/choice.cpp | 49 ++++++++++++++++++++++++++--------------- src/msw/combobox.cpp | 15 +++++++------ 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/include/wx/msw/choice.h b/include/wx/msw/choice.h index 496c20f9f6..294605d527 100644 --- a/include/wx/msw/choice.h +++ b/include/wx/msw/choice.h @@ -106,7 +106,8 @@ protected: // common part of all ctors void Init() { - m_lastAcceptedSelection = wxID_NONE; + m_lastAcceptedSelection = + m_pendingSelection = wxID_NONE; m_heightOwn = wxDefaultCoord; } @@ -162,10 +163,13 @@ protected: virtual void MSWEndDeferWindowPos(); #endif // wxUSE_DEFERRED_SIZING - // last "completed" selection, i.e. not the transient one while the user is - // browsing the popup list: this is only used when != wxID_NONE which is - // the case while the drop down is opened - int m_lastAcceptedSelection; + // These variables are only used while the drop down is opened. + // + // The first one contains the item that had been originally selected before + // the drop down was opened and the second one the item we should select + // when the drop down is closed again. + int m_lastAcceptedSelection, + m_pendingSelection; // the height of the control itself if it was set explicitly or // wxDefaultCoord if it hadn't diff --git a/src/msw/choice.cpp b/src/msw/choice.cpp index b6670350f3..30dad609d0 100644 --- a/src/msw/choice.cpp +++ b/src/msw/choice.cpp @@ -740,9 +740,7 @@ bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) /* The native control provides a great variety in the events it sends in the different selection scenarios (undoubtedly for greater amusement of - the programmers using it). For the reference, here are the cases when - the final selection is accepted (things are quite interesting when it - is cancelled too): + the programmers using it). Here are the different cases: A. Selecting with just the arrows without opening the dropdown: 1. CBN_SELENDOK @@ -764,6 +762,12 @@ bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) Admire the different order of messages in all of those cases, it must surely have taken a lot of effort to Microsoft developers to achieve such originality. + + Additionally, notice that CBN_SELENDCANCEL doesn't seem to actually + cancel anything, if we get CBN_SELCHANGE before it, as it happens in + the case (B), the selection is still accepted. This doesn't make much + sense and directly contradicts MSDN documentation but is how the native + comboboxes behave and so we do the same thing. */ switch ( param ) { @@ -776,31 +780,40 @@ bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) break; case CBN_CLOSEUP: - // if the selection was accepted by the user, it should have been - // reset to wxID_NONE by CBN_SELENDOK, otherwise the selection was - // cancelled and we must restore the old one - if ( m_lastAcceptedSelection != wxID_NONE ) + if ( m_pendingSelection != wxID_NONE ) { - SetSelection(m_lastAcceptedSelection); - m_lastAcceptedSelection = wxID_NONE; + // This can only happen in the case (B), so set the item + // selected in the drop down as our real selection. + SendSelectionChangedEvent(wxEVT_COMMAND_CHOICE_SELECTED); + m_pendingSelection = wxID_NONE; } break; case CBN_SELENDOK: - // reset it to prevent CBN_CLOSEUP from undoing the selection (it's - // ok to reset it now as GetCurrentSelection() will now return the - // same thing anyhow) - m_lastAcceptedSelection = wxID_NONE; + // Reset the variables to prevent CBN_CLOSEUP from doing anything, + // it's not needed if we do get CBN_SELENDOK. + m_lastAcceptedSelection = + m_pendingSelection = wxID_NONE; SendSelectionChangedEvent(wxEVT_COMMAND_CHOICE_SELECTED); break; + case CBN_SELCHANGE: + // If we get this event after CBN_SELENDOK, i.e. cases (A) or (C) + // above, we don't have anything to do. But in the case (B) we need + // to remember that the selection should really change once the + // drop down is closed. + if ( m_lastAcceptedSelection != wxID_NONE ) + m_pendingSelection = GetCurrentSelection(); + break; - // don't handle CBN_SELENDCANCEL: just leave m_lastAcceptedSelection - // valid and the selection will be undone in CBN_CLOSEUP above - - // don't handle CBN_SELCHANGE neither, we don't want to generate events - // while the dropdown is opened -- but do add it if we ever need this + case CBN_SELENDCANCEL: + // Do not reset m_pendingSelection here -- it would make sense but, + // as described above, native controls keep the selection even when + // closing the drop down by pressing Escape or TAB, so conform to + // their behaviour. + m_lastAcceptedSelection = wxID_NONE; + break; default: return false; diff --git a/src/msw/combobox.cpp b/src/msw/combobox.cpp index d0b9836baf..b823cc5899 100644 --- a/src/msw/combobox.cpp +++ b/src/msw/combobox.cpp @@ -274,26 +274,27 @@ bool wxComboBox::MSWCommand(WXUINT param, WXWORD id) case CBN_DROPDOWN: // remember the last selection, just as wxChoice does m_lastAcceptedSelection = GetCurrentSelection(); - if ( m_lastAcceptedSelection == -1 ) - { - // but unlike with wxChoice we may have no selection but still - // have some text and we should avoid erasing it if the drop - // down is cancelled (see #8474) - m_lastAcceptedSelection = wxID_NONE; - } { wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_DROPDOWN, GetId()); event.SetEventObject(this); ProcessCommand(event); } break; + case CBN_CLOSEUP: + // Do the same thing as in wxChoice but using different event type. + if ( m_pendingSelection != wxID_NONE ) + { + SendSelectionChangedEvent(wxEVT_COMMAND_COMBOBOX_SELECTED); + m_pendingSelection = wxID_NONE; + } { wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_CLOSEUP, GetId()); event.SetEventObject(this); ProcessCommand(event); } break; + case CBN_SELENDOK: #ifndef __SMARTPHONE__ // we need to reset this to prevent the selection from being undone -- 2.45.2