mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Added drag'n'drop area to EditCaptionBox.
This commit is contained in:
parent
6ac9ef34eb
commit
4eaba39a7c
@ -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<const QMimeData*> 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<QImage>(data->imageData());
|
||||
}
|
||||
if (!image.isNull()) {
|
||||
return Storage::PrepareMediaFromImage(
|
||||
std::move(image),
|
||||
QByteArray(),
|
||||
st::sendMediaPreviewSize);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto CheckMimeData(not_null<const QMimeData*> 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<const QMimeData*> data) {
|
||||
@ -647,50 +691,34 @@ bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
|
||||
return false;
|
||||
}
|
||||
using Error = Storage::PreparedList::Error;
|
||||
using AlbumType = Storage::PreparedFile::AlbumType;
|
||||
auto list = ListFromMimeData(data);
|
||||
|
||||
auto list = [&] {
|
||||
auto url = QList<QUrl>();
|
||||
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<QImage>(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<InformBox>(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<Info::Image>(fileMedia)) {
|
||||
return !Storage::ValidateThumbDimensions(
|
||||
image->data.width(),
|
||||
image->data.height());
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (!data->hasText() || imageAsDoc) {
|
||||
Ui::show(
|
||||
Box<InformBox>(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<const QMimeData*> 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) {
|
||||
|
@ -79,6 +79,8 @@ private:
|
||||
void updateEmojiPanelGeometry();
|
||||
void emojiFilterForGeometry(not_null<QEvent*> event);
|
||||
|
||||
void setupDragArea();
|
||||
|
||||
void save();
|
||||
void captionResized();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -40,9 +40,10 @@ constexpr auto kDragAreaEvents = {
|
||||
|
||||
DragArea::Areas DragArea::SetupDragAreaToContainer(
|
||||
not_null<Ui::RpWidget*> container,
|
||||
Fn<bool()> &&dragEnterFilter,
|
||||
Fn<bool(not_null<const QMimeData*>)> &&dragEnterFilter,
|
||||
Fn<void(bool)> &&setAcceptDropsField,
|
||||
Fn<void()> &&updateControlsGeometry) {
|
||||
Fn<void()> &&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();
|
||||
|
@ -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<Storage::MimeDataState(const QMimeData *data)>;
|
||||
|
||||
static Areas SetupDragAreaToContainer(
|
||||
not_null<Ui::RpWidget*> container,
|
||||
Fn<bool()> &&dragEnterFilter = nullptr,
|
||||
Fn<bool(not_null<const QMimeData*>)> &&dragEnterFilter = nullptr,
|
||||
Fn<void(bool)> &&setAcceptDropsField = nullptr,
|
||||
Fn<void()> &&updateControlsGeometry = nullptr);
|
||||
Fn<void()> &&updateControlsGeometry = nullptr,
|
||||
CallbackComputeState &&computeState = nullptr);
|
||||
|
||||
void setText(const QString &text, const QString &subtext);
|
||||
|
||||
|
@ -449,7 +449,9 @@ HistoryWidget::HistoryWidget(
|
||||
|
||||
_attachDragAreas = DragArea::SetupDragAreaToContainer(
|
||||
this,
|
||||
crl::guard(this, [=] { return (!_history || !_canSendMessages); }),
|
||||
crl::guard(this, [=](not_null<const QMimeData*> d) {
|
||||
return _history && _canSendMessages;
|
||||
}),
|
||||
crl::guard(this, [=](bool f) { _field->setAcceptDrops(f); }),
|
||||
crl::guard(this, [=] { updateControlsGeometry(); }));
|
||||
_attachDragAreas.document->setDroppedCallback([=](const QMimeData *data) {
|
||||
|
@ -1181,7 +1181,7 @@ void ScheduledWidget::clearSelected() {
|
||||
void ScheduledWidget::setupDragArea() {
|
||||
const auto areas = DragArea::SetupDragAreaToContainer(
|
||||
this,
|
||||
[=] { return !_history; },
|
||||
[=](not_null<const QMimeData*> d) { return _history; },
|
||||
nullptr,
|
||||
[=] { updateControlsGeometry(); });
|
||||
|
||||
|
@ -291,19 +291,15 @@ std::optional<PreparedList> 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;
|
||||
|
Loading…
Reference in New Issue
Block a user