diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 2899bdaaa1..b80f2a90f8 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -113,7 +113,8 @@ QByteArray Settings::serialize() const { + sizeof(qint64) + sizeof(qint32) * 2 + Serialize::bytearraySize(windowPosition) - + sizeof(qint32); + + sizeof(qint32) + + Serialize::bytearraySize(_photoEditorBrush); for (const auto &[id, rating] : recentEmojiPreloadData) { size += Serialize::stringSize(id) + sizeof(quint16); } @@ -216,7 +217,8 @@ QByteArray Settings::serialize() const { << qint32(_workMode.current()) << proxy << qint32(_hiddenGroupCallTooltips.value()) - << qint32(_disableOpenGL ? 1 : 0); + << qint32(_disableOpenGL ? 1 : 0) + << _photoEditorBrush; } return result; } @@ -297,6 +299,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 workMode = static_cast(_workMode.current()); QByteArray proxy; qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value()); + QByteArray photoEditorBrush = _photoEditorBrush; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -444,6 +447,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> disableOpenGL; } + if (!stream.atEnd()) { + stream >> photoEditorBrush; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -573,6 +579,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { ? Tooltip::Microphone : Tooltip(0)); }(); + _photoEditorBrush = photoEditorBrush; } QString Settings::getSoundPath(const QString &key) const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 881060db3e..63c803c6a4 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -434,6 +434,13 @@ public: _videoPipGeometry = geometry; } + [[nodiscard]] QByteArray photoEditorBrush() const { + return _photoEditorBrush; + } + void setPhotoEditorBrush(QByteArray brush) { + _photoEditorBrush = brush; + } + [[nodiscard]] float64 rememberedSongVolume() const { return _rememberedSongVolume; } @@ -696,6 +703,8 @@ private: bool _rememberedSoundNotifyFromTray = false; bool _rememberedFlashBounceNotifyFromTray = false; + QByteArray _photoEditorBrush; + }; } // namespace Core diff --git a/Telegram/SourceFiles/editor/color_picker.cpp b/Telegram/SourceFiles/editor/color_picker.cpp index cd61efff7d..1a53917285 100644 --- a/Telegram/SourceFiles/editor/color_picker.cpp +++ b/Telegram/SourceFiles/editor/color_picker.cpp @@ -17,7 +17,7 @@ namespace Editor { namespace { constexpr auto kPrecision = 1000; -constexpr auto kMinBrushSize = 0.1; +constexpr auto kMinBrushSize = 0.1f; constexpr auto kMouseSkip = 1.4; constexpr auto kMinInnerHeight = 0.2; @@ -83,7 +83,9 @@ inline float64 InterpolationRatio(int from, int to, int result) { } // namespace -ColorPicker::ColorPicker(not_null parent) +ColorPicker::ColorPicker( + not_null parent, + const Brush &savedBrush) : _circleColor(Qt::white) , _width(st::photoEditorColorPickerWidth) , _lineHeight(st::photoEditorColorPickerLineHeight) @@ -93,7 +95,14 @@ ColorPicker::ColorPicker(not_null parent) , _outlinedStop(FindOutlinedStop(_circleColor, _gradientStops, _width)) , _gradientBrush( GradientBrush(QPoint(_width, _lineHeight / 2), _gradientStops)) -, _brush(Brush{ .sizeRatio = kMinBrushSize, .color = QColor() }) { +, _brush(Brush{ + .sizeRatio = (savedBrush.sizeRatio + ? savedBrush.sizeRatio + : kMinBrushSize), + .color = (savedBrush.color.isValid() + ? savedBrush.color + : _gradientStops.front().second), +}) { _colorLine->resize(_width, _lineHeight); _canvasForCircle->resize( _width + circleHeight(kMax), @@ -101,6 +110,8 @@ ColorPicker::ColorPicker(not_null parent) _canvasForCircle->setAttribute(Qt::WA_TransparentForMouseEvents); + _down.pos = QPoint(colorToPosition(savedBrush.color), 0); + _colorLine->paintRequest( ) | rpl::start_with_next([=] { Painter p(_colorLine); @@ -146,6 +157,9 @@ ColorPicker::ColorPicker(not_null parent) } const auto e = static_cast(event.get()); updateMousePosition(e->pos(), progress); + if (isRelease) { + _saveBrushRequests.fire_copy(_brush); + } _canvasForCircle->update(); }, _colorLine->lifetime()); @@ -168,18 +182,14 @@ void ColorPicker::updateMousePosition(const QPoint &pos, float64 progress) { const auto from = 0; const auto to = bottom - skip; - const auto size = (mappedY > to) - ? _brush.current().sizeRatio // Don't change value. - : std::clamp( - 1. - InterpolationRatio(from, to, _down.pos.y()), + // Don't change the brush size when we are on the color line. + if (mappedY <= to) { + _brush.sizeRatio = std::clamp( + float(1. - InterpolationRatio(from, to, _down.pos.y())), kMinBrushSize, - 1.); - const auto color = positionToColor(_down.pos.x()); - - _brush = Brush{ - .sizeRatio = float(size), - .color = color, - }; + 1.f); + } + _brush.color = positionToColor(_down.pos.x()); } void ColorPicker::moveLine(const QPoint &position) { @@ -239,9 +249,9 @@ void ColorPicker::paintCircle(Painter &p) { const auto innerH = InterpolateF( h * kMinInnerHeight, h * kMaxInnerHeight, - _brush.current().sizeRatio); + _brush.sizeRatio); - p.setBrush(_brush.current().color); + p.setBrush(_brush.color); const auto innerRect = QRectF( r.x() + (r.width() - innerH) / 2., @@ -297,8 +307,18 @@ void ColorPicker::setVisible(bool visible) { _canvasForCircle->setVisible(visible); } -rpl::producer ColorPicker::brushValue() const { - return _brush.value(); +rpl::producer ColorPicker::saveBrushRequests() const { + return _saveBrushRequests.events(); +} + +int ColorPicker::colorToPosition(const QColor &color) const { + const auto step = 1. / kPrecision; + for (auto i = 0.; i <= 1.; i += step) { + if (positionToColor(i * _width) == color) { + return i * _width; + } + } + return 0; } } // namespace Editor diff --git a/Telegram/SourceFiles/editor/color_picker.h b/Telegram/SourceFiles/editor/color_picker.h index 46f11b500a..f125c58ed2 100644 --- a/Telegram/SourceFiles/editor/color_picker.h +++ b/Telegram/SourceFiles/editor/color_picker.h @@ -25,17 +25,18 @@ public: std::optional nextStopPos = std::nullopt; }; - ColorPicker(not_null parent); + ColorPicker(not_null parent, const Brush &savedBrush); void moveLine(const QPoint &position); void setVisible(bool visible); - rpl::producer brushValue() const; + rpl::producer saveBrushRequests() const; private: void paintCircle(Painter &p); void paintOutline(Painter &p, const QRectF &rect); QColor positionToColor(int x) const; + int colorToPosition(const QColor &color) const; int circleHeight(float64 progress = 0.) const; void updateMousePosition(const QPoint &pos, float64 progress); @@ -54,10 +55,12 @@ private: QPoint pos; bool pressed = false; } _down; - rpl::variable _brush; + Brush _brush; Ui::Animations::Simple _circleAnimation; + rpl::event_stream _saveBrushRequests; + }; } // namespace Editor diff --git a/Telegram/SourceFiles/editor/photo_editor.cpp b/Telegram/SourceFiles/editor/photo_editor.cpp index 4a0aa55995..4009dec0a1 100644 --- a/Telegram/SourceFiles/editor/photo_editor.cpp +++ b/Telegram/SourceFiles/editor/photo_editor.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "editor/photo_editor.h" +#include "core/application.h" +#include "core/core_settings.h" #include "editor/color_picker.h" #include "editor/photo_editor_content.h" #include "editor/photo_editor_controls.h" @@ -14,6 +16,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_editor.h" namespace Editor { +namespace { + +constexpr auto kPrecision = 100000; + +[[nodiscard]] QByteArray Serialize(const Brush &brush) { + auto result = QByteArray(); + auto stream = QDataStream(&result, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_5_3); + stream << qint32(brush.sizeRatio * kPrecision) << brush.color; + stream.device()->close(); + + return result; +} + +[[nodiscard]] Brush Deserialize(const QByteArray &data) { + auto stream = QDataStream(data); + auto result = Brush(); + auto size = qint32(0); + stream >> size >> result.color; + result.sizeRatio = size / float(kPrecision); + return (stream.status() != QDataStream::Ok) + ? Brush() + : result; +} + +} // namespace PhotoEditor::PhotoEditor( not_null parent, @@ -28,7 +56,10 @@ PhotoEditor::PhotoEditor( _modifications, _undoController)) , _controls(base::make_unique_q(this, _undoController)) -, _colorPicker(std::make_unique(this)) { +, _colorPicker(std::make_unique( + this, + Deserialize(Core::App().settings().photoEditorBrush()))) { + sizeValue( ) | rpl::start_with_next([=](const QSize &size) { if (size.isEmpty()) { @@ -97,9 +128,18 @@ PhotoEditor::PhotoEditor( } }, lifetime()); - _colorPicker->brushValue( + rpl::single( + Deserialize(Core::App().settings().photoEditorBrush()) + ) | rpl::then( + _colorPicker->saveBrushRequests() ) | rpl::start_with_next([=](const Brush &brush) { _content->applyBrush(brush); + + const auto serialized = Serialize(brush); + if (Core::App().settings().photoEditorBrush() != serialized) { + Core::App().settings().setPhotoEditorBrush(serialized); + Core::App().saveSettingsDelayed(); + } }, lifetime()); }