Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Left to right rendering effect #263

Open
mjip opened this issue Jul 12, 2020 · 11 comments
Open

Left to right rendering effect #263

mjip opened this issue Jul 12, 2020 · 11 comments

Comments

@mjip
Copy link

mjip commented Jul 12, 2020

Is your feature request related to a problem? Please describe.
I would like for an effect to render text (or ASCII characters) from left to right, like it's being typed out.

Describe the solution you'd like
Similar to the mirage effect (where text can be rendered one character at a time, only randomly), it would be cool to have text rendered one letter at a time from left to right, so it appears to be typed in real-time.

@peterbrittain
Copy link
Owner

Makes sense. I get the idea... Would you be interested in implementing it? Should be relatively easy to use Mirage as a template for the new Effect and change the logic to maintain a count of where you've got to in the text to be displayed and just print up to that (then increment the counter for the next update)...

@mjip
Copy link
Author

mjip commented Jul 13, 2020

Yep, PR incoming :^) It was relatively easy using the Mirage class as a template

@vinayak-mehta
Copy link

vinayak-mehta commented Aug 25, 2020

I was able to implement this by extending DynamicRenderer. The bars.py really helped in understanding how DynamicRenderer works, thanks for all the examples!

When I instantiate an object of the class mentioned above, I pass in the text I want to be printed one character at a time, and save the state of how much of that text has been consumed inside an instance attribute, each time DynamicRenderer is called. This could be another way of adding the Typewriter effect to asciimatics, I would love to see this in the basics.py example! It looks like this:

gif

The API to use this class looks similar to this:

Print(
    screen,
    Typewriter(text="import os", width=100, height=100),
    y,
    x=x,
    colour=Screen.COLOUR_WHITE,
    bg=Screen.COLOUR_BLACK,
    transparent=False,
    speed=4,
)

@peterbrittain What do you think about the implementation? I can work on creating a PR based on the fixes / API changes that you suggest. Also, I'm not sure how it should work with FigletText.

Should it also have an option to print the text all at once instead of one character at a time? Or should that be left to another Print() with static text?

@peterbrittain
Copy link
Owner

peterbrittain commented Aug 26, 2020

Thanks @vinayak-mehta . That looks great! Sorry for the delay... I've been thinking about the best way to handle these things.

After much musing, I think that it makes most sense for this to be a chained renderer that takes another renderer as input and gradually displays the content of that renderer using similar logic to your existing renderer. Key benefit is that this allows more composition (e.g. for another renderer that supports coloured text, which could be the existing StaticRenderer right now or a future syntax highlighting renderer for your code example).

This slightly complicates your simple case (just type some pre-defined text), but only by adding one more function call. For example:

Print(
    screen,
    Typewriter(text=StaticRenderer(images=["import os"]), width=100, height=100),
    y
)

What do you think? Is that worth the change to your code?

BTW - I don't think we should worry about FigletText. Frankly, doing that a letter at a time will require detailed knowledge of the figlet font and so will be a separate custom renderer (which we can worry about another day).

@vinayak-mehta
Copy link

vinayak-mehta commented Aug 28, 2020

@peterbrittain Sorry for the late reply, this week has been hectic but I'm planning to work on this over the weekend!

After much musing, I think that it makes most sense for this to be a chained renderer that takes another renderer as input and gradually displays the content of that renderer using similar logic to your existing renderer. Key benefit is that this allows more composition (e.g. for another renderer that supports coloured text, which could be the existing StaticRenderer right now or a future syntax highlighting renderer for your code example).

This is a great suggestion and makes a lot of sense, I hadn't thought about this. A future syntax highlightning renderer would be awesome! Can you give me some pointers on how to make something like syntax highlighting work? Right now I'm using DynamicRenderer's _write() method to do something like self._write(text, x, y, colour=Screen.COLOUR_GREEN) to print colored text. Would syntax highlighting involve doing multiple self._write calls for different parts of the code at different coordinates with different colors? Is there another way?

What do you think? Is that worth the change to your code?

The suggestion makes a lot of sense. I guess instead of extending DynamicRenderer in the implementation I mention above, I could just use Typewriter and have a list of static and dynamic Print objects to simulate a terminal session playback.

BTW - I don't think we should worry about FigletText. Frankly, doing that a letter at a time will require detailed knowledge of the figlet font and so will be a separate custom renderer (which we can worry about another day).

👍

@peterbrittain
Copy link
Owner

@vinayak-mehta I'm not quite sure what you're getting at for possible implementation... Let me try to elucidate what I meant.

Have a look at the Rainbow renderer and how it is used. You could use something similar for a Typewriter renderer. Instead of adding volours, you could use the full text and colour map and just return an extra character each time. Using a dynamic renderer that writes directly to the buffer would be more memory efficient and so better.

To create a syntax colouring option, I suggest we create a renderer that can use a parser to convert a blob of text into text/colour map. The next step would be to create a pygments parser to use in that renderer.

@vinayak-mehta
Copy link

vinayak-mehta commented Aug 29, 2020

Thanks for the pointer! I'll have to look into the Rainbow renderer and learn how color maps work. I took a look at the xmas.py example to see how the ${c,a,b} syntax works and it makes sense to me now! I didn't know about this syntax before. I was able to use it to add text formatting support to present!

I'll try to work on the Typewriter PR today.

On a separate note, would you like a PR to add a Screen.A_ITALIC attr (and maybe some other attrs too)? I saw that Python 3.7 added A_ITALIC in the curses module. I guess this change would have to be added in a backwards compatible way.

@peterbrittain
Copy link
Owner

Thanks for the offer, but I don't think we should add more attributes. I've tried to keep asciimatics limited to stuff that you can do in all environments and deliberately avoided the less well supported ones. In the case for italics, it is only possible to do that in recent builds of Windows 10 (that support ansi escape codes). It might make sense to try that out once I have added support for that screen type, but not just yet.

@vinayak-mehta
Copy link

Makes sense 👍

Sorry for being inactive on the implementation. I've been busy with some other deadlines + a medical emergency at home. I will get back to this as soon as I can find time.

@peterbrittain
Copy link
Owner

NP. Real life always comes first...

@peterbrittain
Copy link
Owner

This issue has various ideas in it, which are still worth doing at some point. For now I just wanted to flag that the AnsiArtPlayer and AsciinemaPlayer renderers may do what you want in some cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants