5장. 마우스와 키보드
마우스 기초 마우스 처리 윈도우 운영체제는 마우스와 관련된 모든 변화를 메시지 형태로 프로그램에 전달한다.
마우스 기초 마우스 메시지 전달 마우스 메시지는 원칙적으로 마우스 커서 밑에 있는 윈도우가 받는다. 마우스 메시지를 Frame Window 로 전달 마우스 메시지를 View 로 전달
Client 영역 마우스 메시지 Client 영역 마우스 메시지 메시지 발생 시점 WM_LBUTTONDOWN 마우스 왼쪽 버튼을 누를 때 WM_LBUTTONUP 마우스 왼쪽 버튼을 뗄 때 WM_LBUTTONDBLCLK 마우스 왼쪽 버튼을 더블 클릭할 때 WM_MBUTTONDOWN 마우스 가운데 버튼을 누를 때 WM_MBUTTONUP 마우스 가운데 버튼을 뗄 때 WM_MBUTTONDBLCLK 마우스 가운데 버튼을 더블 클릭할 때 WM_RBUTTONDOWN 마우스 오른쪽 버튼을 누를 때 WM_RBUTTONUP 마우스 오른쪽 버튼을 뗄 때 WM_RBUTTONDBLCLK 마우스 오른쪽 버튼을 더블 클릭할 때 WM_MOUSEMOVE 마우스를 움직일 때
Client 영역 마우스 메시지 마우스 왼쪽 버튼 두 번 클릭 VS 더블 클릭 메시지 발생순서 비교
Client 영역 마우스 메시지 클라이언트 영역 마우스 메시지 핸들러 메시지 메시지 맵 매크로 메시지 핸들러 WM_LBUTTONDOWN ON_WM_LBUTTONDOWN() OnLButtonDown() WM_LBUTTONUP ON_WM_LBUTTONUP() OnLButtonUp() WM_LBUTTONDBLCLK ON_WM_LBUTTONDBLCLK() OnLButtonDblClk() WM_MBUTTONDOWN ON_WM_MBUTTONDOWN() OnMButtonDown() WM_MBUTTONUP ON_WM_MBUTTONUP() OnMButtonUp() WM_MBUTTONDBLCLK ON_WM_MBUTTONDBLCLK() OnMButtonDblClk() WM_RBUTTONDOWN ON_WM_RBUTTONDOWN() OnRButtonDown() WM_RBUTTONUP ON_WM_RBUTTONUP() OnRButtonUp() WM_RBUTTONDBLCLK ON_WM_RBUTTONDBLCLK() OnRButtonDblClk() WM_MOUSEMOVE ON_WM_MOUSEMOVE() OnMouseMove()
Client 영역 마우스 메시지 메시지 핸들러 형태 nFlags point 메시지가 생성될 당시의 키보드나 마우스 버튼의 상태를 나타내는 비트 마스크 point 메시지가 생성될 당시의 마우스 커서 위치(클라이언트 좌표) afx_msg void On*(UINT nFlags, CPoint point); 비트 마스크 의미 MK_CONTROL [Ct기] 키 누름 MK_SHIFT [Shift] 키 누름 MK_LBUTTON 마우스 왼쪽 버튼 누름 MK_MBUTTON 마우스 가운데 버튼 누름 MK_RBUTTON 마우스 오른쪽 버튼 누름
Client 영역 마우스 메시지 nFlagns와 비트 마스크 연산 예 void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { if(nFlags & MK_SHIFT){ // [Shift] 키가 눌렸다면 ... }
Client 영역 마우스 메시지 사용 예 void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC dc(this); dc.SetMapMode(MM_LOMETRIC); // 매핑 모드를 변경한다. CPoint pt = point; // point 객체를 복사한다. dc.DPtoLP(&pt); // 장치 좌표를 논리 좌표로 변환한다. dc.Rectangle(pt.x-100, pt.y+100, pt.x+100, pt.y-100); }
실습 5-1. Draw Circles void CChildView::OnMouseMove(UINT nFlags, CPoint point) { // 그리기 모드면 타원을 지우고 그리기를 반복한다. if(m_bDrawMode) CClientDC dc(this); dc.SelectStockObject(NULL_BRUSH); // 이전에 그린 타원을 지운다. dc.SetROP2(R2_NOT); dc.Ellipse(m_x1, m_y1, m_x2, m_y2); // 새로운 타원을 그린다. m_x2 = point.x; m_y2 = point.y; } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) // 최종적인 타원을 그린다. dc.SetROP2(R2_COPYPEN); // 그리기 모드를 끝낸다. m_bDrawMode = FALSE; class CChildView : public CWnd { public: BOOL m_bDrawMode; // 그리기 작업이 진행 중임을 나타낸다. int m_x1, m_y1, m_x2, m_y2; // 타원에 외접하는 직사각형의 좌상단/우하단 좌표 CChildView::CChildView() { m_bDrawMode = FALSE; } void CChildView::OnLButtonDown(UINT nFlags, CPoint point) // 그리기 모드를 시작한다. m_bDrawMode = TRUE; // 좌표를 저장한다. m_x1 = m_x2 = point.x; m_y1 = m_y2 = point.y;
Client 영역 마우스 메시지 마우스 캡처 (Mouse Capture) 용도 관련 함수 마우스 캡처를 하면 마우스 커서의 위치에 관계없이 모든 마우스 메시지를 특정 윈도우가 받을 수 있다. 관련 함수 API 함수 MFC 함수 의미 SetCapture() CWnd::SetCapture() 마우스 캡처를 시작한다. ReleaseCapture() 없음 마우스 캡처를 해제한다. GetCapture() CWnd::GetCapture() 어느 윈도우가 현재 마우스 캡처를 하고 있는지 알아낸다.
실습 5-2. Mouse Capture void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { // 마우스 캡처를 시작한다. (MFC 함수 사용) SetCapture(); // 그리기 모드를 시작한다. m_bDrawMode = TRUE; // 좌표를 저장한다. m_x1 = m_x2 = point.x; m_y1 = m_y2 = point.y; } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { CClientDC dc(this); dc.SelectStockObject(NULL_BRUSH); // 최종적인 타원을 그린다. dc.SetROP2(R2_COPYPEN); m_x2 = point.x; m_y2 = point.y; dc.Ellipse(m_x1, m_y1, m_x2, m_y2); // 그리기 모드를 끝낸다. m_bDrawMode = FALSE; // 마우스 캡처를 해제한다. (API 함수 사용) ::ReleaseCapture(); }
키보드 다루기 윈도우의 키보드 메시지 처리 윈도우 운영체제는 키보드와 관련된 모든 이벤트를 프로그램에 메시지 형태로 전달한다.
키보드 다루기 키보드 메시지 전달 키보드 포커스 키보드 메시지는 키보드 포커스를 가진 윈도우가 받음 활성 윈도우 또는 활성 윈도우의 자식 윈도우가 가지는 일종의 속성
키보드 포커스 (Keyboard Focus) 키보드 포커스 변화 WM_KILLFOCUS WM_SETFOCUS WM_SETFOCUS
Keystroke 메세지 키 누름 메시지(Keystroke Message) 키 누름 메시지 종류 키보드를 누르거나 떼는 동작에 의해 발생하는 메시지 키 누름 메시지 종류 메시지 발생 시점 WM_KEYDOWN [F10], [Alt] 이외의 키를 누를 때 WM_KEYUP [F10], [Alt] 이외의 키를 뗄 때 WM_SYSKEYDOWN [F10], [Alt], [Alt]+[키 조합]을 누를 때 WM_SYSKEYUP [F10], [Alt], [Alt]+[키 조합]을 뗄 때
Keystroke 메세지 키 누름 메시지 핸들러 형태 nChar nRepCnt nFlags 키에 할당된 가상 키 코드값 → 다음 페이지 표 참조 nRepCnt 키를 계속 누르고 있을 경우 1보다 큰 값을 가진다. nFlags 키와 관련된 부가적인 정보를 담고 있다. afx_msg void On*(UINT nChar, UINT nRepCnt, UINT nFlags); ① ② ③
Keystroke 메세지 가상 키코드 가상 키 코드 해당 키 VK_CANCEL Ctrl-Break VK_HOME Home VK_BACK Backspace VK_LEFT ← VK_TAB Tab VK_UP ↑ VK_RETURN Enter VK_RIGHT → VK_SHIFT Shift VK_DOWN ↓ VK_CONTROL Ctrl VK_SNAPSHOT Print Screen VK_MENU Alt VK_INSERT Insert VK_PAUSE Pause VK_DELETE Delete VK_CAPITAL Caps Lock '0' ~ '9' 0 ~ 9 VK_ESCAPE Esc 'A' ~ 'Z' A ~ Z VK_SPACE Spacebar VK_F1 ~ VK_F12 F1 ~ F12 VK_PRIOR PgUp VK_NUMLOCK Num Lock VK_NEXT PgDn VK_SCROLL Scroll Lock VK_END End
실습 5-7. Keystroke 메시지 처리하기 void CChildView::OnSize(UINT nType, int cx, int cy) { m_xMax = cx; m_yMax = cy; } void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch(nChar){ case VK_LEFT: m_xPos-=20; break; case VK_RIGHT: m_xPos+=20; case VK_UP: m_yPos-=20; case VK_DOWN: m_yPos+=20; case VK_SPACE: m_bFill = !m_bFill; } /* 20 <= m_xPos <= m_xMax-20 */ m_xPos = min(max(20, m_xPos), m_xMax-20); /* 20 <= m_yPos <= m_yMax-20 */ m_yPos = min(max(20, m_yPos), m_yMax-20); Invalidate(); class CChildView : public CWnd { public: int m_xPos, m_yPos; // 도형의 현재 위치 int m_xMax, m_yMax; // 클라이언트 영역의 크기 BOOL m_bFill; // 도형의 내부를 채울지 여부 CChildView::CChildView() { m_xPos = m_yPos = 60; // 임의 값으로 초기화 m_bFill = FALSE; // 도형 내부를 채우지 않음 } void CChildView::OnPaint() { CPaintDC dc(this); if(m_bFill == TRUE) dc.SelectStockObject(BLACK_BRUSH); dc.Ellipse(m_xPos-20, m_yPos-20, m_xPos+20, m_yPos+20); }
Character 메시지 Character 메시지 필요성 [R] 키를 누른 경우? 문자 가상 키 코드 조합 r 영문 입력 모드에서 [R] 또는 [Caps Lock]+[Shift]+[R] 키를 누른 경우 R 영문 입력 모드에서 [Caps Lock]+[R] 또는 [Shift]+[R] 키를 누른 경우 ㄱ 한글 입력 모드에서 [R] 키를 누른 경우 ㄲ 한글 입력 모드에서 [Shift]+[R] 키를 누른 경우
문자 메시지 문자 메시지 발생 시나리오
Character 메시지 메시지 핸들러 형태 nChar nRepCnt nFlags 키에 해당하는 문자 코드값을 가진다. 키를 계속 누르고 있을 경우 1보다 큰 값을 가진다. nFlags 키와 관련된 부가적인 정보를 담고 있다. afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) ; ① ② ③ afx_msg void OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags) ;
실습 5-8. Character 메시지 처리하기 void CChildView::OnPaint() { CPaintDC dc(this); // 화면 출력용 폰트를 선택한다. CFont font; font.CreatePointFont(150, _T("궁서")); dc.SelectObject(&font); // 현재까지 입력된 글자들을 화면에 출력한다. CRect rect; GetClientRect(&rect); dc.DrawText(m_str.GetData(), m_str.GetSize(), &rect, DT_LEFT); } class CChildView : public CWnd { public: CArray<TCHAR, TCHAR> m_str; void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // Backspace 입력 시 맨 마지막 글자를 삭제한다. if(nChar == _T('\b')){ if(m_str.GetSize() > 0) m_str.RemoveAt(m_str.GetSize()-1); } // 그 밖의 경우에는 동적 배열에 글자를 추가한다. else{ m_str.Add(nChar); // 화면을 갱신한다. Invalidate();