All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalafx.application.JFXApp.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2015, ScalaFX Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the ScalaFX Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package scalafx.application

import javafx.application.Application
import javafx.{application => jfxa, stage => jfxs}

import scala.collection.JavaConversions.{mapAsJavaMap, seqAsJavaList}
import scala.collection.mutable.{Buffer, ListBuffer}
import scala.collection.{Map, Seq, mutable}
import scala.language.implicitConversions
import scalafx.application.JFXApp.{Parameters, PrimaryStage}
import scalafx.delegate.SFXDelegate
import scalafx.stage.Stage

object JFXApp {
  var STAGE: jfxs.Stage = null
  var ACTIVE_APP: JFXApp = null
  var AUTO_SHOW = true

  /**
   * Regular expression for parsing name/value parameters.
   */
  private val keyValue = """^--([A-Za-z_][^=]*?)=(.*)$""".r

  object Parameters {
    implicit def sfxParameters2jfx(p: Parameters): Application.Parameters = if (p != null) p.delegate else null

    /**
     * Creates a new instance of Parameters
     */
    private[application] def apply(arguments: Seq[String]): Parameters =
      if (arguments.isEmpty) EmptyParameters else new ParametersImpl(arguments)

  }

  /**
   * Wraps
   * [[http://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.Parameters.html Application.Parameters]]
   * class.
   */
  abstract class Parameters extends SFXDelegate[jfxa.Application.Parameters] {

    /**
     * Retrieves a read-only map of the named parameters.
     */
    def named: Map[String, String]

    /**
     * Retrieves a read-only list of the raw arguments.
     */
    def raw: Seq[String]

    /**
     * Retrieves a read-only list of the unnamed parameters.
     */
    def unnamed: Seq[String]

  }

  /**
   * Default implementation for Parameters class.
   */
  private[application] class ParametersImpl(arguments: Seq[String]) extends Parameters {

    private var namedArguments: mutable.Map[String, String] = mutable.Map.empty[String, String]
    private var unnamedArguments = Buffer.empty[String]
    private var filled = false

    private def parseArguments() {
      if (!filled) {
        arguments.foreach(arg =>
          keyValue.findFirstMatchIn(arg) match {
            case None          => unnamedArguments += arg
            case Some(matcher) => namedArguments(matcher.group(1)) = matcher.group(2)
          })
        filled = true
      }
    }

    def raw = arguments

    def named = {
      parseArguments()
      namedArguments
    }

    def unnamed = {
      parseArguments()
      unnamedArguments
    }

    lazy val delegate = new jfxa.Application.Parameters {
      def getRaw = raw
      def getNamed = named
      def getUnnamed = unnamed
    }

  }

  /**
   * Empty parameters for an application
   */
  private[application] object EmptyParameters extends Parameters {
    def raw = Seq.empty[String]
    def named = Map.empty[String, String]
    def unnamed = Seq.empty[String]
    lazy val delegate = new jfxa.Application.Parameters {
      def getRaw = raw
      def getNamed = named
      def getUnnamed = unnamed
    }
  }

  /** Simple helper class for construction of primary application stages.
    *
    * The primary stage has to wrap an instance of a JavaFX primary stage created by JavaFX when application
    * is initialized.
    *
    * {{{
    *   object SimpleScalaFXApp extends JFXApp {
    *      stage = new PrimaryStage {
    *        title = "Simple ScalaFX App"
    *        scene = new Scene {
    *          root = new StackPane {
    *            padding = Insets(20)
    *            content = new Rectangle {
    *              width = 200
    *              height = 200
    *              fill = Color.DEEPSKYBLUE
    *            }
    *          }
    *        }
    *      }
    *   }
    * }}}
    */
  class PrimaryStage extends Stage(JFXApp.STAGE)

}

/** ScalaFX applications can extend JFXApp to create properly initialized JavaFX applications.
  *
  * On the back end `JFXApp` first calls [[http://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html#launch javafx.application.Application.launch]] then executes body of its
  * constructor when
  * [[http://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html#start(javafx.stage.Stage) javafx.application.Application.start(primaryStage:Stage)]]
  * is called. Here is an example use:
  *
  * {{{
  *   object SimpleScalaFXApp extends JFXApp {
  *      stage = new PrimaryStage {
  *        title = "Simple ScalaFX App"
  *        scene = new Scene {
  *          root = new StackPane {
  *            padding = Insets(20)
  *            content = new Rectangle {
  *              width = 200
  *              height = 200
  *              fill = Color.DEEPSKYBLUE
  *            }
  *          }
  *        }
  *      }
  *   }
  * }}}
  *
  */
trait JFXApp extends DelayedInit {

  // Since JFXApp is now a trait, it is immune from the behavior of the DelayedInit marker trait. All JFXApp
  // initialization code is executed immediately, rather than being passed to delayedInit() and executed when init() is
  // called during JavaFX application startup. Put non-essential initialization in main() prior to the application
  // startup.

  /** JFXApp stage must be an instance of [[scalafx.application.JFXApp.PrimaryStage]] to ensure that it
    * actually is a proper wrapper for the primary stage supplied by JavaFX. */
  var stage: PrimaryStage = null

  private var arguments: Seq[String] = _

  /** Buffer code (constructor/initialization code) for all classes & objects that implement JFXApp. This code is
    * passed in through compiler-generated calls to delayedInit. The resulting code is then executed - in the same
    * order - in main. (Note that traits inheriting or mixed in with JFXApp have their initialization performed
    * immediately. See [[scala.DelayedInit]] for more information.
    */
  private val subClassInitCode = new ListBuffer[() => Unit]

  /**
   * Set of parameters for an application
   */
  protected lazy val parameters: Parameters = Parameters(arguments)

  /* Add class/object construction/initialization code to the code execution buffer.
   *
   * This function is called multiple times (by the Scala compiler) with the initialization/construction code of each
   * class and object (but not trait!) that extends JFXApp. This code is buffered until it can be executed in main().
   *
   * @note I (Mike Allen) think there's a good case for making this final, to prevent user code from destroying the
   * application initialization logic.
   */
  def delayedInit(x: => Unit) {
    subClassInitCode += (() => x)
  }

  /* Perform app-related initialization, and execute initialization/construction code for all classes and object that
   * extend this trait.
   *
   * @note I (Mike Allen) think there's a good case for making this final, to prevent user code from destroying the
   * application initialization logic.
   */
  def main(args: Array[String]) {
    JFXApp.ACTIVE_APP = this
    arguments = args
    // Put any further non-essential initialization here.
    /* Launch the JFX application.
    */
    jfxa.Application.launch(classOf[AppHelper], args: _*)
  }

  /** Perform sub-class initialization when directed to duing application startup.
    *
    * Execute the construction/initialization code of all classes/objects that extend JFXApp, that was earlier passed
    * to delayedInit() by the compiler.
    */
  private[application] final def init(): Unit = for (initCode <- subClassInitCode) initCode()

  /**
   * This method is called when the application should stop, and provides a convenient place to prepare
   * for application exit and destroy resources.
   *
   * It is called from javafx.Application.stop method.
   * The implementation of this method provided by the JFXApp class does nothing.
   *
   * NOTE: This method is called on the JavaFX Application Thread, the same as javafx.Application.stop method.
   */
  def stopApp() {
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy