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