ホームに戻る
 ウインドウのクラス化

0、はじめに

ウインドウをクラス化します。
クラス化することによりコードの見た目をすっきりさせて、
インターフェイス部分の開発効率の向上を目指します。
使用例としてライツアウトというゲームを作成しました。

1、ファイルの構成

本体ウインドウを nmWindow と名づけました。
また、本体ウインドウのクライアントに貼る
子ウインドウを nmCanvas と名付けました。
ともに、nmWindow.cpp というファイル内に記述します。
そのヘッダは nmWindow.h になります。
実行ファイルは LightsOut.cpp です。
LightsOut.cpp から nmWindow.h を読んでます。
bcc32 でのコンパイルは以下のようです。

bcc32 -W LightsOut.cpp nmWindow.cpp

2、工夫について

まず、クラス内にはプロシージャを static でしか定義できません。
プロシージャを継承、拡張できるようにするために。
static のプロシージャから
仮想関数のメソッドプロシージャを呼ぶようにしています。
次に、プロシージャを呼んだオブジェクトを
拾ってこれないという問題があります。
これは WM_CREATE のところで SetWindowLong を使い。
次に呼ばれたときには GetWindowLong を呼ぶことで解決します。
最後に、クラス内のマウスクリック処理を本体の処理に返す構造です。
これは addMouseListener で本体のポインタを渡しておき、
イベントが起こったら本体の処理を呼べるようにしています。
ここは MouseListener というインターフェイスで実装しています。

3、コード本体

/*
*   LightsOut.cpp
*/

#include <windows.h>
#include "nmWindow.h"

#define WIDTH 5
#define HEIGHT 5

class nmPanel : public nmCanvas{
  int num;
  int state_flag;
public:
  nmPanel():num(0), state_flag(0), nmCanvas(){};
  virtual ~nmPanel(){};
  int getNum(){return num;}
  void setNum(int num){this->num = num;}
  void reverseState();
  virtual LRESULT CanvasProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

void nmPanel::reverseState(){
  if(state_flag == 1){
    state_flag = 0;
  }
  else{
    state_flag = 1;
  }

  repaint();
}

// 描画
int DrawGr(HWND hWnd, HDC hdc, int color)
{
  HPEN hPen, hOldPen;
  HBRUSH hBrush, hOldBrush;
  RECT rt;
  GetClientRect(hWnd, &rt);

  hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
  hOldPen = (HPEN)SelectObject(hdc, (HPEN)hPen);
  hBrush = CreateSolidBrush(RGB(0, 0, color));
  hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
  Rectangle(hdc, 0, 0, rt.right, rt.bottom);
  SelectObject(hdc, hOldPen);
  SelectObject(hdc, hOldBrush);
  DeleteObject(hPen);
  DeleteObject(hBrush);

  return 0;
}

LRESULT nmPanel::CanvasProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message){
    case WM_PAINT:
      {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        if(state_flag == 1){
          DrawGr(hWnd, hdc, 255);
        }
        else{
          DrawGr(hWnd, hdc, 0);
        }
        EndPaint(hWnd, &ps);
      }
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0L;
}

class LightsOut : public MouseListener{
  nmWindow *testWnd;
  nmPanel *testPanel;
public:
  LightsOut():testWnd(NULL), testPanel(NULL){};
  void init(HINSTANCE hInstance);
  void deinit(){
    if(testWnd != NULL){
      delete testWnd;
      testWnd = NULL;
    }
    if(testPanel != NULL){
      delete [] testPanel;
      testPanel = NULL;
    }
  }
  virtual void mouseLButtonDown(int x, int y, LPVOID p){

    nmPanel* nmc = (nmPanel*)p;

    int num = nmc->getNum();

    testPanel[num].reverseState();

    // 上の判定
    if((num / WIDTH) >= 1){
      testPanel[num - WIDTH].reverseState();
    }
    // 左の判定
    if((num % WIDTH) >= 1){
      testPanel[num - 1].reverseState();
    }
    // 右の判定
    if((num % WIDTH) < (WIDTH - 1)){
      testPanel[num + 1].reverseState();
    }
    // 下の判定
    if((num / WIDTH) < (WIDTH - 1)){
      testPanel[num + WIDTH].reverseState();
    }
  }
  virtual void mouseRButtonDown(int x, int y, LPVOID p){};
};

void LightsOut::init(HINSTANCE hInstance){
  int i, t;

  testWnd = new nmWindow();
  testWnd->registerWindow(hInstance);
  testWnd->createWindow(hInstance, "LightsOut");
  testWnd->setWindowPos(100, 100, 400, 400);

  testPanel = new nmPanel[WIDTH * HEIGHT];

  for(i = 0; i < WIDTH * HEIGHT; i++){
    testPanel[i].setNum(i);
  }

  for(t = 0; t < HEIGHT; t++){
    for(i = 0; i < WIDTH; i++){
      int index = t * WIDTH + i;
      int w = 400 / WIDTH;
      int h = 400 / HEIGHT;
      testPanel[index].createWindow(testWnd->gethWnd(), 100 + index);
      testPanel[index].setWindowPos(i * w, t * h, w, h);
      testPanel[index].addMouseListener(this);
    }
  }
}

int APIENTRY WinMain(HINSTANCE hInstance,
  HINSTANCE hPreInstance,
  LPSTR cmdLine,
  int cmdShow)
{
  LightsOut *lo = new LightsOut();

  lo->init(hInstance);

  MSG msg;
  while(GetMessage(&msg, NULL, NULL, NULL))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  lo->deinit();

  return msg.wParam;
}

/*
*   nmWindow.h
*/

#ifndef NM_WINDOW
#define NM_WINDOW

class MouseListener{
public:
  virtual ~MouseListener(){}
  virtual void mouseLButtonDown(int x, int y, LPVOID p) = 0;
  virtual void mouseRButtonDown(int x, int y, LPVOID p) = 0;
};

class nmWindow{
  HWND hWnd;
  static char *class_name;
public:
  nmWindow(){hWnd = NULL;}
  virtual ~nmWindow(){};
  HWND gethWnd(void){return hWnd;}
  HINSTANCE gethInstance(void){return (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE);}
  int registerWindow(HINSTANCE hInstance);
  int createWindow(HINSTANCE hInstance, char *title);
  void setWindowPos(int x, int y, int w, int z);
  void SetPointer(HWND hWnd);
  static LRESULT CALLBACK CallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  virtual LRESULT MainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

class nmCanvas{
  HWND hCanvas;
  MouseListener *ml;
  static int regist_flag;
  static char *class_name;
  int registerWindow(HINSTANCE hInstance);
public:
  nmCanvas():hCanvas(NULL), ml(NULL){};
  virtual ~nmCanvas(){};
  int createWindow(HWND hWnd, int id);
  void setWindowPos(int x, int y, int w, int z);
  void repaint(){InvalidateRect(hCanvas, NULL, TRUE);}
  void SetPointer(HWND hWnd);
  void mouseLButtonDown(int x, int y){
    if(ml){
      ml->mouseLButtonDown(x, y, this);
    }
  }
  void mouseRButtonDown(int x, int y){
    if(ml){
      ml->mouseRButtonDown(x, y, this);
    }
  }
  void addMouseListener(MouseListener *ml){this->ml = ml;}
  static LRESULT CALLBACK CallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  virtual LRESULT CanvasProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

#endif

/*
*   nmWindow.cpp
*/

#include <windows.h>
#include "nmWindow.h"

/*
*  nmWindow
*/

char *nmWindow::class_name = "nmwin";

int nmWindow::registerWindow(HINSTANCE hInstance){
  WNDCLASS wd;

  wd.style = CS_HREDRAW | CS_VREDRAW;
  wd.lpfnWndProc = nmWindow::CallProc;
  wd.cbClsExtra = wd.cbWndExtra = 0;
  wd.hInstance = hInstance;
  wd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wd.hCursor = LoadCursor(NULL, IDC_ARROW);
  wd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  wd.lpszMenuName = NULL;
  wd.lpszClassName = class_name;

  if(!RegisterClass(&wd)){
    return 0;
  }

  return 1;
}

int nmWindow::createWindow(HINSTANCE hInstance, char *title){
  this->hWnd = CreateWindow(
    class_name, title,
    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    0, 0,
    300,
    200,
    NULL, NULL, hInstance,
    (LPVOID)this);

  return 1;
}

void nmWindow::setWindowPos(int x, int y, int w, int z){
  RECT rect;

  rect.left = x;
  rect.top = y;
  rect.right = x + w;
  rect.bottom = y + z;

  AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

  SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0);
}

void nmWindow::SetPointer(HWND hWnd){
  SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);
}

LRESULT CALLBACK nmWindow::CallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  nmWindow* nmw = (nmWindow*)GetWindowLong(hWnd, GWL_USERDATA);

  if(!nmw){
    if(message == WM_CREATE){
      nmw = (nmWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
    }

    if(nmw){
      nmw->SetPointer(hWnd);
    }
  }

  if(nmw){
    LRESULT lResult = nmw->MainProc(hWnd, message, wParam, lParam);
    return lResult;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT nmWindow::MainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  switch(message){
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0L;
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
}

/*
*  nmCanvas
*/

char *nmCanvas::class_name = "nmcan";
int nmCanvas::regist_flag = 0;

int nmCanvas::registerWindow(HINSTANCE hInstance){
  WNDCLASS wd;

  wd.style = CS_HREDRAW | CS_VREDRAW;
  wd.lpfnWndProc = nmCanvas::CallProc;
  wd.cbClsExtra = wd.cbWndExtra = 0;
  wd.hInstance = hInstance;
  wd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wd.hCursor = LoadCursor(NULL, IDC_ARROW);
  wd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  wd.lpszMenuName = NULL;
  wd.lpszClassName = class_name;

  if(!RegisterClass(&wd)){
    return 0;
  }

  return 1;
}

int nmCanvas::createWindow(HWND hWnd, int id){
  HINSTANCE hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE);

  if(regist_flag == 0){
    int result = registerWindow(hInst);
    if(result == 0){
      return 0;
    }
    regist_flag = 1;
  }

  hCanvas = CreateWindow(
    class_name, "", WS_CHILD | WS_VISIBLE,
    0, 0, 10, 10,
    hWnd, (HMENU)id, hInst,
    (LPVOID)this);

  return 1;
}

void nmCanvas::setWindowPos(int x, int y, int w, int z){
  SetWindowPos(hCanvas, HWND_TOP, x, y, w, z, 0);
}

void nmCanvas::SetPointer(HWND hWnd){
  SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);
}

LRESULT CALLBACK nmCanvas::CallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  nmCanvas* nmc = (nmCanvas*)GetWindowLong(hWnd, GWL_USERDATA);

  if(!nmc){
    if(message == WM_CREATE){
      nmc = (nmCanvas*)((LPCREATESTRUCT)lParam)->lpCreateParams;
    }

    if(nmc){
      nmc->SetPointer(hWnd);
    }
  }

  if(nmc){
    if(message == WM_LBUTTONDOWN){
      int xPos, yPos;

      xPos = LOWORD(lParam);
      yPos = HIWORD(lParam);

      nmc->mouseLButtonDown(xPos, yPos);
    }
    else if(message == WM_RBUTTONDOWN){
      int xPos, yPos;

      xPos = LOWORD(lParam);
      yPos = HIWORD(lParam);

      nmc->mouseRButtonDown(xPos, yPos);
    }
    LRESULT lResult = nmc->CanvasProc(hWnd, message, wParam, lParam);
    return lResult;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT nmCanvas::CanvasProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  switch(message){
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0L;
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
}

inserted by FC2 system