cfwl_checkbox.cpp revision 4d3acf4ec42bf6e838f9060103aff98fbf170794
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_checkbox.h" 8 9#include <algorithm> 10#include <memory> 11#include <utility> 12 13#include "third_party/base/ptr_util.h" 14#include "xfa/fde/tto/fde_textout.h" 15#include "xfa/fwl/cfwl_app.h" 16#include "xfa/fwl/cfwl_event.h" 17#include "xfa/fwl/cfwl_messagekey.h" 18#include "xfa/fwl/cfwl_messagemouse.h" 19#include "xfa/fwl/cfwl_notedriver.h" 20#include "xfa/fwl/cfwl_themebackground.h" 21#include "xfa/fwl/cfwl_themetext.h" 22#include "xfa/fwl/cfwl_widgetmgr.h" 23#include "xfa/fwl/ifwl_themeprovider.h" 24 25namespace { 26 27const int kCaptionMargin = 5; 28 29} // namespace 30 31CFWL_CheckBox::CFWL_CheckBox(const CFWL_App* app) 32 : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr), 33 m_dwTTOStyles(FDE_TTOSTYLE_SingleLine), 34 m_iTTOAlign(FDE_TTOALIGNMENT_Center), 35 m_bBtnDown(false), 36 m_fBoxHeight(16.0f) { 37 m_rtClient.Reset(); 38 m_rtBox.Reset(); 39 m_rtCaption.Reset(); 40 m_rtFocus.Reset(); 41} 42 43CFWL_CheckBox::~CFWL_CheckBox() {} 44 45FWL_Type CFWL_CheckBox::GetClassID() const { 46 return FWL_Type::CheckBox; 47} 48 49void CFWL_CheckBox::SetBoxSize(FX_FLOAT fHeight) { 50 m_fBoxHeight = fHeight; 51} 52 53void CFWL_CheckBox::Update() { 54 if (IsLocked()) 55 return; 56 if (!m_pProperties->m_pThemeProvider) 57 m_pProperties->m_pThemeProvider = GetAvailableTheme(); 58 59 UpdateTextOutStyles(); 60 Layout(); 61} 62 63void CFWL_CheckBox::DrawWidget(CFX_Graphics* pGraphics, 64 const CFX_Matrix* pMatrix) { 65 if (!pGraphics) 66 return; 67 if (!m_pProperties->m_pThemeProvider) 68 return; 69 70 IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider; 71 if (HasBorder()) { 72 DrawBorder(pGraphics, CFWL_Part::Border, m_pProperties->m_pThemeProvider, 73 pMatrix); 74 } 75 76 int32_t dwStates = GetPartStates(); 77 78 CFWL_ThemeBackground param; 79 param.m_pWidget = this; 80 param.m_iPart = CFWL_Part::Background; 81 param.m_dwStates = dwStates; 82 param.m_pGraphics = pGraphics; 83 if (pMatrix) 84 param.m_matrix.Concat(*pMatrix); 85 param.m_rtPart = m_rtClient; 86 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) 87 param.m_pData = &m_rtFocus; 88 pTheme->DrawBackground(¶m); 89 90 param.m_iPart = CFWL_Part::CheckBox; 91 param.m_rtPart = m_rtBox; 92 pTheme->DrawBackground(¶m); 93 94 CFWL_ThemeText textParam; 95 textParam.m_pWidget = this; 96 textParam.m_iPart = CFWL_Part::Caption; 97 textParam.m_dwStates = dwStates; 98 textParam.m_pGraphics = pGraphics; 99 if (pMatrix) 100 textParam.m_matrix.Concat(*pMatrix); 101 textParam.m_rtPart = m_rtCaption; 102 textParam.m_wsText = L"Check box"; 103 textParam.m_dwTTOStyles = m_dwTTOStyles; 104 textParam.m_iTTOAlign = m_iTTOAlign; 105 pTheme->DrawText(&textParam); 106} 107 108void CFWL_CheckBox::SetCheckState(int32_t iCheck) { 109 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask; 110 switch (iCheck) { 111 case 1: 112 m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked; 113 break; 114 case 2: 115 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State) 116 m_pProperties->m_dwStates |= FWL_STATE_CKB_Neutral; 117 break; 118 default: 119 break; 120 } 121 RepaintRect(m_rtClient); 122} 123 124void CFWL_CheckBox::Layout() { 125 m_pProperties->m_rtWidget.width = 126 FXSYS_round(m_pProperties->m_rtWidget.width); 127 m_pProperties->m_rtWidget.height = 128 FXSYS_round(m_pProperties->m_rtWidget.height); 129 m_rtClient = GetClientRect(); 130 131 FX_FLOAT fBoxTop = m_rtClient.top; 132 FX_FLOAT fBoxLeft = m_rtClient.left; 133 FX_FLOAT fTextLeft = fBoxLeft + m_fBoxHeight; 134 FX_FLOAT fTextRight = m_rtClient.right(); 135 m_rtBox.Set(fBoxLeft, fBoxTop, m_fBoxHeight, m_fBoxHeight); 136 m_rtCaption.Set(fTextLeft, m_rtClient.top, fTextRight - fTextLeft, 137 m_rtClient.height); 138 m_rtCaption.Inflate(-kCaptionMargin, -kCaptionMargin); 139 140 CFX_RectF rtFocus; 141 rtFocus.Set(m_rtCaption.left, m_rtCaption.top, m_rtCaption.width, 142 m_rtCaption.height); 143 144 CalcTextRect(L"Check box", m_pProperties->m_pThemeProvider, m_dwTTOStyles, 145 m_iTTOAlign, rtFocus); 146 147 FX_FLOAT fWidth = std::max(m_rtCaption.width, rtFocus.width); 148 FX_FLOAT fHeight = std::min(m_rtCaption.height, rtFocus.height); 149 FX_FLOAT fLeft = m_rtCaption.left; 150 FX_FLOAT fTop = m_rtCaption.top; 151 m_rtFocus.Set(fLeft, fTop, fWidth, fHeight); 152 m_rtFocus.Inflate(1, 1); 153} 154 155uint32_t CFWL_CheckBox::GetPartStates() const { 156 int32_t dwStates = CFWL_PartState_Normal; 157 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) == 158 FWL_STATE_CKB_Neutral) { 159 dwStates = CFWL_PartState_Neutral; 160 } else if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) == 161 FWL_STATE_CKB_Checked) { 162 dwStates = CFWL_PartState_Checked; 163 } 164 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 165 dwStates |= CFWL_PartState_Disabled; 166 else if (m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered) 167 dwStates |= CFWL_PartState_Hovered; 168 else if (m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed) 169 dwStates |= CFWL_PartState_Pressed; 170 else 171 dwStates |= CFWL_PartState_Normal; 172 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) 173 dwStates |= CFWL_PartState_Focused; 174 return dwStates; 175} 176 177void CFWL_CheckBox::UpdateTextOutStyles() { 178 m_iTTOAlign = FDE_TTOALIGNMENT_TopLeft; 179 m_dwTTOStyles = 0; 180 m_dwTTOStyles |= FDE_TTOSTYLE_SingleLine; 181} 182 183void CFWL_CheckBox::NextStates() { 184 uint32_t dwFirststate = m_pProperties->m_dwStates; 185 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_RadioButton) { 186 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) == 187 FWL_STATE_CKB_Unchecked) { 188 CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr(); 189 if (!pWidgetMgr->IsFormDisabled()) { 190 CFX_ArrayTemplate<CFWL_Widget*> radioarr; 191 pWidgetMgr->GetSameGroupRadioButton(this, radioarr); 192 CFWL_CheckBox* pCheckBox = nullptr; 193 int32_t iCount = radioarr.GetSize(); 194 for (int32_t i = 0; i < iCount; i++) { 195 pCheckBox = static_cast<CFWL_CheckBox*>(radioarr[i]); 196 if (pCheckBox != this && 197 pCheckBox->GetStates() & FWL_STATE_CKB_Checked) { 198 pCheckBox->SetCheckState(0); 199 CFX_RectF rt = pCheckBox->GetWidgetRect(); 200 rt.left = rt.top = 0; 201 m_pWidgetMgr->RepaintWidget(pCheckBox, rt); 202 break; 203 } 204 } 205 } 206 m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked; 207 } 208 } else { 209 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) == 210 FWL_STATE_CKB_Neutral) { 211 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask; 212 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State) 213 m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked; 214 } else if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) == 215 FWL_STATE_CKB_Checked) { 216 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask; 217 } else { 218 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State) 219 m_pProperties->m_dwStates |= FWL_STATE_CKB_Neutral; 220 else 221 m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked; 222 } 223 } 224 225 RepaintRect(m_rtClient); 226 if (dwFirststate == m_pProperties->m_dwStates) 227 return; 228 229 CFWL_Event wmCheckBoxState(CFWL_Event::Type::CheckStateChanged, this); 230 DispatchEvent(&wmCheckBoxState); 231} 232 233void CFWL_CheckBox::OnProcessMessage(CFWL_Message* pMessage) { 234 if (!pMessage) 235 return; 236 237 switch (pMessage->GetType()) { 238 case CFWL_Message::Type::SetFocus: 239 OnFocusChanged(true); 240 break; 241 case CFWL_Message::Type::KillFocus: 242 OnFocusChanged(false); 243 break; 244 case CFWL_Message::Type::Mouse: { 245 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage); 246 switch (pMsg->m_dwCmd) { 247 case FWL_MouseCommand::LeftButtonDown: 248 OnLButtonDown(); 249 break; 250 case FWL_MouseCommand::LeftButtonUp: 251 OnLButtonUp(pMsg); 252 break; 253 case FWL_MouseCommand::Move: 254 OnMouseMove(pMsg); 255 break; 256 case FWL_MouseCommand::Leave: 257 OnMouseLeave(); 258 break; 259 default: 260 break; 261 } 262 break; 263 } 264 case CFWL_Message::Type::Key: { 265 CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage); 266 if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown) 267 OnKeyDown(pKey); 268 break; 269 } 270 default: 271 break; 272 } 273 274 CFWL_Widget::OnProcessMessage(pMessage); 275} 276 277void CFWL_CheckBox::OnDrawWidget(CFX_Graphics* pGraphics, 278 const CFX_Matrix* pMatrix) { 279 DrawWidget(pGraphics, pMatrix); 280} 281 282void CFWL_CheckBox::OnFocusChanged(bool bSet) { 283 if (bSet) 284 m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused; 285 else 286 m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused; 287 288 RepaintRect(m_rtClient); 289} 290 291void CFWL_CheckBox::OnLButtonDown() { 292 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 293 return; 294 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) 295 SetFocus(true); 296 297 m_bBtnDown = true; 298 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered; 299 m_pProperties->m_dwStates |= FWL_STATE_CKB_Pressed; 300 RepaintRect(m_rtClient); 301} 302 303void CFWL_CheckBox::OnLButtonUp(CFWL_MessageMouse* pMsg) { 304 if (!m_bBtnDown) 305 return; 306 307 m_bBtnDown = false; 308 if (!m_rtClient.Contains(pMsg->m_fx, pMsg->m_fy)) 309 return; 310 311 m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered; 312 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Pressed; 313 NextStates(); 314} 315 316void CFWL_CheckBox::OnMouseMove(CFWL_MessageMouse* pMsg) { 317 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 318 return; 319 320 bool bRepaint = false; 321 if (m_bBtnDown) { 322 if (m_rtClient.Contains(pMsg->m_fx, pMsg->m_fy)) { 323 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed) == 0) { 324 bRepaint = true; 325 m_pProperties->m_dwStates |= FWL_STATE_CKB_Pressed; 326 } 327 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered)) { 328 bRepaint = true; 329 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered; 330 } 331 } else { 332 if (m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed) { 333 bRepaint = true; 334 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Pressed; 335 } 336 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered) == 0) { 337 bRepaint = true; 338 m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered; 339 } 340 } 341 } else { 342 if (m_rtClient.Contains(pMsg->m_fx, pMsg->m_fy)) { 343 if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered) == 0) { 344 bRepaint = true; 345 m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered; 346 } 347 } 348 } 349 if (bRepaint) 350 RepaintRect(m_rtBox); 351} 352 353void CFWL_CheckBox::OnMouseLeave() { 354 if (m_bBtnDown) 355 m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered; 356 else 357 m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered; 358 359 RepaintRect(m_rtBox); 360} 361 362void CFWL_CheckBox::OnKeyDown(CFWL_MessageKey* pMsg) { 363 if (pMsg->m_dwKeyCode == FWL_VKEY_Tab) 364 return; 365 if (pMsg->m_dwKeyCode == FWL_VKEY_Return || 366 pMsg->m_dwKeyCode == FWL_VKEY_Space) { 367 NextStates(); 368 } 369} 370