New Dialogs support - feedback needed

40 views
Skip to first unread message

Jarek Sacha

unread,
Jan 10, 2015, 6:13:36 PM1/10/15
to scala...@googlegroups.com
Support for JavaFX 8u40 Dialogs is now mostly implemented.  All dialog classes are wrapped Dialog, Alert, ChoiceInputDialog, TextInputDialog, and related. Source code is on SFX8u40 branch. You can also use latest snapshot from Sonatype (8.0.40-SNAPSHOT). There are two demos that illustrate use of Dialog classes DialogsDemo (for Alerts) and LoginDialogDemo (custom dialog).  Most looks pretty good. Here is an example of using error alert:
new Alert(AlertType.Error) {
  title = "Error Dialog"
  headerText = "Look, an Error Dialog"
  contentText = "Ooops, there was an error!"
}.showAndWait()
Take look and let me know if you have suggestion on things that could be improved.

There is one use case that I am not completely happy. Confirmation Alerts return an Option indicating what button was pressed to close a dialog. Option inner type is `javafx.scene.control.ButtonType`. Since it is JavaFX rather than ScalaFX type matching can look bit verbose. Here is an example:
val alert = new Alert(AlertType.Confirmation) {
  title = "Confirmation Dialog"
  headerText = "Look, a Confirmation Dialog"
  contentText = "Are you ok with this?"
}

val result = alert.showAndWait()

result match {
  case Some(ButtonType.OK.delegate)     => println("OK")
  case Some(ButtonType.Cancel.delegate) => println("Cancel")
  case _                                => println("Something else")
}
Notice that type of the `result` is `Option[javafx.scene.control.ButtonType]`, which requires use of `Some(ButtonType.OK.delegate)` and `Some(ButtonType.Cancel.delegate)` in the match statement. This is a "common" issue with "inner" types that cannot be automatically wrapped by implicit conversions (at lest not by current ScalaFX).

To avoid `delegate` references I added a helper `apply` to `ButtonType` that converts the option to ScalaFX (`Option[scalafx.scene.control.ButtonType]`). It has to be used explicitly, but makes the use case a bit cleaner (no delegates):
val alert = new Alert(AlertType.Confirmation) {
  title = "Confirmation Dialog"
  headerText = "Look, a Confirmation Dialog"
  contentText = "Are you ok with this?"
}

val result = alert.showAndWait()

ButtonType(result) match {
  case Some(ButtonType.OK)     => println("OK")
  case Some(ButtonType.Cancel) => println("Cancel")
  case _                       => println("Something else")
}
I am looking for some suggestions how to improve it. Optimally this could be simply:
result match {
  case Some(ButtonType.OK)     => println("OK")
  case Some(ButtonType.Cancel) => println("Cancel")
  case _                       => println("Something else")
}
The questions is how this could be done. I tried playing with Dialog parameter type (here javafx.scene.control.ButtonType) trying to do some dependent type tricks to add a wrapper (here scalafx.scene.control.ButtonType) if it is available. I think it is at least theoretically possible, but I could not figure out yet how to do it. It is a more general problem in ScalaFX (inner types that should be wrapped), so solving this here could be used in may other places.

Let me know what you think,

Jarek


Jarek Sacha

unread,
Jan 13, 2015, 11:04:24 PM1/13/15
to scala...@googlegroups.com
On 1/10/2015 6:13 PM, Jarek Sacha wrote:
I am looking for some suggestions how to improve it. Optimally this could be simply:
result match {
  case Some(ButtonType.OK)     => println("OK")
  case Some(ButtonType.Cancel) => println("Cancel")
  case _                       => println("Something else")
}
The questions is how this could be done.
OK. I figured out how this could be done. That is, `Dialog.showAndWait() : Option[R]` can have its type parameter modified is type-safe way, at compilation time. We can have `Alert.showAndWait()` return `Option[sfx.ButtonType]` rather than `Option[jfx.ButtonType]`. Implementation of `showAndWait` was changed to:
def showAndWait[F](j2s: F = { x: R => x})(implicit convert: DConvert[R, F]): Option[convert.S] = {
  val v = delegate.showAndWait()
  if (v.isPresent) Some(convert(v.get, j2s)) else None
}
and is intended to be used simply as:
val result = dialog.showAndWait()
Despite complicated signature of the parameters in the implementation, they do not need to be used in user code.

Type conversion is done by the default parameters: `j2s` and `convert`. `DConvert` is implemented as:
trait DConvert[T, F] {
  type S
  def apply(t: T, f: F): S
}

object DConvert {
  def apply[T, F](implicit conv: DConvert[T, F]) = conv
  implicit def t2r[T, R]: DConvert[T, T => R] = new DConvert[T, T => R] {
    type S = R
    def apply(t: T, f: T => R) = f(t)
  }
}
it is supposed to capture return type from the `j2s` function (the first default parameter to `showAndWait`). By default `j2s` is just an identity, the return type is not modified, but it can be something else, for instance return a wrapper
   (x:jfx.ButtonType) => new sfx.ButtonType(x)
effectively modifying the return type to `Option[sfx.ButtonType]`. Quite possibly this can be further simplified. Suggestions are welcomed.
The new version was committed to SFX8u40 branch. The updated 8.0.40-SNAPSHOT was also published.

Jarek
Reply all
Reply to author
Forward
0 new messages