Added color picker to photo editor.
This commit is contained in:
parent
4849376347
commit
dc7f440902
|
@ -508,6 +508,8 @@ PRIVATE
|
|||
dialogs/dialogs_search_from_controllers.h
|
||||
dialogs/dialogs_widget.cpp
|
||||
dialogs/dialogs_widget.h
|
||||
editor/color_picker.cpp
|
||||
editor/color_picker.h
|
||||
editor/editor_crop.cpp
|
||||
editor/editor_crop.h
|
||||
editor/editor_paint.cpp
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
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/color_picker.h"
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
#include "styles/style_editor.h"
|
||||
|
||||
#include <QtGui/QLinearGradient>
|
||||
|
||||
namespace Editor {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPrecision = 1000;
|
||||
constexpr auto kMinBrushSize = 0.1;
|
||||
constexpr auto kMouseSkip = 1.4;
|
||||
|
||||
constexpr auto kMinInnerHeight = 0.2;
|
||||
constexpr auto kMaxInnerHeight = 0.8;
|
||||
|
||||
constexpr auto kCircleDuration = crl::time(200);
|
||||
|
||||
constexpr auto kMax = 1.0;
|
||||
|
||||
ColorPicker::OutlinedStop FindOutlinedStop(
|
||||
const QColor &color,
|
||||
const QGradientStops &stops,
|
||||
int width) {
|
||||
for (auto i = 0; i < stops.size(); i++) {
|
||||
const auto ¤t = stops[i];
|
||||
if (current.second == color) {
|
||||
const auto prev = ((i - 1) < 0)
|
||||
? std::nullopt
|
||||
: std::make_optional<int>(stops[i - 1].first * width);
|
||||
const auto next = ((i + 1) >= stops.size())
|
||||
? std::nullopt
|
||||
: std::make_optional<int>(stops[i + 1].first * width);
|
||||
return ColorPicker::OutlinedStop{
|
||||
.stopPos = (current.first * width),
|
||||
.prevStopPos = prev,
|
||||
.nextStopPos = next,
|
||||
};
|
||||
}
|
||||
}
|
||||
return ColorPicker::OutlinedStop();
|
||||
}
|
||||
|
||||
QGradientStops Colors() {
|
||||
return QGradientStops{
|
||||
{ 0.00f, QColor(234, 39, 57) },
|
||||
{ 0.14f, QColor(219, 58, 210) },
|
||||
{ 0.24f, QColor(48, 81, 227) },
|
||||
{ 0.39f, QColor(73, 197, 237) },
|
||||
{ 0.49f, QColor(128, 200, 100) },
|
||||
{ 0.62f, QColor(252, 222, 101) },
|
||||
{ 0.73f, QColor(252, 150, 77) },
|
||||
{ 0.85f, QColor(0, 0, 0) },
|
||||
{ 1.00f, QColor(255, 255, 255) } };
|
||||
}
|
||||
|
||||
QBrush GradientBrush(const QPoint &p, const QGradientStops &stops) {
|
||||
auto gradient = QLinearGradient(0, p.y(), p.x(), p.y());
|
||||
gradient.setStops(stops);
|
||||
return QBrush(std::move(gradient));
|
||||
}
|
||||
|
||||
float RatioPrecise(float a) {
|
||||
return int(a * kPrecision) / float(kPrecision);
|
||||
}
|
||||
|
||||
inline float64 InterpolateF(float a, float b, float64 b_ratio) {
|
||||
return a + float64(b - a) * b_ratio;
|
||||
};
|
||||
|
||||
inline float64 InterpolationRatio(int from, int to, int result) {
|
||||
return (result - from) / float64(to - from);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ColorPicker::ColorPicker(not_null<Ui::RpWidget*> parent)
|
||||
: _circleColor(Qt::white)
|
||||
, _width(st::photoEditorColorPickerWidth)
|
||||
, _lineHeight(st::photoEditorColorPickerLineHeight)
|
||||
, _colorLine(base::make_unique_q<Ui::RpWidget>(parent))
|
||||
, _canvasForCircle(base::make_unique_q<Ui::RpWidget>(parent))
|
||||
, _gradientStops(Colors())
|
||||
, _outlinedStop(FindOutlinedStop(_circleColor, _gradientStops, _width))
|
||||
, _gradientBrush(
|
||||
GradientBrush(QPoint(_width, _lineHeight / 2), _gradientStops))
|
||||
, _brush(Brush{ .sizeRatio = kMinBrushSize, .color = QColor() }) {
|
||||
_colorLine->resize(_width, _lineHeight);
|
||||
_canvasForCircle->resize(
|
||||
_width + circleHeight(kMax),
|
||||
st::photoEditorColorPickerCanvasHeight);
|
||||
|
||||
_canvasForCircle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
_colorLine->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
Painter p(_colorLine);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_gradientBrush);
|
||||
|
||||
const auto radius = _colorLine->height() / 2.;
|
||||
p.drawRoundedRect(_colorLine->rect(), radius, radius);
|
||||
}, _colorLine->lifetime());
|
||||
|
||||
_canvasForCircle->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
Painter p(_canvasForCircle);
|
||||
paintCircle(p);
|
||||
}, _canvasForCircle->lifetime());
|
||||
|
||||
_colorLine->events(
|
||||
) | rpl::start_with_next([=](not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
const auto isPress = (type == QEvent::MouseButtonPress)
|
||||
|| (type == QEvent::MouseButtonDblClick);
|
||||
const auto isMove = (type == QEvent::MouseMove);
|
||||
const auto isRelease = (type == QEvent::MouseButtonRelease);
|
||||
if (!isPress && !isMove && !isRelease) {
|
||||
return;
|
||||
}
|
||||
_down.pressed = !isRelease;
|
||||
|
||||
const auto progress = _circleAnimation.value(isPress ? 0. : 1.);
|
||||
if (!isMove) {
|
||||
const auto from = progress;
|
||||
const auto to = isPress ? 1. : 0.;
|
||||
_circleAnimation.stop();
|
||||
|
||||
_circleAnimation.start(
|
||||
[=] { _canvasForCircle->update(); },
|
||||
from,
|
||||
to,
|
||||
kCircleDuration * std::abs(to - from),
|
||||
anim::easeOutCirc);
|
||||
}
|
||||
const auto e = static_cast<QMouseEvent*>(event.get());
|
||||
updateMousePosition(e->pos(), progress);
|
||||
|
||||
_canvasForCircle->update();
|
||||
}, _colorLine->lifetime());
|
||||
}
|
||||
|
||||
void ColorPicker::updateMousePosition(const QPoint &pos, float64 progress) {
|
||||
const auto mapped = _canvasForCircle->mapFromParent(
|
||||
_colorLine->mapToParent(pos));
|
||||
|
||||
const auto height = circleHeight(progress);
|
||||
const auto mappedY = int(mapped.y() - height * kMouseSkip);
|
||||
const auto bottom = _canvasForCircle->height() - circleHeight(kMax);
|
||||
const auto &skip = st::photoEditorColorPickerCircleSkip;
|
||||
|
||||
_down.pos = QPoint(
|
||||
std::clamp(pos.x(), 0, _width),
|
||||
std::clamp(mappedY, 0, bottom - skip));
|
||||
|
||||
// Convert Y to the brush size.
|
||||
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()),
|
||||
kMinBrushSize,
|
||||
1.);
|
||||
const auto color = positionToColor(_down.pos.x());
|
||||
|
||||
_brush = Brush{
|
||||
.sizeRatio = float(size),
|
||||
.color = color,
|
||||
};
|
||||
}
|
||||
|
||||
void ColorPicker::moveLine(const QPoint &position) {
|
||||
_colorLine->move(position
|
||||
- QPoint(_colorLine->width() / 2, _colorLine->height() / 2));
|
||||
|
||||
_canvasForCircle->move(
|
||||
_colorLine->x() - circleHeight(kMax) / 2,
|
||||
_colorLine->y()
|
||||
+ _colorLine->height()
|
||||
+ ((circleHeight() - _colorLine->height()) / 2)
|
||||
- _canvasForCircle->height());
|
||||
}
|
||||
|
||||
QColor ColorPicker::positionToColor(int x) const {
|
||||
const auto from = 0;
|
||||
const auto to = _width;
|
||||
const auto gradientRatio = InterpolationRatio(from, to, x);
|
||||
|
||||
for (auto i = 1; i < _gradientStops.size(); i++) {
|
||||
const auto &previous = _gradientStops[i - 1];
|
||||
const auto ¤t = _gradientStops[i];
|
||||
const auto &fromStop = previous.first;
|
||||
const auto &toStop = current.first;
|
||||
const auto &fromColor = previous.second;
|
||||
const auto &toColor = current.second;
|
||||
|
||||
if ((fromStop <= gradientRatio) && (toStop >= gradientRatio)) {
|
||||
const auto stopRatio = RatioPrecise(
|
||||
(gradientRatio - fromStop) / float64(toStop - fromStop));
|
||||
return anim::color(fromColor, toColor, stopRatio);
|
||||
}
|
||||
}
|
||||
return QColor();
|
||||
}
|
||||
|
||||
void ColorPicker::paintCircle(Painter &p) {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_circleColor);
|
||||
|
||||
const auto progress = _circleAnimation.value(_down.pressed ? 1. : 0.);
|
||||
const auto h = circleHeight(progress);
|
||||
const auto bottom = _canvasForCircle->height() - h;
|
||||
|
||||
const auto circleX = _down.pos.x() + (circleHeight(kMax) - h) / 2;
|
||||
const auto circleY = _circleAnimation.animating()
|
||||
? anim::interpolate(bottom, _down.pos.y(), progress)
|
||||
: _down.pressed
|
||||
? _down.pos.y()
|
||||
: bottom;
|
||||
|
||||
const auto r = QRect(circleX, circleY, h, h);
|
||||
p.drawEllipse(r);
|
||||
|
||||
const auto innerH = InterpolateF(
|
||||
h * kMinInnerHeight,
|
||||
h * kMaxInnerHeight,
|
||||
_brush.current().sizeRatio);
|
||||
|
||||
p.setBrush(_brush.current().color);
|
||||
|
||||
const auto innerRect = QRectF(
|
||||
r.x() + (r.width() - innerH) / 2.,
|
||||
r.y() + (r.height() - innerH) / 2.,
|
||||
innerH,
|
||||
innerH);
|
||||
|
||||
paintOutline(p, innerRect);
|
||||
p.drawEllipse(innerRect);
|
||||
}
|
||||
|
||||
void ColorPicker::paintOutline(Painter &p, const QRectF &rect) {
|
||||
const auto &s = _outlinedStop;
|
||||
if (!s.stopPos) {
|
||||
return;
|
||||
}
|
||||
const auto draw = [&](float opacity) {
|
||||
const auto was = p.opacity();
|
||||
p.save();
|
||||
p.setOpacity(opacity);
|
||||
p.setPen(Qt::lightGray);
|
||||
p.setPen(Qt::NoBrush);
|
||||
p.drawEllipse(rect);
|
||||
p.restore();
|
||||
};
|
||||
const auto x = _down.pos.x();
|
||||
if (s.prevStopPos && (x >= s.prevStopPos && x <= s.stopPos)) {
|
||||
const auto from = *s.prevStopPos;
|
||||
const auto to = *s.stopPos;
|
||||
const auto ratio = InterpolationRatio(from, to, x);
|
||||
if (ratio >= 0. && ratio <= 1.) {
|
||||
draw(ratio);
|
||||
}
|
||||
} else if (s.nextStopPos && (x >= s.stopPos && x <= s.nextStopPos)) {
|
||||
const auto from = *s.stopPos;
|
||||
const auto to = *s.nextStopPos;
|
||||
const auto ratio = InterpolationRatio(from, to, x);
|
||||
if (ratio >= 0. && ratio <= 1.) {
|
||||
draw(1. - ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ColorPicker::circleHeight(float64 progress) const {
|
||||
return anim::interpolate(
|
||||
st::photoEditorColorPickerCircleSize,
|
||||
st::photoEditorColorPickerCircleBigSize,
|
||||
progress);
|
||||
}
|
||||
|
||||
void ColorPicker::setVisible(bool visible) {
|
||||
_colorLine->setVisible(visible);
|
||||
_canvasForCircle->setVisible(visible);
|
||||
}
|
||||
|
||||
rpl::producer<Brush> ColorPicker::brushValue() const {
|
||||
return _brush.value();
|
||||
}
|
||||
|
||||
} // namespace Editor
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/unique_qptr.h"
|
||||
#include "editor/photo_editor_common.h"
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Editor {
|
||||
|
||||
class ColorPicker final {
|
||||
public:
|
||||
struct OutlinedStop {
|
||||
std::optional<int> stopPos = std::nullopt;
|
||||
std::optional<int> prevStopPos = std::nullopt;
|
||||
std::optional<int> nextStopPos = std::nullopt;
|
||||
};
|
||||
|
||||
ColorPicker(not_null<Ui::RpWidget*> parent);
|
||||
|
||||
void moveLine(const QPoint &position);
|
||||
void setVisible(bool visible);
|
||||
|
||||
rpl::producer<Brush> brushValue() const;
|
||||
|
||||
private:
|
||||
void paintCircle(Painter &p);
|
||||
void paintOutline(Painter &p, const QRectF &rect);
|
||||
QColor positionToColor(int x) const;
|
||||
int circleHeight(float64 progress = 0.) const;
|
||||
void updateMousePosition(const QPoint &pos, float64 progress);
|
||||
|
||||
const QColor _circleColor;
|
||||
const int _width;
|
||||
const int _lineHeight;
|
||||
|
||||
const base::unique_qptr<Ui::RpWidget> _colorLine;
|
||||
const base::unique_qptr<Ui::RpWidget> _canvasForCircle;
|
||||
|
||||
const QGradientStops _gradientStops;
|
||||
const OutlinedStop _outlinedStop;
|
||||
const QBrush _gradientBrush;
|
||||
|
||||
struct {
|
||||
QPoint pos;
|
||||
bool pressed = false;
|
||||
} _down;
|
||||
rpl::variable<Brush> _brush;
|
||||
|
||||
Ui::Animations::Simple _circleAnimation;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Editor
|
|
@ -12,6 +12,7 @@ using "ui/widgets/widgets.style";
|
|||
using "ui/chat/chat.style";
|
||||
|
||||
photoEditorControlsHeight: 100px;
|
||||
photoEditorControlsTopSkip: 50px;
|
||||
|
||||
photoEditorButtonIconFg: historyComposeIconFg;
|
||||
photoEditorButtonIconFgOver: historyComposeIconFgOver;
|
||||
|
@ -44,3 +45,13 @@ photoEditorUndoButtonInactive: icon {{ "photo_editor/undo", photoEditorButtonIco
|
|||
photoEditorRedoButtonInactive: icon {{ "photo_editor/undo-flip_horizontal", photoEditorButtonIconFgInactive }};
|
||||
|
||||
photoEditorTextButtonPadding: margins(10px, 0px, 10px, 0px);
|
||||
|
||||
photoEditorColorPickerTopSkip: 20px;
|
||||
photoEditorColorPickerWidth: 250px;
|
||||
photoEditorColorPickerLineHeight: 20px;
|
||||
photoEditorColorPickerCanvasHeight: 300px;
|
||||
photoEditorColorPickerCircleSize: 24px;
|
||||
photoEditorColorPickerCircleBigSize: 50px;
|
||||
|
||||
photoEditorColorPickerCircleSkip: 50px;
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Editor {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxBrush = 25.;
|
||||
constexpr auto kMinBrush = 1.;
|
||||
|
||||
constexpr auto kViewStyle = "QGraphicsView {\
|
||||
background-color: transparent;\
|
||||
border: 0px\
|
||||
|
@ -116,9 +119,6 @@ void Paint::applyTransform(QRect geometry, int angle, bool flipped) {
|
|||
void Paint::initDrawing() {
|
||||
using Result = base::EventFilterResult;
|
||||
|
||||
_brushData.size = 10;
|
||||
_brushData.color = Qt::red;
|
||||
|
||||
auto callback = [=](not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
const auto isPress = (type == QEvent::GraphicsSceneMousePress);
|
||||
|
@ -249,4 +249,10 @@ std::vector<QGraphicsItem*> Paint::groups(Qt::SortOrder order) const {
|
|||
) | ranges::views::filter(GroupsFilter) | ranges::to_vector;
|
||||
}
|
||||
|
||||
void Paint::applyBrush(const Brush &brush) {
|
||||
_brushData.color = brush.color;
|
||||
_brushData.size =
|
||||
(kMinBrush + float64(kMaxBrush - kMinBrush) * brush.sizeRatio);
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
[[nodiscard]] std::shared_ptr<QGraphicsScene> saveScene() const;
|
||||
|
||||
void applyTransform(QRect geometry, int angle, bool flipped);
|
||||
void applyBrush(const Brush &brush);
|
||||
void cancel();
|
||||
void keepResult();
|
||||
void updateUndoState();
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "editor/photo_editor.h"
|
||||
|
||||
#include "editor/color_picker.h"
|
||||
#include "editor/photo_editor_content.h"
|
||||
#include "editor/photo_editor_controls.h"
|
||||
#include "editor/undo_controller.h"
|
||||
|
@ -26,9 +27,13 @@ PhotoEditor::PhotoEditor(
|
|||
photo,
|
||||
_modifications,
|
||||
_undoController))
|
||||
, _controls(base::make_unique_q<PhotoEditorControls>(this, _undoController)) {
|
||||
, _controls(base::make_unique_q<PhotoEditorControls>(this, _undoController))
|
||||
, _colorPicker(std::make_unique<ColorPicker>(this)) {
|
||||
sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
if (size.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto geometry = QRect(QPoint(), size);
|
||||
const auto contentRect = geometry
|
||||
- style::margins(0, 0, 0, st::photoEditorControlsHeight);
|
||||
|
@ -36,12 +41,17 @@ PhotoEditor::PhotoEditor(
|
|||
const auto controlsRect = geometry
|
||||
- style::margins(0, contentRect.height(), 0, 0);
|
||||
_controls->setGeometry(controlsRect);
|
||||
|
||||
_colorPicker->moveLine(QPoint(
|
||||
controlsRect.x() + controlsRect.width() / 2,
|
||||
controlsRect.y() + st::photoEditorColorPickerTopSkip));
|
||||
}, lifetime());
|
||||
|
||||
_mode.value(
|
||||
) | rpl::start_with_next([=](const PhotoEditorMode &mode) {
|
||||
_content->applyMode(mode);
|
||||
_controls->applyMode(mode);
|
||||
_colorPicker->setVisible(mode.mode == PhotoEditorMode::Mode::Paint);
|
||||
}, lifetime());
|
||||
|
||||
_controls->rotateRequests(
|
||||
|
@ -86,6 +96,11 @@ PhotoEditor::PhotoEditor(
|
|||
};
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_colorPicker->brushValue(
|
||||
) | rpl::start_with_next([=](const Brush &brush) {
|
||||
_content->applyBrush(brush);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void PhotoEditor::save() {
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace Editor {
|
||||
|
||||
class ColorPicker;
|
||||
class PhotoEditorContent;
|
||||
class PhotoEditorControls;
|
||||
class UndoController;
|
||||
|
@ -37,6 +38,7 @@ private:
|
|||
|
||||
base::unique_qptr<PhotoEditorContent> _content;
|
||||
base::unique_qptr<PhotoEditorControls> _controls;
|
||||
const std::unique_ptr<ColorPicker> _colorPicker;
|
||||
|
||||
rpl::variable<PhotoEditorMode> _mode = PhotoEditorMode{
|
||||
.mode = PhotoEditorMode::Mode::Transform,
|
||||
|
|
|
@ -36,6 +36,11 @@ struct PhotoModifications {
|
|||
|
||||
};
|
||||
|
||||
struct Brush {
|
||||
float sizeRatio = 0.;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
[[nodiscard]] QImage ImageModified(
|
||||
QImage image,
|
||||
const PhotoModifications &mods);
|
||||
|
|
|
@ -94,6 +94,9 @@ void PhotoEditorContent::applyModifications(
|
|||
void PhotoEditorContent::save(PhotoModifications &modifications) {
|
||||
modifications.crop = _crop->saveCropRect(_imageRect, _photo->rect());
|
||||
_paint->keepResult();
|
||||
if (!modifications.paint) {
|
||||
modifications.paint = _paint->saveScene();
|
||||
}
|
||||
}
|
||||
|
||||
void PhotoEditorContent::applyMode(const PhotoEditorMode &mode) {
|
||||
|
@ -113,4 +116,8 @@ void PhotoEditorContent::applyMode(const PhotoEditorMode &mode) {
|
|||
_mode = mode;
|
||||
}
|
||||
|
||||
void PhotoEditorContent::applyBrush(const Brush &brush) {
|
||||
_paint->applyBrush(brush);
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
void applyModifications(PhotoModifications modifications);
|
||||
void applyMode(const PhotoEditorMode &mode);
|
||||
void applyBrush(const Brush &brush);
|
||||
void save(PhotoModifications &modifications);
|
||||
|
||||
private:
|
||||
|
|
|
@ -192,6 +192,8 @@ PhotoEditorControls::PhotoEditorControls(
|
|||
|
||||
}, lifetime());
|
||||
|
||||
const auto &buttonsTop = st::photoEditorControlsTopSkip;
|
||||
|
||||
rpl::combine(
|
||||
sizeValue(),
|
||||
_mode.value()
|
||||
|
@ -208,10 +210,10 @@ PhotoEditorControls::PhotoEditorControls(
|
|||
|
||||
current->moveToLeft(
|
||||
(size.width() - current->width()) / 2,
|
||||
0);
|
||||
buttonsTop);
|
||||
|
||||
_cancel->moveToLeft(current->x() - _cancel->width(), 0);
|
||||
_done->moveToLeft(current->x() + current->width(), 0);
|
||||
_cancel->moveToLeft(current->x() - _cancel->width(), buttonsTop);
|
||||
_done->moveToLeft(current->x() + current->width(), buttonsTop);
|
||||
|
||||
}, lifetime());
|
||||
|
||||
|
|
Loading…
Reference in New Issue