Class: JRubyFX::Controller

Inherits:
Object
  • Object
show all
Extended by:
FXImports
Includes:
JRubyFX, DSL
Defined in:
lib/jrubyfx/fxml_controller.rb

Overview

Inherit from this class for FXML controllers

Constant Summary collapse

@@fxml_linked_args =

FXML linked variable names by subclass

{}

Constants included from FXImports

FXImports::JFX_CLASS_HIERARCHY

Constants included from DSL

DSL::ENUM_OVERRIDES, DSL::NAME_TO_CLASSES

Constants included from JRubyFX

VERSION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DSL

included, inject_enum_method_converter, inject_symbol_converter, load_dsl, load_enum_converter, #method_missing

Methods included from JRubyFX

#build, #run_later, #with

Methods included from Utils::CommonUtils

#attempt_conversion, #populate_properties, #split_args_from_properties

Constructor Details

#initializeController Also known as: initialize_orig

this is the default initialize that the FXML loader will call



133
134
# File 'lib/jrubyfx/fxml_controller.rb', line 133

def initialize()
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class JRubyFX::DSL

Instance Attribute Details

#stageObject

Controllers usually need access to the stage.



28
29
30
# File 'lib/jrubyfx/fxml_controller.rb', line 28

def stage
  @stage
end

Class Method Details

.fx_handler(names, type = ActionEvent, &block) ⇒ Object

call-seq:

fx_handler(callback) { |event_info| block } => Method
fx_handler(callback, EventType) { |event_info| block } => Method
fx_type_handler(callback) { |event_info| block } => Method

Registers a function of name name for a FXML defined event with the body in the block Note: there are overrides for most of the default types, so you should never need to manually specify the type argument unless you have custom events. The overrides are in the format fx_*_handler where * is the event type (ex: fx_key_handler for KeyEvent).

Overloads

  • fx_key_handler is for KeyEvent

  • fx_mouse_handler is for MouseEvent

  • fx_touch_handler is for TouchEvent

  • fx_gesture_handler is for GestureEvent

  • fx_context_handler is for ContextMenuEvent

  • fx_context_menu_handler is for ContextMenuEvent

  • fx_drag_handler is for DragEvent

  • fx_ime_handler is for InputMethodEvent

  • fx_input_method_handler is for InputMethodEvent

  • fx_window_handler is for WindowEvent

  • fx_action_handler is for ActionEvent

  • fx_generic_handler is for Event

Examples

fx_handler :click do
  puts "button clicked"
end

fx_mouse_handler :moved do |event|
  puts "Mouse Moved"
  p event
end

fx_key_handler :keypress do
  puts "Key Pressed"
end

Equivalent Java

@FXML
private void click(ActionEvent event) {
  System.out.println("button clicked");
}

@FXML
private void moved(MouseEvent event) {
  System.out.println("Mouse Moved");
}

@FXML
private void keypress(KeyEvent event) {
  System.out.println("Key Pressed");
}


85
86
87
88
89
90
91
92
93
94
# File 'lib/jrubyfx/fxml_controller.rb', line 85

def self.fx_handler(names, type=ActionEvent, &block)
  [names].flatten.each do |name|
    class_eval do
      #must define this way so block executes in class scope, not static scope
      define_method(name, block)
      #the first arg is the return type, the rest are params
      add_method_signature name, [Void::TYPE, type]
    end
  end
end

.fx_id(*name) ⇒ Object

call-seq:

fx_id :name, ...

Register one or more variable names to bind to a fx:id in the FXML file.

Example

fx_id :myVar

Equivalent Java

@FXML
private ClassName myVar;


191
192
193
194
# File 'lib/jrubyfx/fxml_controller.rb', line 191

def self.fx_id(*name)
  # we must distinguish between subclasses, hence self.
  (@@fxml_linked_args[self] ||= []).concat(name)
end

.fx_id_optional(*names) ⇒ Object

call-seq:

fx_id_optional :name, ...

Register one or more variable name to bind to a fx:id in the FXML file if it exists. If the name cannot be found, don’t complain.

Example

fx_id_optional :myVar

Equivalent Java

@FXML
private ClassName myVar;


209
210
211
# File 'lib/jrubyfx/fxml_controller.rb', line 209

def self.fx_id_optional(*names)
  fx_id *names.map {|i| {i => :quiet} }
end

.load_fxml(filename, stage, settings = {}) ⇒ Object

Load given fxml file onto the given stage. settings is an optional hash of:

  • :initialize => [array of arguments to pass to the initialize function]

  • :width => Default width of the Scene

  • :height => Default height of the Scene

  • :fill => Fill color of the Scene’s background

  • :depth_buffer => JavaFX Scene DepthBuffer argument (look it up)

  • :relative_to => number of calls back, or filename. filename is evaluated as being relative to this. Default is relative to caller (1)

Returns a scene, either a new one, or the FXML root if its a Scene.

Examples

controller = MyFXController.load_fxml("Demo.fxml", stage)

Equivalent Java

Parent root = FXMLLoader.load(getClass().getResource("Demo.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
controller = root.getController();


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/jrubyfx/fxml_controller.rb', line 267

def self.load_fxml(filename, stage, settings={})
  # Create our class as a java class with any arguments it wants
  ctrl = self.new_java *settings[:initialize_ruby].to_a
  # save the stage so we can reference it if needed later
  ctrl.stage = stage
  # load the FXML file
  parent = load_fxml_resource(filename, ctrl, settings[:relative_to] || 1)
  # set the controller and stage scene, so that all the fx_id variables are hooked up
  ctrl.scene = stage.scene = if parent.is_a? Scene
    parent
  elsif settings.has_key? :fill
    Scene.new(parent, settings[:width] || -1, settings[:height] || -1, settings[:fill] || Color::WHITE)
  else
    Scene.new(parent, settings[:width] || -1, settings[:height] || -1, settings[:depth_buffer] || settings[:depthBuffer] || false)
  end
  # instead of using the initializable interface, roll our own so we don't have to deal with java.
  ctrl.initialize_fxml(*settings[:initialize_fxml].to_a)
  # return the controller. If they want the new scene, they can call the scene() method on it
  return ctrl
end

.load_fxml_resource(filename, ctrlr = nil, relative_to = 0) ⇒ Object

call-seq:

load_fxml_resource(filename) => Parent
load_fxml_resource(filename, controller_instance) => Parent
load_fxml_resource(filename, controller_instance, relative_to) => Parent

Load a FXML file given a filename and a controller and return the root element relative_to can be a file that this should be relative to, or an index of the caller number. If you are calling this from a function, pass 0 as you are the immediate caller of this function.

Examples

root = JRubyFX::Controller.load_fxml_resource("Demo.fxml")

root = JRubyFX::Controller.load_fxml_resource("Demo.fxml", my_controller)

Equivalent Java

Parent root = FXMLLoader.load(getClass().getResource("Demo.fxml"));


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/jrubyfx/fxml_controller.rb', line 306

def self.load_fxml_resource(filename, ctrlr=nil, relative_to=0)
  fx = FXMLLoader.new()
  fx.location = if JRubyFX::Application.in_jar?
    # If we are in a jar file, use the class loader to get the file from the jar (like java)
    JRuby.runtime.jruby_class_loader.get_resource(filename)
  else
    if relative_to.is_a? Fixnum or relative_to == nil
      # caller[0] returns a string like so:
      # "/home/user/.rvm/rubies/jruby-1.7.1/lib/ruby/1.9/irb/workspace.rb:80:in `eval'"
      # and then we use a regex to filter out the filename
      relative_to = caller[relative_to||0][/(.*):[0-9]+:in /, 1] # the 1 is the first match, aka everything up to the :
    end
    # If we are in the normal filesystem, create a normal file url path relative to the main file
    URL.new(URL.new("file:"), "#{File.dirname(relative_to)}/#{filename}")
  end
  # we must set this here for JFX to call our events
  fx.controller = ctrlr
  return fx.load()
end

.method_added(name) ⇒ Object

When initialize is defined, rename it to initialized, and restore initialize to default so java does not double call us



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/jrubyfx/fxml_controller.rb', line 143

def self.method_added(name)
  if name == :initialize && !@in_alias
    puts <<WARNIT
*****************************************************************************
*****************************************************************************
**                   WARNING! WARNING! WARNING! WARNING!                   **
** WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! **
**                   WARNING! WARNING! WARNING! WARNING!                   **
*****************************************************************************
**                                                                         **
** You probably meant to define `initialize_fxml` instead of `initialize`  **
** `initialize` is ambiguous in JavaFX controllers as FXMLLoader will call **
**  it if it has 0 arguments in addition to it being ruby's constructor.   **
**  If you need access to FXML elements (defined with `fx_id :myEltId`),   **
**   then use `initialize_fxml`. If you need the ruby constructor, which   **
**     does not have access to FXML yet, use `initialize_ruby` instead     **
**                                                                         **
**            Assuming you wanted `initialize_fxml` for this run           **
**                                                                         **
*****************************************************************************
**                   WARNING! WARNING! WARNING! WARNING!                   **
** WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! **
**                   WARNING! WARNING! WARNING! WARNING!                   **
*****************************************************************************
*****************************************************************************
WARNIT
    @in_alias = true
    alias_method :initialize_fxml_warn, :initialize
    alias_method :initialize, :initialize_orig
    @in_alias = false
  end
end

.new(*args, &block) ⇒ Object

When FXMLLoader detects a method called initialze taking 0 args, then it calls it We don’t want this, as ruby new calls initialize Override new to avoid calling initialize



126
127
128
129
130
# File 'lib/jrubyfx/fxml_controller.rb', line 126

def self.new(*args, &block)
  obj = self.allocate
  obj.send(:initialize_ruby, *args, &block) if defined? obj.initialize_ruby
  obj
end

.new_java(*args) ⇒ Object

Magic self-java-ifying new call. (Creates a Java instance)



242
243
244
245
# File 'lib/jrubyfx/fxml_controller.rb', line 242

def self.new_java(*args)
  self.become_java!
  self.new(*args)
end

Instance Method Details

#initialize_fxml(*args) ⇒ Object

this is the default initialized method so we can always call it



136
137
138
# File 'lib/jrubyfx/fxml_controller.rb', line 136

def initialize_fxml(*args)
  self.send(:initialize_fxml_warn, *args) if defined? initialize_fxml_warn
end

#sceneObject

Return the scene object (getter)



236
237
238
# File 'lib/jrubyfx/fxml_controller.rb', line 236

def scene()
  @scene
end

#scene=(s) ⇒ Object

Set scene object (setter), and update fxml-injected values. If you are manually loading FXML, you MUST call this to link fx_id specified names.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/jrubyfx/fxml_controller.rb', line 216

def scene=(s)
  @scene = s
  (@@fxml_linked_args[self.class] ||= []).each do |name|
    quiet = false
    # you can specify name => [quiet/verbose], so we need to check for that
    if name.is_a? Hash
      quiet = name.values[0] == :quiet
      name = name.keys[0]
    end
    # set each instance variable from the lookup on the scene
    val = s.lookup("##{name}")
    if val == nil && !quiet
      puts "[WARNING] fx_id not found: #{name}. Is id set to a different value than fx:id? (if this is expected, use fx_id_optional)"
    end
    instance_variable_set("@#{name}".to_sym, val)
  end
end