mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Added ability to drag and drop images in photo editor.
This commit is contained in:
parent
a93ec9c2c2
commit
96b40f43e9
14 changed files with 246 additions and 18 deletions
|
@ -533,6 +533,8 @@ PRIVATE
|
|||
editor/scene/scene_item_base.h
|
||||
editor/scene/scene_item_canvas.cpp
|
||||
editor/scene/scene_item_canvas.h
|
||||
editor/scene/scene_item_image.cpp
|
||||
editor/scene/scene_item_image.h
|
||||
editor/scene/scene_item_line.cpp
|
||||
editor/scene/scene_item_line.h
|
||||
editor/scene/scene_item_sticker.cpp
|
||||
|
|
|
@ -9,21 +9,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "editor/controllers/stickers_panel_controller.h"
|
||||
#include "editor/controllers/undo_controller.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Editor {
|
||||
|
||||
struct Controllers final {
|
||||
Controllers(
|
||||
std::unique_ptr<StickersPanelController> stickersPanelController,
|
||||
std::unique_ptr<UndoController> undoController)
|
||||
std::unique_ptr<UndoController> undoController,
|
||||
Fn<void(object_ptr<Ui::BoxContent>)> showBox)
|
||||
: stickersPanelController(std::move(stickersPanelController))
|
||||
, undoController(std::move(undoController)) {
|
||||
, undoController(std::move(undoController))
|
||||
, showBox(std::move(showBox)) {
|
||||
}
|
||||
~Controllers() {
|
||||
};
|
||||
|
||||
const std::unique_ptr<StickersPanelController> stickersPanelController;
|
||||
const std::unique_ptr<UndoController> undoController;
|
||||
const Fn<void(object_ptr<Ui::BoxContent>)> showBox;
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
|
|
|
@ -7,14 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "editor/editor_paint.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "editor/controllers/controllers.h"
|
||||
#include "editor/scene/scene.h"
|
||||
#include "editor/scene/scene_item_base.h"
|
||||
#include "editor/scene/scene_item_canvas.h"
|
||||
#include "editor/scene/scene_item_image.h"
|
||||
#include "editor/scene/scene_item_sticker.h"
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
namespace Editor {
|
||||
namespace {
|
||||
|
@ -46,6 +52,7 @@ Paint::Paint(
|
|||
const QSize &imageSize,
|
||||
std::shared_ptr<Controllers> controllers)
|
||||
: RpWidget(parent)
|
||||
, _controllers(controllers)
|
||||
, _lastZ(std::make_shared<float64>(9000.))
|
||||
, _scene(EnsureScene(modifications, imageSize))
|
||||
, _view(base::make_unique_q<QGraphicsView>(_scene.get(), this))
|
||||
|
@ -256,4 +263,45 @@ void Paint::applyBrush(const Brush &brush) {
|
|||
(kMinBrush + float64(kMaxBrush - kMinBrush) * brush.sizeRatio));
|
||||
}
|
||||
|
||||
void Paint::handleMimeData(const QMimeData *data) {
|
||||
const auto add = [&](QImage image) {
|
||||
if (image.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto s = _scene->sceneRect().size();
|
||||
const auto size = std::min(s.width(), s.height()) / 2;
|
||||
const auto x = s.width() / 2;
|
||||
const auto y = s.height() / 2;
|
||||
if (!Ui::ValidateThumbDimensions(image.width(), image.height())) {
|
||||
_controllers->showBox(
|
||||
Box<InformBox>(tr::lng_edit_media_invalid_file(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto item = std::make_shared<ItemImage>(
|
||||
App::pixmapFromImageInPlace(std::move(image)),
|
||||
_transform.zoom.value(),
|
||||
_lastZ,
|
||||
size,
|
||||
x,
|
||||
y);
|
||||
item->setFlip(_transform.flipped);
|
||||
item->setRotation(-_transform.angle);
|
||||
_scene->addItem(item);
|
||||
_scene->clearSelection();
|
||||
};
|
||||
|
||||
using Error = Ui::PreparedList::Error;
|
||||
auto result = data->hasUrls()
|
||||
? Storage::PrepareMediaList(
|
||||
data->urls().mid(0, 1),
|
||||
_imageSize.width() / 2)
|
||||
: Ui::PreparedList(Error::EmptyFile, QString());
|
||||
if (result.error == Error::None) {
|
||||
add(base::take(result.files.front().preview));
|
||||
} else if (data->hasImage()) {
|
||||
add(qvariant_cast<QImage>(data->imageData()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
void keepResult();
|
||||
void updateUndoState();
|
||||
|
||||
void handleMimeData(const QMimeData *data);
|
||||
|
||||
private:
|
||||
struct SavedItem {
|
||||
std::shared_ptr<QGraphicsItem> item;
|
||||
|
@ -50,6 +52,7 @@ private:
|
|||
bool isItemToRemove(const std::shared_ptr<QGraphicsItem> &item) const;
|
||||
bool isItemHidden(const std::shared_ptr<QGraphicsItem> &item) const;
|
||||
|
||||
const std::shared_ptr<Controllers> _controllers;
|
||||
const std::shared_ptr<float64> _lastZ;
|
||||
const std::shared_ptr<Scene> _scene;
|
||||
const base::unique_qptr<QGraphicsView> _view;
|
||||
|
|
|
@ -58,7 +58,8 @@ PhotoEditor::PhotoEditor(
|
|||
this,
|
||||
controller->sessionController())
|
||||
: nullptr,
|
||||
std::make_unique<UndoController>()))
|
||||
std::make_unique<UndoController>(),
|
||||
[=] (object_ptr<Ui::BoxContent> c) { controller->show(std::move(c)); }))
|
||||
, _content(base::make_unique_q<PhotoEditorContent>(
|
||||
this,
|
||||
photo,
|
||||
|
|
|
@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "editor/editor_crop.h"
|
||||
#include "editor/editor_paint.h"
|
||||
#include "history/history_drag_area.h"
|
||||
#include "media/view/media_view_pip.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
|
||||
namespace Editor {
|
||||
|
||||
|
@ -92,6 +94,8 @@ PhotoEditorContent::PhotoEditorContent(
|
|||
_imageRect,
|
||||
_photo->pix(_imageRect.width(), _imageRect.height()));
|
||||
}, lifetime());
|
||||
|
||||
setupDragArea();
|
||||
}
|
||||
|
||||
void PhotoEditorContent::applyModifications(
|
||||
|
@ -133,4 +137,24 @@ bool PhotoEditorContent::handleKeyPress(not_null<QKeyEvent*> e) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void PhotoEditorContent::setupDragArea() {
|
||||
auto dragEnterFilter = [=](const QMimeData *data) {
|
||||
return (_mode.mode == PhotoEditorMode::Mode::Transform)
|
||||
? false
|
||||
: Storage::ValidatePhotoEditorMediaDragData(data);
|
||||
};
|
||||
|
||||
const auto areas = DragArea::SetupDragAreaToContainer(
|
||||
this,
|
||||
std::move(dragEnterFilter),
|
||||
nullptr,
|
||||
nullptr,
|
||||
[](const QMimeData *d) { return Storage::MimeDataState::Image; },
|
||||
true);
|
||||
|
||||
areas.photo->setDroppedCallback([=](const QMimeData *data) {
|
||||
_paint->handleMimeData(data);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
|
||||
bool handleKeyPress(not_null<QKeyEvent*> e) const;
|
||||
|
||||
void setupDragArea();
|
||||
|
||||
private:
|
||||
|
||||
const QSize _photoSize;
|
||||
|
|
|
@ -74,6 +74,10 @@ ItemBase::ItemBase(
|
|||
.min = int(st::photoEditorItemMinSize / zoom),
|
||||
.max = int(st::photoEditorItemMaxSize / zoom),
|
||||
};
|
||||
_horizontalSize = std::clamp(
|
||||
_horizontalSize,
|
||||
float64(_sizeLimits.min),
|
||||
float64(_sizeLimits.max));
|
||||
|
||||
updatePens(QPen(
|
||||
QBrush(),
|
||||
|
@ -303,7 +307,13 @@ float64 ItemBase::size() const {
|
|||
}
|
||||
|
||||
void ItemBase::updateVerticalSize() {
|
||||
_verticalSize = _horizontalSize * _aspectRatio;
|
||||
const auto verticalSize = _horizontalSize * _aspectRatio;
|
||||
_verticalSize = std::max(
|
||||
verticalSize,
|
||||
float64(st::photoEditorItemMinSize));
|
||||
if (verticalSize < st::photoEditorItemMinSize) {
|
||||
_horizontalSize = _verticalSize / _aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
void ItemBase::setAspectRatio(float64 aspectRatio) {
|
||||
|
|
58
Telegram/SourceFiles/editor/scene/scene_item_image.cpp
Normal file
58
Telegram/SourceFiles/editor/scene/scene_item_image.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "editor/scene/scene_item_image.h"
|
||||
|
||||
namespace Editor {
|
||||
namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
ItemImage::ItemImage(
|
||||
const QPixmap &&pixmap,
|
||||
rpl::producer<float64> zoomValue,
|
||||
std::shared_ptr<float64> zPtr,
|
||||
int size,
|
||||
int x,
|
||||
int y)
|
||||
: ItemBase(std::move(zoomValue), std::move(zPtr), size, x, y)
|
||||
, _pixmap(std::move(pixmap)) {
|
||||
setAspectRatio(_pixmap.isNull()
|
||||
? 1.0
|
||||
: (_pixmap.height() / float64(_pixmap.width())));
|
||||
}
|
||||
|
||||
void ItemImage::paint(
|
||||
QPainter *p,
|
||||
const QStyleOptionGraphicsItem *option,
|
||||
QWidget *w) {
|
||||
p->drawPixmap(contentRect().toRect(), _pixmap);
|
||||
ItemBase::paint(p, option, w);
|
||||
}
|
||||
|
||||
void ItemImage::performFlip() {
|
||||
_pixmap = _pixmap.transformed(QTransform().scale(-1, 1));
|
||||
update();
|
||||
}
|
||||
|
||||
std::shared_ptr<ItemBase> ItemImage::duplicate(
|
||||
rpl::producer<float64> zoomValue,
|
||||
std::shared_ptr<float64> zPtr,
|
||||
int size,
|
||||
int x,
|
||||
int y) const {
|
||||
auto pixmap = _pixmap;
|
||||
return std::make_shared<ItemImage>(
|
||||
std::move(pixmap),
|
||||
std::move(zoomValue),
|
||||
std::move(zPtr),
|
||||
size,
|
||||
x,
|
||||
y);
|
||||
}
|
||||
|
||||
} // namespace Editor
|
40
Telegram/SourceFiles/editor/scene/scene_item_image.h
Normal file
40
Telegram/SourceFiles/editor/scene/scene_item_image.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "editor/scene/scene_item_base.h"
|
||||
|
||||
namespace Editor {
|
||||
|
||||
class ItemImage : public ItemBase {
|
||||
public:
|
||||
ItemImage(
|
||||
const QPixmap &&pixmap,
|
||||
rpl::producer<float64> zoomValue,
|
||||
std::shared_ptr<float64> zPtr,
|
||||
int size,
|
||||
int x,
|
||||
int y);
|
||||
void paint(
|
||||
QPainter *p,
|
||||
const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget) override;
|
||||
protected:
|
||||
void performFlip() override;
|
||||
std::shared_ptr<ItemBase> duplicate(
|
||||
rpl::producer<float64> zoomValue,
|
||||
std::shared_ptr<float64> zPtr,
|
||||
int size,
|
||||
int x,
|
||||
int y) const override;
|
||||
private:
|
||||
QPixmap _pixmap;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Editor
|
|
@ -51,7 +51,8 @@ DragArea::Areas DragArea::SetupDragAreaToContainer(
|
|||
Fn<bool(not_null<const QMimeData*>)> &&dragEnterFilter,
|
||||
Fn<void(bool)> &&setAcceptDropsField,
|
||||
Fn<void()> &&updateControlsGeometry,
|
||||
DragArea::CallbackComputeState &&computeState) {
|
||||
DragArea::CallbackComputeState &&computeState,
|
||||
bool hideSubtext) {
|
||||
|
||||
using DragState = Storage::MimeDataState;
|
||||
|
||||
|
@ -133,24 +134,32 @@ DragArea::Areas DragArea::SetupDragAreaToContainer(
|
|||
case DragState::Files:
|
||||
attachDragDocument->setText(
|
||||
tr::lng_drag_files_here(tr::now),
|
||||
tr::lng_drag_to_send_files(tr::now));
|
||||
hideSubtext
|
||||
? QString()
|
||||
: tr::lng_drag_to_send_files(tr::now));
|
||||
attachDragDocument->otherEnter();
|
||||
attachDragPhoto->hideFast();
|
||||
break;
|
||||
case DragState::PhotoFiles:
|
||||
attachDragDocument->setText(
|
||||
tr::lng_drag_images_here(tr::now),
|
||||
tr::lng_drag_to_send_no_compression(tr::now));
|
||||
hideSubtext
|
||||
? QString()
|
||||
: tr::lng_drag_to_send_no_compression(tr::now));
|
||||
attachDragPhoto->setText(
|
||||
tr::lng_drag_photos_here(tr::now),
|
||||
tr::lng_drag_to_send_quick(tr::now));
|
||||
hideSubtext
|
||||
? QString()
|
||||
: tr::lng_drag_to_send_quick(tr::now));
|
||||
attachDragDocument->otherEnter();
|
||||
attachDragPhoto->otherEnter();
|
||||
break;
|
||||
case DragState::Image:
|
||||
attachDragPhoto->setText(
|
||||
tr::lng_drag_images_here(tr::now),
|
||||
tr::lng_drag_to_send_quick(tr::now));
|
||||
hideSubtext
|
||||
? QString()
|
||||
: tr::lng_drag_to_send_quick(tr::now));
|
||||
attachDragDocument->hideFast();
|
||||
attachDragPhoto->otherEnter();
|
||||
break;
|
||||
|
|
|
@ -32,7 +32,8 @@ public:
|
|||
Fn<bool(not_null<const QMimeData*>)> &&dragEnterFilter = nullptr,
|
||||
Fn<void(bool)> &&setAcceptDropsField = nullptr,
|
||||
Fn<void()> &&updateControlsGeometry = nullptr,
|
||||
CallbackComputeState &&computeState = nullptr);
|
||||
CallbackComputeState &&computeState = nullptr,
|
||||
bool hideSubtext = false);
|
||||
|
||||
void setText(const QString &text, const QString &subtext);
|
||||
|
||||
|
|
|
@ -85,6 +85,27 @@ void PrepareDetailsInParallel(PreparedList &result, int previewWidth) {
|
|||
|
||||
} // namespace
|
||||
|
||||
bool ValidatePhotoEditorMediaDragData(not_null<const QMimeData*> data) {
|
||||
if (data->urls().size() > 1) {
|
||||
return false;
|
||||
} else if (data->hasImage()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data->hasUrls()) {
|
||||
const auto url = data->urls().front();
|
||||
if (url.isLocalFile()) {
|
||||
using namespace Core;
|
||||
const auto info = QFileInfo(Platform::File::UrlToLocal(url));
|
||||
const auto filename = info.fileName();
|
||||
return FileIsImage(filename, MimeTypeForFile(info).name())
|
||||
&& HasExtensionFrom(filename, Ui::ExtensionsForCompression());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidateEditMediaDragData(
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::AlbumType albumType) {
|
||||
|
@ -173,7 +194,6 @@ PreparedList PrepareMediaList(const QList<QUrl> &files, int previewWidth) {
|
|||
PreparedList PrepareMediaList(const QStringList &files, int previewWidth) {
|
||||
auto result = PreparedList();
|
||||
result.files.reserve(files.size());
|
||||
const auto extensionsToCompress = Ui::ExtensionsForCompression();
|
||||
for (const auto &file : files) {
|
||||
const auto fileinfo = QFileInfo(file);
|
||||
const auto filesize = fileinfo.size();
|
||||
|
|
|
@ -26,24 +26,30 @@ enum class MimeDataState {
|
|||
Image,
|
||||
};
|
||||
|
||||
std::optional<Ui::PreparedList> PreparedFileFromFilesDialog(
|
||||
[[nodiscard]] std::optional<Ui::PreparedList> PreparedFileFromFilesDialog(
|
||||
FileDialog::OpenResult &&result,
|
||||
Fn<bool(const Ui::PreparedList&)> checkResult,
|
||||
Fn<void(tr::phrase<>)> errorCallback,
|
||||
int previewWidth);
|
||||
MimeDataState ComputeMimeDataState(const QMimeData *data);
|
||||
bool ValidateEditMediaDragData(
|
||||
[[nodiscard]] MimeDataState ComputeMimeDataState(const QMimeData *data);
|
||||
[[nodiscard]] bool ValidatePhotoEditorMediaDragData(
|
||||
not_null<const QMimeData*> data);
|
||||
[[nodiscard]] bool ValidateEditMediaDragData(
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::AlbumType albumType);
|
||||
Ui::PreparedList PrepareMediaList(const QList<QUrl> &files, int previewWidth);
|
||||
Ui::PreparedList PrepareMediaList(const QStringList &files, int previewWidth);
|
||||
Ui::PreparedList PrepareMediaFromImage(
|
||||
[[nodiscard]] Ui::PreparedList PrepareMediaList(
|
||||
const QList<QUrl> &files,
|
||||
int previewWidth);
|
||||
[[nodiscard]] Ui::PreparedList PrepareMediaList(
|
||||
const QStringList &files,
|
||||
int previewWidth);
|
||||
[[nodiscard]] Ui::PreparedList PrepareMediaFromImage(
|
||||
QImage &&image,
|
||||
QByteArray &&content,
|
||||
int previewWidth);
|
||||
void PrepareDetails(Ui::PreparedFile &file, int previewWidth);
|
||||
void UpdateImageDetails(Ui::PreparedFile &file, int previewWidth);
|
||||
|
||||
bool ApplyModifications(const Ui::PreparedList &list);
|
||||
[[nodiscard]] bool ApplyModifications(const Ui::PreparedList &list);
|
||||
|
||||
} // namespace Storage
|
||||
|
|
Loading…
Add table
Reference in a new issue