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