제공: 한빛미디어 네트워크 기사
저자: 김상형
출처: 윈도우즈 API 정복(개정판) 제1권 중 18장. 스크롤 바
종류와 구조
스크롤 바는 보여주어야 할 내용이 화면에 표시할 수 있는 양보다 더 많을 때 사용한다. 당장 보이지 않고 가려진 부분으로 이동할 때 스크롤 바를 조작하여 아래 위로 스크롤한다. 워드 프로세서처럼 문서의 길이가 길 때 한 화면에 문서 전체를 출력할 수 없으므로 보고 싶은 부분을 스크롤 바로 선택하여 봐야 한다. 비주얼 C++의 소스 편집창도 마찬가지로 소스의 길이가 길기 때문에 스크롤 바가 필수적으로 필요하다.
스크롤 바는 또한 넓은 범위 중에 한 값을 신속하게 선택할 때도 종종 사용되는데 예를 들어 볼륨이나 색상 등 정확한 값을 요하지 않는 값을 대충 입력받을 때 편리하다. 키보드가 아닌 마우스로 값을 빠르게 선택할 수 있다. 형태로 볼 때 수평 스크롤 바와 수직 스크롤 바로 구분되며 생성방법에 따라 다음과 같이 두 가지 종류로도 나눌 수 있다. 사용 방법은 둘 다 비슷하므로 한 종류만 쓸 수 있으면 나머지도 같은 방법으로 사용할 수 있다.
■ 표준 스크롤 바 : 윈도우의 오른쪽과 아래쪽에 밀착되어 나타나며 작업영역이 데이터를 한꺼번에 다 보여주기 좁을 때 문서를 스크롤 하기 위한 용도로 사용된다. 윈도우 클래스에 WS_HSCROLL, WS_VSCROLL 스타일을 지정하면 이 스크롤 바가 만들어진다. 표준 스크롤 바는 소속 윈도우에 부착되어 있는 그래픽 컨트롤일 뿐 차일드 컨트롤이 아니며 작업영역에서도 제외된다.
■ 스크롤 바 컨트롤 : CreateWindow로 만들어지는 독립된 윈도우이며 "scrollbar" 윈도우 클래스로 만든다. 표준 스크롤 바와는 달리 윈도우이므로 별도의 핸들을 가진다. 특별한 용도가 정해져 있지는 않으며 값을 입력받는 일반적인 용도로 사용할 수 있다.
스크롤 바는 세 가지 요소로 구성된다. 수직 스크롤 바를 예로 든다면 다음과 같으며 수평 스크롤 바도 가로 방향으로 되어 있는 것 외에는 수직 스크롤 바와 동일하다. 윈도우즈를 쓸 수 있는 사람이라면 다 알고 있는 내용이기는 하지만 각 부분의 기능을 한 번 정리해 보자.
■ 화살표 버튼 : 양쪽 끝에 하나씩 있는데 값을 미세하게(보통 한 줄) 조정할 때 사용한다. 계속 누르고 있으면 연속적으로 값이 증감된다. 위쪽 화살표(수평일 경우 왼쪽)는 값을 감소시키고 아래쪽 화살표(수평일 경우 오른쪽)는 값을 증가시킨다.
■ 썸 : 몸통의 중앙에 있으며 스크롤 바의 현재 위치를 표시한다. 썸을 마우스로 드래그하면 드래그한 위치로 즉시 이동하므로 가장 빠르게 이동할 수 있는 방법이다.
■ 몸통 : 썸을 기준으로 아래쪽과 위쪽이 있는데 화살표 버튼과 마찬가지로 값을 증감시킨다. 화살표 버튼에 비해서 이동 간격이 훨씬 더 넓으며 보통 한 페이지 단위로 스크롤한다. 계속 누르고 있으면 썸이 마우스 위치에 올 때까지 스크롤된다.
스타일
스크롤 바도 윈도우이므로 스타일을 가진다. 단 윈도우에 부착되는 표준 스크롤 바는 위치와 모양이 이미 정해져 있으므로 스타일을 지정할 수 없으며 스크롤 바 컨트롤에 한해서만 적용할 수 있다. 스크롤 바는 모양이 일정하기 때문에 다른 컨트롤에 비해 스타일이 거의 없는 편이다. 간단히 표로 정리해 보자.
이 중 실제로 사용되는 스타일은 수평, 수직을 선택하는 SBS_HORZ, SBS_VERT 뿐이며 나머지 네개의 스타일은 거의 사용되지 않는다. CreateWindow 함수로 스크롤 바를 만들 때 좌표와 크기를 주는데 SBS_HORZ, SBS_VERT 스타일만 주면 지정한 좌표를 그대로 사용한다. 하지만 아래쪽의 네 스타일중 하나를 추가로 지정하면 폭과 높이를 디폴트 값으로 사용하고 어느 한 변에 붙임으로 스크롤 바가 너무 뚱뚱해지지 않게 한다. 그다지 실용적인 스타일이 아니므로 간단하게 그림으로 정리한다. CreateWindow로 수평 스크롤 바를 만들되 폭은 400, 높이는 50으로 주었다고 하자.
SBS_HORZ 스타일만 주면 지정한 폭과 높이에 꼭 맞게 스크롤 바를 만들지만 정렬 스타일이 지정되어 있으면 디폴트 높이를 사용하고 나머지 영역은 버린다. 여기서 디폴트 높이라고 하는 것은 GetSystemMetrics 함수의 SM_CYSCROLL 인수로 얻어지는 높이를 말한다.
메시지
버튼이나 에디트 등의 컨트롤들은 자신에게 조금이라도 변화가 있을 때 부모 윈도우로 WM_COMMAND 통지 메시지를 보낸다. 반면 스크롤 바는 특이하게도 WM_COMMAND를 보내지 않고 대신 WM_HSCROLL, WM_VSCROLL 메시지를 부모 윈도우에게 보낸다. 사건의 발생 사실을 단순히 알리는 것뿐만 아니라 어느 부분을 스크롤했는지, 현재 썸의 위치는 어디인지 등의 추가 정보를 전달해야 하기 때문이다. 수평 스크롤 바는 WM_HSCROLL 메시지를 보내고 수직 스크롤 바는
WM_VSCROLL 메시지를 보내는데 두 메시지 모두 프로그래밍하는 방법은 동일하다. 이 때 전달되는 인수는 다음과 같다.
스크롤 코드는 사용자가 누른 스크롤 바의 위치, 즉 요구되는 스크롤 동작을 나타낸다. 스크롤 바의 위치에 따라 다음과 같은 매크로 상수가 정의되어 있다.
수평 스크롤 바인가 수직 스크롤 바인가에 따라 다른 이름이 붙여져 있지만 사실은 이 둘을 굳이 구분할 필요가 없다. 예를 들어 값 증가는 수평일 경우 SB_LINERIGHT, 수직일 경우 SB_LINEDOWN이지만 이 둘을 실제로는 같은 값으로 정의되어 있다. 스크롤 바의 방향에 따라 수평은 LEFT, RIGHT로 이름이 붙어 있고 수직은 UP, DOWN으로 되어 있을 뿐이다. 프로그램은 각 스크롤 코드에 따라 다음과 같은 처리를 해야 한다.
마지막 두 코드에 대해서는 약간의 부연 설명이 필요할 것 같다. 두 코드 모두 사용자가 썸을 마우스로 조작할 때 발생하는데 SB_THUMBTRACK은 드래그를 하는 중에 연속적으로 보내지며 SB_THUMBPOSITION은 드래그가 끝나는 시점에서 한 번만 보내진다. 프로그램은 이 두 메시지 중에 반드시 하나는 처리해야 하는데 두 메시지를 모두 무시해 버리면 사용자가 썸을 직접 조작할 때아무 반응도 하지 않을 것이다.
두 코드가 상호 대체성이 있기 때문에 둘 다 처리할 필요는 없다. 둘 중 어떤 메시지를 처리할 것인가는 프로그램이 데이터를 그리는 속도가 얼마나 빠른가에 따라 달라진다. 사용자가 썸을 드래그 하는 속도에 맞게 데이터를 빨리빨리 그릴 수 있으면 SB_THUMBTRACK 메시지를 처리하여 드래그 하는 중에 스크롤을 바로바로 처리하면 좋을 것이다. 메모장이나 비주얼 C++ 편집기는 이 코드를 처리한다.
반면 CAD나 벡터 편집 프로그램처럼 데이터가 복잡한 프로그램은 사용자가 드래그하는 족족 스크롤 하기에는 너무 느리기 때문에 드래그가 끝나는 시점인 SB_THUMBPOSITION 코드에서 한번만 스크롤 처리를 한다. 썸을 놓기 전에는 실제로 스크롤이 일어나지 않기 때문에 약간 불편하다. 속도가 충분히 빠르다면 가급적이면 SB_THUMBTRACK 메시지를 처리하는 것이 좋으며 요즘은 컴퓨터가 워낙 빨라 대부분 이 메시지를 처리하는 추세이다. 웹 브라우저같은 복잡한 프로그램도 드래그 중의 스크롤을 지원하는 정도이므로 지원하지 않는 프로그램의 예를 찾기가 어려워졌다. CAD나 지도 프로그램 정도의 복잡한 출력을 하는 프로그램이 속도상의 문제로 이 메시지를 처리하지 않는다.
스크롤 범위와 위치
스크롤 바는 범위를 가진다. 스크롤 바가 가질 수 있는 가장 작은 값인 최소값과 가장 큰 값인 최대값의 차가 바로 범위(Range)이다. 범위는 스크롤 바의 용도에 따라 달라지는데 색상값을 입력받는 스크롤 바라면 0~255까지일 것이고 백분율을 입력받는 스크롤 바라면 0~100까지의 범위를 가질 것이다. 또 작업영역을 스크롤하는 표준 스크롤 바는 작업영역의 크기만큼의 스크롤 범위를 가진다.
스크롤 바의 디폴트 범위는 표준 스크롤 바일 경우는 0~100까지이며 스크롤 바 컨트롤일 경우는 0~0까지이다. 즉 스크롤 바 컨트롤은 디폴트로 주어지는 범위가 아예 없는 셈이다. 그래서 스크롤 바를 만든 직후에 용도에 맞게 범위를 지정해야 한다. 스크롤 바의 범위를 지정하는 함수와 범위를 얻어오는 함수는 다음과 같다.
BOOL SetScrollRange( HWND hWnd, int nBar, int nMinPos, int nMaxPos, BOOL bRedraw );
BOOL GetScrollRange( HWND hWnd, int nBar, LPINT lpMinPos, LPINT lpMaxPos );
두 번째 인수 nBar는 범위 지정의 대상이 되는 스크롤 바이며 첫 번째 인수 hWnd는 nBar에 따라 다른 의미를 가진다.
표준 스크롤 바와 스크롤 바 컨트롤을 칭하기 위해 두 인수의 조합을 사용하는데 수평, 수직 표준스크롤 바와 스크롤 바 컨트롤을 모두 가진 프로그램의 경우 다음과 같이 각 스크롤 바를 칭한다. 컨트롤은 고유의 핸들이 있으므로 핸들과 SB_CTL로 지정하며 표준 스크롤 바는 핸들이 없으므로 소속된 윈도우의 핸들과 SB_HORZ, SB_VERT로 지정한다. 다른 스크롤 관련 함수들도 동일한 방법으로 대상 스크롤 바를 지정한다.
nMinPos, nMaxPos는 최소값, 최대값이되 만약 두 값이 같으면 스크롤 바는 숨겨진다. bRedraw는 범위 변경 후 스크롤 바를 다시 그릴 것인가를 지정한다. 스크롤 바를 만든 직후에 초기 범위를 지정할 때는 다시 그릴 필요가 없지만 실행중에 범위가 변경될 때는 다시 그려야 썸의 위치가 범위에 맞게 재조정된다.
GetScrollRange 함수는 스크롤 바의 범위를 조사하는데 두 개의 참조 인수로 최소, 최대값을 리턴받는다. 위치(Position)는 스크롤 바의 썸 위치이며 이 값이 곧 스크롤 바의 현재 값이다. 당연한 얘기겠지만 위치값은 최소값 이상이어야 하며 최대값 이하여야 한다. 위치를 읽거나 변경하는 함수는 다음 두 함수이다.
int SetScrollPos( HWND hWnd, int nBar, int nPos, BOOL bRedraw );
int GetScrollPos( HWND hWnd, int nBar );
hWnd, nBar 인수는 대상 스크롤 바를 지정하는데 SetScrollRange 함수에서와 같다. nPos는 스크롤바의 새 위치를 지정하는데 이 값은 당연히 범위안의 값이어야 한다. 하지만 불가피하게 범위를 벗어나더라도 SetScrollPos 함수가 한계 점검을 해 보고 범위 안으로 강제 조정한다. 즉 최소값보다 작으면 최소값으로 만들고 최대값보다 크면 최대값으로 만든다. bRedraw가 TRUE이면 위치 변경 후 스크롤 바를 다시 그린다.
참고로 여기서 알아본 함수 네 가지는 모두 16비트 윈도우즈에서부터 사용하던 함수이며 Win32에서는 이 함수들을 대체하는 새로운 함수가 있다. 새로운 함수들에 대해서는 잠시 후에 다시 알아볼 예정이다. 하지만 이 함수들이 새 함수에 비해 사용 방법이 훨씬 더 쉽기 때문에 아직도 많이 사용되고 있다. 새로운 함수들이 더 많은 기능을 제공하기는 하지만 기능이 늘어나면 그만큼 사용하기는 까다로와지기 마련이다.