• 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 "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 
CFWL_ScrollBar(const CFWL_App * app,std::unique_ptr<CFWL_WidgetProperties> properties,CFWL_Widget * pOuter)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 
~CFWL_ScrollBar()61 CFWL_ScrollBar::~CFWL_ScrollBar() {}
62 
GetClassID() const63 FWL_Type CFWL_ScrollBar::GetClassID() const {
64   return FWL_Type::ScrollBar;
65 }
66 
Update()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 
DrawWidget(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)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 
SetTrackPos(FX_FLOAT fTrackPos)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 
DoScroll(CFWL_EventScroll::Code dwCode,FX_FLOAT fPos)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 
DrawTrack(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,bool bLower,const CFX_Matrix * pMatrix)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 
DrawArrowBtn(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,bool bMinBtn,const CFX_Matrix * pMatrix)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 
DrawThumb(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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 
Layout()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 
CalcButtonLen()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 
CalcMinButtonRect()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 
CalcMaxButtonRect()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 
CalcThumbButtonRect(const CFX_RectF & rtThumb)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 
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     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 
GetTrackPointPos(const CFX_PointF & point)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 
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,FX_FLOAT fPos)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 
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(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)358 void CFWL_ScrollBar::OnDrawWidget(CFX_Graphics* pGraphics,
359                                   const CFX_Matrix* pMatrix) {
360   DrawWidget(pGraphics, pMatrix);
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);
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