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(¶ms);
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(¶ms);
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