/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "editor/photo_editor_controls.h" #include "editor/controllers/controllers.h" #include "lang/lang_keys.h" #include "ui/image/image_prepare.h" #include "ui/widgets/buttons.h" #include "styles/style_editor.h" namespace Editor { class EdgeButton final : public Ui::RippleButton { public: EdgeButton( not_null parent, const QString &text, int height, bool left, const style::color &bg, const style::color &fg, const style::RippleAnimation &st); protected: QImage prepareRippleMask() const override; QPoint prepareRippleStartPosition() const override; private: void init(); const style::color &_fg; Ui::Text::String _text; const int _width; const QRect _rippleRect; const QColor _bg; const bool _left; QImage rounded(std::optional color) const; }; EdgeButton::EdgeButton( not_null parent, const QString &text, int height, bool left, const style::color &bg, const style::color &fg, const style::RippleAnimation &st) : Ui::RippleButton(parent, st) , _fg(fg) , _text(st::semiboldTextStyle, text.toUpper()) , _width(_text.maxWidth() + st::photoEditorTextButtonPadding.left() + st::photoEditorTextButtonPadding.right()) , _rippleRect(QRect(0, 0, _width, height)) , _bg(bg->c) , _left(left) { resize(_width, height); init(); } void EdgeButton::init() { // const auto bg = rounded(_bg); paintRequest( ) | rpl::start_with_next([=] { Painter p(this); // p.drawImage(QPoint(), bg); paintRipple(p, _rippleRect.x(), _rippleRect.y()); p.setPen(_fg); const auto textTop = (height() - _text.minHeight()) / 2; _text.draw(p, 0, textTop, width(), style::al_center); }, lifetime()); } QImage EdgeButton::rounded(std::optional color) const { auto result = QImage( _rippleRect.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(cIntRetinaFactor()); result.fill(color.value_or(Qt::white)); using Option = Images::Option; const auto options = Option::Smooth | Option::RoundedLarge | (_left ? Option::RoundedTopLeft : Option::RoundedTopRight) | (_left ? Option::RoundedBottomLeft : Option::RoundedBottomRight); return Images::prepare(std::move(result), 0, 0, options, 0, 0); } QImage EdgeButton::prepareRippleMask() const { return rounded(std::nullopt); } QPoint EdgeButton::prepareRippleStartPosition() const { return mapFromGlobal(QCursor::pos()) - _rippleRect.topLeft(); } class ButtonBar final : public Ui::RpWidget { public: ButtonBar( not_null parent, const style::color &bg); private: QImage _roundedBg; }; ButtonBar::ButtonBar( not_null parent, const style::color &bg) : RpWidget(parent) { sizeValue( ) | rpl::start_with_next([=](const QSize &size) { const auto children = RpWidget::children(); if (children.empty()) { return; } const auto widgets = ranges::view::all( children ) | ranges::view::filter([](not_null object) { return object->isWidgetType(); }) | ranges::view::transform([](not_null object) { return static_cast(object.get()); }) | ranges::to_vector; const auto residualWidth = size.width() - ranges::accumulate(widgets, 0, ranges::plus(), &QWidget::width); const auto step = residualWidth / float(widgets.size() - 1); auto left = 0.; for (const auto &widget : widgets) { widget->moveToLeft(int(left), 0); left += widget->width() + step; } auto result = QImage( size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(cIntRetinaFactor()); result.fill(bg->c); const auto options = Images::Option::Smooth | Images::Option::RoundedLarge | Images::Option::RoundedAll; _roundedBg = Images::prepare(std::move(result), 0, 0, options, 0, 0); }, lifetime()); paintRequest( ) | rpl::start_with_next([=] { Painter p(this); p.drawImage(QPoint(), _roundedBg); }, lifetime()); } PhotoEditorControls::PhotoEditorControls( not_null parent, std::shared_ptr controllers, const PhotoModifications modifications, bool doneControls) : RpWidget(parent) , _bg(st::roundedBg) , _buttonHeight(st::photoEditorButtonBarHeight) , _transformButtons(base::make_unique_q(this, _bg)) , _paintBottomButtons(base::make_unique_q(this, _bg)) , _transformCancel(base::make_unique_q( _transformButtons, tr::lng_cancel(tr::now), _buttonHeight, true, _bg, st::activeButtonFg, st::photoEditorRotateButton.ripple)) , _rotateButton(base::make_unique_q( _transformButtons, st::photoEditorRotateButton)) , _flipButton(base::make_unique_q( _transformButtons, st::photoEditorFlipButton)) , _paintModeButton(base::make_unique_q( _transformButtons, st::photoEditorPaintModeButton)) , _transformDone(base::make_unique_q( _transformButtons, tr::lng_box_done(tr::now), _buttonHeight, false, _bg, st::lightButtonFg, st::photoEditorRotateButton.ripple)) , _paintCancel(base::make_unique_q( _paintBottomButtons, tr::lng_cancel(tr::now), _buttonHeight, true, _bg, st::activeButtonFg, st::photoEditorRotateButton.ripple)) , _undoButton(base::make_unique_q( _paintBottomButtons, st::photoEditorUndoButton)) , _redoButton(base::make_unique_q( _paintBottomButtons, st::photoEditorRedoButton)) , _paintModeButtonActive(base::make_unique_q( _paintBottomButtons, st::photoEditorPaintModeButton)) , _stickersButton(controllers->stickersPanelController ? base::make_unique_q( _paintBottomButtons, st::photoEditorStickersButton) : nullptr) , _paintDone(base::make_unique_q( _paintBottomButtons, tr::lng_box_done(tr::now), _buttonHeight, false, _bg, st::lightButtonFg, st::photoEditorRotateButton.ripple)) { { const auto &padding = st::photoEditorButtonBarPadding; const auto w = st::photoEditorButtonBarWidth - padding.left() - padding.right(); _transformButtons->resize(w, _buttonHeight); _paintBottomButtons->resize(w, _buttonHeight); } { const auto icon = &st::photoEditorPaintIconActive; _paintModeButtonActive->setIconOverride(icon, icon); } _paintModeButtonActive->setAttribute(Qt::WA_TransparentForMouseEvents); rpl::combine( sizeValue(), _mode.value() ) | rpl::start_with_next([=]( const QSize &size, const PhotoEditorMode &mode) { if (size.isEmpty()) { return; } const auto buttonsTop = size.height() - st::photoEditorControlsBottomSkip - _transformButtons->height(); const auto ¤t = _transformButtons->isHidden() ? _paintBottomButtons : _transformButtons; current->moveToLeft( (size.width() - current->width()) / 2, buttonsTop); }, lifetime()); controllers->undoController->setPerformRequestChanges(rpl::merge( _undoButton->clicks() | rpl::map_to(Undo::Undo), _redoButton->clicks() | rpl::map_to(Undo::Redo))); controllers->undoController->canPerformChanges( ) | rpl::start_with_next([=](const UndoController::EnableRequest &r) { const auto isUndo = (r.command == Undo::Undo); const auto &button = isUndo ? _undoButton : _redoButton; button->setAttribute(Qt::WA_TransparentForMouseEvents, !r.enable); if (!r.enable) { button->clearState(); } button->setIconOverride(r.enable ? nullptr : isUndo ? &st::photoEditorUndoButtonInactive : &st::photoEditorRedoButtonInactive); }, lifetime()); if (_stickersButton) { controllers->stickersPanelController->setShowRequestChanges( _stickersButton->clicks( ) | rpl::map_to(std::optional(std::nullopt))); controllers->stickersPanelController->setMoveRequestChanges( _paintBottomButtons->positionValue( ) | rpl::map([=](const QPoint &containerPos) { return QPoint( (x() + width()) / 2, y() + containerPos.y() + _stickersButton->y()); })); controllers->stickersPanelController->panelShown( ) | rpl::start_with_next([=](bool shown) { const auto icon = shown ? &st::photoEditorStickersIconActive : nullptr; _stickersButton->setIconOverride(icon, icon); }, _stickersButton->lifetime()); } rpl::single( rpl::empty_value() ) | rpl::skip(modifications.flipped ? 0 : 1) | rpl::then( _flipButton->clicks() | rpl::to_empty ) | rpl::start_with_next([=] { _flipped = !_flipped; const auto icon = _flipped ? &st::photoEditorFlipIconActive : nullptr; _flipButton->setIconOverride(icon, icon); }, _flipButton->lifetime()); } rpl::producer PhotoEditorControls::rotateRequests() const { return _rotateButton->clicks() | rpl::map_to(90); } rpl::producer<> PhotoEditorControls::flipRequests() const { return _flipButton->clicks() | rpl::to_empty; } rpl::producer<> PhotoEditorControls::paintModeRequests() const { return _paintModeButton->clicks() | rpl::to_empty; } rpl::producer<> PhotoEditorControls::doneRequests() const { return rpl::merge( _transformDone->clicks() | rpl::to_empty, _paintDone->clicks() | rpl::to_empty); } rpl::producer<> PhotoEditorControls::cancelRequests() const { return rpl::merge( _transformCancel->clicks() | rpl::to_empty, _paintCancel->clicks() | rpl::to_empty); } void PhotoEditorControls::applyMode(const PhotoEditorMode &mode) { using Mode = PhotoEditorMode::Mode; _transformButtons->setVisible(mode.mode == Mode::Transform); _paintBottomButtons->setVisible(mode.mode == Mode::Paint); _mode = mode; } } // namespace Editor