Browse Source
Updated project further
Updated project further
Updated scala version to 2.13.4 Compiles to Java 11 now Updated sbt to 1.4.3 Updated scalafx version to 14 Removed unneeded dependencies Removed Akka code Added lint and fatal warnings scalac options Added parantheses to side effecting methods Added >> and << methods to observablesmaster
Rohan Sircar
3 years ago
17 changed files with 234 additions and 229 deletions
-
7.gitignore
-
1.sbtopts
-
16build.sbt
-
6project/BasicSettings.scala
-
7project/Dependencies.scala
-
2project/ProjectSettings.scala
-
2project/build.properties
-
1project/plugins.sbt
-
136src/main/scala/org/gerweck/scalafx/akka/AkkaFX.scala
-
116src/main/scala/org/gerweck/scalafx/akka/AkkaFXCollections.scala
-
2src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala
-
5src/main/scala/org/gerweck/scalafx/util/control/ControlPanel.scala
-
4src/main/scala/org/gerweck/scalafx/util/control/DialogButtons.scala
-
24src/main/scala/org/gerweck/scalafx/util/observable.scala
-
8src/main/scala/org/gerweck/scalafx/util/observableCollection.scala
-
124src/main/scala/org/gerweck/scalafx/util/prefs/ObservablePref.scala
-
2version.sbt
@ -1,4 +1,3 @@ |
|||||
-J-Xmx4G |
-J-Xmx4G |
||||
-J-XX:MaxMetaspaceSize=4g |
-J-XX:MaxMetaspaceSize=4g |
||||
-J-XX:+UnlockCommercialFeatures |
|
||||
-J-XX:+FlightRecorder |
-J-XX:+FlightRecorder |
@ -1 +1 @@ |
|||||
sbt.version=1.3.8 |
|
||||
|
sbt.version=1.4.3 |
@ -0,0 +1 @@ |
|||||
|
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "2.0.2") |
@ -1,74 +1,74 @@ |
|||||
package org.gerweck.scalafx.akka |
|
||||
|
// package org.gerweck.scalafx.akka |
||||
|
|
||||
import scala.concurrent._ |
|
||||
|
// import scala.concurrent._ |
||||
|
|
||||
import akka.Done |
|
||||
import akka.stream.OverflowStrategy |
|
||||
import akka.stream.scaladsl._ |
|
||||
|
// import akka.Done |
||||
|
// import akka.stream.OverflowStrategy |
||||
|
// import akka.stream.scaladsl._ |
||||
|
|
||||
import scalafx.application.Platform.runLater |
|
||||
import scalafx.beans.property.Property |
|
||||
import scalafx.beans.value.ObservableValue |
|
||||
|
// import scalafx.application.Platform.runLater |
||||
|
// import scalafx.beans.property.Property |
||||
|
// import scalafx.beans.value.ObservableValue |
||||
|
|
||||
/** A master object that exposes all the Akka-ScalaFX bridges. |
|
||||
* |
|
||||
* @author Sarah Gerweck <sarah.a180@gmail.com> |
|
||||
*/ |
|
||||
object AkkaFX extends AkkaStreamFX with AkkaFXCollections |
|
||||
|
// /** A master object that exposes all the Akka-ScalaFX bridges. |
||||
|
// * |
||||
|
// * @author Sarah Gerweck <sarah.a180@gmail.com> |
||||
|
// */ |
||||
|
// object AkkaFX extends AkkaStreamFX with AkkaFXCollections |
||||
|
|
||||
trait AkkaStreamFX extends Any { |
|
||||
/** A [[akka.stream.scaladsl.Sink]] that sends all values to a |
|
||||
* [[scalafx.beans.property.Property]]. |
|
||||
* |
|
||||
* Each event that's written into the `Sink` will trigger an update of the |
|
||||
* `Property` with the streamed value. |
|
||||
*/ |
|
||||
def observableSink[A](prop: Property[A, _]): Sink[A, Future[Done]] = { |
|
||||
Sink.foreach[A] { a => |
|
||||
runLater { |
|
||||
prop.value = a |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
// trait AkkaStreamFX extends Any { |
||||
|
// /** A [[akka.stream.scaladsl.Sink]] that sends all values to a |
||||
|
// * [[scalafx.beans.property.Property]]. |
||||
|
// * |
||||
|
// * Each event that's written into the `Sink` will trigger an update of the |
||||
|
// * `Property` with the streamed value. |
||||
|
// */ |
||||
|
// def observableSink[A](prop: Property[A, _]): Sink[A, Future[Done]] = { |
||||
|
// Sink.foreach[A] { a => |
||||
|
// runLater { |
||||
|
// prop.value = a |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
/** A [[akka.stream.scaladsl.Source]] that generates an event for each |
|
||||
* change of an [[scalafx.beans.value.ObservableValue]]. |
|
||||
* |
|
||||
* This source adds an `onChange` handler to the given `ObservableValue`. |
|
||||
* Each time it observes a change, the new value is pushed from the |
|
||||
* `Source`. The change handler is registered as soon as the source is |
|
||||
* materialized into a graph. It should be safe to use a single source |
|
||||
* in several graphs, as each will register its own change listener upon |
|
||||
* materialization. |
|
||||
* |
|
||||
* @param prop The value to observe. |
|
||||
* |
|
||||
* @param queueSize The maximum number of values to queue while waiting for |
|
||||
* the downstream flow to consume more data. |
|
||||
* |
|
||||
* @param overflow What to do when the queue is full because the downstream |
|
||||
* flow cannot keep up. The default behavior is to block, slowing the UI's |
|
||||
* main thread until some events are consumed, freeing space in the queue. |
|
||||
*/ |
|
||||
def observableSource[A](prop: ObservableValue[_, A], |
|
||||
queueSize: Int = 10, |
|
||||
overflow: OverflowStrategy = OverflowStrategy.backpressure |
|
||||
)(implicit ec: ExecutionContext) = { |
|
||||
val src = Source |
|
||||
.queue[A](queueSize, overflow) |
|
||||
.mapMaterializedValue { m => |
|
||||
val sub = { |
|
||||
prop.onChange { (dta, oldV, newV) => |
|
||||
m.offer(newV) |
|
||||
} |
|
||||
} |
|
||||
m.watchCompletion foreach { c => |
|
||||
runLater { |
|
||||
sub.cancel() |
|
||||
} |
|
||||
} |
|
||||
m |
|
||||
} |
|
||||
src |
|
||||
} |
|
||||
} |
|
||||
|
// /** A [[akka.stream.scaladsl.Source]] that generates an event for each |
||||
|
// * change of an [[scalafx.beans.value.ObservableValue]]. |
||||
|
// * |
||||
|
// * This source adds an `onChange` handler to the given `ObservableValue`. |
||||
|
// * Each time it observes a change, the new value is pushed from the |
||||
|
// * `Source`. The change handler is registered as soon as the source is |
||||
|
// * materialized into a graph. It should be safe to use a single source |
||||
|
// * in several graphs, as each will register its own change listener upon |
||||
|
// * materialization. |
||||
|
// * |
||||
|
// * @param prop The value to observe. |
||||
|
// * |
||||
|
// * @param queueSize The maximum number of values to queue while waiting for |
||||
|
// * the downstream flow to consume more data. |
||||
|
// * |
||||
|
// * @param overflow What to do when the queue is full because the downstream |
||||
|
// * flow cannot keep up. The default behavior is to block, slowing the UI's |
||||
|
// * main thread until some events are consumed, freeing space in the queue. |
||||
|
// */ |
||||
|
// def observableSource[A](prop: ObservableValue[_, A], |
||||
|
// queueSize: Int = 10, |
||||
|
// overflow: OverflowStrategy = OverflowStrategy.backpressure |
||||
|
// )(implicit ec: ExecutionContext) = { |
||||
|
// val src = Source |
||||
|
// .queue[A](queueSize, overflow) |
||||
|
// .mapMaterializedValue { m => |
||||
|
// val sub = { |
||||
|
// prop.onChange { (dta, oldV, newV) => |
||||
|
// m.offer(newV) |
||||
|
// } |
||||
|
// } |
||||
|
// m.watchCompletion() foreach { c => |
||||
|
// runLater { |
||||
|
// sub.cancel() |
||||
|
// } |
||||
|
// } |
||||
|
// m |
||||
|
// } |
||||
|
// src |
||||
|
// } |
||||
|
// } |
@ -1,72 +1,72 @@ |
|||||
package org.gerweck.scalafx.akka |
|
||||
|
// package org.gerweck.scalafx.akka |
||||
|
|
||||
import language.implicitConversions |
|
||||
|
// import language.implicitConversions |
||||
|
|
||||
import scala.collection.mutable.{ Clearable, Growable } |
|
||||
import scala.concurrent._ |
|
||||
import scala.concurrent.duration._ |
|
||||
import scala.util._ |
|
||||
|
// import scala.collection.mutable.{ Clearable, Growable } |
||||
|
// import scala.concurrent._ |
||||
|
// import scala.concurrent.duration._ |
||||
|
// import scala.util._ |
||||
|
|
||||
import akka.{ Done, NotUsed } |
|
||||
import akka.stream.Materializer |
|
||||
import akka.stream.scaladsl._ |
|
||||
|
// import akka.{ Done, NotUsed } |
||||
|
// import akka.stream.Materializer |
||||
|
// import akka.stream.scaladsl._ |
||||
|
|
||||
import scalafx.application.Platform.runLater |
|
||||
import scalafx.beans.Observable |
|
||||
import scalafx.beans.property._ |
|
||||
|
// import scalafx.application.Platform.runLater |
||||
|
// import scalafx.beans.Observable |
||||
|
// import scalafx.beans.property._ |
||||
|
|
||||
import org.gerweck.scalafx.util.FutureObservable |
|
||||
|
// import org.gerweck.scalafx.util.FutureObservable |
||||
|
|
||||
trait AkkaFXCollections extends Any { |
|
||||
implicit def sourceToRichSource[A, B](source: Source[A, B]) = new AkkaFXCollections.RichSource(source) |
|
||||
} |
|
||||
|
// trait AkkaFXCollections extends Any { |
||||
|
// implicit def sourceToRichSource[A, B](source: Source[A, B]) = new AkkaFXCollections.RichSource(source) |
||||
|
// } |
||||
|
|
||||
object AkkaFXCollections { |
|
||||
def collectionWriterSink[A] |
|
||||
(buffer: Observable with Growable[A] with Clearable, |
|
||||
clearFirst: Boolean, |
|
||||
groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds) |
|
||||
: Sink[A, Future[Done]] = { |
|
||||
sealed trait PopulateAction |
|
||||
case class InsertRows(data: Seq[A]) extends PopulateAction |
|
||||
case object ClearData extends PopulateAction |
|
||||
|
// object AkkaFXCollections { |
||||
|
// def collectionWriterSink[A] |
||||
|
// (buffer: Observable with Growable[A] with Clearable, |
||||
|
// clearFirst: Boolean, |
||||
|
// groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds) |
||||
|
// : Sink[A, Future[Done]] = { |
||||
|
// sealed trait PopulateAction |
||||
|
// case class InsertRows(data: Seq[A]) extends PopulateAction |
||||
|
// case object ClearData extends PopulateAction |
||||
|
|
||||
val grouping = { |
|
||||
Flow[A] |
|
||||
.groupedWithin(groupingSize, groupingTimeout) |
|
||||
.map(InsertRows) |
|
||||
.named("GroupInsertActions") |
|
||||
} |
|
||||
|
// val grouping = { |
||||
|
// Flow[A] |
||||
|
// .groupedWithin(groupingSize, groupingTimeout) |
||||
|
// .map(InsertRows) |
||||
|
// .named("GroupInsertActions") |
||||
|
// } |
||||
|
|
||||
val clearData = { |
|
||||
if (clearFirst) |
|
||||
Source.single(ClearData) |
|
||||
else |
|
||||
Source.empty |
|
||||
}.named("OptionalClearAction") |
|
||||
|
// val clearData = { |
||||
|
// if (clearFirst) |
||||
|
// Source.single(ClearData) |
||||
|
// else |
||||
|
// Source.empty |
||||
|
// }.named("OptionalClearAction") |
||||
|
|
||||
val combinedSource: Flow[A, PopulateAction, NotUsed] = grouping.prepend(clearData) |
|
||||
combinedSource .toMat { |
|
||||
Sink .foreach[PopulateAction] { |
|
||||
case ClearData => runLater { buffer.clear() } |
|
||||
case InsertRows(data) => runLater { buffer ++= data } |
|
||||
} .named("BufferWriter") |
|
||||
}(Keep.right) |
|
||||
} |
|
||||
|
// val combinedSource: Flow[A, PopulateAction, NotUsed] = grouping.prepend(clearData) |
||||
|
// combinedSource .toMat { |
||||
|
// Sink .foreach[PopulateAction] { |
||||
|
// case ClearData => runLater { buffer.clear() } |
||||
|
// case InsertRows(data) => runLater { buffer ++= data } |
||||
|
// } .named("BufferWriter") |
||||
|
// }(Keep.right) |
||||
|
// } |
||||
|
|
||||
class RichSource[A, B](val inner: Source[A, B]) extends AnyVal { |
|
||||
def populateCollection[C >: A] |
|
||||
(buffer: Observable with Growable[C] with Clearable, |
|
||||
clearFirst: Boolean, |
|
||||
groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds) |
|
||||
(implicit mat: Materializer, ec: ExecutionContext) |
|
||||
: ReadOnlyObjectProperty[Option[Try[Done]]] = { |
|
||||
|
// class RichSource[A, B](val inner: Source[A, B]) extends AnyVal { |
||||
|
// def populateCollection[C >: A] |
||||
|
// (buffer: Observable with Growable[C] with Clearable, |
||||
|
// clearFirst: Boolean, |
||||
|
// groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds) |
||||
|
// (implicit mat: Materializer, ec: ExecutionContext) |
||||
|
// : ReadOnlyObjectProperty[Option[Try[Done]]] = { |
||||
|
|
||||
val sink = collectionWriterSink(buffer, clearFirst, groupingSize, groupingTimeout) |
|
||||
|
// val sink = collectionWriterSink(buffer, clearFirst, groupingSize, groupingTimeout) |
||||
|
|
||||
val graph = inner.toMat(sink)(Keep.right) |
|
||||
|
// val graph = inner.toMat(sink)(Keep.right) |
||||
|
|
||||
FutureObservable.ofTryOption(graph.run()) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
// FutureObservable.ofTryOption(graph.run()) |
||||
|
// } |
||||
|
// } |
||||
|
// } |
@ -1,62 +1,62 @@ |
|||||
package org.gerweck.scalafx.util |
|
||||
package prefs |
|
||||
|
|
||||
import java.util.prefs._ |
|
||||
|
|
||||
import scalafx.application.Platform.runLater |
|
||||
import scalafx.beans.property.ObjectProperty |
|
||||
|
|
||||
import org.gerweck.scala.util.prefs._ |
|
||||
|
|
||||
/* TODO: take an implicit that will deteremine whether this is an `ObjectProperty` or what */ |
|
||||
class ObservablePref[A] protected (path: String)(implicit handler: Pref.AccessHandler[A], prefs: Preferences) |
|
||||
extends Pref[A](path) { thisPref => |
|
||||
|
|
||||
lazy val observe: ObjectProperty[A] = { |
|
||||
val initialValue: A = this() |
|
||||
val property = ObjectProperty[A](initialValue) |
|
||||
|
|
||||
/* We build two bridges, one that listens for changes in the preferences system and pushes |
|
||||
* them into the observable property, and another that listens for updates to the property and |
|
||||
* pushes them to the preference system. Each bridge is gated so that it only activates if the |
|
||||
* value has actually changed, which prevents the infinite looping that would otherwise occur |
|
||||
* in a bidirectional bridge. */ |
|
||||
|
|
||||
/* Preferences => Property bridge */ |
|
||||
|
|
||||
/* In Scala 2.12, the callback could just be bare inside `addPreferenceChangeListener`. |
|
||||
* However, it must be created explicitly since we cross-compile against Scala 2.11. */ |
|
||||
val changeListener = new PreferenceChangeListener { |
|
||||
def preferenceChange(pce: PreferenceChangeEvent): Unit = { |
|
||||
if (pce.getKey == path) { |
|
||||
runLater { |
|
||||
val currentValue = thisPref() |
|
||||
if (property.value != currentValue) { |
|
||||
property.value = currentValue |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
prefs.addPreferenceChangeListener(changeListener) |
|
||||
|
|
||||
/* Property => Preferences bridge */ |
|
||||
property.onChange { (_, _, newV) => |
|
||||
if (newV != this()) { |
|
||||
this() = newV |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Return the bridged property */ |
|
||||
property |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
object ObservablePref { |
|
||||
def apply[A](path: String)(implicit handler: PrefHandler[A], prefs: Preferences) = { |
|
||||
new ObservablePref(path)(new Pref.AccessHandler.Optional, prefs) |
|
||||
} |
|
||||
def apply[A](path: String, default: A)(implicit handler: PrefHandler[A], prefs: Preferences) = { |
|
||||
new ObservablePref(path)(new Pref.AccessHandler.Defaulted(default), prefs) |
|
||||
} |
|
||||
} |
|
||||
|
// package org.gerweck.scalafx.util |
||||
|
// package prefs |
||||
|
|
||||
|
// import java.util.prefs._ |
||||
|
|
||||
|
// import scalafx.application.Platform.runLater |
||||
|
// import scalafx.beans.property.ObjectProperty |
||||
|
|
||||
|
// import org.gerweck.scala.util.prefs._ |
||||
|
|
||||
|
// /* TODO: take an implicit that will deteremine whether this is an `ObjectProperty` or what */ |
||||
|
// class ObservablePref[A] protected (path: String)(implicit handler: Pref.AccessHandler[A], prefs: Preferences) |
||||
|
// extends Pref[A](path) { thisPref => |
||||
|
|
||||
|
// lazy val observe: ObjectProperty[A] = { |
||||
|
// val initialValue: A = this() |
||||
|
// val property = ObjectProperty[A](initialValue) |
||||
|
|
||||
|
// /* We build two bridges, one that listens for changes in the preferences system and pushes |
||||
|
// * them into the observable property, and another that listens for updates to the property and |
||||
|
// * pushes them to the preference system. Each bridge is gated so that it only activates if the |
||||
|
// * value has actually changed, which prevents the infinite looping that would otherwise occur |
||||
|
// * in a bidirectional bridge. */ |
||||
|
|
||||
|
// /* Preferences => Property bridge */ |
||||
|
|
||||
|
// /* In Scala 2.12, the callback could just be bare inside `addPreferenceChangeListener`. |
||||
|
// * However, it must be created explicitly since we cross-compile against Scala 2.11. */ |
||||
|
// val changeListener = new PreferenceChangeListener { |
||||
|
// def preferenceChange(pce: PreferenceChangeEvent): Unit = { |
||||
|
// if (pce.getKey == path) { |
||||
|
// runLater { |
||||
|
// val currentValue = thisPref() |
||||
|
// if (property.value != currentValue) { |
||||
|
// property.value = currentValue |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// prefs.addPreferenceChangeListener(changeListener) |
||||
|
|
||||
|
// /* Property => Preferences bridge */ |
||||
|
// property.onChange { (_, _, newV) => |
||||
|
// if (newV != this()) { |
||||
|
// this() = newV |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
// /* Return the bridged property */ |
||||
|
// property |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
// object ObservablePref { |
||||
|
// def apply[A](path: String)(implicit handler: PrefHandler[A], prefs: Preferences) = { |
||||
|
// new ObservablePref(path)(new Pref.AccessHandler.Optional, prefs) |
||||
|
// } |
||||
|
// def apply[A](path: String, default: A)(implicit handler: PrefHandler[A], prefs: Preferences) = { |
||||
|
// new ObservablePref(path)(new Pref.AccessHandler.Defaulted(default), prefs) |
||||
|
// } |
||||
|
// } |
@ -1 +1 @@ |
|||||
version in ThisBuild := "0.14.4-SNAPSHOT" |
|
||||
|
version in ThisBuild := "0.15.0" |
Write
Preview
Loading…
Cancel
Save
Reference in new issue