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/src/foxitlib.h"
8 #include "xfa/src/fwl/src/core/include/fwl_targetimp.h"
9 #include "xfa/src/fwl/src/core/include/fwl_noteimp.h"
10 #include "xfa/src/fwl/src/core/include/fwl_widgetimp.h"
11 #include "xfa/src/fwl/src/basewidget/include/fwl_scrollbarimp.h"
12 #define FWL_SCROLLBAR_Elapse 500
13 #define FWL_SCROLLBAR_MinThumb 5
14 
15 // static
Create(const CFWL_WidgetImpProperties & properties,IFWL_Widget * pOuter)16 IFWL_ScrollBar* IFWL_ScrollBar::Create(
17     const CFWL_WidgetImpProperties& properties,
18     IFWL_Widget* pOuter) {
19   IFWL_ScrollBar* pScrollBar = new IFWL_ScrollBar;
20   CFWL_ScrollBarImp* pScrollBarImpl = new CFWL_ScrollBarImp(properties, pOuter);
21   pScrollBar->SetImpl(pScrollBarImpl);
22   pScrollBarImpl->SetInterface(pScrollBar);
23   return pScrollBar;
24 }
IFWL_ScrollBar()25 IFWL_ScrollBar::IFWL_ScrollBar() {}
IsVertical()26 FX_BOOL IFWL_ScrollBar::IsVertical() {
27   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->IsVertical();
28 }
GetRange(FX_FLOAT & fMin,FX_FLOAT & fMax)29 FWL_ERR IFWL_ScrollBar::GetRange(FX_FLOAT& fMin, FX_FLOAT& fMax) {
30   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetRange(fMin, fMax);
31 }
SetRange(FX_FLOAT fMin,FX_FLOAT fMax)32 FWL_ERR IFWL_ScrollBar::SetRange(FX_FLOAT fMin, FX_FLOAT fMax) {
33   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetRange(fMin, fMax);
34 }
GetPageSize()35 FX_FLOAT IFWL_ScrollBar::GetPageSize() {
36   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetPageSize();
37 }
SetPageSize(FX_FLOAT fPageSize)38 FWL_ERR IFWL_ScrollBar::SetPageSize(FX_FLOAT fPageSize) {
39   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetPageSize(fPageSize);
40 }
GetStepSize()41 FX_FLOAT IFWL_ScrollBar::GetStepSize() {
42   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetStepSize();
43 }
SetStepSize(FX_FLOAT fStepSize)44 FWL_ERR IFWL_ScrollBar::SetStepSize(FX_FLOAT fStepSize) {
45   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetStepSize(fStepSize);
46 }
GetPos()47 FX_FLOAT IFWL_ScrollBar::GetPos() {
48   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetPos();
49 }
SetPos(FX_FLOAT fPos)50 FWL_ERR IFWL_ScrollBar::SetPos(FX_FLOAT fPos) {
51   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetPos(fPos);
52 }
GetTrackPos()53 FX_FLOAT IFWL_ScrollBar::GetTrackPos() {
54   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetTrackPos();
55 }
SetTrackPos(FX_FLOAT fTrackPos)56 FWL_ERR IFWL_ScrollBar::SetTrackPos(FX_FLOAT fTrackPos) {
57   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetTrackPos(fTrackPos);
58 }
DoScroll(FX_DWORD dwCode,FX_FLOAT fPos)59 FX_BOOL IFWL_ScrollBar::DoScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
60   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->DoScroll(dwCode, fPos);
61 }
CFWL_ScrollBarImp(const CFWL_WidgetImpProperties & properties,IFWL_Widget * pOuter)62 CFWL_ScrollBarImp::CFWL_ScrollBarImp(const CFWL_WidgetImpProperties& properties,
63                                      IFWL_Widget* pOuter)
64     : CFWL_WidgetImp(properties, pOuter),
65       m_hTimer(nullptr),
66       m_fRangeMin(0),
67       m_fRangeMax(-1),
68       m_fPageSize(0),
69       m_fStepSize(0),
70       m_fPos(0),
71       m_fTrackPos(0),
72       m_iMinButtonState(FWL_PARTSTATE_SCB_Normal),
73       m_iMaxButtonState(FWL_PARTSTATE_SCB_Normal),
74       m_iThumbButtonState(FWL_PARTSTATE_SCB_Normal),
75       m_iMinTrackState(FWL_PARTSTATE_SCB_Normal),
76       m_iMaxTrackState(FWL_PARTSTATE_SCB_Normal),
77       m_fLastTrackPos(0),
78       m_cpTrackPointX(0),
79       m_cpTrackPointY(0),
80       m_iMouseWheel(0),
81       m_bTrackMouseLeave(FALSE),
82       m_bMouseHover(FALSE),
83       m_bMouseDown(FALSE),
84       m_bRepaintThumb(FALSE),
85       m_fButtonLen(0),
86       m_bMinSize(FALSE),
87       m_bCustomLayout(FALSE),
88       m_fMinThumb(FWL_SCROLLBAR_MinThumb) {
89   m_rtClient.Reset();
90   m_rtThumb.Reset();
91   m_rtMinBtn.Reset();
92   m_rtMaxBtn.Reset();
93   m_rtMinTrack.Reset();
94   m_rtMaxTrack.Reset();
95 }
~CFWL_ScrollBarImp()96 CFWL_ScrollBarImp::~CFWL_ScrollBarImp() {}
GetClassName(CFX_WideString & wsClass) const97 FWL_ERR CFWL_ScrollBarImp::GetClassName(CFX_WideString& wsClass) const {
98   wsClass = FWL_CLASS_ScrollBar;
99   return FWL_ERR_Succeeded;
100 }
GetClassID() const101 FX_DWORD CFWL_ScrollBarImp::GetClassID() const {
102   return FWL_CLASSHASH_ScrollBar;
103 }
Initialize()104 FWL_ERR CFWL_ScrollBarImp::Initialize() {
105   if (CFWL_WidgetImp::Initialize() != FWL_ERR_Succeeded)
106     return FWL_ERR_Indefinite;
107   m_pDelegate = new CFWL_ScrollBarImpDelegate(this);
108   return FWL_ERR_Succeeded;
109 }
Finalize()110 FWL_ERR CFWL_ScrollBarImp::Finalize() {
111   delete m_pDelegate;
112   m_pDelegate = nullptr;
113   return CFWL_WidgetImp::Finalize();
114 }
GetWidgetRect(CFX_RectF & rect,FX_BOOL bAutoSize)115 FWL_ERR CFWL_ScrollBarImp::GetWidgetRect(CFX_RectF& rect, FX_BOOL bAutoSize) {
116   if (bAutoSize) {
117     rect.Set(0, 0, 0, 0);
118     FX_FLOAT* pfMinWidth = static_cast<FX_FLOAT*>(
119         GetThemeCapacity(FWL_WGTCAPACITY_ScrollBarWidth));
120     if (!pfMinWidth)
121       return FWL_ERR_Indefinite;
122     if (IsVertical()) {
123       rect.Set(0, 0, (*pfMinWidth), (*pfMinWidth) * 3);
124     } else {
125       rect.Set(0, 0, (*pfMinWidth) * 3, (*pfMinWidth));
126     }
127     CFWL_WidgetImp::GetWidgetRect(rect, TRUE);
128   } else {
129     rect = m_pProperties->m_rtWidget;
130   }
131   return FWL_ERR_Succeeded;
132 }
Update()133 FWL_ERR CFWL_ScrollBarImp::Update() {
134   if (IsLocked()) {
135     return FWL_ERR_Indefinite;
136   }
137   if (!m_pProperties->m_pThemeProvider) {
138     m_pProperties->m_pThemeProvider = GetAvailableTheme();
139   }
140   Layout();
141   return FWL_ERR_Succeeded;
142 }
DrawWidget(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)143 FWL_ERR CFWL_ScrollBarImp::DrawWidget(CFX_Graphics* pGraphics,
144                                       const CFX_Matrix* pMatrix) {
145   if (!pGraphics)
146     return FWL_ERR_Indefinite;
147   if (!m_pProperties->m_pThemeProvider)
148     return FWL_ERR_Indefinite;
149   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
150   if (HasBorder()) {
151     DrawBorder(pGraphics, FWL_PART_SCB_Border, pTheme, pMatrix);
152   }
153   if (HasEdge()) {
154     DrawEdge(pGraphics, FWL_PART_SCB_Edge, pTheme, pMatrix);
155   }
156   DrawTrack(pGraphics, pTheme, TRUE, pMatrix);
157   DrawTrack(pGraphics, pTheme, FALSE, pMatrix);
158   DrawArrowBtn(pGraphics, pTheme, TRUE, pMatrix);
159   DrawArrowBtn(pGraphics, pTheme, FALSE, pMatrix);
160   DrawThumb(pGraphics, pTheme, pMatrix);
161   return FWL_ERR_Succeeded;
162 }
IsVertical()163 inline FX_BOOL CFWL_ScrollBarImp::IsVertical() {
164   return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_SCB_Vert;
165 }
GetRange(FX_FLOAT & fMin,FX_FLOAT & fMax)166 FWL_ERR CFWL_ScrollBarImp::GetRange(FX_FLOAT& fMin, FX_FLOAT& fMax) {
167   fMin = m_fRangeMin;
168   fMax = m_fRangeMax;
169   return FWL_ERR_Succeeded;
170 }
SetRange(FX_FLOAT fMin,FX_FLOAT fMax)171 FWL_ERR CFWL_ScrollBarImp::SetRange(FX_FLOAT fMin, FX_FLOAT fMax) {
172   m_fRangeMin = fMin;
173   m_fRangeMax = fMax;
174   return FWL_ERR_Succeeded;
175 }
GetPageSize()176 FX_FLOAT CFWL_ScrollBarImp::GetPageSize() {
177   return m_fPageSize;
178 }
SetPageSize(FX_FLOAT fPageSize)179 FWL_ERR CFWL_ScrollBarImp::SetPageSize(FX_FLOAT fPageSize) {
180   m_fPageSize = fPageSize;
181   return FWL_ERR_Succeeded;
182 }
GetStepSize()183 FX_FLOAT CFWL_ScrollBarImp::GetStepSize() {
184   return m_fStepSize;
185 }
SetStepSize(FX_FLOAT fStepSize)186 FWL_ERR CFWL_ScrollBarImp::SetStepSize(FX_FLOAT fStepSize) {
187   m_fStepSize = fStepSize;
188   return FWL_ERR_Succeeded;
189 }
GetPos()190 FX_FLOAT CFWL_ScrollBarImp::GetPos() {
191   return m_fPos;
192 }
SetPos(FX_FLOAT fPos)193 FWL_ERR CFWL_ScrollBarImp::SetPos(FX_FLOAT fPos) {
194   m_fPos = fPos;
195   return FWL_ERR_Succeeded;
196 }
GetTrackPos()197 FX_FLOAT CFWL_ScrollBarImp::GetTrackPos() {
198   return m_fTrackPos;
199 }
SetTrackPos(FX_FLOAT fTrackPos)200 FWL_ERR CFWL_ScrollBarImp::SetTrackPos(FX_FLOAT fTrackPos) {
201   m_fTrackPos = fTrackPos;
202   CalcThumbButtonRect(m_rtThumb);
203   CalcMinTrackRect(m_rtMinTrack);
204   CalcMaxTrackRect(m_rtMaxTrack);
205   return FWL_ERR_Succeeded;
206 }
DoScroll(FX_DWORD dwCode,FX_FLOAT fPos)207 FX_BOOL CFWL_ScrollBarImp::DoScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
208   switch (dwCode) {
209     case FWL_SCBCODE_Min:
210     case FWL_SCBCODE_Max:
211     case FWL_SCBCODE_PageBackward:
212     case FWL_SCBCODE_PageForward:
213     case FWL_SCBCODE_StepBackward:
214       break;
215     case FWL_SCBCODE_StepForward:
216       break;
217     case FWL_SCBCODE_Pos:
218     case FWL_SCBCODE_TrackPos:
219     case FWL_SCBCODE_EndScroll:
220       break;
221     default: { return FALSE; }
222   }
223   return OnScroll(dwCode, fPos);
224 }
Run(FWL_HTIMER hTimer)225 int32_t CFWL_ScrollBarImp::Run(FWL_HTIMER hTimer) {
226   if (m_hTimer) {
227     FWL_StopTimer(m_hTimer);
228   }
229   if (!SendEvent()) {
230     m_hTimer = FWL_StartTimer(this, 0);
231   }
232   return 1;
233 }
SetOuter(IFWL_Widget * pOuter)234 FWL_ERR CFWL_ScrollBarImp::SetOuter(IFWL_Widget* pOuter) {
235   m_pOuter = pOuter;
236   return FWL_ERR_Succeeded;
237 }
DrawTrack(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,FX_BOOL bLower,const CFX_Matrix * pMatrix)238 void CFWL_ScrollBarImp::DrawTrack(CFX_Graphics* pGraphics,
239                                   IFWL_ThemeProvider* pTheme,
240                                   FX_BOOL bLower,
241                                   const CFX_Matrix* pMatrix) {
242   CFWL_ThemeBackground param;
243   param.m_pWidget = m_pInterface;
244   param.m_iPart = bLower ? FWL_PART_SCB_LowerTrack : FWL_PART_SCB_UpperTrack;
245   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
246                          ? FWL_PARTSTATE_SCB_Disabled
247                          : (bLower ? m_iMinTrackState : m_iMaxTrackState);
248   param.m_pGraphics = pGraphics;
249   param.m_matrix.Concat(*pMatrix);
250   param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
251   pTheme->DrawBackground(&param);
252 }
DrawArrowBtn(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,FX_BOOL bMinBtn,const CFX_Matrix * pMatrix)253 void CFWL_ScrollBarImp::DrawArrowBtn(CFX_Graphics* pGraphics,
254                                      IFWL_ThemeProvider* pTheme,
255                                      FX_BOOL bMinBtn,
256                                      const CFX_Matrix* pMatrix) {
257   CFWL_ThemeBackground param;
258   param.m_pWidget = m_pInterface;
259   param.m_iPart = bMinBtn ? FWL_PART_SCB_ForeArrow : FWL_PART_SCB_BackArrow;
260   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
261                          ? FWL_PARTSTATE_SCB_Disabled
262                          : (bMinBtn ? m_iMinButtonState : m_iMaxButtonState);
263   param.m_pGraphics = pGraphics;
264   param.m_matrix.Concat(*pMatrix);
265   param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
266   if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0) {
267     pTheme->DrawBackground(&param);
268   }
269 }
DrawThumb(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)270 void CFWL_ScrollBarImp::DrawThumb(CFX_Graphics* pGraphics,
271                                   IFWL_ThemeProvider* pTheme,
272                                   const CFX_Matrix* pMatrix) {
273   if (!IsEnabled()) {
274   }
275   CFWL_ThemeBackground param;
276   param.m_pWidget = m_pInterface;
277   param.m_iPart = FWL_PART_SCB_Thumb;
278   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
279                          ? FWL_PARTSTATE_SCB_Disabled
280                          : m_iThumbButtonState;
281   param.m_pGraphics = pGraphics;
282   param.m_matrix.Concat(*pMatrix);
283   param.m_rtPart = m_rtThumb;
284   pTheme->DrawBackground(&param);
285 }
Layout()286 void CFWL_ScrollBarImp::Layout() {
287   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
288   CFWL_ThemePart part;
289   part.m_pWidget = m_pInterface;
290   m_fMinThumb = *static_cast<FX_FLOAT*>(
291       pTheme->GetCapacity(&part, FWL_CAPACITY_SCB_Size));
292   m_bCustomLayout = pTheme->IsCustomizedLayout(m_pInterface);
293   GetClientRect(m_rtClient);
294   CalcButtonLen();
295   CalcMinButtonRect(m_rtMinBtn);
296   CalcMaxButtonRect(m_rtMaxBtn);
297   CalcThumbButtonRect(m_rtThumb);
298   CalcMinTrackRect(m_rtMinTrack);
299   CalcMaxTrackRect(m_rtMaxTrack);
300 }
CalcButtonLen()301 void CFWL_ScrollBarImp::CalcButtonLen() {
302   m_fButtonLen = IsVertical() ? m_rtClient.width : m_rtClient.height;
303   FX_FLOAT fLength = IsVertical() ? m_rtClient.height : m_rtClient.width;
304   if (fLength < m_fButtonLen * 2) {
305     m_fButtonLen = fLength / 2;
306     m_bMinSize = TRUE;
307   } else {
308     m_bMinSize = FALSE;
309   }
310 }
CalcMinButtonRect(CFX_RectF & rect)311 void CFWL_ScrollBarImp::CalcMinButtonRect(CFX_RectF& rect) {
312   if (m_bCustomLayout) {
313     IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
314     CFWL_ThemePart pPart;
315     pPart.m_rtPart = m_rtMinBtn;
316     pPart.m_pWidget = m_pInterface;
317     pPart.m_iPart = FWL_PART_SCB_ForeArrow;
318     pPart.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
319                            ? FWL_PARTSTATE_SCB_Disabled
320                            : m_iMinButtonState;
321     pTheme->GetPartRect(&pPart, rect);
322   } else {
323     rect.left = m_rtClient.left;
324     rect.top = m_rtClient.top;
325     rect.width = IsVertical() ? m_rtClient.width : m_fButtonLen;
326     rect.height = IsVertical() ? m_fButtonLen : m_rtClient.height;
327   }
328 }
CalcMaxButtonRect(CFX_RectF & rect)329 void CFWL_ScrollBarImp::CalcMaxButtonRect(CFX_RectF& rect) {
330   if (m_bCustomLayout) {
331     IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
332     CFWL_ThemePart pPart;
333     pPart.m_rtPart = m_rtMaxBtn;
334     pPart.m_pWidget = m_pInterface;
335     pPart.m_iPart = FWL_PART_SCB_BackArrow;
336     pPart.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
337                            ? FWL_PARTSTATE_SCB_Disabled
338                            : m_iMaxButtonState;
339     pTheme->GetPartRect(&pPart, rect);
340   } else {
341     rect.left =
342         IsVertical() ? m_rtClient.left : m_rtClient.right() - m_fButtonLen;
343     rect.top =
344         IsVertical() ? m_rtClient.bottom() - m_fButtonLen : m_rtClient.top;
345     rect.width = IsVertical() ? m_rtClient.width : m_fButtonLen;
346     rect.height = IsVertical() ? m_fButtonLen : m_rtClient.height;
347   }
348 }
CalcThumbButtonRect(CFX_RectF & rect)349 void CFWL_ScrollBarImp::CalcThumbButtonRect(CFX_RectF& rect) {
350   if (!IsEnabled()) {
351     m_rtThumb.Reset();
352     return;
353   }
354   if (m_bMinSize) {
355     m_rtThumb.Empty();
356     return;
357   }
358   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
359   memset(&rect, 0, sizeof(CFX_Rect));
360   if (fRange < 0) {
361     if (IsVertical()) {
362       rect.Set(m_rtClient.left, m_rtMaxBtn.bottom(), m_rtClient.width, 0);
363     } else {
364       rect.Set(m_rtMaxBtn.right(), m_rtClient.top, 0, m_rtClient.height);
365     }
366     return;
367   }
368   CFX_RectF rtClient = m_rtClient;
369   FX_FLOAT fLength = IsVertical() ? rtClient.height : rtClient.width;
370   FX_FLOAT fSize = m_fButtonLen;
371   if (m_bCustomLayout) {
372     if (IsVertical()) {
373       fLength = fLength - m_rtMinBtn.height - m_rtMaxBtn.height;
374       if (fLength < m_rtMinBtn.height || fLength < m_rtMaxBtn.height) {
375         fLength = 0.0f;
376       }
377     } else {
378       fLength = fLength - m_rtMinBtn.width - m_rtMaxBtn.width;
379       if (fLength < m_rtMinBtn.width || fLength < m_rtMaxBtn.width) {
380         fLength = 0.0f;
381       }
382     }
383   } else {
384     fLength -= fSize * 2.0f;
385     if (fLength < fSize) {
386       fLength = 0.0f;
387     }
388   }
389   FX_FLOAT fThumbSize = fLength * fLength / (fRange + fLength);
390   if (fThumbSize < m_fMinThumb) {
391     fThumbSize = m_fMinThumb;
392   }
393   FX_FLOAT fDiff = fLength - fThumbSize;
394   if (fDiff < 0.0f) {
395     fDiff = 0.0f;
396   }
397   FX_FLOAT fTrackPos = m_fTrackPos;
398   if (fTrackPos > m_fRangeMax) {
399     fTrackPos = m_fRangeMax;
400   }
401   if (fTrackPos < m_fRangeMin) {
402     fTrackPos = m_fRangeMin;
403   }
404   if (!fRange)
405     return;
406   if (m_bCustomLayout) {
407     FX_FLOAT iPos = fDiff * (fTrackPos - m_fRangeMin) / fRange;
408     rect.left = rtClient.left;
409     if (!IsVertical()) {
410       if ((m_rtMinBtn.right() == m_rtMaxBtn.left && m_rtMinBtn.width > 0 &&
411            m_rtMaxBtn.width > 0) ||
412           (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width)) {
413         rect.left += iPos;
414       } else {
415         rect.left += m_rtMinBtn.right() + iPos;
416       }
417     }
418     rect.top = rtClient.top;
419     if (IsVertical()) {
420       if ((m_rtMinBtn.bottom() == m_rtMaxBtn.top && m_rtMinBtn.height > 0 &&
421            m_rtMaxBtn.height > 0) ||
422           (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height)) {
423         rect.top += iPos;
424       } else {
425         rect.top += m_rtMinBtn.bottom() + iPos;
426       }
427     }
428     rect.width = IsVertical() ? rtClient.width : fThumbSize;
429     rect.height = IsVertical() ? fThumbSize : rtClient.height;
430   } else {
431     FX_FLOAT iPos = fSize + fDiff * (fTrackPos - m_fRangeMin) / fRange;
432     rect.left = rtClient.left;
433     if (!IsVertical()) {
434       rect.left += iPos;
435     }
436     rect.top = rtClient.top;
437     if (IsVertical()) {
438       rect.top += iPos;
439     }
440     rect.width = IsVertical() ? rtClient.width : fThumbSize;
441     rect.height = IsVertical() ? fThumbSize : rtClient.height;
442   }
443 }
CalcMinTrackRect(CFX_RectF & rect)444 void CFWL_ScrollBarImp::CalcMinTrackRect(CFX_RectF& rect) {
445   if (m_bMinSize) {
446     rect.Empty();
447     return;
448   }
449   FX_FLOAT fBottom = m_rtThumb.bottom();
450   FX_FLOAT fRight = m_rtThumb.right();
451   FX_FLOAT ix = (m_rtThumb.left + fRight) / 2;
452   FX_FLOAT iy = (m_rtThumb.top + fBottom) / 2;
453   rect.left = m_rtClient.left;
454   rect.top = m_rtClient.top;
455   FX_BOOL bVertical = IsVertical();
456   rect.width = bVertical ? m_rtClient.width : ix;
457   rect.height = bVertical ? iy : m_rtClient.height;
458   if (m_bCustomLayout) {
459     if (bVertical) {
460       if (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height) {
461         rect.top = m_rtClient.top;
462       } else if (m_rtMinBtn.top < m_rtThumb.top) {
463         rect.top = m_rtMinBtn.bottom();
464         rect.height -= (m_rtMinBtn.bottom() - m_rtClient.top);
465       }
466     } else {
467       if (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width) {
468         rect.left = m_rtClient.left;
469       } else if (m_rtMinBtn.left < m_rtThumb.left) {
470         rect.left = m_rtMinBtn.right();
471         rect.width -= (m_rtMinBtn.right() - m_rtClient.left);
472       }
473     }
474   }
475 }
CalcMaxTrackRect(CFX_RectF & rect)476 void CFWL_ScrollBarImp::CalcMaxTrackRect(CFX_RectF& rect) {
477   if (m_bMinSize) {
478     rect.Empty();
479     return;
480   }
481   FX_FLOAT ix = (m_rtThumb.left + m_rtThumb.right()) / 2;
482   FX_FLOAT iy = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
483   FX_BOOL bVertical = IsVertical();
484   rect.left = bVertical ? m_rtClient.left : ix;
485   rect.top = bVertical ? iy : m_rtClient.top;
486   rect.width = bVertical ? m_rtClient.width : m_rtClient.right() - ix;
487   rect.height = bVertical ? m_rtClient.bottom() - iy : m_rtClient.height;
488   if (m_bCustomLayout) {
489     if (bVertical) {
490       if (m_rtMinBtn.top > m_rtThumb.top && m_rtMinBtn.height > 0 &&
491           m_rtMaxBtn.height > 0) {
492         rect.height -= (m_rtClient.bottom() - m_rtMinBtn.top);
493       } else if (m_rtMinBtn.height > 0 && m_rtMaxBtn.height > 0) {
494         rect.height -= (m_rtClient.bottom() - m_rtMaxBtn.top);
495       }
496     } else {
497       if (m_rtMinBtn.left > m_rtThumb.left && m_rtMinBtn.width > 0 &&
498           m_rtMaxBtn.width > 0) {
499         rect.width -= (m_rtClient.right() - m_rtMinBtn.left);
500       } else if (m_rtMinBtn.width > 0 && m_rtMaxBtn.width > 0) {
501         rect.width -= (m_rtClient.right() - m_rtMaxBtn.left);
502       }
503     }
504   }
505 }
GetTrackPointPos(FX_FLOAT fx,FX_FLOAT fy)506 FX_FLOAT CFWL_ScrollBarImp::GetTrackPointPos(FX_FLOAT fx, FX_FLOAT fy) {
507   FX_FLOAT fDiffX = fx - m_cpTrackPointX;
508   FX_FLOAT fDiffY = fy - m_cpTrackPointY;
509   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
510   FX_FLOAT fPos;
511   if (m_bCustomLayout) {
512     if (IsVertical()) {
513       if (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height) {
514         fPos = fRange * fDiffY / (m_rtClient.height - m_rtThumb.height);
515       } else if (m_rtMinBtn.bottom() == m_rtMaxBtn.top) {
516         fPos = fRange * fDiffY /
517                (m_rtMinBtn.top - m_rtClient.top - m_rtThumb.height);
518       } else {
519         fPos = fRange * fDiffY /
520                (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
521       }
522     } else {
523       if (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width) {
524         fPos = fRange * fDiffX / (m_rtClient.width - m_rtThumb.width);
525       } else if (m_rtMinBtn.right() == m_rtMaxBtn.left) {
526         fPos = fRange * fDiffX /
527                (m_rtMinBtn.left - m_rtClient.left - m_rtThumb.width);
528       } else {
529         fPos = fRange * fDiffX /
530                (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
531       }
532     }
533   } else {
534     if (IsVertical()) {
535       fPos = fRange * fDiffY /
536              (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
537     } else {
538       fPos = fRange * fDiffX /
539              (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
540     }
541   }
542   fPos += m_fLastTrackPos;
543   if (fPos < m_fRangeMin) {
544     fPos = m_fRangeMin;
545   }
546   if (fPos > m_fRangeMax) {
547     fPos = m_fRangeMax;
548   }
549   return fPos;
550 }
GetTrackRect(CFX_RectF & rect,FX_BOOL bLower)551 void CFWL_ScrollBarImp::GetTrackRect(CFX_RectF& rect, FX_BOOL bLower) {
552   FX_BOOL bDisabled = m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled;
553   if (bDisabled || m_bCustomLayout) {
554     rect = bLower ? m_rtMinTrack : m_rtMaxTrack;
555   } else {
556     FX_FLOAT fW = m_rtThumb.width / 2;
557     FX_FLOAT fH = m_rtThumb.height / 2;
558     FX_BOOL bVert = IsVertical();
559     if (bLower) {
560       if (bVert) {
561         FX_FLOAT fMinTrackHeight = m_rtMinTrack.height - fH - m_rtMinBtn.height;
562         fMinTrackHeight = (fMinTrackHeight >= 0.0f) ? fMinTrackHeight : 0.0f;
563         rect.Set(m_rtMinTrack.left, m_rtMinTrack.top + m_rtMinBtn.height,
564                  m_rtMinTrack.width, fMinTrackHeight);
565       } else {
566         FX_FLOAT fMinTrackWidth =
567             m_rtMinTrack.width - fW - m_rtMinBtn.width + 2;
568         fMinTrackWidth = (fMinTrackWidth >= 0.0f) ? fMinTrackWidth : 0.0f;
569         rect.Set(m_rtMinTrack.left + m_rtMinBtn.width - 1, m_rtMinTrack.top,
570                  fMinTrackWidth, m_rtMinTrack.height);
571       }
572     } else {
573       if (bVert) {
574         FX_FLOAT fMaxTrackHeight = m_rtMaxTrack.height - fH - m_rtMaxBtn.height;
575         fMaxTrackHeight = (fMaxTrackHeight >= 0.0f) ? fMaxTrackHeight : 0.0f;
576         rect.Set(m_rtMaxTrack.left, m_rtMaxTrack.top + fH, m_rtMaxTrack.width,
577                  fMaxTrackHeight);
578       } else {
579         FX_FLOAT fMaxTrackWidth =
580             m_rtMaxTrack.width - fW - m_rtMaxBtn.width + 2;
581         fMaxTrackWidth = (fMaxTrackWidth >= 0.0f) ? fMaxTrackWidth : 0.0f;
582         rect.Set(m_rtMaxTrack.left + fW, m_rtMaxTrack.top, fMaxTrackWidth,
583                  m_rtMaxTrack.height);
584       }
585     }
586   }
587 }
SendEvent()588 FX_BOOL CFWL_ScrollBarImp::SendEvent() {
589   if (m_iMinButtonState == FWL_PARTSTATE_SCB_Pressed) {
590     DoScroll(FWL_SCBCODE_StepBackward, m_fTrackPos);
591     return FALSE;
592   }
593   if (m_iMaxButtonState == FWL_PARTSTATE_SCB_Pressed) {
594     DoScroll(FWL_SCBCODE_StepForward, m_fTrackPos);
595     return FALSE;
596   }
597   if (m_iMinTrackState == FWL_PARTSTATE_SCB_Pressed) {
598     DoScroll(FWL_SCBCODE_PageBackward, m_fTrackPos);
599     return m_rtThumb.Contains(m_cpTrackPointX, m_cpTrackPointY);
600   }
601   if (m_iMaxTrackState == FWL_PARTSTATE_SCB_Pressed) {
602     DoScroll(FWL_SCBCODE_PageForward, m_fTrackPos);
603     return m_rtThumb.Contains(m_cpTrackPointX, m_cpTrackPointY);
604   }
605   if (m_iMouseWheel) {
606     FX_WORD dwCode =
607         m_iMouseWheel < 0 ? FWL_SCBCODE_StepForward : FWL_SCBCODE_StepBackward;
608     DoScroll(dwCode, m_fTrackPos);
609   }
610   return TRUE;
611 }
OnScroll(FX_DWORD dwCode,FX_FLOAT fPos)612 FX_BOOL CFWL_ScrollBarImp::OnScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
613   FX_BOOL bRet = TRUE;
614   CFWL_EvtScroll ev;
615   ev.m_iScrollCode = dwCode;
616   ev.m_pSrcTarget = m_pInterface;
617   ev.m_fPos = fPos;
618   ev.m_pRet = &bRet;
619   DispatchEvent(&ev);
620   return bRet;
621 }
CFWL_ScrollBarImpDelegate(CFWL_ScrollBarImp * pOwner)622 CFWL_ScrollBarImpDelegate::CFWL_ScrollBarImpDelegate(CFWL_ScrollBarImp* pOwner)
623     : m_pOwner(pOwner) {}
OnProcessMessage(CFWL_Message * pMessage)624 int32_t CFWL_ScrollBarImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
625   if (!pMessage)
626     return 0;
627   int32_t iRet = 1;
628   FX_DWORD dwMsgCode = pMessage->GetClassID();
629   if (dwMsgCode == FWL_MSGHASH_Mouse) {
630     CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
631     FX_DWORD dwCmd = pMsg->m_dwCmd;
632     switch (dwCmd) {
633       case FWL_MSGMOUSECMD_LButtonDown: {
634         OnLButtonDown(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
635         break;
636       }
637       case FWL_MSGMOUSECMD_LButtonUp: {
638         OnLButtonUp(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
639         break;
640       }
641       case FWL_MSGMOUSECMD_MouseMove: {
642         OnMouseMove(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
643         break;
644       }
645       case FWL_MSGMOUSECMD_MouseLeave: {
646         OnMouseLeave();
647         break;
648       }
649       default: { iRet = 0; }
650     }
651   } else if (dwMsgCode == FWL_MSGHASH_MouseWheel) {
652     CFWL_MsgMouseWheel* pMsg = static_cast<CFWL_MsgMouseWheel*>(pMessage);
653     OnMouseWheel(pMsg->m_fx, pMsg->m_fy, pMsg->m_dwFlags, pMsg->m_fDeltaX,
654                  pMsg->m_fDeltaY);
655   } else {
656     iRet = 0;
657   }
658   return iRet;
659 }
OnDrawWidget(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)660 FWL_ERR CFWL_ScrollBarImpDelegate::OnDrawWidget(CFX_Graphics* pGraphics,
661                                                 const CFX_Matrix* pMatrix) {
662   return m_pOwner->DrawWidget(pGraphics, pMatrix);
663 }
OnLButtonDown(FX_DWORD dwFlags,FX_FLOAT fx,FX_FLOAT fy)664 void CFWL_ScrollBarImpDelegate::OnLButtonDown(FX_DWORD dwFlags,
665                                               FX_FLOAT fx,
666                                               FX_FLOAT fy) {
667   if (!m_pOwner->IsEnabled()) {
668     return;
669   }
670   m_pOwner->m_bMouseDown = TRUE;
671   m_pOwner->SetGrab(TRUE);
672   m_pOwner->m_cpTrackPointX = fx;
673   m_pOwner->m_cpTrackPointY = fy;
674   m_pOwner->m_fLastTrackPos = m_pOwner->m_fTrackPos;
675   if (m_pOwner->m_rtMinBtn.Contains(fx, fy)) {
676     DoMouseDown(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
677   } else {
678     if (m_pOwner->m_rtThumb.Contains(fx, fy)) {
679       DoMouseDown(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx,
680                   fy);
681     } else {
682       if (m_pOwner->m_rtMaxBtn.Contains(fx, fy)) {
683         DoMouseDown(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx,
684                     fy);
685       } else {
686         if (m_pOwner->m_rtMinTrack.Contains(fx, fy)) {
687           DoMouseDown(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx,
688                       fy);
689         } else {
690           DoMouseDown(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx,
691                       fy);
692         }
693       }
694     }
695   }
696   if (!m_pOwner->SendEvent()) {
697     m_pOwner->m_hTimer = FWL_StartTimer(m_pOwner, FWL_SCROLLBAR_Elapse);
698   }
699 }
OnLButtonUp(FX_DWORD dwFlags,FX_FLOAT fx,FX_FLOAT fy)700 void CFWL_ScrollBarImpDelegate::OnLButtonUp(FX_DWORD dwFlags,
701                                             FX_FLOAT fx,
702                                             FX_FLOAT fy) {
703   FWL_StopTimer(m_pOwner->m_hTimer);
704   m_pOwner->m_bMouseDown = FALSE;
705   DoMouseUp(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
706   DoMouseUp(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx, fy);
707   DoMouseUp(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx, fy);
708   DoMouseUp(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx, fy);
709   DoMouseUp(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx, fy);
710   m_pOwner->SetGrab(FALSE);
711 }
OnMouseMove(FX_DWORD dwFlags,FX_FLOAT fx,FX_FLOAT fy)712 void CFWL_ScrollBarImpDelegate::OnMouseMove(FX_DWORD dwFlags,
713                                             FX_FLOAT fx,
714                                             FX_FLOAT fy) {
715   DoMouseMove(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
716   DoMouseMove(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx, fy);
717   DoMouseMove(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx, fy);
718   DoMouseMove(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx, fy);
719   DoMouseMove(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx, fy);
720 }
OnMouseLeave()721 void CFWL_ScrollBarImpDelegate::OnMouseLeave() {
722   DoMouseLeave(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState);
723   DoMouseLeave(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState);
724   DoMouseLeave(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState);
725   DoMouseLeave(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState);
726   DoMouseLeave(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState);
727 }
OnMouseWheel(FX_FLOAT fx,FX_FLOAT fy,FX_DWORD dwFlags,FX_FLOAT fDeltaX,FX_FLOAT fDeltaY)728 void CFWL_ScrollBarImpDelegate::OnMouseWheel(FX_FLOAT fx,
729                                              FX_FLOAT fy,
730                                              FX_DWORD dwFlags,
731                                              FX_FLOAT fDeltaX,
732                                              FX_FLOAT fDeltaY) {
733   m_pOwner->m_iMouseWheel = (int32_t)fDeltaX;
734   m_pOwner->SendEvent();
735   m_pOwner->m_iMouseWheel = 0;
736 }
DoMouseDown(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,FX_FLOAT fx,FX_FLOAT fy)737 void CFWL_ScrollBarImpDelegate::DoMouseDown(int32_t iItem,
738                                             const CFX_RectF& rtItem,
739                                             int32_t& iState,
740                                             FX_FLOAT fx,
741                                             FX_FLOAT fy) {
742   if (!rtItem.Contains(fx, fy)) {
743     return;
744   }
745   if (iState == FWL_PARTSTATE_SCB_Pressed) {
746     return;
747   }
748   iState = FWL_PARTSTATE_SCB_Pressed;
749   m_pOwner->Repaint(&rtItem);
750 }
DoMouseUp(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,FX_FLOAT fx,FX_FLOAT fy)751 void CFWL_ScrollBarImpDelegate::DoMouseUp(int32_t iItem,
752                                           const CFX_RectF& rtItem,
753                                           int32_t& iState,
754                                           FX_FLOAT fx,
755                                           FX_FLOAT fy) {
756   int32_t iNewState = rtItem.Contains(fx, fy) ? FWL_PARTSTATE_SCB_Hovered
757                                               : FWL_PARTSTATE_SCB_Normal;
758   if (iState == iNewState) {
759     return;
760   }
761   iState = iNewState;
762   m_pOwner->Repaint(&rtItem);
763   m_pOwner->OnScroll(FWL_SCBCODE_EndScroll, m_pOwner->m_fTrackPos);
764 }
DoMouseMove(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState,FX_FLOAT fx,FX_FLOAT fy)765 void CFWL_ScrollBarImpDelegate::DoMouseMove(int32_t iItem,
766                                             const CFX_RectF& rtItem,
767                                             int32_t& iState,
768                                             FX_FLOAT fx,
769                                             FX_FLOAT fy) {
770   if (!m_pOwner->m_bMouseDown) {
771     int32_t iNewState = rtItem.Contains(fx, fy) ? FWL_PARTSTATE_SCB_Hovered
772                                                 : FWL_PARTSTATE_SCB_Normal;
773     if (iState == iNewState) {
774       return;
775     }
776     iState = iNewState;
777     m_pOwner->Repaint(&rtItem);
778   } else if ((2 == iItem) &&
779              (m_pOwner->m_iThumbButtonState == FWL_PARTSTATE_SCB_Pressed)) {
780     FX_FLOAT fPos = m_pOwner->GetTrackPointPos(fx, fy);
781     m_pOwner->m_fTrackPos = fPos;
782     m_pOwner->OnScroll(FWL_SCBCODE_TrackPos, fPos);
783   }
784 }
DoMouseLeave(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState)785 void CFWL_ScrollBarImpDelegate::DoMouseLeave(int32_t iItem,
786                                              const CFX_RectF& rtItem,
787                                              int32_t& iState) {
788   if (iState == FWL_PARTSTATE_SCB_Normal) {
789     return;
790   }
791   iState = FWL_PARTSTATE_SCB_Normal;
792   m_pOwner->Repaint(&rtItem);
793 }
DoMouseHover(int32_t iItem,const CFX_RectF & rtItem,int32_t & iState)794 void CFWL_ScrollBarImpDelegate::DoMouseHover(int32_t iItem,
795                                              const CFX_RectF& rtItem,
796                                              int32_t& iState) {
797   if (iState == FWL_PARTSTATE_SCB_Hovered) {
798     return;
799   }
800   iState = FWL_PARTSTATE_SCB_Hovered;
801   m_pOwner->Repaint(&rtItem);
802 }
803