diff --git a/include/wx/combo.h b/include/wx/combo.h index aefae67cb8..c9910e8486 100644 --- a/include/wx/combo.h +++ b/include/wx/combo.h @@ -197,6 +197,10 @@ public: // get the popup window containing the popup control wxWindow *GetPopupWindow() const { return m_winPopup; } + // Set the control to use instead of the default text control for the main + // (always visible) part of the combobox. + void SetMainControl(wxWindow* win); + // Get the text control which is part of the combobox. wxTextCtrl *GetTextCtrl() const { return m_text; } @@ -623,8 +627,12 @@ protected: // This is used when control is unfocused and m_valueString is empty wxString m_hintText; - // the text control and button we show all the time + // This pointer is non-null if we use a text control, and not some other + // window, as the main control. wxTextCtrl* m_text; + + // the window and button we show all the time + wxWindow* m_mainWindow; wxWindow* m_btn; // wxPopupWindow or similar containing the window managed by the interface. diff --git a/interface/wx/combo.h b/interface/wx/combo.h index bbce09b047..c772aec966 100644 --- a/interface/wx/combo.h +++ b/interface/wx/combo.h @@ -751,6 +751,47 @@ public: */ virtual void SetInsertionPointEnd(); + /** + Uses the given window instead of the default text control as the main + window of the combo control. + + By default, combo controls without @c wxCB_READONLY style create a + wxTextCtrl which shows the current value and allows to edit it. This + method allows to use some other window instead of this wxTextCtrl. + + This method can be called after creating the combo fully, however in + this case a wxTextCtrl is unnecessarily created just to be immediately + destroyed when it's replaced by a custom window. If you wish to avoid + this, you can use the following approach, also shown in the combo + sample: + + @code + // Create the combo control using its default ctor. + wxComboCtrl* combo = new wxComboCtrl(); + + // Create the custom main control using its default ctor too. + SomeWindow* main = new SomeWindow(); + + // Set the custom main control before creating the combo. + combo->SetMainControl(main); + + // And only create it now: wxTextCtrl won't be unnecessarily + // created because the combo already has a main window. + combo->Create(panel, wxID_ANY, wxEmptyString); + + // Finally create the main window itself, now that its parent was + // created. + main->Create(combo, ...); + @endcode + + Note that when a custom main window is used, none of the methods of + this class inherited from wxTextEntry, such as SetValue(), Replace(), + SetInsertionPoint() etc do anything and simply return. + + @since 3.1.6 + */ + void SetMainControl(wxWindow* win); + //@{ /** Attempts to set the control margins. When margins are given as wxPoint, diff --git a/samples/combo/combo.cpp b/samples/combo/combo.cpp index 9c4d86c450..cddbc62ce4 100644 --- a/samples/combo/combo.cpp +++ b/samples/combo/combo.cpp @@ -884,7 +884,7 @@ MyFrame::MyFrame(const wxString& title) // rowSizer = new wxBoxSizer( wxHORIZONTAL ); rowSizer->Add( new wxStaticText(panel,wxID_ANY, - "wxComboCtrl with custom button action:"), 1, + "wxComboCtrl with custom button and custom main control:"), 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 4 ); @@ -898,7 +898,31 @@ MyFrame::MyFrame(const wxString& title) (long)0 ); + // This is a perfectly useless control, as the popup and main control + // don't interact with each other, but it shows that we can use something + // other than wxTextCtrl for the main part of wxComboCtrl too. + // + // In a real program, custom popup and main control would work together, + // i.e. changing selection in one of them would update the other one. + // + // Also note the complicated dance we need to go through to create the + // controls in the right order: we want to create the custom main control + // before actually creating the wxComboCtrl window, as otherwise it would + // unnecessarily create a wxTextCtrl by default, forcing us to use its + // default ctor and Create() later, but this, in turn, also requires using + // default ctor for the main control and creating it later too, as it can't + // be created before its parent window is. + wxComboCtrl* comboCustom = new wxComboCtrl(); + wxCheckBox* cbox = new wxCheckBox(); + comboCustom->SetMainControl(cbox); + comboCustom->Create(panel, wxID_ANY, wxEmptyString); + cbox->Create(comboCustom, wxID_ANY, "Checkbox as main control"); + cbox->SetBackgroundColour(*wxWHITE); + + comboCustom->SetPopupControl(new ListViewComboPopup()); + rowSizer->Add( fsc, 1, wxALIGN_CENTER_VERTICAL|wxALL, 4 ); + rowSizer->Add( comboCustom, 1, wxALIGN_CENTER_VERTICAL|wxALL, 4 ); colSizer->Add( rowSizer, 0, wxEXPAND|wxALL, 5 ); diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp index 02f082a9fc..307240e424 100644 --- a/src/common/combocmn.cpp +++ b/src/common/combocmn.cpp @@ -748,6 +748,7 @@ void wxComboCtrlBase::Init() m_popupWinState = Hidden; m_btn = NULL; m_text = NULL; + m_mainWindow = NULL; m_popupInterface = NULL; #if !wxUSE_POPUPWIN @@ -825,9 +826,34 @@ bool wxComboCtrlBase::Create(wxWindow *parent, return true; } +void +wxComboCtrlBase::SetMainControl(wxWindow* win) +{ + // We can't have both a custom window and a text control, so get rid of the + // latter if we have it. + if ( m_text ) + { + m_text->Destroy(); + + // Note that we currently always set it to NULL, even if the custom + // window is a (subclass of) wxTextCtrl because our m_text must be a + // wxComboCtrlTextCtrl for things to work correctly. + m_text = NULL; + } + + // We don't do anything with the previous main window, if any, it's the + // caller's responsibility to delete or hide it, as needed. + m_mainWindow = win; +} + void wxComboCtrlBase::CreateTextCtrl(int style) { + // If we're using a custom main window explicitly set using + // SetMainControl(), don't recreate it and just keep using it. + if ( m_mainWindow && !m_text ) + return; + if ( !(m_windowStyle & wxCB_READONLY) ) { if ( m_text ) @@ -843,6 +869,8 @@ wxComboCtrlBase::CreateTextCtrl(int style) style |= wxTE_PROCESS_ENTER; m_text = new wxComboCtrlTextCtrl(); + m_mainWindow = m_text; + m_text->Create(this, wxID_ANY, m_valueString, wxDefaultPosition, wxSize(10,-1), style); @@ -1036,7 +1064,7 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) m_tcArea.height = sz.y - ((m_widthCustomBorder+FOCUS_RING)*2); /* - if ( m_text ) + if ( m_mainWindow ) { ::wxMessageBox(wxString::Format(wxT("ButtonArea (%i,%i,%i,%i)\n"),m_btnArea.x,m_btnArea.y,m_btnArea.width,m_btnArea.height) + wxString::Format(wxT("TextCtrlArea (%i,%i,%i,%i)"),m_tcArea.x,m_tcArea.y,m_tcArea.width,m_tcArea.height)); @@ -1046,12 +1074,14 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust ) { - if ( !m_text ) + if ( !m_mainWindow || !m_mainWindow->GetHandle() ) return; wxSize sz = GetClientSize(); - if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER ) + // This function actually positions any main window, not just a text + // control, but it only does any special adjustments for m_text. + if ( m_text && (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER ) { int x; @@ -1096,10 +1126,11 @@ void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust } else { - // If it has border, have textctrl fill the entire text field. + // If the main window has border or is not a text control at all, have + // it fill the entire available space. int w = m_tcArea.width - m_widthCustomPaint; if (w < 0) w = 0; - m_text->SetSize( m_tcArea.x + m_widthCustomPaint, + m_mainWindow->SetSize( m_tcArea.x + m_widthCustomPaint, m_tcArea.y, w, m_tcArea.height ); @@ -1108,7 +1139,8 @@ void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust wxSize wxComboCtrlBase::DoGetBestSize() const { - int width = m_text ? m_text->GetBestSize().x : FromDIP(80); + int width = m_mainWindow && m_mainWindow->GetHandle() + ? m_mainWindow->GetBestSize().x : FromDIP(80); return GetSizeFromTextSize(width); } @@ -1229,8 +1261,8 @@ bool wxComboCtrlBase::Enable(bool enable) if ( m_btn ) m_btn->Enable(enable); - if ( m_text ) - m_text->Enable(enable); + if ( m_mainWindow ) + m_mainWindow->Enable(enable); Refresh(); @@ -1245,8 +1277,8 @@ bool wxComboCtrlBase::Show(bool show) if (m_btn) m_btn->Show(show); - if (m_text) - m_text->Show(show); + if (m_mainWindow) + m_mainWindow->Show(show); return true; } @@ -1256,14 +1288,14 @@ bool wxComboCtrlBase::SetFont ( const wxFont& font ) if ( !wxControl::SetFont(font) ) return false; - if ( m_text ) + if ( m_mainWindow ) { // Without hiding the wxTextCtrl there would be some // visible 'flicker' (at least on Windows XP). - m_text->Hide(); - m_text->SetFont(font); + m_mainWindow->Hide(); + m_mainWindow->SetFont(font); OnResize(); - m_text->Show(); + m_mainWindow->Show(); } return true; @@ -1278,12 +1310,12 @@ void wxComboCtrlBase::DoSetToolTip(wxToolTip *tooltip) if ( tooltip ) { const wxString &tip = tooltip->GetTip(); - if ( m_text ) m_text->SetToolTip(tip); + if ( m_mainWindow ) m_mainWindow->SetToolTip(tip); if ( m_btn ) m_btn->SetToolTip(tip); } else { - if ( m_text ) m_text->SetToolTip( NULL ); + if ( m_mainWindow ) m_mainWindow->SetToolTip( NULL ); if ( m_btn ) m_btn->SetToolTip( NULL ); } } @@ -1293,8 +1325,8 @@ bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour) { if ( wxControl::SetForegroundColour(colour) ) { - if ( m_text ) - m_text->SetForegroundColour(colour); + if ( m_mainWindow ) + m_mainWindow->SetForegroundColour(colour); return true; } return false; @@ -1302,8 +1334,8 @@ bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour) bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour) { - if ( m_text ) - m_text->SetBackgroundColour(colour); + if ( m_mainWindow ) + m_mainWindow->SetBackgroundColour(colour); m_tcBgCol = colour; m_hasTcBgCol = true; return true; @@ -1311,8 +1343,8 @@ bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour) wxColour wxComboCtrlBase::GetBackgroundColour() const { - if ( m_text ) - return m_text->GetBackgroundColour(); + if ( m_mainWindow ) + return m_mainWindow->GetBackgroundColour(); return m_tcBgCol; } @@ -1824,10 +1856,10 @@ void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event ) if ( event.GetEventType() == wxEVT_SET_FOCUS ) { - if ( !m_resetFocus && GetTextCtrl() && !GetTextCtrl()->HasFocus() ) + if ( !m_resetFocus && m_mainWindow && !m_mainWindow->HasFocus() ) { m_resetFocus = true; - GetTextCtrl()->SetFocus(); + m_mainWindow->SetFocus(); m_resetFocus = false; } } @@ -1840,8 +1872,8 @@ void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) ) if ( m_resetFocus ) { m_resetFocus = false; - if ( GetTextCtrl() ) - GetTextCtrl()->SetFocus(); + if ( m_mainWindow ) + m_mainWindow->SetFocus(); } } @@ -2387,14 +2419,14 @@ void wxComboCtrlBase::SetButtonBitmaps( const wxBitmap& bmpNormal, void wxComboCtrlBase::SetCustomPaintWidth( int width ) { - if ( m_text ) + if ( m_mainWindow ) { - // move textctrl accordingly - wxRect r = m_text->GetRect(); + // move the main window accordingly + wxRect r = m_mainWindow->GetRect(); int inc = width - m_widthCustomPaint; r.x += inc; r.width -= inc; - m_text->SetSize( r ); + m_mainWindow->SetSize( r ); } m_widthCustomPaint = width;