Added initial controller of message sending animations.
This commit is contained in:
parent
cb7da60ec7
commit
b46adec413
|
@ -1125,6 +1125,9 @@ PRIVATE
|
||||||
ui/chat/choose_theme_controller.h
|
ui/chat/choose_theme_controller.h
|
||||||
ui/effects/fireworks_animation.cpp
|
ui/effects/fireworks_animation.cpp
|
||||||
ui/effects/fireworks_animation.h
|
ui/effects/fireworks_animation.h
|
||||||
|
ui/effects/message_sending_animation_common.h
|
||||||
|
ui/effects/message_sending_animation_controller.cpp
|
||||||
|
ui/effects/message_sending_animation_controller.h
|
||||||
ui/effects/round_checkbox.cpp
|
ui/effects/round_checkbox.cpp
|
||||||
ui/effects/round_checkbox.h
|
ui/effects/round_checkbox.h
|
||||||
ui/effects/send_action_animations.cpp
|
ui/effects/send_action_animations.cpp
|
||||||
|
|
|
@ -138,9 +138,12 @@ void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const {
|
||||||
height() - st::msgDateImgPadding.y() * 2 - st::msgDateFont->height)
|
height() - st::msgDateImgPadding.y() * 2 - st::msgDateFont->height)
|
||||||
: _contentSize.height();
|
: _contentSize.height();
|
||||||
const auto inner = QRect(usex, usey, usew, useh);
|
const auto inner = QRect(usex, usey, usew, useh);
|
||||||
_content->draw(p, context, inner);
|
if (context.skipDrawingParts != PaintContext::SkipDrawingParts::Content) {
|
||||||
|
_content->draw(p, context, inner);
|
||||||
|
}
|
||||||
|
|
||||||
if (!inWebPage) {
|
if (!inWebPage && (context.skipDrawingParts
|
||||||
|
!= PaintContext::SkipDrawingParts::Surrounding)) {
|
||||||
drawSurrounding(p, inner, context, via, reply, forwarded);
|
drawSurrounding(p, inner, context, via, reply, forwarded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,6 +135,14 @@ struct ChatPaintContext {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is supported only in unwrapped media for now.
|
||||||
|
enum class SkipDrawingParts {
|
||||||
|
None,
|
||||||
|
Content,
|
||||||
|
Surrounding,
|
||||||
|
};
|
||||||
|
SkipDrawingParts skipDrawingParts = SkipDrawingParts::None;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] int HistoryServiceMsgRadius();
|
[[nodiscard]] int HistoryServiceMsgRadius();
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
struct MessageSendingAnimationFrom {
|
||||||
|
std::optional<MsgId> localId;
|
||||||
|
QRect globalStartGeometry;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
|
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/view/history_view_list_widget.h" // kItemRevealDuration
|
||||||
|
#include "history/view/media/history_view_media.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/chat/chat_theme.h"
|
||||||
|
#include "ui/effects/animation_value.h"
|
||||||
|
#include "ui/effects/animation_value_f.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Content final : public RpWidget {
|
||||||
|
public:
|
||||||
|
Content(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
QRect globalGeometryFrom,
|
||||||
|
MessageSendingAnimationController::SendingInfoTo &&to);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> destroyRequests() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateCache();
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
not_null<HistoryItem*> _item;
|
||||||
|
not_null<ChatTheme*> _theme;
|
||||||
|
not_null<HistoryView::Media*> _media;
|
||||||
|
QImage _cache;
|
||||||
|
QRect _from;
|
||||||
|
QRect _to;
|
||||||
|
|
||||||
|
Animations::Simple _animation;
|
||||||
|
float64 _minScale = 0;
|
||||||
|
|
||||||
|
rpl::event_stream<> _destroyRequests;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Content::Content(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
QRect globalGeometryFrom,
|
||||||
|
MessageSendingAnimationController::SendingInfoTo &&to)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _controller(controller)
|
||||||
|
, _item(to.item)
|
||||||
|
, _theme(to.theme)
|
||||||
|
, _media(_item->mainView()->media())
|
||||||
|
, _from(parent->mapFromGlobal(globalGeometryFrom)) {
|
||||||
|
|
||||||
|
show();
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
raise();
|
||||||
|
|
||||||
|
base::take(
|
||||||
|
to.globalEndGeometry
|
||||||
|
) | rpl::distinct_until_changed(
|
||||||
|
) | rpl::start_with_next([=](const QRect &r) {
|
||||||
|
_to = parent->mapFromGlobal(r);
|
||||||
|
_minScale = float64(_from.height()) / _to.height();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
updateCache();
|
||||||
|
|
||||||
|
_controller->session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
updateCache();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
const auto innerContentRect = _media->contentRectForReactions();
|
||||||
|
auto animationCallback = [=](float64 value) {
|
||||||
|
auto resultFrom = QRect(
|
||||||
|
QPoint(),
|
||||||
|
_cache.size() / style::DevicePixelRatio());
|
||||||
|
resultFrom.moveCenter(_from.center());
|
||||||
|
|
||||||
|
const auto resultTo = _to.topLeft() + innerContentRect.topLeft();
|
||||||
|
moveToLeft(
|
||||||
|
anim::interpolate(resultFrom.x(), resultTo.x(), value),
|
||||||
|
anim::interpolate(resultFrom.y(), resultTo.y(), value));
|
||||||
|
update();
|
||||||
|
|
||||||
|
if (value == 1.) {
|
||||||
|
_destroyRequests.fire({});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
animationCallback(0.);
|
||||||
|
_animation.start(
|
||||||
|
std::move(animationCallback),
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
HistoryView::ListWidget::kItemRevealDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Content::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), Qt::transparent);
|
||||||
|
|
||||||
|
if (_cache.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto progress = _animation.value(_animation.animating() ? 0. : 1.);
|
||||||
|
|
||||||
|
const auto scale = anim::interpolateF(_minScale, 1., progress);
|
||||||
|
|
||||||
|
const auto size = _cache.size() / style::DevicePixelRatio();
|
||||||
|
p.translate(
|
||||||
|
(1 - progress) * ((size.width() - (size.width() * _minScale)) / 2),
|
||||||
|
(1 - progress) * ((size.height() - (size.height() * _minScale)) / 2));
|
||||||
|
p.scale(scale, scale);
|
||||||
|
p.drawImage(QPoint(), _cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Content::destroyRequests() const {
|
||||||
|
return _destroyRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Content::updateCache() {
|
||||||
|
const auto innerContentRect = _media->contentRectForReactions();
|
||||||
|
_cache = QImage(
|
||||||
|
innerContentRect.size() * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_cache.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
_cache.fill(Qt::transparent);
|
||||||
|
{
|
||||||
|
Painter p(&_cache);
|
||||||
|
PainterHighQualityEnabler hq(p);
|
||||||
|
|
||||||
|
auto context = _controller->preparePaintContext({
|
||||||
|
.theme = _theme,
|
||||||
|
});
|
||||||
|
using Context = Ui::ChatPaintContext;
|
||||||
|
context.skipDrawingParts = Context::SkipDrawingParts::Surrounding;
|
||||||
|
context.outbg = true;
|
||||||
|
p.translate(-innerContentRect.left(), -innerContentRect.top());
|
||||||
|
_media->draw(p, context);
|
||||||
|
}
|
||||||
|
resize(
|
||||||
|
_cache.width() / style::DevicePixelRatio(),
|
||||||
|
_cache.height() / style::DevicePixelRatio());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
MessageSendingAnimationController::MessageSendingAnimationController(
|
||||||
|
not_null<Window::SessionController*> controller)
|
||||||
|
: _controller(controller) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageSendingAnimationController::appendSending(
|
||||||
|
MessageSendingAnimationFrom from) {
|
||||||
|
if (anim::Disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (from.localId) {
|
||||||
|
_itemSendPending[*from.localId] = from.globalStartGeometry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageSendingAnimationController::startAnimation(SendingInfoTo &&to) {
|
||||||
|
if (anim::Disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto container = _controller->content();
|
||||||
|
const auto item = to.item;
|
||||||
|
|
||||||
|
const auto it = _itemSendPending.find(item->fullId().msg);
|
||||||
|
if (it == end(_itemSendPending)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto msg = it->first;
|
||||||
|
|
||||||
|
auto content = base::make_unique_q<Content>(
|
||||||
|
container,
|
||||||
|
_controller,
|
||||||
|
it->second,
|
||||||
|
std::move(to));
|
||||||
|
content->destroyRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_itemSendPending.erase(msg);
|
||||||
|
_processing.erase(item);
|
||||||
|
}, content->lifetime());
|
||||||
|
|
||||||
|
_processing.emplace(item, std::move(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageSendingAnimationController::hasLocalMessage(MsgId msgId) const {
|
||||||
|
return _itemSendPending.contains(msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageSendingAnimationController::hasAnimatedMessage(
|
||||||
|
not_null<HistoryItem*> item) const {
|
||||||
|
return _processing.contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/unique_qptr.h"
|
||||||
|
#include "ui/effects/message_sending_animation_common.h"
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class SessionController;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class RpWidget;
|
||||||
|
class ChatTheme;
|
||||||
|
|
||||||
|
class MessageSendingAnimationController final {
|
||||||
|
public:
|
||||||
|
explicit MessageSendingAnimationController(
|
||||||
|
not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
struct SendingInfoTo {
|
||||||
|
rpl::producer<QRect> globalEndGeometry;
|
||||||
|
not_null<HistoryItem*> item;
|
||||||
|
not_null<Ui::ChatTheme*> theme;
|
||||||
|
};
|
||||||
|
|
||||||
|
void appendSending(MessageSendingAnimationFrom from);
|
||||||
|
void startAnimation(SendingInfoTo &&to);
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasLocalMessage(MsgId msgId) const;
|
||||||
|
[[nodiscard]] bool hasAnimatedMessage(not_null<HistoryItem*> item) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
base::flat_map<MsgId, QRect> _itemSendPending;
|
||||||
|
|
||||||
|
base::flat_map<
|
||||||
|
not_null<HistoryItem*>,
|
||||||
|
base::unique_qptr<RpWidget>> _processing;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/message_bubble.h"
|
#include "ui/chat/message_bubble.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/chat/chat_theme.h"
|
#include "ui/chat/chat_theme.h"
|
||||||
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
#include "ui/style/style_palette_colorizer.h"
|
#include "ui/style/style_palette_colorizer.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/toasts/common_toasts.h"
|
#include "ui/toasts/common_toasts.h"
|
||||||
|
@ -526,6 +527,8 @@ SessionController::SessionController(
|
||||||
, _window(window)
|
, _window(window)
|
||||||
, _emojiInteractions(
|
, _emojiInteractions(
|
||||||
std::make_unique<ChatHelpers::EmojiInteractions>(session))
|
std::make_unique<ChatHelpers::EmojiInteractions>(session))
|
||||||
|
, _sendingAnimation(
|
||||||
|
std::make_unique<Ui::MessageSendingAnimationController>(this))
|
||||||
, _tabbedSelector(
|
, _tabbedSelector(
|
||||||
std::make_unique<ChatHelpers::TabbedSelector>(
|
std::make_unique<ChatHelpers::TabbedSelector>(
|
||||||
_window->widget(),
|
_window->widget(),
|
||||||
|
@ -627,6 +630,11 @@ not_null<::MainWindow*> SessionController::widget() const {
|
||||||
return _window->widget();
|
return _window->widget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SessionController::sendingAnimation() const
|
||||||
|
-> Ui::MessageSendingAnimationController & {
|
||||||
|
return *_sendingAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
auto SessionController::tabbedSelector() const
|
auto SessionController::tabbedSelector() const
|
||||||
-> not_null<ChatHelpers::TabbedSelector*> {
|
-> not_null<ChatHelpers::TabbedSelector*> {
|
||||||
return _tabbedSelector.get();
|
return _tabbedSelector.get();
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct ChatThemeKey;
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
struct ChatThemeBackground;
|
struct ChatThemeBackground;
|
||||||
struct ChatThemeBackgroundData;
|
struct ChatThemeBackgroundData;
|
||||||
|
class MessageSendingAnimationController;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -279,6 +280,8 @@ public:
|
||||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther,
|
Ui::LayerOptions options = Ui::LayerOption::KeepOther,
|
||||||
anim::type animated = anim::type::normal);
|
anim::type animated = anim::type::normal);
|
||||||
|
|
||||||
|
[[nodiscard]] auto sendingAnimation() const
|
||||||
|
-> Ui::MessageSendingAnimationController &;
|
||||||
[[nodiscard]] auto tabbedSelector() const
|
[[nodiscard]] auto tabbedSelector() const
|
||||||
-> not_null<ChatHelpers::TabbedSelector*>;
|
-> not_null<ChatHelpers::TabbedSelector*>;
|
||||||
void takeTabbedSelectorOwnershipFrom(not_null<QWidget*> parent);
|
void takeTabbedSelectorOwnershipFrom(not_null<QWidget*> parent);
|
||||||
|
@ -509,6 +512,9 @@ private:
|
||||||
const not_null<Controller*> _window;
|
const not_null<Controller*> _window;
|
||||||
const std::unique_ptr<ChatHelpers::EmojiInteractions> _emojiInteractions;
|
const std::unique_ptr<ChatHelpers::EmojiInteractions> _emojiInteractions;
|
||||||
|
|
||||||
|
using SendingAnimation = Ui::MessageSendingAnimationController;
|
||||||
|
const std::unique_ptr<SendingAnimation> _sendingAnimation;
|
||||||
|
|
||||||
std::unique_ptr<Passport::FormController> _passportForm;
|
std::unique_ptr<Passport::FormController> _passportForm;
|
||||||
std::unique_ptr<FiltersMenu> _filters;
|
std::unique_ptr<FiltersMenu> _filters;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue