Merge branch 'qt-textctrl-hittest' of https://github.com/AliKet/wxWidgets

Implement several previously missing wxTextCtrl functions in wxQt.

See #23984.
This commit is contained in:
Vadim Zeitlin 2023-10-25 23:46:30 +02:00
commit 9e8a913e4d
3 changed files with 149 additions and 10 deletions

View file

@ -52,6 +52,14 @@ public:
virtual void ShowPosition(long pos) override;
virtual wxTextCtrlHitTestResult HitTest(const wxPoint& pt, long *pos) const override;
virtual wxTextCtrlHitTestResult HitTest(const wxPoint& pt,
wxTextCoord *col,
wxTextCoord *row) const override
{
return wxTextCtrlBase::HitTest(pt, col, row);
}
virtual void SetInsertionPoint(long pos) override;
virtual long GetInsertionPoint() const override;
virtual void SetSelection( long from, long to ) override;
@ -68,10 +76,15 @@ public:
virtual void EmptyUndoBuffer() override;
virtual bool IsEditable() const override;
virtual void SetEditable(bool editable) override;
virtual wxString DoGetValue() const override;
virtual void DoSetValue(const wxString &text, int flags = 0) override;
virtual void WriteText(const wxString& text) override;
virtual void SetMaxLength(unsigned long len) override;
virtual QWidget *GetHandle() const override;
protected:

View file

@ -17,6 +17,12 @@
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QTextEdit>
#include <limits>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
#define wxHAS_QT_INPUTREJECTED
#endif
/*
* Abstract base class for wxQtSingleLineEdit and wxQtMultiLineEdit.
* This splits the polymorphic behaviour into two separate classes, avoiding
@ -37,8 +43,10 @@ public:
virtual bool GetSelection(long *from, long *to) const = 0;
virtual long XYToPosition(long x, long y) const = 0;
virtual bool PositionToXY(long pos, long *x, long *y) const = 0;
virtual wxTextCtrlHitTestResult HitTest(const wxPoint& pt, long *pos) const = 0;
virtual QScrollArea *ScrollBarsContainer() const = 0;
virtual void WriteText( const wxString &text ) = 0;
virtual void SetMaxLength(unsigned long len) = 0;
virtual void MarkDirty() = 0;
virtual void DiscardEdits() = 0;
virtual void blockSignals(bool block) = 0;
@ -96,8 +104,16 @@ public:
return GetHandler()->GetValue();
}
// cursorRect() is protected in base class. Make it public
// so it can be accessed by wxQtSingleLineEdit::HitTest()
using QLineEdit::cursorRect;
private:
void textChanged();
#ifdef wxHAS_QT_INPUTREJECTED
void inputRejected();
#endif
};
class wxQtTextEdit : public wxQtEventSignalHandler< QTextEdit, wxTextCtrl >
@ -241,6 +257,27 @@ public:
return true;
}
virtual wxTextCtrlHitTestResult
HitTest(const wxPoint& pt, long* pos) const override
{
auto qtEdit = static_cast<wxQtTextEdit* const>(m_edit);
auto cursor = qtEdit->cursorForPosition( wxQtConvertPoint(pt) );
auto curRect = qtEdit->cursorRect(cursor);
if ( pos )
*pos = cursor.position();
if ( pt.y > curRect.y() + qtEdit->fontMetrics().height() )
return wxTE_HT_BELOW;
if ( pt.x > curRect.x() + qtEdit->fontMetrics().averageCharWidth() )
return wxTE_HT_BEYOND;
return wxTE_HT_ON_TEXT;
}
virtual void WriteText( const wxString &text ) override
{
m_edit->insertPlainText(wxQtConvertString( text ));
@ -248,6 +285,11 @@ public:
m_edit->ensureCursorVisible();
}
virtual void SetMaxLength(unsigned long WXUNUSED(len)) override
{
wxMISSING_IMPLEMENTATION("not implemented for multiline control");
}
virtual void MarkDirty() override
{
return m_edit->setWindowModified( true );
@ -422,6 +464,15 @@ public:
m_edit->insert(wxQtConvertString( text ));
}
virtual void SetMaxLength(unsigned long len) override
{
// Notice that setMaxLength() takes an int and not an unsigned int
m_edit->setMaxLength(
len > std::numeric_limits<int>::max()
? std::numeric_limits<int>::max() : len
);
}
virtual void MarkDirty() override
{
return m_edit->setModified( true );
@ -489,6 +540,25 @@ public:
return true;
}
virtual wxTextCtrlHitTestResult
HitTest(const wxPoint& pt, long *pos) const override
{
auto qtEdit = static_cast<wxQtLineEdit* const>(m_edit);
auto curPos = qtEdit->cursorPositionAt( wxQtConvertPoint(pt) );
auto curRect = qtEdit->cursorRect();
if ( pos )
*pos = curPos;
if ( pt.y > curRect.y() + qtEdit->fontMetrics().height() )
return wxTE_HT_BELOW;
if ( pt.x > curRect.x() + qtEdit->fontMetrics().averageCharWidth() )
return wxTE_HT_BEYOND;
return wxTE_HT_ON_TEXT;
}
virtual QScrollArea *ScrollBarsContainer() const override
{
return nullptr;
@ -515,6 +585,11 @@ wxQtLineEdit::wxQtLineEdit( wxWindow *parent, wxTextCtrl *handler )
{
connect(this, &QLineEdit::textChanged,
this, &wxQtLineEdit::textChanged);
#ifdef wxHAS_QT_INPUTREJECTED
connect(this, &QLineEdit::inputRejected,
this, &wxQtLineEdit::inputRejected);
#endif
}
void wxQtLineEdit::textChanged()
@ -526,6 +601,15 @@ void wxQtLineEdit::textChanged()
}
}
#ifdef wxHAS_QT_INPUTREJECTED
void wxQtLineEdit::inputRejected()
{
wxCommandEvent event(wxEVT_TEXT_MAXLEN, GetHandler()->GetId());
event.SetString( GetHandler()->GetValue() );
EmitEvent( event );
}
#endif // wxHAS_QT_INPUTREJECTED
wxQtTextEdit::wxQtTextEdit( wxWindow *parent, wxTextCtrl *handler )
: wxQtEventSignalHandler< QTextEdit, wxTextCtrl >( parent, handler )
{
@ -676,6 +760,12 @@ void wxTextCtrl::ShowPosition(long WXUNUSED(pos))
{
}
wxTextCtrlHitTestResult
wxTextCtrl::HitTest(const wxPoint& pt, long *pos) const
{
return m_qtEdit->HitTest(pt, pos);
}
bool wxTextCtrl::DoLoadFile(const wxString& WXUNUSED(file), int WXUNUSED(fileType))
{
return false;
@ -726,6 +816,24 @@ void wxTextCtrl::EmptyUndoBuffer()
m_qtEdit->EmptyUndoBuffer();
}
bool wxTextCtrl::IsEditable() const
{
return HasFlag(wxTE_READONLY);
}
void wxTextCtrl::SetEditable(bool editable)
{
long flags = GetWindowStyle();
if ( editable )
flags &= ~wxTE_READONLY;
else
flags |= wxTE_READONLY;
SetWindowStyle(flags);
m_qtEdit->SetStyleFlags(flags);
}
void wxTextCtrl::SetInsertionPoint(long pos)
{
m_qtEdit->SetInsertionPoint(pos);
@ -767,6 +875,11 @@ void wxTextCtrl::WriteText( const wxString &text )
m_qtEdit->WriteText(text);
}
void wxTextCtrl::SetMaxLength(unsigned long len)
{
m_qtEdit->SetMaxLength(len);
}
void wxTextCtrl::DoSetValue( const wxString &text, int flags )
{
if ( text != DoGetValue() )

View file

@ -28,7 +28,7 @@
#include "wx/dataobj.h"
#endif // wxUSE_CLIPBOARD
#ifdef __WXGTK__
#if defined(__WXGTK__) || defined(__WXQT__)
#include "waitfor.h"
#endif
@ -353,9 +353,6 @@ void TextCtrlTestCase::Redirector()
void TextCtrlTestCase::HitTestSingleLine()
{
#ifdef __WXQT__
WARN("Does not work under WxQt");
#else
m_text->ChangeValue("Hit me");
// We don't know the size of the text borders, so we can't really do any
@ -366,6 +363,21 @@ void TextCtrlTestCase::HitTestSingleLine()
long pos = -1;
#ifdef __WXQT__
// Although it works fine interactively, testing HitTest() is not
// straightforward under wxQt: We have to call SetInsertionPoint()
// explicitly with the position returned by HitTest() before we call
// it a second time (in the test below) to get the correct result.
auto SetCursorPositionHack = [&](int n)
{
m_text->HitTest(wxPoint(n*sizeChar.x, yMid), &pos);
m_text->SetInsertionPoint(pos);
wxYield();
};
#else
#define SetCursorPositionHack(n) // do nothing under the other ports
#endif
#ifdef __WXGTK__
wxYield();
#endif
@ -374,6 +386,7 @@ void TextCtrlTestCase::HitTestSingleLine()
// first few characters under it.
SECTION("Normal")
{
SetCursorPositionHack(2);
REQUIRE( m_text->HitTest(wxPoint(2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT );
CHECK( pos >= 0 );
CHECK( pos < 3 );
@ -383,6 +396,7 @@ void TextCtrlTestCase::HitTestSingleLine()
// character.
SECTION("Beyond")
{
SetCursorPositionHack(20);
REQUIRE( m_text->HitTest(wxPoint(20*sizeChar.x, yMid), &pos) == wxTE_HT_BEYOND );
CHECK( pos == m_text->GetLastPosition() );
}
@ -395,8 +409,8 @@ void TextCtrlTestCase::HitTestSingleLine()
m_text->ChangeValue(wxString(200, 'X'));
m_text->SetInsertionPointEnd();
#ifdef __WXGTK__
// wxGTK must be given an opportunity to lay the text out.
#if defined(__WXGTK__) || defined(__WXQT__)
// wxGTK and wxQt must be given an opportunity to lay the text out.
YieldForAWhile();
#endif
@ -404,13 +418,12 @@ void TextCtrlTestCase::HitTestSingleLine()
CHECK( pos > 3 );
// Using negative coordinates works even under Xvfb, so test at least
// for this -- however this only works in wxGTK, not wxMSW.
#ifdef __WXGTK__
// for this -- however this only works in wxGTK and wxQt, not wxMSW.
#if defined(__WXGTK__) || defined(__WXQT__)
REQUIRE( m_text->HitTest(wxPoint(-2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT );
CHECK( pos > 3 );
#endif // __WXGTK__
#endif // __WXGTK__ || __WXQT__
}
#endif
}
#if 0