diff --git a/docs/changes.txt b/docs/changes.txt index b314d6abb6..f3fed53b77 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -92,6 +92,7 @@ All (GUI): wxGTK: +- Implement wxTextCtrl::HitTest() for single line controls. - Fix the build with glib < 2.32 (e.g. CentOS 6). wxMSW: diff --git a/interface/wx/textctrl.h b/interface/wx/textctrl.h index 70b296c5b0..1bb50d9e90 100644 --- a/interface/wx/textctrl.h +++ b/interface/wx/textctrl.h @@ -1337,7 +1337,7 @@ public: parameter is not modified. Please note that this function is currently only implemented in wxUniv, - wxMSW and wxGTK2 ports and always returns @c wxTE_HT_UNKNOWN in the + wxMSW and wxGTK ports and always returns @c wxTE_HT_UNKNOWN in the other ports. @beginWxPerlOnly @@ -1363,7 +1363,7 @@ public: parameters are not modified. Please note that this function is currently only implemented in wxUniv, - wxMSW and wxGTK2 ports and always returns @c wxTE_HT_UNKNOWN in the + wxMSW and wxGTK ports and always returns @c wxTE_HT_UNKNOWN in the other ports. @beginWxPerlOnly diff --git a/samples/widgets/textctrl.cpp b/samples/widgets/textctrl.cpp index c89ba6ca93..96e5074954 100644 --- a/samples/widgets/textctrl.cpp +++ b/samples/widgets/textctrl.cpp @@ -276,11 +276,17 @@ public: int flags) : wxTextCtrl(parent, id, value, wxDefaultPosition, wxDefaultSize, flags) { + Bind(wxEVT_LEFT_DOWN, &WidgetsTextCtrl::OnLeftClick, this); } -protected: - void OnRightClick(wxMouseEvent& event) +private: + // Show the result of HitTest() at the mouse position if Alt is pressed. + void OnLeftClick(wxMouseEvent& event) { + event.Skip(); + if ( !event.AltDown() ) + return; + wxString where; wxTextCoord x, y; switch ( HitTest(event.GetPosition(), &x, &y) ) @@ -312,12 +318,7 @@ protected: } wxLogMessage(wxT("Mouse is %s (%ld, %ld)"), where.c_str(), x, y); - - event.Skip(); } - -private: - wxDECLARE_EVENT_TABLE(); }; // ---------------------------------------------------------------------------- @@ -353,10 +354,6 @@ wxBEGIN_EVENT_TABLE(TextWidgetsPage, WidgetsPage) EVT_RADIOBOX(wxID_ANY, TextWidgetsPage::OnCheckOrRadioBox) wxEND_EVENT_TABLE() -wxBEGIN_EVENT_TABLE(WidgetsTextCtrl, wxTextCtrl) - EVT_RIGHT_UP(WidgetsTextCtrl::OnRightClick) -wxEND_EVENT_TABLE() - // ============================================================================ // implementation // ============================================================================ @@ -593,6 +590,17 @@ void TextWidgetsPage::CreateContent() 0, wxALL, 5 ); + sizerMiddleDown->Add + ( + new wxStaticText + ( + this, + wxID_ANY, + "Alt-click in the text to see HitTest() result" + ), + wxSizerFlags().Border() + ); + wxSizer *sizerMiddle = new wxBoxSizer(wxVERTICAL); sizerMiddle->Add(sizerMiddleUp, 0, wxGROW); sizerMiddle->Add(sizerMiddleDown, 1, wxGROW | wxTOP, 5); diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index 281099dce9..b8551d37c7 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -1465,8 +1465,53 @@ wxTextCtrl::HitTest(const wxPoint& pt, long *pos) const { if ( !IsMultiLine() ) { - // not supported - return wxTE_HT_UNKNOWN; + // These variables will contain the position inside PangoLayout. + int x = pt.x, + y = pt.y; + + // Get the offsets of PangoLayout inside the control. + // + // Note that contrary to what GTK+ documentation implies, the + // horizontal offset already accounts for scrolling, i.e. it will be + // negative if text is scrolled. + gint ofsX = 0, + ofsY = 0; + gtk_entry_get_layout_offsets(GTK_ENTRY(m_text), &ofsX, &ofsY); + + x -= ofsX; + y -= ofsY; + + // And scale the coordinates for Pango. + x *= PANGO_SCALE; + y *= PANGO_SCALE; + + PangoLayout* const layout = gtk_entry_get_layout(GTK_ENTRY(m_text)); + + int idx = -1, + ofs = 0; + if ( !pango_layout_xy_to_index(layout, x, y, &idx, &ofs) ) + { + // Try to guess why did it fail. + if ( x < 0 || y < 0 ) + { + if ( pos ) + *pos = 0; + + return wxTE_HT_BEFORE; + } + else + { + if ( pos ) + *pos = wxTextEntry::GetLastPosition(); + + return wxTE_HT_BEYOND; + } + } + + if ( pos ) + *pos = idx; + + return wxTE_HT_ON_TEXT; } int x, y; diff --git a/tests/controls/textctrltest.cpp b/tests/controls/textctrltest.cpp index 7264bc5d82..eceaee7e47 100644 --- a/tests/controls/textctrltest.cpp +++ b/tests/controls/textctrltest.cpp @@ -59,6 +59,7 @@ private: WXUISIM_TEST( MaxLength ); CPPUNIT_TEST( PositionToXYSingleLine ); CPPUNIT_TEST( XYToPositionSingleLine ); + CPPUNIT_TEST( HitTestSingleLine ); SINGLE_AND_MULTI_TESTS(); // Now switch to the multi-line text controls. @@ -112,6 +113,7 @@ private: void MaxLength(); void StreamInput(); void Redirector(); + void HitTestSingleLine(); //void ProcessEnter(); void Url(); void Style(); @@ -164,12 +166,8 @@ long TextCtrlTestCase::ms_style = 0; void TextCtrlTestCase::CreateText(long extraStyles) { - wxSize size; - if ( ms_style == wxTE_MULTILINE ) - size = wxSize(400, TEXT_HEIGHT); - m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "", - wxDefaultPosition, size, + wxDefaultPosition, wxSize(400, TEXT_HEIGHT), ms_style | extraStyles); } @@ -342,6 +340,51 @@ void TextCtrlTestCase::Redirector() #endif } +void TextCtrlTestCase::HitTestSingleLine() +{ + m_text->ChangeValue("Hit me"); + + // We don't know the size of the text borders, so we can't really do any + // exact tests, just try to verify that the results are roughly as + // expected. + const wxSize sizeChar = m_text->GetTextExtent("X"); + const int yMid = sizeChar.y / 2; + + long pos = -1; + + // Hitting a point near the left side of the control should find one of the + // first few characters under it. + SECTION("Normal") + { + REQUIRE( m_text->HitTest(wxPoint(2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT ); + CHECK( pos >= 0 ); + CHECK( pos < 3 ); + } + + // Hitting a point well beyond the end of the text shouldn't find any valid + // character. + SECTION("Beyond") + { + REQUIRE( m_text->HitTest(wxPoint(20*sizeChar.x, yMid), &pos) == wxTE_HT_BEYOND ); + CHECK( pos == m_text->GetLastPosition() ); + } + + // Making the control scroll, by ensuring that its contents is too long to + // show inside its window, should change the hit test result for the same + // position as used above. + SECTION("Scrolled") + { + m_text->ChangeValue(wxString(200, 'X')); + m_text->SetInsertionPointEnd(); + + // wxGTK must be given an opportunity to lay the text out. + wxYield(); + + REQUIRE( m_text->HitTest(wxPoint(2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT ); + CHECK( pos > 3 ); + } +} + #if 0 void TextCtrlTestCase::ProcessEnter() {