サイトトップ

Director Flash 書籍 業務内容 プロフィール

Macromedia Director非公式テクニカルノート

MIAWのmodalをtrueにしてもステージが前面に切り替わる

ID: FN0108002 Product: Director

Platform: Windows
Viersion: 8.0 and 8.5

1. 現象
「プロジェクタ作成オプション」で「タイトルバーを表示」に設定した場合の現象です。MIAW(Movie In A Window: ウィンドウ内ムービー)の'modal'ウィンドウプロパティを'true'に設定しても、ステージのタイトルバーをクリックするとステージが前面に切り替わり、MIAWが背後に表示されてしまうことがあります。

2. 原因
Windows版のDirector 8.0および8.5のプロジェクタで報告されている現象です。DirectorのMIAWの扱いに不具合があるものと考えられます。updateStageのQuirk List - Director 8.5でも確認されました(「MIAW,modal」の項)。

3. 対処法
サードパーティのエクストラなどを使って、MIAWをつねに最前面に表示するよう設定します。MasterApp XtraとBuddy APIを使用して、MIAWを最前面に設定する例をご紹介します。この設定は、MIAW側で行います。一方、'modal'を'true'に設定するのは、MIAWを開く(通常ステージ)側だと思われます。したがって、以下のスクリプト中では、'modal'の設定は行っていません。けれど、以下のスクリプトを動作させるためには、MIAWの'modal'を'true'に設定する必要があります。また、スクリプトは、プロジェクタで実行することを前提としています。オーサリング時には、正しく動作しませんので、ご注意ください。

MasterApp Xtraを使った場合
MasterApp Xtraは、ウィンドウやタスクなど、オペレーティングシステムの制御に関わる数多くのメソッドを提供します。クロスプラットホーム対応です。MasterApp Xtraのメソッドについて、詳しくはエクストラ付属のドキュメントまたはサイトのUser Guideをご参照ください。

使用するメソッドは、'mappKeepOnTop'と'mappDontKeepOnTop'、'mappGetActiveWindow'の計3つです。

まず、'mappKeepOnTop'は、指定したウィンドウをすべてのウィンドウのつねに最前面に表示します。Win32のみの機能ですので、ご注意ください。他のアプリケーションのウィンドウも含まれます。'mappDontKeepOnTop'は、'mappKeepOnTop'でつねに最前面に表示する設定にしたウィンドウを通常の状態に戻します。引数や戻り値の設定は、両者共通です。

メソッドの使用方法
sResult = mappKeepOnTop(ウィンドウID番号)

sResult = mappDontKeepOnTop(ウィンドウID番号)

引数
ウィンドウを指定する整数のID番号です。他のメソッドを使って取得します。

戻り値
問題がなければ"OK"、あれば"Error:"で始まるストリングを返します。

つぎに、'mappGetActiveWindow'は、Directorアプリケーションまたはプロジェクタのウィンドウの中で、最前面に開いているウィンドウのID番号を返します。

メソッドの使用方法
nWinID = mappGetActiveWindow()

引数
ありません。

戻り値
整数のウィンドウ番号を返します。可視あるいはアクティブなウィンドウがなければ、0が返ります。

--[ウィンドウを開いたときつねに最前面に表示する]
-- MIAWとして開くムービーのムービースクリプトとして設定します
global gnWinID

on openWindow
  gnWinID = mappGetActiveWindow()
  sResult = mappKeepOnTop(gnWinID)
end

on closeWindow
  sResult = mappDontKeepOnTop(gnWinID)
  gnWinID = void
end

on deactivateWindow
  script("KeepOnTop").new()
end

'on openWindow'と'on closeWindow'ハンドラまでのスクリプトの処理は、前述したシンタックスについての説明でご理解いただけると思います。なぜ'on deactivateWindow'ハンドラを加えたかといいますと、2つのハンドラだけではステージのタイトルバーをクリックしたときに問題が生じるためです。なお、MIAWを閉じるとき、'forget window'を使いますと、'closeWindow'や'deactivateWindow'のイベントは発生せず、ハンドラが実行されません。また、MIAW自身から'forget window'を実行すると、Winodowsのプロジェクタは落ちてしまうことがあります([FN0108018]「MIAWを閉じるとプロジェクタが落ちる」参照)。

ステージが前面に切り替わって表示されるという現象は、前述した2つのハンドラで回避できます。ところが、ステージのタイトルバーをクリックすると、MIAWは前面に表示されているものの、ウィンドウのフォーカスだけはステージに切り替わってしまいます。そのため、ステージがアクティブになり、マウスイベントを受け取ってしまいます。これでは、モーダルの設定とはいえません。

そこで、'on deactivateWindow'ハンドラを記述し、アクティブウィンドウがMIAWからステージに切り替わったら、再度MIAWにフォーカスを戻そうということです。MIAWのウィンドウIDはすでに取得済みです。そして、MasterApp Xtraには、指定したウィンドウをアクティブにする'mappSetActiveWindow'というメソッドがあります。

もっとも、'deactivateWindow'イベントを受け取ると同時にこの処理を行ったのでは、タイミングが早過ぎるようです。一瞬間を空けて処理を行うために、ここでは親スクリプトとタイムアウトオブジェクトを使うことにしました。その親スクリプト名が「KeepOnTop」です。親スクリプトについて詳しくは、『Lingo辞書』または市販の解説書(たとえば大重美幸『Lingoスーパーマニュアル−Director 8対応−』オーム社)をご参照ください。

'mappSetActiveWindow'メソッドについて、簡単に説明しておきます。

メソッドの使用方法
nResult= mappSetActiveWindow(ウィンドウID番号)

引数
ウィンドウを指定する整数のID番号です。他のメソッドを使って取得します。

戻り値
直前にアクティブだったウィンドウ番号を返します。正しく処理できなかったときは、0が返ります。

--[KeepOnTop]
-- MIAWとして開くムービーの親スクリプトとして設定します
global gnWinID

on new(me)
  timeout(me.string).new(1, #xReactivate, me)
end

on xReactivate(me, oTimeout)
  oTimeout.forget()
  if not voidP(gnWinID) then
    nResult = mappSetActiveWindow(gnWinID)
  end if
end

まず、'on new'ハンドラでタイムアウトオブジェクトを作成します(タイムアウトオブジェクトについて、詳しくは『Lingo辞書』の'timeout()'関数および'new()'関数をご参照ください)。タイムアウトオブジェクト名はユニークな名前なら何でもよいので、親スクリプトのアドレス参照をストリングにして使っています。一瞬の間を空けるために、タイムアウト時間は1ミリ秒としました。タイムアウトハンドラが#xReactiveate、ターゲットオブジェクトはこの親スクリプトで生成した子オブジェクト('me')です。これで、子オブジェクト生成後1ミリ秒経過すると、このオブジェクトの'on xReactivate'ハンドラが呼び出されます。

on xReactivateハンドラは、一回だけの処理です。ですから、その第1ステートメントで、タイムアウトオブジェクトを削除します。ドキュメントに明示されていないようですが、タイムアウトハンドラは、その第2引数としてタイムアウトオブジェクトを受け取ります。

第3ステートメントは、MIAWをアクティブにする処理です。これは、第2ステートメントの'if...then'構文で、MIAWのウィンドウIDを格納しているグローバル変数gnWinIDが'void'でない場合に実行されます。変数値が'void'に設定されるのは、すでにご説明したムービースクリプトの'on closeWindow'ハンドラが実行されたときです。

ウィンドウが閉じても、それだけではまだMIAWがメモリに残るため、'on deactivate'ハンドラは引き続き実行されます。そのため、そのままでは非表示のMIAWがアクティブになり、ステージにフォーカスがもどりません。そこで、ウィンドウを閉じたとき('on closeWindow')には、グローバル変数を'void'に設定して、アクティブウィンドウの切り替えをしないようにしたのです。

Buddy APIを使った場合
Buddy APIも、ウィンドウやファイルその他のオペレーティングシステムの制御に関わる主要なメソッドを提供しています。クロスプラットホームの対応ですが、それぞれのバージョンの違いにご注意ください。本稿作成段階で、Windows版が3.5なのに対して、Macintosh版は無償ということもあってか1.3bとバージョンに大きな開きがあります。Buddy APIのメソッド一覧は、サイトのFull Function Listをご覧下さい。また、メソッドについて詳しくは、エクストラ付属のヘルプをご参照ください。

使用するメソッドは、'baSetWindowState'と'baWinHandle'、'baActivateWindow'、それから'baFindWindow'の計4つです。

まず、'baSetWindowState'メソッドは、指定したウィンドウの状態を設定します。つねに最前面に表示の設定とその解除は、引数の変更によって指定することができます。

メソッドの使用方法
baSetWindowState(ウィンドウハンドル番号, 状態)

引数
ウィンドウハンドル番号: ウィンドウを指定する整数です。他のメソッドを使って取得します。MasterApp Xtraの「ウィンドウID番号」と役割は同じですが、それぞれのエクストラが独自に管理する番号ですので、具体的な整数値は異なります。

状態: ウィンドウの状態をストリングで指定します。設定できる状態には各種ありますが、今回はつねに前面に表示させる"StayOnTop"と、その解除の"DontStayOnTop"を使用します。他の設定につきましては、エクストラのヘルプをご参照ください。

戻り値
'void'です。

つぎに、'baWinHandle'メソッドは、Directorのメインウィンドウのハンドル番号を返します。

メソッドの使用方法
nResult = baWinHandle()

引数
ありません。

戻り値
ウィンドウのハンドル番号を整数で返します。

'baActiveateWindow'メソッドは、指定したウィンドウをアクティブにします。

メソッドの使用方法
nResult= baActiveateWindow(ウィンドウハンドル番号)

引数
ウィンドウハンドル番号を整数で指定します。

戻り値
正しく処理できたときは1、そうでないときは0を整数で返します。

'baFindWindow'メソッドは、タイトルまたはクラスを指定してウィンドウを探すことができます。

メソッドの使用方法
nResult= baFindWindow(クラス, タイトル)

引数
クラス: ウィンドウのクラス名を、ストリングで指定します。

タイトル: ウィンドウのタイトル名を、ストリングで指定します。

引数のどちらかを無指定とする場合には、'empty'("")を指定します。

戻り値
ウィンドウのハンドル番号を整数で返します。ウィンドウが見つからないときは、0を返します。

--[ウィンドウを開いたときつねに最前面に表示する]
-- MIAWとして開くムービーのムービースクリプトとして設定します
global gnMyWindow

on openWindow

  gnMyWindow = baWinHandle()
  baSetWindowState(gnMyWindow, "StayOnTop")
end

on closeWindow
  baSetWindowState(gnMyWindow, "DontStayOnTop")
  gnMyWindow = void
end

on deactivateWindow
  script("KeepOnTop").new()
end

--[KeepOnTop]
-- MIAWとして開くムービーの親スクリプトとして設定します
global gnMyWindow

on new(me)
  tell the stage to timeout(me.string).new(1, #xReactivate, me)
end

on xReactivate(me, oTimeout)
  oTimeout.forget()
  if voidP(gnMyWindow) then

    -- プロジェクタのファイル名を取得
    sItemDelimiter = the itemDelimiter
    the itemDelimiter = "."
    sStage = (the applicationName).item[1]
    the itemDelimiter = sItemDelimiter

    -- ステージのウィンドウをアクティブにする
    nStage = baFindWindow(empty, sStage)
    nResult = baActivateWindow(nStage)
  else
    nResult = baActivateWindow(gnMyWindow)
  end if
end

スクリプトの構造は、MasterApp Xtraのサンプルと同様です。ただ、Buddy APIは、MIAWを閉じただけでは、つぎのウィンドウ(ステージ)に直ちにフォーカスが戻らず、非アクティブ表示されるようです。そのため、on xReactivateハンドラで、MIAWが閉じられた(グローバル変数gMyWindowが'void'の)場合には、つぎのウィンドウをアクティブに設定するようにしました。

'timeOut'オブジェクトをステージに対して生成したのは、このオブジェクトが(ステージを含めた)ウィンドウごとに設定されるものだからです。MIAWを閉じる('close window')と同時に'forget window'した場合に、'timeOut'オブジェクトはMIAWとともに消滅します。そうしますと、子オブジェクトへの参照もなくなり、スクリプトが実行されることなく、子オブジェクトも消滅することになります。'timeOut'オブジェクトを'forget window'後も残しておくためには、ステージに対して生成する必要がある訳です。

上記のサンプルは、MIAWを閉じたときステージをアクティブにしています。'baFindWindow'メソッドを使って、ウィンドウタイトルからステージのハンドル番号を取得します。ステージのウィンドウタイトルは、デフォルトではプロジェクタのファイル名になります。ただし、拡張子は必要ありませんので、'itmeDelimiter'を"."に設定して、ファイル名のみを取り出しています。

_____

作成者: 野中文雄
協力者: ginga baba
更新日: 2001年8月24日 解説への注意事項の補足とBuddy APIの親スクリプトを修正
更新日: 2001年8月15日 MIAWを閉じたとき、つぎのウィンドウがアクティブにならない問題にスクリプトを対応
更新日: 2001年8月11日 タイトルバーのクリックでフォーカスが切り替わる現象にスクリプトを対応
作成日: 2001年8月8日


Copyright © 2001-2003 Fumio Nonaka.  All rights reserved.