[C++Builder] TApLabel 만들기 (1) 준비

볼랜드포럼에 올린글을 블로그에 재게시 합니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=133


이번에는 본격적인 Component 를 만들어 봅시다.
글자와 외곽선에 3D 효과를  내는 컴포넌트입니다. 그리고 컴포넌트 에디터를 이용하여
속성을 변경합니다. 이번 Component 제작에 참고한 것은 김성동님의 컴포넌트 제작 강좌입니다.
사실, 핵심기능은 김성동님의 Delphi 소스를 C++Builder 용으로 옮긴 것입니다.

3차원 레이블 컴포넌트 개발 

본격적으로 들어가기에 앞서서 기본적인 property type 에 대해서 알아봅시다. 정수형인
int , 문자열형인 AnsiString 은 어렵지 않으므로 넘어가고 열거형, 집합형, 객체형의 property 가
Object Inspector 에서 어떻게 나타나는지 알아봅시다.

열거형:
enum TEdgeStyle{esNone, esFlat, esRaised, esRaisedHeavy, esSunken, esSunkenHeavy, esBump, esEtched};



집합형:
enum TEdge{eLeft, eTop, eRight, eBottom};
typedef Set<TEdge, eLeft, eBottom> TEdges;



객체형:
class TFont3D : public TPersistent //Font3D property를 위한 class
{
private:
  TTextStyle FTextStyle;
  TShadowDirection FShadowDirection;
  TColor FShadowColor;
  int FShadowDepth;
  TNotifyEvent FOnChange;
  void __fastcall DoOnChange(void);
  void __fastcall SetShadowDepth(const int Value);
  void __fastcall SetShadowDirection(const TShadowDirection Value);
  void __fastcall SetTextStyle(const TTextStyle Value);
  void __fastcall SetShadowColor(const TColor Value);

protected:

public:
  __property TNotifyEvent OnChange = {read = FOnChange, write = FOnChange};
  void __fastcall Assign(TPersistent* Source);

__published:
  __property TColor ShadowColor = {read = FShadowColor, write = SetShadowColor, default = DefShadowColor};
  __property TShadowDirection ShadowDirection = {read = FShadowDirection, write = SetShadowDirection, default = DefShadowDirection};
  __property int ShadowDepth = {read = FShadowDepth, write = SetShadowDepth, default = DefShadowDepth};
  __property TTextStyle TextStyle = {read = FTextStyle, write = SetTextStyle, default = DefTextStyle};
};




문자를 출력하는 Windows API 함수는 DrawText 입니다.
외곽선을 결정하는 Windows API 함수는 DrawEdge 입니다.

우선 DrawText 를 알아봅시다. prototype 은 다음과 같습니다.

int DrawText(

    HDC hDC, // handle to device context
    LPCTSTR lpString, // pointer to string to draw
    int nCount, // string length, in characters
    LPRECT lpRect, // pointer to structure with formatting dimensions 
    UINT uFormat  // text-drawing flags
   );

첫 인자로 핸들을 넘겨주고 두번째는 출력할 문자열, 세번째는 문자열의 길이 , 네번째는 출력할
화면의 위치와 크기를 나타내는 구조체 포인터 , 마지막은 어떤 모양으로 출력할 것인가를 나타내는
Flag 값입니다.

3D 효과를 내기 위해서는 글자에 명암을 주어야 합니다. 빛이 비스듬히 비추어서 한쪽은 Highlight 되어서
밝은 빛이 나고 반대쪽은 Shadow가 생기는 것입니다. 빛이 왼쪽 위에서 비스듬히 비춘다면 왼쪽 위는
밝게 빛나는 색이 되고 오른쪽 아래는 그림자 색이 됩니다. 그리고 그 위에는 원래의 글자가 있는 것입니다.

이것을 코드로 표현 하면 다음과 같이 됩니다.

OffsetRect(rDraw, -1, -1);
ACanvas->Font->Color = clBtnHighlight;
DrawText(ACanvas->Handle, Caption.c_str(), -1, &rDraw, TextFlag);

OffsetRect(rDraw, 2, 2);
ACanvas->Font->Color = FFont3D->ShadowColor;
DrawText(ACanvas->Handle, Caption.c_str(), -1, &rDraw, TextFlag);

OffsetRect(rDraw, -1, -1);
ACanvas->Font->Color = this->Font->Color;
DrawText(ACanvas->Handle, Caption.c_str(), -1, &rDraw, TextFlag);


처음에는 위치를 대각선 방향으로 한 픽셀 이동시키고 색을 Highlight 로 바꾸어서
글자를 출력합니다. 그리고 그 다음에는 오른쪽 아래 대각선 방향으로 이동하여 색을 ShadowColor
로 바꾸고 글자를 출력, 마지막으로 원래 위치로 이동하여 원래의 글자를 출력합니다.



위의 3개의 글자를 하나씩 겹치면 제일 아래의 글자가 완성됩니다.

순서를 어떻게 하느냐에 따라서 밖으로 돌출되거나 안으로 들어가는 효과를 낼 수 있습니다.


DrawEdge 를 알아봅시다. prototype 은 다음과 같습니다.

BOOL DrawEdge(

    HDC hdc, // handle to device context
    LPRECT qrc, // pointer to rectangle coordinates
    UINT edge, // type of inner and outer edge to draw
    UINT grfFlags // type of border
   ); 


첫번째 인자는 핸들, 두번째는 사각형의 좌표를 나타내는 구조체 포인터, 세번째는 외곽선의 종류
네번째는 외곽선을 어떻게 그릴것인가 하는 것입니다.

Form 위에 Button 과 Label 을 올려놓고 Button 의 이벤트 핸들러를 다음과 같이 작성합니다.
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  TRect rClient;
  rClient = Label1->ClientRect;
  DrawEdge(Label1->Canvas->Handle, &rClient, EDGE_SUNKEN, BF_RECT );
}


다음과 같이 외곽선이 그려지는 것을 알 수 있습니다.