| // Copyright 2014 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "fpdfsdk/pwl/cpwl_caret.h" | |
| #include <sstream> | |
| #include <utility> | |
| #include "core/fxge/cfx_fillrenderoptions.h" | |
| #include "core/fxge/cfx_graphstatedata.h" | |
| #include "core/fxge/cfx_path.h" | |
| #include "core/fxge/cfx_renderdevice.h" | |
| CPWL_Caret::CPWL_Caret( | |
| const CreateParams& cp, | |
| std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) | |
| : CPWL_Wnd(cp, std::move(pAttachedData)) {} | |
| CPWL_Caret::~CPWL_Caret() = default; | |
| void CPWL_Caret::DrawThisAppearance(CFX_RenderDevice* pDevice, | |
| const CFX_Matrix& mtUser2Device) { | |
| if (!IsVisible() || !m_bFlash) | |
| return; | |
| CFX_FloatRect rcRect = GetCaretRect(); | |
| CFX_FloatRect rcClip = GetClipRect(); | |
| float fCaretX = rcRect.left + m_fWidth * 0.5f; | |
| float fCaretTop = rcRect.top; | |
| float fCaretBottom = rcRect.bottom; | |
| if (!rcClip.IsEmpty()) { | |
| rcRect.Intersect(rcClip); | |
| if (rcRect.IsEmpty()) | |
| return; | |
| fCaretTop = rcRect.top; | |
| fCaretBottom = rcRect.bottom; | |
| } | |
| CFX_Path path; | |
| path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom), | |
| CFX_Path::Point::Type::kMove); | |
| path.AppendPoint(CFX_PointF(fCaretX, fCaretTop), | |
| CFX_Path::Point::Type::kLine); | |
| CFX_GraphStateData gsd; | |
| gsd.m_LineWidth = m_fWidth; | |
| pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0), | |
| CFX_FillRenderOptions::EvenOddOptions()); | |
| } | |
| void CPWL_Caret::OnTimerFired() { | |
| m_bFlash = !m_bFlash; | |
| InvalidateRect(nullptr); | |
| // Note, |this| may no longer be viable at this point. If more work needs | |
| // to be done, add an observer. | |
| } | |
| CFX_FloatRect CPWL_Caret::GetCaretRect() const { | |
| return CFX_FloatRect(m_ptFoot.x, m_ptFoot.y, m_ptHead.x + m_fWidth, | |
| m_ptHead.y); | |
| } | |
| void CPWL_Caret::SetCaret(bool bVisible, | |
| const CFX_PointF& ptHead, | |
| const CFX_PointF& ptFoot) { | |
| if (!bVisible) { | |
| m_ptHead = CFX_PointF(); | |
| m_ptFoot = CFX_PointF(); | |
| m_bFlash = false; | |
| if (!IsVisible()) | |
| return; | |
| m_pTimer.reset(); | |
| CPWL_Wnd::SetVisible(false); | |
| // Note, |this| may no longer be viable at this point. If more work needs | |
| // to be done, check the return value of SetVisible(). | |
| return; | |
| } | |
| if (!IsVisible()) { | |
| static constexpr int32_t kCaretFlashIntervalMs = 500; | |
| m_ptHead = ptHead; | |
| m_ptFoot = ptFoot; | |
| m_pTimer = std::make_unique<CFX_Timer>(GetTimerHandler(), this, | |
| kCaretFlashIntervalMs); | |
| if (!CPWL_Wnd::SetVisible(true)) | |
| return; | |
| m_bFlash = true; | |
| Move(m_rcInvalid, false, true); | |
| // Note, |this| may no longer be viable at this point. If more work needs | |
| // to be done, check the return value of Move(). | |
| return; | |
| } | |
| if (m_ptHead == ptHead && m_ptFoot == ptFoot) | |
| return; | |
| m_ptHead = ptHead; | |
| m_ptFoot = ptFoot; | |
| m_bFlash = true; | |
| Move(m_rcInvalid, false, true); | |
| // Note, |this| may no longer be viable at this point. If more work | |
| // needs to be done, check the return value of Move(). | |
| } | |
| bool CPWL_Caret::InvalidateRect(const CFX_FloatRect* pRect) { | |
| if (!pRect) | |
| return CPWL_Wnd::InvalidateRect(nullptr); | |
| CFX_FloatRect rcRefresh = *pRect; | |
| if (!rcRefresh.IsEmpty()) { | |
| rcRefresh.Inflate(0.5f, 0.5f); | |
| rcRefresh.Normalize(); | |
| } | |
| rcRefresh.top += 1; | |
| rcRefresh.bottom -= 1; | |
| return CPWL_Wnd::InvalidateRect(&rcRefresh); | |
| } | |
| bool CPWL_Caret::SetVisible(bool bVisible) { | |
| return true; | |
| } |