You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
5.1 KiB

  1. package nova.monadic_sfx
  2. import java.util.concurrent.TimeUnit
  3. import scala.util.Random
  4. import com.softwaremill.macwire._
  5. import io.odin.Logger
  6. import monix.bio.IO
  7. import monix.bio.Task
  8. import monix.eval.Coeval
  9. import monix.{eval => me}
  10. import nova.monadic_sfx.executors.Schedulers
  11. import nova.monadic_sfx.util.controls.JFXButton
  12. import nova.monadic_sfx.implicits._
  13. import nova.monadic_sfx.ui.MyFxApp
  14. import nova.monadic_sfx.ui.components.router.FXRouter
  15. import nova.monadic_sfx.ui.components.router.Page
  16. import nova.monadic_sfx.ui.components.todo.TodoListStore
  17. import nova.monadic_sfx.ui.components.todo.TodoListView
  18. import org.gerweck.scalafx.util._
  19. import scalafx.Includes._
  20. import scalafx.application.JFXApp.PrimaryStage
  21. import scalafx.beans.property.ObjectProperty
  22. import scalafx.beans.property.StringProperty
  23. import scalafx.collections.ObservableBuffer
  24. import scalafx.geometry.Insets
  25. import scalafx.geometry.Pos
  26. import scalafx.scene.Parent
  27. import scalafx.scene.Scene
  28. import scalafx.scene.control.Label
  29. import scalafx.scene.control.TableColumn
  30. import scalafx.scene.control.TableView
  31. import scalafx.scene.layout.BorderPane
  32. import scalafx.scene.layout.FlowPane
  33. import scalafx.scene.layout.HBox
  34. import scalafx.scene.layout.Priority
  35. class MainApp(
  36. // spawnProtocol: ActorSystem[SpawnProtocol.Command],
  37. schedulers: Schedulers,
  38. startTime: Long
  39. )(implicit logger: Logger[Task]) {
  40. private lazy val _scene = new Scene {
  41. root = new HBox {
  42. padding = Insets(20)
  43. }
  44. }
  45. private lazy val stage = new PrimaryStage {
  46. title = "Simple ScalaFX App"
  47. scene = _scene
  48. width = 640
  49. height = 480
  50. }
  51. val program = for {
  52. (fxApp, fxAppFib) <- wire[MyFxApp].init(stage)
  53. _ <-
  54. wire[MainAppDelegate].init
  55. .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode))
  56. .executeOn(schedulers.fx)
  57. currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS)
  58. _ <- logger.info(
  59. s"Application started in ${(currentTime - startTime) / 1000f} seconds"
  60. )
  61. _ <- fxAppFib.join
  62. } yield ()
  63. }
  64. class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) {
  65. val buttonStyle = """| -fx-padding: 0.7em 0.57em;
  66. | -fx-font-size: 14px;
  67. | -jfx-button-type: RAISED;
  68. | -fx-background-color: rgb(77,102,204);
  69. | -fx-pref-width: 200;
  70. | -fx-text-fill: WHITE; """.stripMargin
  71. val init =
  72. for {
  73. //FXRouter does not allocate mutable state so it's ok to use pure here
  74. router <- Task.pure(new FXRouter[Page])
  75. routerStore <- router.store(Page.Home, logger)
  76. todoStore <- TodoListStore(logger)
  77. todoComponent <- TodoListView(todoStore)
  78. resolver: PartialFunction[Page, Task[Parent]] = {
  79. case Page.Home =>
  80. Task(new Label {
  81. text = "HomePage"
  82. })
  83. case Page.UserHome(id0) =>
  84. Task(new Label {
  85. text = s"User Home, Id = $id0"
  86. })
  87. case Page.Todo =>
  88. Task(todoComponent)
  89. }
  90. routerNode <-
  91. Task
  92. .deferAction(implicit s =>
  93. Task(new HBox {
  94. //TODO find a better way to do this
  95. var oldValue: Option[Parent] = None
  96. children <-- router
  97. .render(resolver)(routerStore)
  98. // call cancel on the old component to cancel all subscriptions
  99. .doOnNextF(newValue =>
  100. Coeval { oldValue.foreach(_ => ()) } >> Coeval {
  101. oldValue = Some(newValue)
  102. }
  103. )
  104. .map(_.delegate)
  105. })
  106. )
  107. mainSceneNode <- Task.deferAction(implicit s =>
  108. Task(new BorderPane {
  109. hgrow = Priority.Always
  110. vgrow = Priority.Always
  111. center = routerNode
  112. bottom = new FlowPane {
  113. alignment = Pos.Center
  114. hgap = 20
  115. children = Seq(
  116. new JFXButton {
  117. text = "Todo"
  118. style = buttonStyle
  119. obsAction
  120. .useLazyEval(
  121. me.Task.pure(FXRouter.Replace(Page.Todo))
  122. ) --> routerStore
  123. },
  124. new JFXButton {
  125. text = "UserHome"
  126. style = buttonStyle
  127. obsAction
  128. .useLazyEval(
  129. me.Task(FXRouter.Replace(Page.UserHome(Random.nextInt(20))))
  130. ) --> routerStore
  131. }
  132. )
  133. }
  134. })
  135. )
  136. } yield mainSceneNode
  137. }
  138. class TestModel(_name: String, _age: Int) {
  139. val name = StringProperty(_name).readOnly
  140. val age = ObjectProperty(_age).readOnly
  141. }
  142. object Test {
  143. val items = ObservableBuffer(
  144. new TestModel("hmm", 1),
  145. new TestModel("hmm2", 2)
  146. )
  147. val ttv = new TableView[TestModel](items) {
  148. columns ++= Seq(
  149. new TableColumn[TestModel, String] {
  150. text = "Name"
  151. cellValueFactory = { _.value.name }
  152. },
  153. new TableColumn[TestModel, Int] {
  154. text = "Age"
  155. cellValueFactory = { _.value.age }
  156. }
  157. )
  158. }
  159. }