プレゼンテーションツール Caraibe の基本構造

Coding Challenge に出そうと思っている JavaFX のプレゼンテーションツールですが、名前を Caraibe (カライブ) としました。

Caraibe はカリブ海諸島のトリニタリオ種を使ったチョコレートの名前です。ジャン=ポール エヴァンやピエールマルコリーニのボンボンショコラにも Caraibe という名前のものがあります。おいしいですよ。

まぁ、それはそれでおいといて、Caraibe の基本構造を説明しておきます。基本部分のクラス図はこんな感じ。青いクラスは JavaFX が提供しているライブラリのクラスです。

f:id:skrb:20090523153318j:image

Caraibe は複数のページ (Page クラス) から構成されています。Page クラスをまとめるのが PageAggregator クラスです。Page クラスと PageAggregator クラスは javafx.scene.CustomNode クラスのサブクラスにしました。

Page クラスは複数のパート (Part クラス) を持っています。そして、Part クラスは表示を行なう node アトリビュートと、node を表示するときに何らかの処理を行なう 関数型の action アトリビュートを持っています。

マウスクリックされると、PageAggregation オブジェクトは表示中の Page オブジェクトに対して action メソッドをコールします。すると、Page オブジェクトは保持している Part オブジェクトのうち、表示していないものの node を表示させ、そしてその Part オブジェクトの action をコールします。

action では node をフェードインさせるなどの処理を記述しておきます。

もし、表示する Part オブジェクトがなければ、Page クラスは action メソッドの戻り値として false を PageAggregator オブジェクトに返します。

すると、PageAggregator オブジェクトは新しい Page オブジェクトを生成し、表示中の Page オブジェクトを削除し、新しい Page オブジェクトを表示します。この時に、ページ遷移用のアニメーションなどを付け加えることも可能です。

基本的にはこれだけです。

ユーザが記述する JavaFX Script のスクリプトは Caraibe オブジェクトの生成と PageAggregation の生成、そして Page オブジェクトを作成させるための PageFactory クラスです。

たとえば、こんな感じ。

Caraibe {
    width: 1024
    height: 768

    pageAggregator: PageAggregator {
        // 背景を作成
        background: {
            // Illustrator/Photoshop Plug-in で生成した FXZ ファイルを
            // 読み込んで背景にする
            var content 
                = FXDLoader.loadContent("{__DIR__}background.fxz");
            content.getNode("background");
        }
        
        // ページ遷移用のオブジェクトの生成
        pageTransition: SlideInOutTransition {}

        // ページ生成用のファクトリオブジェクトを生成する
        pageFactories: [
            PageFactory {
                pageTitle: "Agenda"
                // createPage がファクトリメソッド
                createPage: function(): Page {
                    // Illustrator/Photoshop Plug-in で生成した page1.fxz から
                    // ノードの情報を読み込む
                    var content 
                        = FXDLoader.loadContent("{__DIR__}page1.fxz");

                    parts: [
                        Parts {
                            node: content.getNode("part01")
                            action: function(page: Page, index: Integer) {
                                // フェードインさせる
                                FadeEffect {
                                    node: page.getNode(index);
                                    duration: 1s
                                }.start();
                            }
                        },
                        Parts {
                            node: content.getNode("part02")
                            action: function(page: Page, index: Integer) {
                                // 前に表示していたものをフェードアウト
                                FadeEffect {
                                    // 不透明度を 1.0 から 0 に変化
                                    fromValue: 1.0
                                    toValue: 0.0
                                    node: page.getNode(0);
                                    duration: 1s
                                }.start();
                                // フェードイン
                                FadeEffect {
                                    node: page.getNode(index);
                                    duration: 1s
                                }.start();
                            }
                        },
                    ]
                }
            }
        ]
    }
}

このスクリプトはページが 1 ページで、2 つのパートからなっています。はじめにフェードインで 1 つめのパートを表示した後、クリックされると 1 つめに表示したパートをフェードアウトさせて、次のパートをフェードインさせます。

何ページものファクトリを 1 つのスクリプトに書くのは大変なので、ページごとにスクリプトを分けることもできます。

ここで、PageAggregator クラスに Page クラスのシーケンスを直接持たせるのではなく、PageFactory クラスを持たせているのが、工夫したところ。

Page クラスを直接持たせてしまうと、アプリケーションのを起動した時にすべての Page オブジェクトを生成してしまい、そのため起動にとても時間がかかってしまうのです。

そこで、ファクトリクラスを持たせて、表示する時に Page オブジェクトを生成するようにさせたわけです。

ページの生成に関する部分のシーケンスはこうなってます。

f:id:skrb:20090523160045j:image

当初はここを javafx.util.FXEvaluator クラスを使って動的に作成しようと思ったのですが、FXEvaluator クラスは Applet だとセキュリティの問題から使えないのです。そこで、Factory クラスを用いるようにしました。

ただ、今は表示する直前に生成しているので、Page オブジェクトに動画を含んでいる場合など生成に時間がかかる場合、ページの遷移がもたついてしまうという欠点があります。

できれば、表示しているページの次のページはバックグラウンドで生成するような機構を入れたいのですが、とりあえず Ver. 1 が完成してからですね。