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

scalafx.scene.control.ComboBox.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-2022, 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.scene.control

import javafx.scene.control as jfxsc
import javafx.{collections as jfxc, scene as jfxs, util as jfxu}
import scalafx.Includes.*
import scalafx.beans.property.{IntegerProperty, ObjectProperty, ReadOnlyObjectProperty}
import scalafx.collections.ObservableBuffer
import scalafx.collections.ObservableBuffer.*
import scalafx.delegate.SFXDelegate
import scalafx.scene.Node
import scalafx.util.StringConverter
import scalafx.util.StringConverter.*

import scala.language.implicitConversions

object ComboBox {
  implicit def sfxComboBox2jfx[T](cb: ComboBox[T]): jfxsc.ComboBox[T] = if (cb != null) cb.delegate else null
}

/**
 * Wraps [[https://openjfx.io/javadoc/16/javafx.controls/javafx/scene/control/ComboBox.html JavaFX ComboBox]],
 * an implementation of the ComboBoxBase abstract class for the most common form of ComboBox,
 * where a popup list is shown to users providing them with a choice that they may select from.
 *
 * On top of ComboBoxBase, the ComboBox class introduces additional API.
 * Most importantly, it adds an items property that works in much the same way as the ListView items property.
 * In other words, it is the content of the items list that is displayed to users when they click on the ComboBox button.
 *
 * It is not recommended to add `Node`s as ComboBox items.
 * ScalaFX provides for a convenient customization of a ComboBox content.
 * Required `Node` or other customization is add ed through cell factory.
 * Following code will show colored `Rectangle` representing item's `Color`
 * {{{
 *   val comboBox = new ComboBox[Color] {
 *     items = Seq(Color.Red, Color.Green, Color.Blue)
 *     // Custom factory displaying list items are colored rectangles
 *     // `color` is the value of the item displayed
 *     cellFactory = (cell, color) => {
 *       cell.contentDisplay = ContentDisplay.GraphicOnly
 *       cell.graphic = Rectangle(10, 10, color)
 *     }
 *   }
 * }}}
 *
 * @param delegate the wrapped JavaFX class.
 * @tparam T The type of the value that has been selected or otherwise entered in to this ComboBox
 */
class ComboBox[T](override val delegate: jfxsc.ComboBox[T] = new jfxsc.ComboBox[T])
    extends ComboBoxBase(delegate)
    with SFXDelegate[jfxsc.ComboBox[T]] {

  /**
   * Creates a default ComboBox instance from a [[scalafx.collections.ObservableBuffer]]
   * with the provided items list and a default selection model.
   */
  def this(items: ObservableBuffer[T]) = this(new jfxsc.ComboBox[T](items))

  /**
   * Creates a default ComboBox instance from a [[scala.Seq]]
   * with the provided items list and a default selection model.
   */
  def this(items: Seq[T]) = this(new jfxsc.ComboBox[T](ObservableBuffer.from(items)))

  /**
   * Providing a custom cell factory allows for complete customization of the rendering of items in the ComboBox.
   */
  def cellFactory: ObjectProperty[jfxu.Callback[jfxsc.ListView[T], jfxsc.ListCell[T]]] = delegate.cellFactoryProperty
  def cellFactory_=(callback: javafx.util.Callback[jfxsc.ListView[T], jfxsc.ListCell[T]]): Unit = {
    cellFactory() = callback
  }

  def cellFactory_=(f: ListView[T] => ListCell[T]): Unit = {
    delegate.cellFactoryProperty.setValue(new jfxu.Callback[jfxsc.ListView[T], jfxsc.ListCell[T]] {
      def call(v: jfxsc.ListView[T]): jfxsc.ListCell[T] = {
        f(v)
      }
    })
  }

  /**
   * A convenience method for creation of custom cell factory.
   * The caller is responsible for providing an operation `op` that renders a non-empty cells from a non-null value.
   *
   * Implementation provides logic for handling empty cells and `null` values.
   *
   * The `op` provides two arguments: a pre-created `cell` and `value` for that cell.
   * Caller can customize content of the `cell` based on the `value`.
   *
   * The `value` is guaranteed to be non `null`.
   * The `null` values are automatically rendered as empty cells by the implementation.
   *
   * Here is an example where `value`'s type is a class `Person` that contains two text fields: `firstName` and `lastName`.
   * {{{
   *   case class Person(firstName:String, lastName:String)
   *   ...
   *   cellFactory = (cell, value) => {
   *     cell.text = value.firstName + " " + value.lastName
   *   }
   * }}}
   *
   * Another example where `value` is of type `Color` and the cell factory creates a circle representing that color:
   * {{{
   *   cellFactory = (cell, value) => {
   *     cell.graphic = new Circle {
   *        fill = value
   *        radius = 8
   *     }
   *   }
   * }}}
   *
   * @param op a method that will create content for a given `cell`.
   *           It gets as an input automatically created custom `cell` and a non-null `value` of that cell.
   *           `op` is called in the cell's `updateItem` method.
   */
  def cellFactory_=(op: (ListCell[T], T) => Unit): Unit = {
    val callback =
      Option(op)
        .map { op =>
          new jfxu.Callback[jfxsc.ListView[T], jfxsc.ListCell[T]] {
            def call(tv: jfxsc.ListView[T]): jfxsc.ListCell[T] = {
              new jfxsc.ListCell[T] {
                val sfxThis = new ListCell(this)
                override def updateItem(item: T, empty: Boolean): Unit = {
                  super.updateItem(item, empty)
                  if (empty || item == null) {
                    setText(null)
                    setGraphic(null)
                  } else {
                    op(sfxThis, item)
                  }
                }
              }
            }
          }
        }
        .orNull
    delegate.cellFactoryProperty.setValue(callback)
  }

  /**
   * Converts the user-typed input (when the ComboBox is editable) to an object of type T, such that the input may be retrieved via the value property.
   */
  def converter: ObjectProperty[jfxu.StringConverter[T]] = delegate.converterProperty

  def converter_=(v: StringConverter[T]): Unit = {
    converter() = v
  }

  /**
   * The list of items to show within the ComboBox popup.
   */
  def items: ObjectProperty[jfxc.ObservableList[T]] = delegate.itemsProperty

  def items_=(v: ObservableBuffer[T]): Unit = {
    items() = v
  }

  /**
   * This Node is shown to the user when the ComboBox has no content to show.
   */
  def placeholder: ObjectProperty[jfxs.Node] = delegate.placeholderProperty

  def placeholder_=(v: Node): Unit = {
    ObjectProperty.fillProperty[jfxs.Node](placeholder, v)
  }

  /**
   * The selection model for the ComboBox.
   */
  def selectionModel: ObjectProperty[jfxsc.SingleSelectionModel[T]] = delegate.selectionModelProperty

  def selectionModel_=(v: SingleSelectionModel[T]): Unit = {
    selectionModel() = v.delegate
  }

  /**
   * The maximum number of rows to be visible in the ComboBox popup when it is showing.
   */
  def visibleRowCount: IntegerProperty = delegate.visibleRowCountProperty

  def visibleRowCount_=(v: Int): Unit = {
    visibleRowCount() = v
  }

  /**
   * The button cell is used to render what is shown in the ComboBox 'button' area.
   * If a cell is set here, it does not change the rendering of the ComboBox popup list -
   * that rendering is controlled via the cell factory API.
   *
   * @since 2.2
   */
  def buttonCell: ObjectProperty[jfxsc.ListCell[T]] = delegate.buttonCellProperty()

  def buttonCell_=(v: ListCell[T]): Unit = {
    buttonCell() = v
  }

  /**
   * The editor for the ComboBox.
   *
   * @since 2.2
   */
  def editor: ReadOnlyObjectProperty[jfxsc.TextField] = delegate.editorProperty()

  /**
   * Append a item at end of list of items
   *
   * @param item Item to be added.
   * @return Combobox itself
   */
  def +=(item: T): Unit = {
    this.items.value += item
  }

  /**
   * Remove a item in list of items
   *
   * @param item Item to be removed.
   * @return Combobox itself
   */
  def -=(item: T): Unit = {
    this.items.value -= item
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy