Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » Bug in CExtSpinWnd behaviour and drawing. Collapse All
Subject Author Date
Jeroen Walter Apr 11, 2011 - 3:55 AM

Hi



There is a bug in CExtSpinWnd (using profuis v2.87, but code in 2.91 is the more or less the same).

The problem:

I want to notify the user when the value associated with the CExtSpinWnd becomes too large and ask the user if he wants to continue.

I do this via the UDN_DELTAPOS notification of the spincontrol. The UDN_DELTAPOS notification should allow me to accept the position change or to reject it.



This notification is called during the OnLButtonDown handler, in the call to CSpinButtonCtrl::OnLButtonDown, right before _ProcessMouseClick is called.

Now, after I have notified the user using a modal dialog in the UDN_DELTAPOS handler, the CExtSpinWnd calls _ProcessMouseClick.

_ProcessMouseClick assumes that the mouse button is still pressed (which it isn’t as the user had to click away the message box).

Consequently, _ProcessMouseClick captures the mouse via ::SetCapture. This should not happen, as the user interaction with the spin control already has ended.

The effect is that the next mouse click always triggers the same OnLButtonDown instead of the OnLButtonUp, even if the mouse is not on the control !

This is caused by _ProcessMouseMove, as it still thinks that the mousebutton is pressed.

This causes the UDN_DELTAPOS notification to be send again, notifying the user again, which it shouldn’t.



Furthermore, the spin control is drawn incorrectly, because the hover state is not determined correctly in this scenario.



I’ve made a small fix, which hopefully is sufficient. If this is not the right way, please let me know.



_ProcessMouseMove must get the nFlags parameter from OnMouseMove and check if the buttons are still pressed.



bool CExtSpinWnd::_ProcessMouseMove(UINT nFlags, CPoint point)

{

    ASSERT_VALID( this );



    if(        CExtPopupMenuWnd::IsMenuTracking()

        ||    (! CExtPopupMenuWnd::TestHoverEnabledFromActiveHWND( GetSafeHwnd() ) )

        )

        return false;



  // FIX: check if button is still pressed.

  if (m_bButtonUpPressed || m_bButtonDownPressed)

  {

    bool pressed = nFlags & MK_LBUTTON;

    m_bButtonUpPressed &= pressed;

    m_bButtonDownPressed &= pressed;

    _ProcessMouseClick( point, false, MK_LBUTTON );

  }

 



CRect rcButtonUp, rcButtonDown;

.... rest of the original code ....

}


 

Technical Support Apr 13, 2011 - 2:20 AM

Thank you for reporting this issue. The following version of the fix does not require adding a method parameter, does not perform unnecessary button releasing event emulation and correctly handles Windows swapped mouse buttons setting:

bool CExtSpinWnd::_ProcessMouseMove( CPoint point )
{
    ASSERT_VALID( this );
    if(        CExtPopupMenuWnd::IsMenuTracking() 
        ||    (! CExtPopupMenuWnd::TestHoverEnabledFromActiveHWND( GetSafeHwnd() ) )
        )
        return false;
    if( m_bButtonUpPressed || m_bButtonDownPressed )
    {
        bool bMouseButtonsNotSwapped = ( ::GetSystemMetrics( SM_SWAPBUTTON ) != 0 ) ? false : true;
        int nMouseKey = bMouseButtonsNotSwapped ? VK_LBUTTON : VK_RBUTTON;
        bool bReallyPressed = CExtPopupMenuWnd::IsKeyPressed( nMouseKey ) ? true : false;
        if( ! bReallyPressed )
        {
            SendMessage( WM_CANCELMODE );
            return false;
        }
    }
CRect rcButtonUp, rcButtonDown;
    GetButtonRect( rcButtonUp, rcButtonDown );
bool bButtonUpHoveredPrev   = m_bButtonUpHovered;
bool bButtonDownHoveredPrev = m_bButtonDownHovered;
bool bButtonUpHoveredNext   = false;
bool bButtonDownHoveredNext = false;
    if( ( ! rcButtonUp.IsRectEmpty() ) && rcButtonUp.PtInRect( point ) )
        bButtonUpHoveredNext = true;
    if( ( ! rcButtonDown.IsRectEmpty() ) && rcButtonDown.PtInRect( point ) )
        bButtonDownHoveredNext = true;
    if( bButtonUpHoveredNext || bButtonDownHoveredNext )
    {
        if( ::GetCapture() != GetSafeHwnd() )
            ::SetCapture( GetSafeHwnd() );
    }
    else if( ( ! m_bButtonUpPressed ) && ( ! m_bButtonDownPressed) )
    {
        if( ::GetCapture() == GetSafeHwnd() )
            ::ReleaseCapture();
    }
    if( bButtonUpHoveredNext != bButtonUpHoveredPrev || bButtonDownHoveredNext != bButtonDownHoveredPrev )
    {
        bool bAnimationLocked = AnimationClient_CacheGeneratorIsLocked();
        if( ! bAnimationLocked )
        {
            AnimationClient_CacheGeneratorLock();
             AnimationClient_CacheNextStateMinInfo(
                false,
                ( bButtonUpHoveredNext || bButtonDownHoveredNext ) ? __EAPT_BY_HOVERED_STATE_TURNED_ON : __EAPT_BY_HOVERED_STATE_TURNED_OFF
                );
        }
        m_bButtonUpHovered = bButtonUpHoveredNext;
        m_bButtonDownHovered = bButtonDownHoveredNext;
        if( ! bAnimationLocked )
        {
             AnimationClient_CacheNextStateMinInfo(
                true,
                ( bButtonUpHoveredNext || bButtonDownHoveredNext ) ? __EAPT_BY_HOVERED_STATE_TURNED_ON : __EAPT_BY_HOVERED_STATE_TURNED_OFF
                );
            AnimationClient_CacheGeneratorUnlock();
        }
        Invalidate();
    }
    return true;
}

Jeroen Walter Apr 13, 2011 - 2:32 AM

Thanks, it works :)

Jeroen Walter Apr 11, 2011 - 4:00 AM

mmm


a better fix would be:


 

if (m_bButtonUpPressed || m_bButtonDownPressed)
  {
    if (!(nFlags & MK_LBUTTON))
    {
	  // Simulate button up event.
	  _ProcessMouseClick( point, false, MK_LBUTTON );
    }
  }
 


Still, I’m not sure if this is the right fix.