C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[163] TShape에 다른 모양의 도형을 그려보고 마우스로 움직여 보자.
김태선 [cppbuilder] 20079 읽음    2008-08-11 03:23
TShape은 좋은 컴포인데 단점은 몇가지 모양 밖에 없다는 것입니다.
원하는 도형은 어떻게 그릴까요?
TShape을 상속받아 Paint() 메소드에서 새로운 모양을 그려주면 됩니다.

아래는 소스입니다.

아래 코드는 빌더 2007에서 실험되었습니다. 빌더 6에서 바로 동작하는지는 확인 안했지만 아마도 될 듯합니다.

//---------------------------------------------------------------------------
// 컴포넌트 교체식으로 만든 내 마음대로 도형 그리기.

class TShape : public Extctrls::TShape
{
    typedef Extctrls::TShape     inherited;
private:
    struct TF
    {
        TPen        *FPen;
        TBrush        *FBrush;
        TShapeType    FShape;
    };
    TF*    GetF()  // private영역의 변수를 제어하기 위해.
    {
        return (TF*)((UINT)this + sizeof(Controls::TGraphicControl));
    }
protected:
    virtual void __fastcall Paint(void)
    {
        if (Tag < 1000)
        {
            inherited::Paint();
            return;
        }
        TF        *F = GetF();
        TCanvas *C = Canvas;
        int      X, Y, W, H, S;

        C->Pen = F->FPen;
        C->Brush = F->FBrush;
        X = C->Pen->Width / 2;
        Y = X;
        W = Width  - C->Pen->Width + 1;
        H = Height - C->Pen->Width + 1;
        if (C->Pen->Width == 0)
        {
            W--; H--;
        }
        if (W < H) S = W; else S = H;
        if (Tag >= 2000)            // 정사각형 영역에 들어오는 모양이면
        {
            X += (W - S) / 2;
            Y += (H - S) / 2;
            W = S;
            H = S;
        }
        TPoint     pt[3];
        ZeroMemory(pt, sizeof(pt));
        switch(Tag % 1000)
        {
            case 0:    // 좌측으로 삼각형
                pt[0] = Point(X, Y + H / 2);
                pt[1] = Point(X + W - 1, Y);
                pt[2] = Point(X + W - 1, Y + H - 1);
                C->Polygon(pt, 2);
                break;
            case 1: // 우측으로 삼각형
                pt[0] = Point(X + W - 1, Y + H / 2);
                pt[1] = Point(X, Y);
                pt[2] = Point(X, Y + H - 1);
                C->Polygon(pt, 2);
                break;
            case 2: // 위로 삼각형
                pt[0] = Point(X + W / 2, Y);
                pt[1] = Point(X, Y + H - 1);
                pt[2] = Point(X + W - 1, Y + H - 1);
                C->Polygon(pt, 2);
                break;
            case 3: // 아래로 삼각형
                pt[0] = Point(X + W / 2, Y + H - 1);
                pt[1] = Point(X, Y);
                pt[2] = Point(X + W - 1, Y);
                C->Polygon(pt, 2);
                break;
        }
    }

};
#define TShape            ::TShape
//---------------------------------------------------------------------------

Tag 값이 1000미만이면 기존 TShape와 동작이 똑 같습니다.
Tag 값이 1000 ~ 1003 까지 값을 넣으면 삼각형이 원하는 방향으로 그려집니다.
Tag 값을 2000 ~ 2003 까지 값을 넣으면 삼각형이 원하는 방향으로 그려지지만 정사각형 범위안에서 그려집니다.
예제는 삼각형만을 그렸으나, 원하는 모양이 있다면 각자 추가하면 될 것입니다.
하지만 아무래도 예쁜 모양을 위해서는 TImage를 쓰는게 낫겠죠.

위에서 상속받은 클래스의 private영역의 내부 변수 제어를 하는 방법은 눈여겨 볼만한 코드입니다.
그런데 위에서
        C->Pen = F->FPen;
        C->Brush = F->FBrush;
이렇게 굳이 어렵게 하지 않고 그냥 간단히 프로퍼티에 있으니
        C->Pen = Pen;
        C->Brush = Brush;
하는 쪽이 더 편리합니다만 위 예제는 예를 보이기 위한 것이라 저렇게 처리 했습니다.
프로퍼티에서 나타나지 않는 내부 변수가 필요할 경우도 있기 때문입니다.


자 그러면 이 컴포넌트를 마우스로 드래그하여 움직여 볼까요?
원래 TWinControl 후손이라면 아주 쉽게 단 2줄로 이동이 가능합니다.
//---------------------------------------------------------------------------
void __fastcall TForm2::Panel1MouseDown(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    ReleaseCapture();
    SendMessage(Panel1->Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
//---------------------------------------------------------------------------
하지만 TGraphicControl 후손이라 이런 처리가 안됩니다.
이런 TWinControl 핸들을 만들때 처리했던 윈도우 만들기와 관련 처리를 같이 해야하는데
컴포넌트 움직이자고 그 처리를 하기는 좀 무리해 보입니다. 그럴 양이면
그냥 TWinControl 후손 컴포넌트에서 적당한 것을 골라 기능을 수정해서 쓰겠죠.
그래서 아주 쉽게 코딩으로 움직이는 상투적인 방법을 쓰겠습니다.


아래는 TShape을 마우스로 드래그하여 움직이는 예제입니다.
//---------------------------------------------------------------------------
struct
{
    bool    bDown;
    TPoint    DownPoint;
} m;
//---------------------------------------------------------------------------
void __fastcall TForm2::Shape2MouseDown(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    m.bDown = true;
    m.DownPoint = Point(X, Y);
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Shape2MouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
    if (m.bDown == false)
        return;
    if (Point(X, Y) != m.DownPoint) // 빌더6에서는 x, y를 각각 비교해야 합니다.
    {
        Shape2->Left += X - m.DownPoint.x;
        Shape2->Top  += Y - m.DownPoint.y;
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Shape2MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    m.bDown = false;
}
//---------------------------------------------------------------------------

아주 깔끔하고 쉽게 움직입니다.
TShape대산 TImage에 아주 예쁜 화살표 같은 그림을 넣어 움직이는 것이 사실상 더 보기가 좋습니다.
그럴때도 코드는 위와 같습니다.
Transparent = ture; 로 놓으면 그림의 배경이 투명해 지닌까 아주 보기 좋게 되겠죠.
아래는 그림이 위 아래로만 마우스에 끌려 이동하는 예제입니다.

//---------------------------------------------------------------------------
void __fastcall TForm2::Image1MouseDown(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    m.bDown = true;
    m.DownPoint = Point(X, Y);
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Image1MouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
    if (m.bDown == false)
        return;
    if (Y != m.DownPoint.y)  // 그림이 위 아래로만 움직이게 한다.
    {
        // 필요하면 움직이는 영역제한은 여기서 한다.
        Image1->Top  += Y - m.DownPoint.y;
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Image1MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    m.bDown = false;
}
//---------------------------------------------------------------------------




사실 제가 필요해서 만들다가 곁다리로 적은 게시물이라서 갑자기 TGraphicControl  후손
컴포넌트 움직이는 것 까지 내용이 들어가 버렸는데... 대충 이해 바랍니다.


그럼..
장성호 [nasilso]   2008-08-11 09:45 X
빌더 6에서 잘 동작합니다.

장성호 [nasilso]   2008-08-11 11:07 X
그런데 질문있습니다.

도형을 마우스로 드래그하여 움직이는 방법에 대해서도 기술해 주셨는데요...
일반적인 핸들이 있는 Control에서는 올려주신 방법으로 가능하지만
원래 핸들이 없던 Graphic컨트롤에서는 위 방법으로 어떻게 가능한지 궁금합니다.

아래 게시글의 Handle부분 코드를 Copy해서 테스트 해봐도 잘 안되네요...
(TLabel , TImage , TShape 모두다...)

김태선 [cppbuilder]   2008-08-11 14:00 X
맞습니다. 간밤에 잠시 착각을 했네요.
저 코드만으로는 움직이지 않습니다.
부가적인 처리를 더 해줘야죠.
장성호 [nasilso]   2008-08-11 14:45 X
Delphi7 이후로 Object-Pascal 에서 지원하는
Helper Class를 보면서 많이 부러워 했었는데...

C++Builder에서는 "교체식"으로 Helper Class와 거의 같은 기능을
지원하게 만들수 있겠네요

Non-visual 컴포넌트 및 class에도 잘 사용하면 매우 유용할듯..

김태선 [cppbuilder]   2008-08-11 14:58 X
예.. 개발과정에서 유용하게 쓸수 있는 기법이죠.

^^;;

+ -

관련 글 리스트
163 TShape에 다른 모양의 도형을 그려보고 마우스로 움직여 보자. 김태선 20079 2008/08/11
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.