스크롤되는 크레디트 다이얼로그
(Scrolling Credits Dialog)

이 내용은 코드구루(http://www.codeguru.com)에서 가져온 것입니다.

 

 소스코드 받기 (credits.h/cpp) : 아래의 소스와 같은 것.

 크레디트 다이얼로그란 보통, 영화가 끝난 후, 만드는데 참여한 사람들의 이름이 죽 화면에 스크롤되는 것처럼, 어떤 프로그램에서  특정한 키 시퀀스를 눌렸을 때 그 제품 제작에 참여한 사람들의 이름이 죽 스크롤되는 대화상자를 말한다.

 이 다이얼로그는 사용자가 지정한 색깔과 폰트로 글을 보여주고 또한 스크롤되는 화면에 비트맵도 보여준다. 나타내고 싶은 글을 CPP 파일 안에 정의된 고정 배열에 써넣고, 다른 폰트, 색 등을 사용하기 위해서 escape sequence을 사용한다.

 크레디트는 순환하면서, 시작부터 계속 반복하여 돌아간다.

 

단계

1. 리소스 에디터를 사용하여 다이얼로그를 하나 만든다.  ID는 IDD_DISPLAY 라고 한다.

    다이얼로그에 있는 OK 버튼은 사용자가 다이얼로그를 없애기 위해 사용할 수 있다.

2. 크레디트  표시 윈도우를 static text 로 생성한다. (충분히 큰 크기로 만든다.)

   이 텍스트박스를 IDC_DISPLAY_STATIC 이라고 이름 짓는다. (혹은 사용자의 마음에 드는  다른 이름으로, 하지만 다른 이름을 사용한다면, 이 글에서 제공하는 소스코드에서 IDC_DISPLAY_STATIC 을 사용자가 만든 새로운 이름으로 바꿔주어야 한다.)

3. 클래스 위저드를 사용하여 이 다이얼로그박스에 연결될 새로운 클래스를 만든다.

    클래스명은 CCredits로 하고, Base Class 는 CDialog로 한다.

4. 클래스 위저드로 다음의 함수와 메시지 핸들러들을 만든다.

         virtual void OnOK();
         afx_msg void OnPaint();
         virtual BOOL OnInitDialog();
         afx_msg void OnTimer(UINT nIDEvent);
         afx_msg void OnDestroy();

5. 아래 소스 코드의 굵은 글씨부분이 직접 추가한 부분이다. 복사-붙여넣기를 하면 된다. 소스코드가 필요하다면 위의 링크에서 받을 수 있다.

6. 아래 코드를 다 넣은 후, 다른 곳에서 DoModal() 함수로 불러 사용하면 된다.

   예를 들어 AboutBox에서 불러온다고 생각하면 AboutBox의 다이얼로그에 버튼 하나를 추가하고 그 버튼의 핸들러에 다음처럼 코드를 넣으면 된다.

   그림을 출력할 경우 미리 그림을 만들어 두는 것을 잊지 말 것

 

=============소스 코드===================

// credits.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// CCredits dialog

#define     DISPLAY_TIMER_ID    150     // timer id

class CCredits : public CDialog
{
// Construction
public:
        CCredits(CWnd* pParent = NULL);   // standard constructor

        RECT    m_ScrollRect,r;         // rect of Static Text frame
        int     nArrIndex,nCounter;      // work ints
        CString     m_szWork;           // holds display line
        BOOL       m_bFirstTime;
        BOOL       m_bDrawText;
        int             nClip;
        int             nCurrentFontHeight;
        CWnd*      m_pDisplayFrame;
        CBitmap    m_bmpWork;                     // bitmap holder
        CBitmap*   pBmpOld;                         // other bitmap work members
        CBitmap*    m_bmpCurrent;
        HBITMAP   m_hBmpOld;
        CSize      m_size;                              // drawing helpers
        CPoint     m_pt;
        BITMAP     m_bmpInfo;
        CDC        m_dcMem;
        BOOL       m_bProcessingBitmap;

// Dialog Data
        //{{AFX_DATA(CCredits)
        enum { IDD = IDD_DISPLAY };
        //}}AFX_DATA

// Overrides
        // ClassWizard generated virtual function overrides
        //{{AFX_VIRTUAL(CCredits)
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
        //}}AFX_VIRTUAL

// Implementation
protected:
        // Generated message map functions
        //{{AFX_MSG(CCredits)
        virtual void OnOK();                      // <-----클래스 위저드에서 추가한 부분

        afx_msg void OnPaint();               // <-----클래스 위저드에서 추가한 부분
        virtual BOOL OnInitDialog();           // <-----클래스 위저드에서 추가한 부분
        afx_msg void OnTimer(UINT nIDEvent);  // <-----클래스 위저드에서 추가한 부분
        afx_msg void OnDestroy();           // <-----클래스 위저드에서 추가한 부분
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// credits.cpp : implementation file
//**************************************************************************
// Note : 스크롤되는 화면에는 그림도 나타나게 할 수 있는데, 이 예제에서는
//           ID가 "BITMAP1" 인 비트맵 하나를  사용했다.
//           여기서 주의할 점은 이 ID 에 따옴표도 포함된다는 것이다.
//            ( "BITMAP1"   not   BITMAP1 )
//            그리고 pArrCredit[] 배열에 그림의 이름을 넣을 때 파일명과
//            escape sequence 사이에 빈 공간을 두면 안된다.
//            ( "BITMAP1\b" = 옳은 경우,  "BITMAP1   \b" = 틀린 경우)

#include "stdafx.h"
#include "resource.h"
#include "credits.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#define SCROLLAMOUNT    -1
#define DISPLAY_SLOW    70
#define DISPLAY_MEDIUM  40
#define DISPLAY_FAST    10
#define DISPLAY_SPEED   DISPLAY_MEDIUM

#define RED             RGB(255,0,0)
#define GREEN           RGB(0,255,0)
#define BLUE            RGB(0,0,255)
#define WHITE                   RGB(255,255,255)
#define YELLOW                  RGB(255,255,0)
#define TURQUOISE       RGB(0,255,255)
#define PURPLE                  RGB(255,0,255)
#define BLACK                   RGB(0,0,0)

#define BACKGROUND_COLOR                     BLACK
#define TOP_LEVEL_TITLE_COLOR        RED
#define TOP_LEVEL_GROUP_COLOR       YELLOW
#define GROUP_TITLE_COLOR                   TURQUOISE
#define NORMAL_TEXT_COLOR             WHITE

// 폰트의 높이를 정하는 부분
#define TOP_LEVEL_TITLE_HEIGHT           21             
#define TOP_LEVEL_GROUP_HEIGHT        19     
#define GROUP_TITLE_HEIGHT               17     
#define NORMAL_TEXT_HEIGHT               15

// 출력할 문자배열과 함께 쓰일 escape sequence를 정의하는 부분
// 큰 글자에서 작은 글자순으로 표시되어 있다.

#define TOP_LEVEL_TITLE        '\n'
#define TOP_LEVEL_GROUP      '\r'
#define GROUP_TITLE               '\t'
#define NORMAL_TEXT             '\f'
#define DISPLAY_BITMAP          '\b'

#define ARRAYCOUNT      8          // 출력될 문장의 갯수

// 출력될 문장을 고정배열에 넣는다.
char *pArrCredit[] = {  "크레디트 다이얼로그\n",
                                 "",
                                 "BITMAP1\b",    // 비트맵 ID에는 따옴표도 포함되어 있다.
                                 "TOP LEVEL TITLE\n",
                                 "TOP LEVEL GROUP\r",
                                 "GROUP TITLE\t",
                                 "NORMAL TEXT\f",
                                 ""              };

/////////////////////////////////////////////////////////////////////////////
// CCredits dialog
 
CCredits::CCredits(CWnd* pParent /*=NULL*/)
        : CDialog(CCredits::IDD, pParent)
{
        //{{AFX_DATA_INIT(CCredits)
        //}}AFX_DATA_INIT
}

void CCredits::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CCredits)
        //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCredits, CDialog)
        //{{AFX_MSG_MAP(CCredits)
        ON_WM_PAINT()
        ON_WM_TIMER()
        ON_WM_DESTROY()
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCredits message handlers

void CCredits::OnOK()
{
        KillTimer(DISPLAY_TIMER_ID);
        CDialog::OnOK();
}

//************************************************************************
//       InitDialog
//
//              Setup the display rect and start the timer.
//************************************************************************
BOOL CCredits::OnInitDialog()
{
        CDialog::OnInitDialog();
       BOOL bRet;
        UINT nRet;

        nCurrentFontHeight = NORMAL_TEXT_HEIGHT;

        CClientDC dc(this);
        bRet = m_dcMem.CreateCompatibleDC(&dc);

        m_bProcessingBitmap=FALSE;
        nArrIndex=0;
        nCounter=1;
        nClip=0;
        m_bFirstTime=TRUE;
        m_bDrawText=FALSE;
        m_hBmpOld = 0;

        m_pDisplayFrame=(CWnd*)GetDlgItem(IDC_DISPLAY_STATIC);  

        // If you assert here, you did not assign your static display control
        // the IDC_ value that was used in the GetDlgItem(...). This is the
        // control that will display the credits.
        _ASSERTE(m_pDisplayFrame);

        m_pDisplayFrame->GetClientRect(&m_ScrollRect);
        nRet = SetTimer(DISPLAY_TIMER_ID,DISPLAY_SPEED,NULL);
         _ASSERTE(nRet != 0);
 
       return TRUE;  // return TRUE unless you set the focus to a control
                           // EXCEPTION: OCX Property Pages should return FALSE
}

//************************************************************************
//       OnTimer
//
//              On each of the display timers, scroll the window 1 unit. Each 20
//      units, fetch the next array element and load into work string. Call
//      Invalidate and UpdateWindow to invoke the OnPaint which will paint
//      the contents of the newly updated work string.
//************************************************************************
void CCredits::OnTimer(UINT nIDEvent)
{
        if (nIDEvent != DISPLAY_TIMER_ID) {
                CDialog::OnTimer(nIDEvent);
                return;
        }

        if (!m_bProcessingBitmap)
        if (nCounter++ % nCurrentFontHeight == 0) {     // every x timer events, show new line
                nCounter=1;
                m_szWork = pArrCredit[nArrIndex++];
                if (nArrIndex > ARRAYCOUNT-1) nArrIndex=0;
                nClip = 0;
                m_bDrawText=TRUE;
                }

        m_pDisplayFrame->ScrollWindow(0,SCROLLAMOUNT,&m_ScrollRect,&m_ScrollRect);
        nClip = nClip + abs(SCROLLAMOUNT);      

        CRect r;
        CWnd* pWnd = GetDlgItem(IDC_DISPLAY_STATIC);
        ASSERT_VALID(pWnd);
        pWnd->GetClientRect(&r);
        pWnd->ClientToScreen(r);
        ScreenToClient(&r);
        InvalidateRect(r,FALSE); // FALSE does not erase background

        CDialog::OnTimer(nIDEvent);
}

//************************************************************************
//       OnPaint
//
//              Send the newly updated work string to the display rect.
//************************************************************************
void CCredits::OnPaint()
{
        CPaintDC dc(this); // device context for painting
        
        PAINTSTRUCT ps;
        CDC* pDc = m_pDisplayFrame->BeginPaint(&ps);

        pDc->SetBkMode(TRANSPARENT);

        //*********************************************************************
        //      FONT SELECTION
        CFont m_fntArial;
        CFont* pOldFont;
        BOOL bSuccess;
        BOOL bUnderline;
        BOOL bItalic;

        if (!m_szWork.IsEmpty())
        switch (m_szWork[m_szWork.GetLength()-1] ) {
        case NORMAL_TEXT:
        default:
                bItalic = FALSE;
                bUnderline = FALSE;
                nCurrentFontHeight = NORMAL_TEXT_HEIGHT;
                bSuccess = m_fntArial.CreateFont(NORMAL_TEXT_HEIGHT, 0, 0, 0,
                        FW_THIN, bItalic, bUnderline, 0,
                        ANSI_CHARSET,  OUT_DEFAULT_PRECIS,
                        CLIP_DEFAULT_PRECIS,  PROOF_QUALITY,
                        VARIABLE_PITCH | 0x04 | FF_DONTCARE,  (LPSTR)"Arial");

                pDc->SetTextColor(NORMAL_TEXT_COLOR);
                pOldFont  = pDc->SelectObject(&m_fntArial);

                break;

        case TOP_LEVEL_GROUP:
                bItalic = FALSE;
                bUnderline = FALSE;
                nCurrentFontHeight = TOP_LEVEL_GROUP_HEIGHT;
                bSuccess = m_fntArial.CreateFont(TOP_LEVEL_GROUP_HEIGHT, 0, 0, 0,
                                 FW_BOLD, bItalic, bUnderline, 0,  ANSI_CHARSET,
                                 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,  PROOF_QUALITY,
                                 VARIABLE_PITCH | 0x04 | FF_DONTCARE, (LPSTR)"Arial");                   

                 pDc->SetTextColor(TOP_LEVEL_GROUP_COLOR);
                 pOldFont  = pDc->SelectObject(&m_fntArial);

                break;

        case GROUP_TITLE:
                bItalic = FALSE;
                bUnderline = FALSE;
                nCurrentFontHeight = GROUP_TITLE_HEIGHT;
                bSuccess = m_fntArial.CreateFont(GROUP_TITLE_HEIGHT, 0, 0, 0,
                        FW_BOLD, bItalic, bUnderline, 0,
                        ANSI_CHARSET, OUT_DEFAULT_PRECIS,
                        CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
                        VARIABLE_PITCH | 0x04 | FF_DONTCARE, (LPSTR)"Arial");

                pDc->SetTextColor(GROUP_TITLE_COLOR);
                pOldFont  = pDc->SelectObject(&m_fntArial);

                break;

        case TOP_LEVEL_TITLE:
                bItalic = FALSE;
                bUnderline = TRUE;
                nCurrentFontHeight = TOP_LEVEL_TITLE_HEIGHT;
                bSuccess = m_fntArial.CreateFont(TOP_LEVEL_TITLE_HEIGHT, 0, 0, 0,
                        FW_BOLD, bItalic, bUnderline, 0,  
                        ANSI_CHARSET, OUT_DEFAULT_PRECIS,
                        CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
                        VARIABLE_PITCH | 0x04 | FF_DONTCARE,  (LPSTR)"Arial");

                pDc->SetTextColor(TOP_LEVEL_TITLE_COLOR);
                pOldFont  = pDc->SelectObject(&m_fntArial);

                break;

        case DISPLAY_BITMAP:
                if (!m_bProcessingBitmap) {
                        CString szBitmap = m_szWork.Left(m_szWork.GetLength()-1);
                         if (!m_bmpWork.LoadBitmap((const char *)szBitmap)) {
                                CString str;
                                str.Format("Could not find bitmap resource \"%s\". "
                                                "Be sure to assign the bitmap a QUOTED resource name", szBitmap);
                                KillTimer(DISPLAY_TIMER_ID);
                                MessageBox(str);

                                return;
                           }
                           m_bmpCurrent = &m_bmpWork;
                           m_bmpCurrent->GetObject(sizeof(BITMAP), &m_bmpInfo);
                           m_size.cx = m_bmpInfo.bmWidth;       // width  of dest rect

                           RECT workRect;
                           m_pDisplayFrame->GetClientRect(&workRect);
                           m_pDisplayFrame->ClientToScreen(&workRect);
                           ScreenToClient(&workRect);
                            // upper left point of dest
                           m_pt.x = (workRect.right -
                                 ((workRect.right-workRect.left)/2) - (m_bmpInfo.bmWidth/2));
                           m_pt.y = workRect.bottom;

                           pBmpOld = m_dcMem.SelectObject(m_bmpCurrent);
                           if (m_hBmpOld == 0)   m_hBmpOld = (HBITMAP) pBmpOld->GetSafeHandle();

                           m_bProcessingBitmap = TRUE;
                    }

                    break;
             }

             CBrush bBrush(BLACK);
             CBrush* pOldBrush;
             pOldBrush  = pDc->SelectObject(&bBrush);

             // Only fill rect comprised of gap left by bottom of scrolling window
             r=m_ScrollRect;
             r.top = r.bottom-abs(SCROLLAMOUNT);
             pDc->DPtoLP(&r);

             if (m_bFirstTime) {
                    m_bFirstTime=FALSE;
                    pDc->FillRect(&m_ScrollRect,&bBrush);

             } else
                    pDc->FillRect(&r,&bBrush);
                    r=m_ScrollRect;
                    r.top = r.bottom-nClip;

                    if (!m_bProcessingBitmap) {
                            int x = pDc->DrawText((const char *)m_szWork, m_szWork.GetLength()-1, &r,
                                   DT_TOP| DT_CENTER| DT_NOPREFIX | DT_SINGLELINE);     
             
                             m_bDrawText=FALSE;
              
                    } else {
                             dc.StretchBlt( m_pt.x, m_pt.y-nClip, m_size.cx, nClip, &m_dcMem, 0, 0,
                                            m_bmpInfo.bmWidth-1, nClip, SRCCOPY );

                             if (nClip > m_bmpInfo.bmHeight) {
                                      m_bmpWork.DeleteObject();
      
                             m_bProcessingBitmap = FALSE;
                             nClip=0;
                             m_szWork.Empty();
                             nCounter=1;
                    }
                    pDc->SelectObject(pOldBrush);
                    bBrush.DeleteObject();
                    m_pDisplayFrame->EndPaint(&ps);

                    return;
           }
           pDc->SelectObject(pOldBrush);
           bBrush.DeleteObject();

           if (!m_szWork.IsEmpty()) {
                pDc->SelectObject(pOldFont);
                m_fntArial.DeleteObject();
           }

           m_pDisplayFrame->EndPaint(&ps);
           // Do not call CDialog::OnPaint() for painting messages
}

void CCredits::OnDestroy()
{
        CDialog::OnDestroy();

        m_dcMem.SelectObject(CBitmap::FromHandle(m_hBmpOld));
        m_bmpWork.DeleteObject();
}

 

- the end of this article -