Home | History | Annotate | Download | only in fwl
      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(&params);
    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(&params);
    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