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に貼り付けておいても、実行されない…。