Commit 58c42686 authored by David's avatar David

Updates work. CPU Temp and Current time are usable!

Time to start some refactoring for styling and better space usage.
parent f11b4708
......@@ -17,18 +17,21 @@ import javafx.scene.text.Text
import javafx.util.Duration
import mu.KotlinLogging
import tornadofx.*
import java.nio.file.Paths
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.TimeUnit
import kotlin.random.Random
//Generic the shit out of this!
data class TickerEntry<T : Node>(
val title: String,
val content: T,
val reschedule: Boolean = false,
val update: ((content: T) -> Unit)? = null,
val updateObservable: ((content: T) -> Completable)? = null
//TODO: need to add a size buffer, some dynamic things get weird
......@@ -73,22 +76,47 @@ class TickerView : View() {
override fun onDock() {
//Submit a text to be marquee'd
//After some kind of delay maybe
//Race condition, the thing hadn't completely sized before adding it, only a problem early on
// making sure to have a delay int here lays it out proper
//Lets create one that does a time
//This causes the lag that causes them to smear together, need to update the rendering logic to not be threaded
// per each item
val time = TickerEntry("CurrentTime", createText("one"), update = { text ->
//Automatically called by the thing on the right thread...
text.text =
//TODO: needs a way of cancelling
//TODO: with a subscription, I need a way to unsub it later
val time2 = TickerEntry("CurrentTimeObserved", createText("one"), updateObservable = { text ->
//Lets create a timer view for the current time, and one for the CPU temp that get rescheduled when they're done
val currentTime = TickerEntry("Current Time", createText("88:88:88"), reschedule = true) { text ->
val secondClock = Clock.tickSeconds(ZoneId.systemDefault())
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss")
Observable.interval(1, TimeUnit.SECONDS)
.map {
LocalDateTime.ofInstant(secondClock.instant(), ZoneId.systemDefault())
.doOnNextFx {
text.text = formatter.format(it)
val DEGREE = "\u00b0"
val currentTemp = TickerEntry("CPU Temp", createText("88${DEGREE}C"), reschedule = true) { text ->
val temperatureFile = Paths.get("/sys/class/thermal/thermal_zone0/temp").toFile()
Observable.interval(1, TimeUnit.SECONDS)
.map {
if (temperatureFile.exists()) {
} else {
//TODO: add some randomization to this, because coloring!
Random.nextInt(45000, 88000)
.map {
.doOnNextFx {
text.text = "%.2f%sC".format(it, DEGREE)
//Do the updating logic out of band on an observable that can be cancelled when it's gone
val time2 = TickerEntry("CurrentTimeObserved", createText("2011-12-03T10:15:30.000"), updateObservable = { text ->
Observable.interval(1, TimeUnit.SECONDS)
.doOnNextFx {
text.text =
......@@ -101,18 +129,18 @@ class TickerView : View() {
val entries2 = listOf(
time2 as TickerEntry<Node>,
TickerEntry<Node>("time", createText(
val entries = listOf(
time as TickerEntry<Node>,
currentTime as TickerEntry<Node>,
TickerEntry<Node>("blorp", createText("Third!")),
TickerEntry<Node>("blerp", createText("this is the second")),
//time as TickerEntry<Node>,
//TickerEntry<Node>("test", createText(sampleText)),
//TickerEntry<Node>("more", createText("Some more words")),
TickerEntry<Node>("another", createText("This is happening")),
TickerEntry<Node>("time", createText(
TickerEntry<Node>("time", createText(,
currentTemp as TickerEntry<Node>
......@@ -125,7 +153,7 @@ class TickerView : View() {
.doOnNext {
logger.debug("Adding a ticker to be sent this seems silly")
logger.debug("Queuing a ticker")
......@@ -145,14 +173,10 @@ class MarqueeView : View() {
private val logger = KotlinLogging.logger {}
private val OFFSET = 5.0 //Amount of space between entries!
// 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
override val root = pane
......@@ -173,8 +197,6 @@ class MarqueeView : View() {
root.clip = rectangle
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>) {
......@@ -187,8 +209,9 @@ class MarqueeView : View() {
//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
val timeline = Timeline()//Don't appear to need to unstart/restart this, it seems to not spinlock too bad
//TODO: this is kind of an expensive operation, should trigger only once...
fun lastOneCleared(): Boolean {
//Determine if the last entry in the activeQueue has cleared
val last = activeTicks.last()
......@@ -202,18 +225,17 @@ class MarqueeView : View() {
//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
root.add(newTickerEntry.content) //this is where it gets added
//But I never remove them!?!? UH OH
//But I never remove them!?!? UH OH They get removed later
//Something isn't working out right here.
activeTicks.forEach { entry ->
val content = entry.content
......@@ -231,19 +253,17 @@ class MarqueeView : View() {
entry.content.removeFromParent() //Is this legit?
activeTicks.remove(entry) //no longer here, shouldn't ruin the loop
if (subscriptions.containsKey(entry)) {
subscriptions[entry]!!.dispose() //This should cancel it
subscriptions.remove(entry)!!.dispose() //This should cancel it
logger.debug("disposing observable for ${entry.title}")
if (entry.reschedule) {
//just stick it back in the queue
logger.debug("Requeuing ${entry.title}")
} 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) {
//TODO: I think I cannot update an entry while it's being animated? I'm not sure
//At the least, i don't have to pause it, lets see if I can thread that
//If there's an observable that we haven't started, fire it up!
if (entry.updateObservable != null && !subscriptions.containsKey(entry)) {
//Start up the observable that updates the UI
logger.debug("Starting observable for ${entry.title}")
......@@ -256,13 +276,13 @@ class MarqueeView : View() {
//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.onFinished = EventHandler<ActionEvent> {
//This never gets triggered, even when timeline.stop() is called.
//I don't think this timeline ever actually finishes
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