• 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_spinbutton.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "third_party/base/ptr_util.h"
13 #include "xfa/fwl/cfwl_event.h"
14 #include "xfa/fwl/cfwl_messagekey.h"
15 #include "xfa/fwl/cfwl_messagemouse.h"
16 #include "xfa/fwl/cfwl_notedriver.h"
17 #include "xfa/fwl/cfwl_themebackground.h"
18 #include "xfa/fwl/cfwl_timerinfo.h"
19 #include "xfa/fwl/cfwl_widgetproperties.h"
20 #include "xfa/fwl/ifwl_themeprovider.h"
21 
22 namespace {
23 const int kElapseTime = 200;
24 
25 }  // namespace
26 
CFWL_SpinButton(const CFWL_App * app,std::unique_ptr<CFWL_WidgetProperties> properties)27 CFWL_SpinButton::CFWL_SpinButton(
28     const CFWL_App* app,
29     std::unique_ptr<CFWL_WidgetProperties> properties)
30     : CFWL_Widget(app, std::move(properties), nullptr),
31       m_dwUpState(CFWL_PartState_Normal),
32       m_dwDnState(CFWL_PartState_Normal),
33       m_iButtonIndex(0),
34       m_bLButtonDwn(false),
35       m_pTimerInfo(nullptr),
36       m_Timer(this) {
37   m_rtClient.Reset();
38   m_rtUpButton.Reset();
39   m_rtDnButton.Reset();
40   m_pProperties->m_dwStyleExes |= FWL_STYLEEXE_SPB_Vert;
41 }
42 
~CFWL_SpinButton()43 CFWL_SpinButton::~CFWL_SpinButton() {}
44 
GetClassID() const45 FWL_Type CFWL_SpinButton::GetClassID() const {
46   return FWL_Type::SpinButton;
47 }
48 
Update()49 void CFWL_SpinButton::Update() {
50   if (IsLocked())
51     return;
52 
53   m_rtClient = GetClientRect();
54   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXE_SPB_Vert) {
55     m_rtUpButton = CFX_RectF(m_rtClient.top, m_rtClient.left, m_rtClient.width,
56                              m_rtClient.height / 2);
57     m_rtDnButton =
58         CFX_RectF(m_rtClient.left, m_rtClient.top + m_rtClient.height / 2,
59                   m_rtClient.width, m_rtClient.height / 2);
60   } else {
61     m_rtUpButton = CFX_RectF(m_rtClient.TopLeft(), m_rtClient.width / 2,
62                              m_rtClient.height);
63     m_rtDnButton =
64         CFX_RectF(m_rtClient.left + m_rtClient.width / 2, m_rtClient.top,
65                   m_rtClient.width / 2, m_rtClient.height);
66   }
67 }
68 
HitTest(const CFX_PointF & point)69 FWL_WidgetHit CFWL_SpinButton::HitTest(const CFX_PointF& point) {
70   if (m_rtClient.Contains(point))
71     return FWL_WidgetHit::Client;
72   if (HasBorder() && (m_rtClient.Contains(point)))
73     return FWL_WidgetHit::Border;
74   if (m_rtUpButton.Contains(point))
75     return FWL_WidgetHit::UpButton;
76   if (m_rtDnButton.Contains(point))
77     return FWL_WidgetHit::DownButton;
78   return FWL_WidgetHit::Unknown;
79 }
80 
DrawWidget(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)81 void CFWL_SpinButton::DrawWidget(CFX_Graphics* pGraphics,
82                                  const CFX_Matrix* pMatrix) {
83   if (!pGraphics)
84     return;
85 
86   CFX_RectF rtClip(m_rtClient);
87   if (pMatrix)
88     pMatrix->TransformRect(rtClip);
89 
90   IFWL_ThemeProvider* pTheme = GetAvailableTheme();
91   if (HasBorder())
92     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, pMatrix);
93 
94   DrawUpButton(pGraphics, pTheme, pMatrix);
95   DrawDownButton(pGraphics, pTheme, pMatrix);
96 }
97 
DisableButton()98 void CFWL_SpinButton::DisableButton() {
99   m_dwDnState = CFWL_PartState_Disabled;
100 }
101 
IsUpButtonEnabled()102 bool CFWL_SpinButton::IsUpButtonEnabled() {
103   return m_dwUpState != CFWL_PartState_Disabled;
104 }
105 
IsDownButtonEnabled()106 bool CFWL_SpinButton::IsDownButtonEnabled() {
107   return m_dwDnState != CFWL_PartState_Disabled;
108 }
109 
DrawUpButton(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)110 void CFWL_SpinButton::DrawUpButton(CFX_Graphics* pGraphics,
111                                    IFWL_ThemeProvider* pTheme,
112                                    const CFX_Matrix* pMatrix) {
113   CFWL_ThemeBackground params;
114   params.m_pWidget = this;
115   params.m_iPart = CFWL_Part::UpButton;
116   params.m_pGraphics = pGraphics;
117   params.m_dwStates = m_dwUpState + 1;
118   if (pMatrix)
119     params.m_matrix.Concat(*pMatrix);
120 
121   params.m_rtPart = m_rtUpButton;
122   pTheme->DrawBackground(&params);
123 }
124 
DrawDownButton(CFX_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)125 void CFWL_SpinButton::DrawDownButton(CFX_Graphics* pGraphics,
126                                      IFWL_ThemeProvider* pTheme,
127                                      const CFX_Matrix* pMatrix) {
128   CFWL_ThemeBackground params;
129   params.m_pWidget = this;
130   params.m_iPart = CFWL_Part::DownButton;
131   params.m_pGraphics = pGraphics;
132   params.m_dwStates = m_dwDnState + 1;
133   if (pMatrix)
134     params.m_matrix.Concat(*pMatrix);
135 
136   params.m_rtPart = m_rtDnButton;
137   pTheme->DrawBackground(&params);
138 }
139 
OnProcessMessage(CFWL_Message * pMessage)140 void CFWL_SpinButton::OnProcessMessage(CFWL_Message* pMessage) {
141   if (!pMessage)
142     return;
143 
144   switch (pMessage->GetType()) {
145     case CFWL_Message::Type::SetFocus: {
146       OnFocusChanged(pMessage, true);
147       break;
148     }
149     case CFWL_Message::Type::KillFocus: {
150       OnFocusChanged(pMessage, false);
151       break;
152     }
153     case CFWL_Message::Type::Mouse: {
154       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
155       switch (pMsg->m_dwCmd) {
156         case FWL_MouseCommand::LeftButtonDown:
157           OnLButtonDown(pMsg);
158           break;
159         case FWL_MouseCommand::LeftButtonUp:
160           OnLButtonUp(pMsg);
161           break;
162         case FWL_MouseCommand::Move:
163           OnMouseMove(pMsg);
164           break;
165         case FWL_MouseCommand::Leave:
166           OnMouseLeave(pMsg);
167           break;
168         default:
169           break;
170       }
171       break;
172     }
173     case CFWL_Message::Type::Key: {
174       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
175       if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown)
176         OnKeyDown(pKey);
177       break;
178     }
179     default:
180       break;
181   }
182   CFWL_Widget::OnProcessMessage(pMessage);
183 }
184 
OnDrawWidget(CFX_Graphics * pGraphics,const CFX_Matrix * pMatrix)185 void CFWL_SpinButton::OnDrawWidget(CFX_Graphics* pGraphics,
186                                    const CFX_Matrix* pMatrix) {
187   DrawWidget(pGraphics, pMatrix);
188 }
189 
OnFocusChanged(CFWL_Message * pMsg,bool bSet)190 void CFWL_SpinButton::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
191   if (bSet)
192     m_pProperties->m_dwStates |= (FWL_WGTSTATE_Focused);
193   else
194     m_pProperties->m_dwStates &= ~(FWL_WGTSTATE_Focused);
195 
196   RepaintRect(m_rtClient);
197 }
198 
OnLButtonDown(CFWL_MessageMouse * pMsg)199 void CFWL_SpinButton::OnLButtonDown(CFWL_MessageMouse* pMsg) {
200   m_bLButtonDwn = true;
201   SetGrab(true);
202   SetFocus(true);
203 
204   bool bUpPress = m_rtUpButton.Contains(pMsg->m_pos) && IsUpButtonEnabled();
205   bool bDnPress = m_rtDnButton.Contains(pMsg->m_pos) && IsDownButtonEnabled();
206   if (!bUpPress && !bDnPress)
207     return;
208   if (bUpPress) {
209     m_iButtonIndex = 0;
210     m_dwUpState = CFWL_PartState_Pressed;
211   }
212   if (bDnPress) {
213     m_iButtonIndex = 1;
214     m_dwDnState = CFWL_PartState_Pressed;
215   }
216 
217   CFWL_Event wmPosChanged(CFWL_Event::Type::Click, this);
218   DispatchEvent(&wmPosChanged);
219 
220   RepaintRect(bUpPress ? m_rtUpButton : m_rtDnButton);
221   m_pTimerInfo = m_Timer.StartTimer(kElapseTime, true);
222 }
223 
OnLButtonUp(CFWL_MessageMouse * pMsg)224 void CFWL_SpinButton::OnLButtonUp(CFWL_MessageMouse* pMsg) {
225   if (m_pProperties->m_dwStates & CFWL_PartState_Disabled)
226     return;
227 
228   m_bLButtonDwn = false;
229   SetGrab(false);
230   SetFocus(false);
231   if (m_pTimerInfo) {
232     m_pTimerInfo->StopTimer();
233     m_pTimerInfo = nullptr;
234   }
235   bool bRepaint = false;
236   CFX_RectF rtInvalidate;
237   if (m_dwUpState == CFWL_PartState_Pressed && IsUpButtonEnabled()) {
238     m_dwUpState = CFWL_PartState_Normal;
239     bRepaint = true;
240     rtInvalidate = m_rtUpButton;
241   } else if (m_dwDnState == CFWL_PartState_Pressed && IsDownButtonEnabled()) {
242     m_dwDnState = CFWL_PartState_Normal;
243     bRepaint = true;
244     rtInvalidate = m_rtDnButton;
245   }
246   if (bRepaint)
247     RepaintRect(rtInvalidate);
248 }
249 
OnMouseMove(CFWL_MessageMouse * pMsg)250 void CFWL_SpinButton::OnMouseMove(CFWL_MessageMouse* pMsg) {
251   if (m_bLButtonDwn)
252     return;
253 
254   bool bRepaint = false;
255   CFX_RectF rtInvlidate;
256   if (m_rtUpButton.Contains(pMsg->m_pos)) {
257     if (IsUpButtonEnabled()) {
258       if (m_dwUpState == CFWL_PartState_Hovered) {
259         m_dwUpState = CFWL_PartState_Hovered;
260         bRepaint = true;
261         rtInvlidate = m_rtUpButton;
262       }
263       if (m_dwDnState != CFWL_PartState_Normal && IsDownButtonEnabled()) {
264         m_dwDnState = CFWL_PartState_Normal;
265         if (bRepaint)
266           rtInvlidate.Union(m_rtDnButton);
267         else
268           rtInvlidate = m_rtDnButton;
269 
270         bRepaint = true;
271       }
272     }
273     if (!IsDownButtonEnabled())
274       DisableButton();
275 
276   } else if (m_rtDnButton.Contains(pMsg->m_pos)) {
277     if (IsDownButtonEnabled()) {
278       if (m_dwDnState != CFWL_PartState_Hovered) {
279         m_dwDnState = CFWL_PartState_Hovered;
280         bRepaint = true;
281         rtInvlidate = m_rtDnButton;
282       }
283       if (m_dwUpState != CFWL_PartState_Normal && IsUpButtonEnabled()) {
284         m_dwUpState = CFWL_PartState_Normal;
285         if (bRepaint)
286           rtInvlidate.Union(m_rtUpButton);
287         else
288           rtInvlidate = m_rtUpButton;
289         bRepaint = true;
290       }
291     }
292   } else if (m_dwUpState != CFWL_PartState_Normal ||
293              m_dwDnState != CFWL_PartState_Normal) {
294     if (m_dwUpState != CFWL_PartState_Normal) {
295       m_dwUpState = CFWL_PartState_Normal;
296       bRepaint = true;
297       rtInvlidate = m_rtUpButton;
298     }
299     if (m_dwDnState != CFWL_PartState_Normal) {
300       m_dwDnState = CFWL_PartState_Normal;
301       if (bRepaint)
302         rtInvlidate.Union(m_rtDnButton);
303       else
304         rtInvlidate = m_rtDnButton;
305 
306       bRepaint = true;
307     }
308   }
309   if (bRepaint)
310     RepaintRect(rtInvlidate);
311 }
312 
OnMouseLeave(CFWL_MessageMouse * pMsg)313 void CFWL_SpinButton::OnMouseLeave(CFWL_MessageMouse* pMsg) {
314   if (!pMsg)
315     return;
316   if (m_dwUpState != CFWL_PartState_Normal && IsUpButtonEnabled())
317     m_dwUpState = CFWL_PartState_Normal;
318   if (m_dwDnState != CFWL_PartState_Normal && IsDownButtonEnabled())
319     m_dwDnState = CFWL_PartState_Normal;
320 
321   RepaintRect(m_rtClient);
322 }
323 
OnKeyDown(CFWL_MessageKey * pMsg)324 void CFWL_SpinButton::OnKeyDown(CFWL_MessageKey* pMsg) {
325   bool bUp =
326       pMsg->m_dwKeyCode == FWL_VKEY_Up || pMsg->m_dwKeyCode == FWL_VKEY_Left;
327   bool bDown =
328       pMsg->m_dwKeyCode == FWL_VKEY_Down || pMsg->m_dwKeyCode == FWL_VKEY_Right;
329   if (!bUp && !bDown)
330     return;
331 
332   bool bUpEnable = IsUpButtonEnabled();
333   bool bDownEnable = IsDownButtonEnabled();
334   if (!bUpEnable && !bDownEnable)
335     return;
336 
337   CFWL_Event wmPosChanged(CFWL_Event::Type::Click, this);
338   DispatchEvent(&wmPosChanged);
339 
340   RepaintRect(bUpEnable ? m_rtUpButton : m_rtDnButton);
341 }
342 
Timer(CFWL_SpinButton * pToolTip)343 CFWL_SpinButton::Timer::Timer(CFWL_SpinButton* pToolTip)
344     : CFWL_Timer(pToolTip) {}
345 
Run(CFWL_TimerInfo * pTimerInfo)346 void CFWL_SpinButton::Timer::Run(CFWL_TimerInfo* pTimerInfo) {
347   CFWL_SpinButton* pButton = static_cast<CFWL_SpinButton*>(m_pWidget);
348 
349   if (!pButton->m_pTimerInfo)
350     return;
351 
352   CFWL_Event wmPosChanged(CFWL_Event::Type::Click, pButton);
353   pButton->DispatchEvent(&wmPosChanged);
354 }
355