From 000f8ef422a5c45dcc868ebfb4e86e9eb7dab38d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 21 Jan 2019 20:01:08 +0100 Subject: [PATCH 1/8] Put private wxQtChoice class inside an anonymous namespace No real changes, just avoid polluting global scope unnecessarily. --- src/qt/choice.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index 1bd6511202..8c49d5e933 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -12,6 +12,9 @@ #include "wx/qt/private/winevent.h" #include +namespace +{ + class wxQtChoice : public wxQtEventSignalHandler< QComboBox, wxChoice > { public: @@ -36,6 +39,8 @@ void wxQtChoice::activated(int WXUNUSED(index)) handler->SendSelectionChangedEvent(wxEVT_CHOICE); } +} // anonymous namespace + wxChoice::wxChoice() : m_qtComboBox(NULL) From b27971c501e1f711d843010cd76d1d32d828659a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 16 Jan 2019 11:03:52 +0000 Subject: [PATCH 2/8] Use case-insensitive comparison in Qt wxComboBox and wxChoice Define LexicalSortProxyModel to use the same sort order in wxQt as in the other ports. --- include/wx/qt/choice.h | 2 ++ src/qt/choice.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/qt/combobox.cpp | 5 +++++ 3 files changed, 45 insertions(+) diff --git a/include/wx/qt/choice.h b/include/wx/qt/choice.h index 06602b9b68..5b4e51129a 100644 --- a/include/wx/qt/choice.h +++ b/include/wx/qt/choice.h @@ -71,6 +71,8 @@ protected: virtual void DoClear(); virtual void DoDeleteOneItem(unsigned int pos); + virtual void InitialiseSort(QComboBox *combo); + QComboBox *m_qtComboBox; private: diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index 8c49d5e933..cc1587825e 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -10,11 +10,39 @@ #include "wx/choice.h" #include "wx/qt/private/winevent.h" + #include +#include namespace { +class LexicalSortProxyModel : public QSortFilterProxyModel +{ +public: + bool lessThan( const QModelIndex &left, const QModelIndex &right ) const wxOVERRIDE + { + const QVariant leftData = sourceModel()->data( left ); + const QVariant rightData = sourceModel()->data( right ); + + if ( leftData.type() != QVariant::String ) + return false; + + int insensitiveResult = QString::compare( + leftData.value(), + rightData.value(), + Qt::CaseInsensitive ); + + if ( insensitiveResult == 0 ) + { + return QString::compare( leftData.value(), + rightData.value() ) < 0; + } + + return insensitiveResult < 0; + } +}; + class wxQtChoice : public wxQtEventSignalHandler< QComboBox, wxChoice > { public: @@ -47,6 +75,14 @@ wxChoice::wxChoice() : { } +void wxChoice::InitialiseSort(QComboBox *combo) +{ + QSortFilterProxyModel *proxyModel = new LexicalSortProxyModel(); + proxyModel->setSourceModel(combo->model()); + combo->model()->setParent(proxyModel); + combo->setModel(proxyModel); +} + wxChoice::wxChoice( wxWindow *parent, wxWindowID id, const wxPoint& pos, @@ -95,6 +131,8 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id, { m_qtComboBox = new wxQtChoice( parent, this ); + InitialiseSort(m_qtComboBox); + while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); diff --git a/src/qt/combobox.cpp b/src/qt/combobox.cpp index 4ca376f23d..36efe13300 100644 --- a/src/qt/combobox.cpp +++ b/src/qt/combobox.cpp @@ -128,6 +128,8 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id, const wxString& name ) { m_qtComboBox = new wxQtComboBox( parent, this ); + InitialiseSort(m_qtComboBox); + while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); m_qtComboBox->setEditText( wxQtConvertString( value )); @@ -140,7 +142,10 @@ void wxComboBox::SetValue(const wxString& value) if ( HasFlag(wxCB_READONLY) ) SetStringSelection(value); else + { wxTextEntry::SetValue(value); + m_qtComboBox->setEditText( wxQtConvertString(value) ); + } } wxString wxComboBox::DoGetValue() const From cfdf80d5866aac9fce9f5df56aa9c943598fe25a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 21 Jan 2019 19:57:33 +0100 Subject: [PATCH 3/8] Rename wxChoice::InitialiseSort() to QtInitSort() Use the prefix to indicate that this function is unique to this port. Also don't make unnecessarily make it virtual. --- include/wx/qt/choice.h | 2 +- src/qt/choice.cpp | 4 ++-- src/qt/combobox.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wx/qt/choice.h b/include/wx/qt/choice.h index 5b4e51129a..208005ec06 100644 --- a/include/wx/qt/choice.h +++ b/include/wx/qt/choice.h @@ -71,7 +71,7 @@ protected: virtual void DoClear(); virtual void DoDeleteOneItem(unsigned int pos); - virtual void InitialiseSort(QComboBox *combo); + void QtInitSort(QComboBox *combo); QComboBox *m_qtComboBox; diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index cc1587825e..4bc672b71e 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -75,7 +75,7 @@ wxChoice::wxChoice() : { } -void wxChoice::InitialiseSort(QComboBox *combo) +void wxChoice::QtInitSort( QComboBox *combo ) { QSortFilterProxyModel *proxyModel = new LexicalSortProxyModel(); proxyModel->setSourceModel(combo->model()); @@ -131,7 +131,7 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id, { m_qtComboBox = new wxQtChoice( parent, this ); - InitialiseSort(m_qtComboBox); + QtInitSort( m_qtComboBox ); while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); diff --git a/src/qt/combobox.cpp b/src/qt/combobox.cpp index 36efe13300..3ca015a019 100644 --- a/src/qt/combobox.cpp +++ b/src/qt/combobox.cpp @@ -128,7 +128,7 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id, const wxString& name ) { m_qtComboBox = new wxQtComboBox( parent, this ); - InitialiseSort(m_qtComboBox); + QtInitSort( m_qtComboBox ); while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); From f2538a0bc423886f6d838cb112af038366fe915e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 17 Jan 2019 12:00:01 +0000 Subject: [PATCH 4/8] Reset selection in after deleting selected item from wxChoice --- src/qt/choice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index 4bc672b71e..20c9dac7fb 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -240,6 +240,12 @@ void wxChoice::DoClear() void wxChoice::DoDeleteOneItem(unsigned int pos) { + const int selection = GetSelection(); + + if ( selection >= 0 && static_cast(selection) == pos ) + { + SetSelection( wxNOT_FOUND ); + } m_qtComboBox->removeItem(pos); } From b0defd5876e13e92308fc4375b32d128b18ca2cd Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 17 Jan 2019 12:00:01 +0000 Subject: [PATCH 5/8] Don't select the newly added item in wxChoice --- src/qt/choice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index 20c9dac7fb..491398b718 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -212,11 +212,17 @@ int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items, int wxChoice::DoInsertOneItem(const wxString& item, unsigned int pos) { + // Maintain unselected state + const bool unselected = m_qtComboBox->currentIndex() == -1; + m_qtComboBox->insertItem(pos, wxQtConvertString(item)); if ( IsSorted() ) m_qtComboBox->model()->sort(0); + if ( unselected ) + m_qtComboBox->setCurrentIndex(-1); + return pos; } From 1a7f9124b66989abaaa726e369c5b7ad3418e831 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 17 Jan 2019 12:00:01 +0000 Subject: [PATCH 6/8] Update insertion point in wxTextEntry::Remove() Move the insertion point to the end of the string if its position has become invalid after removing part of the text. --- src/qt/textentry.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/textentry.cpp b/src/qt/textentry.cpp index f1c088165a..6a5cbc71dd 100644 --- a/src/qt/textentry.cpp +++ b/src/qt/textentry.cpp @@ -20,9 +20,11 @@ void wxTextEntry::WriteText(const wxString& WXUNUSED(text)) void wxTextEntry::Remove(long from, long to) { + const long insertionPoint = GetInsertionPoint(); wxString string = GetValue(); string.erase(from, to - from); SetValue(string); + SetInsertionPoint( std::min(insertionPoint, static_cast(string.length())) ); } void wxTextEntry::Copy() From 3618356cf133296a1bab5438e1a5bfdd71e6e5d2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 17 Jan 2019 12:00:01 +0000 Subject: [PATCH 7/8] Fix wxComboBox implementation of wxTextEntry interface Implement missing methods and send, or suppress, the expected events. --- include/wx/qt/combobox.h | 9 ++- src/qt/combobox.cpp | 121 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 7 deletions(-) diff --git a/include/wx/qt/combobox.h b/include/wx/qt/combobox.h index 490e2cb117..bc0222d5cb 100644 --- a/include/wx/qt/combobox.h +++ b/include/wx/qt/combobox.h @@ -52,7 +52,7 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxComboBoxNameStr); - virtual void SetSelection(int n) wxOVERRIDE { wxChoice::SetSelection(n); } + virtual void SetSelection(int n) wxOVERRIDE; virtual void SetSelection(long from, long to) wxOVERRIDE; virtual int GetSelection() const wxOVERRIDE { return wxChoice::GetSelection(); } @@ -70,6 +70,12 @@ public: bool IsTextEmpty() const { return wxTextEntry::IsEmpty(); } virtual void SetValue(const wxString& value) wxOVERRIDE; + virtual void ChangeValue(const wxString& value) wxOVERRIDE; + virtual void AppendText(const wxString &value) wxOVERRIDE; + virtual void Replace(long from, long to, const wxString &value) wxOVERRIDE; + virtual void WriteText(const wxString &value) wxOVERRIDE; + virtual void SetInsertionPoint(long insertion) wxOVERRIDE; + virtual long GetInsertionPoint() const wxOVERRIDE; virtual void Popup(); virtual void Dismiss(); @@ -80,6 +86,7 @@ protected: virtual wxString DoGetValue() const wxOVERRIDE; private: + void SetActualValue(const wxString& value); // From wxTextEntry: virtual wxWindow *GetEditableWindow() wxOVERRIDE { return this; } diff --git a/src/qt/combobox.cpp b/src/qt/combobox.cpp index 3ca015a019..36685b1954 100644 --- a/src/qt/combobox.cpp +++ b/src/qt/combobox.cpp @@ -23,13 +23,37 @@ public: virtual void showPopup() wxOVERRIDE; virtual void hidePopup() wxOVERRIDE; + class IgnoreTextChange + { + public: + // Note that wxComboBox inherits its QComboBox pointer from wxChoice, + // where it can't be stored as wxQtComboBox, but its dynamic type is + // nevertheless always wxQtComboBox, so the cast below is safe. + explicit IgnoreTextChange(QComboBox *combo) + : m_combo(static_cast(combo)) + { + m_combo->m_textChangeIgnored = true; + } + + ~IgnoreTextChange() + { + m_combo->m_textChangeIgnored = false; + } + + private: + wxQtComboBox* m_combo; + }; + private: void activated(int index); void editTextChanged(const QString &text); + + bool m_textChangeIgnored; }; wxQtComboBox::wxQtComboBox( wxWindow *parent, wxComboBox *handler ) - : wxQtEventSignalHandler< QComboBox, wxComboBox >( parent, handler ) + : wxQtEventSignalHandler< QComboBox, wxComboBox >( parent, handler ), + m_textChangeIgnored( false ) { setEditable( true ); connect(this, static_cast(&QComboBox::activated), @@ -61,6 +85,9 @@ void wxQtComboBox::activated(int WXUNUSED(index)) void wxQtComboBox::editTextChanged(const QString &text) { + if ( m_textChangeIgnored ) + return; + wxComboBox *handler = GetHandler(); if ( handler ) { @@ -70,6 +97,11 @@ void wxQtComboBox::editTextChanged(const QString &text) } } +void wxComboBox::SetSelection( int n ) +{ + wxQtComboBox::IgnoreTextChange ignore( m_qtComboBox ); + wxChoice::SetSelection( n ); +} wxComboBox::wxComboBox() { @@ -137,10 +169,12 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id, return QtCreateControl( parent, id, pos, size, style, validator, name ); } -void wxComboBox::SetValue(const wxString& value) +void wxComboBox::SetActualValue(const wxString &value) { if ( HasFlag(wxCB_READONLY) ) - SetStringSelection(value); + { + SetStringSelection( value ); + } else { wxTextEntry::SetValue(value); @@ -148,6 +182,56 @@ void wxComboBox::SetValue(const wxString& value) } } +void wxComboBox::SetValue(const wxString& value) +{ + SetActualValue( value ); + SetInsertionPoint( 0 ); +} + +void wxComboBox::ChangeValue(const wxString &value) +{ + wxQtComboBox::IgnoreTextChange ignore( m_qtComboBox ); + SetValue( value ); +} + +void wxComboBox::AppendText(const wxString &value) +{ + SetActualValue( GetValue() + value ); +} + +void wxComboBox::Replace(long from, long to, const wxString &value) +{ + const wxString original( GetValue() ); + + if ( to < 0 ) + { + to = original.length(); + } + + if ( from == 0 ) + { + SetActualValue( value + original.substr(to, original.length()) ); + } + + wxString front = original.substr( 0, from ) + value; + + long iPoint = front.length(); + if ( front.length() <= original.length() ) + { + SetActualValue( front + original.substr(to, original.length()) ); + } + else + { + SetActualValue( front ); + } + SetInsertionPoint( iPoint ); +} + +void wxComboBox::WriteText(const wxString &value) +{ + m_qtComboBox->lineEdit()->insert( wxQtConvertString( value ) ); +} + wxString wxComboBox::DoGetValue() const { return wxQtConvertString( m_qtComboBox->currentText() ); @@ -171,15 +255,40 @@ void wxComboBox::Clear() void wxComboBox::SetSelection( long from, long to ) { - // SelectAll uses -1 to -1, adjust for qt: - if (from == -1 && to == -1) + if ( from == -1 ) { from = 0; + } + if ( to == -1 ) + { to = GetValue().length(); } + + SetInsertionPoint( from ); // use the inner text entry widget (note that can be null if not editable) if ( m_qtComboBox->lineEdit() != NULL ) - m_qtComboBox->lineEdit()->setSelection(from, to); + { + m_qtComboBox->lineEdit()->setSelection( from, to - from ); + } +} + +void wxComboBox::SetInsertionPoint( long pos ) +{ + // check if pos indicates end of text: + if ( pos == -1 ) + m_qtComboBox->lineEdit()->end( false ); + else + m_qtComboBox->lineEdit()->setCursorPosition( pos ); +} + +long wxComboBox::GetInsertionPoint() const +{ + long selectionStart = m_qtComboBox->lineEdit()->selectionStart(); + + if ( selectionStart >= 0 ) + return selectionStart; + + return m_qtComboBox->lineEdit()->cursorPosition(); } void wxComboBox::GetSelection(long* from, long* to) const From 4c05b3650a7de5eede0ff6063ce38df2bd37baa7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 18 Jan 2019 10:31:56 +0000 Subject: [PATCH 8/8] Add a test for WriteText and fix implementation in wxQt --- tests/controls/textentrytest.cpp | 22 ++++++++++++++++++++++ tests/controls/textentrytest.h | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/controls/textentrytest.cpp b/tests/controls/textentrytest.cpp index 9c1dbe7d95..b053e80489 100644 --- a/tests/controls/textentrytest.cpp +++ b/tests/controls/textentrytest.cpp @@ -201,6 +201,28 @@ void TextEntryTestCase::Replace() CPPUNIT_ASSERT_EQUAL(2, entry->GetInsertionPoint()); } +void TextEntryTestCase::WriteText() +{ + wxTextEntry * const entry = GetTestEntry(); + + entry->SetValue("foo"); + entry->SetInsertionPoint(3); + entry->WriteText("bar"); + CPPUNIT_ASSERT_EQUAL( "foobar", entry->GetValue() ); + + entry->SetValue("foo"); + entry->SetInsertionPoint(0); + entry->WriteText("bar"); + CPPUNIT_ASSERT_EQUAL( "barfoo", entry->GetValue() ); + + entry->SetValue("abxxxhi"); + entry->SetSelection(2, 5); + entry->WriteText("cdefg"); + CPPUNIT_ASSERT_EQUAL( "abcdefghi", entry->GetValue() ); + CPPUNIT_ASSERT_EQUAL( 7, entry->GetInsertionPoint() ); + CPPUNIT_ASSERT_EQUAL( false, entry->HasSelection() ); +} + #if wxUSE_UIACTIONSIMULATOR class TextEventHandler diff --git a/tests/controls/textentrytest.h b/tests/controls/textentrytest.h index 648d601872..fc784c9d6f 100644 --- a/tests/controls/textentrytest.h +++ b/tests/controls/textentrytest.h @@ -44,7 +44,8 @@ protected: WXUISIM_TEST( Editable ); \ CPPUNIT_TEST( Hint ); \ CPPUNIT_TEST( CopyPaste ); \ - CPPUNIT_TEST( UndoRedo ) + CPPUNIT_TEST( UndoRedo ); \ + CPPUNIT_TEST( WriteText ) void SetValue(); void TextChangeEvents(); @@ -55,6 +56,7 @@ protected: void Hint(); void CopyPaste(); void UndoRedo(); + void WriteText(); private: // Selection() test helper: verify that selection is as described by the