SWT

FieldEditorを自作する

SWT/JFaceには、設定ダイアログを簡単に作れる仕組みがある。
PreferenceDialogを使う方法だ。
↓こんなんがさくっと作れる。
preference1.PNG

FieldEditorPreferencePageにFieldEditorをaddしたら、設定オブジェクトのPreferenceStoreから勝手にロードしてデフォルト表示、OKボタン押したら自動でセーブしてくれる。すんごい楽だ。

FieldEditorを継承しているクラスはいろいろあるけど、コンボボックスのFieldEditorが無かった。
無かったら作ってしまえと言うことで作ってみる。
他にどんな種類のFieldEditorがあるかは、EclipseにてFieldEditorの階層を表示すれば継承してるクラス全部見れるのでそこから探すことができる。

FieldEditorを継承することになるけど、その辺を自作するときのポイントなど。
詳しいことはFieldEditorのJavadocを参照。
まずコンストラクタで、親のコンストラクタを使わずに
init(name, labelText);
createControl(fieldEditorParent);
を呼ぶ。
initで設定ファイルのキー項目と設定のタイトルラベルの文字列を設定。

doFillIntoGrid()をOverrideし、実際のコンポーネント部分を作成する
このときgetLabelControl(parentComposite)を呼ぶことで、設定項目のラベルテキストを作成することができるので先に呼んでおく。
その後作りたいGUIコンポーネントを作成。

設定の読み込み、保存のためにdoLoad()とdoStore()を実装する。
この辺はStringFieldEditorとか適当なのを参考にする。

getNumberOfControls()は適当に横幅のカラム返すようにしとく。
adjustForNumColumns()も必要であればStringFieldEditorとか参考にして実装しとく。

と言うことで、基本的にはdoFillIntoGrid()で乗せたいコンポーネントつくって、後は定型のコードをマネすればOKな感じ。楽。

SWTの限界

う〜ん、Explorerクローンを作っているがGUIのLinuxとWindowsで動作が違う・・・。
もちろんそれはSWTの利点なんだがクリックイベントの発生順まで違うとは・・・。

ディレクトリを表示するツリーでディレクトリを開くために[+]マークをクリックするとツリーが開かれ[-]になる。
そのときWindowsはマウスボタンダウンの段階でWindowsのGUIコンポーネントの機能でツリーが開かれた後Javaのクリックイベントが発生する。
Linuxの場合はマウスボタンダウンでJavaのクリックイベントの後にマウスボタンアップでGTKのGUIコンポーネントのマウスクリック処理が行われる。

WindowsのExplorerの動作にできる限り似せるようにしているのでマウスイベントあたりでいろいろごちゃごちゃ処理を行っているのでマウスクリックでの処理の順番やタイミングが違うと意図しない動作になってしまう・・・。

う〜む・・・、不本意だがOSそれぞれによって処理を書く必要があるなぁ。

SWT/Jfaceは好きなんだけど同じコトをさせようと思うとやっぱ困ることがある。
Swingも自在に使えるようにしておいた方がいいな。
Linuxでも使えるナイスデザインのSwing L&F
https://looks.dev.java.net/

ほかにも躓く点があるなら次作るアプリはSwingにしてみるか・・・。

GEF使ってみた

仕事であるモデルをグラフィカルに表示するアプリを作ると言うことでEclipseのサブプロジェクトで作成されているGEF(Graphical Editing Framework)を使ってみた。

主な情報源は
ObserveEclipse

名前の通りグラフィカルに何かを編集するためのフレームワークで、これを使ってUMLを作成するようなプラグインもある。

とりあえず使ってみての感想は「もっと前から使っておけば良かった」という感じ。
ただ本格的なツールを作るためのフレームワークなので軽く使うにはちょっと複雑かな。
でも慣れれば簡単なツールを作るのも楽かも。

フレームワークの概要としてはMVC(モデルビューコントロール)で何らかのモデルをグラフィカルに表示、モデルに対して動的に編集可能(ダブルクリックするとモデル内にエディタが開くとか)

今の仕事は何らかのデータをグラフィカルに表示することが多いのでGEFを自在に使えるようになるとかなり便利そう。
ちょっとコードを書くだけでいろいろ複雑なこともできるみたいだし。

しかし日本語のドキュメントが相変わらず少ないのが辛いなぁ。

Windowsのスタイル変更が原因でSWTが落ちる

SWTを使ってアプリを作成していて変な現象が発生したのでそのメモ。

昨日まで動いていたアプリが今日動かなくなった。例外のスタックトレースは以下の通り。
Exception in thread “main” org.eclipse.jface.text.Assert$AssertionFailedException: Assertion failed:
at org.eclipse.jface.text.Assert.isTrue(Assert.java:177)
at org.eclipse.jface.text.Assert.isTrue(Assert.java:162)
at org.eclipse.jface.text.source.LineNumberRulerColumn.getBaselineBias(LineNumberRulerColumn.java:812)
at org.eclipse.jface.text.source.LineNumberRulerColumn.doPaint(LineNumberRulerColumn.java:688)
・・・・以下略

どうもStyledTextの行番号を表示するRulerの部分で落ちているようだ。
確かにRulerを表示しないようにするとちゃんと動く。
同じコードで別のマシンに持って行くとちゃんと動く。

動いていた状態から変更したと言えばWindowsのスタイルを変更したぐらいだ。
うそーん、と思いつつ原因を調べることに。

スタックトレースからLineNumberRulerColumn.javaの812行目でアサートに失敗しているのでとりあえずEclipseのソースをCVSから取得。

すると「1行の高さよりフォントの高さの方が小さい事」、とAssertしていた。
う~ん、これが原因か。
確かにスタイルを変更するとエディタ部分のフォントがMS ゴシック以外のフォントになってしまっていたがこれがダメなのか。
そしてそのWindowsスタイルのフォントにはtahomaが設定されていた。

もしかしたら、テキスト描画の縦幅を計算するときは英語のtahomaフォントを使用して、日本語を描画するときは別の日本語フォントが自動で選択されたためにフォントの設定に差異が生じてバグっていたのかもしれない。
とりあえずWindowsのスタイル設定で使用するフォントをすべてtahomaからMS Pゴシックに変更するとエラーが出なくなった。すばらしい。

問題が起こったときに原因を自分で調べることができる。オープンソースバンザイ。

でもクライアントのフォントの設定によって動いたり動かなかったりするアプリはまずいよなぁ。
まぁWindowsにわざわざパッチ当ててスタイル変更できるようにする人は少ないから問題ないか。
それに変なフォント入れてなければ普通にMS ゴシックが使われるからエラーにならないし。
ちなみにこのAssertはjface独自で実装されたAssertなのでjavaオプションでAssertをオフにしてもこのAssertを無効にすることはできなかった。

SWTのImageData

SWTで画像の生データのバイト配列はImageDataで扱う

depthが32の場合は1ドットで4バイト使うので以下のようにすると1ドットの情報が得られる。
4つめのバイトは使われていない模様。アルファチャンネルは別にデータを持ってるしなぁ。

bytedata[( x * bpp ) + ( y * imageWidth * bpp) ] = R
bytedata[( x * bpp ) + ( y * imageWidth * bpp) + 1 ] = G
bytedata[( x * bpp ) + ( y * imageWidth * bpp) + 2 ] = B
bytedata[( x * bpp ) + ( y * imageWidth * bpp) + 3 ] = ?

よく見たらこれに関しての詳しいドキュメントがあるじゃないか。Standard Widget Toolkit でのJava 2D画像

最初に書いた式では16bitの場合画面が乱れることがあった。どうもImageの横幅が奇数ドットの場合に乱れていた。これに関しても以下のように説明&解決策があった。日本語ドキュメントバンザイ。
と言うことで最初に書いた式では完璧に動作するわけではない。が、めんどくさいので放置。

各ピクセルは3バイトでコード化されていますが、24ビット画像の場合はピクセル中の1ラインのピクセル・サイズは必ずしも3*widthではありません。2ラインのピクセル間で、一部のインデックスは使われずに残る場合もあります。(次のラインがどのインデックスで始まるかを知るために)各ラインのピクセルに対して実際に何バイト使われているかを知るには、ImageDataのbytesPerLineフィールドの値を使う必要があります。

ApplicationWindowのエラーハンドリング

例のDB用ツールは今までSWTで作成していたが、JFaceを使用するように変更。
メニュー部分をJFace用に修正するのが大変だった・・・。
同時にメイン処理部分を大幅にリファクタリング。
だいぶスリムになった。

今までは意図しない例外や、メモリ不足のエラーが発生した場合、設定を保存して終了していた。
しかしJFaceを使うとそれらの例外やエラーはイベントループでキャッチしてくれるのでそのまま処理を続行できる。
うむ、すばらしい。

で、何も設定しないとエラーが発生してもそのまま何も処理せずスルーするのだがエラーが発生したらエラーメッセージを表示するなどしたい。
方法は以下。

ApplicationWindow#setExceptionHandler( IExceptionHandler handler)
を使う。

以下のような感じで

Canvas上でのマウスホイール

ScrolledCompositeの上にCanvasを置くとスクロールができるCanvasになるが、その上でマウスのホイールをクルクルしてもちょっとしかスクロールしない。

ストレス溜まるのでもっとぐぐっと移動させたい時は

scrolledComposite.getVerticalBar()でスクロールバーを取得して強制的に動かす。
16777217で上、16777218で下。
たぶんホントはちゃんとした定数があると思うけどまぁコレでいいやと言うことでこのままで。

SashForm

中身のWidgetのサイズ設定

パーセントで指定

スクロールバー

Eclipse本家から

例1
Minとか指定せず中身のサイズのみ指定すると中身のサイズ以下の時にスクロールバー
中身はそれ以上に広がらない

例2
Minを指定するとそれ以下の時スクロールバーがでる

を指定すると指定した方向のが幅いっぱいまで広がる
縦横両方Expandに設定するときは中身のサイズを指定しなくてもいいけどExpand指定しないときは中身のサイズを指定してやらないと表示されない(サイズが特定できないためっぽい)

表示位置設定

window作成テンプレート

変更済みだけどSWTサンプル集から

Shellスタイルは
BORDER 枠が立体的になる
CLOSE 閉じるボタン。無いと困る
MIN 最小化ボタン
MAX 最大化ボタン
NO_TRIM 何も無し
RESIZE リサイズを可能にする
TITLE タイトルバー

以下たぶん子ウインドウの動作の違い
親に指定すると枠が黒い線、立体的ではない
APPLICATION_MODAL
MODELESS
PRIMARY_MODAL
SYSTEM_MODAL

基本スタイルに
SHELL_TRIM CLOSE | TITLE | MIN | MAX | RESIZE
DIALOG_TRIM TITLE | CLOSE | BORDER
がある。スタイルを指定しなければたぶんSHELL_TRIM が設定。