diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index b4773f00a6..76e7f31116 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -522,6 +522,8 @@ PRIVATE editor/photo_editor_controls.h editor/photo_editor_layer_widget.cpp editor/photo_editor_layer_widget.h + editor/scene_item_base.cpp + editor/scene_item_base.h editor/undo_controller.cpp editor/undo_controller.h export/export_manager.cpp diff --git a/Telegram/SourceFiles/editor/editor.style b/Telegram/SourceFiles/editor/editor.style index c143feecab..8556ab0bc3 100644 --- a/Telegram/SourceFiles/editor/editor.style +++ b/Telegram/SourceFiles/editor/editor.style @@ -56,3 +56,7 @@ photoEditorColorPickerCircleSkip: 50px; photoEditorCropPointSize: 10px; photoEditorCropMinSize: 20px; + +photoEditorItemHandleSize: 10px; +photoEditorItemMinSize: 16px; +photoEditorItemMaxSize: 512px; diff --git a/Telegram/SourceFiles/editor/scene_item_base.cpp b/Telegram/SourceFiles/editor/scene_item_base.cpp new file mode 100644 index 0000000000..3ebc40311b --- /dev/null +++ b/Telegram/SourceFiles/editor/scene_item_base.cpp @@ -0,0 +1,174 @@ +/* +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_item_base.h" + +#include "styles/style_editor.h" + +#include +#include +#include + +namespace Editor { +namespace { + +auto Normalized(float64 angle) { + return angle + + ((std::abs(angle) < 360) ? 0 : (-360 * (angle < 0 ? -1 : 1))); +} + +QPen PenStyled(QPen pen, Qt::PenStyle style) { + pen.setStyle(style); + return pen; +} + +} // namespace + +ItemBase::ItemBase(std::shared_ptr zPtr, int size, int x, int y) +: _lastZ(zPtr) +, _handleSize(st::photoEditorItemHandleSize) +, _innerMargins( + _handleSize / 2, + _handleSize / 2, + _handleSize / 2, + _handleSize / 2) +, _selectPen(QBrush(Qt::white), 1, Qt::DashLine, Qt::SquareCap, Qt::RoundJoin) +, _selectPenInactive( + QBrush(Qt::gray), + 1, + Qt::DashLine, + Qt::SquareCap, + Qt::RoundJoin) +, _size(size) { + setFlags(QGraphicsItem::ItemIsMovable + | QGraphicsItem::ItemIsSelectable + | QGraphicsItem::ItemIsFocusable); + setAcceptHoverEvents(true); + setPos(x, y); +} + +QRectF ItemBase::boundingRect() const { + return innerRect() + _innerMargins; +} + +QRectF ItemBase::innerRect() const { + return QRectF(-_size / 2, -_size / 2, _size, _size); +} + +void ItemBase::paint( + QPainter *p, + const QStyleOptionGraphicsItem *option, + QWidget *) { + if (!(option->state & QStyle::State_Selected)) { + return; + } + PainterHighQualityEnabler hq(*p); + const auto &pen = (option->state & QStyle::State_HasFocus) + ? _selectPen + : _selectPenInactive; + p->setPen(pen); + p->drawRect(innerRect()); + + p->setPen(PenStyled(pen, Qt::SolidLine)); + p->setBrush(st::photoEditorItemBaseHandleFg); + p->drawEllipse(rightHandleRect()); + p->drawEllipse(leftHandleRect()); +} + +void ItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (isHandling()) { + const auto mousePos = event->pos(); + const auto isLeft = (_handle == HandleType::Left); + // Resize. + const auto p = isLeft ? (mousePos * -1) : mousePos; + const auto dx = int(2.0 * p.x()); + const auto dy = int(2.0 * p.y()); + prepareGeometryChange(); + _size = std::clamp( + (dx > dy ? dx : dy), + st::photoEditorItemMinSize, + st::photoEditorItemMaxSize); + + // Rotate. + const auto origin = mapToScene(boundingRect().center()); + const auto pos = mapToScene(mousePos); + + const auto diff = pos - origin; + const auto angle = Normalized((isLeft ? 180 : 0) + + (std::atan2(diff.y(), diff.x()) * 180 / M_PI)); + setRotation(angle); + } else { + QGraphicsItem::mouseMoveEvent(event); + } +} + +void ItemBase::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + setCursor(isHandling() + ? Qt::ClosedHandCursor + : (handleType(event->pos()) != HandleType::None) && isSelected() + ? Qt::OpenHandCursor + : Qt::ArrowCursor); + QGraphicsItem::hoverMoveEvent(event); +} + +void ItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event) { + setZValue((*_lastZ)++); + if (event->button() == Qt::LeftButton) { + _handle = handleType(event->pos()); + if (isHandling()) { + setCursor(Qt::ClosedHandCursor); + } + } else { + QGraphicsItem::mousePressEvent(event); + } +} + +void ItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if ((event->button() == Qt::LeftButton) && isHandling()) { + _handle = HandleType::None; + } else { + QGraphicsItem::mouseReleaseEvent(event); + } +} + +int ItemBase::type() const { + return Type; +} + +QRectF ItemBase::rightHandleRect() const { + return QRectF( + (_size / 2) - (_handleSize / 2), + 0 - (_handleSize / 2), + _handleSize, + _handleSize); +} + +QRectF ItemBase::leftHandleRect() const { + return QRectF( + (-_size / 2) - (_handleSize / 2), + 0 - (_handleSize / 2), + _handleSize, + _handleSize); +} + +bool ItemBase::isHandling() const { + return _handle != HandleType::None; +} + +int ItemBase::size() const { + return _size; +} + +ItemBase::HandleType ItemBase::handleType(const QPointF &pos) const { + return rightHandleRect().contains(pos) + ? HandleType::Right + : leftHandleRect().contains(pos) + ? HandleType::Left + : HandleType::None; +} + +} // namespace Editor diff --git a/Telegram/SourceFiles/editor/scene_item_base.h b/Telegram/SourceFiles/editor/scene_item_base.h new file mode 100644 index 0000000000..b77941fed2 --- /dev/null +++ b/Telegram/SourceFiles/editor/scene_item_base.h @@ -0,0 +1,61 @@ +/* +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 + +class QGraphicsSceneHoverEvent; +class QGraphicsSceneMouseEvent; +class QStyleOptionGraphicsItem; + +namespace Editor { + +class ItemBase : public QGraphicsItem { +public: + enum { Type = UserType + 1 }; + + ItemBase(std::shared_ptr zPtr, int size, int x, int y); + QRectF boundingRect() const override; + void paint( + QPainter *p, + const QStyleOptionGraphicsItem *option, + QWidget *widget) override; +protected: + enum HandleType { + None, + Left, + Right, + }; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + + int type() const override; + QRectF innerRect() const; + int size() const; + +private: + HandleType handleType(const QPointF &pos) const; + QRectF rightHandleRect() const; + QRectF leftHandleRect() const; + bool isHandling() const; + + const std::shared_ptr _lastZ; + const int _handleSize; + const QMargins _innerMargins; + const QPen _selectPen; + const QPen _selectPenInactive; + const QPen _handlePen; + + int _size; + HandleType _handle = HandleType::None; + +}; + +} // namespace Editor