Rohan Sircar
4 years ago
19 changed files with 759 additions and 0 deletions
-
24.gitignore
-
1.scalafmt.conf
-
24UNLICENSE
-
61build.sbt
-
2project/build.properties
-
6project/plugin.sbt
-
5src/main/resources/application.conf
-
219src/main/scala/nova/monadic_sfx/SFXActors.scala
-
13src/main/scala/nova/monadic_sfx/Types.scala
-
84src/main/scala/nova/monadic_sfx/executors/GUIExecutor.scala
-
9src/main/scala/nova/monadic_sfx/executors/Schedulers.scala
-
10src/main/scala/nova/monadic_sfx/http/Backend.scala
-
27src/main/scala/nova/monadic_sfx/http/requests/DummyRequest.scala
-
8src/main/scala/nova/monadic_sfx/models/DummyModels.scala
-
79src/main/scala/nova/monadic_sfx/modules/MainModule.scala
-
39src/main/scala/nova/monadic_sfx/pages/HomePage.scala
-
57src/main/scala/nova/monadic_sfx/pages/LoginPage.scala
-
55src/main/scala/nova/monadic_sfx/ui/DefaultUI.scala
-
36src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
@ -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 |
|||
|
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue