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.

361 lines
12 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package nova.monadic_sfx
  2. import java.util.concurrent.TimeUnit
  3. import scala.util.Random
  4. import cats.effect.Resource
  5. import cats.syntax.eq._
  6. import cats.syntax.option._
  7. import com.softwaremill.macwire._
  8. import io.odin.Logger
  9. import monix.bio.IO
  10. import monix.bio.Task
  11. import monix.eval.Coeval
  12. import monix.execution.cancelables.CompositeCancelable
  13. import monix.{eval => me}
  14. import nova.monadic_sfx.executors.Schedulers
  15. import nova.monadic_sfx.implicits._
  16. import nova.monadic_sfx.ui.MyFxApp
  17. import nova.monadic_sfx.ui.components.router.FXRouter
  18. import nova.monadic_sfx.ui.components.router.Page
  19. import nova.monadic_sfx.ui.components.todo.TodoListStore
  20. import nova.monadic_sfx.ui.components.todo.TodoListView
  21. import nova.monadic_sfx.util.MediaPlayerResource
  22. import nova.monadic_sfx.util.controls.JFXButton
  23. import nova.monadic_sfx.util.controls.JFXDialog
  24. import nova.monadic_sfx.util.controls.JFXTextField
  25. import nova.monadic_sfx.util.controls.VideoView
  26. import org.gerweck.scalafx.util._
  27. import org.kordamp.bootstrapfx.BootstrapFX
  28. import scalafx.Includes._
  29. import scalafx.application.JFXApp3.PrimaryStage
  30. import scalafx.beans.property.ObjectProperty
  31. import scalafx.beans.property.StringProperty
  32. import scalafx.collections.ObservableBuffer
  33. import scalafx.geometry.Insets
  34. import scalafx.geometry.Pos
  35. import scalafx.scene.Node
  36. import scalafx.scene.Parent
  37. import scalafx.scene.Scene
  38. import scalafx.scene.control.Button
  39. import scalafx.scene.control.Label
  40. import scalafx.scene.control.TableColumn
  41. import scalafx.scene.control.TableView
  42. import scalafx.scene.control.Tooltip
  43. import scalafx.scene.layout.BorderPane
  44. import scalafx.scene.layout.HBox
  45. import scalafx.scene.layout.Priority
  46. import scalafx.scene.layout.StackPane
  47. import scalafx.util.Duration
  48. class MainApp(
  49. // spawnProtocol: ActorSystem[SpawnProtocol.Command],
  50. schedulers: Schedulers,
  51. startTime: Long,
  52. mediaPlayer: MediaPlayerResource
  53. )(implicit logger: Logger[Task]) {
  54. private lazy val _scene = new Scene {
  55. root = new HBox {
  56. padding = Insets(20)
  57. // style = """| -fx-background-color: rgb(38, 38, 38);
  58. // | -fx-text-fill: white;""".stripMargin
  59. stylesheets ++= Seq(
  60. BootstrapFX.bootstrapFXStylesheet,
  61. os.rel / "static" / "css" / "main.css"
  62. )
  63. }
  64. }
  65. private lazy val stage = new PrimaryStage {
  66. title = "Simple ScalaFX App"
  67. scene = _scene
  68. minWidth = 700
  69. minHeight = 520
  70. width = 1280
  71. height = 720
  72. // resizable = false
  73. }
  74. (for {
  75. (stopSignal, fxAppFib) <- MyFxApp.resource(schedulers, stage)
  76. i <- Resource.make(Task(1))(_ => Task.unit)
  77. } yield (stopSignal, fxAppFib, i)).use {
  78. case (a, b, c) => Task.unit
  79. }
  80. val program = MyFxApp
  81. .resource(schedulers, stage)
  82. .evalMap {
  83. case (stopSignal, fxAppFib) =>
  84. wire[MainAppDelegate].init
  85. .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode))
  86. .executeOn(schedulers.fx) >> Task.pure(stopSignal -> fxAppFib)
  87. }
  88. .use {
  89. case (stopSignal, fxAppFib) =>
  90. for {
  91. // _ <- Task(stage.resizable = false).executeOn(schedulers.fx)
  92. currentTime <- IO.clock.realTime(TimeUnit.MILLISECONDS)
  93. _ <- logger.info(
  94. s"Application started in ${(currentTime - startTime) / 1000f} seconds"
  95. )
  96. // _ <- Task(CSSFX.start(stage))
  97. _ <- fxAppFib.join
  98. } yield ()
  99. }
  100. }
  101. class MainAppDelegate(
  102. schedulers: Schedulers,
  103. mediaPlayer: MediaPlayerResource,
  104. _scene: Scene
  105. )(implicit logger: Logger[Task]) {
  106. val buttonStyle = """| -fx-padding: 0.7em 0.57em;
  107. | -fx-font-size: 14px;
  108. | -jfx-button-type: RAISED;
  109. | -fx-background-color: rgb(77,102,204);
  110. | -fx-pref-width: 200;
  111. | -fx-text-fill: WHITE; """.stripMargin
  112. val router = new FXRouter[Page]
  113. // val players = mediaPlayerFactory.mediaPlayers
  114. // val videoPlayerController = players.newEmbeddedMediaPlayer()
  115. // mediaPlayer.controller.videoSurface.set(
  116. // videoSurfaceForImageView(videoView)
  117. // )
  118. // val videoPlayerControllerCleanup =
  119. // Task(videoPlayerController.controls().stop()) >>
  120. // Task(videoPlayerController.release())
  121. val videoPage = new BorderPane { pane =>
  122. // val mp = new MediaPlayer(
  123. // new Media(
  124. // "https://download.oracle.com/otndocs/products/javafx/oow2010-2.flv"
  125. // // "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_5mb.mp4"
  126. // )
  127. // ) {
  128. // autoPlay = true
  129. // }
  130. // obs(pane).subscribe()(schedulers.async)
  131. hgrow = Priority.Always
  132. center = new VideoView(mediaPlayer.controller) {
  133. alignmentInParent = Pos.Center
  134. preserveRatio = true
  135. // hgrow = Priority.Always
  136. // this.prefWidth <== pane.prefWidth
  137. fitWidth = _scene.width.value - 40
  138. _scene.width
  139. .map(_ - 40)
  140. .onChange((_, _, value) => fitWidth.value = value)
  141. // (new DoubleProperty).<==(_scene.width.map(_ - 10))
  142. // fitWidth.<==(_scene.width.map(_ - 10))
  143. }
  144. // videoPlayerController.video().videoDimension().setSize()
  145. padding = Insets(0, 0, 5, 0)
  146. bottom = new HBox {
  147. val mrl = new StringProperty
  148. spacing = 5
  149. children ++= Seq(
  150. new JFXTextField {
  151. text = "https://www.youtube.com/watch?v=0QKQlf8r7ls"
  152. text ==> mrl
  153. prefWidth = 100
  154. minWidth = 80
  155. },
  156. new JFXButton {
  157. text = "Play Video"
  158. style = buttonStyle
  159. onAction = _ => {
  160. if (mediaPlayer.controller.media().isValid())
  161. mediaPlayer.controller.controls().stop()
  162. mediaPlayer.controller
  163. .media()
  164. // .play(
  165. // "https://download.oracle.com/otndocs/products/javafx/oow2010-2.flv"
  166. // )
  167. // .play("https://www.youtube.com/watch?v=yZIummTz9mM")
  168. .play(mrl.value)
  169. }
  170. },
  171. new JFXButton {
  172. text = "Resume"
  173. style = buttonStyle
  174. onAction = _ => mediaPlayer.controller.controls().play()
  175. },
  176. new JFXButton {
  177. text = "Pause"
  178. style = buttonStyle
  179. onAction = _ => mediaPlayer.controller.controls().pause()
  180. },
  181. new JFXButton {
  182. text = "Stop"
  183. style = buttonStyle
  184. tooltip = new Tooltip {
  185. text = "Stop"
  186. showDelay = Duration(200)
  187. }
  188. onAction = _ => mediaPlayer.controller.controls().stop()
  189. },
  190. new JFXButton {
  191. text = "Get Status"
  192. style = buttonStyle
  193. onAction = _ => {
  194. println(mediaPlayer.controller.status().state())
  195. }
  196. }
  197. )
  198. }
  199. }
  200. val init: Task[Node] = for {
  201. routerStore <- router.store(Page.Home, logger)
  202. todoStore <- TodoListStore(logger)
  203. todoComponent <- TodoListView(todoStore)
  204. resolver: PartialFunction[Page, Parent] = {
  205. case Page.Home => videoPage
  206. // engine.load("https://www.youtube.com/embed/qmlegXdlnqI")
  207. // engine.load("https://youtube.com/embed/aqz-KE-bpKQ")
  208. // engine.load("http://www.youtube.com/embed/IyaFEBI_L24")
  209. case Page.UserHome =>
  210. new Label {
  211. styleClass ++= Seq("text-white")
  212. text = s"User Home, Id = ${Random.nextInt()}"
  213. }
  214. case Page.Todo => todoComponent
  215. }
  216. routerNode: Node <-
  217. Task
  218. .deferAction(implicit s =>
  219. Task(new HBox { box =>
  220. alignment = Pos.Center
  221. //TODO find a better way to do this
  222. // videoView.fitWidth <== box.prefWidth
  223. children <-- router
  224. .render(resolver)(routerStore)
  225. // call cancel on the old component to cancel all subscriptions
  226. .scan[Parent](new Label("empty")) { case (a, b) => b }
  227. .doOnNextF(s => logger.debug(s"Actual receive: $s"))
  228. .map(_.delegate)
  229. })
  230. )
  231. mainSceneNode <- Task.deferAction(implicit s =>
  232. Task(new StackPane { root =>
  233. alignment = Pos.Center
  234. hgrow = Priority.Always
  235. vgrow = Priority.Always
  236. children = new BorderPane {
  237. hgrow = Priority.Always
  238. vgrow = Priority.Always
  239. center = routerNode
  240. bottom = new HBox {
  241. implicit val cc = CompositeCancelable()
  242. alignment = Pos.Center
  243. spacing = 20
  244. children = Seq(
  245. new JFXButton {
  246. text = "Forward"
  247. style = buttonStyle
  248. obsAction.useLazyEval(
  249. me.Task.pure(FXRouter.Forward)
  250. ) --> routerStore
  251. disable <-- routerStore.map {
  252. case (_, FXRouter.State(_, h)) =>
  253. h.state.sp == h.state.values.size - 1
  254. }
  255. },
  256. new JFXButton {
  257. text = "Backward"
  258. style = buttonStyle
  259. obsAction.useLazyEval(
  260. me.Task(println("Fired")) >> me.Task.pure(FXRouter.Backward)
  261. ) --> routerStore
  262. disable <-- routerStore
  263. .doOnNextF(b => Coeval(println(s"Received1: $b")))
  264. .map {
  265. case (_, FXRouter.State(_, h)) => h.state.sp == 0
  266. }
  267. },
  268. new JFXButton {
  269. text = "Home"
  270. style = buttonStyle
  271. disable <-- routerStore
  272. .map { case (_, FXRouter.State(p, _)) => p === Page.Home }
  273. obsAction
  274. .useLazyEval(
  275. me.Task.pure(FXRouter.Replace(Page.Home))
  276. ) --> routerStore
  277. },
  278. new JFXButton {
  279. text = "Todo"
  280. style = buttonStyle
  281. disable <-- routerStore
  282. .map { case (_, FXRouter.State(p, _)) => p === Page.Todo }
  283. obsAction
  284. .useLazyEval(
  285. me.Task.pure(FXRouter.Replace(Page.Todo))
  286. ) --> routerStore
  287. },
  288. new JFXButton {
  289. text = "UserHome"
  290. style = buttonStyle
  291. disable <-- routerStore
  292. .map { case (_, FXRouter.State(p, _)) => p == Page.UserHome }
  293. obsAction
  294. .useLazyEval(
  295. me.Task.pure(FXRouter.Replace(Page.UserHome))
  296. ) --> routerStore
  297. },
  298. new JFXButton {
  299. text = "Dialog"
  300. style = buttonStyle
  301. val d = new JFXDialog {
  302. content = new HBox {
  303. style = "-fx-background-color: black"
  304. children = Seq(new Label {
  305. styleClass ++= Seq("text-white")
  306. text = "Sample Dialog"
  307. })
  308. padding = Insets(20)
  309. }
  310. }
  311. onAction = () => d.show(root)
  312. }
  313. )
  314. }
  315. }
  316. })
  317. )
  318. } yield mainSceneNode
  319. }
  320. class TestModel(_name: String, _age: Int) {
  321. val name = StringProperty(_name).readOnly
  322. val age = ObjectProperty(_age).readOnly
  323. }
  324. object Test {
  325. val items = ObservableBuffer(
  326. new TestModel("hmm", 1),
  327. new TestModel("hmm2", 2)
  328. )
  329. val ttv = new TableView[TestModel](items) {
  330. columns ++= Seq(
  331. new TableColumn[TestModel, String] {
  332. text = "Name"
  333. cellValueFactory = { _.value.name }
  334. },
  335. new TableColumn[TestModel, Int] {
  336. text = "Age"
  337. cellValueFactory = { _.value.age }
  338. }
  339. )
  340. }
  341. }