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.

235 lines
7.5 KiB

  1. package nova.monadic_sfx
  2. import java.util.concurrent.TimeUnit
  3. import scala.util.Random
  4. import com.jfoenix.controls.JFXDialog
  5. import com.softwaremill.macwire._
  6. import io.odin.Logger
  7. import monix.bio.IO
  8. import monix.bio.Task
  9. import monix.eval.Coeval
  10. import monix.{eval => me}
  11. import nova.monadic_sfx.executors.Schedulers
  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 nova.monadic_sfx.util.MutHistory
  19. import nova.monadic_sfx.util.controls.JFXButton
  20. import org.gerweck.scalafx.util._
  21. import org.kordamp.bootstrapfx.BootstrapFX
  22. import scalafx.Includes._
  23. import scalafx.application.JFXApp.PrimaryStage
  24. import scalafx.beans.property.ObjectProperty
  25. import scalafx.beans.property.StringProperty
  26. import scalafx.collections.ObservableBuffer
  27. import scalafx.geometry.Insets
  28. import scalafx.geometry.Pos
  29. import scalafx.scene.Parent
  30. import scalafx.scene.Scene
  31. import scalafx.scene.control.Label
  32. import scalafx.scene.control.TableColumn
  33. import scalafx.scene.control.TableView
  34. import scalafx.scene.layout.BorderPane
  35. import scalafx.scene.layout.HBox
  36. import scalafx.scene.layout.Priority
  37. import scalafx.scene.layout.StackPane
  38. class MainApp(
  39. // spawnProtocol: ActorSystem[SpawnProtocol.Command],
  40. schedulers: Schedulers,
  41. startTime: Long
  42. )(implicit logger: Logger[Task]) {
  43. private lazy val _scene = new Scene {
  44. root = new HBox {
  45. padding = Insets(20)
  46. // style = """| -fx-background-color: rgb(38, 38, 38);
  47. // | -fx-text-fill: white;""".stripMargin
  48. stylesheets ++= Seq(
  49. BootstrapFX.bootstrapFXStylesheet,
  50. os.rel / "static" / "css" / "main.css"
  51. )
  52. }
  53. }
  54. private lazy val stage = new PrimaryStage {
  55. title = "Simple ScalaFX App"
  56. scene = _scene
  57. width = 640
  58. height = 480
  59. // resizable = false
  60. }
  61. val program = for {
  62. (fxApp, fxAppFib) <- wire[MyFxApp].init(stage)
  63. _ <-
  64. wire[MainAppDelegate].init
  65. .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode))
  66. .executeOn(schedulers.fx)
  67. _ <- Task(stage.resizable = false).executeOn(schedulers.fx)
  68. currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS)
  69. _ <- logger.info(
  70. s"Application started in ${(currentTime - startTime) / 1000f} seconds"
  71. )
  72. // _ <- Task(CSSFX.start(stage))
  73. _ <- fxAppFib.join
  74. } yield ()
  75. }
  76. class MainAppDelegate(schedulers: Schedulers)(implicit logger: Logger[Task]) {
  77. val buttonStyle = """| -fx-padding: 0.7em 0.57em;
  78. | -fx-font-size: 14px;
  79. | -jfx-button-type: RAISED;
  80. | -fx-background-color: rgb(77,102,204);
  81. | -fx-pref-width: 200;
  82. | -fx-text-fill: WHITE; """.stripMargin
  83. val init =
  84. for {
  85. //FXRouter does not allocate mutable state so it's ok to use pure here
  86. history <- Task(new MutHistory[Page](Page.Home))
  87. router <- Task.pure(new FXRouter[Page](history))
  88. routerStore <- router.store(Page.Home, logger)
  89. todoStore <- TodoListStore(logger)
  90. todoComponent <- TodoListView(todoStore)
  91. resolver: PartialFunction[Page, Task[Parent]] = {
  92. case Page.Home =>
  93. Task(new Label {
  94. styleClass ++= Seq("text-white")
  95. text = "HomePage"
  96. })
  97. case Page.UserHome(id0) =>
  98. Task(new Label {
  99. styleClass ++= Seq("text-white")
  100. text = s"User Home, Id = $id0"
  101. })
  102. case Page.Todo =>
  103. Task.pure(todoComponent)
  104. }
  105. routerNode <-
  106. Task
  107. .deferAction(implicit s =>
  108. Task(new HBox {
  109. alignment = Pos.Center
  110. //TODO find a better way to do this
  111. var oldValue: Option[Parent] = None
  112. children <-- router
  113. .render(resolver)(routerStore)
  114. // .scanEvalF[Coeval, (Option[Parent], Option[Parent])](
  115. // Coeval.pure(None -> None)
  116. // ) {
  117. // case (oldValue, newValue) => Coeval(None -> None)
  118. // }
  119. // call cancel on the old component to cancel all subscriptions
  120. .doOnNextF(newValue =>
  121. Coeval { oldValue.foreach(_ => ()) } >> Coeval {
  122. oldValue = Some(newValue)
  123. }
  124. )
  125. .map(_.delegate)
  126. })
  127. )
  128. mainSceneNode <- Task.deferAction(implicit s =>
  129. Task(new StackPane { root =>
  130. hgrow = Priority.Always
  131. vgrow = Priority.Always
  132. children = new BorderPane {
  133. hgrow = Priority.Always
  134. vgrow = Priority.Always
  135. center = routerNode
  136. bottom = new HBox {
  137. alignment = Pos.Center
  138. spacing = 20
  139. children = Seq(
  140. new JFXButton {
  141. text = "Forward"
  142. style = buttonStyle
  143. onAction = () => {
  144. history.forward()
  145. routerStore.onNext(FXRouter.HistoryEvent(history.current))
  146. }
  147. },
  148. new JFXButton {
  149. text = "Backward"
  150. style = buttonStyle
  151. onAction = () => {
  152. history.backward()
  153. routerStore.onNext(FXRouter.HistoryEvent(history.current))
  154. }
  155. },
  156. new JFXButton {
  157. text = "Home"
  158. style = buttonStyle
  159. obsAction
  160. .useLazyEval(
  161. me.Task.pure(FXRouter.Replace(Page.Home))
  162. ) --> routerStore
  163. },
  164. new JFXButton {
  165. text = "Todo"
  166. style = buttonStyle
  167. obsAction
  168. .useLazyEval(
  169. me.Task.pure(FXRouter.Replace(Page.Todo))
  170. ) --> routerStore
  171. },
  172. new JFXButton {
  173. text = "UserHome"
  174. style = buttonStyle
  175. obsAction
  176. .useLazyEval(
  177. me.Task(
  178. FXRouter.Replace(Page.UserHome(Random.nextInt(20)))
  179. )
  180. ) --> routerStore
  181. },
  182. new JFXButton {
  183. text = "Dialog"
  184. style = buttonStyle
  185. val d = new JFXDialog()
  186. d.setContent(new HBox {
  187. children = Seq(new Label("hmm"))
  188. padding = Insets(20)
  189. })
  190. d.styleClass ++= Seq("text-white")
  191. onAction = () => d.show(root)
  192. }
  193. )
  194. }
  195. }
  196. })
  197. )
  198. } yield mainSceneNode
  199. }
  200. class TestModel(_name: String, _age: Int) {
  201. val name = StringProperty(_name).readOnly
  202. val age = ObjectProperty(_age).readOnly
  203. }
  204. object Test {
  205. val items = ObservableBuffer(
  206. new TestModel("hmm", 1),
  207. new TestModel("hmm2", 2)
  208. )
  209. val ttv = new TableView[TestModel](items) {
  210. columns ++= Seq(
  211. new TableColumn[TestModel, String] {
  212. text = "Name"
  213. cellValueFactory = { _.value.name }
  214. },
  215. new TableColumn[TestModel, Int] {
  216. text = "Age"
  217. cellValueFactory = { _.value.age }
  218. }
  219. )
  220. }
  221. }