From 488fc08cd68de182a41f33106424f40808339f8b Mon Sep 17 00:00:00 2001 From: Sarah Gerweck Date: Sun, 8 Oct 2017 01:52:11 -0700 Subject: [PATCH] Rewrite `SingletonStage` for safety & power Also, add more documentation so this is actually a usable piece of code. --- .../gerweck/scalafx/util/SingletonStage.scala | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala b/src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala index 2512f68..e4e9ab2 100644 --- a/src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala +++ b/src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala @@ -1,39 +1,75 @@ package org.gerweck.scalafx.util +import scalafx.beans.property.{ ObjectProperty, ReadOnlyObjectProperty } import scalafx.stage.Stage import org.log4s._ -/** A stage that should only be open at most once per application. */ +/** A stage that should only be open at most once per application. + * + * To use this, you should do something like this: + * {{{ + * class AboutWindow extends SingletonStage { + * type InstanceStage = AboutStage + * protected[this] def makeStage() = new Stage with AboutStage + * trait AboutStage extends super.ParentStage { stage => + * title = "About My Application" + * scene = new Scene { + * ??? + * } + * } + * } + * }}} + */ abstract class SingletonStage { private[this] val logger = getLogger + /** The specific type of the underlying stage that you'll create. */ type InstanceStage <: ParentStage - protected[this] final var singletonStage: Option[InstanceStage] = None + private[this] object State { + private[this] val stageProp = ObjectProperty(Option.empty[InstanceStage]) + def currentStage = stageProp.value + def currentStage_=(stage: Option[InstanceStage]): Unit = { + stageProp.value = stage + } + def stage = stageProp.readOnly + } + + /** Get the current stage (or `None` if it doesn't exist). */ + def stage: ReadOnlyObjectProperty[Option[InstanceStage]] = State.stage protected[this] def singletonStageName = getClass.getSimpleName - protected[this] def makeStage(): Stage with InstanceStage + protected[this] def makeStage(): InstanceStage + /** Create the stage, or give it focus if it already exists. + * + * This needs to be executed within the UI thread. If you're not already within some kind + * of event handler, you need to use [[scalafx.application.Platform.runLater]]. + */ def showStage(): Unit = { - singletonStage match { + State.currentStage match { case Some(stg) => logger.debug("Singleton ${name} stage is already open") stg.requestFocus() case None => val ns = makeStage() - singletonStage = Some(ns) + State.currentStage = Some(ns) ns.show() } } protected[this] trait ParentStage extends Stage { - require(singletonStage.isEmpty, s"Cannot have two ${singletonStageName} stages") + require(State.currentStage.isEmpty, s"Cannot have two ${singletonStageName} stages") logger.trace(s"Creating singleton ${singletonStageName} stage") - override def close() = { - singletonStage = None + /** Override this if you need to provide custom `close` behavior. */ + protected[this] def onClose(): Unit = () + + override final def close() = { + State.currentStage = None + onClose() super.close() } }