VS2008의 MFC 9.0에는 주로 Vista를 위한 메시지가 추가되었다.
그러나 아주아주 약간은 예전의 케케묵은 가려움을 긁어주는 메시지 핸들러가 추가되었는데,
그중 하나로 마우스 호버와 마우스 리브 메시지가 제공된다.
호버(HOVER)라 특정 윈도우에 마우스가 올라갔다는 메시지로 WM_MOUSEHOVER 를 발생시키며
리브(LEAVE)는 마우스가 떠날때 발생하는 메시지로 WM_MOUSELEAVE 메시지를 발생시킨다.
예전에는 이 두 메시지가 크게 알려지지 않았다.
그래서 SetCapture()를 이용해 마우스를 추적하거나 심지어 후킹(Hooking)까지 구현하여 원하는 기능을 만드는 사례도 심심찮게 보였다.
하지만 이 호버링을 이용하면 꽤 재미있는 예제들을 만들수가 있는데,
그중 하나가 페이드 윈도우(Fade Window)이다.
페이드 윈도우란 주로 메신저를 켤때 광고창으로 이용되는 사례가 많다.
아래 짤방처럼 창이 뜨고 몇초후면 서서히 엷여지며 사라지게 된다.
그러다가 마우스가 올라가면 다시 윈도우는 진해(?)진다.
MFC 9.0에 호버링 핸들러가 추가된 기념으로 MFC 페이드 윈도우를 만들어보자.
윈도우 기반 App라면 UI의 다양한 곳에서 적용될 수 있을 것이다.
1) 호버링 준비
먼저 MFC라면 WM_MOUSEMOVE, WM_MOUSEHOVER, WM_MOUSELEAVE 메시지를 함수로 만든다.
왜 WM_MOUSEMOVE까지 함수를 만들어야 할까?
아쉽게도 호버와 리브는 단순히 메시지 함수를 만들었다고 작동하지 않는다.
TRACKMOUSEEVENT 구조체에 TME_LEAVE|TME_HOVER 를 등록하고,
TrackMouseEvent() 함수를 호출해야 마우스 추적이 시작된다.
잊지말도록!
일단 이것으로 호버링은 구현되었다.
이제 점점 사라지는 페이드를 구현해보자.
2) 페이드 윈도우는 레이어드 윈도우
여기서 한가지 기억할 것은 페이드 기능은 원래 윈도우에 없으며,
윈도우 2000부터 지원되는 레이어드 윈도우(Layered Window) 속성을 이용하여 투명도를 점차 변화시키는 것이 핵심이다.
윈도우의 버전을 알기 위햐서는 GetVersionEx()을 이용하면 되며
참고로 윈도우 2000은 윈도우 버전 5.0이며 XP는 5.1 이다.
윈도우 Vista는 6.0.6000이니 윈도우2000에서 XP로 갈때에는 고작 0.1만큼 버전업되었지만
비스타는 1.0 이상 업그레이드 된셈이다.
이때 사용되는 함수가 바로 SetLayeredWindowAttributes()이다.
SetLayeredWindowAttributes()는 적어도 VC++ 6.0에는 제공되지 않아 USER32.DLL에서 GetProcAddress()로 꺼내와 사용했었다.
VS2008에서는 afxwin.h에서 당당히 제공되고 있지만 이 코드에서는 예전에 쓰던 방식을 그대로 사용하였다.
참고로 아래는 afxwin.h에서 SetLayeredWindowAttributes()의 함수 버전을 define 한 부분으로 윈도우 2000과 윈도우 XP에서는 인자의 전달방식이 미묘하게(포인터) 다름을 확인 할수 있다.
// Layered Window
#if(_WIN32_WINNT >= 0x0500) // 윈도우 2000이라면
BOOL SetLayeredWindowAttributes(COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
BOOL UpdateLayeredWindow(CDC* pDCDst, POINT *pptDst, SIZE *psize,
CDC* pDCSrc, POINT *pptSrc, COLORREF crKey, BLENDFUNCTION *pblend, DWORD dwFlags);
#endif // _WIN32_WINNT >= 0x0500
#if(_WIN32_WINNT >= 0x0501) // 윈도우 XP 이상이라면
BOOL GetLayeredWindowAttributes(COLORREF *pcrKey, BYTE *pbAlpha, DWORD *pdwFlags) const;
#endif // _WIN32_WINNT >= 0x0501
아뭏든 USER32.DLL에서 SetLayeredWindowAttributes() 포인터를 얻어오자.
HINSTANCE hmodUSER32 = LoadLibrary(_T("USER32.DLL"));
pSetLayeredWindowAttributes=(SLWA)GetProcAddress(hmodUSER32, _T("SetLayeredWindowAttributes"));
HWND hwnd = this->m_hWnd;
SetWindowLong(hwnd, GWL_EXSTYLE,GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
pSetLayeredWindowAttributes(hwnd, 0, (255 * m_iTransp) / 100, LWA_ALPHA);
위 코드에서 m_iTransp 가 바로 투명도에 해당한다. 100이면 다 보이는(?) 것이며 0이면 윈도우는 있지만 투명도 100%가 되어 보이지 않게 된다.
이 값이 점점 변해간다면 윈도우는 투명해지거나 불투명해질 것이다.
3) 타이머의 응용
타이머는 일단 세가지를 사용했다.
첫번째 타이머는 일단 페이드 윈도우가 원래 상태로 보여질 시간을 의미한다.
나타나자마자 그대로 투명화가 진행되면, 제대로 해당 윈도우를 볼 시간 조차 없으니까.
두번째 타이머는 윈도우 98을 위한 타이머이다.
윈도우 98은 레이어드 윈도우가 작동할 수 없으니 몇초후에 창을 종료해야 한다.
세번째 타이머는 실제 페이드를 진행시키는 함수이다.
그리고 중요한 것 하나.
마우스가 창위로 올라가게 되면(HOVER) 세번째 타이머를 종료시켜야 한다.
또한 마우스가 창을 떠나면(LEAVE), 첫번째 타이머는 다시 시작하게 된다.
또한 노파심에서 말하지만 타이머를 종료시키는 KillTimer()를 이용할때는
타이머 ID를 적어서는 안되며 SetTimer()의 리턴값을 이용해야 한다는 점이다.
(ID로 타이머를 죽이는 것은 상당히 위험한 코드이다.)
4) 주요 코드 보기
이제 모든 것이 합쳐진 부분이다.
이 코드는 최적화(?)라던가 모범 코드 사례는 결코 아니며 테스트를 위한 목적으로 되는대로 만든 소스라는 점을 잊지 말길 바란다.
void CMyDlg::OnMouseHover(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_iTransp = 100;
SLWA pSetLayeredWindowAttributes = NULL;
HINSTANCE hmodUSER32 = LoadLibrary(_T("USER32.DLL"));
pSetLayeredWindowAttributes=(SLWA)GetProcAddress(hmodUSER32, _T("SetLayeredWindowAttributes"));
//함수포인터 얻음.
HWND hwnd = this->m_hWnd;
SetWindowLong(hwnd, GWL_EXSTYLE,GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
pSetLayeredWindowAttributes(hwnd, 0, (255 * m_iTransp) / 100, LWA_ALPHA);
KillTimer(m_iTimer1);
KillTimer(m_iTimer2);
KillTimer(m_iTimer3);
CDialog::OnMouseHover(nFlags, point);
}
void CMyDlg::OnMouseLeave()
{
// TODO: Add your message handler code here and/or call default
if ( m_bIsOver2000 == TRUE )
m_iTimer1 = SetTimer(TIMER_1, 2000, NULL);
else
m_iTimer2 = SetTimer(TIMER_2, 3000, NULL);
CDialog::OnMouseLeave();
}
void CMyDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE|TME_HOVER;
tme.dwHoverTime = 1;
TrackMouseEvent(&tme);
CDialog::OnMouseMove(nFlags, point);
}
실제 위 테스트 코드가 사용된 예는 아래 짤방.
4월 22일 정기점검때 피망 웹게임에 반영될 것이다.
다이렉트X를 이용한 게임 프로그램이라도 알파값을 변경하는 방법이 다를뿐, 기본 원리는 같음을 기억하자.
혹시 VS2005 이하를 사용하는 개발자라면 ON_WM_MOUSEHOVER 같은 매크로가 없다는데 좌절할지도 모르겠다. 하지만 매크로가 VS2008에서 추가되었을뿐, 메시지는 존재한다.
헤더파일에는
afx_msg LRESULT OnMouseLeave(WPARAM wparam, LPARAM lparam);
와 같이 함수를 선언하고,
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
처럼 메시지 맵안에서 핸들러를 등록하고
LRESULT ContainerNoticeCtrl::OnMouseHover(WPARAM wparam, LPARAM lparam)
{
return TRUE;
}
LRESULT ContainerNoticeCtrl::OnMouseLeave(WPARAM wparam, LPARAM lparam)
{
return TRUE;
}
사실, VS2008 이전까지는 이렇게 사용해 왔었다.
유용한 팁이 되길~~*
