From 3ec1ff8c7e479c2169f9e38f2fd59e7cf73b8e5c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 12 Apr 2026 17:37:11 +0300 Subject: [PATCH] [img-editor] Synced palette color and tool selection with text focus. --- Telegram/SourceFiles/editor/color_picker.cpp | 22 ++++++++-- Telegram/SourceFiles/editor/color_picker.h | 4 ++ Telegram/SourceFiles/editor/editor_paint.cpp | 16 ++++++++ Telegram/SourceFiles/editor/editor_paint.h | 4 ++ Telegram/SourceFiles/editor/photo_editor.cpp | 41 +++++++++++++++---- Telegram/SourceFiles/editor/photo_editor.h | 1 + .../editor/photo_editor_content.cpp | 16 ++++++++ .../SourceFiles/editor/photo_editor_content.h | 4 ++ Telegram/SourceFiles/editor/scene/scene.cpp | 37 +++++++++++++++++ Telegram/SourceFiles/editor/scene/scene.h | 6 +++ 10 files changed, 139 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/editor/color_picker.cpp b/Telegram/SourceFiles/editor/color_picker.cpp index 411169079a..b703650fe2 100644 --- a/Telegram/SourceFiles/editor/color_picker.cpp +++ b/Telegram/SourceFiles/editor/color_picker.cpp @@ -351,6 +351,7 @@ ColorPicker::ColorPicker( button->show(); } const auto setToolRequest = [=](Brush::Tool tool) { + _toolClicks.fire({}); setTool(tool); }; if (_toolButtons.size() >= 5) { @@ -581,6 +582,11 @@ void ColorPicker::setColor(const QColor &color) { } } +void ColorPicker::setToolSelectionVisible(bool visible) { + _toolSelectionSuppressed = !visible; + _toolSelection->setVisible(visible); +} + void ColorPicker::updateColorButtonColor(const QColor &color, bool animated) { const auto hasValid = _colorButtonFrom.isValid() && _colorButtonTo.isValid(); const auto from = hasValid ? colorButtonColor() : color; @@ -643,11 +649,14 @@ void ColorPicker::setVisible(bool visible) { _paletteWrap->setVisible(visible && _paletteVisible); _sizeControlHoverArea->setVisible(visible); _sizeControl->setVisible(visible); - _toolSelection->setVisible(visible && !_paletteVisible); + const auto showTools = visible + && !_paletteVisible + && !_toolSelectionSuppressed; + _toolSelection->setVisible(showTools); for (const auto &button : _toolButtons) { button->setVisible(visible && !_paletteVisible); } - if (visible && !_paletteVisible) { + if (showTools) { updateToolSelection(false); } } @@ -656,6 +665,10 @@ rpl::producer ColorPicker::saveBrushRequests() const { return _saveBrushRequests.events_starting_with_copy(_brush); } +rpl::producer<> ColorPicker::toolClicks() const { + return _toolClicks.events(); +} + bool ColorPicker::preventHandleKeyPress() const { return _sizeControl->isVisible() && (_sizeControlAnimation.animating() || _sizeDown.pressed); @@ -796,13 +809,14 @@ void ColorPicker::setPaletteVisible(bool visible) { _paletteVisible = visible; _paletteWrap->setVisible(visible); _colorButton->setVisible(!visible); - _toolSelection->setVisible(!visible); + const auto showTools = !visible && !_toolSelectionSuppressed; + _toolSelection->setVisible(showTools); for (const auto &button : _toolButtons) { button->setVisible(!visible); } if (visible) { rebuildPalette(); - } else { + } else if (showTools) { updateToolSelection(false); } } diff --git a/Telegram/SourceFiles/editor/color_picker.h b/Telegram/SourceFiles/editor/color_picker.h index d7e80654fa..3d64431ce6 100644 --- a/Telegram/SourceFiles/editor/color_picker.h +++ b/Telegram/SourceFiles/editor/color_picker.h @@ -31,9 +31,11 @@ public: void setCanvasRect(const QRect &rect); void setVisible(bool visible); void setColor(const QColor &color); + void setToolSelectionVisible(bool visible); bool preventHandleKeyPress() const; rpl::producer saveBrushRequests() const; + rpl::producer<> toolClicks() const; private: void paintSizeControl(QPainter &p); @@ -76,6 +78,7 @@ private: int y = 0; bool pressed = false; } _sizeDown; + bool _toolSelectionSuppressed = false; bool _sizeHoverAreaHovered = false; bool _sizeControlHovered = false; bool _sizeControlExpanded = false; @@ -95,6 +98,7 @@ private: Ui::Animations::Simple _toolSelectionAnimation; rpl::event_stream _saveBrushRequests; + rpl::event_stream<> _toolClicks; std::vector> _paletteButtons; base::unique_qptr _palettePlus; diff --git a/Telegram/SourceFiles/editor/editor_paint.cpp b/Telegram/SourceFiles/editor/editor_paint.cpp index 836e511b36..3f633707b6 100644 --- a/Telegram/SourceFiles/editor/editor_paint.cpp +++ b/Telegram/SourceFiles/editor/editor_paint.cpp @@ -250,14 +250,30 @@ void Paint::createTextItem() { _scene->createTextAtCenter(); } +void Paint::clearSelection() { + _scene->clearSelection(); +} + void Paint::setTextColor(const QColor &color) { _scene->setTextColor(color); } +void Paint::setSelectedTextColor(const QColor &color) { + _scene->setSelectedTextColor(color); +} + rpl::producer Paint::textColorRequests() const { return _scene->textColorRequests(); } +rpl::producer Paint::textItemSelections() const { + return _scene->textItemSelections(); +} + +rpl::producer<> Paint::textItemDeselections() const { + return _scene->textItemDeselections(); +} + void Paint::handleMimeData(const QMimeData *data) { const auto add = [&](QImage image) { if (image.isNull()) { diff --git a/Telegram/SourceFiles/editor/editor_paint.h b/Telegram/SourceFiles/editor/editor_paint.h index 5540b60abc..b7856a5cfe 100644 --- a/Telegram/SourceFiles/editor/editor_paint.h +++ b/Telegram/SourceFiles/editor/editor_paint.h @@ -42,9 +42,13 @@ public: void updateUndoState(); void createTextItem(); + void clearSelection(); void setTextColor(const QColor &color); + void setSelectedTextColor(const QColor &color); [[nodiscard]] rpl::producer textColorRequests() const; + [[nodiscard]] rpl::producer textItemSelections() const; + [[nodiscard]] rpl::producer<> textItemDeselections() const; void handleMimeData(const QMimeData *data); void paintImage(QPainter &p, const QPixmap &image) const; diff --git a/Telegram/SourceFiles/editor/photo_editor.cpp b/Telegram/SourceFiles/editor/photo_editor.cpp index 786e6794e5..315317fa5a 100644 --- a/Telegram/SourceFiles/editor/photo_editor.cpp +++ b/Telegram/SourceFiles/editor/photo_editor.cpp @@ -341,17 +341,27 @@ PhotoEditor::PhotoEditor( } }, lifetime()); + _colorPicker->toolClicks( + ) | rpl::on_next([=] { + _content->clearSelection(); + }, lifetime()); + _colorPicker->saveBrushRequests( ) | rpl::on_next([=](const Brush &brush) { - _content->applyBrush(brush); - _content->setTextColor(brush.color); + if (_textItemSelected) { + _content->setSelectedTextColor(brush.color); + _content->setTextColor(brush.color); + } else { + _content->applyBrush(brush); + _content->setTextColor(brush.color); - _brushTool = brush.tool; - _brushes[ToolIndex(brush.tool)] = brush; - const auto serialized = Serialize(_brushes, _brushTool); - if (Core::App().settings().photoEditorBrush() != serialized) { - Core::App().settings().setPhotoEditorBrush(serialized); - Core::App().saveSettingsDelayed(); + _brushTool = brush.tool; + _brushes[ToolIndex(brush.tool)] = brush; + const auto serialized = Serialize(_brushes, _brushTool); + if (Core::App().settings().photoEditorBrush() != serialized) { + Core::App().settings().setPhotoEditorBrush(serialized); + Core::App().saveSettingsDelayed(); + } } }, lifetime()); @@ -359,6 +369,21 @@ PhotoEditor::PhotoEditor( ) | rpl::on_next([=](const QColor &color) { _colorPicker->setColor(color); }, lifetime()); + + _content->textItemSelections( + ) | rpl::on_next([=](const QColor &color) { + _textItemSelected = true; + _colorPicker->setColor(color); + _colorPicker->setToolSelectionVisible(false); + }, lifetime()); + + _content->textItemDeselections( + ) | rpl::on_next([=] { + _textItemSelected = false; + const auto &brush = _brushes[ToolIndex(_brushTool)]; + _colorPicker->setColor(brush.color); + _colorPicker->setToolSelectionVisible(true); + }, lifetime()); } void PhotoEditor::keyPressEvent(QKeyEvent *e) { diff --git a/Telegram/SourceFiles/editor/photo_editor.h b/Telegram/SourceFiles/editor/photo_editor.h index 00351e5b04..e29edae765 100644 --- a/Telegram/SourceFiles/editor/photo_editor.h +++ b/Telegram/SourceFiles/editor/photo_editor.h @@ -72,6 +72,7 @@ private: .mode = PhotoEditorMode::Mode::Transform, .action = PhotoEditorMode::Action::None, }; + bool _textItemSelected = false; rpl::event_stream _done; rpl::event_stream<> _cancel; diff --git a/Telegram/SourceFiles/editor/photo_editor_content.cpp b/Telegram/SourceFiles/editor/photo_editor_content.cpp index 38b0b16c86..735775e2da 100644 --- a/Telegram/SourceFiles/editor/photo_editor_content.cpp +++ b/Telegram/SourceFiles/editor/photo_editor_content.cpp @@ -166,14 +166,30 @@ void PhotoEditorContent::createTextItem() { _paint->createTextItem(); } +void PhotoEditorContent::clearSelection() { + _paint->clearSelection(); +} + void PhotoEditorContent::setTextColor(const QColor &color) { _paint->setTextColor(color); } +void PhotoEditorContent::setSelectedTextColor(const QColor &color) { + _paint->setSelectedTextColor(color); +} + rpl::producer PhotoEditorContent::textColorRequests() const { return _paint->textColorRequests(); } +rpl::producer PhotoEditorContent::textItemSelections() const { + return _paint->textItemSelections(); +} + +rpl::producer<> PhotoEditorContent::textItemDeselections() const { + return _paint->textItemDeselections(); +} + bool PhotoEditorContent::handleKeyPress(not_null e) const { return false; } diff --git a/Telegram/SourceFiles/editor/photo_editor_content.h b/Telegram/SourceFiles/editor/photo_editor_content.h index 95bb3ee439..1b83437c5b 100644 --- a/Telegram/SourceFiles/editor/photo_editor_content.h +++ b/Telegram/SourceFiles/editor/photo_editor_content.h @@ -32,9 +32,13 @@ public: void applyMode(const PhotoEditorMode &mode); void applyBrush(const Brush &brush); void createTextItem(); + void clearSelection(); void setTextColor(const QColor &color); + void setSelectedTextColor(const QColor &color); [[nodiscard]] rpl::producer textColorRequests() const; + [[nodiscard]] rpl::producer textItemSelections() const; + [[nodiscard]] rpl::producer<> textItemDeselections() const; void applyAspectRatio(float64 ratio); void save(PhotoModifications &modifications); diff --git a/Telegram/SourceFiles/editor/scene/scene.cpp b/Telegram/SourceFiles/editor/scene/scene.cpp index f0ac32d829..8d9dc65b1c 100644 --- a/Telegram/SourceFiles/editor/scene/scene.cpp +++ b/Telegram/SourceFiles/editor/scene/scene.cpp @@ -288,6 +288,27 @@ Scene::Scene(const QRectF &rect) addItem(item); _canvas->setZValue(++_lastLineZ); }, _lifetime); + + QObject::connect( + this, + &QGraphicsScene::selectionChanged, + [=] { + const auto selected = selectedItems(); + auto *textItem = (ItemText*)(nullptr); + if (selected.size() == 1 + && selected.front()->type() == ItemText::Type) { + textItem = static_cast(selected.front()); + } + if (textItem) { + if (!_textItemWasSelected) { + _textItemWasSelected = true; + _textItemSelections.fire_copy(textItem->color()); + } + } else if (_textItemWasSelected) { + _textItemWasSelected = false; + _textItemDeselections.fire({}); + } + }); } void Scene::cancelDrawing() { @@ -378,10 +399,26 @@ void Scene::setTextColor(const QColor &color) { } } +void Scene::setSelectedTextColor(const QColor &color) { + for (auto *item : selectedItems()) { + if (item->type() == ItemText::Type) { + static_cast(item)->setColor(color); + } + } +} + rpl::producer Scene::textColorRequests() const { return _textColorRequests.events(); } +rpl::producer Scene::textItemSelections() const { + return _textItemSelections.events(); +} + +rpl::producer<> Scene::textItemDeselections() const { + return _textItemDeselections.events(); +} + void Scene::setBlurSource(Fn source) { _blurSource = std::move(source); } diff --git a/Telegram/SourceFiles/editor/scene/scene.h b/Telegram/SourceFiles/editor/scene/scene.h index c069e8de7e..fccbd67c76 100644 --- a/Telegram/SourceFiles/editor/scene/scene.h +++ b/Telegram/SourceFiles/editor/scene/scene.h @@ -52,8 +52,11 @@ public: void startTextEditing(ItemText *item); void createTextAtCenter(); void setTextColor(const QColor &color); + void setSelectedTextColor(const QColor &color); [[nodiscard]] rpl::producer textColorRequests() const; + [[nodiscard]] rpl::producer textItemSelections() const; + [[nodiscard]] rpl::producer<> textItemDeselections() const; [[nodiscard]] bool hasUndo() const; [[nodiscard]] bool hasRedo() const; @@ -99,6 +102,9 @@ private: rpl::event_stream<> _addsItem, _removesItem; rpl::event_stream _textColorRequests; + rpl::event_stream _textItemSelections; + rpl::event_stream<> _textItemDeselections; + bool _textItemWasSelected = false; rpl::lifetime _lifetime; };