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 27 PWL_FLOATRANGE::PWL_FLOATRANGE() { 28 Default(); 29 } 30 31 PWL_FLOATRANGE::PWL_FLOATRANGE(float min, float max) { 32 Set(min, max); 33 } 34 35 void PWL_FLOATRANGE::Default() { 36 fMin = 0; 37 fMax = 0; 38 } 39 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 50 bool PWL_FLOATRANGE::In(float x) const { 51 return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) && 52 (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax)); 53 } 54 55 float PWL_FLOATRANGE::GetWidth() const { 56 return fMax - fMin; 57 } 58 59 PWL_SCROLL_PRIVATEDATA::PWL_SCROLL_PRIVATEDATA() { 60 Default(); 61 } 62 63 void PWL_SCROLL_PRIVATEDATA::Default() { 64 ScrollRange.Default(); 65 fScrollPos = ScrollRange.fMin; 66 fClientWidth = 0; 67 fBigStep = 10; 68 fSmallStep = 1; 69 } 70 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 80 void PWL_SCROLL_PRIVATEDATA::SetClientWidth(float width) { 81 fClientWidth = width; 82 } 83 84 void PWL_SCROLL_PRIVATEDATA::SetSmallStep(float step) { 85 fSmallStep = step; 86 } 87 88 void PWL_SCROLL_PRIVATEDATA::SetBigStep(float step) { 89 fBigStep = step; 90 } 91 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 100 void PWL_SCROLL_PRIVATEDATA::AddSmall() { 101 if (!SetPos(fScrollPos + fSmallStep)) 102 SetPos(ScrollRange.fMax); 103 } 104 105 void PWL_SCROLL_PRIVATEDATA::SubSmall() { 106 if (!SetPos(fScrollPos - fSmallStep)) 107 SetPos(ScrollRange.fMin); 108 } 109 110 void PWL_SCROLL_PRIVATEDATA::AddBig() { 111 if (!SetPos(fScrollPos + fBigStep)) 112 SetPos(ScrollRange.fMax); 113 } 114 115 void PWL_SCROLL_PRIVATEDATA::SubBig() { 116 if (!SetPos(fScrollPos - fBigStep)) 117 SetPos(ScrollRange.fMin); 118 } 119 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 128 CPWL_SBButton::~CPWL_SBButton() {} 129 130 ByteString CPWL_SBButton::GetClassName() const { 131 return "CPWL_SBButton"; 132 } 133 134 void CPWL_SBButton::OnCreate(CreateParams* pParamsToAdjust) { 135 pParamsToAdjust->eCursorType = FXCT_ARROW; 136 } 137 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 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 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 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 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 330 CPWL_ScrollBar::~CPWL_ScrollBar() {} 331 332 ByteString CPWL_ScrollBar::GetClassName() const { 333 return "CPWL_ScrollBar"; 334 } 335 336 void CPWL_ScrollBar::OnCreate(CreateParams* pParamsToAdjust) { 337 pParamsToAdjust->eCursorType = FXCT_ARROW; 338 } 339 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 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 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 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 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 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 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 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 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 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 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 594 float CPWL_ScrollBar::GetScrollBarWidth() const { 595 if (!IsVisible()) 596 return 0; 597 598 return PWL_SCROLLBAR_WIDTH; 599 } 600 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 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 637 void CPWL_ScrollBar::SetScrollStep(float fBigStep, float fSmallStep) { 638 m_sData.SetBigStep(fBigStep); 639 m_sData.SetSmallStep(fSmallStep); 640 } 641 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 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 712 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {} 713 714 void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {} 715 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 728 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {} 729 730 void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {} 731 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 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 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 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 826 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 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 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 914 void CPWL_ScrollBar::CreateChildWnd(const CreateParams& cp) { 915 CreateButtons(cp); 916 } 917 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