Used MutHistory to implement forward/backward

router buttons
This commit is contained in:
Rohan Sircar 2020-12-23 14:03:10 +05:30
parent d18c884168
commit 5b50f161d2
2 changed files with 91 additions and 29 deletions

View File

@ -4,6 +4,7 @@ import java.util.concurrent.TimeUnit
import scala.util.Random import scala.util.Random
import com.jfoenix.controls.JFXDialog
import com.softwaremill.macwire._ import com.softwaremill.macwire._
import io.odin.Logger import io.odin.Logger
import monix.bio.IO import monix.bio.IO
@ -11,14 +12,16 @@ import monix.bio.Task
import monix.eval.Coeval import monix.eval.Coeval
import monix.{eval => me} import monix.{eval => me}
import nova.monadic_sfx.executors.Schedulers import nova.monadic_sfx.executors.Schedulers
import nova.monadic_sfx.util.controls.JFXButton
import nova.monadic_sfx.implicits._ import nova.monadic_sfx.implicits._
import nova.monadic_sfx.ui.MyFxApp import nova.monadic_sfx.ui.MyFxApp
import nova.monadic_sfx.ui.components.router.FXRouter import nova.monadic_sfx.ui.components.router.FXRouter
import nova.monadic_sfx.ui.components.router.Page import nova.monadic_sfx.ui.components.router.Page
import nova.monadic_sfx.ui.components.todo.TodoListStore import nova.monadic_sfx.ui.components.todo.TodoListStore
import nova.monadic_sfx.ui.components.todo.TodoListView 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.gerweck.scalafx.util._
import org.kordamp.bootstrapfx.BootstrapFX
import scalafx.Includes._ import scalafx.Includes._
import scalafx.application.JFXApp.PrimaryStage import scalafx.application.JFXApp.PrimaryStage
import scalafx.beans.property.ObjectProperty import scalafx.beans.property.ObjectProperty
@ -32,9 +35,9 @@ import scalafx.scene.control.Label
import scalafx.scene.control.TableColumn import scalafx.scene.control.TableColumn
import scalafx.scene.control.TableView import scalafx.scene.control.TableView
import scalafx.scene.layout.BorderPane import scalafx.scene.layout.BorderPane
import scalafx.scene.layout.FlowPane
import scalafx.scene.layout.HBox import scalafx.scene.layout.HBox
import scalafx.scene.layout.Priority import scalafx.scene.layout.Priority
import scalafx.scene.layout.StackPane
class MainApp( class MainApp(
// spawnProtocol: ActorSystem[SpawnProtocol.Command], // spawnProtocol: ActorSystem[SpawnProtocol.Command],
@ -45,6 +48,12 @@ class MainApp(
private lazy val _scene = new Scene { private lazy val _scene = new Scene {
root = new HBox { root = new HBox {
padding = Insets(20) 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 scene = _scene
width = 640 width = 640
height = 480 height = 480
// resizable = false
} }
val program = for { val program = for {
@ -61,10 +71,12 @@ class MainApp(
wire[MainAppDelegate].init wire[MainAppDelegate].init
.flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode)) .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode))
.executeOn(schedulers.fx) .executeOn(schedulers.fx)
_ <- Task(stage.resizable = false).executeOn(schedulers.fx)
currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS) currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS)
_ <- logger.info( _ <- logger.info(
s"Application started in ${(currentTime - startTime) / 1000f} seconds" s"Application started in ${(currentTime - startTime) / 1000f} seconds"
) )
// _ <- Task(CSSFX.start(stage))
_ <- fxAppFib.join _ <- fxAppFib.join
} yield () } yield ()
@ -81,30 +93,39 @@ class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) {
val init = val init =
for { for {
//FXRouter does not allocate mutable state so it's ok to use pure here //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) routerStore <- router.store(Page.Home, logger)
todoStore <- TodoListStore(logger) todoStore <- TodoListStore(logger)
todoComponent <- TodoListView(todoStore) todoComponent <- TodoListView(todoStore)
resolver: PartialFunction[Page, Task[Parent]] = { resolver: PartialFunction[Page, Task[Parent]] = {
case Page.Home => case Page.Home =>
Task(new Label { Task(new Label {
styleClass ++= Seq("text-white")
text = "HomePage" text = "HomePage"
}) })
case Page.UserHome(id0) => case Page.UserHome(id0) =>
Task(new Label { Task(new Label {
styleClass ++= Seq("text-white")
text = s"User Home, Id = $id0" text = s"User Home, Id = $id0"
}) })
case Page.Todo => case Page.Todo =>
Task(todoComponent) Task.pure(todoComponent)
} }
routerNode <- routerNode <-
Task Task
.deferAction(implicit s => .deferAction(implicit s =>
Task(new HBox { Task(new HBox {
alignment = Pos.Center
//TODO find a better way to do this //TODO find a better way to do this
var oldValue: Option[Parent] = None var oldValue: Option[Parent] = None
children <-- router children <-- router
.render(resolver)(routerStore) .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 // call cancel on the old component to cancel all subscriptions
.doOnNextF(newValue => .doOnNextF(newValue =>
Coeval { oldValue.foreach(_ => ()) } >> Coeval { Coeval { oldValue.foreach(_ => ()) } >> Coeval {
@ -116,14 +137,41 @@ class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) {
) )
mainSceneNode <- Task.deferAction(implicit s => mainSceneNode <- Task.deferAction(implicit s =>
Task(new BorderPane { Task(new StackPane { root =>
hgrow = Priority.Always
vgrow = Priority.Always
children = new BorderPane {
hgrow = Priority.Always hgrow = Priority.Always
vgrow = Priority.Always vgrow = Priority.Always
center = routerNode center = routerNode
bottom = new FlowPane { bottom = new HBox {
alignment = Pos.Center alignment = Pos.Center
hgap = 20 spacing = 20
children = Seq( 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 { new JFXButton {
text = "Todo" text = "Todo"
style = buttonStyle style = buttonStyle
@ -137,11 +185,25 @@ class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) {
style = buttonStyle style = buttonStyle
obsAction obsAction
.useLazyEval( .useLazyEval(
me.Task(FXRouter.Replace(Page.UserHome(Random.nextInt(20)))) me.Task(
FXRouter.Replace(Page.UserHome(Random.nextInt(20)))
)
) --> routerStore ) --> 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)
} }
) )
} }
}
}) })
) )
} yield mainSceneNode } yield mainSceneNode

View File

@ -5,10 +5,10 @@ import java.time.LocalDateTime
import io.circe.Encoder import io.circe.Encoder
import io.odin.Logger import io.odin.Logger
import monix.bio.Task import monix.bio.Task
import monix.reactive.OverflowStrategy
import monix.reactive.subjects.ConcurrentSubject
import monix.eval.Coeval import monix.eval.Coeval
import monix.reactive.Observable import monix.reactive.Observable
import monix.reactive.OverflowStrategy
import monix.reactive.subjects.ConcurrentSubject
object Store { object Store {
def createL[A, M]( def createL[A, M](