-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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 notifications when passed through with a cursor too quickly #28815
base: dev
Are you sure you want to change the base?
Conversation
Telegram/lib_ui
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure how I'm supposed to handle the lib_ui submodule in my commits (both of which require corresponding changes in lib_ui). Please, tell me if/how I should fix it.
By the way, I believe that with these changes, in the tdesktop/Telegram/SourceFiles/window/notifications_manager_default.cpp Lines 472 to 497 in 4505a2b
|
You say in one of commit messages:
But that's not true, here's the function in question:
It does check exactly |
I also suspect the issue you mark here as being fixed couldn't be fixed by changes in this PR, as they likely stem from the same problem of broken QWidget::leaveEvent you try to work around in #28816? |
@@ -543,12 +543,17 @@ void Widget::hideStop() { | |||
if (_hiding) { | |||
_hiding = false; | |||
_hidingDelayed = {}; | |||
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim); | |||
// Using `.change` instead of `.start` because we want to start | |||
// animation from the current opacity level, not blink it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see Ui::Animations::Simple::start already has some handling of unfinished animation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, the lib_ui was not documented, so I can't be sure about that, but I assumed that:
Ui::Animations::Simple::start
, when called on an ongoing animation, should reset it completely, set opacity tofrom
, and go from there toto
Ui::Animations::Simple::change
, when called on an ongoing animation, should not reset thefrom
: it starts from where the previous animation got, and continues from there.
If my assumptions are correct, I think, you should agree that here we need .change()
(i.e., restore the notification from whatever state it was in) rather than .start()
(blink the notification by first setting its opacity to zero and then restoring it).
If my assumptions are wrong, I kindly ask you to explain the intended behavior of the .start()
and .change()
methods of an animation in lib_ui.
Yes, it can:
Relevant code: void Manager::startAllHiding() {
if (!hasReplyingNotification()) {
int notHidingCount = 0;
for (const auto ¬ification : _notifications) {
if (notification->isShowing()) {
++notHidingCount;
} else {
notification->startHiding();
}
}
// ... and that involves the bool isShowing() const {
return _a_opacity.animating() && !_hiding;
} then the manager conditionally calls void Notification::startHiding() {
if (!_history) return;
hideSlow();
} which calls void Widget::hideSlow() {
if (anim::Disabled()) {
_hiding = true;
base::call_delayed(
st::notifySlowHide,
this,
[=, guard = _hidingDelayed.make_guard()] {
if (guard && _hiding) {
hideFast();
}
});
} else {
hideAnimated(st::notifySlowHide, anim::easeInCirc);
}
} and that's where void Widget::hideAnimated(float64 duration, const anim::transition &func) {
_hiding = true;
_a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., duration, func);
} |
As I understand, you are saying that (1) this is the same issue as in #28816 and (2) this PR does not fix it?
|
I'm not sure what's wrong here? The hiding animation gets started only after _hiding is set to true. I don't see a moment when it could hide without _hiding being set for |
It's also weird that it's |
I believe what you try to fix here needs a method that would do (perhaps name it startHidingTimer?)
and then call it when isShowing() returns true. With no other changes. |
Oh, you're right. I just realized I misunderstood the purpose of But then, @ilya-fedin, could you help me understand the following piece of code: void Manager::startAllHiding() {
if (!hasReplyingNotification()) {
int notHidingCount = 0;
for (const auto ¬ification : _notifications) {
if (notification->isShowing()) {
++notHidingCount;
} else {
notification->startHiding();
}
}
// ... What is the purpose of the inner condition? Why does the manager refuse to hide a notification that is fading in? Seems like it is exactly what is causing the issue: if the cursor leaves the notification too quickly,
But why should the behavior be different depending on whether the notification is fadingIn or not? |
Btw, I would argue that |
I guess its purpose is exactly preventing hiding while it's still showing. Apparently to give the user more time to decide whether he wants to click on the notification. The current behavior of not hiding at all may just be intended then...
I understand it as "in the process of showing". For your explanation, I would expect the naming to be |
I asked @john-preston whether it's a bug and whether it should be fixed with just removing the condition or starting |
372a75d
to
a00fac7
Compare
…ently Previously, `Window::Notifications::Default::Manager` would not start hiding notifications that are fading in when other notifications should hide. This would lead to some notifications never hiding, e.g., when the cursor passes through the notification too quickly and there was not enough time for the notification to fade in completely. Also renamed `Widget::isShowing` -> `Widget::isFadingIn` for clarity. Fixes telegramdesktop#28811.
When a notification is to stop hiding (i.e., fade in), it is support to start fading in from whatever opacity it has at the moment, not from 0. Fixed that part to call the `.change()` method instead of `.start()` and added a clarification comment. When a notification is to start hiding (i.e., fade out), it is supposed to start fading out from the maximum opacity, even if it was not fully restored (which only happens if the cursor passed through the notification too quickly). Left the call to `.start()` and added a clarification comment.
@ilya-fedin, I have fixed the discussed issues (and updated both desktop-app/lib_ui#254 and this PR). Please, see the new changes |
bool isShowing() const { | ||
bool isFadingIn() const { | ||
return _a_opacity.animating() && !_hiding; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can see, this method is not used anywhere anymore. Should we remove it?
Do you really want to have animation APIs change in the scope of this PR? I haven't noticed any problems without animation changes, it continues to disappear just fine from the opacity it had. Your changes might affect other animation API usages and encounter regressions in places where the old behavior assumed... |
There is one: if while a notification is disappearing (i.e., halfway through the fade out), it is again crossed with the cursor, it will be fading in while the cursor is on it, but if the cursor leaves too quickly (before 150ms), the notification is not fully restored yet at that point. Without the lib_ui change, in that case, the notification will start fading out from the partially restored state; with the change, the moment the cursor leaves, the notification is reset to opacity 1, regardless of how long the cursor spent in the notification area.
I guess, the only ways to ensure that is by either checking other uses, or testing. For the last few days I am running a patched version of Telegram Desktop (with my fixes applied), and it seems fine. I want to additionally note that the behavior is only changed in the case when there was an old animation, which has not finished yet, and another one is started with |
And?.. I built with such change:
Here's what I see: _20250103_212752.mp4I don't see any issue with the animation... |
Same for me. I don't think this behavior is correct. In my understanding, if my cursor has crossed a notification, the notification should reset completely and start fading out from the initial state (i.e., opacity 1). That's what former experience with Telegram Desktop felt like: if a notification is fading out already and I want to keep it on the screen but my cursor happens to be too far, I can just rush and cross the notification (without making the it precise enough to stay in the notification area) and that will keep it. |
Hm. How your change makes it happen? There's just no time for it to fade in completely. And, well, that's why I proposed to make a new method that would start the _hideTimer: it starts hiding later, after it's completely shown. |
Do you mean how I make notifications fade in completely? I fix the behavior of the
I agree that this would also lead to the desired behavior. But I wonder if this is the right solution. I guess, the key question here is what kind of behavior of lib_ui is right. In my understanding, if there is an ongoing animation, and the |
I.e. you make it jump to opaque rather animate? |
It switches instantly to the value of |
No need to, I already tried your branch |
You have to wait @john-preston's review now |
In this PR, for
Window::Notification::Default::internal::Widget
I:.isShowing()
to.isFadingIn()
to clarify its behavior;Ui::Animations::Simple::{start,change}
in notifications hiding/unhiding animations.Is blocked by desktop-app/lib_ui#254. Fixes #28811.
Kindly, see the commit messages for details.