* 눈에 보이면 컨트롤, 보이지 않으면 컴포넌트라고 부르자.


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); // 물론 이런거 스택으로 복사해 운반할 똘아이는 없겠지?

};



class IMAGE_VIEW : public UI_OBJECT
{
}; // 귀찮으니 출석만 불렀다. image_index 를 갖게 해서 이미지의 일부를 블리팅 해줄 수 있게 하면 편함. 이를테면 트레이바의 변화하는 아이콘이라든지~
// 물론 뭐 상속받아 스크롤바 달고 이미지 뷰어로 써도 됨


대충 컨트롤들은 이런식이다.

갤러리 에디터창에다 막 짜넣고 있기 땜에 가뜩이나 제정신 아니지만 말이 안되는 구석이 쪼매 있을 순 있다.


4. 눈에 보이지 않는 컴포넌트들
UI_MANAGER
자식들이 생성될때 생성자 파라메터에 전달된 manager 의 이름을 통해 manager->add(this); 정도 해 두자.
list 에 주렁 주렁 달아놓고 있다가 프로그램 종료될때나 뷰가 갱신될때 리스트 따라가며 delete 해주게.
그 다음으로 중요한 역할은 키보드, 마우스, 터치 등의 이벤트를 컨테이너 컨트롤들에게 전달해 주는거다.
컨테이너들에게만 전달하는 이유는 특정 판넬이 disable 되거나 눈에 보이지 않아야 하는 경우도 있으니까다.
(그 위의 컨트롤들이 쌩쌩하면 웃기잖아)

UI_IMAGE_LIST
난 PNG_LIST 를 만들어 쓴다.
만약 UI 리스트에서 전체 UI 테마 이미지들을 재사용할 수 있게 해 준다면,
각각의 IMAGE타입 컨트롤들은 UI_IMAGE_LIST 의 image_index 만 알고 있으면 된다.
생성해제는 역시 UI_MANAGER가 하는거고 말야. 이런건 공용이니까, 다른 컨트롤들을 다 파괴하고 나서 마지막에 파괴하는게 좋지.

UI_TIMER
있음 좋고 (스크롤바나 제스츄어 인식용으로도 쓰인다)


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); 처럼 해서

함수 포인터에 등록된 함수가 있으면 호출해주는 구조로 가면 된다.


간단.


일단 자러감.