mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[img-editor] Added wheel zoom and middle-button panning for scene items.
This commit is contained in:
@@ -36,6 +36,9 @@ constexpr auto kMinCanvasZoom = 1.;
|
||||
constexpr auto kMaxCanvasZoom = 8.;
|
||||
constexpr auto kCanvasZoomStep = 1.15;
|
||||
constexpr auto kZoomEpsilon = 0.0001;
|
||||
constexpr auto kMinItemZoom = 0.1;
|
||||
constexpr auto kMaxItemZoom = 10.;
|
||||
constexpr auto kCanvasZoomStepFine = 1.015;
|
||||
|
||||
std::shared_ptr<Scene> EnsureScene(
|
||||
PhotoModifications &mods,
|
||||
@@ -55,13 +58,15 @@ Paint::Paint(
|
||||
PhotoModifications &modifications,
|
||||
const QSize &imageSize,
|
||||
std::shared_ptr<Controllers> controllers,
|
||||
Fn<QImage(QRect)> blurSource)
|
||||
Fn<QImage(QRect)> blurSource,
|
||||
bool fixedCrop)
|
||||
: RpWidget(parent)
|
||||
, _controllers(controllers)
|
||||
, _scene(EnsureScene(modifications, imageSize))
|
||||
, _view(base::make_unique_q<QGraphicsView>(_scene.get(), this))
|
||||
, _viewport(_view->viewport())
|
||||
, _imageSize(imageSize) {
|
||||
, _imageSize(imageSize)
|
||||
, _fixedCrop(fixedCrop) {
|
||||
Expects(modifications.paint != nullptr);
|
||||
|
||||
_scene->setBlurSource(std::move(blurSource));
|
||||
@@ -165,6 +170,51 @@ Paint::Paint(
|
||||
|
||||
}
|
||||
|
||||
bool Paint::zoomSceneItems(float64 wheelDelta, bool fine) {
|
||||
if (!wheelDelta) {
|
||||
return false;
|
||||
}
|
||||
const auto step = wheelDelta
|
||||
/ float64(QWheelEvent::DefaultDeltasPerStep);
|
||||
const auto base = fine ? kCanvasZoomStepFine : kCanvasZoomStep;
|
||||
const auto factor = std::pow(base, step);
|
||||
const auto center = rect::center(_scene->sceneRect());
|
||||
auto applied = false;
|
||||
for (const auto &item : _scene->items()) {
|
||||
const auto raw = item.get();
|
||||
const auto oldScale = raw->scale();
|
||||
const auto newScale = std::clamp(
|
||||
oldScale * factor,
|
||||
kMinItemZoom,
|
||||
kMaxItemZoom);
|
||||
if (std::abs(newScale - oldScale) < kZoomEpsilon) {
|
||||
continue;
|
||||
}
|
||||
const auto ratio = newScale / oldScale;
|
||||
raw->setScale(newScale);
|
||||
const auto pos = raw->pos();
|
||||
raw->setPos(center + (pos - center) * ratio);
|
||||
applied = true;
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
void Paint::panSceneItems(QPointF sceneDelta) {
|
||||
if (sceneDelta.isNull()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &item : _scene->items()) {
|
||||
item->setPos(item->pos() + sceneDelta);
|
||||
}
|
||||
}
|
||||
|
||||
QPointF Paint::mapWidgetDeltaToScene(QPoint delta) const {
|
||||
if (!_view) {
|
||||
return QPointF(delta);
|
||||
}
|
||||
return _view->mapToScene(delta) - _view->mapToScene(QPoint());
|
||||
}
|
||||
|
||||
Paint::~Paint() {
|
||||
if (_viewport) {
|
||||
_viewport->removeEventFilter(this);
|
||||
@@ -393,6 +443,12 @@ bool Paint::eventFilter(QObject *obj, QEvent *e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_fixedCrop) {
|
||||
zoomSceneItems(
|
||||
delta,
|
||||
wheel->modifiers().testFlag(Qt::ShiftModifier));
|
||||
return true;
|
||||
}
|
||||
const auto step = delta / float64(QWheelEvent::DefaultDeltasPerStep);
|
||||
const auto factor = std::pow(kCanvasZoomStep, step);
|
||||
const auto newZoom = std::clamp(
|
||||
@@ -421,7 +477,8 @@ bool Paint::eventFilter(QObject *obj, QEvent *e) {
|
||||
const auto mouse = static_cast<QMouseEvent*>(e);
|
||||
if (mouse->button() == Qt::MiddleButton) {
|
||||
_pan = {
|
||||
.active = (_transform.userZoom > kMinCanvasZoom),
|
||||
.active = (_fixedCrop
|
||||
|| _transform.userZoom > kMinCanvasZoom),
|
||||
.point = mouse->pos(),
|
||||
};
|
||||
if (_pan.active) {
|
||||
@@ -436,7 +493,9 @@ bool Paint::eventFilter(QObject *obj, QEvent *e) {
|
||||
const auto delta = point - _pan.point;
|
||||
_pan.point = point;
|
||||
|
||||
if (_transform.userZoom > kMinCanvasZoom) {
|
||||
if (_fixedCrop) {
|
||||
panSceneItems(mapWidgetDeltaToScene(delta));
|
||||
} else if (_transform.userZoom > kMinCanvasZoom) {
|
||||
view->horizontalScrollBar()->setValue(
|
||||
view->horizontalScrollBar()->value() - delta.x());
|
||||
view->verticalScrollBar()->setValue(
|
||||
|
||||
@@ -29,7 +29,8 @@ public:
|
||||
PhotoModifications &modifications,
|
||||
const QSize &imageSize,
|
||||
std::shared_ptr<Controllers> controllers,
|
||||
Fn<QImage(QRect)> blurSource);
|
||||
Fn<QImage(QRect)> blurSource,
|
||||
bool fixedCrop = false);
|
||||
~Paint() override;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Scene> saveScene() const;
|
||||
@@ -55,6 +56,10 @@ public:
|
||||
void paintImage(QPainter &p, const QPixmap &image) const;
|
||||
void resetView();
|
||||
|
||||
bool zoomSceneItems(float64 wheelDelta, bool fine = false);
|
||||
void panSceneItems(QPointF sceneDelta);
|
||||
[[nodiscard]] QPointF mapWidgetDeltaToScene(QPoint delta) const;
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
void updateViewGeometry();
|
||||
@@ -74,6 +79,7 @@ private:
|
||||
const base::unique_qptr<QGraphicsView> _view;
|
||||
QPointer<QWidget> _viewport;
|
||||
const QSize _imageSize;
|
||||
const bool _fixedCrop = false;
|
||||
QRect _imageGeometry;
|
||||
QRect _outerGeometry;
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "media/view/media_view_pip.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QWheelEvent>
|
||||
|
||||
namespace Editor {
|
||||
|
||||
using Media::View::FlipSizeByRotation;
|
||||
@@ -26,6 +29,7 @@ PhotoEditorContent::PhotoEditorContent(
|
||||
EditorData data)
|
||||
: RpWidget(parent)
|
||||
, _photoSize(photo->size())
|
||||
, _fixedCrop(data.fixedCrop)
|
||||
, _paint(base::make_unique_q<Paint>(
|
||||
this,
|
||||
modifications,
|
||||
@@ -42,7 +46,8 @@ PhotoEditorContent::PhotoEditorContent(
|
||||
auto result = img.copy(pixelRect.intersected(img.rect()));
|
||||
result.setDevicePixelRatio(dpr);
|
||||
return result;
|
||||
}))
|
||||
},
|
||||
data.fixedCrop))
|
||||
, _crop(base::make_unique_q<Crop>(
|
||||
this,
|
||||
modifications,
|
||||
@@ -110,6 +115,47 @@ PhotoEditorContent::PhotoEditorContent(
|
||||
}, lifetime());
|
||||
|
||||
setupDragArea();
|
||||
|
||||
if (_fixedCrop) {
|
||||
const auto pan = _crop->lifetime().make_state<
|
||||
std::optional<QPoint>
|
||||
>();
|
||||
_crop->events(
|
||||
) | rpl::on_next([=](not_null<QEvent*> e) {
|
||||
const auto type = e->type();
|
||||
if (type == QEvent::Wheel) {
|
||||
const auto wheel = static_cast<QWheelEvent*>(e.get());
|
||||
_paint->zoomSceneItems(
|
||||
wheel->angleDelta().y(),
|
||||
wheel->modifiers().testFlag(Qt::ShiftModifier));
|
||||
e->accept();
|
||||
} else if (type == QEvent::MouseButtonPress) {
|
||||
const auto mouse = static_cast<QMouseEvent*>(e.get());
|
||||
if (mouse->button() == Qt::MiddleButton) {
|
||||
*pan = mouse->pos();
|
||||
_crop->setCursor(Qt::ClosedHandCursor);
|
||||
e->accept();
|
||||
}
|
||||
} else if (type == QEvent::MouseMove) {
|
||||
if (pan->has_value()) {
|
||||
const auto mouse = static_cast<QMouseEvent*>(e.get());
|
||||
const auto point = mouse->pos();
|
||||
const auto delta = point - **pan;
|
||||
*pan = point;
|
||||
_paint->panSceneItems(
|
||||
_paint->mapWidgetDeltaToScene(delta));
|
||||
e->accept();
|
||||
}
|
||||
} else if (type == QEvent::MouseButtonRelease) {
|
||||
const auto mouse = static_cast<QMouseEvent*>(e.get());
|
||||
if (mouse->button() == Qt::MiddleButton && pan->has_value()) {
|
||||
pan->reset();
|
||||
_crop->unsetCursor();
|
||||
e->accept();
|
||||
}
|
||||
}
|
||||
}, _crop->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
void PhotoEditorContent::applyModifications(
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
private:
|
||||
|
||||
const QSize _photoSize;
|
||||
const bool _fixedCrop = false;
|
||||
const base::unique_qptr<Paint> _paint;
|
||||
const base::unique_qptr<Crop> _crop;
|
||||
const std::shared_ptr<Image> _photo;
|
||||
|
||||
Reference in New Issue
Block a user