From: Vadim Zeitlin Date: Sun, 13 Aug 2006 00:35:02 +0000 (+0000) Subject: better key handling for MSW and GTK (patch 1526666) X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/b445b6a76eddacd5bab45c71f640dad672d83393?ds=inline better key handling for MSW and GTK (patch 1526666) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@40579 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/combo.h b/include/wx/combo.h index 36d9a720e2..ec19e490d1 100644 --- a/include/wx/combo.h +++ b/include/wx/combo.h @@ -297,6 +297,14 @@ public: // Utilies needed by the popups or native implementations // + // Returns true if given key combination should toggle the popup. + // NB: This is a separate from other keyboard handling because: + // 1) Replaceability. + // 2) Centralized code (otherwise it'd be split up between + // wxComboCtrl key handler and wxVListBoxComboPopup's + // key handler). + virtual bool IsKeyPopupToggle(const wxKeyEvent& event) const = 0; + // Draws focus background (on combo control) in a way typical on platform. // Unless you plan to paint your own focus indicator, you should always call this // in your wxComboPopup::PaintComboControl implementation. @@ -351,7 +359,7 @@ protected: void CreateTextCtrl( int extraStyle, const wxValidator& validator ); // Installs standard input handler to combo (and optionally to the textctrl) - void InstallInputHandlers( bool alsoTextCtrl = true ); + void InstallInputHandlers(); // Draws dropbutton. Using wxRenderer or bitmaps, as appropriate. void DrawButton( wxDC& dc, const wxRect& rect, bool paintBg = true ); @@ -402,6 +410,7 @@ protected: void OnFocusEvent(wxFocusEvent& event); void OnTextCtrlEvent(wxCommandEvent& event); void OnSysColourChanged(wxSysColourChangedEvent& event); + void OnKeyEvent(wxKeyEvent& event); // Set customization flags (directs how wxComboCtrlBase helpers behave) void Customize( wxUint32 flags ) { m_iFlags |= flags; } @@ -432,10 +441,7 @@ protected: // popup interface wxComboPopup* m_popupInterface; - // this is for this control itself - wxEvtHandler* m_extraEvtHandler; - - // this is for text + // this is input etc. handler for the text control wxEvtHandler* m_textEvtHandler; // this is for the top level window diff --git a/include/wx/generic/combo.h b/include/wx/generic/combo.h index feca13db3c..e6df1706e0 100644 --- a/include/wx/generic/combo.h +++ b/include/wx/generic/combo.h @@ -65,6 +65,8 @@ public: virtual ~wxGenericComboControl(); + virtual bool IsKeyPopupToggle(const wxKeyEvent& event) const; + static int GetFeatures() { return wxComboCtrlFeatures::All; } #if defined(__WXUNIVERSAL__) diff --git a/include/wx/msw/combo.h b/include/wx/msw/combo.h index 7204e649d8..5c02285db6 100644 --- a/include/wx/msw/combo.h +++ b/include/wx/msw/combo.h @@ -60,6 +60,7 @@ public: virtual ~wxComboCtrl(); virtual void DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags ) const; + virtual bool IsKeyPopupToggle(const wxKeyEvent& event) const; static int GetFeatures() { return wxComboCtrlFeatures::All; } diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp index b9792d8df8..b5cd42e424 100644 --- a/src/common/combocmn.cpp +++ b/src/common/combocmn.cpp @@ -427,8 +427,7 @@ void wxComboPopup::Dismiss() // ---------------------------------------------------------------------------- // -// This is pushed to the event handler queue of either combo box -// or its textctrl (latter if not readonly combo). +// This is pushed to the event handler queue of the child textctrl. // class wxComboBoxExtraInputHandler : public wxEvtHandler { @@ -459,44 +458,16 @@ END_EVENT_TABLE() void wxComboBoxExtraInputHandler::OnKey(wxKeyEvent& event) { - int keycode = event.GetKeyCode(); + // Let the wxComboCtrl event handler have a go first. + wxComboCtrlBase* combo = m_combo; + wxObject* prevObj = event.GetEventObject(); - if ( keycode == WXK_TAB && !m_combo->IsPopupShown() ) - { - wxNavigationKeyEvent evt; - evt.SetFlags(wxNavigationKeyEvent::FromTab| - (!event.ShiftDown()?wxNavigationKeyEvent::IsForward: - wxNavigationKeyEvent::IsBackward)); - evt.SetEventObject(m_combo); - m_combo->GetParent()->GetEventHandler()->AddPendingEvent(evt); - return; - } - - if ( m_combo->IsPopupShown() ) - { - // pass it to the popped up control - m_combo->GetPopupControl()->GetControl()->AddPendingEvent(event); - } - else // no popup - { - int comboStyle = m_combo->GetWindowStyle(); - wxComboPopup* popupInterface = m_combo->GetPopupControl(); - - if ( !popupInterface ) - { - event.Skip(); - return; - } + event.SetId(combo->GetId()); + event.SetEventObject(combo); + combo->GetEventHandler()->ProcessEvent(event); - if ( (comboStyle & wxCB_READONLY) || - ( keycode != WXK_RIGHT && keycode != WXK_LEFT ) - ) - { - popupInterface->OnComboKeyEvent(event); - } - else - event.Skip(); - } + event.SetId(((wxWindow*)prevObj)->GetId()); + event.SetEventObject(prevObj); } void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event) @@ -511,17 +482,15 @@ void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event) m_combo->SetSelection(-1,-1); } - if ( event.GetId() != m_combo->GetId() ) - { - // Add textctrl set focus events as combo set focus events - // NOTE: Simply changing the event and skipping didn't seem - // to do the trick. - wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId()); - evt2.SetEventObject(m_combo); - m_combo->GetEventHandler()->ProcessEvent(evt2); - } - else - event.Skip(); + // Send focus indication to parent. + // NB: This is needed for cases where the textctrl gets focus + // instead of its parent. While this may trigger multiple + // wxEVT_SET_FOCUSes (since m_text->SetFocus is called + // from combo's focus event handler), they should be quite + // harmless. + wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId()); + evt2.SetEventObject(m_combo); + m_combo->GetEventHandler()->ProcessEvent(evt2); event.Skip(); } @@ -633,6 +602,7 @@ BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl) EVT_SET_FOCUS(wxComboCtrlBase::OnFocusEvent) EVT_KILL_FOCUS(wxComboCtrlBase::OnFocusEvent) //EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent) + EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent) EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent) EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged) END_EVENT_TABLE() @@ -652,7 +622,6 @@ void wxComboCtrlBase::Init() m_text = (wxTextCtrl*) NULL; m_popupInterface = (wxComboPopup*) NULL; - m_extraEvtHandler = (wxEvtHandler*) NULL; m_popupExtraHandler = (wxEvtHandler*) NULL; m_textEvtHandler = (wxEvtHandler*) NULL; @@ -718,17 +687,13 @@ bool wxComboCtrlBase::Create(wxWindow *parent, return true; } -void wxComboCtrlBase::InstallInputHandlers( bool alsoTextCtrl ) +void wxComboCtrlBase::InstallInputHandlers() { - if ( m_text && alsoTextCtrl ) + if ( m_text ) { m_textEvtHandler = new wxComboBoxExtraInputHandler(this); m_text->PushEventHandler(m_textEvtHandler); } - - wxComboBoxExtraInputHandler* inputHandler = new wxComboBoxExtraInputHandler(this); - PushEventHandler(inputHandler); - m_extraEvtHandler = inputHandler; } void @@ -774,13 +739,10 @@ wxComboCtrlBase::~wxComboCtrlBase() DestroyPopup(); - RemoveEventHandler(m_extraEvtHandler); - if ( m_text ) m_text->RemoveEventHandler(m_textEvtHandler); delete m_textEvtHandler; - delete m_extraEvtHandler; } @@ -1476,23 +1438,68 @@ void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event ) event.Skip(); } -void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& ) +void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) { - // First click is the first part of double-click - // Some platforms don't generate down-less mouse up-event - // (Windows does, GTK+2 doesn't), so that's why we have - // to do this. - m_timeLastMouseUp = ::wxGetLocalTimeMillis(); + if ( IsPopupShown() ) + { + // pass it to the popped up control + GetPopupControl()->GetControl()->AddPendingEvent(event); + } + else // no popup + { + int keycode = event.GetKeyCode(); - if ( m_text ) + if ( keycode == WXK_TAB ) + { + wxNavigationKeyEvent evt; + evt.SetFlags(wxNavigationKeyEvent::FromTab| + (!event.ShiftDown() ? wxNavigationKeyEvent::IsForward + : wxNavigationKeyEvent::IsBackward)); + evt.SetEventObject(this); + GetParent()->GetEventHandler()->AddPendingEvent(evt); + return; + } + + if ( IsKeyPopupToggle(event) ) + { + OnButtonClick(); + return; + } + + int comboStyle = GetWindowStyle(); + wxComboPopup* popupInterface = GetPopupControl(); + + if ( !popupInterface ) + { + event.Skip(); + return; + } + + if ( (comboStyle & wxCB_READONLY) || + (keycode != WXK_RIGHT && keycode != WXK_LEFT) ) + { + popupInterface->OnComboKeyEvent(event); + } + else + event.Skip(); + } +} + +void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event ) +{ + if ( event.GetEventType() == wxEVT_SET_FOCUS ) { - m_text->SetFocus(); + // First click is the first part of double-click + // Some platforms don't generate down-less mouse up-event + // (Windows does, GTK+2 doesn't), so that's why we have + // to do this. + m_timeLastMouseUp = ::wxGetLocalTimeMillis(); + + if ( m_text && m_text != ::wxWindow::FindFocus() ) + m_text->SetFocus(); } - else - // no need to check for m_widthCustomPaint - that - // area never gets special handling when selected - // (in writable mode, that is) - Refresh(); + + Refresh(); } void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) diff --git a/src/generic/combog.cpp b/src/generic/combog.cpp index 5559bff3c8..8366a70ea0 100644 --- a/src/generic/combog.cpp +++ b/src/generic/combog.cpp @@ -161,7 +161,7 @@ bool wxGenericComboControl::Create(wxWindow *parent, CreateTextCtrl( wxNO_BORDER, validator ); // Add keyboard input handlers for main control and textctrl - InstallInputHandlers( true ); + InstallInputHandlers(); // Set background SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering @@ -321,6 +321,28 @@ void wxGenericComboControl::OnMouseEvent( wxMouseEvent& event ) } +bool wxGenericComboControl::IsKeyPopupToggle(const wxKeyEvent& event) const +{ + int keycode = event.GetKeyCode(); + bool isPopupShown = IsPopupShown(); + + // This code is AFAIK appropriate for wxGTK. + + if ( isPopupShown ) + { + if ( keycode == WXK_ESCAPE || + ( keycode == WXK_UP && event.AltDown() ) ) + return true; + } + else + { + if ( keycode == WXK_DOWN && event.AltDown() ) + return true; + } + + return false; +} + #ifdef __WXUNIVERSAL__ bool wxGenericComboControl::PerformAction(const wxControlAction& action, diff --git a/src/generic/odcombo.cpp b/src/generic/odcombo.cpp index b48d79347d..c72efc280c 100644 --- a/src/generic/odcombo.cpp +++ b/src/generic/odcombo.cpp @@ -410,17 +410,24 @@ void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event)) void wxVListBoxComboPopup::OnKey(wxKeyEvent& event) { - // Select item if ENTER is pressed - if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER ) - { - DismissWithEvent(); - } - // Hide popup if ESC is pressed - else if ( event.GetKeyCode() == WXK_ESCAPE ) + // Hide popup if certain key or key combination was pressed + if ( m_combo->IsKeyPopupToggle(event) ) { StopPartialCompletion(); Dismiss(); } + else if ( event.AltDown() ) + { + // On both wxGTK and wxMSW, pressing Alt down seems to + // completely freeze things in popup (ie. arrow keys and + // enter won't work). + return; + } + // Select item if ENTER is pressed + else if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER ) + { + DismissWithEvent(); + } else { int comboStyle = m_combo->GetWindowStyle(); diff --git a/src/msw/combo.cpp b/src/msw/combo.cpp index 401dc1033d..255869e59f 100644 --- a/src/msw/combo.cpp +++ b/src/msw/combo.cpp @@ -135,7 +135,7 @@ bool wxComboCtrl::Create(wxWindow *parent, CreateTextCtrl( wxNO_BORDER, validator ); // Add keyboard input handlers for main control and textctrl - InstallInputHandlers( true ); + InstallInputHandlers(); // Prepare background for double-buffering SetBackgroundStyle( wxBG_STYLE_CUSTOM ); @@ -529,5 +529,30 @@ wxCoord wxComboCtrl::GetNativeTextIndent() const return NATIVE_TEXT_INDENT_CLASSIC; } +bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const +{ + int keycode = event.GetKeyCode(); + bool isPopupShown = IsPopupShown(); + + if ( isPopupShown && keycode == WXK_ESCAPE ) + return true; + + // On XP or with writable combo in Classic, + // Alt is required, in addition to up/down, to + // show the popup. + if ( keycode == WXK_DOWN || keycode == WXK_UP ) + { + if ( event.AltDown() || + ( !isPopupShown && + HasFlag(wxCB_READONLY) && + !wxUxThemeEngine::GetIfActive() + ) ) + { + return true; + } + } + + return false; +} #endif // wxUSE_COMBOCTRL