. 창 크기 조절

차일드의 보임/숨김 상태뿐만 아니라 각 윈도우의 크기가 얼마나 되는가도 중요한 차일드 배치상태중 하나이다. 크기 조정이 가능한 값은 검색결과창의 높이와 파일창의 폭이 있는데 이 두 값은 정수이기 때문에 메뉴로는 입력받기 어렵다. 차일드의 크기를 변경하는 가장 직관적인 방법은 마우스로 차일드의 경계를 드래그하는 것이다.

GetChildSize 함수는 차일드의 크기를 계산할 때 GAP만큼 간격을 띄우고 Relayout 함수는 이 간격을 유지한 채로 차일드를 배치한다. 이 간격은 차일드로 덮이지 않은 순수하게 비워진 부분이며 이 여백에는 메인 윈도우의 작업영역이 그대로 드러나게 된다. 따라서 이 여백을 드래그하여 차일드의 크기를 조정하는 작업은 메인 윈도우가 할 일이다. 왜냐하면 이 영역에서의 마우스 메시지가 메인 윈도우로 전달되기 때문이다.

iDrag, DragOriginal 전역변수들이 차일드 크기 조절에 사용되는데 iDrag는 어느 방향으로 드래그를 하고 있는지를 기억하며 DragOriginal은 드래그를 하기 전의 차일드 크기값을 저장한다. 이 두 변수는 실행중에 사용되는 것들이므로 따로 초기화할 필요가 없다. 사실 둘 다 전역변수이므로 0으로 자동 초기화된다. 크기 조절에 사용할 커서 리소스 두 개도 만들어져 있다.

 

하나는 수평으로 크기를 조정할 때의 커서이며 나머지 하나는 수직으로 크기를 조정할 때의 커서이다. 사용자들은 이 커서가 나타날 때 드래그가 가능하다는 것을 알 수 있게 된다. 이 리소스를 OnCreate에서 읽어온다.

 

int OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     hCurHorz=LoadCursor(g_hInst,MAKEINTRESOURCE(IDC_SIZEHORZ));

     hCurVert=LoadCursor(g_hInst,MAKEINTRESOURCE(IDC_SIZEVERT));

 

경계선 드래그를 지원하려면 커서 관련 메시지와 마우스 관련 메시지 세 개를 처리해야 한다. 다음 메시지 처리 함수의 원형을 선언하도록 하자.

 

BOOL OnSetCursor(HWND hWnd,WPARAM wParam,LPARAM lParam);

void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam);

void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam);

void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam);

void OnNotify(HWND hWnd,WPARAM wParam,LPARAM lParam);

 

OnNotify는 창 크기 조절과는 상관이 없지만 잠시 후 툴팁 출력에 사용되므로 같이 추가하도록 하자. DGWndProc에서 메시지를 받았을 때 이 함수들을 호출한다.

 

LRESULT CALLBACK DGWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     switch(iMessage) {

          ....

          case WM_SETCURSOR:if (OnSetCursor(hWnd,wParam,lParam)) return 0;break;

          case WM_LBUTTONDOWN:OnLButtonDown(hWnd,wParam,lParam);return 0;

          case WM_MOUSEMOVE:OnMouseMove(hWnd,wParam,lParam);return 0;

          case WM_LBUTTONUP:OnLButtonUp(hWnd,wParam,lParam);return 0;

          case WM_NOTIFY:OnNotify(hWnd,wParam,lParam);return 0;

     }

     return(DefFrameProc(hWnd,g_hMDIClient,iMessage,wParam,lParam));

}

 

OnSetCursor 함수에서는 차일드의 경계선에 커서가 있을 때 이 부분에서 드래그가 가능함을 표시하기 위해 커서를 바꾸어 준다. 그 외의 경우는 DefFrameProc으로 이 메시지를 보내야 하며 이때는 FALSE를 리턴한다.

 

BOOL OnSetCursor(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     POINT pt;

     RECT crt;

     int t,s,f,o,w;

 

     GetClientRect(g_hFrameWnd,&crt);

     GetChildSize(t,s,f,o,w);

 

     GetCursorPos(&pt);

     ScreenToClient(hWnd,&pt);

 

     if (LOWORD(lParam)==HTCLIENT) {

          if (Option.bShowFileWnd &&

              pt.x >= w && pt.x <= w+GAP &&

              pt.y > t+f && pt.y < crt.bottom-(o+s)) {

              SetCursor(hCurHorz);

              return TRUE;

          }

 

          if (Option.bShowOutput &&

              pt.y >= crt.bottom-(o+s+GAP)

              && pt.y <= crt.bottom-(o+s)) {

              SetCursor(hCurVert);

              return TRUE;

          }

     }

     return FALSE;

}

 

GetChildSize 함수로 각 차일드의 크기를 구하고 마우스 커서의 좌표를 작업영역 좌표로 바꾸어 놓으면 이 값들을 비교하여 커서가 지금 어디쯤 있는지를 판단할 수 있다. 커서의 수평좌표가 파일창의 폭~GAP사이에 있고 수직좌표는 파일창의 영역에 있을 때 수평으로 크기 조정이 가능하다. 여기서 마우스 버튼을 누르면 수평으로 크기를 변경할 수 있음을 알려주기 위해 수평 조정 커서로 변경하였다.

출력창의 높이와 GAP 사이에 커서가 있으면 수직으로 크기 조정이 가능하며 커서를 수직 조정 커서로 변경한다. OnSetCursor에서는 커서의 위치에 따라 커서모양만 변경한다. 드래그를 시작할 시점은 이 상태에서 마우스 왼쪽 버튼을 누를 때이다. OnLButtonDown 함수를 다음과 같이 작성한다.

 

void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     POINT pt;

     RECT crt;

     int t,s,f,o,w;

 

     GetClientRect(g_hFrameWnd,&crt);

     GetChildSize(t,s,f,o,w);

 

     pt.x=LOWORD(lParam);

     pt.y=HIWORD(lParam);

 

     iDrag=0;

     if (Option.bShowFileWnd &&

          pt.x >= w && pt.x <= w+GAP &&

          pt.y > t+f && pt.y < crt.bottom-(o+s)) {

          iDrag=1;

          DragOriginal=Option.FileWndWidth;

     }

 

     if (Option.bShowOutput &&

          pt.y >= crt.bottom-(o+s+GAP)

          && pt.y <= crt.bottom-(o+s)) {

          iDrag=2;

          DragOriginal=o;

     }

 

     if (iDrag) {

          SetCapture(hWnd);

     }

}

 

드래그를 시작할 때도 OnSetCursor와 동일한 조건 점검을 한 번 더 하여 과연 드래그를 시작해야 할 시점인지를 조사한다. 파일창의 크기를 조절할 때는 iDrag 1, 검색결과창의 크기를 조절할 때는 iDrag 2를 대입하였다. 그 외의 경우는 크기를 변경할 수 없는 영역이므로 iDrag 0이다. iDrag는 어디를 드래그하고 있는 중인지를 나타내는데 만약 드래그 대상이 더 늘어난다면 iDrag의 값 종류를 늘리면 된다. 드래그가 시작되면 커서를 캡처하여 메인 윈도우가 마우스 메시지를 계속 받을 수 있도록 한다.

iDragOriginal 변수는 드래그를 시작하기 전의 차일드 크기값을 저장하는데 이 값은 드래그에 의해 차일드가 숨겨질 때 원래 폭을 저장하기 위해 사용된다. OnLButtonDown에서는 iDrag 1이나 2를 대입하여 드래그를 시작하기만 하며 이 상태에서 마우스가 이동되면 차일드의 크기가 마우스가 이동된 만큼 조정된다. OnMouseMove에 다음 코드를 작성한다.

 

void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     RECT crt;

     int Value;

     int t,s,f,o,w;

 

     if (iDrag==0) {

          return;

     }

 

     GetClientRect(g_hFrameWnd,&crt);

     GetChildSize(t,s,f,o,w);

 

     if (iDrag==1) {

          Value=(int)(short)LOWORD(lParam);

          Option.FileWndWidth=min(max(5,Value),crt.right-10);

          Relayout();

     }

 

     if (iDrag==2) {

          Value=crt.bottom-(int)(short)HIWORD(lParam)-s;

          Option.OutputHeight=min(max(5,Value),

              crt.bottom-(t+f+s)-10);

          Relayout();

     }

}

 

Option.FileWndWidth Option.OutputHeight가 조정 대상 변수이며 iDrag값에 따라 어떤 변수값을 조정할 것인가가 결정된다. 최초 드래그를 시작한 지점부터 마우스가 이동한 만큼 이 변수값들을 증감시켜 주고 Relayout 함수만 호출하면 된다. 변경된 윈도우 크기를 반영하여 모든 차일드의 크기가 재조정될 것이다.

이때 두 윈도우가 가질 수 있는 최소값과 최대값이 있는데 최소값은 둘 다 5로 되어 있다. 파일창의 폭은 최대 작업영역폭에서 10을 뺀 것 이상 될 수 없도록 하였으며 검색결과창은 툴바, 상태란, 파일탭의 높이를 다 더한 것에서 10을 뺀 것보다 작아야 한다. 상식적으로 생각해 봐도 차일드의 폭이 메인 윈도우의 폭보다 더 넓을 수는 없으며 내용이 안보일 정도로 작아져서도 안된다.

OnMouseMove에서 커서가 움직일 때마다 Relayout 함수를 호출하므로 마우스 이동 즉시 차일드의 배치가 실시간으로 조정된다. 마우스 버튼을 놓으면 드래그를 끝내되 몇 가지 예외 처리를 한다.

 

void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     if (iDrag==1) {

          if (Option.FileWndWidth == 5) {

              Option.FileWndWidth=DragOriginal;

              SendMessage(g_hFrameWnd,WM_COMMAND,MAKEWPARAM(IDM_VIEW_FILE,0),0);

          }

     }

     if (iDrag==2) {

          if (Option.OutputHeight == 5) {

              Option.OutputHeight=DragOriginal;

              SendMessage(g_hFrameWnd,WM_COMMAND,MAKEWPARAM(IDM_VIEW_OUTPUT,0),0);

          }

     }

     iDrag=0;

     ReleaseCapture();

}

 

두 차일드가 최소 크기인 5픽셀이면 크기를 조정하는 대신 아예 차일드를 숨기도록 하였다. 차일드의 경계를 드래그하여 아주 작게 만들면 아예 숨기라는 뜻으로 해석하는 것이다. 이때 각 윈도우의 크기값은 드래그를 시작할 때의 값을 그대로 유지하도록 함으로써 차일드를 다시 보이도록 했을 때는 크기 조정 전의 상태로 돌아올 수 있도록 하였다. 모든 배치가 끝난 후 iDrag 0으로 만들고 커서의 캡처를 풀어 주면 차일드 배치가 끝난다.