| // Copyright 2014 PDFium Authors. All rights reserved. |
| // 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; |
| } |