* 눈에 보이면 컨트롤, 보이지 않으면 컴포넌트라고 부르자.
1. 부모 컨트롤
UI 들을 담을 수 있는 컨테이너, 내부 UI 관련 메시지를 전달해주는 메신져 역할
Form (GDI 의 경우) 또는 View (Direct3D, OpenGL, Embedded 나 Mobile 의 경우)
Panel (일반 테마 판넬과 이미지 판넬이 있음)
그밖에 page, tab 등등이 있지만 직접 간단히 구현할 땐 그딴거 다 필요없음.
(판넬에 버튼 여러개 달면 탭임. 판넬에 버튼 세개 달면 스크롤 바임 -> 모조리 다 버튼으로 때울거면 다섯개)
2. 자식 컨트롤
컨테이너에 올려지는 존재.
Label (글씨 출력용 딱히 필요 없는 경우도 많음 : 걍 그릴 때)
Button (일반 테마 버튼과 이미지 버튼이 있음)
Image (일반 이미지를 보여주는 컨트롤과, 스크롤 뷰를 제공하는 이미지 컨트롤,
여러개의 이미지 중에 선택된 index 에 해당하는 이미지를 보여주는 컨트롤이 있음)
Edit (키 입력을 받아들이는 입력줄의 역할. 불필요한 경우도 있음)
3. 기본 컨트롤 구성요소들의 대략적 구조
// sender 를 넘겨주면 수신측에서 이벤트의 의도를 알기 쉬움, 기능 에뮬레이팅도 됨
typedef void (*UI_NOTIFY_EVENT)(UI_OBJECT* sender);
typedef void (*UI_MOUSE_EVENT)(UI_OBJECT* sender, int x, int y, BUTTON_KIND button);
typedef void (*UI_KEYBOARD_EVENT)(UI_OBJECT* sender, int key);
class UI_MANGER;
UI_MANAGER* App; // Singleton이면 좋음
class UI_OBJECT // 일반적인 UI 라면 모두 갖고 있을법한 속성들. 추상계층! 이걸로 눈에 보이는 뭘 만들어 쓰진 않음.
{
protected:
UI_MANAGER* owner; // 전체 UI들을 owner 에 등록해 두면 한번에 파괴하기 좋음. 파괴 코드를 몰아서 구현해둘 수 있고.
UI_OBJECT* parent; // 컨테이너를 의미함. 스펠링 짧으니 부모라고 하자.
// 이 함수들은 메시지 처리쪽에서 호출해줄 함수들. 귀찮으니 최상위 부모 클래스한테 다 쑤셔박을래.
virtual void 클릭(); // 마우스 왼쪽 버튼이나 터치만 (down + up = 클릭) // 왜 영어 클릭이 금칙어냐.
virtual void mouse_down(UI_OBJECT* sender, int x, int y, BUTTON_KIND button); // shift states (shift alt control) 은 빼자. 귀찮
virtual void mouse_move(UI_OBJECT* sender, int x, int y, BUTTON_KIND button); // 여기서 button 은 있어도 되고 없어도 되고
virtual void mouse_up(UI_OBJECT* sender, int x, int y, BUTTON_KIND button);
virtual void key_press(UI_OBJECT* sender, int key); // (down + up = press)
virtual void key_down(UI_OBJECT* sender, int key); // shift states (shift alt control) 은 빼자. 귀찮
virtual void key_up(UI_OBJECT* sender, int key); // 마찬가지
public:
UI_OBJECT(UI_MANAGER* owner){ visible = true; }
virtual ~UI_OBJECT(); // 가상함수로 만들어주는게 매너
// 생성자에서 owner 를 받아서 owner 의 UI_LIST 에 this 를 전달해 주면 좋음. 버튼 만들면서 매니저 클래스로 접근하기 귀찮잖아.
// 걍 각 컨트롤에서 오너 이름만 적어주는게 편함.
// 아래는 클래스 외부의 이벤트 핸들러쪽으로 전달해줄 함수포인터들. 여기 사용된 인자들 종류만큼 함수 포인터 타입을 만들어 두면 좋음.
// 역시 귀찮아져서 부모한테로 다 옮김. 뭐 메모리 얼마나 아끼겠다고.
UI_NOTIFY_EVENT on_클릭;
UI_MOUSE_EVENT on_mouse_down;
UI_MOUSE_EVENT on_mouse_move;
UI_MOUSE_EVENT on_mouse_up;
UI_KEYBOARD_EVENT on_key_press;
UI_KEYBOARD_EVENT on_key_down;
UI_KEYBOARD_EVENT on_key_up;
ALIGNMENT_TYPE align; // 헤헤 설명하기 귀찮구나. 위 아래 위 위 아래 좌 우 전체 어디 붙을지 결정.
int left, top, right, bottom; // 네 코너를 통해서만 컨트롤의 크기가 변경되게
int width(); // 네 코너에 의해 자동 결정
int height(); // 마찬가지
int client_width(); // 테두리, 제목 표시줄을 제외한 자식 컨트롤들이 배치될 수 있는 영역
int client_height(); // 마찬가지
int tag; // 유용함
virtual void paint(HDC dc) = 0; // 혹은 (BITMAP) 가상함수로 만들어서 자식의 paint 가 불리게 하자.
bool visible; // 나 그려야돼 말아야 돼?
};
class BUTTON : public UI_OBJECT // 다 끌고오자. UI 3대장중 첫번째. 버튼 컨트롤이다.
{
private:
public:
BUTTON(UI_MANAGER* owner) : UI_OBJECT(owner) { ... } // 이런식으로 부모 생성자에 오너를 릴레이 해주는거임.
// 다음 함수는 부모에 의해 호출되며, 자신의 위치에 자기를 그림.
virtual void paint(HDC dc); // 또는 BITMAP. 화면 캡춰나 프린팅 용도로도 쓸 수 있음. 상속받을 넘들을 위해서 가상함수로 구현하자.
int group_index; // 같은 번호로 묶여 있는 그룹들끼리 radio button 역할을 하게 할 수 있음. 페이지 컨트롤이나 탭 컨트롤과 같은 전환용도로도 굿.
};
class IMAGE_BUTTON : public BUTTON // 이새끼 금수저
{
private:
BITMAP* bitmap; // 이런건 힢에 만들어야지. 용량 팍팍 쳐먹을테니. 병신들이 함부로 못건들게 private 에 쑤셔박자.
// 그렇지만 난 PNGIMAGE 가 더 좋아. 반투명도 되고 존잘.
public:
IMAGE_BUTTON(UI_MANAGER* owner) : BUTTON(owner) { bitmap = new BITMAP(blah blah)... }
virtual ~IAMGE_BUTTON() { delete bitmap; }
void set_resource(BITMAP* bitmap); // 물론 이런거 스택으로 복사해 운반할 똘아이는 없겠지?
};
class PANEL : public UI_OBJECT // UI 3대장중 두번째. 판넬 컨트롤이다.
{
protected:
slist<UI_OBJECT*> controls; // 컨테이너니까. 자식들 안을 수 있어야지
public:
PANEL(UI_MANAGER* owner) : UI_OBJECT(owner) { ... }
virtual ~PANEL() { controls.clear(); }
};
class IMAGE_PANEL : public PANEL // 이새끼도 금수저
{
protected:
BITMAP* bitmap;
public:
IMAGE_PANEL(UI_MANAGER* owner) : PANEL(owner) { ... }
virtual ~IMAGE_PANEL();
void set_resource(BITMAP* bitmap); // 물론 이런거 스택으로 복사해 운반할 똘아이는 없겠지?
};
5. 구동 메카니즘
전역에 활성 Form 이나 View 의 포인터를 만들어두자. (바탕색이나 가진 판넬 비스무리하게 생긴놈이겠지?)
VIEW* active_view;
UI 중 무언가가 변경되었는지 알 수 있게 전역변수도 하나 따 놓을까? (매니저의 멤버여도 됨)
bool is_changed = true;
키보드 입력 전달하려면 아래것도 있는게 좋겄지?
UI_OBJECT* focused_control;
항상 모든 드로잉 작업은 단 한군데서 지휘를 해서, active_view->paint() -> 소속된 각 컨테이너들의 paint() -> 소속된 각 버튼 따위 자식 컨트롤들의 paint();
로 그려지게 하자. 그러면 자연히 바탕 그려지고 버튼 그려진다.
생성은 이렇게 (이름이 속성으로 있었으면 디버깅하기 좋았을텐데 까먹었다 패스.)
MANAGER* manager = new MANAGER(); // 싱글톤이면 이렇게 초기화 하진 않지만 일단 알기 쉽게
UI_IMAGE_LIST* image_list = new UI_IMAGE_LIST();
imageList->add(create_bitmap_resource("~~~.bmp"));
VIEW* main_view = new VIEW(manager);
PANEL* client_panel = new PANEL(manager);
client_panel->parent = main_view;
client_panel->align = al_client;
client_panel->adjust_size(); // 캬캬 resize 이벤트 안만들었다. 패스.
client_panel->visible = true;
IMAGE_BUTTON* some_button = new IMAGE_BUTTON(manager);
some_button->parent = client_panel;
some_button->set_position(10, 10, 200, 30);
some_button->set_resource(create_bitmap_resource("~~~.bmp")); // 걍 image_list 랑 index 전달하시든지유
soem_button->visible = true;
메시지 펌프에서 루프돌다가 UI를 갱신할 여지가 있으면 다시 다 그려. (일부만 그리게 하려면 또 리스트나 region 가공해야되니 귀찮)
각 부모 컨트롤들은 자식의 mouse_down 같은 메서드들을 호출해주도록 하고, 각각의 컨트롤들은 if (on_클릭) 클릭(this); 처럼 해서
함수 포인터에 등록된 함수가 있으면 호출해주는 구조로 가면 된다.
간단.
일단 자러감.
아 졸려 죽겠다.
와우
우와감사합니다. 근데 제가 궁금한건 ui에 붙이는 코드에 관한건데.... 프레임웍 설계는 좀 다른 문제같은뎅ㅠㅠ
ㅠㅠ - DCW
ㅋ
조아요