From 4eaba39a7cca8854db0fa1636240cdb186d2cdc7 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 13 Jun 2020 16:08:55 +0300 Subject: [PATCH] Added drag'n'drop area to EditCaptionBox. --- .../SourceFiles/boxes/edit_caption_box.cpp | 143 ++++++++++++------ Telegram/SourceFiles/boxes/edit_caption_box.h | 2 + Telegram/SourceFiles/core/mime_type.cpp | 7 + Telegram/SourceFiles/core/mime_type.h | 1 + .../SourceFiles/history/history_drag_area.cpp | 15 +- .../SourceFiles/history/history_drag_area.h | 13 +- .../SourceFiles/history/history_widget.cpp | 4 +- .../view/history_view_scheduled_section.cpp | 2 +- .../storage/storage_media_prepare.cpp | 10 +- 9 files changed, 139 insertions(+), 58 deletions(-) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 8544806ca3..3a9c7c82f4 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "data/data_document_media.h" #include "history/history.h" +#include "history/history_drag_area.h" #include "history/history_item.h" #include "platform/platform_specific.h" #include "lang/lang_keys.h" @@ -38,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_document.h" #include "media/streaming/media_streaming_loader_local.h" +#include "platform/platform_file_utilities.h" #include "storage/localimageloader.h" #include "storage/storage_media_prepare.h" #include "mtproto/mtproto_config.h" @@ -64,6 +66,50 @@ namespace { using namespace ::Media::Streaming; using Data::PhotoSize; +auto ListFromMimeData(not_null data) { + using Error = Storage::PreparedList::Error; + auto result = data->hasUrls() + ? Storage::PrepareMediaList( + // When we edit media, we need only 1 file. + data->urls().mid(0, 1), + st::sendMediaPreviewSize) + : Storage::PreparedList(Error::EmptyFile, QString()); + if (result.error == Error::None) { + return result; + } else if (data->hasImage()) { + auto image = Platform::GetImageFromClipboard(); + if (image.isNull()) { + image = qvariant_cast(data->imageData()); + } + if (!image.isNull()) { + return Storage::PrepareMediaFromImage( + std::move(image), + QByteArray(), + st::sendMediaPreviewSize); + } + } + return result; +} + +auto CheckMimeData(not_null data, bool isAlbum) { + if (data->urls().size() > 1) { + return false; + } else if (data->hasImage()) { + return true; + } + + if (isAlbum && data->hasUrls()) { + const auto url = data->urls().front(); + if (url.isLocalFile()) { + using namespace Core; + const auto info = QFileInfo(Platform::File::UrlToLocal(url)); + return IsMimeAcceptedForAlbum(MimeTypeForFile(info).name()); + } + } + + return true; +} + } // namespace EditCaptionBox::EditCaptionBox( @@ -617,12 +663,8 @@ void EditCaptionBox::prepare() { if (action == Ui::InputField::MimeAction::Check) { if (!data->hasText() && !_isAllowedEditMedia) { return false; - } else if (data->hasImage()) { + } else if (CheckMimeData(data, _isAlbum)) { return true; - } else if (const auto urls = data->urls(); !urls.empty()) { - if (ranges::all_of(urls, &QUrl::isLocalFile)) { - return true; - } } return data->hasText(); } else if (action == Ui::InputField::MimeAction::Insert) { @@ -640,6 +682,8 @@ void EditCaptionBox::prepare() { auto cursor = _field->textCursor(); cursor.movePosition(QTextCursor::End); _field->setTextCursor(cursor); + + setupDragArea(); } bool EditCaptionBox::fileFromClipboard(not_null data) { @@ -647,50 +691,34 @@ bool EditCaptionBox::fileFromClipboard(not_null data) { return false; } using Error = Storage::PreparedList::Error; + using AlbumType = Storage::PreparedFile::AlbumType; + auto list = ListFromMimeData(data); - auto list = [&] { - auto url = QList(); - auto canAddUrl = false; - // When we edit media, we need only 1 file. - if (data->hasUrls()) { - const auto first = data->urls().front(); - url.push_front(first); - canAddUrl = first.isLocalFile(); - } - auto result = canAddUrl - ? Storage::PrepareMediaList(url, st::sendMediaPreviewSize) - : Storage::PreparedList( - Error::EmptyFile, - QString()); - if (result.error == Error::None) { - return result; - } else if (data->hasImage()) { - auto image = Platform::GetImageFromClipboard(); - if (image.isNull()) { - image = qvariant_cast(data->imageData()); - } - if (!image.isNull()) { - _isImage = true; - _photo = true; - return Storage::PrepareMediaFromImage( - std::move(image), - QByteArray(), - st::sendMediaPreviewSize); - } - } - return result; - }(); if (list.error != Error::None || list.files.empty()) { return false; } - if (list.files.front().type == Storage::PreparedFile::AlbumType::None - && _isAlbum) { - Ui::show( - Box(tr::lng_edit_media_album_error(tr::now)), - Ui::LayerOption::KeepOther); + + const auto file = &list.files.front(); + if (_isAlbum && (file->type == AlbumType::None)) { + const auto imageAsDoc = [&] { + using Info = FileMediaInformation; + const auto fileMedia = &file->information->media; + if (const auto image = base::get_if(fileMedia)) { + return !Storage::ValidateThumbDimensions( + image->data.width(), + image->data.height()); + } + return false; + }(); + + if (!data->hasText() || imageAsDoc) { + Ui::show( + Box(tr::lng_edit_media_album_error(tr::now)), + Ui::LayerOption::KeepOther); + } return false; } - + _photo = _isImage = (file->type == AlbumType::Photo); _preparedList = std::move(list); updateEditPreview(); return true; @@ -735,6 +763,35 @@ void EditCaptionBox::setupEmojiPanel() { }); } + +void EditCaptionBox::setupDragArea() { + auto enterFilter = [=](not_null data) { + return !_isAllowedEditMedia ? false : CheckMimeData(data, _isAlbum); + }; + // Avoid both drag areas appearing at one time. + auto computeState = [=](const QMimeData *data) { + const auto state = Storage::ComputeMimeDataState(data); + return (state == Storage::MimeDataState::PhotoFiles) + ? Storage::MimeDataState::Image + : state; + }; + const auto areas = DragArea::SetupDragAreaToContainer( + this, + std::move(enterFilter), + [=](bool f) { _field->setAcceptDrops(f); }, + nullptr, + std::move(computeState)); + + const auto droppedCallback = [=](bool compress) { + return [=](const QMimeData *data) { + fileFromClipboard(data); + Window::ActivateWindow(_controller); + }; + }; + areas.document->setDroppedCallback(droppedCallback(false)); + areas.photo->setDroppedCallback(droppedCallback(true)); +} + void EditCaptionBox::updateBoxSize() { auto newHeight = st::boxPhotoPadding.top() + st::boxPhotoCaptionSkip + _field->height() + errorTopSkip() + st::normalFont->height; if (_photo) { diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 6035bfa86d..4ffc10ffc9 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -79,6 +79,8 @@ private: void updateEmojiPanelGeometry(); void emojiFilterForGeometry(not_null event); + void setupDragArea(); + void save(); void captionResized(); diff --git a/Telegram/SourceFiles/core/mime_type.cpp b/Telegram/SourceFiles/core/mime_type.cpp index 82ecc1d9cd..339415e6a1 100644 --- a/Telegram/SourceFiles/core/mime_type.cpp +++ b/Telegram/SourceFiles/core/mime_type.cpp @@ -111,4 +111,11 @@ bool IsMimeSticker(const QString &mime) { || IsMimeStickerAnimated(mime); } +bool IsMimeAcceptedForAlbum(const QString &mime) { + return (mime == u"image/jpeg"_q) + || (mime == u"image/png"_q) + || (mime == u"video/mp4"_q) + || (mime == u"video/quicktime"_q); +} + } // namespace Core diff --git a/Telegram/SourceFiles/core/mime_type.h b/Telegram/SourceFiles/core/mime_type.h index 1adff6aeae..22a5fda4ce 100644 --- a/Telegram/SourceFiles/core/mime_type.h +++ b/Telegram/SourceFiles/core/mime_type.h @@ -41,5 +41,6 @@ MimeType MimeTypeForData(const QByteArray &data); bool IsMimeStickerAnimated(const QString &mime); bool IsMimeSticker(const QString &mime); +bool IsMimeAcceptedForAlbum(const QString &mime); } // namespace Core diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index d8d27c62f1..ebc6822a0d 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -40,9 +40,10 @@ constexpr auto kDragAreaEvents = { DragArea::Areas DragArea::SetupDragAreaToContainer( not_null container, - Fn &&dragEnterFilter, + Fn)> &&dragEnterFilter, Fn &&setAcceptDropsField, - Fn &&updateControlsGeometry) { + Fn &&updateControlsGeometry, + DragArea::CallbackComputeState &&computeState) { using DragState = Storage::MimeDataState; @@ -161,11 +162,13 @@ DragArea::Areas DragArea::SetupDragAreaToContainer( }; const auto dragEnterEvent = [=](QDragEnterEvent *e) { - if (dragEnterFilter && dragEnterFilter()) { + if (dragEnterFilter && !dragEnterFilter(e->mimeData())) { return; } - *attachDragState = Storage::ComputeMimeDataState(e->mimeData()); + *attachDragState = computeState + ? computeState(e->mimeData()) + : Storage::ComputeMimeDataState(e->mimeData()); updateDragAreas(); if (*attachDragState != DragState::None) { @@ -179,6 +182,10 @@ DragArea::Areas DragArea::SetupDragAreaToContainer( }; const auto dropEvent = [=](QDropEvent *e) { + // Hide fast to avoid visual bugs in resizable boxes. + attachDragDocument->hideFast(); + attachDragPhoto->hideFast(); + *attachDragState = DragState::None; updateDragAreas(); e->acceptProposedAction(); diff --git a/Telegram/SourceFiles/history/history_drag_area.h b/Telegram/SourceFiles/history/history_drag_area.h index 2479b745b3..7fac321227 100644 --- a/Telegram/SourceFiles/history/history_drag_area.h +++ b/Telegram/SourceFiles/history/history_drag_area.h @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" +namespace Storage { +enum class MimeDataState; +} // namespace Storage + class DragArea : public Ui::RpWidget { Q_OBJECT @@ -20,11 +24,16 @@ public: DragArea *document; DragArea *photo; }; + + using CallbackComputeState = + Fn; + static Areas SetupDragAreaToContainer( not_null container, - Fn &&dragEnterFilter = nullptr, + Fn)> &&dragEnterFilter = nullptr, Fn &&setAcceptDropsField = nullptr, - Fn &&updateControlsGeometry = nullptr); + Fn &&updateControlsGeometry = nullptr, + CallbackComputeState &&computeState = nullptr); void setText(const QString &text, const QString &subtext); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 40020994b7..95d73c6e96 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -449,7 +449,9 @@ HistoryWidget::HistoryWidget( _attachDragAreas = DragArea::SetupDragAreaToContainer( this, - crl::guard(this, [=] { return (!_history || !_canSendMessages); }), + crl::guard(this, [=](not_null d) { + return _history && _canSendMessages; + }), crl::guard(this, [=](bool f) { _field->setAcceptDrops(f); }), crl::guard(this, [=] { updateControlsGeometry(); })); _attachDragAreas.document->setDroppedCallback([=](const QMimeData *data) { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index a3dd9b114c..06bfcdb603 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1181,7 +1181,7 @@ void ScheduledWidget::clearSelected() { void ScheduledWidget::setupDragArea() { const auto areas = DragArea::SetupDragAreaToContainer( this, - [=] { return !_history; }, + [=](not_null d) { return _history; }, nullptr, [=] { updateControlsGeometry(); }); diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.cpp b/Telegram/SourceFiles/storage/storage_media_prepare.cpp index 42a6c83551..2b0fef409c 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.cpp +++ b/Telegram/SourceFiles/storage/storage_media_prepare.cpp @@ -291,19 +291,15 @@ std::optional PreparedList::PreparedFileFromFilesDialog( std::move(result.remoteContent), previewWidth); - if (Core::IsMimeSticker(list.files.front().mime)) { + const auto mimeFile = list.files.front().mime; + if (Core::IsMimeSticker(mimeFile)) { errorCallback(tr::lng_edit_media_invalid_file); return std::nullopt; } if (isAlbum) { - const auto albumMimes = { - "image/jpeg", - "image/png", - "video/mp4", - }; const auto file = &list.files.front(); - if (!ranges::contains(albumMimes, file->mime) + if (!Core::IsMimeAcceptedForAlbum(mimeFile) || file->type == Storage::PreparedFile::AlbumType::None) { errorCallback(tr::lng_edit_media_album_error); return std::nullopt;