X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ddadf560da7ec1d7477a40ed5b9277b6468dc5e7..17d98558b35b75e3cad68d96841b4fa5a0c7e6ee:/src/common/combocmn.cpp diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp index fafd36425e..0b5b37d372 100644 --- a/src/common/combocmn.cpp +++ b/src/common/combocmn.cpp @@ -221,6 +221,26 @@ enum #endif +// Returns true if given popup window type can be classified as perfect +// on this platform. +static inline bool IsPopupWinTypePerfect( wxByte popupWinType ) +{ +#if POPUPWIN_IS_PERFECT && TRANSIENT_POPUPWIN_IS_PERFECT + wxUnusedVar(popupWinType); + return true; +#else + return ( popupWinType == POPUPWIN_GENERICTLW + #if POPUPWIN_IS_PERFECT + || popupWinType == POPUPWIN_WXPOPUPWINDOW + #endif + #if TRANSIENT_POPUPWIN_IS_PERFECT + || popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW + #endif + ); +#endif +} + + // // ** TODO ** // * wxComboPopupWindow for external use (ie. replace old wxUniv wxPopupComboWindow) @@ -465,6 +485,7 @@ private: BEGIN_EVENT_TABLE(wxComboPopupWindowEvtHandler, wxEvtHandler) EVT_KEY_DOWN(wxComboPopupWindowEvtHandler::OnKeyEvent) EVT_KEY_UP(wxComboPopupWindowEvtHandler::OnKeyEvent) + EVT_CHAR(wxComboPopupWindowEvtHandler::OnKeyEvent) #if USES_GENERICTLW EVT_ACTIVATE(wxComboPopupWindowEvtHandler::OnActivate) #endif @@ -483,7 +504,7 @@ void wxComboPopupWindowEvtHandler::OnKeyEvent( wxKeyEvent& event ) wxWindowList children = m_combo->GetPopupWindow()->GetChildren(); wxWindowList::iterator node = children.begin(); wxWindow* child = (wxWindow*)*node; - child->GetEventHandler()->AddPendingEvent(event); + child->GetEventHandler()->ProcessEvent(event); } #if USES_GENERICTLW @@ -552,6 +573,11 @@ void wxComboPopup::OnComboKeyEvent( wxKeyEvent& event ) event.Skip(); } +void wxComboPopup::OnComboCharEvent( wxKeyEvent& event ) +{ + event.Skip(); +} + void wxComboPopup::OnComboDoubleClick() { } @@ -655,17 +681,21 @@ void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event) // This is pushed to the event handler queue of the control in popup. // -class wxComboPopupExtraEventHandler : public wxEvtHandler +class wxComboPopupEvtHandler : public wxEvtHandler { public: - wxComboPopupExtraEventHandler( wxComboCtrlBase* combo ) + wxComboPopupEvtHandler( wxComboCtrlBase* combo ) : wxEvtHandler() { m_combo = combo; m_beenInside = false; + + // Let's make it so that the popup control will not receive mouse + // events until mouse left button has been up. + m_blockEventsToPopup = true; } - virtual ~wxComboPopupExtraEventHandler() { } + virtual ~wxComboPopupEvtHandler() { } void OnMouseEvent( wxMouseEvent& event ); @@ -673,24 +703,26 @@ public: void OnPopupDismiss() { m_beenInside = false; + m_blockEventsToPopup = true; } protected: wxComboCtrlBase* m_combo; - bool m_beenInside; + bool m_beenInside; + bool m_blockEventsToPopup; private: DECLARE_EVENT_TABLE() }; -BEGIN_EVENT_TABLE(wxComboPopupExtraEventHandler, wxEvtHandler) - EVT_MOUSE_EVENTS(wxComboPopupExtraEventHandler::OnMouseEvent) +BEGIN_EVENT_TABLE(wxComboPopupEvtHandler, wxEvtHandler) + EVT_MOUSE_EVENTS(wxComboPopupEvtHandler::OnMouseEvent) END_EVENT_TABLE() -void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event ) +void wxComboPopupEvtHandler::OnMouseEvent( wxMouseEvent& event ) { wxPoint pt = event.GetPosition(); wxSize sz = m_combo->GetPopupControl()->GetControl()->GetClientSize(); @@ -700,49 +732,97 @@ void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event ) event.Skip(); - if ( evtType == wxEVT_MOTION || - evtType == wxEVT_LEFT_DOWN || - evtType == wxEVT_RIGHT_DOWN ) + if ( !isInside || !m_combo->IsPopupShown() ) { - // Block motion and click events outside the popup - if ( !isInside || !m_combo->IsPopupShown() ) + // Mouse is outside the popup or popup is not actually shown (yet) + + if ( evtType == wxEVT_MOTION || + evtType == wxEVT_LEFT_DOWN || + evtType == wxEVT_LEFT_UP || + evtType == wxEVT_RIGHT_DOWN ) { + // Block motion and click events outside the popup event.Skip(false); } } - else if ( evtType == wxEVT_LEFT_UP ) + else { - if ( !m_combo->IsPopupShown() ) - { - event.Skip(false); - relayToButton = true; - } - else if ( !m_beenInside ) + // Mouse is inside the popup, which is fully shown + + m_beenInside = true; + + // Do not let the popup control respond to mouse events until + // mouse press used to display the popup has been lifted. This + // is important for users with slower mouse fingers or mouse + // drivers. Note that we have some redundancy here, just in + // case the popup is some native control that does not emit all + // mouse event types. + if ( evtType == wxEVT_MOTION ) { - if ( isInside ) + if ( m_blockEventsToPopup ) { - m_beenInside = true; + if ( event.LeftIsDown() ) + event.Skip(false); + else + m_blockEventsToPopup = false; } - else + } + else if ( evtType == wxEVT_LEFT_DOWN ) + { + if ( m_blockEventsToPopup ) + m_blockEventsToPopup = false; + } + else if ( evtType == wxEVT_LEFT_UP ) + { + if ( m_blockEventsToPopup ) { + // On first left up, stop blocking mouse events (but still + // block this one) + m_blockEventsToPopup = false; + event.Skip(false); + + // Also, this button press was (probably) used to display + // the popup, so relay it back to the drop-down button + // (which supposedly originated it). This is necessary to + // refresh it properly. relayToButton = true; } } + else if ( m_blockEventsToPopup ) + { + event.Skip(false); + } + } + + // + // Some mouse events to popup that happen outside it, before cursor + // has been inside the popup, need to be ignored by it but relayed to + // the dropbutton. + // + if ( evtType == wxEVT_LEFT_UP ) + { + if ( !m_combo->IsPopupShown() ) + { + event.Skip(false); + relayToButton = true; + } + else if ( !isInside && !m_beenInside ) + { + // Popup is shown but the cursor is not inside, nor it has been + relayToButton = true; + } } if ( relayToButton ) { - // - // Some mouse events to popup that happen outside it, before cursor - // has been inside the popup, need to be ignored by it but relayed to - // the dropbutton. - // - wxWindow* eventSink = m_combo; wxWindow* btn = m_combo->GetButton(); if ( btn ) - eventSink = btn; - - eventSink->GetEventHandler()->ProcessEvent(event); + btn->GetEventHandler()->ProcessEvent(event); + else + // Bypass the event handling mechanism. Using it would be + // confusing for the platform-specific wxComboCtrl + // implementations. + m_combo->HandleButtonMouseEvent(event, 0); } } @@ -780,6 +860,7 @@ BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl) EVT_IDLE(wxComboCtrlBase::OnIdleEvent) //EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent) EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent) + EVT_CHAR(wxComboCtrlBase::OnCharEvent) EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent) EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged) END_EVENT_TABLE() @@ -796,7 +877,7 @@ void wxComboCtrlBase::Init() m_text = NULL; m_popupInterface = NULL; - m_popupExtraHandler = NULL; + m_popupEvtHandler = NULL; m_textEvtHandler = NULL; #if INSTALL_TOPLEV_HANDLER @@ -824,6 +905,7 @@ void wxComboCtrlBase::Init() m_extRight = 0; m_marginLeft = -1; m_iFlags = 0; + m_textCtrlStyle = 0; m_timeCanAcceptClick = 0; m_resetFocus = false; @@ -887,7 +969,7 @@ wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator) // not used by the wxPropertyGrid and therefore the tab is processed by // looking at ancestors to see if they have wxTAB_TRAVERSAL. The // navigation event is then sent to the wrong window. - style |= wxTE_PROCESS_TAB; + style |= wxTE_PROCESS_TAB | m_textCtrlStyle; if ( HasFlag(wxTE_PROCESS_ENTER) ) style |= wxTE_PROCESS_ENTER; @@ -904,6 +986,7 @@ wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator) m_text->Create(this, wxID_ANY, m_valueString, wxDefaultPosition, wxSize(10,-1), style, validator); + m_text->SetHint(m_hintText); } } @@ -913,8 +996,27 @@ void wxComboCtrlBase::OnThemeChange() // be the correct colour and themed brush. Instead we'll use // wxSYS_COLOUR_WINDOW in the EVT_PAINT handler as needed. #ifndef __WXMAC__ - SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -#endif + #if defined(__WXMSW__) || defined(__WXGTK__) + wxVisualAttributes vattrs = wxComboBox::GetClassDefaultAttributes(); + #else + wxVisualAttributes vattrs; + vattrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + vattrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + #endif + + // Only change the colours if application has not specified + // custom ones. + if ( !m_hasFgCol ) + { + SetOwnForegroundColour(vattrs.colFg); + m_hasFgCol = false; + } + if ( !m_hasBgCol ) + { + SetOwnBackgroundColour(vattrs.colBg); + m_hasBgCol = false; + } +#endif // !__WXMAC__ } wxComboCtrlBase::~wxComboCtrlBase() @@ -1303,6 +1405,27 @@ wxValidator* wxComboCtrlBase::GetValidator() } #endif // wxUSE_VALIDATORS +bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour) +{ + if ( wxControl::SetForegroundColour(colour) ) + { + if ( m_text ) + m_text->SetForegroundColour(colour); + return true; + } + return false; +} + +bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour) +{ + if ( wxControl::SetBackgroundColour(colour) ) + { + if ( m_text ) + m_text->SetBackgroundColour(colour); + return true; + } + return false; +} // ---------------------------------------------------------------------------- // painting // ---------------------------------------------------------------------------- @@ -1353,20 +1476,46 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags selRect.width -= wcp + (focusSpacingX*2); wxColour bgCol; + wxColour fgCol; + bool doDrawSelRect = true; + // Determine foreground colour + if ( isEnabled ) + { + if ( doDrawFocusRect ) + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + } + else if ( m_hasFgCol ) + { + // Honour the custom foreground colour + fgCol = GetForegroundColour(); + } + else + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + } + } + else + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); + } + + // Determine background colour if ( isEnabled ) { - // If popup is hidden and this control is focused, - // then draw the focus-indicator (selbgcolor background etc.). if ( doDrawFocusRect ) { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) ); bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); } + else if ( m_hasBgCol ) + { + // Honour the custom background colour + bgCol = GetBackgroundColour(); + } else { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); #ifndef __WXMAC__ // see note in OnThemeChange doDrawSelRect = false; bgCol = GetBackgroundColour(); @@ -1377,7 +1526,6 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags } else { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) ); #ifndef __WXMAC__ // see note in OnThemeChange bgCol = GetBackgroundColour(); #else @@ -1385,6 +1533,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags #endif } + dc.SetTextForeground( fgCol ); dc.SetBrush( bgCol ); if ( doDrawSelRect ) { @@ -1688,15 +1837,35 @@ void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event ) } } } - else - if ( IsPopupShown() ) + else if ( evtType == wxEVT_MOUSEWHEEL ) { - // relay (some) mouse events to the popup - if ( evtType == wxEVT_MOUSEWHEEL ) - m_popup->GetEventHandler()->AddPendingEvent(event); + if ( IsPopupShown() ) + { + // relay (some) mouse events to the popup + m_popup->GetEventHandler()->ProcessEvent(event); + } + else if ( event.GetWheelAxis() == 0 && + event.GetWheelRotation() != 0 && + event.GetModifiers() == 0 ) + { + // Translate mousewheel actions into key up/down. This is + // the simplest way of getting native behaviour: scrolling the + // wheel moves selection up/down by one item. + wxKeyEvent kevent(wxEVT_KEY_DOWN); + kevent.m_keyCode = event.GetWheelRotation() > 0 + ? WXK_UP + : WXK_DOWN; + GetEventHandler()->ProcessEvent(kevent); + } + else + { + event.Skip(); + } } else if ( evtType ) + { event.Skip(); + } } void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) @@ -1704,13 +1873,17 @@ void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) if ( IsPopupShown() ) { // pass it to the popped up control - GetPopupControl()->GetControl()->GetEventHandler()->AddPendingEvent(event); + GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event); } else // no popup { - if ( GetParent()->HasFlag(wxTAB_TRAVERSAL) && - HandleAsNavigationKey(event) ) - return; + wxWindow* mainCtrl = GetMainWindowOfCompositeControl(); + + if ( mainCtrl->GetParent()->HasFlag(wxTAB_TRAVERSAL) ) + { + if ( mainCtrl->HandleAsNavigationKey(event) ) + return; + } if ( IsKeyPopupToggle(event) ) { @@ -1739,6 +1912,27 @@ void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) } } +void wxComboCtrlBase::OnCharEvent(wxKeyEvent& event) +{ + if ( IsPopupShown() ) + { + // pass it to the popped up control + GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event); + } + else // no popup + { + wxComboPopup* popupInterface = GetPopupControl(); + if ( popupInterface ) + { + popupInterface->OnComboCharEvent(event); + } + else + { + event.Skip(); + } + } +} + void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event ) { if ( event.GetEventType() == wxEVT_SET_FOCUS ) @@ -1821,8 +2015,8 @@ void wxComboCtrlBase::CreatePopup() popupInterface->Create(m_winPopup); m_popup = popup = popupInterface->GetControl(); - m_popupExtraHandler = new wxComboPopupExtraEventHandler(this); - popup->PushEventHandler( m_popupExtraHandler ); + m_popupEvtHandler = new wxComboPopupEvtHandler(this); + popup->PushEventHandler( m_popupEvtHandler ); // This may be helpful on some platforms // (eg. it bypasses a wxGTK popupwindow bug where @@ -1838,23 +2032,20 @@ void wxComboCtrlBase::DestroyPopup() HidePopup(true); if ( m_popup ) - m_popup->RemoveEventHandler(m_popupExtraHandler); + m_popup->RemoveEventHandler(m_popupEvtHandler); - delete m_popupExtraHandler; + wxDELETE(m_popupEvtHandler); - delete m_popupInterface; + wxDELETE(m_popupInterface); if ( m_winPopup ) { m_winPopup->RemoveEventHandler(m_popupWinEvtHandler); - delete m_popupWinEvtHandler; - m_popupWinEvtHandler = NULL; + wxDELETE(m_popupWinEvtHandler); m_winPopup->Destroy(); + m_winPopup = NULL; } - m_popupExtraHandler = NULL; - m_popupInterface = NULL; - m_winPopup = NULL; m_popup = NULL; } @@ -1955,7 +2146,8 @@ void wxComboCtrlBase::ShowPopup() // that if transient popup is open, then tab traversal is to be ignored. // However, I think this code would still be needed for cases where // transient popup doesn't work yet (wxWinCE?). - wxWindow* parent = GetParent(); + wxWindow* mainCtrl = GetMainWindowOfCompositeControl(); + wxWindow* parent = mainCtrl->GetParent(); int parentFlags = parent->GetWindowStyle(); if ( parentFlags & wxTAB_TRAVERSAL ) { @@ -2114,6 +2306,13 @@ void wxComboCtrlBase::DoShowPopup( const wxRect& rect, int WXUNUSED(flags) ) winPopup->Show(); m_popupWinState = Visible; + + // If popup window was a generic top-level window, or the + // wxPopupWindow implemenation on this platform is classified as + // perfect, then we should be able to safely set focus to the popup + // control. + if ( IsPopupWinTypePerfect(m_popupWinType) ) + m_popup->SetFocus(); } else if ( IsPopupWindowState(Hidden) ) { @@ -2143,8 +2342,8 @@ void wxComboCtrlBase::OnPopupDismiss(bool generateEvent) // Inform popup control itself m_popupInterface->OnDismiss(); - if ( m_popupExtraHandler ) - ((wxComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss(); + if ( m_popupEvtHandler ) + ((wxComboPopupEvtHandler*)m_popupEvtHandler)->OnPopupDismiss(); #if INSTALL_TOPLEV_HANDLER // Remove top level window event handler @@ -2215,6 +2414,9 @@ void wxComboCtrlBase::SetButtonPosition( int width, int height, m_btnSide = side; m_btnSpacingX = spacingX; + if ( width > 0 || height > 0 || spacingX ) + m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON; + RecalcAndRefresh(); } @@ -2341,6 +2543,14 @@ wxCoord wxComboCtrlBase::GetNativeTextIndent() const return DEFAULT_TEXT_INDENT; } +void wxComboCtrlBase::SetTextCtrlStyle( int style ) +{ + m_textCtrlStyle = style; + + if ( m_text ) + m_text->SetWindowStyle(style); +} + // ---------------------------------------------------------------------------- // methods forwarded to wxTextCtrl // ---------------------------------------------------------------------------- @@ -2473,4 +2683,19 @@ void wxComboCtrlBase::Undo() m_text->Undo(); } +bool wxComboCtrlBase::SetHint(const wxString& hint) +{ + m_hintText = hint; + bool res = true; + if ( m_text ) + res = m_text->SetHint(hint); + Refresh(); + return res; +} + +wxString wxComboCtrlBase::GetHint() const +{ + return m_hintText; +} + #endif // wxUSE_COMBOCTRL