S-JIS[2011-01-15/2011-10-16] 変更履歴
ScalaのSwingのFrame(MainFrame)はフレーム(ウィンドウ)を表示するクラス。(JavaのJFrame相当)
Frameはフレーム(ウィンドウ)を表示するクラス。[2011-10-16]
ウィンドウの閉じるボタンを押したときに実行されるcloseOperaionメソッドは中身が空(無処理)なので、閉じるボタンを押すとウィンドウが消えたように見えるが、インスタンスは残り続ける。
閉じられたときにインスタンスも消したい場合は、disposeメソッドを呼び出すようにする。
class F extends scala.swing.Frame {
title = "Frame test"
override def closeOperation() = dispose()
}
MainFrameはFrameの子クラスであり、違いは、MainFrameのcloseOperaionメソッドの中身が「System.exit(0)」であること。
(単なるFrameのcloseOperation()には何も書かれていない。無処理)
つまりウィンドウを終了したらアプリケーション全体も終了する場合はMainFrameを使い、そうでない場合はFrameを使う。
SimpleSwingApplicationのtopメソッドでMainFrameインスタンスを返すようにするのが一番簡単な使い方。
最も簡単な例(と解説)はSimpleSwingApplicationのサンプルで書いたので、もうちょっと複雑な例を。
画像と説明を出力するウィンドウを作ってみる。
こんなレイアウトを想定。
画像 |
||||
|
中央に画像を出し、下部に説明を出す。(BorderPanelのCenterに画像、Southに説明)
下部を左右に分けて、左側に属性、右側にファイル名を出す。(BoxPanelを左右に使う)
属性は何種類かあるので何行か出力する。(BoxPanelを上下に使う)
import scala.swing._
import BorderPanel.Position
object IconFrameSample extends SimpleSwingApplication {
override def top = new MainFrame {
title = "画像表示サンプル"
contents = new BorderPanel {
val filename = """C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Blue hills.jpg"""
val icon1 = Swing.Icon(filename)
val nx = icon1.getIconWidth
val ny = icon1.getIconHeight
// 画像
add(new Label {
icon = icon1
}, Position.Center)
// 説明
add(new BoxPanel(Orientation.Horizontal) { //横方向
//BoxPanelの左側
contents += new BoxPanel(Orientation.Vertical) { //縦方向
contents += new Label("w=" + nx)
contents += new Label("h=" + ny)
}
//BoxPanelの右側
contents += new ScrollPane(
new TextArea(filename) {
editable = false
}
)
}, Position.South)
}
}
}
MainFrameのcontents(内容)にはBorderPanelを指定している。
BorderPanelの“画像”部には画像(アイコン)のラベルを指定している。
BorderPanelの“説明”部にはBoxPanelを指定している。
MainFrameのcontentsは1種類しか指定できないのでcontentsに代入する形をとっているが、
BoxPanelは複数のコンポーネントを並べるものなので、「+=」演算子を使って追加するような形になっている。
テキスト領域(TextArea)をスクロールさせたい時はScrollPaneでラップするというのも、Swingではよくある方式。
SwingのFrame(javax.swing.JFrame)では、通常のコンポーネントを描くペイン(Scalaのcontents・JavaのContentPane)の上に、GlassPaneというものがある。[2011-04-08]
Glass(グラス・ガラス)の名の通り、GlassPaneは基本的に透明(非表示)なので、普段はContentPaneで描画されたものがそのままフレームとして表示されるが、
GlassPaneに描画することも出来るし、GlassPaneでイベントを受け取ることも出来る。
GlassPaneはフレーム(ウィンドウ)サイズと同じ大きさであることが保証されるので、ウィンドウ全体で最初にイベントを受け取りたい(横取りしたい)場合に使える。
import scala.swing._
object GlassPaneSample extends SimpleSwingApplication {
override def top = new MainFrame {
title = "サンプル"
// 通常のContentPane
contents = new FlowPanel {
contents += new Label("ラベル1")
contents += new Button("ボタン")
contents += new Label("ラベル2")
}
// グラスペイン
val glassPane = new Component {
var point = new Point
listenTo(this.mouse.clicks)
reactions += {
case event.MouseClicked(_, pt, modifiers, _, _) =>
if (modifiers == 0) { //左クリックのみ
point = pt
repaint()
} else {
println(modifiers)
}
}
override def paint(g: Graphics2D) {
g.setColor(java.awt.Color.BLUE)
g.drawRect(point.x, point.y, 16, 16)
}
}
peer.setGlassPane(glassPane.peer)
peer.getGlassPane.setVisible(true)
}
}
ContentPaneにはScalaのSwingではcontentsというメンバーでアクセスできるが、GlassPaneに関しては特にメンバーが用意されていないようだ。
そこで、peerを使ってjavax.swingのコンポーネントに直接アクセスする。(peer.setGlassPane)
重要なのが、GlassPaneに対するsetVisible(true)。
デフォルトではこれがfalseなので、描画もされないしイベントを捕捉することも出来ない。
GlassPaneはContentPaneの様にPanelを使っても描画できると思うが、普通はコンポーネントを配置することは無いと思うので、Panelでなく単なるComponentでよい。
今回の例では、マウスで左クリックされた座標に四角を描いている。
イベントの中で再描画を指示するのがrepaintメソッド。
(revalidateは呼ばなくても大丈夫なようだ)
実際に描画を行うpaintメソッドに関しては、独自コンポーネントを作る際の描画方法と同様。
なお、GlassPaneでイベントを横取りすると、ContentPaneのイベントは実行されないようだ。
つまりボタンをContentPaneに貼り付けておいても、実行されない…。