diff --git a/Telegram/SourceFiles/editor/color_picker.cpp b/Telegram/SourceFiles/editor/color_picker.cpp index 63f2696b81..bb015f993c 100644 --- a/Telegram/SourceFiles/editor/color_picker.cpp +++ b/Telegram/SourceFiles/editor/color_picker.cpp @@ -8,9 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "editor/color_picker.h" #include "base/basic_types.h" +#include "lang/lang_keys.h" +#include "ui/abstract_button.h" +#include "ui/layers/generic_box.h" #include "ui/painter.h" #include "ui/rect.h" #include "ui/rp_widget.h" +#include "ui/widgets/color_editor.h" #include "styles/style_editor.h" @@ -31,13 +35,71 @@ inline float64 InterpolationRatio(int from, int to, int result) { return (result - from) / float64(to - from); }; +class PlusCircle final : public Ui::AbstractButton { +public: + using Ui::AbstractButton::AbstractButton; + +private: + void paintEvent(QPaintEvent *event) override { + auto p = QPainter(this); + PainterHighQualityEnabler hq(p); + + const auto border = st::photoEditorColorButtonBorder; + const auto half = border / 2.; + const auto rect = QRectF(QWidget::rect()) + .adjusted(half, half, -half, -half); + + p.setPen(border > 0 + ? QPen(st::photoEditorColorButtonBorderFg, border) + : Qt::NoPen); + p.setBrush(Qt::NoBrush); + + const auto lineWidth = st::photoEditorColorPalettePlusLine; + auto pen = QPen(st::photoEditorColorPalettePlusFg, lineWidth); + pen.setCapStyle(Qt::RoundCap); + p.setPen(pen); + + const auto c = rect::center(rect); + const auto r = rect.width() / 2. - lineWidth * 1.75; + p.drawLine(QPointF(c.x() - r, c.y()), QPointF(c.x() + r, c.y())); + p.drawLine(QPointF(c.x(), c.y() - r), QPointF(c.x(), c.y() + r)); + } +}; + +std::vector PaletteColors() { + return { + QColor(0, 0, 0), + QColor(255, 255, 255), + QColor(234, 39, 57), + QColor(252, 150, 77), + QColor(252, 222, 101), + QColor(128, 200, 100), + QColor(73, 197, 237), + QColor(48, 81, 227), + QColor(219, 58, 210), + QColor(255, 114, 169), + }; +} + } // namespace ColorPicker::ColorPicker( not_null parent, + std::shared_ptr show, const Brush &savedBrush) : _parent(parent) -, _colorButton(std::in_place, parent) +, _show(std::move(show)) +, _colorButton( + std::in_place, + parent, + [=](uint8) { + auto set = Data::ColorProfileSet(); + set.palette = { _brush.color }; + return set; + }, + uint8(0), + true) +, _paletteWrap(std::in_place, parent) , _sizeControlHoverArea(std::in_place, parent) , _sizeControl(std::in_place, parent) , _brush(Brush{ @@ -51,6 +113,9 @@ ColorPicker::ColorPicker( _colorButton->resize( st::photoEditorColorButtonSize, st::photoEditorColorButtonSize); + _colorButton->setSelectionCutout(true); + _colorButton->setForceCircle(true); + _paletteWrap->setVisible(false); _sizeControl->resize( st::photoEditorBrushSizeControlHitPadding * 2 + st::photoEditorBrushSizeControlExpandShift @@ -62,11 +127,9 @@ ColorPicker::ColorPicker( updateSizeControlPositionFromRatio(); moveSizeControl(_parent->size()); - _colorButton->paintRequest( - ) | rpl::on_next([=] { - auto p = QPainter(_colorButton); - paintColorButton(p); - }, _colorButton->lifetime()); + _colorButton->setClickedCallback([=] { + setPaletteVisible(!_paletteVisible); + }); _sizeControl->paintOn([=](QPainter &p) { paintSizeControl(p); @@ -145,11 +208,15 @@ ColorPicker::ColorPicker( _sizeControl->update(); } }, _sizeControl->lifetime()); + + rebuildPalette(); } void ColorPicker::moveLine(const QPoint &position) { + _colorButtonCenter = position; _colorButton->move(position - QPoint(_colorButton->width() / 2, _colorButton->height() / 2)); + updatePaletteGeometry(); } void ColorPicker::setCanvasRect(const QRect &rect) { @@ -157,23 +224,6 @@ void ColorPicker::setCanvasRect(const QRect &rect) { moveSizeControl(_parent->size()); } -void ColorPicker::paintColorButton(QPainter &p) { - PainterHighQualityEnabler hq(p); - - const auto border = st::photoEditorColorButtonBorder; - const auto half = border / 2.; - const auto rect = QRectF(_colorButton->rect()) - .adjusted(half, half, -half, -half); - - if (border > 0) { - p.setPen(QPen(st::photoEditorColorButtonBorderFg, border)); - } else { - p.setPen(Qt::NoPen); - } - p.setBrush(_brush.color); - p.drawEllipse(rect); -} - void ColorPicker::paintSizeControl(QPainter &p) { auto hq = PainterHighQualityEnabler(p); @@ -193,6 +243,7 @@ void ColorPicker::paintSizeControl(QPainter &p) { void ColorPicker::setVisible(bool visible) { if (!visible) { + _paletteVisible = false; _sizeDown.pressed = false; _sizeHoverAreaHovered = false; _sizeControlHovered = false; @@ -200,6 +251,7 @@ void ColorPicker::setVisible(bool visible) { _sizeControlAnimation.stop(); } _colorButton->setVisible(visible); + _paletteWrap->setVisible(visible && _paletteVisible); _sizeControlHoverArea->setVisible(visible); _sizeControl->setVisible(visible); } @@ -213,6 +265,125 @@ bool ColorPicker::preventHandleKeyPress() const { && (_sizeControlAnimation.animating() || _sizeDown.pressed); } +void ColorPicker::rebuildPalette() { + _paletteButtons.clear(); + _palettePlus = nullptr; + + auto colors = PaletteColors(); + auto hasCurrent = false; + for (const auto &c : colors) { + if (c == _brush.color) { + hasCurrent = true; + break; + } + } + if (!hasCurrent) { + colors.push_back(_brush.color); + } + + auto index = uint8(0); + for (const auto &c : colors) { + auto button = base::make_unique_q( + _paletteWrap, + [c](uint8) { + auto set = Data::ColorProfileSet(); + set.palette = { c }; + return set; + }, + index++, + c == _brush.color); + button->setSelectionCutout(true); + button->setClickedCallback([=] { + _brush.color = c; + rebuildPalette(); + _colorButton->update(); + _saveBrushRequests.fire_copy(_brush); + setPaletteVisible(false); + }); + button->show(); + _paletteButtons.push_back(std::move(button)); + } + + _palettePlus = base::make_unique_q(_paletteWrap); + _palettePlus->setClickedCallback([=] { + if (!_show) { + return; + } + _show->showBox(Box([=](not_null box) { + struct State { + QColor color; + }; + const auto state = box->lifetime().make_state(); + state->color = _brush.color; + auto editor = box->addRow( + object_ptr( + box, + ColorEditor::Mode::HSL, + _brush.color), + style::margins()); + box->setWidth(editor->width()); + editor->colorValue( + ) | rpl::on_next([=](QColor c) { + state->color = c; + }, editor->lifetime()); + box->addButton(tr::lng_box_done(), [=] { + _brush.color = state->color; + rebuildPalette(); + _colorButton->update(); + _saveBrushRequests.fire_copy(_brush); + setPaletteVisible(false); + box->closeBox(); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + }); + _palettePlus->show(); + updatePaletteGeometry(); +} + +void ColorPicker::updatePaletteGeometry() { + if (!_paletteWrap) { + return; + } + const auto size = st::photoEditorColorPaletteItemSize; + const auto gap = st::photoEditorColorPaletteGap; + const auto count = int(_paletteButtons.size()); + const auto plusSize = size; + const auto width = (count * size) + + ((count > 0) ? (count - 1) * gap : 0) + + gap + plusSize; + const auto height = std::max(size, plusSize); + + _paletteWrap->resize(width, height); + auto x = 0; + for (const auto &button : _paletteButtons) { + button->resize(size, size); + button->move(x, (height - size) / 2); + x += size + gap; + } + if (_palettePlus) { + _palettePlus->resize(plusSize, plusSize); + _palettePlus->move(x, (height - plusSize) / 2); + } + + if (_colorButtonCenter.isNull()) { + return; + } + _paletteWrap->move(_colorButtonCenter - QPoint(width / 2, height / 2)); +} + +void ColorPicker::setPaletteVisible(bool visible) { + if (_paletteVisible == visible) { + return; + } + _paletteVisible = visible; + _paletteWrap->setVisible(visible); + _colorButton->setVisible(!visible); + if (visible) { + rebuildPalette(); + } +} + void ColorPicker::moveSizeControl(const QSize &size) { if (size.isEmpty()) { return; diff --git a/Telegram/SourceFiles/editor/color_picker.h b/Telegram/SourceFiles/editor/color_picker.h index 58e79ce32d..dd1d0f1e80 100644 --- a/Telegram/SourceFiles/editor/color_picker.h +++ b/Telegram/SourceFiles/editor/color_picker.h @@ -10,16 +10,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "editor/photo_editor_inner_common.h" #include "ui/effects/animations.h" +#include "ui/peer/color_sample.h" namespace Ui { class RpWidget; +class Show; } // namespace Ui namespace Editor { class ColorPicker final { public: - ColorPicker(not_null parent, const Brush &savedBrush); + ColorPicker( + not_null parent, + std::shared_ptr show, + const Brush &savedBrush); void moveLine(const QPoint &position); void setCanvasRect(const QRect &rect); @@ -29,8 +34,10 @@ public: rpl::producer saveBrushRequests() const; private: - void paintColorButton(QPainter &p); void paintSizeControl(QPainter &p); + void rebuildPalette(); + void updatePaletteGeometry(); + void setPaletteVisible(bool visible); void moveSizeControl(const QSize &size); void updateSizeControlExpanded(); void updateSizeControlMousePosition(int y); @@ -48,8 +55,10 @@ private: [[nodiscard]] float64 sizeControlHandleSize() const; const not_null _parent; + const std::shared_ptr _show; - const base::unique_qptr _colorButton; + const base::unique_qptr _colorButton; + const base::unique_qptr _paletteWrap; const base::unique_qptr _sizeControlHoverArea; const base::unique_qptr _sizeControl; @@ -61,12 +70,17 @@ private: bool _sizeControlHovered = false; bool _sizeControlExpanded = false; QRect _canvasRect; + QPoint _colorButtonCenter; + bool _paletteVisible = false; Brush _brush; Ui::Animations::Simple _sizeControlAnimation; rpl::event_stream _saveBrushRequests; + std::vector> _paletteButtons; + base::unique_qptr _palettePlus; + }; } // namespace Editor diff --git a/Telegram/SourceFiles/editor/editor.style b/Telegram/SourceFiles/editor/editor.style index ba4a5ff6e9..490e1f00e3 100644 --- a/Telegram/SourceFiles/editor/editor.style +++ b/Telegram/SourceFiles/editor/editor.style @@ -93,6 +93,12 @@ photoEditorRedoButtonInactive: icon {{ "photo_editor/undo-flip_horizontal", phot photoEditorColorButtonSize: 20px; photoEditorColorButtonBorder: 1px; photoEditorColorButtonBorderFg: mediaviewCaptionFg; +photoEditorColorPaletteItemSize: 20px; +photoEditorColorPaletteGap: 6px; +photoEditorColorPaletteSelectionWidth: 2px; +photoEditorColorPaletteSelectionFg: mediaviewCaptionFg; +photoEditorColorPalettePlusLine: 2px; +photoEditorColorPalettePlusFg: mediaviewCaptionFg; photoEditorBrushSizeControlLeftSkip: 0px; photoEditorBrushSizeControlHeight: 280px; diff --git a/Telegram/SourceFiles/editor/photo_editor.cpp b/Telegram/SourceFiles/editor/photo_editor.cpp index 754409b61a..fb8fbd30ff 100644 --- a/Telegram/SourceFiles/editor/photo_editor.cpp +++ b/Telegram/SourceFiles/editor/photo_editor.cpp @@ -79,7 +79,7 @@ PhotoEditor::PhotoEditor( std::move(sessionShow)) : nullptr, std::make_unique(), - std::move(show))) + show)) , _content(base::make_unique_q( this, photo, @@ -93,6 +93,7 @@ PhotoEditor::PhotoEditor( data)) , _colorPicker(std::make_unique( this, + std::move(show), Deserialize(Core::App().settings().photoEditorBrush()))) { sizeValue(