• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
8 
9 #include <algorithm>
10 #include <sstream>
11 #include <utility>
12 #include <vector>
13 
14 #include "core/fxge/cfx_pathdata.h"
15 #include "core/fxge/cfx_renderdevice.h"
16 #include "fpdfsdk/pwl/cpwl_wnd.h"
17 #include "third_party/base/ptr_util.h"
18 
19 namespace {
20 
21 constexpr float kButtonWidth = 9.0f;
22 constexpr float kPosButtonMinWidth = 2.0f;
23 constexpr float kScrollBarTriangleHalfLength = 2.0f;
24 
25 }  // namespace
26 
27 #define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(CFX_Color::kGray, 0.50)
28 
Reset()29 void PWL_FLOATRANGE::Reset() {
30   fMin = 0.0f;
31   fMax = 0.0f;
32 }
33 
Set(float min,float max)34 void PWL_FLOATRANGE::Set(float min, float max) {
35   fMin = std::min(min, max);
36   fMax = std::max(min, max);
37 }
38 
In(float x) const39 bool PWL_FLOATRANGE::In(float x) const {
40   return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) &&
41          (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax));
42 }
43 
GetWidth() const44 float PWL_FLOATRANGE::GetWidth() const {
45   return fMax - fMin;
46 }
47 
PWL_SCROLL_PRIVATEDATA()48 PWL_SCROLL_PRIVATEDATA::PWL_SCROLL_PRIVATEDATA() {
49   Default();
50 }
51 
Default()52 void PWL_SCROLL_PRIVATEDATA::Default() {
53   ScrollRange.Reset();
54   fScrollPos = ScrollRange.fMin;
55   fClientWidth = 0;
56   fBigStep = 10;
57   fSmallStep = 1;
58 }
59 
SetScrollRange(float min,float max)60 void PWL_SCROLL_PRIVATEDATA::SetScrollRange(float min, float max) {
61   ScrollRange.Set(min, max);
62 
63   if (IsFloatSmaller(fScrollPos, ScrollRange.fMin))
64     fScrollPos = ScrollRange.fMin;
65   if (IsFloatBigger(fScrollPos, ScrollRange.fMax))
66     fScrollPos = ScrollRange.fMax;
67 }
68 
SetClientWidth(float width)69 void PWL_SCROLL_PRIVATEDATA::SetClientWidth(float width) {
70   fClientWidth = width;
71 }
72 
SetSmallStep(float step)73 void PWL_SCROLL_PRIVATEDATA::SetSmallStep(float step) {
74   fSmallStep = step;
75 }
76 
SetBigStep(float step)77 void PWL_SCROLL_PRIVATEDATA::SetBigStep(float step) {
78   fBigStep = step;
79 }
80 
SetPos(float pos)81 bool PWL_SCROLL_PRIVATEDATA::SetPos(float pos) {
82   if (ScrollRange.In(pos)) {
83     fScrollPos = pos;
84     return true;
85   }
86   return false;
87 }
88 
AddSmall()89 void PWL_SCROLL_PRIVATEDATA::AddSmall() {
90   if (!SetPos(fScrollPos + fSmallStep))
91     SetPos(ScrollRange.fMax);
92 }
93 
SubSmall()94 void PWL_SCROLL_PRIVATEDATA::SubSmall() {
95   if (!SetPos(fScrollPos - fSmallStep))
96     SetPos(ScrollRange.fMin);
97 }
98 
AddBig()99 void PWL_SCROLL_PRIVATEDATA::AddBig() {
100   if (!SetPos(fScrollPos + fBigStep))
101     SetPos(ScrollRange.fMax);
102 }
103 
SubBig()104 void PWL_SCROLL_PRIVATEDATA::SubBig() {
105   if (!SetPos(fScrollPos - fBigStep))
106     SetPos(ScrollRange.fMin);
107 }
108 
CPWL_SBButton(const CreateParams & cp,std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,PWL_SCROLLBAR_TYPE eScrollBarType,PWL_SBBUTTON_TYPE eButtonType)109 CPWL_SBButton::CPWL_SBButton(
110     const CreateParams& cp,
111     std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
112     PWL_SCROLLBAR_TYPE eScrollBarType,
113     PWL_SBBUTTON_TYPE eButtonType)
114     : CPWL_Wnd(cp, std::move(pAttachedData)),
115       m_eScrollBarType(eScrollBarType),
116       m_eSBButtonType(eButtonType) {
117   GetCreationParams()->eCursorType = FXCT_ARROW;
118 }
119 
120 CPWL_SBButton::~CPWL_SBButton() = default;
121 
DrawThisAppearance(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device)122 void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
123                                        const CFX_Matrix& mtUser2Device) {
124   if (!IsVisible())
125     return;
126 
127   CFX_FloatRect rectWnd = GetWindowRect();
128   if (rectWnd.IsEmpty())
129     return;
130 
131   CFX_PointF ptCenter = GetCenterPoint();
132   int32_t nTransparency = GetTransparency();
133 
134   if (m_eScrollBarType == SBT_HSCROLL) {
135     CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
136 
137     CFX_PointF pt1;
138     CFX_PointF pt2;
139     CFX_PointF pt3;
140     static constexpr float kScrollBarTriangleQuarterLength =
141         kScrollBarTriangleHalfLength * 0.5;
142     if (m_eSBButtonType == PSBT_MIN) {
143       pt1 =
144           CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, ptCenter.y);
145       pt2 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
146                        ptCenter.y + kScrollBarTriangleHalfLength);
147       pt3 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
148                        ptCenter.y - kScrollBarTriangleHalfLength);
149     } else if (m_eSBButtonType == PSBT_MAX) {
150       pt1 =
151           CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, ptCenter.y);
152       pt2 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
153                        ptCenter.y + kScrollBarTriangleHalfLength);
154       pt3 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
155                        ptCenter.y - kScrollBarTriangleHalfLength);
156     }
157 
158     if (rectWnd.right - rectWnd.left > kScrollBarTriangleHalfLength * 2 &&
159         rectWnd.top - rectWnd.bottom > kScrollBarTriangleHalfLength) {
160       CFX_PathData path;
161       path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
162       path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
163       path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
164       path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
165 
166       pDevice->DrawPath(&path, &mtUser2Device, nullptr,
167                         PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency), 0,
168                         FXFILL_ALTERNATE);
169     }
170     return;
171   }
172 
173   // draw border
174   pDevice->DrawStrokeRect(mtUser2Device, rectWnd,
175                           ArgbEncode(nTransparency, 100, 100, 100), 0.0f);
176   pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
177                           ArgbEncode(nTransparency, 255, 255, 255), 1.0f);
178 
179   if (m_eSBButtonType != PSBT_POS) {
180     // draw background
181     if (IsEnabled()) {
182       pDevice->DrawShadow(mtUser2Device, true, false,
183                           rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80,
184                           220);
185     } else {
186       pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f),
187                             ArgbEncode(255, 255, 255, 255));
188     }
189 
190     // draw arrow
191     if (rectWnd.top - rectWnd.bottom > 6.0f) {
192       float fX = rectWnd.left + 1.5f;
193       float fY = rectWnd.bottom;
194       std::vector<CFX_PointF> pts;
195       static constexpr float kOffsetsX[] = {2.5f, 2.5f, 4.5f, 6.5f,
196                                             6.5f, 4.5f, 2.5f};
197       static constexpr float kOffsetsY[] = {5.0f, 6.0f, 4.0f, 6.0f,
198                                             5.0f, 3.0f, 5.0f};
199       static constexpr float kOffsetsMinY[] = {4.0f, 3.0f, 5.0f, 3.0f,
200                                                4.0f, 6.0f, 4.0f};
201       static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsY),
202                     "Wrong offset count");
203       static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsMinY),
204                     "Wrong offset count");
205       const float* pOffsetsY =
206           m_eSBButtonType == PSBT_MIN ? kOffsetsMinY : kOffsetsY;
207       for (size_t i = 0; i < FX_ArraySize(kOffsetsX); ++i)
208         pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i]));
209       pDevice->DrawFillArea(mtUser2Device, pts,
210                             IsEnabled()
211                                 ? ArgbEncode(nTransparency, 255, 255, 255)
212                                 : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
213     }
214     return;
215   }
216 
217   if (IsEnabled()) {
218     // draw shadow effect
219     CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f);
220     CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f);
221 
222     ptTop.x += 1.5f;
223     ptBottom.x += 1.5f;
224 
225     const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210),
226                                 ArgbEncode(nTransparency, 220, 220, 220),
227                                 ArgbEncode(nTransparency, 240, 240, 240),
228                                 ArgbEncode(nTransparency, 240, 240, 240),
229                                 ArgbEncode(nTransparency, 210, 210, 210),
230                                 ArgbEncode(nTransparency, 180, 180, 180),
231                                 ArgbEncode(nTransparency, 150, 150, 150),
232                                 ArgbEncode(nTransparency, 150, 150, 150),
233                                 ArgbEncode(nTransparency, 180, 180, 180),
234                                 ArgbEncode(nTransparency, 210, 210, 210)};
235     for (FX_COLORREF ref : refs) {
236       pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f);
237 
238       ptTop.x += 1.0f;
239       ptBottom.x += 1.0f;
240     }
241   } else {
242     pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
243                           ArgbEncode(255, 255, 255, 255));
244   }
245 
246   // draw friction
247   if (rectWnd.Height() <= 8.0f)
248     return;
249 
250   FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120);
251   if (!IsEnabled())
252     crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255);
253 
254   float nFrictionWidth = 5.0f;
255   float nFrictionHeight = 5.5f;
256 
257   CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f,
258                                  ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
259   CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f,
260                                   ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
261 
262   for (size_t i = 0; i < 3; ++i) {
263     pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f);
264     ptLeft.y += 2.0f;
265     ptRight.y += 2.0f;
266   }
267 }
268 
OnLButtonDown(const CFX_PointF & point,uint32_t nFlag)269 bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
270   CPWL_Wnd::OnLButtonDown(point, nFlag);
271 
272   if (CPWL_Wnd* pParent = GetParentWindow())
273     pParent->NotifyLButtonDown(this, point);
274 
275   m_bMouseDown = true;
276   SetCapture();
277 
278   return true;
279 }
280 
OnLButtonUp(const CFX_PointF & point,uint32_t nFlag)281 bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
282   CPWL_Wnd::OnLButtonUp(point, nFlag);
283 
284   if (CPWL_Wnd* pParent = GetParentWindow())
285     pParent->NotifyLButtonUp(this, point);
286 
287   m_bMouseDown = false;
288   ReleaseCapture();
289 
290   return true;
291 }
292 
OnMouseMove(const CFX_PointF & point,uint32_t nFlag)293 bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
294   CPWL_Wnd::OnMouseMove(point, nFlag);
295 
296   if (CPWL_Wnd* pParent = GetParentWindow())
297     pParent->NotifyMouseMove(this, point);
298 
299   return true;
300 }
301 
CPWL_ScrollBar(const CreateParams & cp,std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,PWL_SCROLLBAR_TYPE sbType)302 CPWL_ScrollBar::CPWL_ScrollBar(
303     const CreateParams& cp,
304     std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
305     PWL_SCROLLBAR_TYPE sbType)
306     : CPWL_Wnd(cp, std::move(pAttachedData)), m_sbType(sbType) {
307   GetCreationParams()->eCursorType = FXCT_ARROW;
308 }
309 
310 CPWL_ScrollBar::~CPWL_ScrollBar() = default;
311 
OnDestroy()312 void CPWL_ScrollBar::OnDestroy() {
313   // Until cleanup takes place in the virtual destructor for CPWL_Wnd
314   // subclasses, implement the virtual OnDestroy method that does the
315   // cleanup first, then invokes the superclass OnDestroy ... gee,
316   // like a dtor would.
317   m_pMinButton.Release();
318   m_pMaxButton.Release();
319   m_pPosButton.Release();
320   CPWL_Wnd::OnDestroy();
321 }
322 
RePosChildWnd()323 bool CPWL_ScrollBar::RePosChildWnd() {
324   CFX_FloatRect rcClient = GetClientRect();
325   CFX_FloatRect rcMinButton, rcMaxButton;
326   float fBWidth = 0;
327 
328   switch (m_sbType) {
329     case SBT_HSCROLL:
330       if (rcClient.right - rcClient.left >
331           kButtonWidth * 2 + kPosButtonMinWidth + 2) {
332         rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
333                                     rcClient.left + kButtonWidth, rcClient.top);
334         rcMaxButton =
335             CFX_FloatRect(rcClient.right - kButtonWidth, rcClient.bottom,
336                           rcClient.right, rcClient.top);
337       } else {
338         fBWidth = (rcClient.right - rcClient.left - kPosButtonMinWidth - 2) / 2;
339 
340         if (fBWidth > 0) {
341           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
342                                       rcClient.left + fBWidth, rcClient.top);
343           rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom,
344                                       rcClient.right, rcClient.top);
345         } else {
346           if (!SetVisible(false))
347             return false;
348         }
349       }
350       break;
351     case SBT_VSCROLL:
352       if (IsFloatBigger(rcClient.top - rcClient.bottom,
353                         kButtonWidth * 2 + kPosButtonMinWidth + 2)) {
354         rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth,
355                                     rcClient.right, rcClient.top);
356         rcMaxButton =
357             CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
358                           rcClient.bottom + kButtonWidth);
359       } else {
360         fBWidth = (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2;
361 
362         if (IsFloatBigger(fBWidth, 0)) {
363           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth,
364                                       rcClient.right, rcClient.top);
365           rcMaxButton =
366               CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
367                             rcClient.bottom + fBWidth);
368         } else {
369           if (!SetVisible(false))
370             return false;
371         }
372       }
373       break;
374   }
375 
376   ObservedPtr<CPWL_ScrollBar> thisObserved(this);
377   if (m_pMinButton) {
378     m_pMinButton->Move(rcMinButton, true, false);
379     if (!thisObserved)
380       return false;
381   }
382   if (m_pMaxButton) {
383     m_pMaxButton->Move(rcMaxButton, true, false);
384     if (!thisObserved)
385       return false;
386   }
387   if (!MovePosButton(false))
388     return false;
389 
390   return true;
391 }
392 
DrawThisAppearance(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device)393 void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice,
394                                         const CFX_Matrix& mtUser2Device) {
395   CFX_FloatRect rectWnd = GetWindowRect();
396 
397   if (IsVisible() && !rectWnd.IsEmpty()) {
398     pDevice->DrawFillRect(&mtUser2Device, rectWnd, GetBackgroundColor(),
399                           GetTransparency());
400 
401     pDevice->DrawStrokeLine(
402         &mtUser2Device, CFX_PointF(rectWnd.left + 2.0f, rectWnd.top - 2.0f),
403         CFX_PointF(rectWnd.left + 2.0f, rectWnd.bottom + 2.0f),
404         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
405 
406     pDevice->DrawStrokeLine(
407         &mtUser2Device, CFX_PointF(rectWnd.right - 2.0f, rectWnd.top - 2.0f),
408         CFX_PointF(rectWnd.right - 2.0f, rectWnd.bottom + 2.0f),
409         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
410   }
411 }
412 
OnLButtonDown(const CFX_PointF & point,uint32_t nFlag)413 bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
414   CPWL_Wnd::OnLButtonDown(point, nFlag);
415 
416   if (HasFlag(PWS_AUTOTRANSPARENT)) {
417     if (GetTransparency() != 255) {
418       SetTransparency(255);
419       if (!InvalidateRect(nullptr))
420         return true;
421     }
422   }
423 
424   CFX_FloatRect rcMinArea, rcMaxArea;
425 
426   if (m_pPosButton && m_pPosButton->IsVisible()) {
427     CFX_FloatRect rcClient = GetClientRect();
428     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
429 
430     switch (m_sbType) {
431       case SBT_HSCROLL:
432         rcMinArea = CFX_FloatRect(rcClient.left + kButtonWidth, rcClient.bottom,
433                                   rcPosButton.left, rcClient.top);
434         rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom,
435                                   rcClient.right - kButtonWidth, rcClient.top);
436 
437         break;
438       case SBT_VSCROLL:
439         rcMinArea = CFX_FloatRect(rcClient.left, rcPosButton.top,
440                                   rcClient.right, rcClient.top - kButtonWidth);
441         rcMaxArea = CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth,
442                                   rcClient.right, rcPosButton.bottom);
443         break;
444     }
445 
446     rcMinArea.Normalize();
447     rcMaxArea.Normalize();
448 
449     if (rcMinArea.Contains(point)) {
450       m_sData.SubBig();
451       if (!MovePosButton(true))
452         return true;
453       NotifyScrollWindow();
454     }
455 
456     if (rcMaxArea.Contains(point)) {
457       m_sData.AddBig();
458       if (!MovePosButton(true))
459         return true;
460       NotifyScrollWindow();
461     }
462   }
463 
464   return true;
465 }
466 
OnLButtonUp(const CFX_PointF & point,uint32_t nFlag)467 bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
468   CPWL_Wnd::OnLButtonUp(point, nFlag);
469 
470   if (HasFlag(PWS_AUTOTRANSPARENT)) {
471     if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) {
472       SetTransparency(PWL_SCROLLBAR_TRANSPARENCY);
473       if (!InvalidateRect(nullptr))
474         return true;
475     }
476   }
477 
478   m_pTimer.reset();
479   m_bMouseDown = false;
480   return true;
481 }
482 
SetScrollInfo(const PWL_SCROLL_INFO & info)483 void CPWL_ScrollBar::SetScrollInfo(const PWL_SCROLL_INFO& info) {
484   if (info == m_OriginInfo)
485     return;
486 
487   m_OriginInfo = info;
488   float fMax =
489       std::max(0.0f, info.fContentMax - info.fContentMin - info.fPlateWidth);
490   SetScrollRange(0, fMax, info.fPlateWidth);
491   SetScrollStep(info.fBigStep, info.fSmallStep);
492 }
493 
SetScrollPosition(float pos)494 void CPWL_ScrollBar::SetScrollPosition(float pos) {
495   switch (m_sbType) {
496     case SBT_HSCROLL:
497       pos = pos - m_OriginInfo.fContentMin;
498       break;
499     case SBT_VSCROLL:
500       pos = m_OriginInfo.fContentMax - pos;
501       break;
502   }
503   SetScrollPos(pos);
504 }
505 
NotifyLButtonDown(CPWL_Wnd * child,const CFX_PointF & pos)506 void CPWL_ScrollBar::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) {
507   if (child == m_pMinButton)
508     OnMinButtonLBDown(pos);
509   else if (child == m_pMaxButton)
510     OnMaxButtonLBDown(pos);
511   else if (child == m_pPosButton)
512     OnPosButtonLBDown(pos);
513 }
514 
NotifyLButtonUp(CPWL_Wnd * child,const CFX_PointF & pos)515 void CPWL_ScrollBar::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) {
516   if (child == m_pMinButton)
517     OnMinButtonLBUp(pos);
518   else if (child == m_pMaxButton)
519     OnMaxButtonLBUp(pos);
520   else if (child == m_pPosButton)
521     OnPosButtonLBUp(pos);
522 }
523 
NotifyMouseMove(CPWL_Wnd * child,const CFX_PointF & pos)524 void CPWL_ScrollBar::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) {
525   if (child == m_pMinButton)
526     OnMinButtonMouseMove(pos);
527   else if (child == m_pMaxButton)
528     OnMaxButtonMouseMove(pos);
529   else if (child == m_pPosButton)
530     OnPosButtonMouseMove(pos);
531 }
532 
CreateButtons(const CreateParams & cp)533 void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) {
534   CreateParams scp = cp;
535   scp.dwBorderWidth = 2;
536   scp.nBorderStyle = BorderStyle::BEVELED;
537   scp.dwFlags =
538       PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
539 
540   if (!m_pMinButton) {
541     auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
542                                                      m_sbType, PSBT_MIN);
543     m_pMinButton = pButton.get();
544     AddChild(std::move(pButton));
545     m_pMinButton->Realize();
546   }
547 
548   if (!m_pMaxButton) {
549     auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
550                                                      m_sbType, PSBT_MAX);
551     m_pMaxButton = pButton.get();
552     AddChild(std::move(pButton));
553     m_pMaxButton->Realize();
554   }
555 
556   if (!m_pPosButton) {
557     auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
558                                                      m_sbType, PSBT_POS);
559     m_pPosButton = pButton.get();
560     ObservedPtr<CPWL_ScrollBar> thisObserved(this);
561     if (m_pPosButton->SetVisible(false) && thisObserved) {
562       AddChild(std::move(pButton));
563       m_pPosButton->Realize();
564     }
565   }
566 }
567 
GetScrollBarWidth() const568 float CPWL_ScrollBar::GetScrollBarWidth() const {
569   if (!IsVisible())
570     return 0;
571 
572   return PWL_SCROLLBAR_WIDTH;
573 }
574 
SetScrollRange(float fMin,float fMax,float fClientWidth)575 void CPWL_ScrollBar::SetScrollRange(float fMin,
576                                     float fMax,
577                                     float fClientWidth) {
578   if (!m_pPosButton)
579     return;
580 
581   ObservedPtr<CPWL_ScrollBar> thisObserved(this);
582   m_sData.SetScrollRange(fMin, fMax);
583   m_sData.SetClientWidth(fClientWidth);
584 
585   if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
586     m_pPosButton->SetVisible(false);
587     // Note, |this| may no longer be viable at this point. If more work needs
588     // to be done, check thisObserved.
589     return;
590   }
591 
592   if (!m_pPosButton->SetVisible(true) || !thisObserved)
593     return;
594 
595   MovePosButton(true);
596   // Note, |this| may no longer be viable at this point. If more work needs
597   // to be done, check the return value of MovePosButton().
598 }
599 
SetScrollPos(float fPos)600 void CPWL_ScrollBar::SetScrollPos(float fPos) {
601   float fOldPos = m_sData.fScrollPos;
602   m_sData.SetPos(fPos);
603   if (!IsFloatEqual(m_sData.fScrollPos, fOldPos)) {
604     MovePosButton(true);
605     // Note, |this| may no longer be viable at this point. If more work needs
606     // to be done, check the return value of MovePosButton().
607   }
608 }
609 
SetScrollStep(float fBigStep,float fSmallStep)610 void CPWL_ScrollBar::SetScrollStep(float fBigStep, float fSmallStep) {
611   m_sData.SetBigStep(fBigStep);
612   m_sData.SetSmallStep(fSmallStep);
613 }
614 
MovePosButton(bool bRefresh)615 bool CPWL_ScrollBar::MovePosButton(bool bRefresh) {
616   ASSERT(m_pMinButton);
617   ASSERT(m_pMaxButton);
618 
619   if (m_pPosButton->IsVisible()) {
620     CFX_FloatRect rcClient;
621     CFX_FloatRect rcPosArea, rcPosButton;
622 
623     rcClient = GetClientRect();
624     rcPosArea = GetScrollArea();
625 
626     float fLeft, fRight, fTop, fBottom;
627 
628     switch (m_sbType) {
629       case SBT_HSCROLL:
630         fLeft = TrueToFace(m_sData.fScrollPos);
631         fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
632 
633         if (fRight - fLeft < kPosButtonMinWidth)
634           fRight = fLeft + kPosButtonMinWidth;
635 
636         if (fRight > rcPosArea.right) {
637           fRight = rcPosArea.right;
638           fLeft = fRight - kPosButtonMinWidth;
639         }
640 
641         rcPosButton =
642             CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top);
643 
644         break;
645       case SBT_VSCROLL:
646         fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
647         fTop = TrueToFace(m_sData.fScrollPos);
648 
649         if (IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth))
650           fBottom = fTop - kPosButtonMinWidth;
651 
652         if (IsFloatSmaller(fBottom, rcPosArea.bottom)) {
653           fBottom = rcPosArea.bottom;
654           fTop = fBottom + kPosButtonMinWidth;
655         }
656 
657         rcPosButton =
658             CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
659 
660         break;
661     }
662 
663     ObservedPtr<CPWL_ScrollBar> thisObserved(this);
664     m_pPosButton->Move(rcPosButton, true, bRefresh);
665     if (!thisObserved)
666       return false;
667   }
668 
669   return true;
670 }
671 
OnMinButtonLBDown(const CFX_PointF & point)672 void CPWL_ScrollBar::OnMinButtonLBDown(const CFX_PointF& point) {
673   m_sData.SubSmall();
674   if (!MovePosButton(true))
675     return;
676 
677   NotifyScrollWindow();
678   m_bMinOrMax = true;
679   m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
680 }
681 
OnMinButtonLBUp(const CFX_PointF & point)682 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
683 
OnMinButtonMouseMove(const CFX_PointF & point)684 void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {}
685 
OnMaxButtonLBDown(const CFX_PointF & point)686 void CPWL_ScrollBar::OnMaxButtonLBDown(const CFX_PointF& point) {
687   m_sData.AddSmall();
688   if (!MovePosButton(true))
689     return;
690 
691   NotifyScrollWindow();
692   m_bMinOrMax = false;
693   m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
694 }
695 
OnMaxButtonLBUp(const CFX_PointF & point)696 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
697 
OnMaxButtonMouseMove(const CFX_PointF & point)698 void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {}
699 
OnPosButtonLBDown(const CFX_PointF & point)700 void CPWL_ScrollBar::OnPosButtonLBDown(const CFX_PointF& point) {
701   m_bMouseDown = true;
702 
703   if (m_pPosButton) {
704     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
705 
706     switch (m_sbType) {
707       case SBT_HSCROLL:
708         m_nOldPos = point.x;
709         m_fOldPosButton = rcPosButton.left;
710         break;
711       case SBT_VSCROLL:
712         m_nOldPos = point.y;
713         m_fOldPosButton = rcPosButton.top;
714         break;
715     }
716   }
717 }
718 
OnPosButtonLBUp(const CFX_PointF & point)719 void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) {
720   if (m_bMouseDown) {
721     if (!m_bNotifyForever)
722       NotifyScrollWindow();
723   }
724   m_bMouseDown = false;
725 }
726 
OnPosButtonMouseMove(const CFX_PointF & point)727 void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) {
728   float fOldScrollPos = m_sData.fScrollPos;
729 
730   float fNewPos = 0;
731 
732   switch (m_sbType) {
733     case SBT_HSCROLL:
734       if (fabs(point.x - m_nOldPos) < 1)
735         return;
736       fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos);
737       break;
738     case SBT_VSCROLL:
739       if (fabs(point.y - m_nOldPos) < 1)
740         return;
741       fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos);
742       break;
743   }
744 
745   if (m_bMouseDown) {
746     switch (m_sbType) {
747       case SBT_HSCROLL:
748 
749         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
750           fNewPos = m_sData.ScrollRange.fMin;
751         }
752 
753         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
754           fNewPos = m_sData.ScrollRange.fMax;
755         }
756 
757         m_sData.SetPos(fNewPos);
758 
759         break;
760       case SBT_VSCROLL:
761 
762         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
763           fNewPos = m_sData.ScrollRange.fMin;
764         }
765 
766         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
767           fNewPos = m_sData.ScrollRange.fMax;
768         }
769 
770         m_sData.SetPos(fNewPos);
771 
772         break;
773     }
774 
775     if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) {
776       if (!MovePosButton(true))
777         return;
778 
779       if (m_bNotifyForever)
780         NotifyScrollWindow();
781     }
782   }
783 }
784 
NotifyScrollWindow()785 void CPWL_ScrollBar::NotifyScrollWindow() {
786   CPWL_Wnd* pParent = GetParentWindow();
787   if (!pParent || m_sbType != SBT_VSCROLL)
788     return;
789 
790   pParent->ScrollWindowVertically(m_OriginInfo.fContentMax -
791                                   m_sData.fScrollPos);
792 }
793 
GetScrollArea() const794 CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const {
795   CFX_FloatRect rcClient = GetClientRect();
796   CFX_FloatRect rcArea;
797 
798   if (!m_pMinButton || !m_pMaxButton)
799     return rcClient;
800 
801   CFX_FloatRect rcMin = m_pMinButton->GetWindowRect();
802   CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect();
803 
804   float fMinWidth = rcMin.Width();
805   float fMinHeight = rcMin.Height();
806   float fMaxWidth = rcMax.Width();
807   float fMaxHeight = rcMax.Height();
808 
809   switch (m_sbType) {
810     case SBT_HSCROLL:
811       if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) {
812         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
813                                rcClient.right - fMaxWidth - 1, rcClient.top);
814       } else {
815         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
816                                rcClient.left + fMinWidth + 1, rcClient.top);
817       }
818       break;
819     case SBT_VSCROLL:
820       if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) {
821         rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
822                                rcClient.right, rcClient.top - fMaxHeight - 1);
823       } else {
824         rcArea =
825             CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
826                           rcClient.right, rcClient.bottom + fMinHeight + 1);
827       }
828       break;
829   }
830 
831   rcArea.Normalize();
832 
833   return rcArea;
834 }
835 
TrueToFace(float fTrue)836 float CPWL_ScrollBar::TrueToFace(float fTrue) {
837   CFX_FloatRect rcPosArea;
838   rcPosArea = GetScrollArea();
839 
840   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
841   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
842 
843   float fFace = 0;
844 
845   switch (m_sbType) {
846     case SBT_HSCROLL:
847       fFace = rcPosArea.left +
848               fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth;
849       break;
850     case SBT_VSCROLL:
851       fFace = rcPosArea.top -
852               fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth;
853       break;
854   }
855 
856   return fFace;
857 }
858 
FaceToTrue(float fFace)859 float CPWL_ScrollBar::FaceToTrue(float fFace) {
860   CFX_FloatRect rcPosArea;
861   rcPosArea = GetScrollArea();
862 
863   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
864   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
865 
866   float fTrue = 0;
867 
868   switch (m_sbType) {
869     case SBT_HSCROLL:
870       fTrue = (fFace - rcPosArea.left) * fFactWidth /
871               (rcPosArea.right - rcPosArea.left);
872       break;
873     case SBT_VSCROLL:
874       fTrue = (rcPosArea.top - fFace) * fFactWidth /
875               (rcPosArea.top - rcPosArea.bottom);
876       break;
877   }
878 
879   return fTrue;
880 }
881 
CreateChildWnd(const CreateParams & cp)882 void CPWL_ScrollBar::CreateChildWnd(const CreateParams& cp) {
883   CreateButtons(cp);
884 }
885 
OnTimerFired()886 void CPWL_ScrollBar::OnTimerFired() {
887   PWL_SCROLL_PRIVATEDATA sTemp = m_sData;
888   if (m_bMinOrMax)
889     m_sData.SubSmall();
890   else
891     m_sData.AddSmall();
892 
893   if (sTemp == m_sData)
894     return;
895 
896   if (!MovePosButton(true))
897     return;
898 
899   NotifyScrollWindow();
900 }
901