• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CFWL_ScrollBar(const CFWL_App * app,std::unique_ptr<CFWL_WidgetProperties> properties,CFWL_Widget * pOuter)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 
~CFWL_ScrollBar()62 CFWL_ScrollBar::~CFWL_ScrollBar() {}
63 
GetClassID() const64 FWL_Type CFWL_ScrollBar::GetClassID() const {
65   return FWL_Type::ScrollBar;
66 }
67 
Update()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 
DrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)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 
SetTrackPos(float fTrackPos)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 
DoScroll(CFWL_EventScroll::Code dwCode,float fPos)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 
DrawTrack(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,bool bLower,const CFX_Matrix * pMatrix)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 
DrawArrowBtn(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,bool bMinBtn,const CFX_Matrix * pMatrix)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 
DrawThumb(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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 
Layout()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 
CalcButtonLen()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 
CalcMinButtonRect()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 
CalcMaxButtonRect()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 
CalcThumbButtonRect(const CFX_RectF & rtThumb)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 
CalcMinTrackRect(const CFX_RectF & rtMinRect)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 
CalcMaxTrackRect(const CFX_RectF & rtMaxRect)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 
GetTrackPointPos(const CFX_PointF & point)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 
SendEvent()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 
OnScroll(CFWL_EventScroll::Code dwCode,float fPos)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 
OnProcessMessage(CFWL_Message * pMessage)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 
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)358 void CFWL_ScrollBar::OnDrawWidget(CXFA_Graphics* pGraphics,
359                                   const CFX_Matrix& matrix) {
360   DrawWidget(pGraphics, matrix);
361 }
362 
OnLButtonDown(const CFX_PointF & point)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 
OnLButtonUp(const CFX_PointF & point)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 
OnMouseMove(const CFX_PointF & point)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 
OnMouseLeave()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 
OnMouseWheel(const CFX_PointF & delta)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 
DoMouseDown(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,const CFX_PointF & point)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 
DoMouseUp(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,const CFX_PointF & point)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 
DoMouseMove(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,const CFX_PointF & point)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 
DoMouseLeave(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState)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 
DoMouseHover(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState)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 
Timer(CFWL_ScrollBar * pToolTip)485 CFWL_ScrollBar::Timer::Timer(CFWL_ScrollBar* pToolTip) : CFWL_Timer(pToolTip) {}
486 
Run(CFWL_TimerInfo * pTimerInfo)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