Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

Code Habit

윈도우 위에 마우스가 위치 했는 지 검사하기 ( Timer 활용 ) 본문

카테고리 없음

윈도우 위에 마우스가 위치 했는 지 검사하기 ( Timer 활용 )

코드베어 2020. 6. 17. 16:04

윈도우 위에 마우스가 올라가 있는 상태를 보통 'hover'로 정의한다. hover의 사전적 의미로는 '맴돌다'로 윈도우나 컨트롤 위에 마우스가 위치해 있는 상태를 말한다. 보통 이 상태를 체크하고 싶을 때 'TrackMouseEvent'를 이용한다. 하지만 이런식으로 처리하면 윈도우 안에 자식 컨트롤이 있을 경우 원하는데로 결과가 안나올 수 가 있다. 왜냐하면 패널 안에 있는 자식 컨트롤 위로 마우스 포인터가 이동할때 부모 윈도우에 'WM_MOUSELEAVE' 메시지가 호출되어 마우스가 윈도우 영역을 벗어났다고 인식할 수 있기 때문이다.

 

그래서 여러개의 자식 컨트롤들을 가지고 있는 윈도우의 Hover상태를 체크 하기 위해서는 다른 식의 접근이 필요하다. 필자는 Timer를 활용해 보겠다.

 

다음은 WTL기반으로 구현한 예제 소스이다. (원본 소스가 많은 관계로 필요한 부분만 적겠다.)

// windowpanel.h
 
class CWindowPanel : public CWindowImpl< CWindowPanel>
{
    CWindowPanel();        
    ~CWindowPanel(){};
private:
    BEGIN_MSG_MAP( CWindowPanel )
        MESSAGE_HANDLER( WM_CREATE, OnCreate )
        MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
        MESSAGE_HANDLER( WM_PAINT,  OnPaint )
        MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove )
        MESSAGE_HANDLER( WM_MOUSELEAVE, OnMouseLeave )
        MESSAGE_HANDLER( WM_TIMER, OnTimer )
    END_MSG_MAP()
 
    LRESULT     OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
    LRESULT        OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); 
    LRESULT        OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT        OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT        OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT        OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
 
    BOOL m_bHover;    // Hover 상태인지 체크
};
 
 
// windowpanel.cpp
CWindowPanel::CWindowPanel()
{
    m_bHover = FALSE;
}
 
LRESULT CWindowPanel::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    this->SetTimer( TMID_HOVER, 20 );    // 미리 정의한 TMID_HOVER로 타이머 실행
}
 
LRESULT    CWindowPanel::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    this->KillTimer( TMID_HOVER );         // 타이머 해제 
}
 
LRESULT CWindowPanel::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    ... 
    if( m_bHover ) {
        // Hover 상태를 그림
    } else {
        // Not Hover 상태를 그림
    }
 
    ...    
}
 
LRESULT CWindowPanel::OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    if!m_bHover ) {
        m_bHover = TRUE;    // Hover 상태 변경 -> IN !
    Invalidate();
}
    
    return S_OK;
}
 
LRESULT    CWindowPanel::OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    if( m_bHover ) {
        m_bHover = FALSE;     // Hover 상태 변경 -> OUT !
    Invalidate();
}
    
    return S_OK;
}
 
LRESULT    CWindowPanel::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    switch( ULONG(wParam) )
    {
    case TMID_HOVER:
        {
            POINT pt;
            RECT rect;
            
            GetCursorPos(&pt);    // 현재 마우스 포인터 위치를 구함
            GetWindowRect(&rect); // 윈도우 영역을 구함
 
            if!PtInRect(&rect, pt) ) {             // 마우스 포인터가 윈도우 영역 밖에 있는지 체크 !
                PostMessage( WM_MOUSELEAVE, 00 );    // WM_LEAVE메시지를 호출하여 m_bHover 상태값 변경 -> FALSE
            }
        }
        break;
    default:
        break;
    }
 
    return S_OK;
}

필요한 부분만 적는다고 했는데..생각보다 예제가 길다..ㅎㅎ.. 요약해보면 이렇다. 마우스가 해당 윈도우 위에서 움직이면 WM_MOUSEMOVE 메시지가 호출되는데 이 때 m_bHover 상태를 TRUE로 변경하고 Invalidate()를 호출하여 OnPaint()에서 다시 그리도록 한다. 그리고 타이머로 마우스 포인터의 위치가 윈도우 영역 위에 있는지 20 microsecond마다 체크하여 벗어나면 WM_MOUSELEAVE 메시지를 호출하여 상태 값을 변경하고 다시 그린다.