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

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 }
}
)
}
}