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 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 43 CFWL_SpinButton::~CFWL_SpinButton() {} 44 45 FWL_Type CFWL_SpinButton::GetClassID() const { 46 return FWL_Type::SpinButton; 47 } 48 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 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 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 98 void CFWL_SpinButton::DisableButton() { 99 m_dwDnState = CFWL_PartState_Disabled; 100 } 101 102 bool CFWL_SpinButton::IsUpButtonEnabled() { 103 return m_dwUpState != CFWL_PartState_Disabled; 104 } 105 106 bool CFWL_SpinButton::IsDownButtonEnabled() { 107 return m_dwDnState != CFWL_PartState_Disabled; 108 } 109 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 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 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 185 void CFWL_SpinButton::OnDrawWidget(CFX_Graphics* pGraphics, 186 const CFX_Matrix* pMatrix) { 187 DrawWidget(pGraphics, pMatrix); 188 } 189 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 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 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 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 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 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 343 CFWL_SpinButton::Timer::Timer(CFWL_SpinButton* pToolTip) 344 : CFWL_Timer(pToolTip) {} 345 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