• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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/cxx17_backports.h"
14 #include "xfa/fwl/cfwl_app.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/ifwl_themeprovider.h"
21 
22 namespace {
23 
24 constexpr int kScrollbarElapsedMsecs = 500;
25 constexpr float kMinThumbSize = 5.0f;
26 
27 }  // namespace
28 
CFWL_ScrollBar(CFWL_App * app,const Properties & properties,CFWL_Widget * pOuter)29 CFWL_ScrollBar::CFWL_ScrollBar(CFWL_App* app,
30                                const Properties& properties,
31                                CFWL_Widget* pOuter)
32     : CFWL_Widget(app, properties, pOuter) {}
33 
34 CFWL_ScrollBar::~CFWL_ScrollBar() = default;
35 
GetClassID() const36 FWL_Type CFWL_ScrollBar::GetClassID() const {
37   return FWL_Type::ScrollBar;
38 }
39 
Update()40 void CFWL_ScrollBar::Update() {
41   if (IsLocked())
42     return;
43 
44   Layout();
45 }
46 
DrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)47 void CFWL_ScrollBar::DrawWidget(CFGAS_GEGraphics* pGraphics,
48                                 const CFX_Matrix& matrix) {
49   if (!pGraphics)
50     return;
51 
52   if (HasBorder())
53     DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
54 
55   DrawLowerTrack(pGraphics, matrix);
56   DrawUpperTrack(pGraphics, matrix);
57   DrawMinArrowBtn(pGraphics, matrix);
58   DrawMaxArrowBtn(pGraphics, matrix);
59   DrawThumb(pGraphics, matrix);
60 }
61 
SetTrackPos(float fTrackPos)62 void CFWL_ScrollBar::SetTrackPos(float fTrackPos) {
63   m_fTrackPos = fTrackPos;
64   m_ThumbRect = CalcThumbButtonRect(m_ThumbRect);
65   m_MinTrackRect = CalcMinTrackRect(m_MinTrackRect);
66   m_MaxTrackRect = CalcMaxTrackRect(m_MaxTrackRect);
67 }
68 
DoScroll(CFWL_EventScroll::Code dwCode,float fPos)69 bool CFWL_ScrollBar::DoScroll(CFWL_EventScroll::Code dwCode, float fPos) {
70   if (dwCode == CFWL_EventScroll::Code::None)
71     return false;
72   return OnScroll(dwCode, fPos);
73 }
74 
DrawUpperTrack(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)75 void CFWL_ScrollBar::DrawUpperTrack(CFGAS_GEGraphics* pGraphics,
76                                     const CFX_Matrix& mtMatrix) {
77   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kUpperTrack, this,
78                              pGraphics);
79   param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
80                          ? CFWL_PartState::kDisabled
81                          : m_iMaxTrackState;
82   param.m_matrix = mtMatrix;
83   param.m_PartRect = m_MaxTrackRect;
84   GetThemeProvider()->DrawBackground(param);
85 }
86 
DrawLowerTrack(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)87 void CFWL_ScrollBar::DrawLowerTrack(CFGAS_GEGraphics* pGraphics,
88                                     const CFX_Matrix& mtMatrix) {
89   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kLowerTrack, this,
90                              pGraphics);
91   param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
92                          ? CFWL_PartState::kDisabled
93                          : m_iMinTrackState;
94   param.m_matrix = mtMatrix;
95   param.m_PartRect = m_MinTrackRect;
96   GetThemeProvider()->DrawBackground(param);
97 }
98 
DrawMaxArrowBtn(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)99 void CFWL_ScrollBar::DrawMaxArrowBtn(CFGAS_GEGraphics* pGraphics,
100                                      const CFX_Matrix& mtMatrix) {
101   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackArrow, this, pGraphics);
102   param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
103                          ? CFWL_PartState::kDisabled
104                          : m_iMaxButtonState;
105   param.m_matrix = mtMatrix;
106   param.m_PartRect = m_MaxBtnRect;
107   if (param.m_PartRect.height > 0 && param.m_PartRect.width > 0)
108     GetThemeProvider()->DrawBackground(param);
109 }
110 
DrawMinArrowBtn(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)111 void CFWL_ScrollBar::DrawMinArrowBtn(CFGAS_GEGraphics* pGraphics,
112                                      const CFX_Matrix& mtMatrix) {
113   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kForeArrow, this, pGraphics);
114   param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
115                          ? CFWL_PartState::kDisabled
116                          : m_iMinButtonState;
117   param.m_matrix = mtMatrix;
118   param.m_PartRect = m_MinBtnRect;
119   if (param.m_PartRect.height > 0 && param.m_PartRect.width > 0)
120     GetThemeProvider()->DrawBackground(param);
121 }
122 
DrawThumb(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & mtMatrix)123 void CFWL_ScrollBar::DrawThumb(CFGAS_GEGraphics* pGraphics,
124                                const CFX_Matrix& mtMatrix) {
125   CFWL_ThemeBackground param(CFWL_ThemePart::Part::kThumb, this, pGraphics);
126   param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
127                          ? CFWL_PartState::kDisabled
128                          : m_iThumbButtonState;
129   param.m_matrix = mtMatrix;
130   param.m_PartRect = m_ThumbRect;
131   GetThemeProvider()->DrawBackground(param);
132 }
133 
Layout()134 void CFWL_ScrollBar::Layout() {
135   m_ClientRect = GetClientRect();
136 
137   CalcButtonLen();
138   m_MinBtnRect = CalcMinButtonRect();
139   m_MaxBtnRect = CalcMaxButtonRect();
140   m_ThumbRect = CalcThumbButtonRect(m_ThumbRect);
141   m_MinTrackRect = CalcMinTrackRect(m_MinTrackRect);
142   m_MaxTrackRect = CalcMaxTrackRect(m_MaxTrackRect);
143 }
144 
CalcButtonLen()145 void CFWL_ScrollBar::CalcButtonLen() {
146   m_fButtonLen = IsVertical() ? m_ClientRect.width : m_ClientRect.height;
147   float fLength = IsVertical() ? m_ClientRect.height : m_ClientRect.width;
148   if (fLength < m_fButtonLen * 2) {
149     m_fButtonLen = fLength / 2;
150     m_bMinSize = true;
151   } else {
152     m_bMinSize = false;
153   }
154 }
155 
CalcMinButtonRect()156 CFX_RectF CFWL_ScrollBar::CalcMinButtonRect() {
157   if (IsVertical())
158     return CFX_RectF(m_ClientRect.TopLeft(), m_ClientRect.width, m_fButtonLen);
159   return CFX_RectF(m_ClientRect.TopLeft(), m_fButtonLen, m_ClientRect.height);
160 }
161 
CalcMaxButtonRect()162 CFX_RectF CFWL_ScrollBar::CalcMaxButtonRect() {
163   if (IsVertical()) {
164     return CFX_RectF(m_ClientRect.left, m_ClientRect.bottom() - m_fButtonLen,
165                      m_ClientRect.width, m_fButtonLen);
166   }
167   return CFX_RectF(m_ClientRect.right() - m_fButtonLen, m_ClientRect.top,
168                    m_fButtonLen, m_ClientRect.height);
169 }
170 
CalcThumbButtonRect(const CFX_RectF & rtThumb)171 CFX_RectF CFWL_ScrollBar::CalcThumbButtonRect(const CFX_RectF& rtThumb) {
172   CFX_RectF rect;
173   if (!IsEnabled())
174     return rect;
175 
176   if (m_bMinSize) {
177     rect.left = rtThumb.left;
178     rect.top = rtThumb.top;
179     return rect;
180   }
181 
182   float fRange = m_fRangeMax - m_fRangeMin;
183   if (fRange < 0) {
184     if (IsVertical()) {
185       return CFX_RectF(m_ClientRect.left, m_MaxBtnRect.bottom(),
186                        m_ClientRect.width, 0);
187     }
188     return CFX_RectF(m_MaxBtnRect.right(), m_ClientRect.top, 0,
189                      m_ClientRect.height);
190   }
191 
192   CFX_RectF rtClient = m_ClientRect;
193   float fLength = IsVertical() ? rtClient.height : rtClient.width;
194   float fSize = m_fButtonLen;
195   fLength -= fSize * 2.0f;
196   if (fLength < fSize)
197     fLength = 0.0f;
198 
199   float fThumbSize = fLength * fLength / (fRange + fLength);
200   fThumbSize = std::max(fThumbSize, kMinThumbSize);
201 
202   float fDiff = std::max(fLength - fThumbSize, 0.0f);
203   float fTrackPos = pdfium::clamp(m_fTrackPos, m_fRangeMin, m_fRangeMax);
204   if (!fRange)
205     return rect;
206 
207   float iPos = fSize + fDiff * (fTrackPos - m_fRangeMin) / fRange;
208   rect.left = rtClient.left;
209   rect.top = rtClient.top;
210   if (IsVertical()) {
211     rect.top += iPos;
212     rect.width = rtClient.width;
213     rect.height = fThumbSize;
214   } else {
215     rect.left += iPos;
216     rect.width = fThumbSize;
217     rect.height = rtClient.height;
218   }
219   return rect;
220 }
221 
CalcMinTrackRect(const CFX_RectF & rtMinRect)222 CFX_RectF CFWL_ScrollBar::CalcMinTrackRect(const CFX_RectF& rtMinRect) {
223   CFX_RectF rect;
224   if (m_bMinSize) {
225     rect.left = rtMinRect.left;
226     rect.top = rtMinRect.top;
227     return rect;
228   }
229 
230   rect.left = m_ClientRect.left;
231   rect.top = m_ClientRect.top;
232   if (IsVertical()) {
233     rect.width = m_ClientRect.width;
234     rect.height = (m_ThumbRect.top + m_ThumbRect.bottom()) / 2;
235   } else {
236     rect.width = (m_ThumbRect.left + m_ThumbRect.right()) / 2;
237     rect.height = m_ClientRect.height;
238   }
239   return rect;
240 }
241 
CalcMaxTrackRect(const CFX_RectF & rtMaxRect)242 CFX_RectF CFWL_ScrollBar::CalcMaxTrackRect(const CFX_RectF& rtMaxRect) {
243   if (m_bMinSize)
244     return CFX_RectF(rtMaxRect.TopLeft(), 0, 0);
245 
246   if (IsVertical()) {
247     float iy = (m_ThumbRect.top + m_ThumbRect.bottom()) / 2;
248     return CFX_RectF(m_ClientRect.left, iy, m_ClientRect.width,
249                      m_ClientRect.bottom() - iy);
250   }
251 
252   float ix = (m_ThumbRect.left + m_ThumbRect.right()) / 2;
253   return CFX_RectF(ix, m_ClientRect.top, m_ClientRect.height - ix,
254                    m_ClientRect.height);
255 }
256 
GetTrackPointPos(const CFX_PointF & point)257 float CFWL_ScrollBar::GetTrackPointPos(const CFX_PointF& point) {
258   CFX_PointF diff = point - m_cpTrackPoint;
259   float fRange = m_fRangeMax - m_fRangeMin;
260   float fPos;
261 
262   if (IsVertical()) {
263     fPos = fRange * diff.y /
264            (m_MaxBtnRect.top - m_MinBtnRect.bottom() - m_ThumbRect.height);
265   } else {
266     fPos = fRange * diff.x /
267            (m_MaxBtnRect.left - m_MinBtnRect.right() - m_ThumbRect.width);
268   }
269 
270   fPos += m_fLastTrackPos;
271   return pdfium::clamp(fPos, m_fRangeMin, m_fRangeMax);
272 }
273 
SendEvent()274 bool CFWL_ScrollBar::SendEvent() {
275   if (m_iMinButtonState == CFWL_PartState::kPressed) {
276     DoScroll(CFWL_EventScroll::Code::StepBackward, m_fTrackPos);
277     return false;
278   }
279   if (m_iMaxButtonState == CFWL_PartState::kPressed) {
280     DoScroll(CFWL_EventScroll::Code::StepForward, m_fTrackPos);
281     return false;
282   }
283   if (m_iMinTrackState == CFWL_PartState::kPressed) {
284     DoScroll(CFWL_EventScroll::Code::PageBackward, m_fTrackPos);
285     return m_ThumbRect.Contains(m_cpTrackPoint);
286   }
287   if (m_iMaxTrackState == CFWL_PartState::kPressed) {
288     DoScroll(CFWL_EventScroll::Code::PageForward, m_fTrackPos);
289     return m_ThumbRect.Contains(m_cpTrackPoint);
290   }
291   if (m_iMouseWheel) {
292     CFWL_EventScroll::Code dwCode = m_iMouseWheel < 0
293                                         ? CFWL_EventScroll::Code::StepForward
294                                         : CFWL_EventScroll::Code::StepBackward;
295     DoScroll(dwCode, m_fTrackPos);
296   }
297   return true;
298 }
299 
OnScroll(CFWL_EventScroll::Code dwCode,float fPos)300 bool CFWL_ScrollBar::OnScroll(CFWL_EventScroll::Code dwCode, float fPos) {
301   CFWL_EventScroll ev(this, dwCode, fPos);
302   DispatchEvent(&ev);
303   return true;
304 }
305 
OnProcessMessage(CFWL_Message * pMessage)306 void CFWL_ScrollBar::OnProcessMessage(CFWL_Message* pMessage) {
307   CFWL_Message::Type type = pMessage->GetType();
308   if (type == CFWL_Message::Type::kMouse) {
309     CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
310     switch (pMsg->m_dwCmd) {
311       case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
312         OnLButtonDown(pMsg->m_pos);
313         break;
314       case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
315         OnLButtonUp(pMsg->m_pos);
316         break;
317       case CFWL_MessageMouse::MouseCommand::kMove:
318         OnMouseMove(pMsg->m_pos);
319         break;
320       case CFWL_MessageMouse::MouseCommand::kLeave:
321         OnMouseLeave();
322         break;
323       default:
324         break;
325     }
326   } else if (type == CFWL_Message::Type::kMouseWheel) {
327     auto* pMsg = static_cast<CFWL_MessageMouseWheel*>(pMessage);
328     OnMouseWheel(pMsg->delta());
329   }
330 }
331 
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)332 void CFWL_ScrollBar::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
333                                   const CFX_Matrix& matrix) {
334   DrawWidget(pGraphics, matrix);
335 }
336 
OnLButtonDown(const CFX_PointF & point)337 void CFWL_ScrollBar::OnLButtonDown(const CFX_PointF& point) {
338   if (!IsEnabled())
339     return;
340 
341   m_bMouseDown = true;
342   SetGrab(true);
343 
344   m_cpTrackPoint = point;
345   m_fLastTrackPos = m_fTrackPos;
346   if (m_MinBtnRect.Contains(point))
347     DoMouseDown(0, m_MinBtnRect, &m_iMinButtonState, point);
348   else if (m_ThumbRect.Contains(point))
349     DoMouseDown(1, m_ThumbRect, &m_iThumbButtonState, point);
350   else if (m_MaxBtnRect.Contains(point))
351     DoMouseDown(2, m_MaxBtnRect, &m_iMaxButtonState, point);
352   else if (m_MinTrackRect.Contains(point))
353     DoMouseDown(3, m_MinTrackRect, &m_iMinTrackState, point);
354   else
355     DoMouseDown(4, m_MaxTrackRect, &m_iMaxTrackState, point);
356 
357   if (!SendEvent()) {
358     m_pTimer = std::make_unique<CFX_Timer>(GetFWLApp()->GetTimerHandler(), this,
359                                            kScrollbarElapsedMsecs);
360   }
361 }
362 
OnLButtonUp(const CFX_PointF & point)363 void CFWL_ScrollBar::OnLButtonUp(const CFX_PointF& point) {
364   m_pTimer.reset();
365   m_bMouseDown = false;
366   DoMouseUp(0, m_MinBtnRect, &m_iMinButtonState, point);
367   DoMouseUp(1, m_ThumbRect, &m_iThumbButtonState, point);
368   DoMouseUp(2, m_MaxBtnRect, &m_iMaxButtonState, point);
369   DoMouseUp(3, m_MinTrackRect, &m_iMinTrackState, point);
370   DoMouseUp(4, m_MaxTrackRect, &m_iMaxTrackState, point);
371   SetGrab(false);
372 }
373 
OnMouseMove(const CFX_PointF & point)374 void CFWL_ScrollBar::OnMouseMove(const CFX_PointF& point) {
375   DoMouseMove(0, m_MinBtnRect, &m_iMinButtonState, point);
376   DoMouseMove(1, m_ThumbRect, &m_iThumbButtonState, point);
377   DoMouseMove(2, m_MaxBtnRect, &m_iMaxButtonState, point);
378   DoMouseMove(3, m_MinTrackRect, &m_iMinTrackState, point);
379   DoMouseMove(4, m_MaxTrackRect, &m_iMaxTrackState, point);
380 }
381 
OnMouseLeave()382 void CFWL_ScrollBar::OnMouseLeave() {
383   DoMouseLeave(0, m_MinBtnRect, &m_iMinButtonState);
384   DoMouseLeave(1, m_ThumbRect, &m_iThumbButtonState);
385   DoMouseLeave(2, m_MaxBtnRect, &m_iMaxButtonState);
386   DoMouseLeave(3, m_MinTrackRect, &m_iMinTrackState);
387   DoMouseLeave(4, m_MaxTrackRect, &m_iMaxTrackState);
388 }
389 
OnMouseWheel(const CFX_Vector & delta)390 void CFWL_ScrollBar::OnMouseWheel(const CFX_Vector& delta) {
391   m_iMouseWheel = delta.y;
392   SendEvent();
393   m_iMouseWheel = 0;
394 }
395 
DoMouseDown(int32_t iItem,const CFX_RectF & rtItem,CFWL_PartState * pState,const CFX_PointF & point)396 void CFWL_ScrollBar::DoMouseDown(int32_t iItem,
397                                  const CFX_RectF& rtItem,
398                                  CFWL_PartState* pState,
399                                  const CFX_PointF& point) {
400   if (!rtItem.Contains(point))
401     return;
402   if (*pState == CFWL_PartState::kPressed)
403     return;
404 
405   *pState = CFWL_PartState::kPressed;
406   RepaintRect(rtItem);
407 }
408 
DoMouseUp(int32_t iItem,const CFX_RectF & rtItem,CFWL_PartState * pState,const CFX_PointF & point)409 void CFWL_ScrollBar::DoMouseUp(int32_t iItem,
410                                const CFX_RectF& rtItem,
411                                CFWL_PartState* pState,
412                                const CFX_PointF& point) {
413   CFWL_PartState iNewState = rtItem.Contains(point) ? CFWL_PartState::kHovered
414                                                     : CFWL_PartState::kNormal;
415   if (*pState == iNewState)
416     return;
417 
418   *pState = iNewState;
419   RepaintRect(rtItem);
420   OnScroll(CFWL_EventScroll::Code::EndScroll, m_fTrackPos);
421 }
422 
DoMouseMove(int32_t iItem,const CFX_RectF & rtItem,CFWL_PartState * pState,const CFX_PointF & point)423 void CFWL_ScrollBar::DoMouseMove(int32_t iItem,
424                                  const CFX_RectF& rtItem,
425                                  CFWL_PartState* pState,
426                                  const CFX_PointF& point) {
427   if (!m_bMouseDown) {
428     CFWL_PartState iNewState = rtItem.Contains(point) ? CFWL_PartState::kHovered
429                                                       : CFWL_PartState::kNormal;
430     if (*pState == iNewState)
431       return;
432 
433     *pState = iNewState;
434     RepaintRect(rtItem);
435   } else if ((2 == iItem) &&
436              (m_iThumbButtonState == CFWL_PartState::kPressed)) {
437     m_fTrackPos = GetTrackPointPos(point);
438     OnScroll(CFWL_EventScroll::Code::TrackPos, m_fTrackPos);
439   }
440 }
441 
DoMouseLeave(int32_t iItem,const CFX_RectF & rtItem,CFWL_PartState * pState)442 void CFWL_ScrollBar::DoMouseLeave(int32_t iItem,
443                                   const CFX_RectF& rtItem,
444                                   CFWL_PartState* pState) {
445   if (*pState == CFWL_PartState::kNormal)
446     return;
447 
448   *pState = CFWL_PartState::kNormal;
449   RepaintRect(rtItem);
450 }
451 
DoMouseHover(int32_t iItem,const CFX_RectF & rtItem,CFWL_PartState * pState)452 void CFWL_ScrollBar::DoMouseHover(int32_t iItem,
453                                   const CFX_RectF& rtItem,
454                                   CFWL_PartState* pState) {
455   if (*pState == CFWL_PartState::kHovered)
456     return;
457 
458   *pState = CFWL_PartState::kHovered;
459   RepaintRect(rtItem);
460 }
461 
OnTimerFired()462 void CFWL_ScrollBar::OnTimerFired() {
463   m_pTimer.reset();
464   if (!SendEvent()) {
465     m_pTimer =
466         std::make_unique<CFX_Timer>(GetFWLApp()->GetTimerHandler(), this, 0);
467   }
468 }
469