package nova.monadic_sfx import java.util.concurrent.TimeUnit import scala.util.Random import cats.effect.Resource import cats.syntax.eq._ import cats.syntax.option._ import com.softwaremill.macwire._ import io.odin.Logger import monix.bio.IO import monix.bio.Task import monix.eval.Coeval import monix.execution.cancelables.CompositeCancelable import monix.{eval => me} import nova.monadic_sfx.executors.Schedulers 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.MediaPlayerResource import nova.monadic_sfx.util.controls.JFXButton import nova.monadic_sfx.util.controls.JFXDialog import nova.monadic_sfx.util.controls.JFXTextField import nova.monadic_sfx.util.controls.VideoView import org.gerweck.scalafx.util._ import org.kordamp.bootstrapfx.BootstrapFX import scalafx.Includes._ import scalafx.application.JFXApp3.PrimaryStage import scalafx.beans.property.ObjectProperty import scalafx.beans.property.StringProperty import scalafx.collections.ObservableBuffer import scalafx.geometry.Insets import scalafx.geometry.Pos import scalafx.scene.Node import scalafx.scene.Parent import scalafx.scene.Scene import scalafx.scene.control.Button import scalafx.scene.control.Label import scalafx.scene.control.TableColumn import scalafx.scene.control.TableView import scalafx.scene.control.Tooltip import scalafx.scene.layout.BorderPane import scalafx.scene.layout.HBox import scalafx.scene.layout.Priority import scalafx.scene.layout.StackPane import scalafx.util.Duration class MainApp( // spawnProtocol: ActorSystem[SpawnProtocol.Command], schedulers: Schedulers, startTime: Long, mediaPlayer: MediaPlayerResource )(implicit logger: Logger[Task]) { 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" ) } } private lazy val stage = new PrimaryStage { title = "Simple ScalaFX App" scene = _scene minWidth = 700 minHeight = 520 width = 1280 height = 720 // resizable = false } (for { (stopSignal, fxAppFib) <- MyFxApp.resource(schedulers, stage) i <- Resource.make(Task(1))(_ => Task.unit) } yield (stopSignal, fxAppFib, i)).use { case (a, b, c) => Task.unit } val program = MyFxApp .resource(schedulers, stage) .evalMap { case (stopSignal, fxAppFib) => wire[MainAppDelegate].init .flatMap(mainSceneNode => Task(_scene.getChildren += mainSceneNode)) .executeOn(schedulers.fx) >> Task.pure(stopSignal -> fxAppFib) } .use { case (stopSignal, fxAppFib) => for { // _ <- 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 () } } class MainAppDelegate( schedulers: Schedulers, mediaPlayer: MediaPlayerResource, _scene: Scene )(implicit logger: Logger[Task]) { val buttonStyle = """| -fx-padding: 0.7em 0.57em; | -fx-font-size: 14px; | -jfx-button-type: RAISED; | -fx-background-color: rgb(77,102,204); | -fx-pref-width: 200; | -fx-text-fill: WHITE; """.stripMargin val router = new FXRouter[Page] // val players = mediaPlayerFactory.mediaPlayers // val videoPlayerController = players.newEmbeddedMediaPlayer() // mediaPlayer.controller.videoSurface.set( // videoSurfaceForImageView(videoView) // ) // val videoPlayerControllerCleanup = // Task(videoPlayerController.controls().stop()) >> // Task(videoPlayerController.release()) val videoPage = new BorderPane { pane => // val mp = new MediaPlayer( // new Media( // "https://download.oracle.com/otndocs/products/javafx/oow2010-2.flv" // // "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_5mb.mp4" // ) // ) { // autoPlay = true // } // obs(pane).subscribe()(schedulers.async) hgrow = Priority.Always center = new VideoView(mediaPlayer.controller) { alignmentInParent = Pos.Center preserveRatio = true // hgrow = Priority.Always // this.prefWidth <== pane.prefWidth fitWidth = _scene.width.value - 40 _scene.width .map(_ - 40) .onChange((_, _, value) => fitWidth.value = value) // (new DoubleProperty).<==(_scene.width.map(_ - 10)) // fitWidth.<==(_scene.width.map(_ - 10)) } // videoPlayerController.video().videoDimension().setSize() padding = Insets(0, 0, 5, 0) bottom = new HBox { val mrl = new StringProperty spacing = 5 children ++= Seq( new JFXTextField { text = "https://www.youtube.com/watch?v=0QKQlf8r7ls" text ==> mrl prefWidth = 100 minWidth = 80 }, new JFXButton { text = "Play Video" style = buttonStyle onAction = _ => { if (mediaPlayer.controller.media().isValid()) mediaPlayer.controller.controls().stop() mediaPlayer.controller .media() // .play( // "https://download.oracle.com/otndocs/products/javafx/oow2010-2.flv" // ) // .play("https://www.youtube.com/watch?v=yZIummTz9mM") .play(mrl.value) } }, new JFXButton { text = "Resume" style = buttonStyle onAction = _ => mediaPlayer.controller.controls().play() }, new JFXButton { text = "Pause" style = buttonStyle onAction = _ => mediaPlayer.controller.controls().pause() }, new JFXButton { text = "Stop" style = buttonStyle tooltip = new Tooltip { text = "Stop" showDelay = Duration(200) } onAction = _ => mediaPlayer.controller.controls().stop() }, new JFXButton { text = "Get Status" style = buttonStyle onAction = _ => { println(mediaPlayer.controller.status().state()) } } ) } } val init: Task[Node] = for { routerStore <- router.store(Page.Home, logger) todoStore <- TodoListStore(logger) todoComponent <- TodoListView(todoStore) resolver: PartialFunction[Page, Parent] = { case Page.Home => videoPage // engine.load("https://www.youtube.com/embed/qmlegXdlnqI") // engine.load("https://youtube.com/embed/aqz-KE-bpKQ") // engine.load("http://www.youtube.com/embed/IyaFEBI_L24") case Page.UserHome => new Label { styleClass ++= Seq("text-white") text = s"User Home, Id = ${Random.nextInt()}" } case Page.Todo => todoComponent } routerNode: Node <- Task .deferAction(implicit s => Task(new HBox { box => alignment = Pos.Center //TODO find a better way to do this // videoView.fitWidth <== box.prefWidth children <-- router .render(resolver)(routerStore) // call cancel on the old component to cancel all subscriptions .scan[Parent](new Label("empty")) { case (a, b) => b } .doOnNextF(s => logger.debug(s"Actual receive: $s")) .map(_.delegate) }) ) mainSceneNode <- Task.deferAction(implicit s => Task(new StackPane { root => alignment = Pos.Center hgrow = Priority.Always vgrow = Priority.Always children = new BorderPane { hgrow = Priority.Always vgrow = Priority.Always center = routerNode bottom = new HBox { implicit val cc = CompositeCancelable() alignment = Pos.Center spacing = 20 children = Seq( new JFXButton { text = "Forward" style = buttonStyle obsAction.useLazyEval( me.Task.pure(FXRouter.Forward) ) --> routerStore disable <-- routerStore.map { case (_, FXRouter.State(_, h)) => h.state.sp == h.state.values.size - 1 } }, new JFXButton { text = "Backward" style = buttonStyle obsAction.useLazyEval( me.Task(println("Fired")) >> me.Task.pure(FXRouter.Backward) ) --> routerStore disable <-- routerStore .doOnNextF(b => Coeval(println(s"Received1: $b"))) .map { case (_, FXRouter.State(_, h)) => h.state.sp == 0 } }, new JFXButton { text = "Home" style = buttonStyle disable <-- routerStore .map { case (_, FXRouter.State(p, _)) => p === Page.Home } obsAction .useLazyEval( me.Task.pure(FXRouter.Replace(Page.Home)) ) --> routerStore }, new JFXButton { text = "Todo" style = buttonStyle disable <-- routerStore .map { case (_, FXRouter.State(p, _)) => p === Page.Todo } obsAction .useLazyEval( me.Task.pure(FXRouter.Replace(Page.Todo)) ) --> routerStore }, new JFXButton { text = "UserHome" style = buttonStyle disable <-- routerStore .map { case (_, FXRouter.State(p, _)) => p == Page.UserHome } obsAction .useLazyEval( me.Task.pure(FXRouter.Replace(Page.UserHome)) ) --> routerStore }, new JFXButton { text = "Dialog" style = buttonStyle val d = new JFXDialog { content = new HBox { style = "-fx-background-color: black" children = Seq(new Label { styleClass ++= Seq("text-white") text = "Sample Dialog" }) padding = Insets(20) } } onAction = () => d.show(root) } ) } } }) ) } yield mainSceneNode } class TestModel(_name: String, _age: Int) { val name = StringProperty(_name).readOnly val age = ObjectProperty(_age).readOnly } object Test { val items = ObservableBuffer( new TestModel("hmm", 1), new TestModel("hmm2", 2) ) val ttv = new TableView[TestModel](items) { columns ++= Seq( new TableColumn[TestModel, String] { text = "Name" cellValueFactory = { _.value.name } }, new TableColumn[TestModel, Int] { text = "Age" cellValueFactory = { _.value.age } } ) } }