Edit media captions in message field.

This commit is contained in:
John Preston 2023-04-07 18:32:53 +04:00
parent e3f2dcec22
commit 42c96b4c7f
11 changed files with 355 additions and 130 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "mainwidget.h" // controller->content() -> QWidget*
#include "mtproto/mtproto_config.h"
#include "platform/platform_specific.h"
#include "storage/localimageloader.h" // SendMediaType
@ -69,7 +70,9 @@ namespace {
constexpr auto kChangesDebounceTimeout = crl::time(1000);
auto ListFromMimeData(not_null<const QMimeData*> data, bool premium) {
[[nodiscard]] Ui::PreparedList ListFromMimeData(
not_null<const QMimeData*> data,
bool premium) {
using Error = Ui::PreparedList::Error;
const auto list = Core::ReadMimeUrls(data);
auto result = !list.isEmpty()
@ -89,7 +92,7 @@ auto ListFromMimeData(not_null<const QMimeData*> data, bool premium) {
return result;
}
Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
[[nodiscard]] Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
if (item->groupId().empty()) {
return Ui::AlbumType();
}
@ -109,17 +112,130 @@ Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
return Ui::AlbumType();
}
bool CanBeCompressed(Ui::AlbumType type) {
[[nodiscard]] bool CanBeCompressed(Ui::AlbumType type) {
return (type == Ui::AlbumType::None)
|| (type == Ui::AlbumType::PhotoVideo);
}
void ChooseReplacement(
not_null<Window::SessionController*> controller,
Ui::AlbumType type,
Fn<void(Ui::PreparedList&&)> chosen) {
const auto weak = base::make_weak(controller);
const auto callback = [=](FileDialog::OpenResult &&result) {
const auto strong = weak.get();
if (!strong) {
return;
}
const auto showError = [=](tr::phrase<> t) {
if (const auto strong = weak.get()) {
strong->showToast({ t(tr::now) });
}
};
const auto checkResult = [=](const Ui::PreparedList &list) {
if (list.files.size() != 1) {
return false;
}
const auto &file = list.files.front();
const auto mime = file.information->filemime;
if (Core::IsMimeSticker(mime)) {
showError(tr::lng_edit_media_invalid_file);
return false;
} else if (type != Ui::AlbumType::None
&& !file.canBeInAlbumType(type)) {
showError(tr::lng_edit_media_album_error);
return false;
}
return true;
};
const auto premium = strong->session().premium();
auto list = Storage::PreparedFileFromFilesDialog(
std::move(result),
checkResult,
showError,
st::sendMediaPreviewSize,
premium);
if (list) {
chosen(std::move(*list));
}
};
const auto filters = (type == Ui::AlbumType::PhotoVideo)
? FileDialog::PhotoVideoFilesFilter()
: FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(
controller->content().get(),
tr::lng_choose_file(tr::now),
filters,
crl::guard(controller, callback));
}
void EditPhotoImage(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
bool wasSpoiler,
Fn<void(Ui::PreparedList)> done) {
const auto large = media
? media->image(Data::PhotoSize::Large)
: nullptr;
const auto parent = controller->content();
const auto previewWidth = st::sendMediaPreviewSize;
auto callback = [=](const Editor::PhotoModifications &mods) {
if (!mods) {
return;
}
const auto large = media->image(Data::PhotoSize::Large);
if (!large) {
return;
}
auto copy = large->original();
auto list = Storage::PrepareMediaFromImage(
std::move(copy),
QByteArray(),
previewWidth);
using ImageInfo = Ui::PreparedFileInformation::Image;
auto &file = list.files.front();
file.spoiler = wasSpoiler;
const auto image = std::get_if<ImageInfo>(&file.information->media);
image->modifications = mods;
const auto sideLimit = PhotoSideLimit();
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
done(std::move(list));
};
const auto fileImage = std::make_shared<Image>(*large);
auto editor = base::make_unique_q<Editor::PhotoEditor>(
parent,
&controller->window(),
fileImage,
Editor::PhotoModifications());
const auto raw = editor.get();
auto layer = std::make_unique<Editor::LayerWidget>(
parent,
std::move(editor));
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
}
} // namespace
EditCaptionBox::EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item)
: EditCaptionBox({}, controller, item, PrepareEditText(item), {}, {}) {
}
EditCaptionBox::EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
Ui::PreparedList &&list,
Fn<void()> saved)
: _controller(controller)
, _historyItem(item)
, _isAllowedEditMedia(item->media()
@ -135,7 +251,10 @@ EditCaptionBox::EditCaptionBox(
tr::lng_photo_caption()))
, _emojiToggle(base::make_unique_q<Ui::EmojiButton>(
this,
st::boxAttachEmoji)) {
st::boxAttachEmoji))
, _initialText(std::move(text))
, _initialList(std::move(list))
, _saved(std::move(saved)) {
Expects(item->media() != nullptr);
Expects(item->media()->allowsEditCaption());
@ -148,6 +267,57 @@ EditCaptionBox::EditCaptionBox(
EditCaptionBox::~EditCaptionBox() = default;
void EditCaptionBox::StartMediaReplace(
not_null<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
Fn<void()> saved) {
const auto session = &controller->session();
const auto item = session->data().message(itemId);
if (!item) {
return;
}
const auto show = [=](Ui::PreparedList &&list) mutable {
controller->show(Box<EditCaptionBox>(
controller,
item,
std::move(text),
std::move(list),
std::move(saved)));
};
ChooseReplacement(
controller,
ComputeAlbumType(item),
crl::guard(controller, show));
}
void EditCaptionBox::StartPhotoEdit(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
Fn<void()> saved) {
const auto session = &controller->session();
const auto item = session->data().message(itemId);
if (!item) {
return;
}
const auto hasSpoiler = item->media() && item->media()->hasSpoiler();
EditPhotoImage(controller, media, hasSpoiler, [=](
Ui::PreparedList &&list) mutable {
const auto item = session->data().message(itemId);
if (!item) {
return;
}
controller->show(Box<EditCaptionBox>(
controller,
item,
std::move(text),
std::move(list),
std::move(saved)));
});
}
void EditCaptionBox::prepare() {
addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
@ -158,7 +328,9 @@ void EditCaptionBox::prepare() {
setupEmojiPanel();
setInitialText();
rebuildPreview();
if (!setPreparedList(std::move(_initialList))) {
rebuildPreview();
}
setupEditEventHandler();
SetupShadowsToScrollContent(this, _scroll, _contentHeight.events());
@ -290,16 +462,15 @@ void EditCaptionBox::setupField() {
}
void EditCaptionBox::setInitialText() {
const auto initial = PrepareEditText(_historyItem);
_field->setTextWithTags(
initial,
_initialText,
Ui::InputField::HistoryAction::Clear);
auto cursor = _field->textCursor();
cursor.movePosition(QTextCursor::End);
_field->setTextCursor(cursor);
_checkChangedTimer.setCallback([=] {
if (_field->getTextWithAppliedMarkdown() == initial) {
if (_field->getTextWithAppliedMarkdown() == _initialText) {
setCloseByOutsideClick(true);
}
});
@ -353,132 +524,44 @@ void EditCaptionBox::setupControls() {
}
void EditCaptionBox::setupEditEventHandler() {
const auto toastParent = Ui::BoxShow(this).toastParent();
const auto callback = [=](FileDialog::OpenResult &&result) {
auto showError = [toastParent](tr::phrase<> t) {
Ui::Toast::Show(toastParent, t(tr::now));
};
const auto checkResult = [=](const Ui::PreparedList &list) {
if (list.files.size() != 1) {
return false;
}
const auto &file = list.files.front();
const auto mime = file.information->filemime;
if (Core::IsMimeSticker(mime)) {
showError(tr::lng_edit_media_invalid_file);
return false;
} else if (_albumType != Ui::AlbumType::None
&& !file.canBeInAlbumType(_albumType)) {
showError(tr::lng_edit_media_album_error);
return false;
}
return true;
};
const auto premium = _controller->session().premium();
auto list = Storage::PreparedFileFromFilesDialog(
std::move(result),
checkResult,
showError,
st::sendMediaPreviewSize,
premium);
if (list) {
setPreparedList(std::move(*list));
}
};
const auto buttonCallback = [=] {
const auto filters = (_albumType == Ui::AlbumType::PhotoVideo)
? FileDialog::PhotoVideoFilesFilter()
: FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(
this,
tr::lng_choose_file(tr::now),
filters,
crl::guard(this, callback));
};
_editMediaClicks.events(
) | rpl::start_with_next(
buttonCallback,
lifetime());
) | rpl::start_with_next([=] {
ChooseReplacement(_controller, _albumType, crl::guard(this, [=](
Ui::PreparedList &&list) {
setPreparedList(std::move(list));
}));
}, lifetime());
}
void EditCaptionBox::setupPhotoEditorEventHandler() {
const auto openedOnce = lifetime().make_state<bool>(false);
_photoEditorOpens.events(
) | rpl::start_with_next([=, controller = _controller] {
const auto increment = [=] {
if (*openedOnce) {
return;
}
if (_preparedList.files.empty()
&& (!_photoMedia
|| !_photoMedia->image(Data::PhotoSize::Large))) {
return;
} else if (!*openedOnce) {
*openedOnce = true;
controller->session().settings().incrementPhotoEditorHintShown();
controller->session().saveSettings();
};
const auto clearError = [=] {
}
if (!_error.isEmpty()) {
_error = QString();
update();
};
const auto previewWidth = st::sendMediaPreviewSize;
}
if (!_preparedList.files.empty()) {
increment();
clearError();
Editor::OpenWithPreparedFile(
this,
controller,
&_preparedList.files.front(),
previewWidth,
st::sendMediaPreviewSize,
[=] { rebuildPreview(); });
} else if (_photoMedia) {
const auto large = _photoMedia->image(Data::PhotoSize::Large);
if (!large) {
return;
}
increment();
clearError();
auto callback = [=](const Editor::PhotoModifications &mods) {
if (!mods || !_photoMedia) {
return;
}
const auto large = _photoMedia->image(Data::PhotoSize::Large);
if (!large) {
return;
}
auto copy = large->original();
const auto wasSpoiler = hasSpoiler();
_preparedList = Storage::PrepareMediaFromImage(
std::move(copy),
QByteArray(),
previewWidth);
using ImageInfo = Ui::PreparedFileInformation::Image;
auto &file = _preparedList.files.front();
file.spoiler = wasSpoiler;
const auto image = std::get_if<ImageInfo>(
&file.information->media);
image->modifications = mods;
const auto sideLimit = PhotoSideLimit();
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
rebuildPreview();
};
const auto fileImage = std::make_shared<Image>(*large);
auto editor = base::make_unique_q<Editor::PhotoEditor>(
this,
&controller->window(),
fileImage,
Editor::PhotoModifications());
const auto raw = editor.get();
auto layer = std::make_unique<Editor::LayerWidget>(
this,
std::move(editor));
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
controller->showLayer(
std::move(layer),
Ui::LayerOption::KeepOther);
} else {
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
Ui::PreparedList &&list) {
setPreparedList(std::move(list));
});
}
}, lifetime());
}
@ -780,13 +863,13 @@ void EditCaptionBox::save() {
: SendMediaType::File,
_field->getTextWithAppliedMarkdown(),
action);
closeBox();
closeAfterSave();
return;
}
const auto done = crl::guard(this, [=] {
_saveRequestId = 0;
closeBox();
closeAfterSave();
});
const auto fail = crl::guard(this, [=](const QString &error) {
@ -795,7 +878,7 @@ void EditCaptionBox::save() {
_error = tr::lng_edit_error(tr::now);
update();
} else if (error == u"MESSAGE_NOT_MODIFIED"_q) {
closeBox();
closeAfterSave();
} else if (error == u"MESSAGE_EMPTY"_q) {
_field->setFocus();
_field->showError();
@ -816,6 +899,16 @@ void EditCaptionBox::save() {
_saveRequestId = Api::EditCaption(item, sending, options, done, fail);
}
void EditCaptionBox::closeAfterSave() {
const auto weak = MakeWeak(this);
if (_saved) {
_saved();
}
if (weak) {
closeBox();
}
}
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {
const auto ctrl = e->modifiers().testFlag(Qt::ControlModifier);
if ((e->key() == Qt::Key_E) && ctrl) {

View File

@ -36,8 +36,27 @@ public:
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
Ui::PreparedList &&list,
Fn<void()> saved);
~EditCaptionBox();
static void StartMediaReplace(
not_null<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
Fn<void()> saved);
static void StartPhotoEdit(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
Fn<void()> saved);
protected:
void prepare() override;
void setInnerFocus() override;
@ -66,6 +85,7 @@ private:
bool validateLength(const QString &text) const;
void applyChanges();
void save();
void closeAfterSave();
bool fileFromClipboard(not_null<const QMimeData*> data);
@ -89,6 +109,10 @@ private:
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
base::unique_qptr<QObject> _emojiFilter;
const TextWithTags _initialText;
Ui::PreparedList _initialList;
Fn<void()> _saved;
std::shared_ptr<Data::PhotoMedia> _photoMedia;
Ui::PreparedList _preparedList;

View File

@ -61,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_web_page.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_media_types.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
@ -2121,6 +2122,8 @@ void HistoryWidget::showHistory(
_saveEditMsgRequestId = 0;
_processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _editMsgId = _replyToId = 0;
_photoEditMedia = nullptr;
updateReplaceMediaButton();
_previewData = nullptr;
_previewCache.clear();
_fieldBarCancel->hide();
@ -2504,6 +2507,25 @@ void HistoryWidget::clearAllLoadRequests() {
}
}
bool HistoryWidget::updateReplaceMediaButton() {
if (!_canReplaceMedia) {
const auto result = (_replaceMedia != nullptr);
_replaceMedia.destroy();
return result;
} else if (_replaceMedia) {
return false;
}
_replaceMedia.create(this, st::historyReplaceMedia);
_replaceMedia->setClickedCallback([=] {
EditCaptionBox::StartMediaReplace(
controller(),
{ _history->peer->id, _editMsgId },
_field->getTextWithTags(),
crl::guard(_list, [=] { cancelEdit(); }));
});
return true;
}
void HistoryWidget::updateFieldSubmitSettings() {
const auto settings = _isInlineBot
? Ui::InputField::SubmitSettings::None
@ -2731,6 +2753,9 @@ void HistoryWidget::updateControlsVisibility() {
_kbScroll->hide();
_fieldBarCancel->hide();
_attachToggle->hide();
if (_replaceMedia) {
_replaceMedia->hide();
}
_tabbedSelectorToggle->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
@ -2795,7 +2820,12 @@ void HistoryWidget::updateControlsVisibility() {
_botCommandStart->setVisible(_cmdStartShown);
}
}
_attachToggle->show();
if (_replaceMedia) {
_replaceMedia->show();
_attachToggle->hide();
} else {
_attachToggle->show();
}
if (_botMenuButton) {
_botMenuButton->show();
}
@ -3685,7 +3715,8 @@ void HistoryWidget::saveEditMsg() {
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
TextUtilities::PrepareForSending(left, prepareFlags);
if (!TextUtilities::CutPart(sending, left, MaxMessageSize)) {
if (!TextUtilities::CutPart(sending, left, MaxMessageSize)
&& (!item->media() || !item->media()->allowsEditCaption())) {
const auto suggestModerateActions = false;
controller()->show(
Box<DeleteMessagesBox>(item, suggestModerateActions));
@ -4239,9 +4270,34 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
}
void HistoryWidget::updateOverStates(QPoint pos) {
auto inReplyEditForward = QRect(st::historyReplySkip, _field->y() - st::historySendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId() || readyToForward());
const auto replyEditForwardInfoRect = QRect(
st::historyReplySkip,
_field->y() - st::historySendPadding - st::historyReplyHeight,
width() - st::historyReplySkip - _fieldBarCancel->width(),
st::historyReplyHeight);
auto inReplyEditForward = (_editMsgId || replyToId() || readyToForward())
&& replyEditForwardInfoRect.contains(pos);
auto inPhotoEdit = inReplyEditForward
&& _photoEditMedia
&& QRect(
replyEditForwardInfoRect.x(),
replyEditForwardInfoRect.y() + st::msgReplyPadding.top(),
st::msgReplyBarSize.height(),
st::msgReplyBarSize.height()).contains(pos);
auto inClickable = inReplyEditForward;
_inReplyEditForward = inReplyEditForward;
if (_inPhotoEdit != inPhotoEdit) {
_inPhotoEdit = inPhotoEdit;
if (_photoEditMedia) {
_inPhotoEditOver.start(
[=] { updateField(); },
_inPhotoEdit ? 0. : 1.,
_inPhotoEdit ? 1. : 0.,
st::defaultMessageBar.duration);
} else {
_inPhotoEditOver.stop();
}
}
_inReplyEditForward = inReplyEditForward && !inPhotoEdit;
if (inClickable != _inClickable) {
_inClickable = inClickable;
setCursor(_inClickable ? style::cur_pointer : style::cur_default);
@ -4862,7 +4918,7 @@ void HistoryWidget::moveFieldControls() {
_kbScroll->setGeometryToLeft(0, bottom, width(), keyboardHeight);
}
// (_botMenuButton) _attachToggle (_sendAs) ---- _inlineResults ------------------------------ _tabbedPanel ------ _fieldBarCancel
// (_botMenuButton) (_attachToggle|_replaceMedia) (_sendAs) ---- _inlineResults ------------------------------ _tabbedPanel ------ _fieldBarCancel
// (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send
// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages)
@ -4872,6 +4928,9 @@ void HistoryWidget::moveFieldControls() {
const auto skip = st::historyBotMenuSkip;
_botMenuButton->moveToLeft(left + skip, buttonsBottom + skip); left += skip + _botMenuButton->width();
}
if (_replaceMedia) {
_replaceMedia->moveToLeft(left, buttonsBottom);
}
_attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width();
if (_sendAs) {
_sendAs->moveToLeft(left, buttonsBottom); left += _sendAs->width();
@ -6038,6 +6097,13 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
st::historyReplyHeight).contains(e->pos());
if (_replyForwardPressed && !_fieldBarCancel->isHidden()) {
updateField();
} else if (_inPhotoEdit && _photoEditMedia) {
EditCaptionBox::StartPhotoEdit(
controller(),
_photoEditMedia,
{ _history->peer->id, _editMsgId },
_field->getTextWithTags(),
crl::guard(_list, [=] { cancelEdit(); }));
} else if (_inReplyEditForward) {
if (readyToForward()) {
_forwardPanel->editOptions(controller());
@ -6957,12 +7023,7 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
}
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
if (const auto media = item->media()) {
if (media->allowsEditCaption()) {
controller()->show(Box<EditCaptionBox>(controller(), item));
return;
}
} else if (_chooseTheme) {
if (_chooseTheme) {
toggleChooseChatTheme(_peer);
} else if (_voiceRecordBar->isActive()) {
controller()->showToast({ tr::lng_edit_caption_voice(tr::now) });
@ -7132,6 +7193,9 @@ void HistoryWidget::cancelEdit() {
return;
}
_canReplaceMedia = false;
_photoEditMedia = nullptr;
updateReplaceMediaButton();
_replyEditMsg = nullptr;
setEditMsgId(0);
_history->clearLocalEditDraft({});
@ -7588,6 +7652,22 @@ void HistoryWidget::updateReplyEditTexts(bool force) {
_editMsgId ? _editMsgId : _replyToId);
}
if (_replyEditMsg) {
const auto media = _replyEditMsg->media();
_canReplaceMedia = media && media->allowsEditMedia();
_photoEditMedia = (_canReplaceMedia
&& media->photo()
&& !media->photo()->isNull())
? media->photo()->createMediaView()
: nullptr;
if (_photoEditMedia) {
_photoEditMedia->wanted(
Data::PhotoSize::Large,
_replyEditMsg->fullId());
}
if (updateReplaceMediaButton()) {
updateControlsVisibility();
updateControlsGeometry();
}
updateReplyEditText(_replyEditMsg);
updateBotKeyboard();
updateReplyToName();
@ -7695,6 +7775,9 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
if (drawMsgText) {
if (hasPreview) {
if (preview) {
const auto overEdit = _photoEditMedia
? _inPhotoEditOver.value(_inPhotoEdit ? 1. : 0.)
: 0.;
auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), preview->pixSingle(
preview->size() / style::DevicePixelRatio(),
@ -7703,12 +7786,21 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
.outer = to.size(),
}));
if (_replySpoiler) {
if (overEdit > 0.) {
p.setOpacity(1. - overEdit);
}
Ui::FillSpoilerRect(
p,
to,
Ui::DefaultImageSpoiler().frame(
_replySpoiler->index(now, pausedSpoiler)));
}
if (overEdit > 0.) {
p.setOpacity(overEdit);
p.fillRect(to, st::historyEditMediaBg);
st::historyEditMedia.paintInCenter(p, to);
p.setOpacity(1.);
}
}
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}

View File

@ -31,6 +31,7 @@ class Error;
namespace Data {
enum class PreviewState : char;
class PhotoMedia;
} // namespace Data
namespace SendMenu {
@ -145,6 +146,7 @@ public:
void firstLoadMessages();
void delayedShowAt(MsgId showAtMsgId);
bool updateReplaceMediaButton();
void updateFieldPlaceholder();
bool updateStickersByEmoji();
@ -634,6 +636,8 @@ private:
HistoryItem *_processingReplyItem = nullptr;
MsgId _editMsgId = 0;
std::shared_ptr<Data::PhotoMedia> _photoEditMedia;
bool _canReplaceMedia = false;
HistoryItem *_replyEditMsg = nullptr;
Ui::Text::String _replyEditMsgText;
@ -732,6 +736,7 @@ private:
object_ptr<Ui::RoundButton> _botMenuButton = { nullptr };
QString _botMenuButtonText;
object_ptr<Ui::IconButton> _attachToggle;
object_ptr<Ui::IconButton> _replaceMedia = { nullptr };
object_ptr<Ui::SendAsButton> _sendAs = { nullptr };
object_ptr<Ui::EmojiButton> _tabbedSelectorToggle;
object_ptr<Ui::IconButton> _botKeyboardShow;
@ -746,7 +751,9 @@ private:
bool _cmdStartShown = false;
object_ptr<Ui::InputField> _field;
base::unique_qptr<Ui::RpWidget> _fieldDisabled;
Ui::Animations::Simple _inPhotoEditOver;
bool _inReplyEditForward = false;
bool _inPhotoEdit = false;
bool _inClickable = false;
bool _kbShown = false;

View File

@ -482,6 +482,15 @@ historyMessagesTTL: IconButtonWithText {
font: font(10px semibold);
}
historyReplaceMedia: IconButton(historyAttach) {
icon: icon {{ "chat/input_replace", windowBgActive }};
iconOver: icon {{ "chat/input_replace", windowBgActive }};
ripple: RippleAnimation(defaultRippleAnimation) {
color: lightButtonBgOver;
}
}
historyEditMediaBg: videoPlayIconBg;
historyEditMedia: icon{{ "chat/input_draw", videoPlayIconFg }};
historyMessagesTTLPickerHeight: 200px;
historyMessagesTTLPickerItemHeight: 40px;
historyMessagesTTLLabel: FlatLabel(defaultFlatLabel) {