Webエリアを配置したフォームはそうでないフォームと振る舞いが微妙に異なる、という印象を受けている。たとえばダイアログを表示すると親ウインドウに隠れてしまって操作できなくなる問題が起きたりする。4Dのマニュアルを調べると、ウインドウを手前に持ってくるコマンドはBring to front、引数はプロセスID。つまりプロセスを一番手前に持ってくるのであって、同じプロセス内で動いているウインドウの前後関係を操作するコマンドは存在しない。4Dは同じプロセス内で表示されたウインドウの前後関係を保証しない、のが仕様であるらしい。確かに今回の問題はmacOSでは起こらずWindowsだけで発生していた。ならばNew processでウインドウを制御するしかない。
New processは、以前(v2011の頃)使っていたが起動が遅くて評判が良くないため使わなくなっていたのだが。どうやらv13あたりで速度が改善されたようだ。それならばと使ってみたところ、実装した結果が良かったので報告する。
当社がコンポーネントで配布している「Calendar」というプロジェクトがある。呼び出し側で、Webエリアが配置されたフォームから呼び出すと裏に回ってしまう。次のようなコードで使う。
//カレンダー表示 vB02_btnCADateのボタンメソッド
C_DATE($date)
$date:=vB02_fldDate
// クリックされたボタンの近くに表示
C_LONGINT($left;$top)
zz_obj_LeftTop (->vB02_btnCADate;->$left;->$top;398;441)
$dlg_ok:=D20_Calendar (->$date;$left;$top;"入力")
If ($dlg_ok=1)
vB02_fldDate:=$date
End if
これが裏に回って困っていた。そこでNew processを導入。次のような、D20_Calendarをラップした「D20_Calendar_np」を作成。コンポーネント側は修正しないとしてみた。
//D20_Calendar_np
//20210905 wat
//ダイアログが後ろに隠れる問題がWinだけで発生。おそらくVPエリア(Webエリア)がらみ
//別プロセスでフロントに持ってくることで解決しようとしている
C_LONGINT($1;$left)
$left:=$1
C_LONGINT($2;$top)
$top:=$2
C_LONGINT($3;$parent_process)
$parent_process:=$3
C_OBJECT($4;$sobCA)
$sobCA:=$4
C_DATE($date;$retDate)
C_LONGINT($dlgOk)
//カレンダーの初期値を取得、共有オブジェクトを読む
$date:=$sobCA.date
$retDate:=$date
$dlgOk:=D20_Calendar (->$date;$left;$top;"入力")
If ($dlgOk=1)
//OKされた時だけ日付を取得
$retDate:=$date
End if
Use ($sobCA)
//共有オブジェクトに代入するときはUseブロックで。
$sobCA.date:=$retDate
$sobCA.dlgOk:=$dlgOk
End use
RESUME PROCESS($parent_process)
呼び出し側は次のようになる。
//カレンダー表示
C_DATE($date)
$date:=vB02_fldDate
// クリックされたボタンの近くに表示するための座標値計算
C_LONGINT($left;$top)
zz_obj_LeftTop (->vB02_btnCADate;->$left;->$top;398;441)
$left:=$left-398
//カレンダー表示を別プロセスで実行、確実にフロントに持ってくる
C_LONGINT($myProc;$proc)
$myProc:=Current process
C_OBJECT($sobCA)
$sobCA:=New shared object
Use ($sobCA)
//共有オブジェクトに代入するときはUseブロックで。
$sobCA.date:=vB02_fldDate
End use
//ニュープロセスで表示
$proc:=New process("D20_Calendar_np";0;"D20_Calendar_np";$left;$top;$myProc;$sobCA)
If ($proc#0)
BRING TO FRONT($proc)
//子プロセスが終了するまで待つ
PAUSE PROCESS($myProc)
//終了した子プロセスから、共有オブジェクトで、結果を取得
$dlgOk:=$sobCA.dlgOk
If ($dlgOk=1)
//共有オブジェクト
vB02_fldDate:=$sobCA.date
//20210902 wat view proを更新
C_TEXT($frmObjName)
$frmObjName:=B02_varVpObjName_get
C_LONGINT($col;$row)
C_TEXT($str)
//作成日
$str:=JCL_str_Date (vB02_fldDate)
$col:=8
$row:=0
JCL_vp_SetTextValue ($frmObjName;$col;$row;$str)
End if
End if
BRING TO FRONT($myProc)
親プロセス(呼び出し)側ではNew processでウインドウを表示するメソッドを実行して、子プロセスをBring to frontしてPAUSE PROCESSで待つ。子プロセスはRESUME PROCESSして終わる。すると親プロセスのPAUSEが解除されて次に進み、共有オブジェクトに値が返ってきている。$sobCA.dlgOkでダイアログがOKされかたを判定して、$sobCA.dateをフォームオブジェクトに代入する、という仕組み。
子プロセスに渡すオブジェクトが「共有オブジェクト(Shared object)」というところがミソ。共有オブジェクトを参照するときは普通のオブジェクトだが、代入するときはUse – End useブロックで排他制御をする必要がある。
コンポーネント側を修正しないように実装してみたが、一方的にカレンダー側がNew processになると、呼び出し側も共有オブジェクトを使うなどの対応に迫られるので、今回の実装はありだなと結論。コンポーネント側に両方のAPIを備えるのが正解だろう。