From 8eca57f419ee9eae1702667957ba0b9482bfab9b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 14 Mar 2021 12:39:55 +0300 Subject: [PATCH] Added saving and discarding between modes in photo editor. --- Telegram/SourceFiles/editor/editor_paint.cpp | 44 +++++++++--- Telegram/SourceFiles/editor/editor_paint.h | 6 +- Telegram/SourceFiles/editor/photo_editor.cpp | 32 ++++++++- Telegram/SourceFiles/editor/photo_editor.h | 4 ++ .../SourceFiles/editor/photo_editor_common.h | 14 +++- .../editor/photo_editor_content.cpp | 14 +++- .../SourceFiles/editor/photo_editor_content.h | 2 +- .../editor/photo_editor_controls.cpp | 67 +++++++++++++++---- .../editor/photo_editor_controls.h | 17 ++++- 9 files changed, 167 insertions(+), 33 deletions(-) diff --git a/Telegram/SourceFiles/editor/editor_paint.cpp b/Telegram/SourceFiles/editor/editor_paint.cpp index c86c5705e..a1a41fc58 100644 --- a/Telegram/SourceFiles/editor/editor_paint.cpp +++ b/Telegram/SourceFiles/editor/editor_paint.cpp @@ -29,6 +29,10 @@ std::shared_ptr EnsureScene(PhotoModifications &mods) { return mods.paint; } +auto FilterItems(QGraphicsItem *i) { + return i->type() == QGraphicsItemGroup::Type; +} + } // namespace Paint::Paint( @@ -38,7 +42,8 @@ Paint::Paint( : RpWidget(parent) , _scene(EnsureScene(modifications)) , _view(base::make_unique_q(_scene.get(), this)) -, _imageSize(imageSize) { +, _imageSize(imageSize) +, _startItemsCount(itemsCount()) { Expects(modifications.paint != nullptr); _view->show(); @@ -49,7 +54,6 @@ Paint::Paint( _scene->setSceneRect(0, 0, imageSize.width(), imageSize.height()); initDrawing(); - } void Paint::applyTransform(QRect geometry, int angle, bool flipped) { @@ -120,14 +124,38 @@ void Paint::initDrawing() { base::install_event_filter(this, _scene.get(), std::move(callback)); } -void Paint::applyMode(PhotoEditorMode mode) { - setAttribute( - Qt::WA_TransparentForMouseEvents, - mode == PhotoEditorMode::Transform); -} - std::shared_ptr Paint::saveScene() const { return _scene; } +void Paint::cancel() { + const auto items = _scene->items(Qt::AscendingOrder); + const auto filtered = ranges::views::all( + items + ) | ranges::views::filter(FilterItems) | ranges::to_vector; + + if (filtered.empty()) { + return; + } + + for (auto i = 0; i < filtered.size(); i++) { + const auto &item = filtered[i]; + if (i < _startItemsCount) { + if (!item->isVisible()) { + item->show(); + } + } else { + _scene->removeItem(item); + } + } +} + +void Paint::keepResult() { + _startItemsCount = itemsCount(); +} + +int Paint::itemsCount() const { + return ranges::count_if(_scene->items(), FilterItems); +} + } // namespace Editor diff --git a/Telegram/SourceFiles/editor/editor_paint.h b/Telegram/SourceFiles/editor/editor_paint.h index 20ea30783..8134f9290 100644 --- a/Telegram/SourceFiles/editor/editor_paint.h +++ b/Telegram/SourceFiles/editor/editor_paint.h @@ -27,15 +27,19 @@ public: [[nodiscard]] std::shared_ptr saveScene() const; void applyTransform(QRect geometry, int angle, bool flipped); - void applyMode(PhotoEditorMode mode); + void cancel(); + void keepResult(); private: void initDrawing(); + int itemsCount() const; const std::shared_ptr _scene; const base::unique_qptr _view; const QSize _imageSize; + int _startItemsCount = 0; + struct { QPointF lastPoint; diff --git a/Telegram/SourceFiles/editor/photo_editor.cpp b/Telegram/SourceFiles/editor/photo_editor.cpp index 75fc2732a..0e8a4dde0 100644 --- a/Telegram/SourceFiles/editor/photo_editor.cpp +++ b/Telegram/SourceFiles/editor/photo_editor.cpp @@ -35,6 +35,12 @@ PhotoEditor::PhotoEditor( _controls->setGeometry(controlsRect); }, lifetime()); + _mode.value( + ) | rpl::start_with_next([=](const PhotoEditorMode &mode) { + _content->applyMode(mode); + _controls->applyMode(mode); + }, lifetime()); + _controls->rotateRequests( ) | rpl::start_with_next([=](int angle) { _modifications.angle += 90; @@ -52,9 +58,31 @@ PhotoEditor::PhotoEditor( _controls->paintModeRequests( ) | rpl::start_with_next([=] { - _content->applyMode(PhotoEditorMode::Paint); + _mode = PhotoEditorMode{ + .mode = PhotoEditorMode::Mode::Paint, + .action = PhotoEditorMode::Action::None, + }; + }, lifetime()); + + _controls->doneRequests( + ) | rpl::start_with_next([=] { + if (_mode.current().mode == PhotoEditorMode::Mode::Paint) { + _mode = PhotoEditorMode{ + .mode = PhotoEditorMode::Mode::Transform, + .action = PhotoEditorMode::Action::Save, + }; + } + }, lifetime()); + + _controls->cancelRequests( + ) | rpl::start_with_next([=] { + if (_mode.current().mode == PhotoEditorMode::Mode::Paint) { + _mode = PhotoEditorMode{ + .mode = PhotoEditorMode::Mode::Transform, + .action = PhotoEditorMode::Action::Discard, + }; + } }, lifetime()); - _content->applyMode(PhotoEditorMode::Transform); } void PhotoEditor::save() { diff --git a/Telegram/SourceFiles/editor/photo_editor.h b/Telegram/SourceFiles/editor/photo_editor.h index c9802f966..5b8a53475 100644 --- a/Telegram/SourceFiles/editor/photo_editor.h +++ b/Telegram/SourceFiles/editor/photo_editor.h @@ -35,6 +35,10 @@ private: base::unique_qptr _content; base::unique_qptr _controls; + rpl::variable _mode = PhotoEditorMode{ + .mode = PhotoEditorMode::Mode::Transform, + .action = PhotoEditorMode::Action::None, + }; rpl::event_stream _done; }; diff --git a/Telegram/SourceFiles/editor/photo_editor_common.h b/Telegram/SourceFiles/editor/photo_editor_common.h index 6dc1b7b31..c0d5c4281 100644 --- a/Telegram/SourceFiles/editor/photo_editor_common.h +++ b/Telegram/SourceFiles/editor/photo_editor_common.h @@ -11,9 +11,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Editor { -enum class PhotoEditorMode { - Transform, - Paint, +struct PhotoEditorMode { + enum class Mode { + Transform, + Paint, + } mode = Mode::Transform; + + enum class Action { + None, + Save, + Discard, + } action = Action::None; }; struct PhotoModifications { diff --git a/Telegram/SourceFiles/editor/photo_editor_content.cpp b/Telegram/SourceFiles/editor/photo_editor_content.cpp index 45b81137b..8ccd30f69 100644 --- a/Telegram/SourceFiles/editor/photo_editor_content.cpp +++ b/Telegram/SourceFiles/editor/photo_editor_content.cpp @@ -93,9 +93,17 @@ void PhotoEditorContent::save(PhotoModifications &modifications) { } } -void PhotoEditorContent::applyMode(PhotoEditorMode mode) { - _crop->setVisible(mode == PhotoEditorMode::Transform); - _paint->applyMode(mode); +void PhotoEditorContent::applyMode(const PhotoEditorMode &mode) { + const auto isTransform = (mode.mode == PhotoEditorMode::Mode::Transform); + _crop->setVisible(isTransform); + + _paint->setAttribute(Qt::WA_TransparentForMouseEvents, isTransform); + + if (mode.action == PhotoEditorMode::Action::Discard) { + _paint->cancel(); + } else if (mode.action == PhotoEditorMode::Action::Save) { + _paint->keepResult(); + } _mode = mode; } diff --git a/Telegram/SourceFiles/editor/photo_editor_content.h b/Telegram/SourceFiles/editor/photo_editor_content.h index 8fa5cc6ec..cf17c01df 100644 --- a/Telegram/SourceFiles/editor/photo_editor_content.h +++ b/Telegram/SourceFiles/editor/photo_editor_content.h @@ -24,7 +24,7 @@ public: PhotoModifications modifications); void applyModifications(PhotoModifications modifications); - void applyMode(PhotoEditorMode mode); + void applyMode(const PhotoEditorMode &mode); void save(PhotoModifications &modifications); private: diff --git a/Telegram/SourceFiles/editor/photo_editor_controls.cpp b/Telegram/SourceFiles/editor/photo_editor_controls.cpp index 832343c24..79584e0d5 100644 --- a/Telegram/SourceFiles/editor/photo_editor_controls.cpp +++ b/Telegram/SourceFiles/editor/photo_editor_controls.cpp @@ -136,15 +136,25 @@ PhotoEditorControls::PhotoEditorControls( bool doneControls) : RpWidget(parent) , _bg(st::mediaviewSaveMsgBg) -, _buttonsContainer(base::make_unique_q(this)) +, _transformButtons(base::make_unique_q(this)) +, _paintButtons(base::make_unique_q(this)) , _rotateButton(base::make_unique_q( - _buttonsContainer, + _transformButtons, st::photoEditorRotateButton)) , _flipButton(base::make_unique_q( - _buttonsContainer, + _transformButtons, st::photoEditorFlipButton)) , _paintModeButton(base::make_unique_q( - _buttonsContainer, + _transformButtons, + st::photoEditorPaintModeButton)) +, _undoButton(base::make_unique_q( + _paintButtons, + st::photoEditorUndoButton)) +, _redoButton(base::make_unique_q( + _paintButtons, + st::photoEditorRedoButton)) +, _paintModeButtonActive(base::make_unique_q( + _paintButtons, st::photoEditorPaintModeButton)) , _cancel(base::make_unique_q( this, @@ -163,7 +173,12 @@ PhotoEditorControls::PhotoEditorControls( st::lightButtonFg, st::photoEditorRotateButton.ripple)) { - _buttonsContainer->updateChildrenPosition(); + _transformButtons->updateChildrenPosition(); + _paintButtons->updateChildrenPosition(); + + _paintModeButtonActive->setIconOverride( + &st::photoEditorPaintModeButton.iconOver); + _paintModeButtonActive->setAttribute(Qt::WA_TransparentForMouseEvents); paintRequest( ) | rpl::start_with_next([=](const QRect &clip) { @@ -171,21 +186,30 @@ PhotoEditorControls::PhotoEditorControls( p.setPen(Qt::NoPen); p.setBrush(_bg); - p.drawRect(_buttonsContainer->geometry()); + p.drawRect(_transformButtons->geometry()); }, lifetime()); - sizeValue( - ) | rpl::start_with_next([=](const QSize &size) { + rpl::combine( + sizeValue(), + _mode.value() + ) | rpl::start_with_next([=]( + const QSize &size, + const PhotoEditorMode &mode) { + if (size.isEmpty()) { + return; + } - _buttonsContainer->moveToLeft( - (size.width() - _buttonsContainer->width()) / 2, + const auto ¤t = _transformButtons->isHidden() + ? _paintButtons + : _transformButtons; + + current->moveToLeft( + (size.width() - current->width()) / 2, 0); - _cancel->moveToLeft(_buttonsContainer->x() - _cancel->width(), 0); - _done->moveToLeft( - _buttonsContainer->x() + _buttonsContainer->width(), - 0); + _cancel->moveToLeft(current->x() - _cancel->width(), 0); + _done->moveToLeft(current->x() + current->width(), 0); }, lifetime()); @@ -203,4 +227,19 @@ rpl::producer<> PhotoEditorControls::paintModeRequests() const { return _paintModeButton->clicks() | rpl::to_empty; } +rpl::producer<> PhotoEditorControls::doneRequests() const { + return _done->clicks() | rpl::to_empty; +} + +rpl::producer<> PhotoEditorControls::cancelRequests() const { + return _cancel->clicks() | rpl::to_empty; +} + +void PhotoEditorControls::applyMode(const PhotoEditorMode &mode) { + using Mode = PhotoEditorMode::Mode; + _transformButtons->setVisible(mode.mode == Mode::Transform); + _paintButtons->setVisible(mode.mode == Mode::Paint); + _mode = mode; +} + } // namespace Editor diff --git a/Telegram/SourceFiles/editor/photo_editor_controls.h b/Telegram/SourceFiles/editor/photo_editor_controls.h index 0fd04f5f9..3d2620383 100644 --- a/Telegram/SourceFiles/editor/photo_editor_controls.h +++ b/Telegram/SourceFiles/editor/photo_editor_controls.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" +#include "editor/photo_editor_common.h" + namespace Ui { class IconButton; } // namespace Ui @@ -27,17 +29,30 @@ public: [[nodiscard]] rpl::producer rotateRequests() const; [[nodiscard]] rpl::producer<> flipRequests() const; [[nodiscard]] rpl::producer<> paintModeRequests() const; + [[nodiscard]] rpl::producer<> doneRequests() const; + [[nodiscard]] rpl::producer<> cancelRequests() const; + + void applyMode(const PhotoEditorMode &mode); private: const style::color &_bg; - const base::unique_qptr _buttonsContainer; + const base::unique_qptr _transformButtons; + const base::unique_qptr _paintButtons; + const base::unique_qptr _rotateButton; const base::unique_qptr _flipButton; const base::unique_qptr _paintModeButton; + + const base::unique_qptr _undoButton; + const base::unique_qptr _redoButton; + const base::unique_qptr _paintModeButtonActive; + const base::unique_qptr _cancel; const base::unique_qptr _done; + rpl::variable _mode; + }; } // namespace Editor