cfwl_combobox.cpp revision 5ae9d0c6fd838a2967cca72aa5751b51dadc2769
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_combobox.h" 8 9#include <algorithm> 10#include <memory> 11#include <utility> 12 13#include "third_party/base/ptr_util.h" 14#include "xfa/fde/cfde_txtedtengine.h" 15#include "xfa/fde/tto/fde_textout.h" 16#include "xfa/fwl/cfwl_app.h" 17#include "xfa/fwl/cfwl_event.h" 18#include "xfa/fwl/cfwl_eventselectchanged.h" 19#include "xfa/fwl/cfwl_eventtextchanged.h" 20#include "xfa/fwl/cfwl_formproxy.h" 21#include "xfa/fwl/cfwl_listbox.h" 22#include "xfa/fwl/cfwl_messagekey.h" 23#include "xfa/fwl/cfwl_messagekillfocus.h" 24#include "xfa/fwl/cfwl_messagemouse.h" 25#include "xfa/fwl/cfwl_messagesetfocus.h" 26#include "xfa/fwl/cfwl_notedriver.h" 27#include "xfa/fwl/cfwl_themebackground.h" 28#include "xfa/fwl/cfwl_themepart.h" 29#include "xfa/fwl/cfwl_themetext.h" 30#include "xfa/fwl/cfwl_widgetmgr.h" 31#include "xfa/fwl/ifwl_themeprovider.h" 32 33CFWL_ComboBox::CFWL_ComboBox(const CFWL_App* app) 34 : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr), 35 m_pComboBoxProxy(nullptr), 36 m_bLButtonDown(false), 37 m_iCurSel(-1), 38 m_iBtnState(CFWL_PartState_Normal) { 39 m_rtClient.Reset(); 40 m_rtBtn.Reset(); 41 m_rtHandler.Reset(); 42 43 if (m_pWidgetMgr->IsFormDisabled()) { 44 DisForm_InitComboList(); 45 DisForm_InitComboEdit(); 46 return; 47 } 48 49 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 50 prop->m_pThemeProvider = m_pProperties->m_pThemeProvider; 51 prop->m_dwStyles |= FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll; 52 m_pListBox = 53 pdfium::MakeUnique<CFWL_ComboList>(m_pOwnerApp, std::move(prop), this); 54 55 if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_DropDown) && !m_pEdit) { 56 m_pEdit = pdfium::MakeUnique<CFWL_ComboEdit>( 57 m_pOwnerApp, pdfium::MakeUnique<CFWL_WidgetProperties>(), this); 58 m_pEdit->SetOuter(this); 59 } 60 if (m_pEdit) 61 m_pEdit->SetParent(this); 62 63 SetStates(m_pProperties->m_dwStates); 64} 65 66CFWL_ComboBox::~CFWL_ComboBox() {} 67 68FWL_Type CFWL_ComboBox::GetClassID() const { 69 return FWL_Type::ComboBox; 70} 71 72void CFWL_ComboBox::AddString(const CFX_WideStringC& wsText) { 73 m_pListBox->AddString(wsText); 74} 75 76void CFWL_ComboBox::RemoveAt(int32_t iIndex) { 77 m_pListBox->RemoveAt(iIndex); 78} 79 80void CFWL_ComboBox::RemoveAll() { 81 m_pListBox->DeleteAll(); 82} 83 84void CFWL_ComboBox::ModifyStylesEx(uint32_t dwStylesExAdded, 85 uint32_t dwStylesExRemoved) { 86 if (m_pWidgetMgr->IsFormDisabled()) { 87 DisForm_ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved); 88 return; 89 } 90 91 bool bAddDropDown = !!(dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown); 92 bool bRemoveDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown); 93 if (bAddDropDown && !m_pEdit) { 94 m_pEdit = pdfium::MakeUnique<CFWL_ComboEdit>( 95 m_pOwnerApp, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr); 96 m_pEdit->SetOuter(this); 97 m_pEdit->SetParent(this); 98 } else if (bRemoveDropDown && m_pEdit) { 99 m_pEdit->SetStates(FWL_WGTSTATE_Invisible); 100 } 101 CFWL_Widget::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved); 102} 103 104void CFWL_ComboBox::Update() { 105 if (m_pWidgetMgr->IsFormDisabled()) { 106 DisForm_Update(); 107 return; 108 } 109 if (IsLocked()) 110 return; 111 112 ResetTheme(); 113 if (IsDropDownStyle() && m_pEdit) 114 ResetEditAlignment(); 115 if (!m_pProperties->m_pThemeProvider) 116 m_pProperties->m_pThemeProvider = GetAvailableTheme(); 117 118 Layout(); 119} 120 121FWL_WidgetHit CFWL_ComboBox::HitTest(const CFX_PointF& point) { 122 if (m_pWidgetMgr->IsFormDisabled()) 123 return DisForm_HitTest(point); 124 return CFWL_Widget::HitTest(point); 125} 126 127void CFWL_ComboBox::DrawWidget(CFX_Graphics* pGraphics, 128 const CFX_Matrix* pMatrix) { 129 if (m_pWidgetMgr->IsFormDisabled()) { 130 DisForm_DrawWidget(pGraphics, pMatrix); 131 return; 132 } 133 134 if (!pGraphics) 135 return; 136 if (!m_pProperties->m_pThemeProvider) 137 return; 138 139 IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider; 140 if (HasBorder()) 141 DrawBorder(pGraphics, CFWL_Part::Border, pTheme, pMatrix); 142 143 if (!IsDropDownStyle()) { 144 CFX_RectF rtTextBk(m_rtClient); 145 rtTextBk.width -= m_rtBtn.width; 146 147 CFWL_ThemeBackground param; 148 param.m_pWidget = this; 149 param.m_iPart = CFWL_Part::Background; 150 param.m_pGraphics = pGraphics; 151 if (pMatrix) 152 param.m_matrix.Concat(*pMatrix); 153 param.m_rtPart = rtTextBk; 154 155 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) { 156 param.m_dwStates = CFWL_PartState_Disabled; 157 } else if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) && 158 (m_iCurSel >= 0)) { 159 param.m_dwStates = CFWL_PartState_Selected; 160 } else { 161 param.m_dwStates = CFWL_PartState_Normal; 162 } 163 pTheme->DrawBackground(¶m); 164 165 if (m_iCurSel >= 0) { 166 if (!m_pListBox) 167 return; 168 169 CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel); 170 171 CFWL_ThemeText theme_text; 172 theme_text.m_pWidget = this; 173 theme_text.m_iPart = CFWL_Part::Caption; 174 theme_text.m_dwStates = m_iBtnState; 175 theme_text.m_pGraphics = pGraphics; 176 theme_text.m_matrix.Concat(*pMatrix); 177 theme_text.m_rtPart = rtTextBk; 178 theme_text.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) 179 ? CFWL_PartState_Selected 180 : CFWL_PartState_Normal; 181 theme_text.m_wsText = hItem ? hItem->GetText() : L""; 182 theme_text.m_dwTTOStyles = FDE_TTOSTYLE_SingleLine; 183 theme_text.m_iTTOAlign = FDE_TTOALIGNMENT_CenterLeft; 184 pTheme->DrawText(&theme_text); 185 } 186 } 187 188 CFWL_ThemeBackground param; 189 param.m_pWidget = this; 190 param.m_iPart = CFWL_Part::DropDownButton; 191 param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 192 ? CFWL_PartState_Disabled 193 : m_iBtnState; 194 param.m_pGraphics = pGraphics; 195 param.m_matrix.Concat(*pMatrix); 196 param.m_rtPart = m_rtBtn; 197 pTheme->DrawBackground(¶m); 198} 199 200void CFWL_ComboBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) { 201 if (!pThemeProvider) 202 return; 203 204 m_pProperties->m_pThemeProvider = pThemeProvider; 205 if (m_pListBox) 206 m_pListBox->SetThemeProvider(pThemeProvider); 207 if (m_pEdit) 208 m_pEdit->SetThemeProvider(pThemeProvider); 209} 210 211CFX_WideString CFWL_ComboBox::GetTextByIndex(int32_t iIndex) const { 212 CFWL_ListItem* pItem = static_cast<CFWL_ListItem*>( 213 m_pListBox->GetItem(m_pListBox.get(), iIndex)); 214 return pItem ? pItem->GetText() : L""; 215} 216 217void CFWL_ComboBox::SetCurSel(int32_t iSel) { 218 int32_t iCount = m_pListBox->CountItems(nullptr); 219 bool bClearSel = iSel < 0 || iSel >= iCount; 220 if (IsDropDownStyle() && m_pEdit) { 221 if (bClearSel) { 222 m_pEdit->SetText(CFX_WideString()); 223 } else { 224 CFWL_ListItem* hItem = m_pListBox->GetItem(this, iSel); 225 m_pEdit->SetText(hItem ? hItem->GetText() : L""); 226 } 227 m_pEdit->Update(); 228 } 229 m_iCurSel = bClearSel ? -1 : iSel; 230} 231 232void CFWL_ComboBox::SetStates(uint32_t dwStates) { 233 if (IsDropDownStyle() && m_pEdit) 234 m_pEdit->SetStates(dwStates); 235 if (m_pListBox) 236 m_pListBox->SetStates(dwStates); 237 CFWL_Widget::SetStates(dwStates); 238} 239 240void CFWL_ComboBox::RemoveStates(uint32_t dwStates) { 241 if (IsDropDownStyle() && m_pEdit) 242 m_pEdit->RemoveStates(dwStates); 243 if (m_pListBox) 244 m_pListBox->RemoveStates(dwStates); 245 CFWL_Widget::RemoveStates(dwStates); 246} 247 248void CFWL_ComboBox::SetEditText(const CFX_WideString& wsText) { 249 if (!m_pEdit) 250 return; 251 252 m_pEdit->SetText(wsText); 253 m_pEdit->Update(); 254} 255 256CFX_WideString CFWL_ComboBox::GetEditText() const { 257 if (m_pEdit) 258 return m_pEdit->GetText(); 259 if (!m_pListBox) 260 return L""; 261 262 CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel); 263 return hItem ? hItem->GetText() : L""; 264} 265 266void CFWL_ComboBox::OpenDropDownList(bool bActivate) { 267 ShowDropList(bActivate); 268} 269 270CFX_RectF CFWL_ComboBox::GetBBox() const { 271 if (m_pWidgetMgr->IsFormDisabled()) 272 return DisForm_GetBBox(); 273 274 CFX_RectF rect = m_pProperties->m_rtWidget; 275 if (!m_pListBox || !IsDropListVisible()) 276 return rect; 277 278 CFX_RectF rtList = m_pListBox->GetWidgetRect(); 279 rtList.Offset(rect.left, rect.top); 280 rect.Union(rtList); 281 return rect; 282} 283 284void CFWL_ComboBox::EditModifyStylesEx(uint32_t dwStylesExAdded, 285 uint32_t dwStylesExRemoved) { 286 if (m_pEdit) 287 m_pEdit->ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved); 288} 289 290void CFWL_ComboBox::DrawStretchHandler(CFX_Graphics* pGraphics, 291 const CFX_Matrix* pMatrix) { 292 CFWL_ThemeBackground param; 293 param.m_pGraphics = pGraphics; 294 param.m_iPart = CFWL_Part::StretchHandler; 295 param.m_dwStates = CFWL_PartState_Normal; 296 param.m_pWidget = this; 297 if (pMatrix) 298 param.m_matrix.Concat(*pMatrix); 299 param.m_rtPart = m_rtHandler; 300 m_pProperties->m_pThemeProvider->DrawBackground(¶m); 301} 302 303void CFWL_ComboBox::ShowDropList(bool bActivate) { 304 if (m_pWidgetMgr->IsFormDisabled()) 305 return DisForm_ShowDropList(bActivate); 306 if (IsDropListVisible() == bActivate) 307 return; 308 if (!m_pComboBoxProxy) 309 InitProxyForm(); 310 311 m_pComboBoxProxy->Reset(); 312 if (!bActivate) { 313 m_pComboBoxProxy->EndDoModal(); 314 315 m_bLButtonDown = false; 316 m_pListBox->SetNotifyOwner(true); 317 SetFocus(true); 318 return; 319 } 320 321 m_pListBox->ChangeSelected(m_iCurSel); 322 ResetListItemAlignment(); 323 324 uint32_t dwStyleAdd = m_pProperties->m_dwStyleExes & 325 (FWL_STYLEEXT_CMB_Sort | FWL_STYLEEXT_CMB_OwnerDraw); 326 m_pListBox->ModifyStylesEx(dwStyleAdd, 0); 327 m_rtList = m_pListBox->GetAutosizedWidgetRect(); 328 329 CFX_RectF rtAnchor(0, 0, m_pProperties->m_rtWidget.width, 330 m_pProperties->m_rtWidget.height); 331 332 m_rtList.width = std::max(m_rtList.width, m_rtClient.width); 333 m_rtProxy = m_rtList; 334 335 GetPopupPos(0, m_rtProxy.height, rtAnchor, m_rtProxy); 336 337 m_pComboBoxProxy->SetWidgetRect(m_rtProxy); 338 m_pComboBoxProxy->Update(); 339 m_pListBox->SetWidgetRect(m_rtList); 340 m_pListBox->Update(); 341 342 CFWL_Event ev(CFWL_Event::Type::PreDropDown, this); 343 DispatchEvent(&ev); 344 345 m_pListBox->SetFocus(true); 346 m_pComboBoxProxy->DoModal(); 347 m_pListBox->SetFocus(false); 348} 349 350void CFWL_ComboBox::MatchEditText() { 351 CFX_WideString wsText = m_pEdit->GetText(); 352 int32_t iMatch = m_pListBox->MatchItem(wsText); 353 if (iMatch != m_iCurSel) { 354 m_pListBox->ChangeSelected(iMatch); 355 if (iMatch >= 0) 356 SyncEditText(iMatch); 357 } else if (iMatch >= 0) { 358 m_pEdit->SetSelected(); 359 } 360 m_iCurSel = iMatch; 361} 362 363void CFWL_ComboBox::SyncEditText(int32_t iListItem) { 364 CFWL_ListItem* hItem = m_pListBox->GetItem(this, iListItem); 365 m_pEdit->SetText(hItem ? hItem->GetText() : L""); 366 m_pEdit->Update(); 367 m_pEdit->SetSelected(); 368} 369 370void CFWL_ComboBox::Layout() { 371 if (m_pWidgetMgr->IsFormDisabled()) 372 return DisForm_Layout(); 373 374 m_rtClient = GetClientRect(); 375 IFWL_ThemeProvider* theme = GetAvailableTheme(); 376 if (!theme) 377 return; 378 379 FX_FLOAT fBtn = theme->GetScrollBarWidth(); 380 m_rtBtn = CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top, fBtn, 381 m_rtClient.height); 382 if (!IsDropDownStyle() || !m_pEdit) 383 return; 384 385 CFX_RectF rtEdit(m_rtClient.left, m_rtClient.top, m_rtClient.width - fBtn, 386 m_rtClient.height); 387 m_pEdit->SetWidgetRect(rtEdit); 388 389 if (m_iCurSel >= 0) { 390 CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel); 391 m_pEdit->LockUpdate(); 392 m_pEdit->SetText(hItem ? hItem->GetText() : L""); 393 m_pEdit->UnlockUpdate(); 394 } 395 m_pEdit->Update(); 396} 397 398void CFWL_ComboBox::ResetTheme() { 399 IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider; 400 if (!pTheme) { 401 pTheme = GetAvailableTheme(); 402 m_pProperties->m_pThemeProvider = pTheme; 403 } 404 if (m_pListBox && !m_pListBox->GetThemeProvider()) 405 m_pListBox->SetThemeProvider(pTheme); 406 if (m_pEdit && !m_pEdit->GetThemeProvider()) 407 m_pEdit->SetThemeProvider(pTheme); 408} 409 410void CFWL_ComboBox::ResetEditAlignment() { 411 if (!m_pEdit) 412 return; 413 414 uint32_t dwAdd = 0; 415 switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditHAlignMask) { 416 case FWL_STYLEEXT_CMB_EditHCenter: { 417 dwAdd |= FWL_STYLEEXT_EDT_HCenter; 418 break; 419 } 420 default: { 421 dwAdd |= FWL_STYLEEXT_EDT_HNear; 422 break; 423 } 424 } 425 switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditVAlignMask) { 426 case FWL_STYLEEXT_CMB_EditVCenter: { 427 dwAdd |= FWL_STYLEEXT_EDT_VCenter; 428 break; 429 } 430 case FWL_STYLEEXT_CMB_EditVFar: { 431 dwAdd |= FWL_STYLEEXT_EDT_VFar; 432 break; 433 } 434 default: { 435 dwAdd |= FWL_STYLEEXT_EDT_VNear; 436 break; 437 } 438 } 439 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditJustified) 440 dwAdd |= FWL_STYLEEXT_EDT_Justified; 441 442 m_pEdit->ModifyStylesEx(dwAdd, FWL_STYLEEXT_EDT_HAlignMask | 443 FWL_STYLEEXT_EDT_HAlignModeMask | 444 FWL_STYLEEXT_EDT_VAlignMask); 445} 446 447void CFWL_ComboBox::ResetListItemAlignment() { 448 if (!m_pListBox) 449 return; 450 451 uint32_t dwAdd = 0; 452 switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_ListItemAlignMask) { 453 case FWL_STYLEEXT_CMB_ListItemCenterAlign: { 454 dwAdd |= FWL_STYLEEXT_LTB_CenterAlign; 455 break; 456 } 457 default: { 458 dwAdd |= FWL_STYLEEXT_LTB_LeftAlign; 459 break; 460 } 461 } 462 m_pListBox->ModifyStylesEx(dwAdd, FWL_STYLEEXT_CMB_ListItemAlignMask); 463} 464 465void CFWL_ComboBox::ProcessSelChanged(bool bLButtonUp) { 466 m_iCurSel = m_pListBox->GetItemIndex(this, m_pListBox->GetSelItem(0)); 467 if (!IsDropDownStyle()) { 468 RepaintRect(m_rtClient); 469 return; 470 } 471 472 CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel); 473 if (!hItem) 474 return; 475 if (m_pEdit) { 476 m_pEdit->SetText(hItem->GetText()); 477 m_pEdit->Update(); 478 m_pEdit->SetSelected(); 479 } 480 481 CFWL_EventSelectChanged ev(this); 482 ev.bLButtonUp = bLButtonUp; 483 DispatchEvent(&ev); 484} 485 486void CFWL_ComboBox::InitProxyForm() { 487 if (m_pComboBoxProxy) 488 return; 489 if (!m_pListBox) 490 return; 491 492 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 493 prop->m_pOwner = this; 494 prop->m_dwStyles = FWL_WGTSTYLE_Popup; 495 prop->m_dwStates = FWL_WGTSTATE_Invisible; 496 497 // TODO(dsinclair): Does this leak? I don't see a delete, but I'm not sure 498 // if the SetParent call is going to transfer ownership. 499 m_pComboBoxProxy = new CFWL_ComboBoxProxy(this, m_pOwnerApp, std::move(prop), 500 m_pListBox.get()); 501 m_pListBox->SetParent(m_pComboBoxProxy); 502} 503 504void CFWL_ComboBox::DisForm_InitComboList() { 505 if (m_pListBox) 506 return; 507 508 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 509 prop->m_pParent = this; 510 prop->m_dwStyles = FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll; 511 prop->m_dwStates = FWL_WGTSTATE_Invisible; 512 prop->m_pThemeProvider = m_pProperties->m_pThemeProvider; 513 m_pListBox = 514 pdfium::MakeUnique<CFWL_ComboList>(m_pOwnerApp, std::move(prop), this); 515} 516 517void CFWL_ComboBox::DisForm_InitComboEdit() { 518 if (m_pEdit) 519 return; 520 521 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 522 prop->m_pParent = this; 523 prop->m_pThemeProvider = m_pProperties->m_pThemeProvider; 524 525 m_pEdit = 526 pdfium::MakeUnique<CFWL_ComboEdit>(m_pOwnerApp, std::move(prop), this); 527 m_pEdit->SetOuter(this); 528} 529 530void CFWL_ComboBox::DisForm_ShowDropList(bool bActivate) { 531 if (DisForm_IsDropListVisible() == bActivate) 532 return; 533 534 if (bActivate) { 535 CFWL_Event preEvent(CFWL_Event::Type::PreDropDown, this); 536 DispatchEvent(&preEvent); 537 538 CFWL_ComboList* pComboList = m_pListBox.get(); 539 int32_t iItems = pComboList->CountItems(nullptr); 540 if (iItems < 1) 541 return; 542 543 ResetListItemAlignment(); 544 pComboList->ChangeSelected(m_iCurSel); 545 546 FX_FLOAT fItemHeight = pComboList->CalcItemHeight(); 547 FX_FLOAT fBorder = GetBorderSize(true); 548 FX_FLOAT fPopupMin = 0.0f; 549 if (iItems > 3) 550 fPopupMin = fItemHeight * 3 + fBorder * 2; 551 552 FX_FLOAT fPopupMax = fItemHeight * iItems + fBorder * 2; 553 CFX_RectF rtList(m_rtClient.left, 0, m_pProperties->m_rtWidget.width, 0); 554 GetPopupPos(fPopupMin, fPopupMax, m_pProperties->m_rtWidget, rtList); 555 556 m_pListBox->SetWidgetRect(rtList); 557 m_pListBox->Update(); 558 } else { 559 SetFocus(true); 560 } 561 562 if (bActivate) { 563 m_pListBox->RemoveStates(FWL_WGTSTATE_Invisible); 564 CFWL_Event postEvent(CFWL_Event::Type::PostDropDown, this); 565 DispatchEvent(&postEvent); 566 } else { 567 m_pListBox->SetStates(FWL_WGTSTATE_Invisible); 568 } 569 570 CFX_RectF rect = m_pListBox->GetWidgetRect(); 571 rect.Inflate(2, 2); 572 RepaintRect(rect); 573} 574 575void CFWL_ComboBox::DisForm_ModifyStylesEx(uint32_t dwStylesExAdded, 576 uint32_t dwStylesExRemoved) { 577 if (!m_pEdit) 578 DisForm_InitComboEdit(); 579 580 bool bAddDropDown = !!(dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown); 581 bool bDelDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown); 582 583 dwStylesExRemoved &= ~FWL_STYLEEXT_CMB_DropDown; 584 m_pProperties->m_dwStyleExes |= FWL_STYLEEXT_CMB_DropDown; 585 586 if (bAddDropDown) 587 m_pEdit->ModifyStylesEx(0, FWL_STYLEEXT_EDT_ReadOnly); 588 else if (bDelDropDown) 589 m_pEdit->ModifyStylesEx(FWL_STYLEEXT_EDT_ReadOnly, 0); 590 CFWL_Widget::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved); 591} 592 593void CFWL_ComboBox::DisForm_Update() { 594 if (m_iLock) 595 return; 596 if (m_pEdit) 597 ResetEditAlignment(); 598 ResetTheme(); 599 Layout(); 600} 601 602FWL_WidgetHit CFWL_ComboBox::DisForm_HitTest(const CFX_PointF& point) { 603 CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width - m_rtBtn.width, 604 m_pProperties->m_rtWidget.height); 605 if (rect.Contains(point)) 606 return FWL_WidgetHit::Edit; 607 if (m_rtBtn.Contains(point)) 608 return FWL_WidgetHit::Client; 609 if (DisForm_IsDropListVisible()) { 610 rect = m_pListBox->GetWidgetRect(); 611 if (rect.Contains(point)) 612 return FWL_WidgetHit::Client; 613 } 614 return FWL_WidgetHit::Unknown; 615} 616 617void CFWL_ComboBox::DisForm_DrawWidget(CFX_Graphics* pGraphics, 618 const CFX_Matrix* pMatrix) { 619 IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider; 620 CFX_Matrix mtOrg(1, 0, 0, 1, 0, 0); 621 if (pMatrix) 622 mtOrg = *pMatrix; 623 624 pGraphics->SaveGraphState(); 625 pGraphics->ConcatMatrix(&mtOrg); 626 if (!m_rtBtn.IsEmpty(0.1f)) { 627 CFWL_ThemeBackground param; 628 param.m_pWidget = this; 629 param.m_iPart = CFWL_Part::DropDownButton; 630 param.m_dwStates = m_iBtnState; 631 param.m_pGraphics = pGraphics; 632 param.m_rtPart = m_rtBtn; 633 pTheme->DrawBackground(¶m); 634 } 635 pGraphics->RestoreGraphState(); 636 637 if (m_pEdit) { 638 CFX_RectF rtEdit = m_pEdit->GetWidgetRect(); 639 CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top); 640 mt.Concat(mtOrg); 641 m_pEdit->DrawWidget(pGraphics, &mt); 642 } 643 if (m_pListBox && DisForm_IsDropListVisible()) { 644 CFX_RectF rtList = m_pListBox->GetWidgetRect(); 645 CFX_Matrix mt(1, 0, 0, 1, rtList.left, rtList.top); 646 mt.Concat(mtOrg); 647 m_pListBox->DrawWidget(pGraphics, &mt); 648 } 649} 650 651CFX_RectF CFWL_ComboBox::DisForm_GetBBox() const { 652 CFX_RectF rect = m_pProperties->m_rtWidget; 653 if (!m_pListBox || !DisForm_IsDropListVisible()) 654 return rect; 655 656 CFX_RectF rtList = m_pListBox->GetWidgetRect(); 657 rtList.Offset(rect.left, rect.top); 658 rect.Union(rtList); 659 return rect; 660} 661 662void CFWL_ComboBox::DisForm_Layout() { 663 m_rtClient = GetClientRect(); 664 m_rtContent = m_rtClient; 665 IFWL_ThemeProvider* theme = GetAvailableTheme(); 666 if (!theme) 667 return; 668 669 FX_FLOAT borderWidth = 1; 670 FX_FLOAT fBtn = theme->GetScrollBarWidth(); 671 if (!(GetStylesEx() & FWL_STYLEEXT_CMB_ReadOnly)) { 672 m_rtBtn = 673 CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top + borderWidth, 674 fBtn - borderWidth, m_rtClient.height - 2 * borderWidth); 675 } 676 677 CFWL_ThemePart part; 678 part.m_pWidget = this; 679 CFX_RectF pUIMargin = theme->GetUIMargin(&part); 680 m_rtContent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width, 681 pUIMargin.height); 682 683 if (!IsDropDownStyle() || !m_pEdit) 684 return; 685 686 CFX_RectF rtEdit(m_rtContent.left, m_rtContent.top, m_rtContent.width - fBtn, 687 m_rtContent.height); 688 m_pEdit->SetWidgetRect(rtEdit); 689 690 if (m_iCurSel >= 0) { 691 CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel); 692 m_pEdit->LockUpdate(); 693 m_pEdit->SetText(hItem ? hItem->GetText() : L""); 694 m_pEdit->UnlockUpdate(); 695 } 696 m_pEdit->Update(); 697} 698 699void CFWL_ComboBox::OnProcessMessage(CFWL_Message* pMessage) { 700 if (m_pWidgetMgr->IsFormDisabled()) { 701 DisForm_OnProcessMessage(pMessage); 702 return; 703 } 704 if (!pMessage) 705 return; 706 707 switch (pMessage->GetType()) { 708 case CFWL_Message::Type::SetFocus: 709 OnFocusChanged(pMessage, true); 710 break; 711 case CFWL_Message::Type::KillFocus: 712 OnFocusChanged(pMessage, false); 713 break; 714 case CFWL_Message::Type::Mouse: { 715 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage); 716 switch (pMsg->m_dwCmd) { 717 case FWL_MouseCommand::LeftButtonDown: 718 OnLButtonDown(pMsg); 719 break; 720 case FWL_MouseCommand::LeftButtonUp: 721 OnLButtonUp(pMsg); 722 break; 723 case FWL_MouseCommand::Move: 724 OnMouseMove(pMsg); 725 break; 726 case FWL_MouseCommand::Leave: 727 OnMouseLeave(pMsg); 728 break; 729 default: 730 break; 731 } 732 break; 733 } 734 case CFWL_Message::Type::Key: 735 OnKey(static_cast<CFWL_MessageKey*>(pMessage)); 736 break; 737 default: 738 break; 739 } 740 741 CFWL_Widget::OnProcessMessage(pMessage); 742} 743 744void CFWL_ComboBox::OnProcessEvent(CFWL_Event* pEvent) { 745 CFWL_Event::Type type = pEvent->GetType(); 746 if (type == CFWL_Event::Type::Scroll) { 747 CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent); 748 CFWL_EventScroll pScrollEv(this); 749 pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode; 750 pScrollEv.m_fPos = pScrollEvent->m_fPos; 751 DispatchEvent(&pScrollEv); 752 } else if (type == CFWL_Event::Type::TextChanged) { 753 CFWL_Event pTemp(CFWL_Event::Type::EditChanged, this); 754 DispatchEvent(&pTemp); 755 } 756} 757 758void CFWL_ComboBox::OnDrawWidget(CFX_Graphics* pGraphics, 759 const CFX_Matrix* pMatrix) { 760 DrawWidget(pGraphics, pMatrix); 761} 762 763void CFWL_ComboBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) { 764 if (bSet) { 765 m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused; 766 if (IsDropDownStyle() && pMsg->m_pSrcTarget != m_pListBox.get()) { 767 if (!m_pEdit) 768 return; 769 m_pEdit->SetSelected(); 770 return; 771 } 772 773 RepaintRect(m_rtClient); 774 return; 775 } 776 777 m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused; 778 if (!IsDropDownStyle() || pMsg->m_pDstTarget == m_pListBox.get()) { 779 RepaintRect(m_rtClient); 780 return; 781 } 782 if (!m_pEdit) 783 return; 784 785 m_pEdit->FlagFocus(false); 786 m_pEdit->ClearSelected(); 787} 788 789void CFWL_ComboBox::OnLButtonDown(CFWL_MessageMouse* pMsg) { 790 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 791 return; 792 793 CFX_RectF& rtBtn = IsDropDownStyle() ? m_rtBtn : m_rtClient; 794 if (!rtBtn.Contains(pMsg->m_pos)) 795 return; 796 797 if (IsDropDownStyle() && m_pEdit) 798 MatchEditText(); 799 800 m_bLButtonDown = true; 801 m_iBtnState = CFWL_PartState_Pressed; 802 RepaintRect(m_rtClient); 803 804 ShowDropList(true); 805 m_iBtnState = CFWL_PartState_Normal; 806 RepaintRect(m_rtClient); 807} 808 809void CFWL_ComboBox::OnLButtonUp(CFWL_MessageMouse* pMsg) { 810 m_bLButtonDown = false; 811 if (m_rtBtn.Contains(pMsg->m_pos)) 812 m_iBtnState = CFWL_PartState_Hovered; 813 else 814 m_iBtnState = CFWL_PartState_Normal; 815 816 RepaintRect(m_rtBtn); 817} 818 819void CFWL_ComboBox::OnMouseMove(CFWL_MessageMouse* pMsg) { 820 int32_t iOldState = m_iBtnState; 821 if (m_rtBtn.Contains(pMsg->m_pos)) { 822 m_iBtnState = 823 m_bLButtonDown ? CFWL_PartState_Pressed : CFWL_PartState_Hovered; 824 } else { 825 m_iBtnState = CFWL_PartState_Normal; 826 } 827 if ((iOldState != m_iBtnState) && 828 !((m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) == 829 FWL_WGTSTATE_Disabled)) { 830 RepaintRect(m_rtBtn); 831 } 832} 833 834void CFWL_ComboBox::OnMouseLeave(CFWL_MessageMouse* pMsg) { 835 if (!IsDropListVisible() && 836 !((m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) == 837 FWL_WGTSTATE_Disabled)) { 838 m_iBtnState = CFWL_PartState_Normal; 839 RepaintRect(m_rtBtn); 840 } 841} 842 843void CFWL_ComboBox::OnKey(CFWL_MessageKey* pMsg) { 844 uint32_t dwKeyCode = pMsg->m_dwKeyCode; 845 if (dwKeyCode == FWL_VKEY_Tab) 846 return; 847 if (pMsg->m_pDstTarget == this) 848 DoSubCtrlKey(pMsg); 849} 850 851void CFWL_ComboBox::DoSubCtrlKey(CFWL_MessageKey* pMsg) { 852 uint32_t dwKeyCode = pMsg->m_dwKeyCode; 853 const bool bUp = dwKeyCode == FWL_VKEY_Up; 854 const bool bDown = dwKeyCode == FWL_VKEY_Down; 855 if (bUp || bDown) { 856 int32_t iCount = m_pListBox->CountItems(nullptr); 857 if (iCount < 1) 858 return; 859 860 bool bMatchEqual = false; 861 int32_t iCurSel = m_iCurSel; 862 bool bDropDown = IsDropDownStyle(); 863 if (bDropDown && m_pEdit) { 864 CFX_WideString wsText = m_pEdit->GetText(); 865 iCurSel = m_pListBox->MatchItem(wsText); 866 if (iCurSel >= 0) { 867 CFWL_ListItem* hItem = m_pListBox->GetItem(this, iCurSel); 868 bMatchEqual = wsText == (hItem ? hItem->GetText() : L""); 869 } 870 } 871 if (iCurSel < 0) { 872 iCurSel = 0; 873 } else if (!bDropDown || bMatchEqual) { 874 if ((bUp && iCurSel == 0) || (bDown && iCurSel == iCount - 1)) 875 return; 876 if (bUp) 877 iCurSel--; 878 else 879 iCurSel++; 880 } 881 m_iCurSel = iCurSel; 882 if (bDropDown && m_pEdit) 883 SyncEditText(m_iCurSel); 884 else 885 RepaintRect(m_rtClient); 886 return; 887 } 888 889 if (IsDropDownStyle()) 890 m_pEdit->GetDelegate()->OnProcessMessage(pMsg); 891} 892 893void CFWL_ComboBox::DisForm_OnProcessMessage(CFWL_Message* pMessage) { 894 if (!pMessage) 895 return; 896 897 bool backDefault = true; 898 switch (pMessage->GetType()) { 899 case CFWL_Message::Type::SetFocus: { 900 backDefault = false; 901 DisForm_OnFocusChanged(pMessage, true); 902 break; 903 } 904 case CFWL_Message::Type::KillFocus: { 905 backDefault = false; 906 DisForm_OnFocusChanged(pMessage, false); 907 break; 908 } 909 case CFWL_Message::Type::Mouse: { 910 backDefault = false; 911 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage); 912 switch (pMsg->m_dwCmd) { 913 case FWL_MouseCommand::LeftButtonDown: 914 DisForm_OnLButtonDown(pMsg); 915 break; 916 case FWL_MouseCommand::LeftButtonUp: 917 OnLButtonUp(pMsg); 918 break; 919 default: 920 break; 921 } 922 break; 923 } 924 case CFWL_Message::Type::Key: { 925 backDefault = false; 926 CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage); 927 if (pKey->m_dwCmd == FWL_KeyCommand::KeyUp) 928 break; 929 if (DisForm_IsDropListVisible() && 930 pKey->m_dwCmd == FWL_KeyCommand::KeyDown) { 931 bool bListKey = pKey->m_dwKeyCode == FWL_VKEY_Up || 932 pKey->m_dwKeyCode == FWL_VKEY_Down || 933 pKey->m_dwKeyCode == FWL_VKEY_Return || 934 pKey->m_dwKeyCode == FWL_VKEY_Escape; 935 if (bListKey) { 936 m_pListBox->GetDelegate()->OnProcessMessage(pMessage); 937 break; 938 } 939 } 940 DisForm_OnKey(pKey); 941 break; 942 } 943 default: 944 break; 945 } 946 if (backDefault) 947 CFWL_Widget::OnProcessMessage(pMessage); 948} 949 950void CFWL_ComboBox::DisForm_OnLButtonDown(CFWL_MessageMouse* pMsg) { 951 bool bDropDown = DisForm_IsDropListVisible(); 952 CFX_RectF& rtBtn = bDropDown ? m_rtBtn : m_rtClient; 953 if (!rtBtn.Contains(pMsg->m_pos)) 954 return; 955 956 if (DisForm_IsDropListVisible()) { 957 DisForm_ShowDropList(false); 958 return; 959 } 960 if (m_pEdit) 961 MatchEditText(); 962 DisForm_ShowDropList(true); 963} 964 965void CFWL_ComboBox::DisForm_OnFocusChanged(CFWL_Message* pMsg, bool bSet) { 966 if (bSet) { 967 m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused; 968 if ((m_pEdit->GetStates() & FWL_WGTSTATE_Focused) == 0) { 969 CFWL_MessageSetFocus msg(nullptr, m_pEdit.get()); 970 m_pEdit->GetDelegate()->OnProcessMessage(&msg); 971 } 972 } else { 973 m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused; 974 DisForm_ShowDropList(false); 975 CFWL_MessageKillFocus msg(m_pEdit.get()); 976 m_pEdit->GetDelegate()->OnProcessMessage(&msg); 977 } 978} 979 980void CFWL_ComboBox::DisForm_OnKey(CFWL_MessageKey* pMsg) { 981 uint32_t dwKeyCode = pMsg->m_dwKeyCode; 982 const bool bUp = dwKeyCode == FWL_VKEY_Up; 983 const bool bDown = dwKeyCode == FWL_VKEY_Down; 984 if (bUp || bDown) { 985 CFWL_ComboList* pComboList = m_pListBox.get(); 986 int32_t iCount = pComboList->CountItems(nullptr); 987 if (iCount < 1) 988 return; 989 990 bool bMatchEqual = false; 991 int32_t iCurSel = m_iCurSel; 992 if (m_pEdit) { 993 CFX_WideString wsText = m_pEdit->GetText(); 994 iCurSel = pComboList->MatchItem(wsText); 995 if (iCurSel >= 0) { 996 CFWL_ListItem* item = m_pListBox->GetSelItem(iCurSel); 997 bMatchEqual = wsText == (item ? item->GetText() : L""); 998 } 999 } 1000 if (iCurSel < 0) { 1001 iCurSel = 0; 1002 } else if (bMatchEqual) { 1003 if ((bUp && iCurSel == 0) || (bDown && iCurSel == iCount - 1)) 1004 return; 1005 if (bUp) 1006 iCurSel--; 1007 else 1008 iCurSel++; 1009 } 1010 m_iCurSel = iCurSel; 1011 SyncEditText(m_iCurSel); 1012 return; 1013 } 1014 if (m_pEdit) 1015 m_pEdit->GetDelegate()->OnProcessMessage(pMsg); 1016} 1017