mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Added reply header with preview to send files box.
This commit is contained in:
@@ -356,6 +356,8 @@ PRIVATE
|
||||
boxes/send_gif_with_caption_box.h
|
||||
boxes/send_files_box.cpp
|
||||
boxes/send_files_box.h
|
||||
boxes/send_files_box_reply_header.cpp
|
||||
boxes/send_files_box_reply_header.h
|
||||
boxes/share_box.cpp
|
||||
boxes/share_box.h
|
||||
boxes/star_gift_auction_box.cpp
|
||||
|
||||
@@ -59,6 +59,7 @@ boxPhotoTitlePosition: point(28px, 20px);
|
||||
boxPhotoPadding: margins(28px, 28px, 28px, 18px);
|
||||
boxPhotoCompressedSkip: 20px;
|
||||
boxPhotoCaptionSkip: 8px;
|
||||
boxPhotoCaptionReplyOverlap: 5px;
|
||||
|
||||
defaultChangeUserpicIcon: icon {{ "new_chat_photo", activeButtonFg }};
|
||||
defaultUploadUserpicIcon: icon {{ "upload_chat_photo", msgDateImgFg }};
|
||||
|
||||
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "boxes/send_gif_with_caption_box.h"
|
||||
#include "boxes/send_credits_box.h"
|
||||
#include "boxes/send_files_box_reply_header.h"
|
||||
#include "ui/effects/scroll_content_shadow.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -429,6 +430,10 @@ int SendFilesBox::Block::fromIndex() const {
|
||||
return _from;
|
||||
}
|
||||
|
||||
bool SendFilesBox::Block::isSingleFile() const {
|
||||
return !_isAlbum && !_isSingleMedia;
|
||||
}
|
||||
|
||||
int SendFilesBox::Block::tillIndex() const {
|
||||
return _till;
|
||||
}
|
||||
@@ -646,9 +651,58 @@ SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor)
|
||||
, _inner(
|
||||
_scroll->setOwnedWidget(
|
||||
object_ptr<Ui::VerticalLayout>(_scroll.data()))) {
|
||||
setReplyTo(descriptor.replyTo);
|
||||
enqueueNextPrepare();
|
||||
}
|
||||
|
||||
void SendFilesBox::setReplyTo(FullReplyTo replyTo) {
|
||||
if (_replyTo == replyTo) {
|
||||
return;
|
||||
} else if (!replyTo.messageId || !replyTo.messageId.peer) {
|
||||
_replyTo = {};
|
||||
if (_replyHeader) {
|
||||
_replyHeader->hideAnimated();
|
||||
}
|
||||
return;
|
||||
}
|
||||
_replyTo = replyTo;
|
||||
if (_replyHeader) {
|
||||
_replyHeader = nullptr;
|
||||
_replyHeaderHeight = 0;
|
||||
}
|
||||
_replyHeader = std::make_unique<SendFiles::ReplyPillHeader>(
|
||||
this,
|
||||
_show,
|
||||
std::move(replyTo));
|
||||
_replyHeader->setRoundedShapeBelow(
|
||||
!_blocks.empty() && !_blocks.front().isSingleFile());
|
||||
_replyHeader->show();
|
||||
_replyHeader->desiredHeight(
|
||||
) | rpl::on_next([=](int height) {
|
||||
if (_replyHeaderHeight.current() != height) {
|
||||
_replyHeaderHeight = height;
|
||||
updateBoxSize();
|
||||
updateControlsGeometry();
|
||||
}
|
||||
}, _replyHeader->lifetime());
|
||||
_replyHeader->closeRequests(
|
||||
) | rpl::on_next([=] {
|
||||
_replyTo = {};
|
||||
if (_replyHeader) {
|
||||
_replyHeader->hideAnimated();
|
||||
}
|
||||
}, _replyHeader->lifetime());
|
||||
_replyHeader->hideFinished(
|
||||
) | rpl::on_next([=] {
|
||||
InvokeQueued(this, [=] {
|
||||
_replyHeader = nullptr;
|
||||
_replyHeaderHeight = 0;
|
||||
updateBoxSize();
|
||||
updateControlsGeometry();
|
||||
});
|
||||
}, _replyHeader->lifetime());
|
||||
}
|
||||
|
||||
Fn<SendMenu::Details()> SendFilesBox::prepareSendMenuDetails(
|
||||
const SendFilesBoxDescriptor &descriptor) {
|
||||
auto initial = descriptor.sendMenuDetails;
|
||||
@@ -1219,6 +1273,10 @@ void SendFilesBox::generatePreviewFrom(int fromBlock) {
|
||||
if (albumStart >= 0) {
|
||||
pushBlock(albumStart, _list.files.size());
|
||||
}
|
||||
if (_replyHeader) {
|
||||
_replyHeader->setRoundedShapeBelow(
|
||||
!_blocks.empty() && !_blocks.front().isSingleFile());
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::pushBlock(int from, int till) {
|
||||
@@ -2083,6 +2141,7 @@ void SendFilesBox::updateBoxSize() {
|
||||
if (!_caption->isHidden()) {
|
||||
footerHeight += st::boxPhotoCaptionSkip + _caption->height();
|
||||
}
|
||||
footerHeight += _replyHeaderHeight.current();
|
||||
const auto pairs = std::array<std::pair<RpWidget*, int>, 4>{ {
|
||||
{ _groupFiles.data(), st::boxPhotoCompressedSkip },
|
||||
{ _sendImagesAsPhotos.data(), st::boxPhotoCompressedSkip },
|
||||
@@ -2170,8 +2229,14 @@ void SendFilesBox::updateControlsGeometry() {
|
||||
bottom -= pair.second + pointer->heightNoMargins();
|
||||
}
|
||||
}
|
||||
_scroll->resize(width(), bottom - _titleHeight.current());
|
||||
_scroll->move(0, _titleHeight.current());
|
||||
const auto replyH = _replyHeaderHeight.current();
|
||||
const auto replyTopOverlap = std::min(st::boxPhotoCaptionSkip, replyH);
|
||||
const auto replyTop = _titleHeight.current() - replyTopOverlap;
|
||||
if (_replyHeader) {
|
||||
_replyHeader->setGeometry(0, replyTop, width(), replyH);
|
||||
}
|
||||
_scroll->resize(width(), bottom - replyTop - replyH);
|
||||
_scroll->move(0, replyTop + replyH);
|
||||
}
|
||||
|
||||
void SendFilesBox::showFinished() {
|
||||
@@ -2316,7 +2381,7 @@ void SendFilesBox::send(
|
||||
}
|
||||
}
|
||||
|
||||
_confirmedCallback(std::move(bundle), options);
|
||||
_confirmedCallback(std::move(bundle), options, _replyTo);
|
||||
}
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "base/flags.h"
|
||||
#include "data/data_msg_id.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
@@ -62,6 +63,10 @@ class CharactersLimitLabel;
|
||||
class ComposeAiButton;
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
namespace SendFiles {
|
||||
class ReplyPillHeader;
|
||||
} // namespace SendFiles
|
||||
|
||||
enum class SendFilesAllow {
|
||||
OnlyOne = (1 << 0),
|
||||
Photos = (1 << 1),
|
||||
@@ -91,7 +96,8 @@ using SendFilesCheck = Fn<bool(
|
||||
|
||||
using SendFilesConfirmed = Fn<void(
|
||||
std::shared_ptr<Ui::PreparedBundle>,
|
||||
Api::SendOptions)>;
|
||||
Api::SendOptions,
|
||||
FullReplyTo)>;
|
||||
|
||||
struct SendFilesBoxDescriptor {
|
||||
std::shared_ptr<ChatHelpers::Show> show;
|
||||
@@ -105,6 +111,7 @@ struct SendFilesBoxDescriptor {
|
||||
const style::ComposeControls *stOverride = nullptr;
|
||||
SendFilesConfirmed confirmed;
|
||||
Fn<void()> cancelled;
|
||||
FullReplyTo replyTo;
|
||||
};
|
||||
|
||||
class SendFilesBox : public Ui::BoxContent {
|
||||
@@ -129,6 +136,7 @@ public:
|
||||
void setCancelledCallback(Fn<void()> callback) {
|
||||
_cancelledCallback = std::move(callback);
|
||||
}
|
||||
void setReplyTo(FullReplyTo replyTo);
|
||||
|
||||
[[nodiscard]] rpl::producer<TextWithTags> takeTextWithTagsRequests() const;
|
||||
|
||||
@@ -164,6 +172,7 @@ private:
|
||||
|
||||
[[nodiscard]] int fromIndex() const;
|
||||
[[nodiscard]] int tillIndex() const;
|
||||
[[nodiscard]] bool isSingleFile() const;
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> takeWidget();
|
||||
|
||||
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
|
||||
@@ -315,6 +324,10 @@ private:
|
||||
rpl::variable<int> _footerHeight = 0;
|
||||
rpl::lifetime _dimensionsLifetime;
|
||||
|
||||
std::unique_ptr<SendFiles::ReplyPillHeader> _replyHeader;
|
||||
rpl::variable<int> _replyHeaderHeight = 0;
|
||||
FullReplyTo _replyTo;
|
||||
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<Ui::VerticalLayout> _inner;
|
||||
std::deque<Block> _blocks;
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
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 "boxes/send_files_box_reply_header.h"
|
||||
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_reply.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/spoiler_mess.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace SendFiles {
|
||||
namespace {
|
||||
|
||||
constexpr auto kAnimationDuration = crl::time(180);
|
||||
|
||||
} // namespace
|
||||
|
||||
ReplyPillHeader::ReplyPillHeader(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FullReplyTo replyTo)
|
||||
: RpWidget(parent)
|
||||
, _show(std::move(show))
|
||||
, _data(&_show->session().data())
|
||||
, _replyTo(std::move(replyTo))
|
||||
, _cancel(Ui::CreateChild<Ui::IconButton>(this, st::sendFilesReplyCancel)) {
|
||||
resize(
|
||||
parent->width(),
|
||||
st::boxPhotoCaptionSkip + st::historyReplyHeight);
|
||||
|
||||
_cancel->setAccessibleName(tr::lng_cancel(tr::now));
|
||||
_cancel->setClickedCallback([=] {
|
||||
hideAnimated();
|
||||
});
|
||||
|
||||
setShownMessage(_data->message(_replyTo.messageId));
|
||||
|
||||
_data->session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::Edited
|
||||
| Data::MessageUpdate::Flag::Destroyed
|
||||
) | rpl::filter([=](const Data::MessageUpdate &update) {
|
||||
return (update.item == _shownMessage);
|
||||
}) | rpl::on_next([=](const Data::MessageUpdate &update) {
|
||||
if (update.flags & Data::MessageUpdate::Flag::Destroyed) {
|
||||
_shownMessage = nullptr;
|
||||
_shownMessageName.clear();
|
||||
_shownMessageText.clear();
|
||||
hideAnimated();
|
||||
} else {
|
||||
updateShownMessageText();
|
||||
RpWidget::update();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
animationCallback();
|
||||
}
|
||||
|
||||
ReplyPillHeader::~ReplyPillHeader() = default;
|
||||
|
||||
rpl::producer<> ReplyPillHeader::closeRequests() const {
|
||||
return _closeRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> ReplyPillHeader::hideFinished() const {
|
||||
return _hideFinished.value()
|
||||
| rpl::filter(rpl::mappers::_1)
|
||||
| rpl::to_empty;
|
||||
}
|
||||
|
||||
rpl::producer<int> ReplyPillHeader::desiredHeight() const {
|
||||
return _desiredHeight.value();
|
||||
}
|
||||
|
||||
void ReplyPillHeader::setRoundedShapeBelow(bool value) {
|
||||
if (_roundedShapeBelow == value) {
|
||||
return;
|
||||
}
|
||||
_roundedShapeBelow = value;
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplyPillHeader::hideAnimated() {
|
||||
if (_hiding) {
|
||||
return;
|
||||
}
|
||||
_hiding = true;
|
||||
_closeRequests.fire({});
|
||||
_showAnimation.start(
|
||||
[=] { animationCallback(); },
|
||||
1.,
|
||||
0.,
|
||||
kAnimationDuration);
|
||||
}
|
||||
|
||||
void ReplyPillHeader::animationCallback() {
|
||||
const auto full = st::boxPhotoCaptionSkip + st::historyReplyHeight;
|
||||
const auto value = _showAnimation.value(_hiding ? 0. : 1.);
|
||||
_desiredHeight = int(base::SafeRound(full * value));
|
||||
update();
|
||||
if (_hiding && !_showAnimation.animating()) {
|
||||
_hideFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ReplyPillHeader::resolveMessageData() {
|
||||
const auto id = _replyTo.messageId;
|
||||
if (!id || !id.peer) {
|
||||
return;
|
||||
}
|
||||
const auto peer = _data->peer(id.peer);
|
||||
const auto itemId = id.msg;
|
||||
const auto callback = crl::guard(this, [=] {
|
||||
if (!_shownMessage) {
|
||||
if (const auto message = _data->message(peer, itemId)) {
|
||||
setShownMessage(message);
|
||||
} else {
|
||||
hideAnimated();
|
||||
}
|
||||
}
|
||||
});
|
||||
_data->session().api().requestMessageData(peer, itemId, callback);
|
||||
}
|
||||
|
||||
void ReplyPillHeader::setShownMessage(HistoryItem *item) {
|
||||
_shownMessage = item;
|
||||
if (item) {
|
||||
updateShownMessageText();
|
||||
const auto context = Core::TextContext({
|
||||
.session = &item->history()->session(),
|
||||
.customEmojiLoopLimit = 1,
|
||||
});
|
||||
_shownMessageName.setMarkedText(
|
||||
st::fwdTextStyle,
|
||||
HistoryView::Reply::ComposePreviewName(
|
||||
item->history(),
|
||||
item,
|
||||
_replyTo),
|
||||
Ui::NameTextOptions(),
|
||||
context);
|
||||
} else {
|
||||
_shownMessageName.clear();
|
||||
_shownMessageText.clear();
|
||||
resolveMessageData();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplyPillHeader::updateShownMessageText() {
|
||||
Expects(_shownMessage != nullptr);
|
||||
|
||||
const auto context = Core::TextContext({
|
||||
.session = &_data->session(),
|
||||
.repaint = [=] { customEmojiRepaint(); },
|
||||
});
|
||||
_shownMessageText.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
(_replyTo.quote.empty()
|
||||
? _shownMessage->inReplyText()
|
||||
: _replyTo.quote),
|
||||
Ui::DialogTextOptions(),
|
||||
context);
|
||||
}
|
||||
|
||||
void ReplyPillHeader::customEmojiRepaint() {
|
||||
if (_repaintScheduled) {
|
||||
return;
|
||||
}
|
||||
_repaintScheduled = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplyPillHeader::resizeEvent(QResizeEvent *e) {
|
||||
_cancel->moveToRight(
|
||||
st::boxPhotoPadding.right() + st::sendBoxAlbumGroupSkipRight,
|
||||
(st::historyReplyHeight - _cancel->height()) / 2);
|
||||
}
|
||||
|
||||
void ReplyPillHeader::paintEvent(QPaintEvent *e) {
|
||||
_repaintScheduled = false;
|
||||
|
||||
Painter p(this);
|
||||
p.setInactive(_show->paused(Window::GifPauseReason::Layer));
|
||||
|
||||
const auto left = st::boxPhotoPadding.left();
|
||||
const auto right = st::boxPhotoPadding.right();
|
||||
const auto bottomSkip = st::boxPhotoCaptionSkip;
|
||||
const auto pillHeight = height() - bottomSkip;
|
||||
if (pillHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto pillRect = QRect(
|
||||
left,
|
||||
0,
|
||||
width() - left - right,
|
||||
pillHeight);
|
||||
if (pillRect.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::windowBgOver);
|
||||
const auto topRadius = st::bubbleRadiusLarge;
|
||||
const auto bottomRadius = _roundedShapeBelow
|
||||
? st::bubbleRadiusSmall
|
||||
: st::bubbleRadiusLarge;
|
||||
const auto rectF = QRectF(pillRect);
|
||||
auto path = QPainterPath();
|
||||
path.moveTo(rectF.left() + topRadius, rectF.top());
|
||||
path.lineTo(rectF.right() - topRadius, rectF.top());
|
||||
path.arcTo(
|
||||
rectF.right() - 2 * topRadius,
|
||||
rectF.top(),
|
||||
2 * topRadius,
|
||||
2 * topRadius,
|
||||
90, -90);
|
||||
path.lineTo(rectF.right(), rectF.bottom() - bottomRadius);
|
||||
path.arcTo(
|
||||
rectF.right() - 2 * bottomRadius,
|
||||
rectF.bottom() - 2 * bottomRadius,
|
||||
2 * bottomRadius,
|
||||
2 * bottomRadius,
|
||||
0, -90);
|
||||
path.lineTo(rectF.left() + bottomRadius, rectF.bottom());
|
||||
path.arcTo(
|
||||
rectF.left(),
|
||||
rectF.bottom() - 2 * bottomRadius,
|
||||
2 * bottomRadius,
|
||||
2 * bottomRadius,
|
||||
270, -90);
|
||||
path.lineTo(rectF.left(), rectF.top() + topRadius);
|
||||
path.arcTo(
|
||||
rectF.left(),
|
||||
rectF.top(),
|
||||
2 * topRadius,
|
||||
2 * topRadius,
|
||||
180, -90);
|
||||
path.closeSubpath();
|
||||
p.fillPath(path, st::windowBgOver);
|
||||
}
|
||||
|
||||
const auto iconPos = st::sendFilesReplyIconPosition
|
||||
+ QPoint(pillRect.left(), pillRect.top());
|
||||
if (!_replyTo.quote.empty()) {
|
||||
st::historyQuoteIcon.paint(p, iconPos, width());
|
||||
} else {
|
||||
st::historyReplyIcon.paint(p, iconPos, width());
|
||||
// Remove 'settings' mini-icon.
|
||||
p.fillRect(
|
||||
QRect(
|
||||
QPoint(style::ConvertScale(16), style::ConvertScale(5))
|
||||
+ iconPos,
|
||||
QSize(style::ConvertScale(11), style::ConvertScale(8))),
|
||||
st::windowBgOver);
|
||||
p.fillRect(
|
||||
QRect(
|
||||
QPoint(style::ConvertScale(22), style::ConvertScale(13))
|
||||
+ iconPos,
|
||||
QSize(style::ConvertScale(5), style::ConvertScale(2))),
|
||||
st::windowBgOver);
|
||||
}
|
||||
|
||||
const auto replySkip = st::historyReplySkip;
|
||||
const auto textLeft = pillRect.left() + replySkip;
|
||||
const auto availableWidth = _cancel->x() - textLeft;
|
||||
if (availableWidth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto pillCenterY = pillRect.top()
|
||||
+ st::historyReplyHeight / 2;
|
||||
|
||||
if (!_shownMessage) {
|
||||
p.setFont(st::msgDateFont);
|
||||
p.setPen(st::historyComposeAreaFgService);
|
||||
const auto top = pillCenterY - st::msgDateFont->height / 2;
|
||||
p.drawText(
|
||||
textLeft,
|
||||
top + st::msgDateFont->ascent,
|
||||
st::msgDateFont->elided(
|
||||
tr::lng_profile_loading(tr::now),
|
||||
availableWidth));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto media = _shownMessage->media();
|
||||
const auto hasPreview = media && media->hasReplyPreview();
|
||||
const auto preview = hasPreview ? media->replyPreview() : nullptr;
|
||||
const auto spoilered = media && media->hasSpoiler();
|
||||
if (!spoilered) {
|
||||
_previewSpoiler = nullptr;
|
||||
} else if (!_previewSpoiler) {
|
||||
_previewSpoiler = std::make_unique<Ui::SpoilerAnimation>([=] {
|
||||
update();
|
||||
});
|
||||
}
|
||||
const auto previewSkipValue = st::historyReplyPreview + st::msgReplyBarSkip;
|
||||
const auto previewSkip = (hasPreview && preview) ? previewSkipValue : 0;
|
||||
const auto contentLeft = textLeft + previewSkip;
|
||||
const auto contentAvailable = availableWidth - previewSkip;
|
||||
|
||||
if (preview) {
|
||||
const auto to = QRect(
|
||||
textLeft,
|
||||
pillCenterY - st::historyReplyPreview / 2,
|
||||
st::historyReplyPreview,
|
||||
st::historyReplyPreview);
|
||||
p.drawPixmap(to.x(), to.y(), preview->pixSingle(
|
||||
preview->size() / style::DevicePixelRatio(),
|
||||
{
|
||||
.options = Images::Option::RoundSmall,
|
||||
.outer = to.size(),
|
||||
}));
|
||||
if (_previewSpoiler) {
|
||||
Ui::FillSpoilerRect(
|
||||
p,
|
||||
to,
|
||||
Ui::DefaultImageSpoiler().frame(
|
||||
_previewSpoiler->index(crl::now(), p.inactive())));
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen(st::historyReplyNameFg);
|
||||
p.setFont(st::msgServiceNameFont);
|
||||
_shownMessageName.drawElided(
|
||||
p,
|
||||
contentLeft,
|
||||
pillRect.top() + st::msgReplyPadding.top(),
|
||||
contentAvailable);
|
||||
|
||||
p.setPen(st::historyComposeAreaFg);
|
||||
_shownMessageText.draw(p, {
|
||||
.position = QPoint(
|
||||
contentLeft,
|
||||
pillRect.top()
|
||||
+ st::msgReplyPadding.top()
|
||||
+ st::msgServiceNameFont->height),
|
||||
.availableWidth = contentAvailable,
|
||||
.palette = &st::historyComposeAreaPalette,
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
.now = crl::now(),
|
||||
.pausedEmoji = p.inactive() || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = p.inactive() || On(PowerSaving::kChatSpoiler),
|
||||
.elisionLines = 1,
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace SendFiles
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 "ui/effects/animations.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/text/text.h"
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
namespace ChatHelpers {
|
||||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Data {
|
||||
class Session;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class SpoilerAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
namespace SendFiles {
|
||||
|
||||
class ReplyPillHeader final : public Ui::RpWidget {
|
||||
public:
|
||||
ReplyPillHeader(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FullReplyTo replyTo);
|
||||
~ReplyPillHeader();
|
||||
|
||||
[[nodiscard]] rpl::producer<> closeRequests() const;
|
||||
[[nodiscard]] rpl::producer<> hideFinished() const;
|
||||
[[nodiscard]] rpl::producer<int> desiredHeight() const;
|
||||
|
||||
void setRoundedShapeBelow(bool value);
|
||||
void hideAnimated();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
void resolveMessageData();
|
||||
void setShownMessage(HistoryItem *item);
|
||||
void updateShownMessageText();
|
||||
void customEmojiRepaint();
|
||||
void animationCallback();
|
||||
|
||||
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||
const not_null<Data::Session*> _data;
|
||||
const FullReplyTo _replyTo;
|
||||
const not_null<Ui::IconButton*> _cancel;
|
||||
|
||||
HistoryItem *_shownMessage = nullptr;
|
||||
Ui::Text::String _shownMessageName;
|
||||
Ui::Text::String _shownMessageText;
|
||||
std::unique_ptr<Ui::SpoilerAnimation> _previewSpoiler;
|
||||
bool _repaintScheduled = false;
|
||||
|
||||
Ui::Animations::Simple _showAnimation;
|
||||
rpl::variable<int> _desiredHeight = 0;
|
||||
rpl::event_stream<> _closeRequests;
|
||||
rpl::variable<bool> _hideFinished = false;
|
||||
bool _hiding = false;
|
||||
bool _roundedShapeBelow = true;
|
||||
|
||||
};
|
||||
|
||||
} // namespace SendFiles
|
||||
@@ -609,7 +609,7 @@ sendBoxAlbumSmallGroupSize: size(30px, 25px);
|
||||
sendBoxAlbumSmallGroupCircleSize: 27px;
|
||||
|
||||
sendBoxFileGroupSkipTop: 2px;
|
||||
sendBoxFileGroupSkipRight: 5px;
|
||||
sendBoxFileGroupSkipRight: 1px;
|
||||
sendBoxFileGroupEditInternalSkip: -1px;
|
||||
|
||||
sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) {
|
||||
@@ -622,7 +622,7 @@ sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_cross", me
|
||||
|
||||
sendBoxAlbumButtonMediaMore: icon {{ "send_media/send_media_more", roundedFg }};
|
||||
sendBoxAlbumGroupButtonMediaMore: icon {{ "send_media/send_media_more", roundedFg, point(4px, 1px) }};
|
||||
sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_cross", roundedFg, point(-2px, 1px) }};
|
||||
sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_cross", roundedFg }};
|
||||
|
||||
defaultComposeIcons: ComposeIcons {
|
||||
settings: icon {{ "emoji/emoji_settings", emojiIconFg }};
|
||||
@@ -1033,6 +1033,19 @@ historyPinnedShowAll: IconButton(historyReplyCancel) {
|
||||
icon: icon {{ "pinned_show_all", historyReplyCancelFg }};
|
||||
iconOver: icon {{ "pinned_show_all", historyReplyCancelFgOver }};
|
||||
}
|
||||
sendFilesReplyIconPosition: point(11px, 7px);
|
||||
sendFilesReplyCancelSize: 24px;
|
||||
sendFilesReplyCancel: IconButton(editMediaButton) {
|
||||
width: sendFilesReplyCancelSize;
|
||||
height: sendFilesReplyCancelSize;
|
||||
icon: icon {{ "send_media/send_media_cross", historyReplyCancelFg }};
|
||||
iconOver: icon {{ "send_media/send_media_cross", historyReplyCancelFgOver }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgRipple;
|
||||
}
|
||||
|
||||
rippleAreaSize: sendFilesReplyCancelSize;
|
||||
}
|
||||
historyPinnedBotButton: RoundButton(defaultActiveButton) {
|
||||
width: -34px;
|
||||
height: 30px;
|
||||
|
||||
@@ -1368,9 +1368,14 @@ void HistoryWidget::sendTextAsFile(
|
||||
_peer,
|
||||
Api::SendType::Normal,
|
||||
sendMenuDetails());
|
||||
box->setReplyTo(replyTo());
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo currentReplyTo) {
|
||||
if (!currentReplyTo.messageId && replyTo().messageId) {
|
||||
cancelReply();
|
||||
}
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(crl::guard(this, [=] {
|
||||
@@ -6782,10 +6787,15 @@ bool HistoryWidget::confirmSendingFiles(
|
||||
_peer,
|
||||
Api::SendType::Normal,
|
||||
sendMenuDetails());
|
||||
box->setReplyTo(replyTo());
|
||||
_field->setTextWithTags({});
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo currentReplyTo) {
|
||||
if (!currentReplyTo.messageId && replyTo().messageId) {
|
||||
cancelReply();
|
||||
}
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(crl::guard(this, [=] {
|
||||
|
||||
@@ -3384,6 +3384,16 @@ void ComposeControls::fireSendTextAsFile(
|
||||
? Api::SendType::ScheduledToUser
|
||||
: Api::SendType::Scheduled)
|
||||
: Api::SendType::Normal;
|
||||
auto confirmed = [=, callback = _sendAsFileConfirmed](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options,
|
||||
FullReplyTo replyTo) {
|
||||
if (!replyTo.messageId
|
||||
&& replyingToMessage().messageId) {
|
||||
cancelReplyMessage();
|
||||
}
|
||||
callback(std::move(bundle), options);
|
||||
};
|
||||
_show->show(Box<SendFilesBox>(SendFilesBoxDescriptor{
|
||||
.show = _show,
|
||||
.list = Ui::PrepareTextAsFile(fileText),
|
||||
@@ -3394,8 +3404,9 @@ void ComposeControls::fireSendTextAsFile(
|
||||
.sendType = sendType,
|
||||
.sendMenuDetails = _sendMenuDetails,
|
||||
.stOverride = &_st,
|
||||
.confirmed = _sendAsFileConfirmed,
|
||||
.confirmed = std::move(confirmed),
|
||||
.cancelled = std::move(restoreText),
|
||||
.replyTo = replyingToMessage(),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1192,10 +1192,16 @@ bool ChatWidget::confirmSendingFiles(
|
||||
_peer,
|
||||
Api::SendType::Normal,
|
||||
sendMenuDetails());
|
||||
box->setReplyTo(_composeControls->replyingToMessage());
|
||||
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo currentReplyTo) {
|
||||
if (!currentReplyTo.messageId
|
||||
&& _composeControls->replyingToMessage().messageId) {
|
||||
_composeControls->cancelReplyMessage();
|
||||
}
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(_composeControls->restoreTextCallback(
|
||||
|
||||
@@ -575,7 +575,8 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(_composeControls->restoreTextCallback(
|
||||
|
||||
@@ -668,8 +668,11 @@ bool ReplyArea::confirmSendingFiles(
|
||||
}
|
||||
|
||||
const auto show = _controller->uiShow();
|
||||
auto confirmed = [=](auto &&...args) {
|
||||
sendingFilesConfirmed(std::forward<decltype(args)>(args)...);
|
||||
auto confirmed = [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options,
|
||||
FullReplyTo) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
};
|
||||
show->show(Box<SendFilesBox>(SendFilesBoxDescriptor{
|
||||
.show = show,
|
||||
|
||||
@@ -1344,7 +1344,8 @@ bool ShortcutMessages::confirmSendingFiles(
|
||||
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(_composeControls->restoreTextCallback(
|
||||
|
||||
@@ -175,8 +175,19 @@ void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
});
|
||||
|
||||
auto hq = std::optional<PainterHighQualityEnabler>();
|
||||
if (drawBackground()) {
|
||||
const auto &padding = st::boxPhotoPadding;
|
||||
const auto bgRect = QRect(
|
||||
padding.left(),
|
||||
0,
|
||||
width() - padding.left() - padding.right(),
|
||||
height());
|
||||
const auto radius = st::bubbleRadiusSmall;
|
||||
auto clipPath = QPainterPath();
|
||||
clipPath.addRoundedRect(bgRect, radius, radius);
|
||||
hq.emplace(p);
|
||||
p.setClipPath(clipPath);
|
||||
if (_previewLeft > padding.left()) {
|
||||
p.fillRect(
|
||||
padding.left(),
|
||||
|
||||
@@ -100,11 +100,12 @@ void SessionController::showDrawToReplyFilesBox(
|
||||
.sendType = Api::SendType::Normal,
|
||||
.confirmed = crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
FullReplyTo currentReplyTo) {
|
||||
if (const auto thread = weak.get()) {
|
||||
sendDrawToReplyFiles(
|
||||
thread,
|
||||
replyTo,
|
||||
currentReplyTo.messageId,
|
||||
std::move(bundle),
|
||||
options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user