Use mouse position to find the item for selection events in wxMSW listbox.

Using LB_GETCARETINDEX doesn't work when the mouse is used to make selection
because it always returns the index of the last item, even if the mouse is
clicked below it, on an area without any listbox items. So use the mouse
position to find the item in this case but still use LB_GETCARETINDEX to find
the item when the keyboard is used.

This required adding a flag to wxListBox storing the kind of the last input
message that it received as there doesn't seem to be any way to determine how
the message was generated otherwise.

This code will be refactored/improved further in the next two commits.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64498 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2010-06-05 22:58:05 +00:00
parent 096d544752
commit f2eb4ad226
2 changed files with 37 additions and 3 deletions

View File

@ -149,6 +149,8 @@ public:
virtual void OnInternalIdle();
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam);
protected:
virtual wxSize DoGetBestClientSize() const;
@ -190,6 +192,11 @@ private:
// i.e. if we need to call SetHorizontalExtent() from OnInternalIdle()
bool m_updateHorizontalExtent;
// flag set to true when we get a keyboard event and reset to false when we
// get a mouse one: this is used to find the correct item for the selection
// event
bool m_selectedByKeyboard;
DECLARE_DYNAMIC_CLASS_NO_COPY(wxListBox)
};

View File

@ -153,6 +153,7 @@ void wxListBox::Init()
{
m_noItems = 0;
m_updateHorizontalExtent = false;
m_selectedByKeyboard = false;
}
bool wxListBox::Create(wxWindow *parent,
@ -644,16 +645,28 @@ wxSize wxListBox::DoGetBestClientSize() const
bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
{
wxEventType evtType;
int n;
int n = wxNOT_FOUND;
if ( param == LBN_SELCHANGE )
{
if ( HasMultipleSelection() )
return CalcAndSendEvent();
evtType = wxEVT_COMMAND_LISTBOX_SELECTED;
n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0);
// NB: conveniently enough, LB_ERR is the same as wxNOT_FOUND
if ( m_selectedByKeyboard )
{
// We shouldn't use the mouse position to find the item as mouse
// can be anywhere, ask the listbox itself. Notice that this can't
// be used when the item is selected using the mouse however as
// LB_GETCARETINDEX will always return a valid item, even if the
// mouse is clicked below all the items, which is why we find the
// item ourselves below in this case.
n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0);
}
else
{
n = HitTest(ScreenToClient(wxGetMousePosition()));
}
}
else if ( param == LBN_DBLCLK )
{
@ -670,6 +683,20 @@ bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
return n != wxNOT_FOUND && SendEvent(evtType, n, true /* selection */);
}
WXLRESULT
wxListBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
// Remember whether there was a keyboard or mouse event before
// LBN_SELCHANGE: this allows us to correctly determine the item affected
// by it in MSWCommand() above in any case.
if ( WM_KEYFIRST <= nMsg && nMsg <= WM_KEYLAST )
m_selectedByKeyboard = true;
else if ( WM_MOUSEFIRST <= nMsg && nMsg <= WM_MOUSELAST )
m_selectedByKeyboard = false;
return wxListBoxBase::MSWWindowProc(nMsg, wParam, lParam);
}
// ----------------------------------------------------------------------------
// owner-drawn list boxes support
// ----------------------------------------------------------------------------