From eb57e0d0b396606b39ef76a864b8bf9dd91a8ed8 Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Wed, 6 Sep 2017 11:44:39 -0400 Subject: Implement word selection in CFDE_TextEditEngine This CL implements the needed logic in CFDE_TextEditEngine to handle word selection. Change-Id: I6b388c23655037fec107d68ec07d33638b959374 Reviewed-on: https://pdfium-review.googlesource.com/13211 Commit-Queue: dsinclair Reviewed-by: Ryan Harrison Reviewed-by: Henrique Nakashima --- core/fxcrt/cfx_wordbreak.cpp | 24 ++----- core/fxcrt/cfx_wordbreak.h | 10 ++- xfa/fde/cfde_texteditengine.cpp | 25 +++++-- xfa/fde/cfde_texteditengine.h | 6 +- xfa/fde/cfde_texteditengine_unittest.cpp | 112 +++++++++++++++++++++++++++++++ xfa/fwl/cfwl_edit.cpp | 15 ++--- 6 files changed, 153 insertions(+), 39 deletions(-) diff --git a/core/fxcrt/cfx_wordbreak.cpp b/core/fxcrt/cfx_wordbreak.cpp index 7a5d8f7dcd..86c6d40522 100644 --- a/core/fxcrt/cfx_wordbreak.cpp +++ b/core/fxcrt/cfx_wordbreak.cpp @@ -2779,16 +2779,14 @@ FX_WordBreakProp GetWordBreakProperty(wchar_t wcCodePoint) { } // namespace -CFX_WordBreak::CFX_WordBreak() {} +CFX_WordBreak::CFX_WordBreak(std::unique_ptr pIter) + : m_pCurIter(std::move(pIter)) { + ASSERT(m_pCurIter); +} CFX_WordBreak::~CFX_WordBreak() {} -void CFX_WordBreak::Attach(IFX_CharIter* pIter) { - ASSERT(pIter); - m_pCurIter.reset(pIter); -} - -void CFX_WordBreak::SetAt(int32_t nIndex) { +std::pair CFX_WordBreak::BoundsAt(int32_t nIndex) { m_pPreIter.reset(); m_pCurIter->SetAt(nIndex); FindNextBreakPos(m_pCurIter.get(), true); @@ -2796,18 +2794,8 @@ void CFX_WordBreak::SetAt(int32_t nIndex) { m_pPreIter = std::move(m_pCurIter); m_pCurIter = m_pPreIter->Clone(); FindNextBreakPos(m_pCurIter.get(), false); -} - -int32_t CFX_WordBreak::GetWordPos() const { - return m_pPreIter->GetAt(); -} - -int32_t CFX_WordBreak::GetWordLength() const { - return m_pCurIter->GetAt() - m_pPreIter->GetAt() + 1; -} -bool CFX_WordBreak::IsEOF(bool bTail) const { - return m_pCurIter->IsEOF(bTail); + return {m_pPreIter->GetAt(), m_pCurIter->GetAt()}; } bool CFX_WordBreak::FindNextBreakPos(IFX_CharIter* pIter, bool bPrev) { diff --git a/core/fxcrt/cfx_wordbreak.h b/core/fxcrt/cfx_wordbreak.h index 59c071be03..6e94c71e95 100644 --- a/core/fxcrt/cfx_wordbreak.h +++ b/core/fxcrt/cfx_wordbreak.h @@ -8,22 +8,20 @@ #define CORE_FXCRT_CFX_WORDBREAK_H_ #include +#include class IFX_CharIter; class CFX_WordBreak { public: - CFX_WordBreak(); + explicit CFX_WordBreak(std::unique_ptr pIter); ~CFX_WordBreak(); - void Attach(IFX_CharIter* pIter); - void SetAt(int32_t nIndex); - int32_t GetWordPos() const; - int32_t GetWordLength() const; + // + std::pair BoundsAt(int32_t nIndex); private: bool FindNextBreakPos(IFX_CharIter* pIter, bool bPrev); - bool IsEOF(bool bTail) const; std::unique_ptr m_pPreIter; std::unique_ptr m_pCurIter; diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp index e92897ea7d..b5948d45ce 100644 --- a/xfa/fde/cfde_texteditengine.cpp +++ b/xfa/fde/cfde_texteditengine.cpp @@ -9,6 +9,7 @@ #include #include +#include "core/fxcrt/cfx_wordbreak.h" #include "xfa/fde/cfde_textout.h" namespace { @@ -927,7 +928,14 @@ std::vector CFDE_TextEditEngine::GetCharacterRectsInRange( return rects; } -CFDE_TextEditEngine::Iterator::Iterator(CFDE_TextEditEngine* engine) +std::pair CFDE_TextEditEngine::BoundsForWordAt( + size_t idx) const { + CFX_WordBreak breaker( + pdfium::MakeUnique(this)); + return breaker.BoundsAt(idx); +} + +CFDE_TextEditEngine::Iterator::Iterator(const CFDE_TextEditEngine* engine) : engine_(engine), current_position_(-1) {} CFDE_TextEditEngine::Iterator::~Iterator() {} @@ -936,8 +944,9 @@ bool CFDE_TextEditEngine::Iterator::Next(bool bPrev) { if (bPrev && current_position_ == -1) return false; if (!bPrev && current_position_ > -1 && - static_cast(current_position_) == engine_->GetLength()) + static_cast(current_position_) == engine_->GetLength()) { return false; + } if (bPrev) --current_position_; @@ -952,7 +961,12 @@ wchar_t CFDE_TextEditEngine::Iterator::GetChar() const { } void CFDE_TextEditEngine::Iterator::SetAt(int32_t nIndex) { - NOTREACHED(); + if (nIndex < 0) + current_position_ = 0; + else if (static_cast(nIndex) >= engine_->GetLength()) + current_position_ = engine_->GetLength(); + else + current_position_ = nIndex; } int32_t CFDE_TextEditEngine::Iterator::GetAt() const { @@ -967,6 +981,7 @@ bool CFDE_TextEditEngine::Iterator::IsEOF(bool bTail) const { } std::unique_ptr CFDE_TextEditEngine::Iterator::Clone() const { - NOTREACHED(); - return pdfium::MakeUnique(engine_.Get()); + auto it = pdfium::MakeUnique(engine_.Get()); + it->current_position_ = current_position_; + return it; } diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h index 35d9664f99..0277b0cc64 100644 --- a/xfa/fde/cfde_texteditengine.h +++ b/xfa/fde/cfde_texteditengine.h @@ -40,7 +40,7 @@ class CFDE_TextEditEngine { public: class Iterator : public IFX_CharIter { public: - explicit Iterator(CFDE_TextEditEngine* engine); + explicit Iterator(const CFDE_TextEditEngine* engine); ~Iterator() override; bool Next(bool bPrev = false) override; @@ -51,7 +51,7 @@ class CFDE_TextEditEngine { std::unique_ptr Clone() const override; private: - CFX_UnownedPtr engine_; + CFX_UnownedPtr engine_; int32_t current_position_; }; @@ -160,6 +160,8 @@ class CFDE_TextEditEngine { size_t GetWidthOfChar(size_t idx); // Non-const so we can force a Layout() if needed. size_t GetIndexForPoint(const CFX_PointF& point); + // + std::pair BoundsForWordAt(size_t idx) const; // Returns std::pair GetCharacterInfo(int32_t start_idx); diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp index da11e46daa..2084914bbf 100644 --- a/xfa/fde/cfde_texteditengine_unittest.cpp +++ b/xfa/fde/cfde_texteditengine_unittest.cpp @@ -416,3 +416,115 @@ TEST_F(CFDE_TextEditEngineTest, GetIndexForPoint) { EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f})); EXPECT_EQ(1U, engine()->GetIndexForPoint({10.0f, 5.0f})); } + +TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) { + size_t start_idx; + size_t end_idx; + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(100); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(0U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(0); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(4U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello World"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(100); + EXPECT_EQ(11U, start_idx); + EXPECT_EQ(11U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(0); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(4U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(1); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(4U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(4); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(4U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str()); + + // Select the space + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(5); + EXPECT_EQ(5U, start_idx); + EXPECT_EQ(5U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(6); + EXPECT_EQ(6U, start_idx); + EXPECT_EQ(10U, end_idx); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"123 456 789"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(5); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"123def789"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(5); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"123def789", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"abc456ghi"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(5); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"abc456ghi", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"hello, world"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(0); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"hello", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"hello, world"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(5); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"np-complete"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(6); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"complete", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"(123) 456-7890"); + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(0); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(1); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"123", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(7); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str()); + + std::tie(start_idx, end_idx) = engine()->BoundsForWordAt(11); + engine()->SetSelection(start_idx, end_idx); + EXPECT_STREQ(L"7890", engine()->GetSelectedText().c_str()); +} diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp index 72adb5392b..99a312bfca 100644 --- a/xfa/fwl/cfwl_edit.cpp +++ b/xfa/fwl/cfwl_edit.cpp @@ -1234,14 +1234,13 @@ void CFWL_Edit::OnLButtonUp(CFWL_MessageMouse* pMsg) { } void CFWL_Edit::OnButtonDoubleClick(CFWL_MessageMouse* pMsg) { - // TODO(dsinclair): Handle OnButtonDoubleClick - // int32_t nCount = 0; - // int32_t nIndex = pPage->SelectWord(DeviceToEngine(pMsg->m_pos), nCount); - // if (nIndex < 0) - // return; - // - // m_EdtEngine.AddSelRange(nIndex, nCount); - // SetCursorPosition(nIndex + nCount - 1); + size_t click_idx = m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)); + size_t start_idx; + size_t end_idx; + std::tie(start_idx, end_idx) = m_EdtEngine.BoundsForWordAt(click_idx); + + m_EdtEngine.SetSelection(start_idx, end_idx); + m_CursorPosition = end_idx; RepaintRect(m_rtEngine); } -- cgit v1.2.3