mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Added simple animation of GIFs sending.
This commit is contained in:
parent
0dc2e1a5ae
commit
2e40798e8e
8 changed files with 192 additions and 16 deletions
|
@ -441,6 +441,19 @@ void GifsListWidget::selectInlineResult(
|
|||
return;
|
||||
}
|
||||
|
||||
const auto messageSendingFrom = [&] {
|
||||
if (options.scheduled) {
|
||||
return Ui::MessageSendingAnimationFrom();
|
||||
}
|
||||
const auto rect = item->innerContentRect().translated(
|
||||
_mosaic.findRect(index).topLeft());
|
||||
return Ui::MessageSendingAnimationFrom{
|
||||
.localId = controller()->session().data().nextLocalMessageId(),
|
||||
.globalStartGeometry = mapToGlobal(rect),
|
||||
.crop = true,
|
||||
};
|
||||
};
|
||||
|
||||
forceSend |= base::IsCtrlPressed();
|
||||
if (const auto photo = item->getPhoto()) {
|
||||
using Data::PhotoSize;
|
||||
|
@ -448,7 +461,7 @@ void GifsListWidget::selectInlineResult(
|
|||
if (forceSend
|
||||
|| (media && media->image(PhotoSize::Thumbnail))
|
||||
|| (media && media->image(PhotoSize::Large))) {
|
||||
_photoChosen.fire_copy({
|
||||
_photoChosen.fire({
|
||||
.photo = photo,
|
||||
.options = options });
|
||||
} else if (!photo->loading(PhotoSize::Thumbnail)) {
|
||||
|
@ -458,9 +471,11 @@ void GifsListWidget::selectInlineResult(
|
|||
const auto media = document->activeMediaView();
|
||||
const auto preview = Data::VideoPreviewState(media.get());
|
||||
if (forceSend || (media && preview.loaded())) {
|
||||
_fileChosen.fire_copy({
|
||||
_fileChosen.fire({
|
||||
.document = document,
|
||||
.options = options });
|
||||
.options = options,
|
||||
.messageSendingFrom = messageSendingFrom(),
|
||||
});
|
||||
} else if (!preview.usingThumbnail()) {
|
||||
if (preview.loading()) {
|
||||
document->cancel();
|
||||
|
@ -472,7 +487,12 @@ void GifsListWidget::selectInlineResult(
|
|||
}
|
||||
} else if (const auto inlineResult = item->getResult()) {
|
||||
if (inlineResult->onChoose(item)) {
|
||||
_inlineResultChosen.fire({ inlineResult, _searchBot, options });
|
||||
_inlineResultChosen.fire({
|
||||
.result = inlineResult,
|
||||
.bot = _searchBot,
|
||||
.options = options,
|
||||
.messageSendingFrom = messageSendingFrom(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2758,6 +2758,29 @@ QRect Message::innerGeometry() const {
|
|||
w + rightActionSize().value_or(QSize(0, 0)).width() * 2,
|
||||
width()));
|
||||
}
|
||||
if (hasBubble()) {
|
||||
result.translate(0, st::msgPadding.top() + st::mediaInBubbleSkip);
|
||||
|
||||
if (displayFromName()) {
|
||||
// See paintFromName().
|
||||
result.translate(0, st::msgNameFont->height);
|
||||
}
|
||||
// Skip displayForwardedFrom() until there are no animations for it.
|
||||
if (displayedReply()) {
|
||||
// See paintReplyInfo().
|
||||
result.translate(
|
||||
0,
|
||||
st::msgReplyPadding.top()
|
||||
+ st::msgReplyBarSize.height()
|
||||
+ st::msgReplyPadding.bottom());
|
||||
}
|
||||
if (!displayFromName() && !displayForwardedFrom()) {
|
||||
// See paintViaBotIdInfo().
|
||||
if (message()->Has<HistoryMessageVia>()) {
|
||||
result.translate(0, st::msgServiceNameFont->height);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,6 +139,15 @@ int Gif::resizeGetHeight(int width) {
|
|||
return _height;
|
||||
}
|
||||
|
||||
QRect Gif::innerContentRect() const {
|
||||
ensureDataMediaCreated(getShownDocument());
|
||||
|
||||
const auto size = (!_thumb.isNull())
|
||||
? (_thumb.size() / style::DevicePixelRatio())
|
||||
: countFrameSize();
|
||||
return QRect(QPoint(), size);
|
||||
}
|
||||
|
||||
void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
|
||||
const auto document = getShownDocument();
|
||||
ensureDataMediaCreated(document);
|
||||
|
|
|
@ -86,6 +86,8 @@ public:
|
|||
|
||||
void unloadHeavyPart() override;
|
||||
|
||||
QRect innerContentRect() const override;
|
||||
|
||||
private:
|
||||
enum class StateFlag {
|
||||
Over = (1 << 0),
|
||||
|
|
|
@ -270,7 +270,10 @@ void Inner::selectInlineResult(
|
|||
const auto document = item->getDocument()
|
||||
? item->getDocument()
|
||||
: item->getPreviewDocument();
|
||||
if (options.scheduled || !document || !document->sticker()) {
|
||||
if (options.scheduled
|
||||
|| item->isFullLine()
|
||||
|| !document
|
||||
|| (!document->sticker() && !document->isGifv())) {
|
||||
return {};
|
||||
}
|
||||
const auto rect = item->innerContentRect().translated(
|
||||
|
@ -278,6 +281,7 @@ void Inner::selectInlineResult(
|
|||
return {
|
||||
.localId = _controller->session().data().nextLocalMessageId(),
|
||||
.globalStartGeometry = mapToGlobal(rect),
|
||||
.crop = document->isGifv(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ui {
|
|||
struct MessageSendingAnimationFrom {
|
||||
std::optional<MsgId> localId;
|
||||
QRect globalStartGeometry;
|
||||
bool crop = false;
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/animations.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
@ -36,7 +37,7 @@ public:
|
|||
Content(
|
||||
not_null<RpWidget*> parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
QRect globalGeometryFrom,
|
||||
const MessageSendingAnimationFrom &fromInfo,
|
||||
MessageSendingAnimationController::SendingInfoTo &&to);
|
||||
|
||||
[[nodiscard]] rpl::producer<> destroyRequests() const;
|
||||
|
@ -48,9 +49,12 @@ private:
|
|||
using Context = Ui::ChatPaintContext;
|
||||
|
||||
void createSurrounding();
|
||||
void createBubble();
|
||||
not_null<HistoryView::Element*> view() const;
|
||||
void drawContent(Painter &p, float64 progress) const;
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const bool _crop;
|
||||
MessageSendingAnimationController::SendingInfoTo _toInfo;
|
||||
QRect _from;
|
||||
QPoint _to;
|
||||
|
@ -59,6 +63,10 @@ private:
|
|||
Animations::Simple _animation;
|
||||
float64 _minScale = 0;
|
||||
|
||||
struct {
|
||||
base::unique_qptr<Ui::RpWidget> widget;
|
||||
QPoint offsetFromContent;
|
||||
} _bubble;
|
||||
base::unique_qptr<Ui::RpWidget> _surrounding;
|
||||
|
||||
rpl::event_stream<> _destroyRequests;
|
||||
|
@ -68,12 +76,13 @@ private:
|
|||
Content::Content(
|
||||
not_null<RpWidget*> parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
QRect globalGeometryFrom,
|
||||
const MessageSendingAnimationFrom &fromInfo,
|
||||
MessageSendingAnimationController::SendingInfoTo &&to)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _crop(fromInfo.crop)
|
||||
, _toInfo(std::move(to))
|
||||
, _from(parent->mapFromGlobal(globalGeometryFrom))
|
||||
, _from(parent->mapFromGlobal(fromInfo.globalStartGeometry))
|
||||
, _innerContentRect(view()->media()->contentRectForReactions())
|
||||
, _minScale(float64(_from.height()) / _innerContentRect.height()) {
|
||||
Expects(_toInfo.view != nullptr);
|
||||
|
@ -111,14 +120,25 @@ Content::Content(
|
|||
moveToLeft(x, y);
|
||||
update();
|
||||
|
||||
if ((value > kSurroundingProgress) && !_surrounding) {
|
||||
createSurrounding();
|
||||
if ((value > kSurroundingProgress)
|
||||
&& !_surrounding
|
||||
&& !_bubble.widget) {
|
||||
if (view()->hasBubble()) {
|
||||
createBubble();
|
||||
} else {
|
||||
createSurrounding();
|
||||
}
|
||||
}
|
||||
if (_surrounding) {
|
||||
_surrounding->moveToLeft(
|
||||
x - _innerContentRect.x(),
|
||||
y - _innerContentRect.y());
|
||||
}
|
||||
if (_bubble.widget) {
|
||||
_bubble.widget->moveToLeft(
|
||||
x - _bubble.offsetFromContent.x(),
|
||||
y - _bubble.offsetFromContent.y());
|
||||
}
|
||||
|
||||
if (value == 1.) {
|
||||
const auto currentView = view();
|
||||
|
@ -140,12 +160,50 @@ not_null<HistoryView::Element*> Content::view() const {
|
|||
}
|
||||
|
||||
void Content::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.fillRect(e->rect(), Qt::transparent);
|
||||
|
||||
const auto progress = _animation.value(_animation.animating() ? 0. : 1.);
|
||||
|
||||
if (!_crop) {
|
||||
Painter p(this);
|
||||
p.fillRect(e->rect(), Qt::transparent);
|
||||
drawContent(p, progress);
|
||||
} else {
|
||||
// Use QImage to make CompositionMode_Clear work.
|
||||
auto image = QImage(
|
||||
size() * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
image.fill(Qt::transparent);
|
||||
|
||||
const auto scaledFromSize = _from.size().scaled(
|
||||
_innerContentRect.size(),
|
||||
Qt::KeepAspectRatio);
|
||||
const auto cropW = std::ceil(
|
||||
(_innerContentRect.width() - scaledFromSize.width())
|
||||
/ 2.
|
||||
* (1. - std::clamp(progress / kSurroundingProgress, 0., 1.)));
|
||||
|
||||
{
|
||||
Painter p(&image);
|
||||
drawContent(p, progress);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Clear);
|
||||
p.fillRect(
|
||||
QRect(0, 0, cropW, _innerContentRect.height()),
|
||||
Qt::black);
|
||||
p.fillRect(
|
||||
QRect(
|
||||
_innerContentRect.width() - cropW,
|
||||
0,
|
||||
cropW,
|
||||
_innerContentRect.height()),
|
||||
Qt::black);
|
||||
}
|
||||
|
||||
Painter p(this);
|
||||
p.drawImage(QPoint(), std::move(image));
|
||||
}
|
||||
}
|
||||
|
||||
void Content::drawContent(Painter &p, float64 progress) const {
|
||||
const auto scale = anim::interpolateF(_minScale, 1., progress);
|
||||
|
||||
p.translate(
|
||||
|
@ -211,6 +269,65 @@ void Content::createSurrounding() {
|
|||
}, _surrounding->lifetime());
|
||||
}
|
||||
|
||||
void Content::createBubble() {
|
||||
_bubble.widget = base::make_unique_q<Ui::RpWidget>(parentWidget());
|
||||
_bubble.widget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto currentView = view();
|
||||
const auto innerGeometry = currentView->innerGeometry();
|
||||
|
||||
const auto tailWidth = st::historyBubbleTailOutLeft.width();
|
||||
_bubble.offsetFromContent = QPoint(
|
||||
currentView->hasOutLayout() ? 0 : tailWidth,
|
||||
innerGeometry.y());
|
||||
|
||||
const auto scaleOffset = QPoint(0, innerGeometry.y());
|
||||
const auto paintOffsetLeft = innerGeometry.x()
|
||||
- _bubble.offsetFromContent.x();
|
||||
|
||||
const auto hasCommentsButton = currentView->data()->repliesAreComments()
|
||||
|| currentView->data()->externalReply();
|
||||
_bubble.widget->resize(innerGeometry.size()
|
||||
+ QSize(
|
||||
currentView->hasOutLayout() ? tailWidth : 0,
|
||||
hasCommentsButton ? innerGeometry.y() : 0));
|
||||
_bubble.widget->show();
|
||||
|
||||
_bubble.widget->stackUnder(this);
|
||||
|
||||
_bubble.widget->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
Painter p(_bubble.widget);
|
||||
|
||||
p.fillRect(r, Qt::transparent);
|
||||
|
||||
const auto progress = _animation.value(0.);
|
||||
const auto revProgress = 1. - progress;
|
||||
|
||||
const auto divider = 1. - kSurroundingProgress;
|
||||
const auto alpha = (divider - revProgress) / divider;
|
||||
p.setOpacity(alpha);
|
||||
|
||||
const auto scale = anim::interpolateF(_minScale, 1., progress);
|
||||
|
||||
p.translate(
|
||||
revProgress * OffsetMid(width() + scaleOffset.x(), _minScale),
|
||||
revProgress * OffsetMid(height() + scaleOffset.y(), _minScale));
|
||||
p.scale(scale, scale);
|
||||
|
||||
const auto currentView = view();
|
||||
|
||||
auto context = _toInfo.paintContext();
|
||||
context.skipDrawingParts = Context::SkipDrawingParts::Content;
|
||||
context.outbg = currentView->hasOutLayout();
|
||||
|
||||
context.translate(paintOffsetLeft, 0);
|
||||
p.translate(-paintOffsetLeft, 0);
|
||||
|
||||
currentView->draw(p, context);
|
||||
}, _bubble.widget->lifetime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MessageSendingAnimationController::MessageSendingAnimationController(
|
||||
|
@ -224,7 +341,7 @@ void MessageSendingAnimationController::appendSending(
|
|||
return;
|
||||
}
|
||||
if (from.localId) {
|
||||
_itemSendPending[*from.localId] = from.globalStartGeometry;
|
||||
_itemSendPending[*from.localId] = std::move(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
private:
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
base::flat_map<MsgId, QRect> _itemSendPending;
|
||||
base::flat_map<MsgId, MessageSendingAnimationFrom> _itemSendPending;
|
||||
|
||||
base::flat_map<
|
||||
not_null<HistoryItem*>,
|
||||
|
|
Loading…
Add table
Reference in a new issue