컴퓨터 소프트웨어의 GUI 요소 중에는 잘 알다시피 체크 박스와 라디오 박스가 있다.
전자는 n개의 항목을 제각각 복수 선택할 수 있기 때문에 선택의 가짓수가 2^n개가 가능하다.
그 반면 후자는 n개의 항목 중 하나만 선택할 수 있기 때문에 선택의 가짓수가 딱 n이 된다.
그리고 이런 개념은 사실 메뉴에도 존재한다.
메뉴 항목은 사용 가능 여부(enabled)와 더불어 체크 여부(checked)라는 상태가 존재하여, 자신이 체크된 것처럼 보이는 시각적 피드백을 줄 수 있다.
Windows는 초창기엔(=16비트 시절) 말 그대로 √ 1종류만이 존재했다. 이를 제어하는 함수는 CheckMenuItem이다.
그러다가 Windows 95/NT4에서부터는 ● 모양의 체크를 표시해 주는 CheckMenuRadioItem 함수도 추가되었다. 이로써 각각의 항목들을 따로 체크할 수 있는 메뉴와, 여러 개 중 한 모드만 선택할 수 있는 메뉴의 구분이 가능해졌다.
CheckMenuRadioItem는 특정 메뉴 항목 하나의 속성을 바꾸는 여타 함수들과는 달리, 메뉴 항목들을 여러 개 한꺼번에 지정한 뒤 하나만 체크를 하고 나머지는 체크를 모두 자동으로 해제하는 형태로 동작한다.
그런데 재미있는 것은, MFC는 95/NT4 이전의 16비트 시절에서부터 메뉴에다 custom 비트맵을 지정하는 독자적인 방식으로 라디오 박스를 자체 지원해 왔다는 점이다.
운영체제에 CheckMenuRadioItem가 추가된 뒤에도 내부적으로 그 함수를 쓰지 않는다. 이것은 비주얼 C++ 2012의 최신 MFC도 변함이 없다.
MFC는 동일한 명령 ID에 대해서 메뉴, 도구모음줄 등 여러 GUI 요소에 대해 일관되게 checkd/enabled 상태를 관리할 수 있게 이 계층만을 CCmdUI라는 클래스로 따로 뽑아 냈다. 그리고 윈도우 메시지의 처리가 끝난 idle 시점 때 모든 GUI 상태들을 업데이트한다.
MFC 소스를 보면, CCmdUI::SetCheck는 CheckMenuItem 함수를 호출하는 형태이다. 그러나 CCmdUI::SetRadio는 운영체제의 API를 쓰는 게 아니라 자체 생성한 bullet 모양 비트맵을 SetMenuItemBitmaps로 지정하는 좀 더 힘든 방법을 쓴다.
고전 테마를 포함해 심지어 Windows XP의 Luna에서도 운영체제가 그려 주는 radio 그림과 MFC가 그려 주는 radio 그림은 차이가 거의 없었다. 둘 다 그냥 글자와 동일한 모양으로 동그란 bullet을 그리는 게 전부였다. 그렇기 때문에 두 구현이 따로 노는 건 그리 문제될 게 없었다.
그러나 문제는 Vista 이후에서부터이다. 운영체제가 그리는 radio 그림은 더 알록달록해지고 배경까지 가미되어 화려해진 반면, MFC가 그리는 radio 그림은 아직까지 단색의 단조로운 bullet이 전부이다. 그래서 시각적으로 이질감이 커졌다. 그것도 일반 체크(√) 항목은 괜찮은데 라디오(●) 그림만 차이가 생긴 것이다.
이해를 돕기 위해 그림을 첨부한다. Windows Vista 이후에 운영체제가 메뉴에다 그려 주는 라디오 체크는 배경에 은은한 무늬가 생겨 있다(왼쪽). 그러나 MFC가 그리는 라디오 체크는 여전히 옛날 스타일대로 단색 동그라미밖에 없으며, 일반 체크와도 형태가 다르다(오른쪽). 오른쪽의 프로그램은 본인이 예전에 MFC 기반으로 개발했던 오목 게임이다. ㅋㅋ
MFC는 운영체제의 새로운 함수를 왜 쓰지 않는 걸까?
그냥 이런 사소한 데에까지 신경을 안 써서 그런 것일 수도 있고, 또 CCmdUI는 각각의 메뉴 항목에 대해 개별적으로 호출되는 반면 CheckMenuRadioItem는 그 자체가 여러 메뉴 항목의 상태를 한꺼번에 바꾸는 함수이기 때문에 기능의 구현 형태가 서로 맞지 않아서 도입하지 않은 것일 수도 있다.
물론, SetMenuItemInfo라는 만능 함수를 쓰면, 개별적으로 라디오 체크 상태를 바꾸는 것도 불가능하지는 않다. 다만, 구조체를 준비해야 하는 데다, 상태(state)만 옵션으로 간단히 바꾸면 되는 게 아니라 메뉴의 유형(type)까지 바꿔야 하니 일이 좀 번거로운 건 사실이다.
다만, 요즘은 MFC에도 잘 알다시피 MS Office나 Visual Studio의 모양대로 GUI 외형을 싹 바꿔 주는 툴킷이 도입되었고, 이런 상태에서는 어차피 메뉴의 요소들이 무조건 모조리 자체적으로 그려진다. 그러니 저런 SetRadio와 SetCheck의 동작 방식의 차이 같은 것도 존재하지 않으며, 그런 걸 논하는 게 아무 의미가 없다. 저건 오로지 운영체제 표준 GUI를 쓸 때만 발생하는 이슈이기 때문이다. ^^
* 글을 맺으며..
WinMain 함수를 포함해 윈도우 클래스 등록, 프로시저 구현을 전부 직접 하면서 Windows용 응용 프로그램을 밑바닥부터 만들어 본 사람이라면, MFC가 내부적으로 프로그래머에게 몰래 해 주는 일이 얼마나 많은지를 어렴풋이 짐작할 수 있다.
- 대화상자를 창의 가운데에다 배치해 주는 것,
- 프레임 윈도우와 뷰 윈도우 사이의 경계에 깔끔한 입체 모양 테두리 넣는 것,
- 고대비 모드일 때 도구 아이콘의 검은색을 흰색으로 바꾸는 것,
- 심지어 콤보 박스 내부에 디폴트 데이터(리소스 에디터에서 만들어 넣었던)들을 집어넣는 것,
- 프레임 윈도우가 키보드 포커스를 얻었을 때 그 아래의 view 윈도우로 포커스를 옮기는 것,
- 프로퍼티 시트의 내부에 들어가는 프로퍼티 페이지들의 글꼴을 운영체제 시스템 글꼴로 바꾸는 것 등..
이런 사소한 것들도 공짜가 아니라 죄다 MFC가 내부에서 해 주는 일들이다.
Windows API만 써서 프로그램을 만드는 방식은 최고의 작고 가볍고 성능 좋은 프로그램을 만들 수 있지만 생산성도 미칠 듯한 저질이기 때문에, 인제 와서 이런 불편한 방식으로 프로그램을 만들 프로그래머는 거의 없을 것이다. 요즘 세상에 C++도 아닌 C는 사실상 어셈블리나 마찬가지다.
Posted by 사무엘