適切なフォントサイズを自動選択するテキストボックス
SWTでリサイズ時に処理を行うには →addControlListenerを使う。ControlListenerにはcontrolResizedがある。
テキストボックスの内容が変化したときに処理を行うには→addModifyListener。modifyTextメソッドが呼ばれる。
残念ながら、かな漢字変換をして複数文字を確定させたときはそのそれぞれの文字に対してmodifyTextイベントが発生するようだ。 文字ごとに処理が行われるのが好ましくない場合はmodifyText内でフラグを立ててタイマーで処理する形になるかなぁ。タイマーを使うと別Threadになるのでいろいろとややこしそうだ。とりあえずSWTの場合はdisplayのスレッドと競合しないようにする必要があるのでdisplay.asyncExecなどを使う必要がある。
というわけで朝チェックアウトした後の待ち合わせから帰りの新幹線にかけて作ったのが下のようなもの。テキストボックスのサイズと中身の文字列の長さによって自動的に適切なフォントサイズを選択してくれるテキストボックス。 高橋メソッドでプレゼン資料を作る場合とか、タブレットPCでテキストボックスを使って文章を入れるときとか、いつも「四角を書く→中身を入れる→いい感じになるようにフォントサイズをいろいろ変えて試す」という手間があってイヤだったので作ってた。タブレットPC用の「自分にとって使いやすい」ノートソフトを作る第一歩ということになるかな。
追記。modifyTextメソッド内でフォント変更の処理を行うと、仮名漢字変換から複数文字を確定させた場合に文字ごとにフォントを変更して再描画するのでルパン三世のオープニングにようにタタタタタタと表示されてしまう。目に優しくない。 modifyTextでフラグを立てて、メインループのdisplay.sleep直前で処理するようにしたら改善されたように思える。 しかしまだ時々ちらつくのは表示用のテキストボックスをフォントサイズ計算用に流用しているせい。これはテスト用だからこうなっているけど実際はノートにぺたぺたと複数のテキストボックスが貼り付けられることになるので、画面外に1つ計算用のテキストボックスを配置してそれを使うことになるだろうな〜。
ソースコード。
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
public class ProperText implements ModifyListener, ControlListener {
private static Text text;
private static Display display;
private Font font;
private int fontsize;
private boolean scheduled;
public static void main(String[] args) {
display = new Display();
Shell shell = new Shell(display);
text = new Text(shell, SWT.BORDER + SWT.MULTI + SWT.WRAP);
ProperText p = new ProperText();
text.addModifyListener(p);
text.addControlListener(p);
shell.setLayout(new FillLayout());
shell.layout();
text.setFont(new Font(display, "MSゴシック", 16, 0));
p.scheduled = true;
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
if (p.scheduled) {
p.calcProperFontsize();
}
display.sleep();
}
}
display.dispose();
}
public void modifyText(ModifyEvent arg0) {
scheduled = true;
}
public boolean isOK() {
int height = text.getLineCount() * (text.getLineHeight() + 1);
if (height > text.getClientArea().height) {
return false;
}
return true;
}
public void changeFontsize() {
font = text.getFont();
FontData fd = font.getFontData()[0];
fd.setHeight(fontsize);
font.dispose();
font = new Font(display, fd);
text.setFont(font);
}
public void calcProperFontsize() {
text.setVisible(false);
font = text.getFont();
FontData fd = font.getFontData()[0];
fontsize = fd.height;
int step = fontsize;
fontsize = 0;
int dir = 1;
boolean startDash = true;
while (true) {
fontsize += step * dir;
changeFontsize();
if (isOK()) {
if (step <= 1) {
break;
}
if (startDash) {
step *= 2;
} else {
step -= step / 2;
}
dir = 1;
} else {
if (step <= 1) {
fontsize -= 1;
changeFontsize();
break;
}
step -= step / 2;
dir = -1;
startDash = false;
}
}
text.setVisible(true);
text.setFocus();
scheduled = false;
}
public void controlResized(ControlEvent e) {
calcProperFontsize();
}
public void controlMoved(ControlEvent arg0) {
}
}