第338章 時計を作る 角を取る


今回は矩形の角を取り、時計の形を整えます。時刻そのものはまだデジタル表示のみです。



非矩形のウィンドウの作り方はすでに第章でやりました。今回は、円と長方形を組み合わせて 非矩形ウィンドウを作ります。

リージョンを作る時スケッチを用意しておくと間違いが少なくてすみます。

左の円のリージョンハンドルをhRound1Rgn, 右の円をhRound2Rgn, 内部の長方形(点線)をhRectRgn とします。

hRound1RgnとhRectRgnのORをhRgn1, hRound2RgnとhRectRgnのORをhRgn2とすると、 必要なリージョンはhRgn1とhRgn2のORということになります。

必要なリージョンができたらSetWindowRgn関数を実行します。非矩形ウィンドウについては 第125章ですでに解説してあります。



では、プログラムを見てみましょう。

// clock02.cpp

#define CLOCK_WIDTH 250        //時計全体のウィンドウ幅
#define CLOCK_HEIGHT 60        //高さ
#define MYTIMER 1            //タイマーID
#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
HFONT SetMyFont(LPCTSTR, int);


char szClassName[] = "clock02";    //ウィンドウクラス
char szAppName[] = "猫クロック"; //アプリケーション名
char szBuf[64]; //時刻表示用

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hCursor = (HCURSOR)LoadImage(NULL,
        MAKEINTRESOURCE(IDC_ARROW),
        IMAGE_CURSOR,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);

    return (RegisterClassEx(&wc));
}

//ウィンドウの生成

BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;


    hWnd = CreateWindow(szClassName,
            "猫でもわかるWindowsプログラミング", //タイトルバーにこの名前が表示されます
            WS_POPUP, //ウィンドウの種類
            0,    //X座標
            0,    //Y座標
            CLOCK_WIDTH,    //幅
            CLOCK_HEIGHT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
ここまでは、前章と同じです。
//ウィンドウプロシージャ

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id, wx, wy, x, y;
    SYSTEMTIME st;
    PAINTSTRUCT ps;
    HDC hdc;
    HFONT hFont;
    SIZE s;
    HRGN hRgn, hRgn1, hRgn2, hRound1Rgn, hRound2Rgn, hRectRgn;
    HBRUSH hBrush;

    switch (msg) {
        case WM_CREATE:
            SetTimer(hWnd, MYTIMER, 500, NULL);
            wx = GetSystemMetrics(SM_CXSCREEN);
            wy = GetSystemMetrics(SM_CYSCREEN);
            x = (wx - CLOCK_WIDTH) / 2;
            y = (wy - CLOCK_HEIGHT) / 2;
            MoveWindow(hWnd, x, y, CLOCK_WIDTH, CLOCK_HEIGHT, TRUE);

            hRgn = CreateRectRgn(0, 0, 1, 1);
            hRgn1 = CreateRectRgn(0, 0, 1, 1);
            hRgn2 = CreateRectRgn(0, 0,1, 1);

            hRound1Rgn = CreateEllipticRgn(0, 0, CLOCK_HEIGHT, CLOCK_HEIGHT);
            hRectRgn = CreateRectRgn(CLOCK_HEIGHT / 2,
                0, 
                CLOCK_WIDTH - CLOCK_HEIGHT / 2,
                CLOCK_HEIGHT);
            CombineRgn(hRgn1, hRound1Rgn, hRectRgn, RGN_OR);
            hRound2Rgn = CreateEllipticRgn(CLOCK_WIDTH - CLOCK_HEIGHT,
                0,
                CLOCK_WIDTH,
                CLOCK_HEIGHT);
            CombineRgn(hRgn2, hRound2Rgn, hRectRgn, RGN_OR);
            CombineRgn(hRgn, hRgn1, hRgn2, RGN_OR);
            SetWindowRgn(hWnd, hRgn, TRUE);

            DeleteObject(hRound1Rgn);
            DeleteObject(hRound2Rgn);
            DeleteObject(hRectRgn);
            DeleteObject(hRgn1);
            DeleteObject(hRgn2);
            
            break;
        case WM_RBUTTONDOWN:
            SendMessage(hWnd, WM_CLOSE, 0, 0);
            break;
        case WM_LBUTTONDOWN:
            PostMessage(hWnd, WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lp);
            break;
        case WM_TIMER:
            if (wp != MYTIMER)
                return DefWindowProc(hWnd, msg, wp, lp);
            GetLocalTime(&st);
            wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            hBrush = CreateSolidBrush(RGB(0, 255, 255));
            SelectObject(hdc, hBrush);
            PatBlt(hdc, 0, 0, CLOCK_WIDTH, CLOCK_HEIGHT, PATCOPY);

            hFont = SetMyFont("MS ゴシック", 40);
            SelectObject(hdc, hFont);
            GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
            x = (CLOCK_WIDTH - s.cx) / 2;
            y = (CLOCK_HEIGHT - s.cy) / 2;
            SetBkMode(hdc, TRANSPARENT);
            TextOut(hdc, x, y, szBuf, (int)strlen(szBuf));
            DeleteObject(hFont);
            DeleteObject(hBrush);
            EndPaint(hWnd, &ps);
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd,
                "終了してもよろしいですか",
                "確認",
                MB_YESNO | MB_ICONQUESTION);
            if (id == IDYES)
                DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            KillTimer(hWnd, MYTIMER);
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
WM_CREATEメッセージが来たら、リージョンを作ってSetWindowRgn関数を 呼んでいます。不要になったリージョンを破棄しています。SetWindowRgnに使った リージョンはプログラム終了時に破棄してもよいのですが、何もしなくてもシステムが 自動的に破棄してくれます。ここでは、自分で破棄しないでシステムに任せることにしました。

WM_PAINTメッセージが来たらPatBlt関数で塗りつぶしています。

BOOL PatBlt(
  HDC hdc,      // デバイスコンテキストハンドル
  int nXLeft,   // x 座標
  int nYLeft,   // y 座標
  int nWidth,   // 幅
  int nHeight,  // 高さ
  DWORD dwRop   // ラスタオペレーションコード
);
指定された長方形を指定された方法で塗りつぶします。

hdcには、デバイスコンテキストハンドルを指定します。

nXLeft, nYLeft, nWidth, nHeightで長方形を指定します。

dwRopは、ラスターオペレーションコードです。これは、次の中から指定します。

PATCOPY指定のパターンで描画先へコピーします
PATINVERTXORで指定のパターンと描画先の色を組み合わせます
DSTINVERT描画先の色を反転します
BLACKNESS黒で描画先を塗りつぶします
WHITENESS白で描画先を塗りつぶします

塗りつぶしが終わったら、テキストを描画します。この時 SetBkMode関数でTRANSPARENTを指定しておかないと、みっともない結果となります。 SetBkMode関数については第26章を参照してください。

HFONT SetMyFont(LPCTSTR face, int h)
{
    HFONT hFont;
    hFont = CreateFont(h,    //フォント高さ
        0,                    //文字幅
        0,                    //テキストの角度
        0,                    //ベースラインとx軸との角度
        FW_REGULAR,            //フォントの重さ(太さ)
        FALSE,                //イタリック体
        FALSE,                //アンダーライン
        FALSE,                //打ち消し線
        SHIFTJIS_CHARSET,    //文字セット
        OUT_DEFAULT_PRECIS,    //出力精度
        CLIP_DEFAULT_PRECIS,//クリッピング精度
        PROOF_QUALITY,        //出力品質
        FIXED_PITCH | FF_MODERN,//ピッチとファミリー
        face);    //書体名
    
    return hFont;
}
この関数に変更はありません。

実行結果は左の図のような感じになります。背景色・文字色などを ユーザーが自由に設定できるように改良してみてください。 また、このようなオプションをプログラム終了時に保存しておいて 次回起動時に反映されるように工夫してみてください。




[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 29/Dec/2002 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。