From 30be036c6d4990f8aa21af5b86abdc0852e686a0 Mon Sep 17 00:00:00 2001
From: Robert Roebling <robert@roebling.de>
Date: Thu, 26 Oct 2006 20:35:57 +0000
Subject: [PATCH]   Make animation of combo-popdown optional.   Replace
 wxMiniSleep() + wxYield() implemenation     by wxTimer based one.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42470 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/comboctrl.tex |   8 +++
 include/wx/combo.h          |  13 +++-
 include/wx/msw/combo.h      |  22 ++++++-
 samples/combo/combo.cpp     | 117 +++++++++++++++++++++++++++---------
 src/common/combocmn.cpp     |   3 +-
 src/msw/combo.cpp           |  95 ++++++++++++++++++-----------
 6 files changed, 192 insertions(+), 66 deletions(-)

diff --git a/docs/latex/wx/comboctrl.tex b/docs/latex/wx/comboctrl.tex
index aa8d440a6d..07d9608518 100644
--- a/docs/latex/wx/comboctrl.tex
+++ b/docs/latex/wx/comboctrl.tex
@@ -295,6 +295,14 @@ In this case, \helpref{AnimateShow}{wxcomboctrlanimateshow} must return \false.}
 \end{twocollist}
 
 
+\membersection{wxComboCtrl::EnablePopupAnimation}\label{wxcomboctrlenablepopupanimation}
+
+\func{void}{EnablePopupAnimation}{\param{bool }{enable = true}}
+
+Enables or disables popup animation, if any, depending on the value of
+the argument.
+
+
 \membersection{wxComboCtrl::GetBitmapDisabled}\label{wxcomboctrlgetbitmapdisabled}
 
 \constfunc{const wxBitmap\&}{GetBitmapDisabled}{\void}
diff --git a/include/wx/combo.h b/include/wx/combo.h
index 8aa155ed71..e8704a4f66 100644
--- a/include/wx/combo.h
+++ b/include/wx/combo.h
@@ -85,7 +85,9 @@ enum
     // Internal use: Set wxTAB_TRAVERSAL to parent when popup is dismissed
     wxCC_IFLAG_PARENT_TAB_TRAVERSAL = 0x0800,
     // Internal use: Secondary popup window type should be used (if available).
-    wxCC_IFLAG_USE_ALT_POPUP        = 0x1000
+    wxCC_IFLAG_USE_ALT_POPUP        = 0x1000,
+    // Internal use: Skip popup animation.
+    wxCC_IFLAG_DISABLE_POPUP_ANIM   = 0x2000
 };
 
 
@@ -322,6 +324,15 @@ public:
             m_iFlags &= ~wxCC_IFLAG_USE_ALT_POPUP;
     }
 
+    // Call with false to disable popup animation, if any.
+    void EnablePopupAnimation( bool enable = true )
+    {
+        if ( enable )
+            m_iFlags &= ~wxCC_IFLAG_DISABLE_POPUP_ANIM;
+        else
+            m_iFlags |= wxCC_IFLAG_DISABLE_POPUP_ANIM;
+    }
+
     //
     // Utilies needed by the popups or native implementations
     //
diff --git a/include/wx/msw/combo.h b/include/wx/msw/combo.h
index 096d0798e2..4f350d8f30 100644
--- a/include/wx/msw/combo.h
+++ b/include/wx/msw/combo.h
@@ -18,6 +18,14 @@
 
 #if wxUSE_COMBOCTRL
 
+#if !defined(__WXWINCE__) && wxUSE_TIMER
+    #include "wx/timer.h"
+    #define wxUSE_COMBOCTRL_POPUP_ANIMATION     1
+#else
+    #define wxUSE_COMBOCTRL_POPUP_ANIMATION     0
+#endif
+
+
 // ----------------------------------------------------------------------------
 // Native wxComboCtrl
 // ----------------------------------------------------------------------------
@@ -60,11 +68,15 @@ public:
     virtual ~wxComboCtrl();
 
     virtual void PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const;
-    virtual bool AnimateShow( const wxRect& rect, int flags );
     virtual bool IsKeyPopupToggle(const wxKeyEvent& event) const;
 
     static int GetFeatures() { return wxComboCtrlFeatures::All; }
 
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+    virtual bool AnimateShow( const wxRect& rect, int flags );
+    void OnTimerEvent( wxTimerEvent& event );
+#endif
+
 protected:
 
     // customization
@@ -79,6 +91,14 @@ protected:
 private:
     void Init();
 
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+    // Popup animation related
+    wxLongLong  m_animStart;
+    wxTimer     m_animTimer;
+    wxRect      m_animRect;
+    int         m_animFlags;
+#endif
+
     DECLARE_EVENT_TABLE()
 
     DECLARE_DYNAMIC_CLASS(wxComboCtrl)
diff --git a/samples/combo/combo.cpp b/samples/combo/combo.cpp
index 785a104038..e4cde90f95 100644
--- a/samples/combo/combo.cpp
+++ b/samples/combo/combo.cpp
@@ -494,65 +494,126 @@ BEGIN_EVENT_TABLE(TreeCtrlComboPopup, wxTreeCtrl)
 END_EVENT_TABLE()
 
 // ----------------------------------------------------------------------------
-// wxComboCtrl with custom popup animation
+// wxComboCtrl with custom popup animation. We use EVT_TIMER, which is quite
+// safe, but requires much more can than doing it in a single function (ie.
+// AnimateShow) and using combination of wxSleep and wxSafeYield.
 // ----------------------------------------------------------------------------
 
+#if wxUSE_TIMER
+
+#define CUSTOM_COMBOBOX_ANIMATION_DURATION  200  // In milliseconds
+
+#include "wx/timer.h"
+
 class wxComboCtrlWithCustomPopupAnim : public wxComboCtrl
 {
 public:
 
-    virtual bool AnimateShow( const wxRect& rect, int WXUNUSED(flags) )
+    virtual bool AnimateShow( const wxRect& rect, int flags )
     {
         MyFrame* myFrame = (MyFrame*) ::wxGetTopLevelParent(this);
 
         if ( !myFrame->m_cbUseAnim->GetValue() )
             return true;
 
-        int width = rect.width;
-        int height = rect.height;
-        wxBitmap bitmap( width, height, -1 );
+        m_animStart = ::wxGetLocalTimeMillis();
+        m_animRect = rect;
+        m_animFlags = flags;
+
         wxScreenDC dc;
+
+        wxBitmap bitmap( rect.width, rect.height, -1 );
         wxMemoryDC memdc( bitmap );
-        memdc.Blit( 0, 0, width, height, &dc, rect.x, rect.y );
+        memdc.Blit( 0, 0, rect.width, rect.height, &dc, rect.x, rect.y );
         memdc.SelectObject(wxNullBitmap); 
+        m_animBackBitmap = bitmap;
+
+        m_animTimer.SetOwner( this, wxID_ANY );
+        m_animTimer.Start( 10, wxTIMER_CONTINUOUS );
 
-        wxLongLong tStart = ::wxGetLocalTimeMillis();
-        const int delay = 300;
-        const int resolution = 10;
+        OnTimerEvent(*((wxTimerEvent*)NULL));  // Event is never used, so we can give NULL
+        return false;
+    }
 
-        int center_x = rect.x + (width/2);
-        int center_y = rect.y + (height/2);
+    void OnTimerEvent( wxTimerEvent& WXUNUSED(event) )
+    {
+        bool stopTimer = false;
 
-        double d_height = (double) height;
+        wxWindow* popup = GetPopupControl()->GetControl();
+        wxScreenDC dc;
+        const wxRect& rect = m_animRect;
 
-        dc.SetPen( *wxBLACK_PEN );
-        dc.SetBrush( *wxTRANSPARENT_BRUSH );
-        for (;;)
+        // Popup was hidden before it was fully shown?
+        if ( IsPopupWindowState(Hidden) )
+        {
+            stopTimer = true;
+        }
+        else
         {
             wxLongLong t = ::wxGetLocalTimeMillis();
-            int pos = (int) (t-tStart).GetLo();
-            if ( pos > delay )
-                break;
 
-            int w = (((pos*256)/delay)*width)/256;
+            int pos = (int) (t-m_animStart).GetLo();
+            if ( pos < CUSTOM_COMBOBOX_ANIMATION_DURATION )
+            {
+                //
+                // Actual animation happens here
+                //
+                int width = rect.width;
+                int height = rect.height;
+
+                int center_x = rect.x + (width/2);
+                int center_y = rect.y + (height/2);
+
+                double d_height = (double) height;
+
+                dc.SetPen( *wxBLACK_PEN );
+                dc.SetBrush( *wxTRANSPARENT_BRUSH );
 
-            double ratio = ((double)w / (double)width);
-            int h = (int)(d_height * ratio);
-            dc.DrawRectangle( center_x - w/2, center_y - h/2, w, h );
-            wxMilliSleep( resolution );
-            wxYield();
-            dc.DrawBitmap( bitmap, rect.x, rect.y );
+                int w = (((pos*256)/CUSTOM_COMBOBOX_ANIMATION_DURATION)*width)/256;
 
-            if ( IsPopupWindowState(Hidden) )
-                return true;
+                double ratio = ((double)w / (double)width);
+                int h = (int)(d_height * ratio);
+                dc.DrawBitmap( m_animBackBitmap, rect.x, rect.y );
+                dc.DrawRectangle( center_x - w/2, center_y - h/2, w, h );
+            }
+            else
+            {
+                stopTimer = true;
+            }
         }
 
-        return true;
+        if ( stopTimer )
+        {
+            dc.DrawBitmap( m_animBackBitmap, rect.x, rect.y );
+            popup->Move( 0, 0 );
+            m_animTimer.Stop();
+            DoShowPopup( m_animRect, m_animFlags );
+        }
     }
 
 protected:
+
+    // Popup animation related
+    wxLongLong  m_animStart;
+    wxTimer     m_animTimer;
+    wxRect      m_animRect;
+    wxBitmap    m_animBackBitmap;
+    int         m_animFlags;
+
+private:
+    DECLARE_EVENT_TABLE()
 };
 
+BEGIN_EVENT_TABLE(wxComboCtrlWithCustomPopupAnim, wxComboCtrl)
+    EVT_TIMER(wxID_ANY, wxComboCtrlWithCustomPopupAnim::OnTimerEvent)
+END_EVENT_TABLE()
+
+#else
+
+#define wxComboCtrlWithCustomPopupAnim wxComboCtrl
+
+#endif
+
 // ----------------------------------------------------------------------------
 // wxComboCtrl with entirely custom button action (opens file dialog)
 // ----------------------------------------------------------------------------
diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp
index 6139dfc179..bcfbb96bfd 100644
--- a/src/common/combocmn.cpp
+++ b/src/common/combocmn.cpp
@@ -1870,7 +1870,8 @@ void wxComboCtrlBase::ShowPopup()
     wxRect popupWinRect( popupX, popupY, szp.x, szp.y );
 
     m_popup = popup;
-    if ( AnimateShow( popupWinRect, showFlags ) )
+    if ( (m_iFlags & wxCC_IFLAG_DISABLE_POPUP_ANIM) ||
+         AnimateShow( popupWinRect, showFlags ) )
     {
         DoShowPopup( popupWinRect, showFlags );
     }
diff --git a/src/msw/combo.cpp b/src/msw/combo.cpp
index 822eaf453c..e784c4a1cd 100644
--- a/src/msw/combo.cpp
+++ b/src/msw/combo.cpp
@@ -70,8 +70,13 @@
 #define TEXTCTRLXADJUST_CLASSIC     1
 #define TEXTCTRLYADJUST_CLASSIC     2
 
+#define COMBOBOX_ANIMATION_RESOLUTION   10
+
 #define COMBOBOX_ANIMATION_DURATION     200  // In milliseconds
 
+#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM    (1<<26)
+
+
 // ============================================================================
 // implementation
 // ============================================================================
@@ -80,6 +85,7 @@
 BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
     EVT_PAINT(wxComboCtrl::OnPaintEvent)
     EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
+    EVT_TIMER(wxID_ANY, wxComboCtrl::OnTimerEvent)
 END_EVENT_TABLE()
 
 
@@ -530,7 +536,7 @@ void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
 
 }
 
-#if !defined(__WXWINCE__)
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
 static wxUint32 GetUserPreferencesMask()
 {
     static wxUint32 userPreferencesMask = 0;
@@ -559,64 +565,83 @@ static wxUint32 GetUserPreferencesMask()
 }
 #endif
 
-bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+void wxComboCtrl::OnTimerEvent( wxTimerEvent& WXUNUSED(event) )
 {
-#if !defined(__WXWINCE__)
-    if ( GetUserPreferencesMask() & (1<<26) )
-    {
-        wxLongLong tStart = ::wxGetLocalTimeMillis();
+    bool stopTimer = false;
 
-        int height = rect.height;
+    wxWindow* popup = GetPopupControl()->GetControl();
 
+    // Popup was hidden before it was fully shown?
+    if ( IsPopupWindowState(Hidden) )
+    {
+        stopTimer = true;
+    }
+    else
+    {
+        wxLongLong t = ::wxGetLocalTimeMillis();
+        const wxRect& rect = m_animRect;
         wxWindow* win = GetPopupWindow();
-        wxWindow* popup = GetPopupControl()->GetControl();
-
-        const int delay = COMBOBOX_ANIMATION_DURATION;
-        const int resolution = 10;
-        int h0 = popup->GetSize().y;
-
-        win->SetSize( rect.x, rect.y, rect.width, 0 );
-        win->Show();
 
-        for (;;)
+        int pos = (int) (t-m_animStart).GetLo();
+        if ( pos < COMBOBOX_ANIMATION_DURATION )
         {
-            wxLongLong t = ::wxGetLocalTimeMillis();
-            int pos = (int) (t-tStart).GetLo();
-            if ( pos > delay )
-                break;
-
-            int h = (((pos*256)/delay)*height)/256;
-            int y = (h0 - h);
+            int height = rect.height;
+            //int h0 = rect.height;
+            int h = (((pos*256)/COMBOBOX_ANIMATION_DURATION)*height)/256;
+            int y = (height - h);
             if ( y < 0 )
                 y = 0;
 
-            if ( flags & ShowAbove )
+            if ( m_animFlags & ShowAbove )
             {
-                win->SetSize( rect.x, rect.y + h0 - h, rect.width, h );
+                win->SetSize( rect.x, rect.y + height - h, rect.width, h );
             }
             else
             {
                 popup->Move( 0, -y );
                 win->SetSize( rect.x, rect.y, rect.width, h );
             }
-
-            wxMilliSleep( resolution );
-            wxYield();
-
-            // Popup was hidden before it was fully shown?
-            if ( IsPopupWindowState(Hidden) )
-                return true;
         }
+        else
+        {
+            stopTimer = true;
+        }
+    }
 
+    if ( stopTimer )
+    {
         popup->Move( 0, 0 );
+        m_animTimer.Stop();
+        DoShowPopup( m_animRect, m_animFlags );
     }
-#else
-    wxUnusedVar(rect);
-    wxUnusedVar(flags);
+}
 #endif
 
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
+{
+    if ( GetUserPreferencesMask() & wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM )
+    {
+        m_animStart = ::wxGetLocalTimeMillis();
+        m_animRect = rect;
+        m_animFlags = flags;
+
+        wxWindow* win = GetPopupWindow();
+        win->SetSize( rect.x, rect.y, rect.width, 0 );
+        win->Show();
+
+        m_animTimer.SetOwner( this, wxID_ANY );
+        m_animTimer.Start( COMBOBOX_ANIMATION_RESOLUTION, wxTIMER_CONTINUOUS );
+
+        OnTimerEvent(*((wxTimerEvent*)NULL));  // Event is never used, so we can give NULL
+
+        return false;
+    }
+
     return true;
 }
+#endif
 
 wxCoord wxComboCtrl::GetNativeTextIndent() const
 {
-- 
2.45.2

