Removed using of raw pointers for QGraphicsItem in photo editor.

Now all items are wrapped in the shared_ptr,
and the Scene loses ownership of all items before being destroyed.
This commit is contained in:
23rd 2021-03-10 18:51:23 +03:00
parent 2791f89f30
commit fde7cef9c8
5 changed files with 76 additions and 44 deletions

View file

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "editor/scene_item_canvas.h" #include "editor/scene_item_canvas.h"
#include "editor/scene_item_sticker.h" #include "editor/scene_item_sticker.h"
#include "editor/controllers.h" #include "editor/controllers.h"
#include "base/event_filter.h" #include "lottie/lottie_single_player.h"
#include <QGraphicsView> #include <QGraphicsView>
@ -38,6 +38,8 @@ std::shared_ptr<Scene> EnsureScene(
} // namespace } // namespace
using ItemPtr = Scene::ItemPtr;
Paint::Paint( Paint::Paint(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
PhotoModifications &modifications, PhotoModifications &modifications,
@ -71,7 +73,7 @@ Paint::Paint(
? Qt::DescendingOrder ? Qt::DescendingOrder
: Qt::AscendingOrder); : Qt::AscendingOrder);
auto proj = [&](QGraphicsItem *i) { auto proj = [&](const ItemPtr &i) {
return isUndo ? i->isVisible() : isItemHidden(i); return isUndo ? i->isVisible() : isItemHidden(i);
}; };
const auto it = ranges::find_if(filtered, std::move(proj)); const auto it = ranges::find_if(filtered, std::move(proj));
@ -108,7 +110,7 @@ Paint::Paint(
const auto size = std::min(s.width(), s.height()) / 2; const auto size = std::min(s.width(), s.height()) / 2;
const auto x = s.width() / 2; const auto x = s.width() / 2;
const auto y = s.height() / 2; const auto y = s.height() / 2;
const auto item = new ItemSticker( const auto item = std::make_shared<ItemSticker>(
document, document,
_zoom.value(), _zoom.value(),
_lastZ, _lastZ,
@ -169,13 +171,13 @@ void Paint::cancel() {
return; return;
} }
for (const auto &group : filtered) { for (const auto &item : filtered) {
const auto it = ranges::find( const auto it = ranges::find(
_previousItems, _previousItems,
group, item,
&SavedItem::item); &SavedItem::item);
if (it == end(_previousItems)) { if (it == end(_previousItems)) {
_scene->removeItem(group); _scene->removeItem(item);
} else { } else {
it->item->setVisible(!it->undid); it->item->setVisible(!it->undid);
} }
@ -188,11 +190,12 @@ void Paint::keepResult() {
for (const auto &item : _itemsToRemove) { for (const auto &item : _itemsToRemove) {
_scene->removeItem(item); _scene->removeItem(item);
} }
_itemsToRemove.clear();
const auto items = _scene->items(); const auto items = _scene->items();
_previousItems = ranges::views::all( _previousItems = ranges::views::all(
items items
) | ranges::views::transform([=](QGraphicsItem *i) -> SavedItem { ) | ranges::views::transform([=](ItemPtr i) -> SavedItem {
return { i, !i->isVisible() }; return { i, !i->isVisible() };
}) | ranges::to_vector; }) | ranges::to_vector;
} }
@ -204,7 +207,7 @@ bool Paint::hasUndo() const {
bool Paint::hasRedo() const { bool Paint::hasRedo() const {
return ranges::any_of( return ranges::any_of(
_scene->items(), _scene->items(),
[=](QGraphicsItem *i) { return isItemHidden(i); }); [=](const ItemPtr &i) { return isItemHidden(i); });
} }
void Paint::clearRedoList() { void Paint::clearRedoList() {
@ -212,10 +215,10 @@ void Paint::clearRedoList() {
auto &&filtered = ranges::views::all( auto &&filtered = ranges::views::all(
items items
) | ranges::views::filter( ) | ranges::views::filter(
[=](QGraphicsItem *i) { return isItemHidden(i); } [=](const ItemPtr &i) { return isItemHidden(i); }
); );
ranges::for_each(std::move(filtered), [&](QGraphicsItem *item) { ranges::for_each(std::move(filtered), [&](ItemPtr item) {
item->hide(); item->hide();
_itemsToRemove.push_back(item); _itemsToRemove.push_back(item);
}); });
@ -223,12 +226,12 @@ void Paint::clearRedoList() {
_hasRedo = false; _hasRedo = false;
} }
bool Paint::isItemHidden(not_null<QGraphicsItem*> item) const { bool Paint::isItemHidden(const ItemPtr &item) const {
return !item->isVisible() && !isItemToRemove(item); return !item->isVisible() && !isItemToRemove(item);
} }
bool Paint::isItemToRemove(not_null<QGraphicsItem*> item) const { bool Paint::isItemToRemove(const ItemPtr &item) const {
return ranges::contains(_itemsToRemove, item.get()); return ranges::contains(_itemsToRemove, item);
} }
void Paint::updateUndoState() { void Paint::updateUndoState() {

View file

@ -39,7 +39,7 @@ public:
private: private:
struct SavedItem { struct SavedItem {
QGraphicsItem *item; std::shared_ptr<QGraphicsItem> item;
bool undid = false; bool undid = false;
}; };
@ -47,8 +47,8 @@ private:
bool hasRedo() const; bool hasRedo() const;
void clearRedoList(); void clearRedoList();
bool isItemToRemove(not_null<QGraphicsItem*> item) const; bool isItemToRemove(const std::shared_ptr<QGraphicsItem> &item) const;
bool isItemHidden(not_null<QGraphicsItem*> item) const; bool isItemHidden(const std::shared_ptr<QGraphicsItem> &item) const;
const std::shared_ptr<float64> _lastZ; const std::shared_ptr<float64> _lastZ;
const std::shared_ptr<Scene> _scene; const std::shared_ptr<Scene> _scene;
@ -56,7 +56,7 @@ private:
const QSize _imageSize; const QSize _imageSize;
std::vector<SavedItem> _previousItems; std::vector<SavedItem> _previousItems;
QList<QGraphicsItem*> _itemsToRemove; std::vector<std::shared_ptr<QGraphicsItem>> _itemsToRemove;
rpl::variable<bool> _hasUndo = true; rpl::variable<bool> _hasUndo = true;
rpl::variable<bool> _hasRedo = true; rpl::variable<bool> _hasRedo = true;

View file

@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Editor { namespace Editor {
namespace { namespace {
using ItemPtr = Scene::ItemPtr;
bool SkipMouseEvent(not_null<QGraphicsSceneMouseEvent*> event) { bool SkipMouseEvent(not_null<QGraphicsSceneMouseEvent*> event) {
return event->isAccepted() || (event->button() == Qt::RightButton); return event->isAccepted() || (event->button() == Qt::RightButton);
} }
@ -25,25 +27,44 @@ bool SkipMouseEvent(not_null<QGraphicsSceneMouseEvent*> event) {
Scene::Scene(const QRectF &rect) Scene::Scene(const QRectF &rect)
: QGraphicsScene(rect) : QGraphicsScene(rect)
, _canvas(new ItemCanvas) { , _canvas(std::make_shared<ItemCanvas>()) {
QGraphicsScene::addItem(_canvas); QGraphicsScene::addItem(_canvas.get());
_canvas->clearPixmap(); _canvas->clearPixmap();
_canvas->grabContentRequests( _canvas->grabContentRequests(
) | rpl::start_with_next([=](ItemCanvas::Content &&content) { ) | rpl::start_with_next([=](ItemCanvas::Content &&content) {
const auto item = new ItemLine(std::move(content.pixmap)); const auto item = std::make_shared<ItemLine>(
std::move(content.pixmap));
item->setPos(content.position); item->setPos(content.position);
addItem(item); addItem(item);
_canvas->setZValue(++_lastLineZ); _canvas->setZValue(++_lastLineZ);
}, _lifetime); }, _lifetime);
} }
void Scene::addItem(not_null<NumberedItem*> item) { void Scene::addItem(std::shared_ptr<NumberedItem> item) {
if (!item) {
return;
}
item->setNumber(_itemNumber++); item->setNumber(_itemNumber++);
QGraphicsScene::addItem(item); QGraphicsScene::addItem(item.get());
_items.push_back(std::move(item));
_addsItem.fire({}); _addsItem.fire({});
} }
void Scene::removeItem(not_null<QGraphicsItem*> item) {
const auto it = ranges::find_if(_items, [&](const ItemPtr &i) {
return i.get() == item;
});
if (it == end(_items)) {
return;
}
removeItem(*it);
}
void Scene::removeItem(const ItemPtr &item) {
_items.erase(ranges::remove(_items, item), end(_items));
}
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) { void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QGraphicsScene::mousePressEvent(event); QGraphicsScene::mousePressEvent(event);
if (SkipMouseEvent(event)) { if (SkipMouseEvent(event)) {
@ -81,23 +102,19 @@ rpl::producer<> Scene::addsItem() const {
return _addsItem.events(); return _addsItem.events();
} }
std::vector<QGraphicsItem*> Scene::items(Qt::SortOrder order) const { std::vector<ItemPtr> Scene::items(
using Item = QGraphicsItem; Qt::SortOrder order) const {
auto rawItems = QGraphicsScene::items(); auto copyItems = _items;
auto filteredItems = ranges::views::all( ranges::sort(copyItems, [&](ItemPtr a, ItemPtr b) {
rawItems const auto numA = qgraphicsitem_cast<NumberedItem*>(
) | ranges::views::filter([](Item *i) { a.get())->number();
return i->type() != ItemCanvas::Type; const auto numB = qgraphicsitem_cast<NumberedItem*>(
}) | ranges::to_vector; b.get())->number();
ranges::sort(filteredItems, [&](not_null<Item*> a, not_null<Item*> b) {
const auto numA = qgraphicsitem_cast<NumberedItem*>(a)->number();
const auto numB = qgraphicsitem_cast<NumberedItem*>(b)->number();
return (order == Qt::AscendingOrder) ? (numA < numB) : (numA > numB); return (order == Qt::AscendingOrder) ? (numA < numB) : (numA > numB);
}); });
return filteredItems; return copyItems;
} }
std::vector<MTPInputDocument> Scene::attachedStickers() const { std::vector<MTPInputDocument> Scene::attachedStickers() const {
@ -105,14 +122,20 @@ std::vector<MTPInputDocument> Scene::attachedStickers() const {
return ranges::views::all( return ranges::views::all(
allItems allItems
) | ranges::views::filter([](QGraphicsItem *i) { ) | ranges::views::filter([](const ItemPtr &i) {
return i->isVisible() && (i->type() == ItemSticker::Type); return i->isVisible() && (i->type() == ItemSticker::Type);
}) | ranges::views::transform([](QGraphicsItem *i) { }) | ranges::views::transform([](const ItemPtr &i) {
return qgraphicsitem_cast<ItemSticker*>(i)->sticker(); return qgraphicsitem_cast<ItemSticker*>(i.get())->sticker();
}) | ranges::to_vector; }) | ranges::to_vector;
} }
Scene::~Scene() { Scene::~Scene() {
// Prevent destroying by scene of all items.
QGraphicsScene::removeItem(_canvas.get());
for (const auto &item : items()) {
// Scene loses ownership of an item.
QGraphicsScene::removeItem(item.get());
}
} }
} // namespace Editor } // namespace Editor

View file

@ -24,13 +24,17 @@ class NumberedItem;
class Scene final : public QGraphicsScene { class Scene final : public QGraphicsScene {
public: public:
using ItemPtr = std::shared_ptr<QGraphicsItem>;
Scene(const QRectF &rect); Scene(const QRectF &rect);
~Scene(); ~Scene();
void applyBrush(const QColor &color, float size); void applyBrush(const QColor &color, float size);
[[nodiscard]] std::vector<QGraphicsItem*> items( [[nodiscard]] std::vector<ItemPtr> items(
Qt::SortOrder order = Qt::DescendingOrder) const; Qt::SortOrder order = Qt::DescendingOrder) const;
void addItem(not_null<NumberedItem*> item); void addItem(std::shared_ptr<NumberedItem> item);
void removeItem(not_null<QGraphicsItem*> item);
void removeItem(const ItemPtr &item);
[[nodiscard]] rpl::producer<> addsItem() const; [[nodiscard]] rpl::producer<> addsItem() const;
[[nodiscard]] rpl::producer<> mousePresses() const; [[nodiscard]] rpl::producer<> mousePresses() const;
@ -40,7 +44,9 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
private: private:
const not_null<ItemCanvas*> _canvas; const std::shared_ptr<ItemCanvas> _canvas;
std::vector<ItemPtr> _items;
float64 _lastLineZ = 0.; float64 _lastLineZ = 0.;
int _itemNumber = 0; int _itemNumber = 0;

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "editor/scene_item_base.h" #include "editor/scene_item_base.h"
#include "editor/scene.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "styles/style_editor.h" #include "styles/style_editor.h"
@ -179,9 +180,8 @@ void ItemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr); _menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
_menu->addAction(tr::lng_selected_delete(tr::now), [=] { _menu->addAction(tr::lng_selected_delete(tr::now), [=] {
if (scene()) { if (const auto s = static_cast<Scene*>(scene())) {
scene()->removeItem(this); // Scene loses ownership of item. s->removeItem(this);
delete this;
} }
}); });