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
Fix Cursor Animation on widget.Entry #1779
Conversation
…is object instead of a raw fyne.Animation in entry.go
This is how the cursor is shown with this PR: fix-cursor-demo.mp4 |
This is a pleasing change, as this makes the cursor consistent with the OS cursor in Gnome (which will not flash or fade while the user is actively typing, although it has a much shorter pause than your video does.) (Also, it's a little jumpy, but again, I get what you're trying to do, and like the direction.) |
Currently, it pauses 500 ms, should it be less?
What do you mean? |
@AlbinoGeek I have decreased the pause time, what do you think now? cursor_anim_1.mp4Testing this feature, I think there is a bug in cursor animation, even without the changes in this draft: cursor_anim_bug.mp4It seems that the problem is in the fyne.Animation implementation (it seems that the animation time is shared maybe, and when cursor animation start, sometimes its defined interval is almost done, so first 'animation' is very short). What do you think @andydotxyz ? |
It's possible depending on how long the scheduler takes to kick it off. You can see in |
@andydotxyz I have seen the animation implementation, and I think first |
Your summary is correct, we could miss the first frame of animation. But is that going to lead to visual issues such as you have reported? The cursor should fade out over half a second and back in over another half, meaning that the first frame is a 60th of the animation, or 30th of the fade. Maybe there is another issue at play? |
You are right. It isn't the root of the problem. In fact, that doesn't even make it better. Not sure, what it is. Is it safe to replace them? |
It was coded originally to fade it in. With the inversion you added it appears immediately then fades out. I'm not sure why this improves the pause code, perhaps there is something else at play? |
I have fixed the initial state of the cursor, and removed some code that was accidentally overriding the colour on refresh. |
Yes, that fixed it. Thanks :)
The problem is that if I don't invert it, it looks like a jumpy transition, see: jumpy_cursor.mp4Inverted animation looks smooth instead: smooth_cursor.mp4
Hmm I am not sure, the pause in this PR is temporary so after some time the animation starts without any action. Animation.pause would do the same as the current Animation.stop, wouldn't it? |
Not really. Stop resets the animation so start runs from the beginning. Pause would remember the current state so it could be resumed (at least that’s what was in my head when I wrote the message). It seems that the animation you want to start is from 1.0 but on entering we want it to start from 0.0. Maybe stopping the enter animation and starting a new one that is the opposite upon the timeout lapsing would solve this without changing the current animation / tests? I think that the current code could be simplified by doing this - and we should be able to avoid the specific ticking code as well I would think. |
You are right! My mistake. However for the cursor animation start and stop are enough, because we want to always restart from the beginning, not from animation was paused.
Yes, you are right, that would solve the tests :)
I don't understand well how is the addition of Animation.pause would simplify current code, as we need to restart the animation from the beginning. |
…uested to make current tests succeed
I assumed that the complicated code in this PR was to manage the pause / resume? It’s almost a custom animation ticker in entry_cursor_anim.go |
Yes, but it is not a common pause, it is a temporary pause that will auto-start the animation from the beginning after some time (not from where it was when it was paused). That's why I think Pause/Resume functions in fyne.Animation would not simplify this code as it is different? Although I think Pause/Resume function would be a great addition :), a function to know check the animation status would be great too! |
I understand this - but surely something like pausing it when typing starts, then after the time has lapsed unpause would help? The only custom code you should need is determine when to call |
To be honest, I don't understand what do you mean.
And then, the animation is restarted here:
|
Difference between stop and pause: Animation is running from 0.0 to 1.0, at 0.5 we want to interrupt it:
Then when we want to restart it, we call the counterpart:
This is, in effect, the same as the difference between stop and pause when watchng a movie or listening to music - pause is a temporary interruption that does not reset your progress. |
@andydotxyz Yes, that's right, but here we want to restart always from 0.0 (not from it was paused). Maybe |
There aren't really needed, they just have sense for testing, otherwise it would be difficult to test (as you can see in entry_cursor_anim_test.go there is a type
Because if I use it, it will not be efficient, as the timer should be stopped when user types, and time.After does not give us that functionality. Quoting golang time package documentation:
Yes, I though it too, but in this way is how the cursor animation is implemented currently in entry.go:
And that's maybe because Focus event is triggered on every click and that could lead to multiple calls to Start when animation is already started. |
That is to handle the situation when the widget loses focus so we don't want the animation any more.
Calling |
I know and that is what I am trying to do. If you look
That is equivalent to the current implementation. Sorry, but I don't understand what difference you are trying to tell me.
I know, I am not creating it again and again, I am just creating the first one (beginning from dim to opaque) and then when user starts typing create a second one (beginning from opaque to dim). The second one is kept until user unfocus the Entry. The first one is needed to avoid changing the test images. And the second one is needed because if I use the first one (dim to opaque) the cursor will look jumpy in the transition. |
Yes I think I misread it sorry, it is creating only as many animations as are needed.
The way that animations work in test at the moment is that they always tick the final element of the animation - you can tick them at other positions if required for testing. The more fine grained animation test approach you have made probably suits better in our overall test support if it's needed. i.e. |
I see, but I am not testing specifically the fyne.Animation, I am testing the cursor animation that has its own custom go routine. |
Maybe this is where I am lost again. I thought it was a regular animation but with a delay so it's not always running. |
Yes, it is. entryCursorAnim is only managing the fyne.Animation, when it should be in a running, interrupted or stopped state. For Interrupted state it is necessary something like a timeout to restart the animation automatically after some time. The new types are useful to test this situation (InterruptedState).
Yes, it is possible, however it will introduce delays in tests, and I think it is undesired, am I wrong? |
I don't really understand why it has to add delays if you remove the abstraction - but I think this relates to your tests wishing to test every tick of animation frame, which is why I suggested it may relate to the wider how-do-we-test-animations question. In the main toolkit when animations are concerned we test the start (before it is started) and end (what happens immediately after start) because that is how the test driver handles animations at this time. I am not sure what your tests are trying to solve that cannot take the same simplified approach. |
…ove mockTicker type from entry_cursor_anim_test.go
Sorry, I don't understand how my tests do what you say. My tests are just testing that the internal fyne.Animation is effectively stopped, when I have updated the way tests are performed. cursorTicker interface and mockTicker types were removed. cursorTicker now is the previous |
…and cursorTicker.resetChan respectively, make the cursorTicker methods non-exported, rename channels used in entry_cursor_anim_test.go
Would I be right in condensing this PR into the following?;
|
Yes, basically that is the goal of this PR. |
Hmmm, then I think it could be simplified;
|
Yes, that's a possibility (Andy suggested a similar idea), the only problem with it, is that it will restart the blink in an unknown animation frame that will cause a jumpy animation. However, I think it is possible to nullify it by waiting for the expected initial frame before letting the animation calculate Cursor color as before. I will try it to see if it's possible. Thanks :) |
I don't think this is true. Animation has no pause, each time you call |
No Pablo is right to point out that limitation in the suggestion I made where the animation is only started when the Entry gains focus and runs continuously but gets essentially "ignored" if there has been input in the last Xms. When the last input time is greater than the threshold the animation will no longer be "ignored" and this could cause the cursor to go from fully opaque to anywhere along the animation curve and could be jarring. Perhaps the solution to this is the addition of |
Oh, I missed the “ignored” bit. If you call |
Not fighting, the animation tick would have an
Well then you need a timer to know when to resume the animation, I was trying to avoid this for simplicity and just using the animation ticks as the timer |
That would be a nice addition :)
I don't think it is possible to call anim.Stop() inside the animation tick function, is it?
Agreed, however it would not be that simple, because when the Entry initially get focus, the current animation (the one in |
I think instead of the "inverted" complication we might just want to change the animation to always be a fade out, so the cursor immediately appears then fades out, and when the animation reverses it fades back in, and then repeats. I think users would expect the cursor to immediately appear when they tap the Entry so it feels responsive. |
Yes, that was the first possibility, unfortunately it would mean a lot of failed tests :(, because now the cursor would not be visible based on the way how animations are tested ( |
Oh, sorry I thought you were talking about stock color animations. It could indeed be done if we code up the color animation code with an if as you suggest. Cunning.
You should be able to call |
The tests can manually tick the animation to whatever they need |
Closed in favor of #1968. |
Description:
Fixes #1778
It seems that #1778 appears, because fade animation is running when user is typing.
Cursor should be static when user is interacting with the Entry (as normally cursor animation).
Implementation in this PR could be improved, but that's the idea basically.
Checklist: