Home | History | Annotate | Download | only in fwl
      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 "xfa/fwl/cfwl_scrollbar.h"
      8 
      9 #include <algorithm>
     10 #include <memory>
     11 #include <utility>
     12 
     13 #include "third_party/base/ptr_util.h"
     14 #include "third_party/base/stl_util.h"
     15 #include "xfa/fwl/cfwl_messagemouse.h"
     16 #include "xfa/fwl/cfwl_messagemousewheel.h"
     17 #include "xfa/fwl/cfwl_notedriver.h"
     18 #include "xfa/fwl/cfwl_themebackground.h"
     19 #include "xfa/fwl/cfwl_themepart.h"
     20 #include "xfa/fwl/cfwl_timerinfo.h"
     21 #include "xfa/fwl/ifwl_themeprovider.h"
     22 
     23 #define FWL_SCROLLBAR_Elapse 500
     24 
     25 namespace {
     26 
     27 const float kMinThumbSize = 5.0f;
     28 
     29 }  // namespace
     30 
     31 CFWL_ScrollBar::CFWL_ScrollBar(
     32     const CFWL_App* app,
     33     std::unique_ptr<CFWL_WidgetProperties> properties,
     34     CFWL_Widget* pOuter)
     35     : CFWL_Widget(app, std::move(properties), pOuter),
     36       m_pTimerInfo(nullptr),
     37       m_fRangeMin(0),
     38       m_fRangeMax(-1),
     39       m_fPageSize(0),
     40       m_fStepSize(0),
     41       m_fPos(0),
     42       m_fTrackPos(0),
     43       m_iMinButtonState(CFWL_PartState_Normal),
     44       m_iMaxButtonState(CFWL_PartState_Normal),
     45       m_iThumbButtonState(CFWL_PartState_Normal),
     46       m_iMinTrackState(CFWL_PartState_Normal),
     47       m_iMaxTrackState(CFWL_PartState_Normal),
     48       m_fLastTrackPos(0),
     49       m_iMouseWheel(0),
     50       m_bMouseDown(false),
     51       m_fButtonLen(0),
     52       m_bMinSize(false),
     53       m_Timer(this) {
     54   m_rtClient.Reset();
     55   m_rtThumb.Reset();
     56   m_rtMinBtn.Reset();
     57   m_rtMaxBtn.Reset();
     58   m_rtMinTrack.Reset();
     59   m_rtMaxTrack.Reset();
     60 }
     61 
     62 CFWL_ScrollBar::~CFWL_ScrollBar() {}
     63 
     64 FWL_Type CFWL_ScrollBar::GetClassID() const {
     65   return FWL_Type::ScrollBar;
     66 }
     67 
     68 void CFWL_ScrollBar::Update() {
     69   if (IsLocked())
     70     return;
     71   if (!m_pProperties->m_pThemeProvider)
     72     m_pProperties->m_pThemeProvider = GetAvailableTheme();
     73 
     74   Layout();
     75 }
     76 
     77 void CFWL_ScrollBar::DrawWidget(CXFA_Graphics* pGraphics,
     78                                 const CFX_Matrix& matrix) {
     79   if (!pGraphics)
     80     return;
     81   if (!m_pProperties->m_pThemeProvider)
     82     return;
     83 
     84   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
     85   if (HasBorder())
     86     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
     87   DrawTrack(pGraphics, pTheme, true, &matrix);
     88   DrawTrack(pGraphics, pTheme, false, &matrix);
     89   DrawArrowBtn(pGraphics, pTheme, true, &matrix);
     90   DrawArrowBtn(pGraphics, pTheme, false, &matrix);
     91   DrawThumb(pGraphics, pTheme, &matrix);
     92 }
     93 
     94 void CFWL_ScrollBar::SetTrackPos(float fTrackPos) {
     95   m_fTrackPos = fTrackPos;
     96   m_rtThumb = CalcThumbButtonRect(m_rtThumb);
     97   m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
     98   m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
     99 }
    100 
    101 bool CFWL_ScrollBar::DoScroll(CFWL_EventScroll::Code dwCode, float fPos) {
    102   if (dwCode == CFWL_EventScroll::Code::None)
    103     return false;
    104   return OnScroll(dwCode, fPos);
    105 }
    106 
    107 void CFWL_ScrollBar::DrawTrack(CXFA_Graphics* pGraphics,
    108                                IFWL_ThemeProvider* pTheme,
    109                                bool bLower,
    110                                const CFX_Matrix* pMatrix) {
    111   CFWL_ThemeBackground param;
    112   param.m_pWidget = this;
    113   param.m_iPart = bLower ? CFWL_Part::LowerTrack : CFWL_Part::UpperTrack;
    114   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    115                          ? CFWL_PartState_Disabled
    116                          : (bLower ? m_iMinTrackState : m_iMaxTrackState);
    117   param.m_pGraphics = pGraphics;
    118   param.m_matrix.Concat(*pMatrix);
    119   param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
    120   pTheme->DrawBackground(&param);
    121 }
    122 
    123 void CFWL_ScrollBar::DrawArrowBtn(CXFA_Graphics* pGraphics,
    124                                   IFWL_ThemeProvider* pTheme,
    125                                   bool bMinBtn,
    126                                   const CFX_Matrix* pMatrix) {
    127   CFWL_ThemeBackground param;
    128   param.m_pWidget = this;
    129   param.m_iPart = bMinBtn ? CFWL_Part::ForeArrow : CFWL_Part::BackArrow;
    130   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    131                          ? CFWL_PartState_Disabled
    132                          : (bMinBtn ? m_iMinButtonState : m_iMaxButtonState);
    133   param.m_pGraphics = pGraphics;
    134   param.m_matrix.Concat(*pMatrix);
    135   param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
    136   if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0)
    137     pTheme->DrawBackground(&param);
    138 }
    139 
    140 void CFWL_ScrollBar::DrawThumb(CXFA_Graphics* pGraphics,
    141                                IFWL_ThemeProvider* pTheme,
    142                                const CFX_Matrix* pMatrix) {
    143   CFWL_ThemeBackground param;
    144   param.m_pWidget = this;
    145   param.m_iPart = CFWL_Part::Thumb;
    146   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    147                          ? CFWL_PartState_Disabled
    148                          : m_iThumbButtonState;
    149   param.m_pGraphics = pGraphics;
    150   param.m_matrix.Concat(*pMatrix);
    151   param.m_rtPart = m_rtThumb;
    152   pTheme->DrawBackground(&param);
    153 }
    154 
    155 void CFWL_ScrollBar::Layout() {
    156   m_rtClient = GetClientRect();
    157 
    158   CalcButtonLen();
    159   m_rtMinBtn = CalcMinButtonRect();
    160   m_rtMaxBtn = CalcMaxButtonRect();
    161   m_rtThumb = CalcThumbButtonRect(m_rtThumb);
    162   m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
    163   m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
    164 }
    165 
    166 void CFWL_ScrollBar::CalcButtonLen() {
    167   m_fButtonLen = IsVertical() ? m_rtClient.width : m_rtClient.height;
    168   float fLength = IsVertical() ? m_rtClient.height : m_rtClient.width;
    169   if (fLength < m_fButtonLen * 2) {
    170     m_fButtonLen = fLength / 2;
    171     m_bMinSize = true;
    172   } else {
    173     m_bMinSize = false;
    174   }
    175 }
    176 
    177 CFX_RectF CFWL_ScrollBar::CalcMinButtonRect() {
    178   if (IsVertical())
    179     return CFX_RectF(m_rtClient.TopLeft(), m_rtClient.width, m_fButtonLen);
    180   return CFX_RectF(m_rtClient.TopLeft(), m_fButtonLen, m_rtClient.height);
    181 }
    182 
    183 CFX_RectF CFWL_ScrollBar::CalcMaxButtonRect() {
    184   if (IsVertical()) {
    185     return CFX_RectF(m_rtClient.left, m_rtClient.bottom() - m_fButtonLen,
    186                      m_rtClient.width, m_fButtonLen);
    187   }
    188   return CFX_RectF(m_rtClient.right() - m_fButtonLen, m_rtClient.top,
    189                    m_fButtonLen, m_rtClient.height);
    190 }
    191 
    192 CFX_RectF CFWL_ScrollBar::CalcThumbButtonRect(const CFX_RectF& rtThumb) {
    193   CFX_RectF rect;
    194   if (!IsEnabled())
    195     return rect;
    196 
    197   if (m_bMinSize) {
    198     rect.left = rtThumb.left;
    199     rect.top = rtThumb.top;
    200     return rect;
    201   }
    202 
    203   float fRange = m_fRangeMax - m_fRangeMin;
    204   if (fRange < 0) {
    205     if (IsVertical()) {
    206       return CFX_RectF(m_rtClient.left, m_rtMaxBtn.bottom(), m_rtClient.width,
    207                        0);
    208     }
    209     return CFX_RectF(m_rtMaxBtn.right(), m_rtClient.top, 0, m_rtClient.height);
    210   }
    211 
    212   CFX_RectF rtClient = m_rtClient;
    213   float fLength = IsVertical() ? rtClient.height : rtClient.width;
    214   float fSize = m_fButtonLen;
    215   fLength -= fSize * 2.0f;
    216   if (fLength < fSize)
    217     fLength = 0.0f;
    218 
    219   float fThumbSize = fLength * fLength / (fRange + fLength);
    220   fThumbSize = std::max(fThumbSize, kMinThumbSize);
    221 
    222   float fDiff = std::max(fLength - fThumbSize, 0.0f);
    223   float fTrackPos = pdfium::clamp(m_fTrackPos, m_fRangeMin, m_fRangeMax);
    224   if (!fRange)
    225     return rect;
    226 
    227   float iPos = fSize + fDiff * (fTrackPos - m_fRangeMin) / fRange;
    228   rect.left = rtClient.left;
    229   rect.top = rtClient.top;
    230   if (IsVertical()) {
    231     rect.top += iPos;
    232     rect.width = rtClient.width;
    233     rect.height = fThumbSize;
    234   } else {
    235     rect.left += iPos;
    236     rect.width = fThumbSize;
    237     rect.height = rtClient.height;
    238   }
    239   return rect;
    240 }
    241 
    242 CFX_RectF CFWL_ScrollBar::CalcMinTrackRect(const CFX_RectF& rtMinRect) {
    243   CFX_RectF rect;
    244   if (m_bMinSize) {
    245     rect.left = rtMinRect.left;
    246     rect.top = rtMinRect.top;
    247     return rect;
    248   }
    249 
    250   rect.left = m_rtClient.left;
    251   rect.top = m_rtClient.top;
    252   if (IsVertical()) {
    253     rect.width = m_rtClient.width;
    254     rect.height = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
    255   } else {
    256     rect.width = (m_rtThumb.left + m_rtThumb.right()) / 2;
    257     rect.height = m_rtClient.height;
    258   }
    259   return rect;
    260 }
    261 
    262 CFX_RectF CFWL_ScrollBar::CalcMaxTrackRect(const CFX_RectF& rtMaxRect) {
    263   if (m_bMinSize)
    264     return CFX_RectF(rtMaxRect.TopLeft(), 0, 0);
    265 
    266   if (IsVertical()) {
    267     float iy = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
    268     return CFX_RectF(m_rtClient.left, iy, m_rtClient.width,
    269                      m_rtClient.bottom() - iy);
    270   }
    271 
    272   float ix = (m_rtThumb.left + m_rtThumb.right()) / 2;
    273   return CFX_RectF(ix, m_rtClient.top, m_rtClient.height - ix,
    274                    m_rtClient.height);
    275 }
    276 
    277 float CFWL_ScrollBar::GetTrackPointPos(const CFX_PointF& point) {
    278   CFX_PointF diff = point - m_cpTrackPoint;
    279   float fRange = m_fRangeMax - m_fRangeMin;
    280   float fPos;
    281 
    282   if (IsVertical()) {
    283     fPos = fRange * diff.y /
    284            (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
    285   } else {
    286     fPos = fRange * diff.x /
    287            (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
    288   }
    289 
    290   fPos += m_fLastTrackPos;
    291   return pdfium::clamp(fPos, m_fRangeMin, m_fRangeMax);
    292 }
    293 
    294 bool CFWL_ScrollBar::SendEvent() {
    295   if (m_iMinButtonState == CFWL_PartState_Pressed) {
    296     DoScroll(CFWL_EventScroll::Code::StepBackward, m_fTrackPos);
    297     return false;
    298   }
    299   if (m_iMaxButtonState == CFWL_PartState_Pressed) {
    300     DoScroll(CFWL_EventScroll::Code::StepForward, m_fTrackPos);
    301     return false;
    302   }
    303   if (m_iMinTrackState == CFWL_PartState_Pressed) {
    304     DoScroll(CFWL_EventScroll::Code::PageBackward, m_fTrackPos);
    305     return m_rtThumb.Contains(m_cpTrackPoint);
    306   }
    307   if (m_iMaxTrackState == CFWL_PartState_Pressed) {
    308     DoScroll(CFWL_EventScroll::Code::PageForward, m_fTrackPos);
    309     return m_rtThumb.Contains(m_cpTrackPoint);
    310   }
    311   if (m_iMouseWheel) {
    312     CFWL_EventScroll::Code dwCode = m_iMouseWheel < 0
    313                                         ? CFWL_EventScroll::Code::StepForward
    314                                         : CFWL_EventScroll::Code::StepBackward;
    315     DoScroll(dwCode, m_fTrackPos);
    316   }
    317   return true;
    318 }
    319 
    320 bool CFWL_ScrollBar::OnScroll(CFWL_EventScroll::Code dwCode, float fPos) {
    321   CFWL_EventScroll ev(this);
    322   ev.m_iScrollCode = dwCode;
    323   ev.m_fPos = fPos;
    324   DispatchEvent(&ev);
    325   return true;
    326 }
    327 
    328 void CFWL_ScrollBar::OnProcessMessage(CFWL_Message* pMessage) {
    329   if (!pMessage)
    330     return;
    331 
    332   CFWL_Message::Type type = pMessage->GetType();
    333   if (type == CFWL_Message::Type::Mouse) {
    334     CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
    335     switch (pMsg->m_dwCmd) {
    336       case FWL_MouseCommand::LeftButtonDown:
    337         OnLButtonDown(pMsg->m_pos);
    338         break;
    339       case FWL_MouseCommand::LeftButtonUp:
    340         OnLButtonUp(pMsg->m_pos);
    341         break;
    342       case FWL_MouseCommand::Move:
    343         OnMouseMove(pMsg->m_pos);
    344         break;
    345       case FWL_MouseCommand::Leave:
    346         OnMouseLeave();
    347         break;
    348       default:
    349         break;
    350     }
    351   } else if (type == CFWL_Message::Type::MouseWheel) {
    352     CFWL_MessageMouseWheel* pMsg =
    353         static_cast<CFWL_MessageMouseWheel*>(pMessage);
    354     OnMouseWheel(pMsg->m_delta);
    355   }
    356 }
    357 
    358 void CFWL_ScrollBar::OnDrawWidget(CXFA_Graphics* pGraphics,
    359                                   const CFX_Matrix& matrix) {
    360   DrawWidget(pGraphics, matrix);
    361 }
    362 
    363 void CFWL_ScrollBar::OnLButtonDown(const CFX_PointF& point) {
    364   if (!IsEnabled())
    365     return;
    366 
    367   m_bMouseDown = true;
    368   SetGrab(true);
    369 
    370   m_cpTrackPoint = point;
    371   m_fLastTrackPos = m_fTrackPos;
    372   if (m_rtMinBtn.Contains(point))
    373     DoMouseDown(0, m_rtMinBtn, m_iMinButtonState, point);
    374   else if (m_rtThumb.Contains(point))
    375     DoMouseDown(1, m_rtThumb, m_iThumbButtonState, point);
    376   else if (m_rtMaxBtn.Contains(point))
    377     DoMouseDown(2, m_rtMaxBtn, m_iMaxButtonState, point);
    378   else if (m_rtMinTrack.Contains(point))
    379     DoMouseDown(3, m_rtMinTrack, m_iMinTrackState, point);
    380   else
    381     DoMouseDown(4, m_rtMaxTrack, m_iMaxTrackState, point);
    382 
    383   if (!SendEvent())
    384     m_pTimerInfo = m_Timer.StartTimer(FWL_SCROLLBAR_Elapse, true);
    385 }
    386 
    387 void CFWL_ScrollBar::OnLButtonUp(const CFX_PointF& point) {
    388   m_pTimerInfo->StopTimer();
    389   m_bMouseDown = false;
    390   DoMouseUp(0, m_rtMinBtn, m_iMinButtonState, point);
    391   DoMouseUp(1, m_rtThumb, m_iThumbButtonState, point);
    392   DoMouseUp(2, m_rtMaxBtn, m_iMaxButtonState, point);
    393   DoMouseUp(3, m_rtMinTrack, m_iMinTrackState, point);
    394   DoMouseUp(4, m_rtMaxTrack, m_iMaxTrackState, point);
    395   SetGrab(false);
    396 }
    397 
    398 void CFWL_ScrollBar::OnMouseMove(const CFX_PointF& point) {
    399   DoMouseMove(0, m_rtMinBtn, m_iMinButtonState, point);
    400   DoMouseMove(1, m_rtThumb, m_iThumbButtonState, point);
    401   DoMouseMove(2, m_rtMaxBtn, m_iMaxButtonState, point);
    402   DoMouseMove(3, m_rtMinTrack, m_iMinTrackState, point);
    403   DoMouseMove(4, m_rtMaxTrack, m_iMaxTrackState, point);
    404 }
    405 
    406 void CFWL_ScrollBar::OnMouseLeave() {
    407   DoMouseLeave(0, m_rtMinBtn, m_iMinButtonState);
    408   DoMouseLeave(1, m_rtThumb, m_iThumbButtonState);
    409   DoMouseLeave(2, m_rtMaxBtn, m_iMaxButtonState);
    410   DoMouseLeave(3, m_rtMinTrack, m_iMinTrackState);
    411   DoMouseLeave(4, m_rtMaxTrack, m_iMaxTrackState);
    412 }
    413 
    414 void CFWL_ScrollBar::OnMouseWheel(const CFX_PointF& delta) {
    415   m_iMouseWheel = static_cast<int32_t>(delta.x);
    416   SendEvent();
    417   m_iMouseWheel = 0;
    418 }
    419 
    420 void CFWL_ScrollBar::DoMouseDown(int32_t iItem,
    421                                  const CFX_RectF& rtItem,
    422                                  int32_t& iState,
    423                                  const CFX_PointF& point) {
    424   if (!rtItem.Contains(point))
    425     return;
    426   if (iState == CFWL_PartState_Pressed)
    427     return;
    428 
    429   iState = CFWL_PartState_Pressed;
    430   RepaintRect(rtItem);
    431 }
    432 
    433 void CFWL_ScrollBar::DoMouseUp(int32_t iItem,
    434                                const CFX_RectF& rtItem,
    435                                int32_t& iState,
    436                                const CFX_PointF& point) {
    437   int32_t iNewState =
    438       rtItem.Contains(point) ? CFWL_PartState_Hovered : CFWL_PartState_Normal;
    439   if (iState == iNewState)
    440     return;
    441 
    442   iState = iNewState;
    443   RepaintRect(rtItem);
    444   OnScroll(CFWL_EventScroll::Code::EndScroll, m_fTrackPos);
    445 }
    446 
    447 void CFWL_ScrollBar::DoMouseMove(int32_t iItem,
    448                                  const CFX_RectF& rtItem,
    449                                  int32_t& iState,
    450                                  const CFX_PointF& point) {
    451   if (!m_bMouseDown) {
    452     int32_t iNewState =
    453         rtItem.Contains(point) ? CFWL_PartState_Hovered : CFWL_PartState_Normal;
    454     if (iState == iNewState)
    455       return;
    456 
    457     iState = iNewState;
    458     RepaintRect(rtItem);
    459   } else if ((2 == iItem) && (m_iThumbButtonState == CFWL_PartState_Pressed)) {
    460     m_fTrackPos = GetTrackPointPos(point);
    461     OnScroll(CFWL_EventScroll::Code::TrackPos, m_fTrackPos);
    462   }
    463 }
    464 
    465 void CFWL_ScrollBar::DoMouseLeave(int32_t iItem,
    466                                   const CFX_RectF& rtItem,
    467                                   int32_t& iState) {
    468   if (iState == CFWL_PartState_Normal)
    469     return;
    470 
    471   iState = CFWL_PartState_Normal;
    472   RepaintRect(rtItem);
    473 }
    474 
    475 void CFWL_ScrollBar::DoMouseHover(int32_t iItem,
    476                                   const CFX_RectF& rtItem,
    477                                   int32_t& iState) {
    478   if (iState == CFWL_PartState_Hovered)
    479     return;
    480 
    481   iState = CFWL_PartState_Hovered;
    482   RepaintRect(rtItem);
    483 }
    484 
    485 CFWL_ScrollBar::Timer::Timer(CFWL_ScrollBar* pToolTip) : CFWL_Timer(pToolTip) {}
    486 
    487 void CFWL_ScrollBar::Timer::Run(CFWL_TimerInfo* pTimerInfo) {
    488   CFWL_ScrollBar* pButton = static_cast<CFWL_ScrollBar*>(m_pWidget.Get());
    489   if (pButton->m_pTimerInfo)
    490     pButton->m_pTimerInfo->StopTimer();
    491 
    492   if (!pButton->SendEvent())
    493     pButton->m_pTimerInfo = StartTimer(0, true);
    494 }
    495