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 "xfa/fwl/cfwl_messagemouse.h"
     15 #include "xfa/fwl/cfwl_messagemousewheel.h"
     16 #include "xfa/fwl/cfwl_notedriver.h"
     17 #include "xfa/fwl/cfwl_themebackground.h"
     18 #include "xfa/fwl/cfwl_themepart.h"
     19 #include "xfa/fwl/cfwl_timerinfo.h"
     20 #include "xfa/fwl/ifwl_themeprovider.h"
     21 
     22 #define FWL_SCROLLBAR_Elapse 500
     23 
     24 namespace {
     25 
     26 const float kMinThumbSize = 5.0f;
     27 
     28 }  // namespace
     29 
     30 CFWL_ScrollBar::CFWL_ScrollBar(
     31     const CFWL_App* app,
     32     std::unique_ptr<CFWL_WidgetProperties> properties,
     33     CFWL_Widget* pOuter)
     34     : CFWL_Widget(app, std::move(properties), pOuter),
     35       m_pTimerInfo(nullptr),
     36       m_fRangeMin(0),
     37       m_fRangeMax(-1),
     38       m_fPageSize(0),
     39       m_fStepSize(0),
     40       m_fPos(0),
     41       m_fTrackPos(0),
     42       m_iMinButtonState(CFWL_PartState_Normal),
     43       m_iMaxButtonState(CFWL_PartState_Normal),
     44       m_iThumbButtonState(CFWL_PartState_Normal),
     45       m_iMinTrackState(CFWL_PartState_Normal),
     46       m_iMaxTrackState(CFWL_PartState_Normal),
     47       m_fLastTrackPos(0),
     48       m_iMouseWheel(0),
     49       m_bMouseDown(false),
     50       m_fButtonLen(0),
     51       m_bMinSize(false),
     52       m_Timer(this) {
     53   m_rtClient.Reset();
     54   m_rtThumb.Reset();
     55   m_rtMinBtn.Reset();
     56   m_rtMaxBtn.Reset();
     57   m_rtMinTrack.Reset();
     58   m_rtMaxTrack.Reset();
     59 }
     60 
     61 CFWL_ScrollBar::~CFWL_ScrollBar() {}
     62 
     63 FWL_Type CFWL_ScrollBar::GetClassID() const {
     64   return FWL_Type::ScrollBar;
     65 }
     66 
     67 void CFWL_ScrollBar::Update() {
     68   if (IsLocked())
     69     return;
     70   if (!m_pProperties->m_pThemeProvider)
     71     m_pProperties->m_pThemeProvider = GetAvailableTheme();
     72 
     73   Layout();
     74 }
     75 
     76 void CFWL_ScrollBar::DrawWidget(CFX_Graphics* pGraphics,
     77                                 const CFX_Matrix* pMatrix) {
     78   if (!pGraphics)
     79     return;
     80   if (!m_pProperties->m_pThemeProvider)
     81     return;
     82 
     83   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
     84   if (HasBorder())
     85     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, pMatrix);
     86   DrawTrack(pGraphics, pTheme, true, pMatrix);
     87   DrawTrack(pGraphics, pTheme, false, pMatrix);
     88   DrawArrowBtn(pGraphics, pTheme, true, pMatrix);
     89   DrawArrowBtn(pGraphics, pTheme, false, pMatrix);
     90   DrawThumb(pGraphics, pTheme, pMatrix);
     91 }
     92 
     93 void CFWL_ScrollBar::SetTrackPos(FX_FLOAT fTrackPos) {
     94   m_fTrackPos = fTrackPos;
     95   m_rtThumb = CalcThumbButtonRect(m_rtThumb);
     96   m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
     97   m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
     98 }
     99 
    100 bool CFWL_ScrollBar::DoScroll(CFWL_EventScroll::Code dwCode, FX_FLOAT fPos) {
    101   if (dwCode == CFWL_EventScroll::Code::None)
    102     return false;
    103   return OnScroll(dwCode, fPos);
    104 }
    105 
    106 void CFWL_ScrollBar::DrawTrack(CFX_Graphics* pGraphics,
    107                                IFWL_ThemeProvider* pTheme,
    108                                bool bLower,
    109                                const CFX_Matrix* pMatrix) {
    110   CFWL_ThemeBackground param;
    111   param.m_pWidget = this;
    112   param.m_iPart = bLower ? CFWL_Part::LowerTrack : CFWL_Part::UpperTrack;
    113   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    114                          ? CFWL_PartState_Disabled
    115                          : (bLower ? m_iMinTrackState : m_iMaxTrackState);
    116   param.m_pGraphics = pGraphics;
    117   param.m_matrix.Concat(*pMatrix);
    118   param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
    119   pTheme->DrawBackground(&param);
    120 }
    121 
    122 void CFWL_ScrollBar::DrawArrowBtn(CFX_Graphics* pGraphics,
    123                                   IFWL_ThemeProvider* pTheme,
    124                                   bool bMinBtn,
    125                                   const CFX_Matrix* pMatrix) {
    126   CFWL_ThemeBackground param;
    127   param.m_pWidget = this;
    128   param.m_iPart = bMinBtn ? CFWL_Part::ForeArrow : CFWL_Part::BackArrow;
    129   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    130                          ? CFWL_PartState_Disabled
    131                          : (bMinBtn ? m_iMinButtonState : m_iMaxButtonState);
    132   param.m_pGraphics = pGraphics;
    133   param.m_matrix.Concat(*pMatrix);
    134   param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
    135   if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0)
    136     pTheme->DrawBackground(&param);
    137 }
    138 
    139 void CFWL_ScrollBar::DrawThumb(CFX_Graphics* pGraphics,
    140                                IFWL_ThemeProvider* pTheme,
    141                                const CFX_Matrix* pMatrix) {
    142   CFWL_ThemeBackground param;
    143   param.m_pWidget = this;
    144   param.m_iPart = CFWL_Part::Thumb;
    145   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    146                          ? CFWL_PartState_Disabled
    147                          : m_iThumbButtonState;
    148   param.m_pGraphics = pGraphics;
    149   param.m_matrix.Concat(*pMatrix);
    150   param.m_rtPart = m_rtThumb;
    151   pTheme->DrawBackground(&param);
    152 }
    153 
    154 void CFWL_ScrollBar::Layout() {
    155   m_rtClient = GetClientRect();
    156 
    157   CalcButtonLen();
    158   m_rtMinBtn = CalcMinButtonRect();
    159   m_rtMaxBtn = CalcMaxButtonRect();
    160   m_rtThumb = CalcThumbButtonRect(m_rtThumb);
    161   m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
    162   m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
    163 }
    164 
    165 void CFWL_ScrollBar::CalcButtonLen() {
    166   m_fButtonLen = IsVertical() ? m_rtClient.width : m_rtClient.height;
    167   FX_FLOAT fLength = IsVertical() ? m_rtClient.height : m_rtClient.width;
    168   if (fLength < m_fButtonLen * 2) {
    169     m_fButtonLen = fLength / 2;
    170     m_bMinSize = true;
    171   } else {
    172     m_bMinSize = false;
    173   }
    174 }
    175 
    176 CFX_RectF CFWL_ScrollBar::CalcMinButtonRect() {
    177   if (IsVertical())
    178     return CFX_RectF(m_rtClient.TopLeft(), m_rtClient.width, m_fButtonLen);
    179   return CFX_RectF(m_rtClient.TopLeft(), m_fButtonLen, m_rtClient.height);
    180 }
    181 
    182 CFX_RectF CFWL_ScrollBar::CalcMaxButtonRect() {
    183   if (IsVertical()) {
    184     return CFX_RectF(m_rtClient.left, m_rtClient.bottom() - m_fButtonLen,
    185                      m_rtClient.width, m_fButtonLen);
    186   }
    187   return CFX_RectF(m_rtClient.right() - m_fButtonLen, m_rtClient.top,
    188                    m_fButtonLen, m_rtClient.height);
    189 }
    190 
    191 CFX_RectF CFWL_ScrollBar::CalcThumbButtonRect(const CFX_RectF& rtThumb) {
    192   CFX_RectF rect;
    193   if (!IsEnabled())
    194     return rect;
    195 
    196   if (m_bMinSize) {
    197     rect.left = rtThumb.left;
    198     rect.top = rtThumb.top;
    199     return rect;
    200   }
    201 
    202   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
    203   if (fRange < 0) {
    204     if (IsVertical()) {
    205       return CFX_RectF(m_rtClient.left, m_rtMaxBtn.bottom(), m_rtClient.width,
    206                        0);
    207     }
    208     return CFX_RectF(m_rtMaxBtn.right(), m_rtClient.top, 0, m_rtClient.height);
    209   }
    210 
    211   CFX_RectF rtClient = m_rtClient;
    212   FX_FLOAT fLength = IsVertical() ? rtClient.height : rtClient.width;
    213   FX_FLOAT fSize = m_fButtonLen;
    214   fLength -= fSize * 2.0f;
    215   if (fLength < fSize)
    216     fLength = 0.0f;
    217 
    218   FX_FLOAT fThumbSize = fLength * fLength / (fRange + fLength);
    219   fThumbSize = std::max(fThumbSize, kMinThumbSize);
    220 
    221   FX_FLOAT fDiff = std::max(fLength - fThumbSize, 0.0f);
    222   FX_FLOAT fTrackPos =
    223       std::max(std::min(m_fTrackPos, m_fRangeMax), m_fRangeMin);
    224   if (!fRange)
    225     return rect;
    226 
    227   FX_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     FX_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   FX_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 FX_FLOAT CFWL_ScrollBar::GetTrackPointPos(const CFX_PointF& point) {
    278   CFX_PointF diff = point - m_cpTrackPoint;
    279   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
    280   FX_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 std::min(std::max(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, FX_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(CFX_Graphics* pGraphics,
    359                                   const CFX_Matrix* pMatrix) {
    360   DrawWidget(pGraphics, pMatrix);
    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);
    489 
    490   if (pButton->m_pTimerInfo)
    491     pButton->m_pTimerInfo->StopTimer();
    492 
    493   if (!pButton->SendEvent())
    494     pButton->m_pTimerInfo = StartTimer(0, true);
    495 }
    496