diff --git a/src/main/scala/nova/monadic_sfx/MainApp.scala b/src/main/scala/nova/monadic_sfx/MainApp.scala index ae8dd84..66d3521 100644 --- a/src/main/scala/nova/monadic_sfx/MainApp.scala +++ b/src/main/scala/nova/monadic_sfx/MainApp.scala @@ -4,6 +4,7 @@ import java.util.concurrent.TimeUnit import scala.util.Random +import com.jfoenix.controls.JFXDialog import com.softwaremill.macwire._ import io.odin.Logger import monix.bio.IO @@ -11,14 +12,16 @@ import monix.bio.Task import monix.eval.Coeval import monix.{eval => me} import nova.monadic_sfx.executors.Schedulers -import nova.monadic_sfx.util.controls.JFXButton import nova.monadic_sfx.implicits._ import nova.monadic_sfx.ui.MyFxApp import nova.monadic_sfx.ui.components.router.FXRouter import nova.monadic_sfx.ui.components.router.Page import nova.monadic_sfx.ui.components.todo.TodoListStore import nova.monadic_sfx.ui.components.todo.TodoListView +import nova.monadic_sfx.util.MutHistory +import nova.monadic_sfx.util.controls.JFXButton import org.gerweck.scalafx.util._ +import org.kordamp.bootstrapfx.BootstrapFX import scalafx.Includes._ import scalafx.application.JFXApp.PrimaryStage import scalafx.beans.property.ObjectProperty @@ -32,9 +35,9 @@ import scalafx.scene.control.Label import scalafx.scene.control.TableColumn import scalafx.scene.control.TableView import scalafx.scene.layout.BorderPane -import scalafx.scene.layout.FlowPane import scalafx.scene.layout.HBox import scalafx.scene.layout.Priority +import scalafx.scene.layout.StackPane class MainApp( // spawnProtocol: ActorSystem[SpawnProtocol.Command], @@ -45,6 +48,12 @@ class MainApp( private lazy val _scene = new Scene { root = new HBox { padding = Insets(20) + // style = """| -fx-background-color: rgb(38, 38, 38); + // | -fx-text-fill: white;""".stripMargin + stylesheets ++= Seq( + BootstrapFX.bootstrapFXStylesheet, + os.rel / "static" / "css" / "main.css" + ) } } @@ -53,6 +62,7 @@ class MainApp( scene = _scene width = 640 height = 480 + // resizable = false } val program = for { @@ -61,10 +71,12 @@ class MainApp( wire[MainAppDelegate].init .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode)) .executeOn(schedulers.fx) + _ <- Task(stage.resizable = false).executeOn(schedulers.fx) currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS) _ <- logger.info( s"Application started in ${(currentTime - startTime) / 1000f} seconds" ) + // _ <- Task(CSSFX.start(stage)) _ <- fxAppFib.join } yield () @@ -81,30 +93,39 @@ class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) { val init = for { //FXRouter does not allocate mutable state so it's ok to use pure here - router <- Task.pure(new FXRouter[Page]) + history <- Task(new MutHistory[Page](Page.Home)) + router <- Task.pure(new FXRouter[Page](history)) routerStore <- router.store(Page.Home, logger) todoStore <- TodoListStore(logger) todoComponent <- TodoListView(todoStore) resolver: PartialFunction[Page, Task[Parent]] = { case Page.Home => Task(new Label { + styleClass ++= Seq("text-white") text = "HomePage" }) case Page.UserHome(id0) => Task(new Label { + styleClass ++= Seq("text-white") text = s"User Home, Id = $id0" }) case Page.Todo => - Task(todoComponent) + Task.pure(todoComponent) } routerNode <- Task .deferAction(implicit s => Task(new HBox { + alignment = Pos.Center //TODO find a better way to do this var oldValue: Option[Parent] = None children <-- router .render(resolver)(routerStore) + // .scanEvalF[Coeval, (Option[Parent], Option[Parent])]( + // Coeval.pure(None -> None) + // ) { + // case (oldValue, newValue) => Coeval(None -> None) + // } // call cancel on the old component to cancel all subscriptions .doOnNextF(newValue => Coeval { oldValue.foreach(_ => ()) } >> Coeval { @@ -116,31 +137,72 @@ class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) { ) mainSceneNode <- Task.deferAction(implicit s => - Task(new BorderPane { + Task(new StackPane { root => hgrow = Priority.Always vgrow = Priority.Always - center = routerNode - bottom = new FlowPane { - alignment = Pos.Center - hgap = 20 - children = Seq( - new JFXButton { - text = "Todo" - style = buttonStyle - obsAction - .useLazyEval( - me.Task.pure(FXRouter.Replace(Page.Todo)) - ) --> routerStore - }, - new JFXButton { - text = "UserHome" - style = buttonStyle - obsAction - .useLazyEval( - me.Task(FXRouter.Replace(Page.UserHome(Random.nextInt(20)))) - ) --> routerStore - } - ) + children = new BorderPane { + hgrow = Priority.Always + vgrow = Priority.Always + center = routerNode + bottom = new HBox { + alignment = Pos.Center + spacing = 20 + children = Seq( + new JFXButton { + text = "Forward" + style = buttonStyle + onAction = () => { + history.forward() + routerStore.onNext(FXRouter.HistoryEvent(history.current)) + } + }, + new JFXButton { + text = "Backward" + style = buttonStyle + onAction = () => { + history.backward() + routerStore.onNext(FXRouter.HistoryEvent(history.current)) + } + }, + new JFXButton { + text = "Home" + style = buttonStyle + obsAction + .useLazyEval( + me.Task.pure(FXRouter.Replace(Page.Home)) + ) --> routerStore + }, + new JFXButton { + text = "Todo" + style = buttonStyle + obsAction + .useLazyEval( + me.Task.pure(FXRouter.Replace(Page.Todo)) + ) --> routerStore + }, + new JFXButton { + text = "UserHome" + style = buttonStyle + obsAction + .useLazyEval( + me.Task( + FXRouter.Replace(Page.UserHome(Random.nextInt(20))) + ) + ) --> routerStore + }, + new JFXButton { + text = "Dialog" + style = buttonStyle + val d = new JFXDialog() + d.setContent(new HBox { + children = Seq(new Label("hmm")) + padding = Insets(20) + }) + d.styleClass ++= Seq("text-white") + onAction = () => d.show(root) + } + ) + } } }) ) diff --git a/src/main/scala/nova/monadic_sfx/util/reactive/store/Store.scala b/src/main/scala/nova/monadic_sfx/util/reactive/store/Store.scala index ba592ed..dbcc39d 100644 --- a/src/main/scala/nova/monadic_sfx/util/reactive/store/Store.scala +++ b/src/main/scala/nova/monadic_sfx/util/reactive/store/Store.scala @@ -5,10 +5,10 @@ import java.time.LocalDateTime import io.circe.Encoder import io.odin.Logger import monix.bio.Task -import monix.reactive.OverflowStrategy -import monix.reactive.subjects.ConcurrentSubject import monix.eval.Coeval import monix.reactive.Observable +import monix.reactive.OverflowStrategy +import monix.reactive.subjects.ConcurrentSubject object Store { def createL[A, M](