ajalt / mordant

Multiplatform text styling for Kotlin command-line applications
https://ajalt.github.io/mordant/
Apache License 2.0
957 stars 34 forks source link

Prevent Animation from clearing the screen #110

Closed vegidio closed 1 year ago

vegidio commented 1 year ago

Hey,

I'm using Terminal.textAnimation in my application, but it when the animation starts it clears all text that I printed before. Is there a way to prevent this from happening?

Thanks!

ajalt commented 1 year ago

Animations don't clear the screen when they start. Can you share a minimized version of the code you're trying?

What terminal, os, and target are you using?

vegidio commented 1 year ago

Yes, I can give you the entire source code actually since it's in my open source project šŸ˜„

  1. The entry point of the project is here Main.kt
  2. On line 24 I call the method getSubmissions.
  3. In the getSubmissions function I print some things on lines 20, 33 and 38.

This is just to give you some context; the code above is the one that will disappear when the animations start:

  1. Now back in the Main.kt file, I call the function downloadMedia on line 30.
  2. The function downloadMedia prints two things on the screen: a progressAnimation and a textAnimation. You can see the code where I update the text animation on line 45 and where I stop it on line 81.

But the problem begins already on step 4 above. As soon as the animation is updated, all the content that I printed on steps 1-3 disappear from the screen. But if I comment out the animation (steps 4-5 above), then the texts that I printed on steps 1-3 are not cleared.

I confirmed that if I comment out the textAnimation code, but I leave the progressAnimation, the progressAnimation does NOT clear the screen. What is clearing the screen seems to be only textAnimation.

I even created a video on asciinema to show what's happening:

asciicast

In the example above my code is running inside a Docker container, but this problem also occurs if I run my program outside Docker, from my Mac terminal (I'm using iTerm2).

I hope this helps you identify the problem. Thanks for helping.

vegidio commented 1 year ago

Sorry! After I shared my entire project I realized that it's much simpler to do what you originally asked, which is to share a minimal reproducible code. Here is an example that I just created:

package io.vinicius.rmd

import com.github.ajalt.mordant.animation.Animation
import com.github.ajalt.mordant.animation.progressAnimation
import com.github.ajalt.mordant.animation.textAnimation
import com.github.ajalt.mordant.rendering.TextStyles
import com.github.ajalt.mordant.terminal.Terminal
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.random.Random
import kotlin.time.Duration.Companion.seconds

val t = Terminal()

fun main() {
    t.print("\nšŸ“ Test ${TextStyles.bold("Test")} Test")

    runBlocking { delay(5.seconds) }

    val words = List(100) {
        (1..100).map { Random.nextInt(97, 123).toChar() }.joinToString("")
    }

    val anim = printMostRecent()

    val progress = t.progressAnimation {
        text("Downloading")
        percentage()
        progressBar(width = 50)
        completed()
        speed(" posts/sec")
        timeRemaining()
    }

    progress.updateTotal(words.size.toLong())
    val current = mutableListOf<String>()

    words.forEachIndexed { index, s ->
        current.add(s)
        progress.update(index + 1)
        anim.update(current.takeLast(5))
    }

    progress.stop()
    anim.stop()
}

fun printMostRecent(): Animation<List<String>> {
    return t.textAnimation { list ->
        list.joinToString("\n", "\n") { it }
    }
}

With this code you will see that Test Test Test string that I print in the very beginning is cleared when the animation starts.

vegidio commented 1 year ago

Ok, now I'm feeling stupid, but despite what I've said before, it seems that progressAnimation is also clearing any text printed before. I could swear it was not doing this before šŸ¤”

In any case, it seems that both progressAnimation and textAnimation are clearing the screen.

Also, sorry for spamming your mailbox with multiple updates on this issue.

ajalt commented 1 year ago

Here's a fully minimized repro:

t.println("xxxxxxx")
val b = t.textAnimation<String> { it }
b.update("foo")
b.update("foo\nbar")

The issue was that you were changing the size of the animation while it was running and mordant was trying to move the cursor to clear a space the size of the new frame, not the previous frame.

BTW, animations assume the cursor is on the start of a line before they draw for the first time, so you should use t.println instead of t.print to avoid the first frame of the animation drawing incorrectly.