« C4D ダイアログのユーザエリアの定期的な割り込み処理… | トップページ | C4D ユーザエリア 個人的テスト(コード非公開) »

C4D ダイアログのユーザエリアで独自のガジェットを作ったとして…イベントやデータのやり取りは?

まぁ、独自のガジェットを作るまで行き着いていないですが…
もし、完成したら…

イベント関連、数値(データ)のやり取り…
なんだか、面倒臭そうだなぁ…

独自のガジェットが完成しているわけではないので、詳しくはテストできないけど…
今回試す、ベースのスクリプトです。

/*
ダイアログの数値ボックスを使って座標を入力し、ユーザエリアの座標に長方形を描画。
ユーザエリアをクリックした座標をテキストボックスに流し込む。
2009.9.17
*/


//-----------------------------------------------------
enum
{
  //ダイアログガジェットid
  ua_id = 1000,//ユーザエリアid
  input_x_tx,
  input_x,     //マウスX座標用エディットナンバーid
  input_y_tx,
  input_y,     //マウスY座標用エディットナンバーid

  //ユーザエリアのサイズ
  ua_w = 300,
  ua_h = 200
}


//-----------------------------------------------------
class testUserArea:GeUserArea
{
  public:
    var mouse_x , mouse_y;

    testUserArea(id , dlg);
    GetUserWidth();
    GetUserHeight();
    Draw(x1 , y1 , x2 , y2);
    InputEvent(msg);
}

testUserArea::testUserArea(id , dlg)
{
  super(id , dlg);
}

testUserArea::GetUserWidth()
{
  return ua_w;
}

testUserArea::GetUserHeight()
{
  return ua_h;
}

testUserArea::Draw(x1 , y1 , x2 , y2)
{
  SetClippingRegion(0 , 0 , ua_w - 1 , ua_h - 1);

  DrawSetPen(vector(0.0));
  DrawRectangle(0 , 0 , ua_w - 1 , ua_h - 1);
}

testUserArea::InputEvent(msg)
{
 
}


//-----------------------------------------------------
class testDialog:GeModalDialog
{
  public:
    var ua;
    var mus_x , mus_y;

    testDialog();
    CreateLayout();
    Init();
    Command(id , msg);
    AskClose();
}

testDialog::testDialog()
{
  super();
}

testDialog::CreateLayout()
{
  SetTitle("testDialog");

  AddUserArea(ua_id , 0 , ua_w , ua_h);
  ua = new(testUserArea , ua_id , this);

  AddGroupBeginV(10000 , BFH_SCALEFIT , 2 , "group1", 0);
    AddGroupBorderSpace(20 , 10 , 20 , 10);

    AddStaticText(input_x_tx , BFH_LEFT , 50 , 0 , "X" , BORDER_NONE);
    AddEditNumber(input_x , BFH_SCALEFIT , 0, 0);
    AddStaticText(input_y_tx , BFH_LEFT , 50 , 0 , "Y" , BORDER_NONE);
    AddEditNumber(input_y , BFH_SCALEFIT , 0, 0);
  AddGroupEnd();

  AddSeparatorH(0);

  AddDlgGroup(DR_DLGGROUP_OK|DR_DLGGROUP_CANCEL);

}

testDialog::Init()
{
 
}

testDialog::Command(id , msg)
{
 
}

testDialog::AskClose()
{
 
}



//-----------------------------------------------------
main(doc , op)
{
  var dlg = new(testDialog);
  dlg->Open(-1 , -1);

  println("dlg->mus_x: " , dlg->mus_x);
  println("dlg->mus_y: " , dlg->mus_y);
}

この段階で実行すると、こんなダイアログが出てきます。

Userarea_15

上がユーザエリアで、ここをクリックしたら、クリックされた所にドットを表示させます。
下のX/Yにクリックされたユーザエリア上の座標を自動入力します。
逆に、下のX/Yに数値を入力したら、ユーザエリアの対応する座標にドットを表示します。
ダイアログを閉じると、コンソールにX/Yの値を表示します。

今の段階では、ダイアログをただ開くだけ。
閉じてもコンソールにはnilが表示されます。

上のスクリプトは、ほぼ完成状態です。
追加部分は…
ダイアログを開いた時の初期化testDialog::Init()
ダイアログを操作している時のイベントが発生したときに呼ばれるtestDialog::Command()
ダイアログを閉じた時に呼ばれるtestDialog::AskClose()
クリックした時のドットを表示させるユーザエリアの再描画のtestUserArea::Draw()
ユーザエリアをクリックした時に呼ばれるtestUserArea::InputEvent()
です。

ダイアログの初期化、閉じた時の処理は、以前の記事で取り上げているので問題無いと思います。

ユーザエリアをクリックした時のドット表示も問題ありません。
殆どが問題無いと思います。

では、ダイアログの初期化から…testDialog::Init()

ダイアログのX/Y値を保持する変数testDialogクラスのメンバ変数mus_x/mous_yに初期値を入力します。
その値を、エディットボックスとユーザエリアに入力します。
初期値は、ユーザエリアの真ん中の値にします。

testDialog::Init()
{
  //X,yの初期値はユーザエリアの中心座標
  mus_x = ua_w / 2;
  mus_y = ua_h / 2;

  //x,y値をユーザエリアとエディットボックスに入力します。
  ua->mouse_x = mus_x;
  ua->mouse_y = mus_y;
  SetInt(input_x , mus_x , 0 , ua_w - 1 , 1);
  SetInt(input_y , mus_y , 0 , ua_h - 1 , 1);
}

ユーザエリアにX/Yを保持する為にtestUserAreaクラスのメンバ変数mouse_x/mouse_yを用意しました。
ユーザエリア内でのX/Y値は、このmouse_x/mouse_yを使います。
Init()メンバ関数は、CreateLayout()メンバ関数が呼ばれた後に実行されます。
ですからユーザエリアを割り当てる変数uaには、既にユーザエリアが割り当てられています。

これで、ダイアログを開くとエディットボックスに数値が入力されます。

Userarea_16

ダイアログを閉じるとコンソールにはX/Y値が表示されます。
が、値を変更しても、コンソールに表示された値は変化しません。

次に、ユーザエリアにドットを表示させます。
ダイアログでのX/Y値をユーザエリアのmouse_x/mouse_yへ入力したので、この値を中心に描画します。

では、testUserArea::Draw()へ追加します。

testUserArea::Draw(x1 , y1 , x2 , y2)
{
  SetClippingRegion(0 , 0 , ua_w - 1 , ua_h - 1);

  DrawSetPen(vector(0.0));
  DrawRectangle(0 , 0 , ua_w - 1 , ua_h - 1);

  DrawSetPen(vector(1.0));
  DrawRectangle(mouse_x -3 , mouse_y -3 , mouse_x + 3 , mouse_y + 3);
}

X/Y値のそれぞれに、±3で7×7の白いドットを描画しました。

Userarea_17

当然ですが、この時点では、まだマウスクリックには対応していません。

さて、これで、ダイアログも、ユーザエリアも初期化が住みました。
と、言ってもユーザエリアは初期化した、と言うには…

これで、testUserArea::Draw()が完成したので、ユーザエリアのmouse_x/mouse_yが変化するとドットも移動します。

さて、スクリプトの中を行ったり来たりする事になりますが…

次に追加して完成させるのは、ダイアログを閉じたときにエディットボックスから値をメンバ変数mus_x/mus_yへ吸い出します。
これで、閉じた後のコンソールの表示が変化に対応します。

testDialog::AskClose()
{
  mus_x = GetInt(input_x);
  mus_y = GetInt(input_y);
}

これで残りは…
ダイアログを操作した時にイベントが発生したら呼ばれるtestDialog::Command()
ユーザエリアをクリックした時のイベントが発生したら呼ばれるtestUserArea::InputEvent()
の2つです。

さて、ダイアログを操作してイベントが発生したら呼ばれるダイアログのCommand()メンバ関数から

Command()メンバ関数が呼ばれるときに、ガジェットのidとメッセージmsgが、渡されます。
エディットボックスの値を変更したら、変更されたガジェットのidが渡されます。
ユーザエリアがクリックされたらユーザエリアのidも渡されなければいけません。

エディットボックスが変更された場合は、対応するガジェットから値を引き出して、ダイアログ用のX/Y変数のmus_x/mus_yへ入力します。
さらに、ユーザエリア用のX/Y変数のmouse_x/mouse_yにも入力します。
ユーザエリアを再描画するとドットが移動します。

ユーザエリアがクリックされるとコンソールに表示されるようにしました。

では、Command()メンバ変数です。

testDialog::Command(id , msg)
{
  switch(id)
  {
    case input_x:
      ua->mouse_x = GetInt(input_x);
      break;

    case input_y:
      ua->mouse_y = GetInt(input_y);
      break;

    case ua_id:
      println("ダイアログ内イベント:ユーザエリア");
  }

  ua->Redraw();
}

msgは使っていません。
idだけswitch()で判別して処理します。
内容は先ほど説明した通りです。

このまま、実行してもユーザエリアでのクリックには、反応しません。
ユーザエリアをクリックしてもコンソールに何も表示されません。
エディットボックスの変更には、ユーザエリアのドットが反応します。

Userarea_18

クリック処理をしていないからではありません。
Command()メンバ関数にユーザエリアのidが届いていません。

このイベントでidを送る処理は後ほど…
Command()メンバ関数は保留です。

では、ユーザエリアのイベント発生で呼ばれるInputEvent()メンバ関数を追加します。
まずは、クリックしたらドットを移動させます。
ついでに、ホイールを回すと上下に移動させます。

testUserArea::InputEvent(msg)
{
  if(msg->GetData(BFM_INPUT_DEVICE) != BFM_INPUT_MOUSE)return;

  switch(msg->GetData(BFM_INPUT_CHANNEL))
  {
    case BFM_INPUT_MOUSELEFT://左クリック
      mouse_x = Global2LocalX(msg->GetData(BFM_INPUT_X));
      mouse_y = Global2LocalY(msg->GetData(BFM_INPUT_Y));
      break;

    case BFM_INPUT_MOUSEWHEEL://ホイール
      mouse_y += (msg->GetData(BFM_INPUT_VALUE) / 120);
      if(mouse_y < 0)mouse_y = 0;
      if(mouse_y > ua_h - 1)mouse_y = ua_h - 1;
  }

  Redraw();
}

入力デバイスがマウス以外は除外し処理を終了。
switch()でキーコードを判別して左ボタン/ホイールそれぞれの処理をさせます。

左ボタンの場合は、座標を取得しユーザエリア座標へ変換したものをユーザエリア用のX/Y変数のmouse_x/mouse_yへ入力します。

ホイールの場合は、ホイールの数値を読み込み、通常±120なので120で割り±1にして、mouse_yに加算します。
数値の加算はユーザエリアの高さの範囲ないです。if()を使いましたがC.O.F.F.E.E.には、専用の関数が用意されています。

最後に、ユーザエリアを再描画します。

これで、クリックに対応しました。

Userarea_19

ですが、ユーザエリアにイベントが起きても、ダイアログにはユーザエリアのidが伝わっていません。
ですので、クリックしてもコンソールには何も表示されません。

さて、ユーザエリアのイベントをダイアログに伝えるには…
GeUserArea::SendParentMessage(msg)を使います。

で、InputEvent()に渡されたmsgをそのまま渡すのかと言うと違うらしいですね…

新たにコンテナを作り、ある指定したID(BFM_ACTION_ID)にユーザエリアのIDを入力し…
SendParentMessage()でコンテナを渡す。
と、言う事らしいです。

では、InputEvent()へ追加します。

testUserArea::InputEvent(msg)
{
  if(msg->GetData(BFM_INPUT_DEVICE) != BFM_INPUT_MOUSE)return;

  switch(msg->GetData(BFM_INPUT_CHANNEL))
  {
    case BFM_INPUT_MOUSELEFT://左クリック
      mouse_x = Global2LocalX(msg->GetData(BFM_INPUT_X));
      mouse_y = Global2LocalY(msg->GetData(BFM_INPUT_Y));
      break;

    case BFM_INPUT_MOUSEWHEEL://ホイール
      mouse_y += (msg->GetData(BFM_INPUT_VALUE) / 120);
      if(mouse_y < 0)mouse_y = 0;
      if(mouse_y > ua_h - 1)mouse_y = ua_h - 1;
  }

  //ダイアログにイベントを送ります。
  var act = new(BaseContainer , BFM_ACTION);
  act->SetData(BFM_ACTION_ID , GetId());
  SendParentMessage(act);

  Redraw();
}

GetId()メンバ関数は現在のユーザエリアのidを取得します。
定数のua_idを使っても良いですが…GetId()の方が確実です。
これで、クリックするとコンソールに”ダイアログ内イベント:ユーザエリア”が表示されます。
ダイアログのCommand()にユーザエリアのidが伝わりました。

では、Command()のユーザエリアの部分を変更します。

testDialog::Command(id , msg)
{
  switch(id)
  {
    case input_x:
      ua->mouse_x = GetInt(input_x);
      break;

    case input_y:
      ua->mouse_y = GetInt(input_y);
      break;

    case ua_id:
      mus_x = ua->mouse_x;
      mus_y = ua->mouse_y;
      SetInt(input_x , mus_x , 0 , ua_w - 1 , 1);
      SetInt(input_y , mus_y , 0 , ua_h - 1 , 1);
  }

  ua->Redraw();
}

ダイアログのイベントがユーザエリアの場合は…
ユーザエリアのX/Y変数のmouse_x/mouse_yからダイアログのX/Y変数のmus_x/mus_yに代入し…
ダイアログのX/Y変数のmus_x/mus_yをエディットボックスに入力します。

これで、クリックしてもエディットボックスの数値は問題なく変更されます。

今回は終わりです。
ユーザエリアのメンバー関数でテストしていないものもありますが、モードレスダイアログの為のものもあります。
そのうちにでも…
ユーザエリアの単純なテストも、これで終了です。
何か凄い独自のガジェットが作れると良いですね…

それでは最後に一連で…

/*
ダイアログの数値ボックスを使って座標を入力し、ユーザエリアの座標に長方形を描画。
ユーザエリアをクリックした座標をテキストボックスに流し込む。
2009.9.17
*/


//-----------------------------------------------------
enum
{
  //ダイアログガジェットid
  ua_id = 1000,//ユーザエリアid
  input_x_tx,
  input_x,     //マウスX座標用エディットナンバーid
  input_y_tx,
  input_y,     //マウスY座標用エディットナンバーid

  //ユーザエリアのサイズ
  ua_w = 300,
  ua_h = 200
}


//-----------------------------------------------------
class testUserArea:GeUserArea
{
  public:
    var mouse_x , mouse_y;

    testUserArea(id , dlg);
    GetUserWidth();
    GetUserHeight();
    Draw(x1 , y1 , x2 , y2);
    InputEvent(msg);
}

testUserArea::testUserArea(id , dlg)
{
  super(id , dlg);
}

testUserArea::GetUserWidth()
{
  return ua_w;
}

testUserArea::GetUserHeight()
{
  return ua_h;
}

testUserArea::Draw(x1 , y1 , x2 , y2)
{
  SetClippingRegion(0 , 0 , ua_w - 1 , ua_h - 1);

  DrawSetPen(vector(0.0));
  DrawRectangle(0 , 0 , ua_w - 1 , ua_h - 1);

  DrawSetPen(vector(1.0));
  DrawRectangle(mouse_x -3 , mouse_y -3 , mouse_x + 3 , mouse_y + 3);
}

testUserArea::InputEvent(msg)
{
  if(msg->GetData(BFM_INPUT_DEVICE) != BFM_INPUT_MOUSE)return;

  switch(msg->GetData(BFM_INPUT_CHANNEL))
  {
    case BFM_INPUT_MOUSELEFT://左クリック
      mouse_x = Global2LocalX(msg->GetData(BFM_INPUT_X));
      mouse_y = Global2LocalY(msg->GetData(BFM_INPUT_Y));
      break;

    case BFM_INPUT_MOUSEWHEEL://ホイール
      mouse_y += (msg->GetData(BFM_INPUT_VALUE) / 120);
      if(mouse_y < 0)mouse_y = 0;
      if(mouse_y > ua_h - 1)mouse_y = ua_h - 1;
  }

  //ダイアログにイベントを送ります。
  var act = new(BaseContainer , BFM_ACTION);
  act->SetData(BFM_ACTION_ID , GetId());
  SendParentMessage(act);

  Redraw();
}


//-----------------------------------------------------
class testDialog:GeModalDialog
{
  public:
    var ua;
    var mus_x , mus_y;

    testDialog();
    CreateLayout();
    Init();
    Command(id , msg);
    AskClose();
}

testDialog::testDialog()
{
  super();
}

testDialog::CreateLayout()
{
  SetTitle("testDialog");

  AddUserArea(ua_id , 0 , ua_w , ua_h);
  ua = new(testUserArea , ua_id , this);

  AddGroupBeginV(10000 , BFH_SCALEFIT , 2 , "group1", 0);
    AddGroupBorderSpace(20 , 10 , 20 , 10);

    AddStaticText(input_x_tx , BFH_LEFT , 50 , 0 , "X" , BORDER_NONE);
    AddEditNumber(input_x , BFH_SCALEFIT , 0, 0);
    AddStaticText(input_y_tx , BFH_LEFT , 50 , 0 , "Y" , BORDER_NONE);
    AddEditNumber(input_y , BFH_SCALEFIT , 0, 0);
  AddGroupEnd();

  AddSeparatorH(0);

  AddDlgGroup(DR_DLGGROUP_OK|DR_DLGGROUP_CANCEL);

}

testDialog::Init()
{
  //X,yの初期値はユーザエリアの中心座標
  mus_x = ua_w / 2;
  mus_y = ua_h / 2;

  //x,y値をユーザエリアとエディットボックスに入力します。
  ua->mouse_x = mus_x;
  ua->mouse_y = mus_y;
  SetInt(input_x , mus_x , 0 , ua_w - 1 , 1);
  SetInt(input_y , mus_y , 0 , ua_h - 1 , 1);
}

testDialog::Command(id , msg)
{
  switch(id)
  {
    case input_x:
      ua->mouse_x = GetInt(input_x);
      break;

    case input_y:
      ua->mouse_y = GetInt(input_y);
      break;

    case ua_id:
      mus_x = ua->mouse_x;
      mus_y = ua->mouse_y;
      SetInt(input_x , mus_x , 0 , ua_w - 1 , 1);
      SetInt(input_y , mus_y , 0 , ua_h - 1 , 1);
  }

  ua->Redraw();
}

testDialog::AskClose()
{
  mus_x = GetInt(input_x);
  mus_y = GetInt(input_y);
}



//-----------------------------------------------------
main(doc , op)
{
  var dlg = new(testDialog);
  dlg->Open(-1 , -1);

  println("dlg->mus_x: " , dlg->mus_x);
  println("dlg->mus_y: " , dlg->mus_y);
}

|

« C4D ダイアログのユーザエリアの定期的な割り込み処理… | トップページ | C4D ユーザエリア 個人的テスト(コード非公開) »