mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Edit media captions in message field.
This commit is contained in:
parent
e3f2dcec22
commit
42c96b4c7f
11 changed files with 355 additions and 130 deletions
BIN
Telegram/Resources/icons/chat/input_draw.png
Normal file
BIN
Telegram/Resources/icons/chat/input_draw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 953 B |
BIN
Telegram/Resources/icons/chat/input_draw@2x.png
Normal file
BIN
Telegram/Resources/icons/chat/input_draw@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/chat/input_draw@3x.png
Normal file
BIN
Telegram/Resources/icons/chat/input_draw@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/chat/input_replace.png
Normal file
BIN
Telegram/Resources/icons/chat/input_replace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 749 B |
BIN
Telegram/Resources/icons/chat/input_replace@2x.png
Normal file
BIN
Telegram/Resources/icons/chat/input_replace@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/chat/input_replace@3x.png
Normal file
BIN
Telegram/Resources/icons/chat/input_replace@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue