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

前回に引き続いてTextFieldネタを。

TextFieldWithHistory/TextFieldWithStoredHistoryクラスとPropertiesComponentクラス

履歴機能付きテキストフィールドってのは,...WithHistoryから察することができよう。WithStoredHistoryやPropertiesComponentについては,おいおい説明するとして,まずは外観をご披露。


これもキャプション部分はJPanel。左のTextFieldWithHistoryに「...」ボタンが付いているが,これは後付けしたもので,TextFieldWithHistory/TextFieldWithHistory単体での外観は右側のなんかアイコンが付いてるテキストフィールドがそう。


見た目から機能は推測できよう。こんな具合に入力履歴を保存できるテキストフィールドが,TextFieldWithHistory/TextFieldWithStoredHistory。


とはいえポトペタで貼っ付けとけば勝手に履歴が残るワケじゃなく,しかるべきタイミングでカレントの値を履歴に残さねばならない(TextFieldWithHistory.addCurrentTextToHistory()を呼んでね)。「しかるべきタイミング」ってのは...まぁ...各自で決めてくれと。


一般的な用途は,TextFieldWithBrowseButtonみたくダイアログと連動して入力値を残す事なんだが,コレ単体では「WithBrowseButton」がない。無けりゃ付けろってことで,GuiUtilsなるユーティリティの手助けを借りてブラウズボタンをくっつける。

textfieldWithHistory = new TextFieldWithHistory();
JPanel panel = GuiUtils.constructFieldWithBrowseButton(textfieldWithHistory, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            VirtualFile[] chooseFiles = FileChooser.chooseFiles(project,
                                                                new FileChooserDescriptor(true, false, false, false, false, false));
            if (chooseFiles.length > 0) {
                textfieldWithHistory.setText(chooseFiles[0].getPath());
                // 選択するたび履歴に残すなら,ここで
                //   textfieldWithHistory.addCurrentTextToHistory();
                // するもよし。
                // あとは,「OK」ボタン押したときとか。
            }
        }
    });
// GuiUtilsの戻り値はJPanelが多いので,UIデザイナでは
// 貼り付け先にパネルを仕込んでおいた方が何かと楽。
anchorPanel.add(panel);

GuiUtilsは,他にもUI加工用のメソッドがあるので,詳しくはSDKJavadocみてね。


TextFieldWithHistoryの説明はだいたい終わったんで,TextFieldWithStoredHistoryを...説明する前にPropertiesComponentを説明する。
#といっても,半分憶測混じりなので,そのつもりでどぞ。


PropertiesComponentってのは,HttpSessionやHttpServletRequestの属性(Attribute)みたいに,特定のスコープで値を保存できる入れ物のことだ(多分あってる)。特定のスコープってのは,勝手に名付けるが

  • アプリケーションスコープ
    • PropertiesComponent.getInstance()で取得。IDEAのインスタンス?プロセス?でひとつ。
  • プロジェクトスコープ
    • PropertiesComponent.getInstance(Project)で取得。IDEAのプロジェクトごとにひとつ。

の2つ。


持てる情報は,文字列とブール値くらいだが,あたしの思い違いでなければ,PropertiesComponentは勝手に永続化されるみたい。
実際,IDEA再起動してもPropertiesComponentから以前入れた値が取れるし。だけど,プロジェクトスコープのくせに,*.ipr, *.iml, *.iwsのどこみても永続化してるふうでもない。一体どこに永続化してんの?そいとも永続化してるって思いこんでるだけ??


まあいい,永続化の真偽はおいとくとして,PropertiesComponentはちょいとしたモノ入れに使っているみたい。見かけたのは,設定パネルみたいに寿命の短いGUIで設定した内容をちょいと保存しておくとか。そんな用途。
便利そうなんだけど,どこまで依存していいか見極めかねてる。


でだ。前置きが長くなったが,TextFieldWithStoredHistoryってのは,履歴情報を勝手にPropertiesComponentに保存してくれるコンポーネントだ。なんでま,WithStoredHistoryなんだろな。TextFieldWithStoredHistoryにはデフォルトコンストラクタはなく,プロパティ名を指定する。このプロパティ名を使ってPropertiesComponentに自身の履歴情報を格納するようだ。ただ,履歴の復元は,こちらが望まないとやってくれない(TextFieldWithStoredHistory.reset()を使う)。
長期的な履歴の保存と復元をしてくれるって点で,TextFieldWithStoredHistoryのほうが実用的なんだと思う。TextFieldWithHistoryは正直使い処が思いつかない。


軽くスルーしていたが,デフォルトコンストラクタを持たないUI部品をIDEAのUIデザイナで管理するには,ちょいとした仕掛けがある。通常,FormにバインドしたJavaクラスのコンストラクタでは,バインドしたUI部品のインスタンスは生成しているとみなしてコーディングできるんだが,デフォルトコンストラクタがないとそう簡単にはいかない(でも,そんな難しくもない)。
どうやって,そのインテンションが出たかは失念してしまったのだが,こんな感じのメソッドが自動生成されるので,そこでIDEAが勝手に生成できないUIパーツの生成コードを記述することができるようだ。こんなのはじめて知ったわ。

private void createUIComponents() {
    textfieldWithStoredHistory = new TextFieldWithStoredHistory("dummy");
    // 履歴を復元しとく
    textfieldWithStoredHistory.reset();
}


とまあ,こんな感じだ。TextFieldWithHistory/TextFieldWithStoredHistoryもPropertiesComponentもプラグインSDKにソース付いてきてるんで,もっと詳しいことはソース読んでくれ。
若干,蛇足な気もするが,こんなUIパーツもあるってんで,紹介しとこ。

ComboboxWithBrowseButtonクラス

TextFiledWithBrowseButtonのコンボボックス版。SDKJavadocで見つけただけで,使い道はよーわからん。

AlternativeJREPanelクラス

外観はこれ。


このあまりにも用途が限定されたUIパーツは,普通のSDKには含まれていない。プラグインで使いたい場合は,SDKのライブラリにidea.jarも仕込んでね。
あえて使い方を説明する必要あるの?って思うが,見ての通りJRE(というJavaSDK)を選択する。


一応,履歴にはすでにIDEAに登録してあるSDKが列挙するんだけど,「Javaだけ」という選別まではしてないので,RubySDKとかプラグインSDKとかもごっちゃになるけど,それはご愛敬。同じ理由で,ブラウズボタンで表示するファイル・チューザもJREだけフィルタリングしてはくれない。
Alternative JREとか言ってるワリに,実は言い値をそのまま持つと言う,びみょうにしょっぱいUIパーツだ。


パーツの完全性にケチつけてもしょうがないんで,話を進めるとしよう。AlternativeJREPanelは,

  • isPathEnabled()で,有効無効の判定
  • getPath()で,指定されたJRE(だと思う)パス

を取得できるんで,なんだかよく知らんが,個別にJREのパスを指定したいときなんかに使うと便利(どんなときだ?)。
あたしゃ,Winstone Integration Pluginで使わせてもらったが,もう二度と使うことはあるまい。:-)


GUI編は,まだ続く。