プラグイン作って覚えたことを淡々と記録するよ(GUI編)

そんなに凝ったGUI作る気はないんだけど,流れ的にいろいろわかったことがあるのでメモっとく。プラグインSDKはEAP7126以降のものを使っているので,IDEA6でも使える保証はない。細かいところでAPIに非互換があるんで,IDEA6では使えないと思った方がいいかもダ。


まず,IDEAプラグインGUIを作るときの良い点は,つぎの2つの尽きるかなと。

  • IDEAのUIデザイナの特徴でもあるSeparating GUI layout from codeを気兼ねなく使える。
    • どうせIDEA上でしか動かないんだし。
  • Swing以外にIDEAが用意しているGUIパーツが使える。
    • だって,IDEAのランタイムと共存してんだし。


前者は,Byte-code instrumentation approachを使うことでGUIレイアウト(*.form)のバウンドコード(*.java)がスッキリする。後者は,Swingには無いリッチなGUIパーツが使える(JGoodies-Formだけじゃないのよ)ので,そこそこのGUIが楽に作れるのが良い。


何個か使えそうなGUIパーツを見つけたので,備忘録がてら紹介していく。

TextFieldWithBrowseButtonクラスとNoPathCompletionクラス

読んで字のごとし。↓こんなGUIパーツを提供する(キャプション部分はJPanelよ)。


「...」ボタンを押したら,なんかしらのダイアログを表示し,その結果をテキストフィールドに反映するってのが主たる用途であろう。表示するダイアログは何でも良いんだけど,API的にファイル・チューザを表示させやすくできている。
ちなみに,IDEA(IDEA7に限定しとこ)のファイル・チューザはこんなやつ。


んで,どんなコードを書くかというと,こんな感じ。

textfieldWithBrowseButton.addBrowseFolderListener(
            "Title", "Description", project,
            new FileChooserDescriptor(true, false, false, false, false, false)
        );

FileChooserDescriptorのコンストラクタ引数の意味についてはプラグインSDKJavadocを参照してくれい。


ちょっと凝って,特定のファイルだけ表示する場合は,こんな感じ。

textfieldWithBrowseButton.addBrowseFolderListener(
            "Title", "Description", project,
            new FileChooserDescriptor(true, false, false, false, false, false) {
                public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
                    // 不可視ファイルと .ファイルは表示しない
                    if (!showHiddenFiles && file.getName().charAt(0) == '.') return false;
                    // ディレクトリとpropertiesファイルだけ表示する
                    return file.isDirectory() || "properties".equals(file.getExtension());
                }
            }
        );


あと,先のスクリーンショットのようにルートを指定することもできる。たとえば,プロジェクトのベースディレクトリをルートに指定する場合は,こんなふうに書く(ちょっとキレイじゃない)。

FileChooserDescriptor fileChooserDescriptor = new FileChooserDescriptor(true, false, false, false, false, false);
// プロジェクトのベースディレクトリをルートに設定
fileChooserDescriptor.setRoot(project.getBaseDir());
textfieldWithBrowseButton.addBrowseFolderListener("Title", "Description", project, fileChooserDescriptor);

(追記)書き忘れたけど,カレントディレクトリは $IDEA_HOME/bin ね.


そんでもって,このTextFieldWithBrowseButtonがファイル・チューザ向けなんだなぁと思わせる理由のひとつにPath Completionをあらかじめ備えているってのがある。


これはこれで便利な機能なんだが,ファイル・チューザ目的以外にTextFieldWithBrowseButtonを使う場合に邪魔になる。そんなわけで,Path Completionを使いたくない場合は,TextFieldWithBrowseButtonではなく,TextFieldWithBrowseButton.NoPathCompletionを使う。これもまた,読んで字のごとしなので,機能は説明しなくてもわかるでしょ。


「ファイル・チューザ目的以外」ってなによ?と問われれば,「クラス選択かなぁ」と答える。IDEA使ってりゃ,なんどかは見かけたことがあるクラス選択ダイアログとはこれのこと。


正式名称は,TreeClassChooserというらしく,こんな具合にTextFieldWithBrowseButton(やNoPathCompletion)に連結して使う。

textfieldWithBrowseButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            TreeClassChooserFactory factory = TreeClassChooserFactory.getInstance(project);
            TreeClassChooser treeClassChooser = factory.createProjectScopeChooser("Select Class");
            treeClassChooser.showDialog();
            PsiClass psiClass = treeClassChooser.getSelectedClass();
            if (psiClass != null) {
                // ExecutionUtilにかましてるのがミソ
                nopathCompletion.setText(ExecutionUtil.getRuntimeQualifiedName(psiClass));
            }
        }
    });


これはあくまで一例。TreeClassChooserFactoryには,いろんなcreateメソッドがあるので,それぞれ目的に応じたチューザを作れる(ハズ)。実際に作ってみたわけではないが,「特定のインターフェイスを実装したクラスだけ」とか「特定のメソッドを持っているクラスだけ」みたいなフィルタリングはできるはず。実際,IDEAでそうゆう画面みたことあるし。
ただ,PSI使ってフィルタリングするんで,ファイル・チューザよりはメンドイとは思う。


とりあえず,今回はここまで。たぶん,続く。