310 lines
8.1 KiB
C++
310 lines
8.1 KiB
C++
/*
|
|
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.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<Ui::RpWidget*> 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<QColor> color) const;
|
|
|
|
};
|
|
|
|
EdgeButton::EdgeButton(
|
|
not_null<Ui::RpWidget*> 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<QColor> 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 HorizontalContainer final : public Ui::RpWidget {
|
|
public:
|
|
HorizontalContainer(not_null<Ui::RpWidget*> parent);
|
|
|
|
void updateChildrenPosition();
|
|
|
|
};
|
|
|
|
HorizontalContainer::HorizontalContainer(not_null<Ui::RpWidget*> parent)
|
|
: RpWidget(parent) {
|
|
}
|
|
|
|
void HorizontalContainer::updateChildrenPosition() {
|
|
auto left = 0;
|
|
auto height = 0;
|
|
for (auto child : RpWidget::children()) {
|
|
if (child->isWidgetType()) {
|
|
const auto widget = static_cast<QWidget*>(child);
|
|
widget->move(left, 0);
|
|
left += widget->width();
|
|
height = std::max(height, widget->height());
|
|
}
|
|
}
|
|
resize(left, height);
|
|
}
|
|
|
|
PhotoEditorControls::PhotoEditorControls(
|
|
not_null<Ui::RpWidget*> parent,
|
|
std::shared_ptr<Controllers> controllers,
|
|
const PhotoModifications modifications,
|
|
bool doneControls)
|
|
: RpWidget(parent)
|
|
, _bg(st::mediaviewSaveMsgBg)
|
|
, _transformButtons(base::make_unique_q<HorizontalContainer>(this))
|
|
, _paintButtons(base::make_unique_q<HorizontalContainer>(this))
|
|
, _rotateButton(base::make_unique_q<Ui::IconButton>(
|
|
_transformButtons,
|
|
st::photoEditorRotateButton))
|
|
, _flipButton(base::make_unique_q<Ui::IconButton>(
|
|
_transformButtons,
|
|
st::photoEditorFlipButton))
|
|
, _paintModeButton(base::make_unique_q<Ui::IconButton>(
|
|
_transformButtons,
|
|
st::photoEditorPaintModeButton))
|
|
, _undoButton(base::make_unique_q<Ui::IconButton>(
|
|
_paintButtons,
|
|
st::photoEditorUndoButton))
|
|
, _redoButton(base::make_unique_q<Ui::IconButton>(
|
|
_paintButtons,
|
|
st::photoEditorRedoButton))
|
|
, _paintModeButtonActive(base::make_unique_q<Ui::IconButton>(
|
|
_paintButtons,
|
|
st::photoEditorPaintModeButton))
|
|
, _stickersButton(controllers->stickersPanelController
|
|
? base::make_unique_q<Ui::IconButton>(
|
|
_paintButtons,
|
|
st::photoEditorStickersButton)
|
|
: nullptr)
|
|
, _cancel(base::make_unique_q<EdgeButton>(
|
|
this,
|
|
tr::lng_cancel(tr::now),
|
|
_flipButton->height(),
|
|
true,
|
|
_bg,
|
|
st::activeButtonFg,
|
|
st::photoEditorRotateButton.ripple))
|
|
, _done(base::make_unique_q<EdgeButton>(
|
|
this,
|
|
tr::lng_box_done(tr::now),
|
|
_flipButton->height(),
|
|
false,
|
|
_bg,
|
|
st::lightButtonFg,
|
|
st::photoEditorRotateButton.ripple))
|
|
, _flipped(modifications.flipped) {
|
|
|
|
_transformButtons->updateChildrenPosition();
|
|
_paintButtons->updateChildrenPosition();
|
|
|
|
_paintModeButtonActive->setIconOverride(
|
|
&st::photoEditorPaintModeButton.iconOver);
|
|
_paintModeButtonActive->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
paintRequest(
|
|
) | rpl::start_with_next([=](const QRect &clip) {
|
|
Painter p(this);
|
|
|
|
const auto ¤t = _transformButtons->isHidden()
|
|
? _paintButtons
|
|
: _transformButtons;
|
|
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(_bg);
|
|
p.drawRect(current->geometry());
|
|
|
|
}, lifetime());
|
|
|
|
const auto &buttonsTop = st::photoEditorControlsTopSkip;
|
|
|
|
rpl::combine(
|
|
sizeValue(),
|
|
_mode.value()
|
|
) | rpl::start_with_next([=](
|
|
const QSize &size,
|
|
const PhotoEditorMode &mode) {
|
|
if (size.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
const auto ¤t = _transformButtons->isHidden()
|
|
? _paintButtons
|
|
: _transformButtons;
|
|
|
|
current->moveToLeft(
|
|
(size.width() - current->width()) / 2,
|
|
buttonsTop);
|
|
|
|
_cancel->moveToLeft(current->x() - _cancel->width(), buttonsTop);
|
|
_done->moveToLeft(current->x() + current->width(), 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<bool>(std::nullopt)));
|
|
|
|
controllers->stickersPanelController->setMoveRequestChanges(
|
|
_paintButtons->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) {
|
|
_stickersButton->setIconOverride(shown
|
|
? &st::photoEditorStickersButton.iconOver
|
|
: nullptr);
|
|
}, _stickersButton->lifetime());
|
|
}
|
|
|
|
_flipButton->clicks(
|
|
) | rpl::start_with_next([=] {
|
|
_flipped = !_flipped;
|
|
_flipButton->setIconOverride(_flipped
|
|
? &st::photoEditorFlipButton.iconOver
|
|
: nullptr);
|
|
}, _flipButton->lifetime());
|
|
|
|
}
|
|
|
|
rpl::producer<int> 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 _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
|