[MFC TOOLS] CScrollView 마우스 픽킹 처리 하기
MFC(C++) Tool Sample 2008. 3. 27. 19:05 |
마우스를 통한 개발툴을 만들다 보면 대부분 스크롤 기능을 포함하는 경우가 많습니다.
CScrollView 화면을 통한 마우스 기반의 툴을 제작 하게 되면 불가피 하게 OnLButtonDown(), OnLButtonUp(), OnMouseMove()를 제어 하게 되어 있습니다.
여기서 부터 작업이 시작 되지요.
스크롤 뷰의 화면을 좀 크게 잡는다 싶으면, 스크롤 바를 움직여 보면 아시겠지만, 제대로 되지 않습니다. 16bit 형태의 값이였나, 아뭍턴 값의 한계로 인해서 스크롤 바의 위치가 제대로 처리 되지 않습니다.
그 경우 아래의 코드를 추가 해야 합니다.
BOOL CScrollTest2View::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)
{
// When you drag the scroll box, the nPos value send from WM_HSCROLL/WM_VSCROLL is 16bit value.
// Therefore retrieve the 32bit scroll box position value.
if(SB_THUMBTRACK == LOBYTE(nScrollCode)) // WM_HSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_HORZ, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}
else if(SB_THUMBTRACK == HIBYTE(nScrollCode)) // WM_VSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_VERT, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}
return CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
}
{
// When you drag the scroll box, the nPos value send from WM_HSCROLL/WM_VSCROLL is 16bit value.
// Therefore retrieve the 32bit scroll box position value.
if(SB_THUMBTRACK == LOBYTE(nScrollCode)) // WM_HSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_HORZ, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}
else if(SB_THUMBTRACK == HIBYTE(nScrollCode)) // WM_VSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_VERT, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}
return CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
}
위의 코드를 추가 하고 나면 넓은 영역의 뷰도 스크롤이 제대로 표현되는 것을 확인하실 수 있을겁니다.
이제 확인해야 될 부분이 마우스 이벤트에 들어 오는 CPoint point 부분입니다.
스크롤 뷰에서 제일 맘에 안드는 부분이 이 부분인데요. point가 왜 스크롤 되고 나면 현재 위치를
구해 주지 않는지 모르겠습니다.
위의 화면에서와 같이 마우스 위치의 지점을 클릭해 보겠습니다. 클릭하고 나면 대략 값이 x:188, y:49 가 나오게 됩니다.
하지만 아래의 스크롤 바를 보면 우측으로 많이 가 있는 것을 볼 수 있습니다.
즉, 스크롤이 움직여도 마우스의 위치 값은 화면에 보이는 영역에 대한 상대 값임을 알 수 있습니다.
그렇다면 여기서 스크롤 되는 정보를 얻어 와서 처리 해주어야 한다는 사실을 알게 될겁니다.
스크롤 정보를 알기 위해서 GetScrollPosition() 를 사용합니다.
이제 스크롤 된 후의 현재 마우스 위치 정보를 아래와 같이 구합니다.
CPoint scrollpos = point + GetScrollPosition();
※예로 제가 작업했던 코드 일부 여기에 포스트...
void CScrollTest2View::OnMouseMove(UINT nFlags, CPoint point)
{
if( m_oldPoint == point )
return;
CPoint scrollpos = GetScrollPosition();
TRACE2("현재 스크롤값:%d, %d\n", scrollpos.x, scrollpos.y);
int x = scrollpos.x % GridSize.cx;
int y = scrollpos.y % GridSize.cy;
m_ptGrid = CPoint( ((int)((point.x+x) / GridSize.cx)) * GridSize.cx,
((int)((point.y+y) / GridSize.cy)) * GridSize.cy );
TRACE2("현재 그리드 위치:%d, %d\n", m_ptGrid.x, m_ptGrid.y);
m_ptGrid.x -= x;
m_ptGrid.y -= y;
TRACE2("현재 그리드 위치:%d, %d\n", m_ptGrid.x, m_ptGrid.y);
XORPen();
m_oldPoint = point;
CScrollView::OnMouseMove(nFlags, point);
}
void CScrollTest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
CPoint pos = CPoint(0, 0);
GetCursorPos( &pos );
this->ScreenToClient( &pos );
CScrollView::OnLButtonDown(nFlags, point);
}
void CScrollTest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
TRACE2("MousePos:%d, %d\n", point.x, point.y);
CPoint scrollpos = point + GetScrollPosition();
TRACE2("ScrollPosition:%d, %d\n", scrollpos.x, scrollpos.y);
m_ptGrid = CPoint( ((int)(scrollpos.x / GridSize.cx)) * GridSize.cx,
((int)(scrollpos.y / GridSize.cy)) * GridSize.cy );
m_vPos.push_back( m_ptGrid );
OutCursorDCxRop();
Invalidate();
CScrollView::OnLButtonUp(nFlags, point);
}
이 값을 사용해서 여러가지 동작들을 추가 하시면 됩니다.
하지만 이것저것 하다 보면 GetScrollPosition()의 값을 보정해서 해줘야 하는 경우가 많이 발생을 하게 됩니다. 그때 마다 너무 많은 작업량이 좀 힘들더라구요.
팁을 드리면 CScrollView에 CView를 추가한 후에 CScrollView의 Create(), OnSize() 부분에 코드를 적당히 넣어서 CView를 내부에 크기 만큼 심어 놓습니다.
그렇게 한후에 마우스 픽킹을 해보시면 마우스 이벤트가 CScrollView로 가지 않고 CView로 가는 것을
확인할 수 있을겁니다.
더욱더 놀라운건 ^^.. 스크롤로 해서 화면을 움직여도 현재 찍힌 화면의 좌표는 스크롤로 이동한 좌표 즉, 화면이 스크롤 만큼 이동한 후에 CView의 이동된 좌표값을 바로 리턴해 준다는 겁니다.
이상.. 가볍게 적었지만 CView 추가 하는 부분은 생략하도록 하겠습니다. (귀찮네요 ^^)
모르거나 필요하시면 제가 바로 답변 드릴테니 뎃글 달아 주세요..