mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-11 06:03:40 +00:00
Edit a photo in the media editor from the rich editor
This commit is contained in:
@@ -1198,6 +1198,10 @@ void WindowHost::Impl::setupWindow(ShowWindowDescriptor &&descriptor) {
|
||||
},
|
||||
.requestMedia = std::move(descriptor.requestMedia),
|
||||
.applyPreparedMedia = std::move(descriptor.applyPreparedMedia),
|
||||
.requestPhotoEditSource
|
||||
= std::move(descriptor.requestPhotoEditSource),
|
||||
.replacePhotoWithList
|
||||
= std::move(descriptor.replacePhotoWithList),
|
||||
.imeCompositionStarts = window->imeCompositionStarts(),
|
||||
},
|
||||
descriptor.peer,
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -82,6 +83,9 @@ struct ShowWindowDescriptor {
|
||||
RequestMediaType)> requestMedia;
|
||||
Fn<void(not_null<Widget*>, Ui::PreparedList, PreparedMediaPasteTarget)>
|
||||
applyPreparedMedia;
|
||||
Fn<QImage(uint64 /*photoId*/)> requestPhotoEditSource;
|
||||
Fn<void(not_null<Widget*>, Ui::PreparedList, State::ReplaceTarget)>
|
||||
replacePhotoWithList;
|
||||
Fn<void(not_null<Widget*>, QPointer<QWidget>, rpl::producer<>)> requestMap;
|
||||
Fn<void()> closed;
|
||||
Fn<void(RichMessageLimitError)> showLimitToast;
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_editing.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/algorithm.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/weak_qptr.h"
|
||||
#include "base/weak_ptr.h"
|
||||
@@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_location.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
@@ -54,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/controls/location_picker.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/separate_panel.h"
|
||||
@@ -1426,6 +1429,19 @@ private:
|
||||
std::move(list),
|
||||
std::move(target));
|
||||
},
|
||||
.requestPhotoEditSource = [session = shared_from_this()](
|
||||
uint64 photoId) {
|
||||
return session->photoEditSource(photoId);
|
||||
},
|
||||
.replacePhotoWithList = [session = shared_from_this()](
|
||||
not_null<Widget*> editor,
|
||||
PreparedList list,
|
||||
State::ReplaceTarget replaceTarget) {
|
||||
session->replaceMediaWithPreparedList(
|
||||
QPointer<Widget>(editor.get()),
|
||||
std::move(list),
|
||||
std::move(replaceTarget));
|
||||
},
|
||||
.requestMap = [session = shared_from_this()](
|
||||
not_null<Widget*> editor,
|
||||
QPointer<QWidget> parent,
|
||||
@@ -1588,6 +1604,47 @@ private:
|
||||
std::move(target));
|
||||
}
|
||||
|
||||
void replaceMediaWithPreparedList(
|
||||
QPointer<Widget> editor,
|
||||
PreparedList list,
|
||||
State::ReplaceTarget replaceTarget) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
if (list.error != PreparedList::Error::None) {
|
||||
showToast(tr::lng_send_media_invalid_files(tr::now));
|
||||
return;
|
||||
}
|
||||
if (!list.files.empty()) {
|
||||
Storage::UpdateImageDetails(
|
||||
list.files.front(),
|
||||
st::sendMediaPreviewSize,
|
||||
PhotoSideLimit(true));
|
||||
}
|
||||
applyPreparedList(
|
||||
editor,
|
||||
std::move(list),
|
||||
++_prepareBatchId,
|
||||
AttachmentInsertMode::ReplaceBlock,
|
||||
std::nullopt,
|
||||
std::move(replaceTarget));
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage photoEditSource(uint64 photoId) {
|
||||
if (const auto i = _originalMediaImages.find(photoId);
|
||||
i != end(_originalMediaImages)) {
|
||||
return i->second;
|
||||
}
|
||||
const auto photo = _session->data().photo(PhotoId(photoId));
|
||||
const auto media = photo->createMediaView();
|
||||
const auto origin = composeDraftOrigin();
|
||||
media->wanted(::Data::PhotoSize::Large, origin);
|
||||
if (const auto large = media->image(::Data::PhotoSize::Large)) {
|
||||
return large->original();
|
||||
}
|
||||
return QImage();
|
||||
}
|
||||
|
||||
void applyPreparedList(
|
||||
QPointer<Widget> editor,
|
||||
PreparedList list,
|
||||
@@ -1753,12 +1810,21 @@ private:
|
||||
AttachmentInsertMode insertMode,
|
||||
std::optional<State::ReplaceTarget> replaceTarget) {
|
||||
const auto meta = BuildAttachmentMeta(file);
|
||||
auto originalImage = QImage();
|
||||
if (file.information) {
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
if (const auto image = std::get_if<ImageInfo>(
|
||||
&file.information->media)) {
|
||||
originalImage = image->data;
|
||||
}
|
||||
}
|
||||
const auto weak = base::make_weak(this);
|
||||
++_pendingAttachmentPrepareCount;
|
||||
_attachmentPrepareQueue.addTask(
|
||||
std::make_unique<PrepareAttachmentTask>(
|
||||
BuildPrepareTaskArgs(_session, _peer->id, std::move(file)),
|
||||
[weak, editor, meta, batchId, order, insertMode, replaceTarget](
|
||||
[weak, editor, meta, batchId, order, insertMode, replaceTarget,
|
||||
originalImage = std::move(originalImage)](
|
||||
std::shared_ptr<FilePrepareResult> prepared) mutable {
|
||||
if (const auto session = weak.get()) {
|
||||
session->attachmentPrepared(
|
||||
@@ -1768,7 +1834,8 @@ private:
|
||||
batchId,
|
||||
order,
|
||||
insertMode,
|
||||
std::move(replaceTarget));
|
||||
std::move(replaceTarget),
|
||||
std::move(originalImage));
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -1780,7 +1847,8 @@ private:
|
||||
uint64 batchId,
|
||||
int order,
|
||||
AttachmentInsertMode insertMode,
|
||||
std::optional<State::ReplaceTarget> replaceTarget) {
|
||||
std::optional<State::ReplaceTarget> replaceTarget,
|
||||
QImage originalImage) {
|
||||
_pendingAttachmentPrepareCount = std::max(
|
||||
_pendingAttachmentPrepareCount - 1,
|
||||
0);
|
||||
@@ -1827,7 +1895,8 @@ private:
|
||||
batchId,
|
||||
order,
|
||||
insertMode,
|
||||
std::move(replaceTarget));
|
||||
std::move(replaceTarget),
|
||||
std::move(originalImage));
|
||||
maybeContinueDeferredSubmit();
|
||||
}
|
||||
|
||||
@@ -1838,7 +1907,8 @@ private:
|
||||
uint64 batchId,
|
||||
int order,
|
||||
AttachmentInsertMode insertMode,
|
||||
std::optional<State::ReplaceTarget> replaceTarget) {
|
||||
std::optional<State::ReplaceTarget> replaceTarget,
|
||||
QImage originalImage) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
@@ -1847,7 +1917,8 @@ private:
|
||||
const auto blockKind = meta.blockKind;
|
||||
const auto uploadId = createAttachmentUpload(
|
||||
std::move(meta),
|
||||
std::move(prepared));
|
||||
std::move(prepared),
|
||||
std::move(originalImage));
|
||||
if (!uploadId) {
|
||||
return;
|
||||
}
|
||||
@@ -1880,7 +1951,8 @@ private:
|
||||
|
||||
[[nodiscard]] std::optional<FullMsgId> createAttachmentUpload(
|
||||
AttachmentMeta meta,
|
||||
std::shared_ptr<FilePrepareResult> prepared) {
|
||||
std::shared_ptr<FilePrepareResult> prepared,
|
||||
QImage originalImage) {
|
||||
if (!prepared) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1940,6 +2012,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (record.blockKind == RichPage::BlockKind::Photo
|
||||
&& !originalImage.isNull()
|
||||
&& record.localMediaId) {
|
||||
_originalMediaImages[record.localMediaId] = std::move(originalImage);
|
||||
}
|
||||
|
||||
_attachments.push_back(std::move(record));
|
||||
_session->uploader().upload(uploadId, prepared);
|
||||
return uploadId;
|
||||
@@ -3269,6 +3347,7 @@ private:
|
||||
Fn<void()> _onWindowClosedContinuation;
|
||||
std::shared_ptr<const RichPage> _submittedPage;
|
||||
std::vector<AttachmentRecord> _attachments;
|
||||
base::flat_map<uint64, QImage> _originalMediaImages;
|
||||
std::deque<QueuedPrepare> _prepareQueue;
|
||||
std::vector<MediaBatch> _mediaBatches;
|
||||
TaskQueue _attachmentPrepareQueue;
|
||||
|
||||
@@ -10,12 +10,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "base/random.h"
|
||||
#include "base/weak_qptr.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "data/data_msg_id.h"
|
||||
#include "data/data_types.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "editor/editor_layer_widget.h"
|
||||
#include "editor/photo_editor.h"
|
||||
#include "editor/photo_editor_common.h"
|
||||
#include "iv/editor/iv_editor_text_entities.h"
|
||||
#include "iv/markdown/iv_markdown_article_paint.h"
|
||||
#include "iv/markdown/iv_markdown_microtex.h"
|
||||
@@ -32,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/click_handler.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/image/image_location.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
@@ -2589,6 +2594,8 @@ Widget::Widget(
|
||||
, _customEmojiPaused(std::move(services.customEmojiPaused))
|
||||
, _requestMedia(std::move(services.requestMedia))
|
||||
, _applyPreparedMedia(std::move(services.applyPreparedMedia))
|
||||
, _requestPhotoEditSource(std::move(services.requestPhotoEditSource))
|
||||
, _replacePhotoWithList(std::move(services.replacePhotoWithList))
|
||||
, _peer(peer)
|
||||
, _state(std::move(state))
|
||||
, _showLimitToast(std::move(showLimitToast))
|
||||
@@ -5464,6 +5471,14 @@ void Widget::showSimpleMediaMenu(
|
||||
requestReplaceMedia(path);
|
||||
},
|
||||
&st::menuIconReplace);
|
||||
if (block->kind == RichPage::BlockKind::Photo) {
|
||||
menu->addAction(
|
||||
tr::lng_context_draw(tr::now),
|
||||
[=] {
|
||||
editPhotoBlock(path);
|
||||
},
|
||||
&st::menuIconPalette);
|
||||
}
|
||||
if (IsPhotoVideoBlockKind(block->kind)) {
|
||||
const auto currentSpoiler = block->spoiler;
|
||||
Menu::AddCheckedAction(
|
||||
@@ -5761,6 +5776,68 @@ void Widget::requestReplaceMedia(State::BlockPath path) {
|
||||
requestMedia(std::move(target));
|
||||
}
|
||||
|
||||
void Widget::editPhotoBlock(State::BlockPath path) {
|
||||
const auto block = BlockFromPath(_state->richPage(), path);
|
||||
if (!block || block->kind != RichPage::BlockKind::Photo) {
|
||||
return;
|
||||
}
|
||||
auto target = _state->replaceTargetForBlock(path);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
if (!_requestPhotoEditSource) {
|
||||
return;
|
||||
}
|
||||
auto source = _requestPhotoEditSource(block->photoId);
|
||||
if (source.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto spoiler = block->spoiler;
|
||||
const auto previewWidth = st::sendMediaPreviewSize;
|
||||
const auto sourceShared = std::make_shared<QImage>(std::move(source));
|
||||
const auto replaceTarget = std::make_shared<State::ReplaceTarget>(
|
||||
std::move(*target));
|
||||
auto fileImage = std::make_shared<Image>(QImage(*sourceShared));
|
||||
auto editor = base::make_unique_q<::Editor::PhotoEditor>(
|
||||
_outer,
|
||||
_show,
|
||||
nullptr,
|
||||
std::move(fileImage),
|
||||
::Editor::PhotoModifications());
|
||||
const auto raw = editor.get();
|
||||
auto layer = std::make_unique<::Editor::LayerWidget>(
|
||||
_outer,
|
||||
std::move(editor));
|
||||
const auto weak = base::make_weak(this);
|
||||
::Editor::InitEditorLayer(layer.get(), raw, [=](
|
||||
::Editor::PhotoModifications mods) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong || !mods || !strong->_replacePhotoWithList) {
|
||||
return;
|
||||
}
|
||||
auto copy = QImage(*sourceShared);
|
||||
auto list = Storage::PrepareMediaFromImage(
|
||||
std::move(copy),
|
||||
QByteArray(),
|
||||
previewWidth);
|
||||
if (list.files.empty()) {
|
||||
return;
|
||||
}
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
auto &file = list.files.front();
|
||||
file.spoiler = spoiler;
|
||||
if (const auto image = std::get_if<ImageInfo>(
|
||||
&file.information->media)) {
|
||||
image->modifications = std::move(mods);
|
||||
}
|
||||
strong->_replacePhotoWithList(
|
||||
not_null<Widget*>(strong),
|
||||
std::move(list),
|
||||
*replaceTarget);
|
||||
});
|
||||
_show->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void Widget::touchEvent(QTouchEvent *e) {
|
||||
if (e->type() == QEvent::TouchCancel) {
|
||||
_pendingTouchHorizontalScrollPoint = std::nullopt;
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <rpl/event_stream.h>
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
@@ -80,6 +81,9 @@ struct WidgetServices {
|
||||
RequestMediaType)> requestMedia;
|
||||
Fn<void(not_null<Widget*>, Ui::PreparedList, PreparedMediaPasteTarget)>
|
||||
applyPreparedMedia;
|
||||
Fn<QImage(uint64 /*photoId*/)> requestPhotoEditSource;
|
||||
Fn<void(not_null<Widget*>, Ui::PreparedList, State::ReplaceTarget)>
|
||||
replacePhotoWithList;
|
||||
rpl::producer<> imeCompositionStarts;
|
||||
};
|
||||
|
||||
@@ -738,6 +742,7 @@ private:
|
||||
Qt::MouseButton button);
|
||||
[[nodiscard]] bool applyMediaBlockChange(Fn<bool()> change);
|
||||
void requestReplaceMedia(State::BlockPath path);
|
||||
void editPhotoBlock(State::BlockPath path);
|
||||
[[nodiscard]] Markdown::PreparedEditSelection structuralSelectionFromHits(
|
||||
const Markdown::PreparedEditHit &anchor,
|
||||
const Markdown::PreparedEditHit &focus) const;
|
||||
@@ -763,6 +768,9 @@ private:
|
||||
RequestMediaType)> _requestMedia;
|
||||
const Fn<void(not_null<Widget*>, Ui::PreparedList, PreparedMediaPasteTarget)>
|
||||
_applyPreparedMedia;
|
||||
const Fn<QImage(uint64)> _requestPhotoEditSource;
|
||||
const Fn<void(not_null<Widget*>, Ui::PreparedList, State::ReplaceTarget)>
|
||||
_replacePhotoWithList;
|
||||
const not_null<PeerData*> _peer;
|
||||
const std::shared_ptr<State> _state;
|
||||
const Fn<void(RichMessageLimitError)> _showLimitToast;
|
||||
|
||||
Reference in New Issue
Block a user