@ -0,0 +1,24 @@ | |||
*.class | |||
*.log | |||
# sbt specific | |||
.cache/ | |||
.history/ | |||
.lib/ | |||
dist/* | |||
target/ | |||
lib_managed/ | |||
src_managed/ | |||
project/boot/ | |||
project/plugins/project/ | |||
metals.sbt | |||
.metals | |||
.bloop | |||
# Scala-IDE specific | |||
.scala_dependencies | |||
.worksheet | |||
.idea/ | |||
.vscode | |||
/project/project |
@ -0,0 +1 @@ | |||
version = "2.6.4" |
@ -0,0 +1,24 @@ | |||
This is free and unencumbered software released into the public domain. | |||
Anyone is free to copy, modify, publish, use, compile, sell, or | |||
distribute this software, either in source code form or as a compiled | |||
binary, for any purpose, commercial or non-commercial, and by any | |||
means. | |||
In jurisdictions that recognize copyright laws, the author or authors | |||
of this software dedicate any and all copyright interest in the | |||
software to the public domain. We make this dedication for the benefit | |||
of the public at large and to the detriment of our heirs and | |||
successors. We intend this dedication to be an overt act of | |||
relinquishment in perpetuity of all present and future rights to this | |||
software under copyright law. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||
OTHER DEALINGS IN THE SOFTWARE. | |||
For more information, please refer to <https://unlicense.org> |
@ -0,0 +1,61 @@ | |||
// Name of the project | |||
name := "ScalaFX Hello World" | |||
// Project version | |||
version := "14-R19" | |||
// Version of Scala used by the project | |||
scalaVersion := "2.13.3" | |||
// Add dependency on ScalaFX library | |||
libraryDependencies += "org.scalafx" %% "scalafx" % "14-R19" | |||
resolvers += Resolver.sonatypeRepo("snapshots") | |||
enablePlugins(JavaFxPlugin) | |||
libraryDependencies ++= Seq( | |||
"org.typelevel" %% "cats-core" % "2.1.1", | |||
"org.typelevel" %% "cats-effect" % "2.1.4", | |||
"io.monix" %% "monix" % "3.2.2", | |||
"io.monix" %% "monix-bio" % "1.0.0", | |||
"io.circe" %% "circe-core" % "0.13.0", | |||
"io.circe" %% "circe-generic" % "0.13.0", | |||
"com.softwaremill.sttp.client" %% "core" % "2.2.5", | |||
"com.softwaremill.sttp.client" %% "monix" % "2.2.5", | |||
"com.softwaremill.sttp.client" %% "circe" % "2.2.5", | |||
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5", | |||
"com.github.valskalla" %% "odin-monix" % "0.8.1", | |||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.8", | |||
"com.softwaremill.macwire" %% "util" % "2.3.7", | |||
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided", | |||
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided", | |||
"com.github.valskalla" %% "odin-slf4j" % "0.8.1" | |||
) | |||
scalacOptions ++= Seq( | |||
"-unchecked", | |||
"-deprecation", | |||
"-Xcheckinit", | |||
"-encoding", | |||
"utf8", | |||
"-feature", | |||
"-Ywarn-unused:imports" | |||
) | |||
// Fork a new JVM for 'run' and 'test:run', to avoid JavaFX double initialization problems | |||
fork := true | |||
// Determine OS version of JavaFX binaries | |||
lazy val osName = System.getProperty("os.name") match { | |||
case n if n.startsWith("Linux") => "linux" | |||
case n if n.startsWith("Mac") => "mac" | |||
case n if n.startsWith("Windows") => "win" | |||
case _ => throw new Exception("Unknown platform!") | |||
} | |||
// Add JavaFX dependencies | |||
lazy val javaFXModules = | |||
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web") | |||
libraryDependencies ++= javaFXModules.map(m => | |||
"org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName | |||
) |
@ -0,0 +1,2 @@ | |||
sbt.version=1.3.10 | |||
@ -0,0 +1,6 @@ | |||
scalacOptions ++= Seq("-unchecked", "-deprecation") | |||
// [https://github.com/sbt/sbteclipse] | |||
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") | |||
// addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10") | |||
addSbtPlugin("com.quadstingray" % "sbt-javafx" % "1.5.2") |
@ -0,0 +1,5 @@ | |||
javafx-dispatcher { | |||
type = "Dispatcher" | |||
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator" | |||
throughput = 1 | |||
} |
@ -0,0 +1,219 @@ | |||
package nova.monadic_sfx | |||
import scalafx.application.JFXApp | |||
import scalafx.application.JFXApp.PrimaryStage | |||
import monix.eval.Task | |||
import monix.execution.Scheduler | |||
// import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend | |||
// import sttp.client._ | |||
// import sttp.client.circe._ | |||
// import io.circe.generic.auto._ | |||
import scala.util.Failure | |||
import scala.util.Success | |||
import akka.{actor => classic} | |||
import nova.monadic_sfx.executors._ | |||
import cats.effect.Resource | |||
import nova.monadic_sfx.models._ | |||
import nova.monadic_sfx.ui.DefaultUI | |||
import nova.monadic_sfx.http.Backend | |||
import nova.monadic_sfx.modules.MainModule | |||
import scalafx.stage.Stage | |||
import scalafx.scene.layout.FlowPane | |||
import nova.monadic_sfx.pages.LoginPage | |||
import scala.concurrent.duration._ | |||
import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend | |||
import scalafx.Includes._ | |||
import scala.concurrent.Await | |||
import akka.actor.typed.scaladsl.adapter._ | |||
import scalafx.application.Platform | |||
import io.odin.syntax._ | |||
// import io.odin._ | |||
import io.odin.monix._ | |||
import nova.monadic_sfx.pages.HomePage | |||
object ScalaFXHelloWorld extends JFXApp with MainModule { | |||
val logger = consoleLogger().withAsyncUnsafe() | |||
lazy val schedulers: Schedulers = new Schedulers() | |||
implicit lazy val defaultScheduler: Scheduler = schedulers.fx | |||
lazy val backendTask = AsyncHttpClientMonixBackend() | |||
lazy val actorSystemTask = Task { | |||
classic.ActorSystem( | |||
name = "FXActorSystem" | |||
) | |||
} | |||
lazy val application = for { | |||
_ <- logger.info("Starting application..") | |||
backend <- backendTask | |||
actorSystem <- actorSystemTask | |||
// to spawn child actors | |||
// _ <- Task { actorSystem.spawn() } | |||
appStage <- Task { makePrimaryStage(backend, actorSystem) } | |||
// splash screen | |||
_ <- Task { | |||
// this stage refers to implicit jfx stage | |||
// makes this impure, but I can't think of a better way right now | |||
stage = appStage | |||
} | |||
// wait 2 seconds before showing home screen | |||
d <- deps | |||
fib1 <- d.send().start | |||
_ <- Task.sleep(2.seconds) | |||
_ <- Task { | |||
// appStage.maximized = true | |||
appStage.height = 800 | |||
appStage.width = 800 | |||
appStage | |||
.scene() | |||
.setRoot( | |||
LoginPage(appStage, backend, actorSystem) | |||
) | |||
} | |||
// _ <- fib1.join | |||
} yield () | |||
application.runToFuture | |||
.onComplete(res => | |||
res match { | |||
case Failure(exception) => { | |||
println("Application start failed. Reason -") | |||
exception.printStackTrace() | |||
} | |||
case Success(value) => println("Application started Successfully") | |||
} | |||
) | |||
// Task | |||
// .suspend { | |||
// val program = Task { | |||
// stage = new PrimaryStage { | |||
// // initStyle(StageStyle.Unified) | |||
// title = "ScalaFX Hello World" | |||
// scene = defaultUI.scene | |||
// } | |||
// } | |||
// val backendResource = AsyncHttpClientMonixBackend | |||
// .resource() | |||
// .use { implicit backend => | |||
// Task | |||
// .suspend( | |||
// (for { | |||
// req <- | |||
// basicRequest | |||
// .get(uri"https://httpbin.org/get") | |||
// .response(asJson[HttpBinResponse]) | |||
// .send() | |||
// } yield println(req)) >> | |||
// Task(println(Thread.currentThread().getName())) | |||
// ) | |||
// // .delayExecution(1.second) | |||
// } | |||
// .executeOn(Scheduler.global) | |||
// val akkaResource = Resource | |||
// .make(Task { | |||
// classic.ActorSystem( | |||
// name = "FXActorSystem" | |||
// ) | |||
// })(sys => Task(println("Shutting down actor system")) >> Task(sys.terminate())) | |||
// .use { implicit system => | |||
// // system.spa | |||
// // system.typed | |||
// // val javaFxActor = system.actorOf( | |||
// // Props[JavaFxActor]().withDispatcher("javafx-dispatcher"), | |||
// // "javaFxActor" | |||
// // ) | |||
// // val swingActor = system.actorOf( | |||
// // Props[SwingActor]().withDispatcher("swing-dispatcher"), | |||
// // "swingActor" | |||
// // ) | |||
// Task.unit | |||
// } | |||
// .delayExecution(1.second) | |||
// backendResource.start >> akkaResource.start >> | |||
// program.to[Task].asyncBoundary >> | |||
// Task(println(Thread.currentThread().getName())) | |||
// .executeOn(Scheduler.global) | |||
// Task.parZip3( | |||
// program.to[Task].executeOn(defaultScheduler), | |||
// // backendResource, | |||
// // dummyRequester.send(), | |||
// // akkaResource, | |||
// Task(println(Thread.currentThread().getName())) | |||
// .executeOn(schedulers.cpu) | |||
// ) | |||
// } | |||
// .runToFuture | |||
// .onComplete(res => | |||
// res match { | |||
// case Failure(exception) => { | |||
// println("Application start failed. Reason -") | |||
// exception.printStackTrace() | |||
// } | |||
// case Success(value) => println("Application started Successfully") | |||
// } | |||
// ) | |||
// new TaskApp { | |||
// override protected def scheduler: Scheduler = | |||
// JFXExecutionContexts.javaFxScheduler | |||
// override def run(args: List[String]): Task[ExitCode] = | |||
// Task.suspend { | |||
// Task { | |||
// AsyncHttpClientMonixBackend().flatMap(implicit backend => { | |||
// val req = RequestPayload("").asJson | |||
// basicRequest.get(uri"").body(req).send() | |||
// }) | |||
// } >> | |||
// program.to[Task].executeOn(JFXExecutionContexts.javaFxScheduler) >> | |||
// Task(println(Thread.currentThread().getName())) | |||
// .executeOn(Scheduler.global) >> | |||
// // Task.unit.asyncBoundary >> | |||
// Task.pure(ExitCode.Success) | |||
// } | |||
// } | |||
// } | |||
// Task.sleep(3.seconds).flatMap { _ => | |||
// | |||
// (for { | |||
// req <- | |||
// basicRequest | |||
// .get(uri"https://httpbin.org/get") | |||
// .response(asJson[HttpBinResponse]) | |||
// .send() | |||
// } yield println(req)) >> Task( | |||
// println(Thread.currentThread().getName()) | |||
// ) >> | |||
// backend.close() | |||
// } | |||
// | |||
def test(stage: Stage) = { | |||
stage.scene().setRoot(new FlowPane()) | |||
} | |||
def makePrimaryStage( | |||
backend: AppTypes.HttpBackend, | |||
actorSystem: classic.ActorSystem | |||
) = { | |||
new PrimaryStage { | |||
scene = new DefaultUI().scene | |||
onCloseRequest = () => { | |||
val f2 = actorSystem.terminate() | |||
val f1 = backend.close().runToFuture | |||
println("Closing backend") | |||
Await.result(f1, 3.seconds) | |||
println("Closing actor system") | |||
println(Thread.currentThread().getName()) | |||
Await.result(f2, 3.seconds) | |||
println("Actor system closed") | |||
Platform.exit() | |||
System.exit(0) | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
package nova.monadic_sfx | |||
import monix.eval.Task | |||
import sttp.client.SttpBackend | |||
import monix.reactive.Observable | |||
import sttp.client.asynchttpclient.WebSocketHandler | |||
import java.nio.ByteBuffer | |||
trait AppTypes {} | |||
object AppTypes { | |||
type HttpBackend = | |||
SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler] | |||
} |
@ -0,0 +1,84 @@ | |||
package nova.monadic_sfx.executors | |||
import akka.dispatch.{ | |||
DispatcherPrerequisites, | |||
ExecutorServiceFactory, | |||
ExecutorServiceConfigurator | |||
} | |||
import com.typesafe.config.Config | |||
import java.util.concurrent.{ | |||
ExecutorService, | |||
AbstractExecutorService, | |||
ThreadFactory, | |||
TimeUnit | |||
} | |||
import java.util.Collections | |||
import javax.swing.SwingUtilities | |||
import javafx.application.Platform | |||
import monix.execution.Scheduler | |||
import scala.concurrent.ExecutionContext | |||
import java.util.concurrent.Executor | |||
// First we wrap invokeLater/runLater as an ExecutorService | |||
abstract class GUIExecutorService extends AbstractExecutorService { | |||
def execute(command: Runnable): Unit | |||
def shutdown(): Unit = () | |||
def shutdownNow() = Collections.emptyList[Runnable] | |||
def isShutdown = false | |||
def isTerminated = false | |||
def awaitTermination(l: Long, timeUnit: TimeUnit) = true | |||
} | |||
object JavaFXExecutorService extends GUIExecutorService { | |||
override def execute(command: Runnable) = Platform.runLater(command) | |||
} | |||
object SwingExecutorService extends GUIExecutorService { | |||
override def execute(command: Runnable) = SwingUtilities.invokeLater(command) | |||
} | |||
class JavaFXEventThreadExecutorServiceConfigurator( | |||
config: Config, | |||
prerequisites: DispatcherPrerequisites | |||
) extends ExecutorServiceConfigurator(config, prerequisites) { | |||
private val f = new ExecutorServiceFactory { | |||
def createExecutorService: ExecutorService = JavaFXExecutorService | |||
} | |||
def createExecutorServiceFactory( | |||
id: String, | |||
threadFactory: ThreadFactory | |||
): ExecutorServiceFactory = f | |||
} | |||
// Then we create an ExecutorServiceConfigurator so that Akka can use our SwingExecutorService for the dispatchers | |||
class SwingEventThreadExecutorServiceConfigurator( | |||
config: Config, | |||
prerequisites: DispatcherPrerequisites | |||
) extends ExecutorServiceConfigurator(config, prerequisites) { | |||
private val f = new ExecutorServiceFactory { | |||
def createExecutorService: ExecutorService = SwingExecutorService | |||
} | |||
def createExecutorServiceFactory( | |||
id: String, | |||
threadFactory: ThreadFactory | |||
): ExecutorServiceFactory = f | |||
} | |||
object JFXExecutionContexts { | |||
val javaFxExecutionContext: ExecutionContext = | |||
ExecutionContext.fromExecutor(new Executor { | |||
def execute(command: Runnable): Unit = { | |||
Platform.runLater(command) | |||
} | |||
}) | |||
val fxScheduler = | |||
Scheduler(javaFxExecutionContext) | |||
} |
@ -0,0 +1,9 @@ | |||
package nova.monadic_sfx.executors | |||
import monix.execution.Scheduler | |||
class Schedulers( | |||
val blockingIO: Scheduler = Scheduler.io(), | |||
val cpu: Scheduler = Scheduler.global, | |||
val fx: Scheduler = JFXExecutionContexts.fxScheduler | |||
) |
@ -0,0 +1,10 @@ | |||
package nova.monadic_sfx.http | |||
import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend | |||
import nova.monadic_sfx.AppTypes | |||
object Backend { | |||
val backend = AsyncHttpClientMonixBackend | |||
.resource() | |||
def apply() = { backend } | |||
} |
@ -0,0 +1,27 @@ | |||
package nova.monadic_sfx.http.requests | |||
import nova.monadic_sfx.AppTypes | |||
import nova.monadic_sfx.AppTypes.HttpBackend | |||
import monix.eval.Task | |||
import sttp.client._ | |||
import sttp.client.circe._ | |||
import io.circe.generic.auto._ | |||
import nova.monadic_sfx.models._ | |||
class DummyRequest(backend: HttpBackend) extends AppTypes { | |||
private implicit val _backend = backend | |||
def send() = { | |||
Task | |||
.suspend( | |||
(for { | |||
req <- | |||
basicRequest | |||
.get(uri"https://httpbin.org/get") | |||
.response(asJson[HttpBinResponse]) | |||
.send() | |||
} yield println(req)) >> | |||
Task(println(Thread.currentThread().getName())) | |||
) | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
package nova.monadic_sfx.models | |||
case class RequestPayload(data: String) | |||
final case class HttpBinResponse( | |||
url: String, | |||
origin: String, | |||
headers: Map[String, String] | |||
) |
@ -0,0 +1,79 @@ | |||
package nova.monadic_sfx.modules | |||
import nova.monadic_sfx.executors.Schedulers | |||
import monix.execution.Scheduler | |||
import nova.monadic_sfx.http.requests.DummyRequest | |||
import monix.eval.Task | |||
import akka.{actor => classic} | |||
import cats.effect.Resource | |||
import nova.monadic_sfx.AppTypes | |||
trait MainModule { | |||
import com.softwaremill.macwire._ | |||
// val schedulers: Schedulers = new Schedulers() | |||
// implicit val defaultScheduler: Scheduler = schedulers.fx | |||
// val program = | |||
// for { | |||
// backend <- Backend() | |||
// fxActorSystem <- | |||
// Resource | |||
// .make(Task { | |||
// classic.ActorSystem( | |||
// name = "FXActorSystem" | |||
// ) | |||
// })(sys => Task(sys.terminate())) | |||
// } yield { | |||
// val dummyRequester = wire[DummyRequest] | |||
// dummyRequester.send() | |||
// } | |||
// val program = Backend().use(backend => | |||
// Resource | |||
// .make(Task { | |||
// classic.ActorSystem( | |||
// name = "FXActorSystem" | |||
// ) | |||
// })(sys => Task(sys.terminate())) | |||
// .use { implicit system => | |||
// // system.spa | |||
// // system.typed | |||
// // val javaFxActor = system.actorOf( | |||
// // Props[JavaFxActor]().withDispatcher("javafx-dispatcher"), | |||
// // "javaFxActor" | |||
// // ) | |||
// // val swingActor = system.actorOf( | |||
// // Props[SwingActor]().withDispatcher("swing-dispatcher"), | |||
// // "swingActor" | |||
// // ) | |||
// Task(new DummyRequest(backend)) >> | |||
// Task.unit | |||
// } | |||
// ) | |||
def schedulers: Schedulers | |||
def defaultScheduler: Scheduler | |||
def backendTask: Task[AppTypes.HttpBackend] | |||
def actorSystemTask: Task[classic.ActorSystem] | |||
def deps = | |||
for { | |||
backend <- backendTask | |||
actorSystem <- actorSystemTask | |||
dummyRequesterTask <- Task { | |||
wireDeps(backend, actorSystem) | |||
} | |||
} yield dummyRequesterTask | |||
def wireDeps( | |||
backend: AppTypes.HttpBackend, | |||
system: classic.ActorSystem | |||
): DummyRequest = { | |||
wire[DummyRequest] | |||
// new DummyRequest(backend) | |||
} | |||
} |
@ -0,0 +1,39 @@ | |||
package nova.monadic_sfx.pages | |||
import nova.monadic_sfx.AppTypes | |||
import scalafx.scene.control.TextField | |||
import scalafx.scene.control._ | |||
import scalafx.scene.layout.VBox | |||
import scalafx.scene.Node | |||
import scalafx.Includes._ | |||
import scalafx.scene.layout.HBox | |||
import scalafx.scene.text.Text | |||
import scalafx.scene.Parent | |||
import scalafx.application.JFXApp.PrimaryStage | |||
class HomePage( | |||
backend: AppTypes.HttpBackend, | |||
system: akka.actor.ActorSystem, | |||
onLogout: () => Unit | |||
) { | |||
private lazy val root = new HBox { | |||
children = List( | |||
new Text { | |||
text = "hello" | |||
}, | |||
new Button { | |||
text = "logout" | |||
onAction = () => onLogout() | |||
} | |||
) | |||
} | |||
def render = root | |||
} | |||
object HomePage { | |||
def apply( | |||
backend: AppTypes.HttpBackend, | |||
system: akka.actor.ActorSystem, | |||
onLogout: () => Unit | |||
): Parent = | |||
new HomePage(backend, system, onLogout).render | |||
} |
@ -0,0 +1,57 @@ | |||
package nova.monadic_sfx.pages | |||
import nova.monadic_sfx.AppTypes | |||
import scalafx.scene.control.TextField | |||
import scalafx.scene.control._ | |||
import scalafx.scene.layout.VBox | |||
import scalafx.scene.Node | |||
import scalafx.Includes._ | |||
import scalafx.scene.Parent | |||
import scalafx.application.JFXApp.PrimaryStage | |||
// import io.odin.syntax._ | |||
// import _root_.monix.eval.Task | |||
// import io.odin.monix._ | |||
// import javafx.beans.property.ObjectProperty | |||
// import javafx.event.{ActionEvent, EventHandler} | |||
class LoginPage( | |||
appStage: PrimaryStage, | |||
backend: AppTypes.HttpBackend, | |||
system: akka.actor.ActorSystem | |||
) { | |||
//pure function callbacks, but with side effects still | |||
private def onLogout(stage: PrimaryStage) = { | |||
println("logging out") | |||
stage.scene().setRoot(render) | |||
} | |||
private def onLogin(stage: PrimaryStage) = { | |||
println("logging in") | |||
stage | |||
.scene() | |||
.setRoot(HomePage(backend, system, () => onLogout(appStage))) | |||
} | |||
private lazy val root = new VBox { | |||
children = Seq( | |||
new TextField { | |||
text = "username" | |||
editable = true | |||
}, | |||
new TextField { | |||
text = "password" | |||
}, | |||
new Button { | |||
text = "Login" | |||
onAction = () => onLogin(appStage) | |||
} | |||
) | |||
} | |||
def render: Parent = root | |||
} | |||
object LoginPage { | |||
def apply( | |||
appStage: PrimaryStage, | |||
backend: AppTypes.HttpBackend, | |||
system: akka.actor.ActorSystem | |||
) = new LoginPage(appStage, backend, system).render | |||
} |
@ -0,0 +1,55 @@ | |||
package nova.monadic_sfx.ui | |||
import scalafx.geometry.Insets | |||
import scalafx.scene.Scene | |||
import scalafx.scene.effect.DropShadow | |||
import scalafx.scene.layout.HBox | |||
import scalafx.scene.paint.Color._ | |||
import scalafx.scene.paint._ | |||
import scalafx.scene.text.Text | |||
import monix.eval.Coeval | |||
class DefaultUI { | |||
val scene = | |||
new Scene { | |||
fill = Color.rgb(38, 38, 38) | |||
content = new HBox { | |||
padding = Insets(50, 80, 50, 80) | |||
children = Seq( | |||
new Text { | |||
text = "Scala" | |||
style = "-fx-font: normal bold 100pt sans-serif" | |||
fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed)) | |||
}, | |||
new Text { | |||
text = "FX" | |||
style = "-fx-font: italic bold 100pt sans-serif" | |||
fill = new LinearGradient( | |||
endX = 0, | |||
stops = Stops(White, DarkGray) | |||
) | |||
effect = new DropShadow { | |||
color = DarkGray | |||
radius = 15 | |||
spread = 0.25 | |||
} | |||
} | |||
) | |||
} | |||
} | |||
// val program = Coeval | |||
// .suspend { | |||
// Coeval(println("hello")) >> | |||
// Coeval(println(Thread.currentThread().getName())) >> | |||
// Coeval { | |||
// stage = new PrimaryStage { | |||
// // initStyle(StageStyle.Unified) | |||
// title = "ScalaFX Hello World" | |||
// scene = scn | |||
// } | |||
// } | |||
// } | |||
} |
@ -0,0 +1,36 @@ | |||
package org.slf4j.impl | |||
import cats.effect.{ContextShift, Clock, Effect, IO, Timer} | |||
import io.odin._ | |||
import io.odin.slf4j.OdinLoggerBinder | |||
import scala.concurrent.ExecutionContext | |||
import _root_.monix.execution.Scheduler | |||
//effect type should be specified inbefore | |||
//log line will be recorded right after the call with no suspension | |||
class StaticLoggerBinder extends OdinLoggerBinder[IO] { | |||
val ec: ExecutionContext = Scheduler.global | |||
implicit val timer: Timer[IO] = IO.timer(ec) | |||
implicit val clock: Clock[IO] = timer.clock | |||
implicit val cs: ContextShift[IO] = IO.contextShift(ec) | |||
implicit val F: Effect[IO] = IO.ioEffect | |||
val loggers: PartialFunction[String, Logger[IO]] = { | |||
case "some.external.package.SpecificClass" => | |||
consoleLogger[IO](minLevel = Level.Warn) //disable noisy external logs | |||
case "org.asynchttpclient.netty.channel.DefaultChannelPool" => | |||
consoleLogger[IO](minLevel = Level.Warn) | |||
case _ => //if wildcard case isn't provided, default logger is no-op | |||
consoleLogger[IO]() | |||
} | |||
} | |||
object StaticLoggerBinder extends StaticLoggerBinder { | |||
var REQUESTED_API_VERSION: String = "1.7" | |||
def getSingleton: StaticLoggerBinder = this | |||
} |