Commit 69e42600 authored by David's avatar David

This is a ticker! And I think it doesn't murder threads

parent 674a0238
package `is`.kow.deskscreen.ticker
import com.github.thomasnield.rxkotlinfx.changes
import com.github.thomasnield.rxkotlinfx.doOnNextFx
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.processors.PublishProcessor
import io.reactivex.rxjavafx.sources.Flag
import io.reactivex.schedulers.Schedulers
import javafx.animation.Animation
import javafx.animation.KeyFrame
......@@ -21,28 +19,22 @@ import javafx.scene.text.Text
import javafx.util.Duration
import mu.KotlinLogging
import tornadofx.*
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread
class TickerEntry(
data class TickerEntry(
val title: String,
val content: String
) {
//Number of pixels from the Right side of the screen
//Will be updated by the entry thing, to render stuff
val xPosition = SimpleIntegerProperty(this, "xPosition", 0)
override fun toString(): String {
return "TickerEntry(title=$title, content=$content)"
}
//TODO: A way to add rendering logic
//TODO: a way to have a notification when it's cleared, for ones that want to be re-executed
}
class TickerModel() : ItemViewModel<TickerEntry>() {
val tickets = FXCollections.observableArrayList<TickerEntry>()
}
/**
* Display a ticker view of things
* I want the ticker to always run, and things can be injected to be the *next* thing to display
......@@ -56,15 +48,9 @@ class TickerModel() : ItemViewModel<TickerEntry>() {
*/
class TickerView : View() {
/**
* I want to emit a TickerEntry to a stream and have it display on the ticker
* It just starts scrolling from the right. Multiple entries get separated and put on the ticker also
* Once it's completely scrolled off, it can be dropped or something....
* https://stackoverflow.com/questions/23062430/javafx-horizontal-marquee-text
* //that's a maybe
*/
private val tickerModel: TickerModel by inject()
private val sampleText = "What is going on? How did this get added to the pane? It shouldn't have."
private val logger = KotlinLogging.logger {}
private val marqueeView: MarqueeView by inject()
override val root = hbox {
......@@ -73,7 +59,8 @@ class TickerView : View() {
this.prefHeight = 30.0
//Somehow need to animate all the things
this.add(MarqueeView(this))
marqueeView.inside(this)
this.add(marqueeView)
}
override fun onDock() {
......@@ -84,9 +71,13 @@ class TickerView : View() {
val entries = listOf(
TickerEntry("blorp", "Third!"),
TickerEntry("blerp", "this is the second"),
TickerEntry("test", sampleText)
TickerEntry("test", sampleText),
TickerEntry("more", "Some more words"),
TickerEntry("another", "This is happening"),
TickerEntry("time", ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME))
)
Observable.fromIterable(entries)
.observeOn(Schedulers.computation())
.concatMap { tickerEntry ->
Observable.interval(1, TimeUnit.SECONDS)
.take(1)
......@@ -94,31 +85,35 @@ class TickerView : View() {
tickerEntry
}
}
.doOnNextFx {
tickerModel.tickets.add(it)
.doOnNext {
logger.debug("Adding a ticker to be sent this seems silly")
marqueeView.tickEntries.onNext(it)
}
.subscribe()
}
}
// Has to know it's parent to handle binding to proper sizes, Should do that elsewhere tho?
class MarqueeView(inside: Pane) : View() {
private val tickerModel: TickerModel by inject()
class MarqueeView : View() {
private val logger = KotlinLogging.logger {}
private val OFFSET = 5.0 //Amount of space between entries!
private val tickEntries = PublishProcessor.create<TickerEntry>()
val tickEntries = PublishProcessor.create<TickerEntry>()
data class ClearedTick(val entry: TickerEntry)
override val root = pane {
private val pane: Pane = pane()
private val singleThread = Executors.newSingleThreadExecutor()
override val root = pane
fun inside(of: Pane) {
//I need this guy to autofill to max size
this.prefWidthProperty().bind(inside.widthProperty())
this.prefHeightProperty().bind(inside.heightProperty())
pane.prefWidthProperty().bind(of.widthProperty())
pane.prefHeightProperty().bind(of.heightProperty())
}
init {
val rectangle = Rectangle(25.0, 25.0)
//Need to figure out what size my clipping rectangle needs to be
......@@ -127,47 +122,24 @@ class MarqueeView(inside: Pane) : View() {
rectangle.heightProperty().bind(root.heightProperty())
root.clip = rectangle
//Hook up an observable looking for the right kind of change to the list
// tickerModel.tickets.changes()
// .filter {
// it.flag == Flag.ADDED
// }
// .map {
// it.value
// }
// .doOnNext {
// logger.debug("Received addition to tickerModel!: $it")
//
// //Queue it up to be processed
// tickEntries.onNext(it)
// //start an animation for this entry
//
// //Need to somehow queue up the entry, so it doesn't fire until the other one is done, or something...
// //Some kind of state tracking for the current item in progress, find it's end layout, if it's greater than the width, start there, otherwise start at width
// }
// .subscribe()
//Is this being take based, or push based. probably push based...
//It isn't delaying the incoming new ones... they're getting published immediately.
//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>({ emitter ->
tickerModel.tickets.changes()
tickEntries
.observeOn(Schedulers.computation())
.filter {
it.flag == Flag.ADDED
}
.map {
it.value
}
.doOnNext {
emitter.onNext(it)
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.MISSING)
.onBackpressureBuffer(1024)
BackpressureStrategy.BUFFER) //Auto buffer of 128
.observeOn(Schedulers.from(singleThread))
//.onBackpressureBuffer(1024)
.doOnNext {
logger.debug("Backpressure driven work for $it")
}
......@@ -183,14 +155,8 @@ class MarqueeView(inside: Pane) : View() {
animateText(it.first, it.second)
}
.map {
it.second.blockingFirst() //This part I have to figure out super bad...
}
.doOnNext {
// This emits properly when it's done, which is awesome, but it's not blocking upstream...
logger.info("$it CLEARED!")
}
.doOnComplete {
logger.info("TICK ENTRIES COMPLETED, retrying for another entry!")
//NOTE: thread gets blocked here, not sure if it matters.
it.second.blockingFirst()
}
.subscribe()
}
......@@ -237,10 +203,8 @@ class MarqueeView(inside: Pane) : View() {
logger.debug("ANIMATED COMPLETELY OUT THE PANEL!")
//Done animating, it's finished
timeline.stop()
//TODO: some kind of callback to evict the item to be garbage collected
} else {
text.layoutX = text.layoutX - 1
text.layoutX = text.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
}
})
......
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