Commit 377113d5 authored by David's avatar David

All uses a single animation timeline now.

Updates work nicely, and it seems to work pretty well
parent 31cbcda5
......@@ -21,6 +21,7 @@ import tornadofx.*
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
......@@ -109,7 +110,8 @@ class TickerView : View() {
}
.doOnNext {
logger.debug("Adding a ticker to be sent this seems silly")
marqueeView.tickEntries.onNext(it)
//marqueeView.tickEntries.onNext(it)
marqueeView.enqueueTickEntry(it)
}
.subscribe()
}
......@@ -120,12 +122,14 @@ class MarqueeView : View() {
private val logger = KotlinLogging.logger {}
private val OFFSET = 5.0 //Amount of space between entries!
val tickEntries = PublishProcessor.create<TickerEntry<Node>>()
// val tickEntries = PublishProcessor.create<TickerEntry<Node>>()
private val activeTicks = ConcurrentLinkedQueue<TickerEntry<Node>>() //This might not need to be threadsafe, only one thing is adding/removing it
private val queuedTicks = ConcurrentLinkedQueue<TickerEntry<Node>>() //This one does, multiple threads!
data class ClearedTick(val entry: TickerEntry<Node>)
private val pane: Pane = pane()
private val singleThread = Executors.newSingleThreadExecutor() //TODO: really need a way to clean this up
// private val singleThread = Executors.newSingleThreadExecutor() //TODO: really need a way to clean this up
override val root = pane
......@@ -145,45 +149,122 @@ class MarqueeView : View() {
rectangle.heightProperty().bind(root.heightProperty())
root.clip = rectangle
//TODO: I don't have a good way of cleaning up the executor service. Need to figure that out
// //TODO: I don't have a good way of cleaning up the executor service. Need to figure that out
//
// //Create a backpressure based flowable that only processes one item at a time.
// Flowable.create<TickerEntry<Node>>({ emitter ->
// tickEntries
// .observeOn(Schedulers.computation())
// .doOnNext {
// logger.debug("About to emit!")
// emitter.onNext(it) //NOTE: This blocks a thread
// logger.debug("Emitted incoming ticker change!")
// }
// .subscribe()
//
// //There isn't really a cancellation thing...
// },
// BackpressureStrategy.BUFFER) //Auto buffer of 128
// .observeOn(Schedulers.from(singleThread))
// //.onBackpressureBuffer(1024)
// .doOnNext {
// logger.debug("Backpressure driven work for $it")
// }
// .map {
// //start an animation, but don't release another until it's past a point
// //Except cannot.... or consume a blocking, but that's gross, probably easier tho....
// //do some kind of callback thing like for steam
// val clearedSubject = PublishProcessor.create<ClearedTick>()
// Pair(it, clearedSubject)
// }
// .doOnNextFx {
// //This will fire off the request to animate the entry, and not move forward until the next thing passes. I think
// animateText(it.first, it.second)
// }
// .map {
// //NOTE: thread gets blocked here, not sure if it matters.
// it.second.blockingFirst()
// }
// .subscribe()
startAnimation() //Fire up the animation process
//TODO: need to have a way of monitoring the queue and active to pause/start the whole thing again
//Maybe that doesn't matter? Will have to monitor CPU usage
}
fun enqueueTickEntry (entry: TickerEntry<Node>) {
queuedTicks.add(entry)
}
//Create a backpressure based flowable that only processes one item at a time.
Flowable.create<TickerEntry<Node>>({ emitter ->
tickEntries
.observeOn(Schedulers.computation())
.doOnNext {
logger.debug("About to emit!")
emitter.onNext(it) //NOTE: This blocks a thread
logger.debug("Emitted incoming ticker change!")
}
.subscribe()
//Will need to fire one of these for each thing, with some space detection, based on when one is ending.
private fun startAnimation() {
//There isn't really a cancellation thing...
},
BackpressureStrategy.BUFFER) //Auto buffer of 128
.observeOn(Schedulers.from(singleThread))
//.onBackpressureBuffer(1024)
.doOnNext {
logger.debug("Backpressure driven work for $it")
}
.map {
//start an animation, but don't release another until it's past a point
//Except cannot.... or consume a blocking, but that's gross, probably easier tho....
//do some kind of callback thing like for steam
val clearedSubject = PublishProcessor.create<ClearedTick>()
Pair(it, clearedSubject)
}
.doOnNextFx {
//This will fire off the request to animate the entry, and not move forward until the next thing passes. I think
animateText(it.first, it.second)
//For each entry in the active ticks, animate it, until it's off th edge, and then drop it from the thing
//if there's space to add a queue'd tick, stick it in there
val timeline = Timeline() //TODO: need to find a way to pause/restart the timeline when it's empty
fun lastOneCleared() : Boolean {
//Determine if the last entry in the activeQueue has cleared
val last = activeTicks.last()
return last.content.layoutBounds.width + last.content.layoutX + OFFSET <= root.width
}
//I want any rendering delays to slow the whole thing, not just the individual item. Also then it's less threads
val updateFrame = KeyFrame(Duration.millis(35.0), EventHandler<ActionEvent> {
//This appears to move too fast!
if(activeTicks.isEmpty() || lastOneCleared()) {
val newTickerEntry: TickerEntry<Node>? = queuedTicks.poll()
if(newTickerEntry != null) {
//Can take one from the queued thing, and plop it in here to start rendering
//TODO: all the pre-setup here
//Then just put it into the active queue, so it will start processing like normal
newTickerEntry.content.layoutX = root.prefWidth //Where to start
activeTicks.add(newTickerEntry)
root.add(newTickerEntry.content) //this is where it gets added
//But I never remove them!?!? UH OH
}
.map {
//NOTE: thread gets blocked here, not sure if it matters.
it.second.blockingFirst()
}
activeTicks.forEach { entry ->
val content = entry.content
//I don't intend to bounce this
val textWidth = content.layoutBounds.width
val layoutX = content.layoutX
//instead I want to move completely from the right all the way off until it's gone
//end state of this text is the entire width of the text off the right so layoutX <= 0 - textWidth - offset
//I don't think I need to do anything about this, only know when it's been moved off the end
if (layoutX <= 0 - textWidth - (2 * OFFSET)) {
logger.debug("ANIMATED ${entry.title} COMPLETELY OUT THE PANEL!")
//Now I ned to figure out how to remove it
entry.content.removeFromParent() //Is this legit?
activeTicks.remove(entry) //no longer here, shouldn't ruin the loop
} else {
content.layoutX = content.layoutX - 1 //TODO: I should probably do animation in larger chunks perhaps, so that it can use less CPU?
//TODO: this might be where I need to make other animation happen
if (entry.update != null) {
timeline.pause()
entry.update.invoke(entry.content)
timeline.play()
}
}
.subscribe()
}
}
})
timeline.keyFrames.add(updateFrame)
//Will have to figure out how to inject changes in here
timeline.cycleCount = Animation.INDEFINITE //Because we're going to manually tell it to stop
timeline.play()
timeline.onFinished = EventHandler<ActionEvent> {
//This never gets triggered, even when timeline.stop() is called.
logger.debug("TIMELINE FINISHED HANDLER!")
}
}
//Will need to fire one of these for each thing, with some space detection, based on when one is ending.
private fun animateText(entry: TickerEntry<Node>, publishSubject: PublishProcessor<ClearedTick>) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment