JRubyFXだけではなくJavaFXでの書き方が知りたかったという例も含めてます。
JRubyFXのWikiとサンプルをもっと見るべきだった。
モーダルウィンドウの作り方
child_stage = Java::JavafxStage::Stage.new
child_stage.initModality Modality::WINDOW_MODAL
child_stage.initOwner @stage
child_stage.show
@stage
は親ウィンドウのstage。
child_stage
はJRubyFX::Application
を継承したクラスに渡せば、普通に扱える。
.set~をなくす
# 2行
l = label 'text'
l.setStyle "-fx-background-color: red;"
# 1行
l = label 'text', style: "-fx-background-color: red;"
# 2行
b = button 'push'
b.setOnAction{ p "pushed!" }
# 1行
b = button 'push', onAction: Proc.new{ p "pushed!" }
set~ 〜
は生成時に ~: 〜
で(多分)代用できる。
Insets
l = label('?', padding: insets(20)) # 四方同じ値
l.setPadding insets(1,2,3,40) # 上右下左
サンプルにあるのに気づかず、JRubyFXでの書き方も分からず、苦労してたどり着いた。
アラートウィンドウの作り方
alert = Java::JavafxSceneControl::Alert.new(
Java::JavafxSceneControl::Alert::AlertType::ERROR)
with(alert, title: 'タイトル', headerText: 'ヘッダー', contentText: 'コンテント')
alert.show
[:INFORMATION, :CONFIRMATION, :ERROR, :NONE, :WARNING]
はどれも似た感じ(だと思う)。
jrubyfx-dialogsというgemを使っても良さそう。
Wikiに書いてあるが、 with
を使えば set~
を一度に出来る。
styleを使わずにラベルの文字色を変える
l = label('aa', textFill: Color.rgb(0,0,255))
l.setTextFill Color.web('FF0000')
JavaFXのColorは作り方に種類があるので、少し悩む。
定義済みの色もかなりあるので、さらに悩む。
styleを使わずに背景色を変える
l = label '背景色変更'
l.setBackground(
Java::JavafxSceneLayout::Background.new(
Java::JavafxSceneLayout::BackgroundFill.new(
Color::PURPLE, # ここのColorインスタンスを変えれば別の色に
Java::JavafxSceneLayout::CornerRadii::EMPTY,
Insets::EMPTY
)
)
)
Colorのインスタンスを受け取って、Backgroundのインスタンスを生成するメソッドを定義しないとだるい。
ファイル・チューザで拡張子を指定
fc = file_chooser do
add_extension_filter('テキストファイル (*.txt)')
end
なぜかadd_extension_filter
のキャメルケースがない。
子ウィンドウから親ウィンドウの要素をいじる
require 'jrubyfx'
class ChildWindow < JRubyFX::Application
attr_accessor :parent, :stage
def initialize(parent, stage = Java::JavafxStage::Stage.new)
super()
@parent = parent
@stage = stage
end
def start(stage=@stage)
parent = @parent
with(stage, width: 400, height: 400, title: 'Child') do
layout_scene do
vbox do
ta = text_area('',prefWidth: 400, prefHeight: 300,
text: 'parent["#vb"].add label("I am Child")')
eb = button('eval', onAction: Proc.new{ eval ta.text })
end
end
show
end
end
end
class ParentWindow < JRubyFX::Application
def start(stage)
with(stage, width: 200, height: 600, title: 'Parent') do
ChildWindow.new(stage).start
layout_scene do
vbox(id: 'vb')
end
show
end
end
end
ParentWindow.launch
JRubyFX::Applicationを継承したクラスのinitializeメソッドは super()
の必要がある。
参考:Javaクラスを継承したRubyクラスのinitializeでエラー - 骨を盗んで肉を盗まず
子ウィンドウのボタンを押すとテキストエリアの中身が実行され、親ウィンドウのVBoxにLabelが追加される。
子ウィンドウから親ウィンドウの要素を直接アクセスしてしまえば良い。
リンクは適当に作れるので、双方向に命令を飛ばし合うことも可能。
コードは用意しないけど、JRubyFX::Controllerを使う場合、コントローラーinitialize
内のself
を別ウィンドウに渡し、受け取った側は(受け取ったself).instans_eval{ ~ }
すると楽。
スコープが外れる
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="vb" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" />
require 'jrubyfx'
fxml_root File.dirname(__FILE__)
class MainCon
include JRubyFX::Controller
fxml 'scope.fxml'
def initialize(stage)
b1 = button('b1')
b1.setOnAction{ |e| ss(e) }
b2 = button('b2') do
setOnAction{ |e| ss(e) }
end
@vb.children.addAll(b1,b2)
end
def ss(e)
p e.source.text
end
end
class Main < JRubyFX::Application
def start(stage)
stage.fxml MainCon
stage.show
end
end
Main.launch
fx:id="vb"
に対応する@vb
に定義したボタンを2つ追加。
b1ボタンを押すとb1
が表示されるが、b2ボタンでは以下のようにNoMethodError
が起こる。
Exception in thread "JavaFX Application Thread" org.jruby.exceptions.RaiseException: (NoMethodError) undefined method `ss' for #<Java::JavafxSceneControl::Button:0x5a3a9e15 @supers={:ss=>1}>
at org.jruby.RubyBasicObject.method_missing(org/jruby/RubyBasicObject.java:1657)
at RUBY.method_missing(/パス/lib/ruby/gems/jrubyfx-1.2.0-java/lib/jrubyfx/dsl.rb:103)
at RUBY.method_missing(/パス/lib/ruby/gems/jrubyfx-1.2.0-java/lib/jrubyfx/core_ext/precompiled.rb:1809)
at RUBY.block in initialize(/パス/src/main.rb:13)
コードの一部を以下のように変えてselfをチェック。
# 前略
b1 = button('b1')
b1.setOnAction{ p self; p self.class.ancestors }
b2 = button('b2') do
setOnAction{ p self; p self.class.ancestors }
end
# 後略
# b1
#<MainCon:0x6fb3dc36 @nodes_by_id={}, @vb=#<Java::JavafxSceneLayout::VBox:0x3d96ed13>, @scene=#<Java::JavafxScene::Scene:0x1b76a607>, @stage=#<Java::JavafxStage::Stage:0x44e6e64f>>
[MainCon, JRubyFX::Controller, JRubyFX::DSL, JRubyFX, JRubyFX::Utils::CommonUtils, JRubyFX::FXImports, Object, Kernel, BasicObject]
# b2
#<Java::JavafxSceneControl::Button:0x5b99e118>
[Java::JavafxSceneControl::Button, Java::JavafxSceneControl::ButtonBase, Java::JavafxSceneControl::Labeled, Java::JavafxSceneControl::Control, Java::JavafxSceneControl::Skinnable, Java::JavafxSceneLayout::Region, Java::JavafxScene::Parent, Java::JavafxScene::Node, JRubyFX::DSL, JRubyFX, JRubyFX::Utils::CommonUtils, JRubyFX::FXImports, Java::JavafxEvent::EventTarget, Java::JavafxCss::Styleable, Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy, JavaProxyMethods, Object, Kernel, BasicObject]
b2の方にはMainCon
がない。
共通する先祖でメソッドを定義するか、b1の書き方にするか、生成時にProcを渡すかはお好きに。