mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[img-editor] Added ability to save tool size and color separately.
This commit is contained in:
@@ -36,6 +36,26 @@ inline float64 InterpolationRatio(int from, int to, int result) {
|
||||
return (result - from) / float64(to - from);
|
||||
};
|
||||
|
||||
[[nodiscard]] int ToolIndex(Brush::Tool tool) {
|
||||
switch (tool) {
|
||||
case Brush::Tool::Pen: return 0;
|
||||
case Brush::Tool::Arrow: return 1;
|
||||
case Brush::Tool::Marker: return 2;
|
||||
case Brush::Tool::Eraser: return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] Brush::Tool ToolFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0: return Brush::Tool::Pen;
|
||||
case 1: return Brush::Tool::Arrow;
|
||||
case 2: return Brush::Tool::Marker;
|
||||
case 3: return Brush::Tool::Eraser;
|
||||
}
|
||||
return Brush::Tool::Pen;
|
||||
}
|
||||
|
||||
class PlusCircle final : public Ui::AbstractButton {
|
||||
public:
|
||||
using Ui::AbstractButton::AbstractButton;
|
||||
@@ -165,7 +185,8 @@ std::vector<QColor> PaletteColors() {
|
||||
ColorPicker::ColorPicker(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
const Brush &savedBrush)
|
||||
const std::array<Brush, 4> &savedBrushes,
|
||||
Brush::Tool savedTool)
|
||||
: _parent(parent)
|
||||
, _show(std::move(show))
|
||||
, _colorButton(
|
||||
@@ -182,21 +203,20 @@ ColorPicker::ColorPicker(
|
||||
, _sizeControlHoverArea(std::in_place, parent)
|
||||
, _sizeControl(std::in_place, parent)
|
||||
, _toolSelection(std::in_place, parent)
|
||||
, _brush(Brush{
|
||||
.sizeRatio = (savedBrush.sizeRatio
|
||||
? savedBrush.sizeRatio
|
||||
: kMinBrushSize),
|
||||
.color = (savedBrush.color.isValid()
|
||||
? savedBrush.color
|
||||
: QColor(234, 39, 57)),
|
||||
.tool = savedBrush.tool,
|
||||
}) {
|
||||
, _brush(savedBrushes[ToolIndex(savedTool)])
|
||||
, _toolBrushes(savedBrushes) {
|
||||
_colorButton->resize(
|
||||
st::photoEditorColorButtonSize,
|
||||
st::photoEditorColorButtonSize);
|
||||
_colorButton->setSelectionCutout(true);
|
||||
_colorButton->setForceCircle(true);
|
||||
|
||||
for (auto i = 0; i != int(_toolBrushes.size()); ++i) {
|
||||
_toolBrushes[i].tool = ToolFromIndex(i);
|
||||
}
|
||||
_brush = _toolBrushes[ToolIndex(savedTool)];
|
||||
_brush.tool = savedTool;
|
||||
|
||||
_toolSelection->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_toolSelection->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
_toolSelection->setVisible(false);
|
||||
@@ -237,26 +257,21 @@ ColorPicker::ColorPicker(
|
||||
st::photoEditorToolButtonSize);
|
||||
button->show();
|
||||
}
|
||||
const auto setTool = [=](Brush::Tool tool) {
|
||||
if (_brush.tool == tool) {
|
||||
return;
|
||||
}
|
||||
_brush.tool = tool;
|
||||
updateToolSelection(true);
|
||||
_saveBrushRequests.fire_copy(_brush);
|
||||
const auto setToolRequest = [=](Brush::Tool tool) {
|
||||
setTool(tool);
|
||||
};
|
||||
if (_toolButtons.size() >= 4) {
|
||||
_toolButtons[0]->setClickedCallback([=] {
|
||||
setTool(Brush::Tool::Pen);
|
||||
setToolRequest(Brush::Tool::Pen);
|
||||
});
|
||||
_toolButtons[1]->setClickedCallback([=] {
|
||||
setTool(Brush::Tool::Arrow);
|
||||
setToolRequest(Brush::Tool::Arrow);
|
||||
});
|
||||
_toolButtons[2]->setClickedCallback([=] {
|
||||
setTool(Brush::Tool::Marker);
|
||||
setToolRequest(Brush::Tool::Marker);
|
||||
});
|
||||
_toolButtons[3]->setClickedCallback([=] {
|
||||
setTool(Brush::Tool::Eraser);
|
||||
setToolRequest(Brush::Tool::Eraser);
|
||||
});
|
||||
}
|
||||
updateToolSelection(false);
|
||||
@@ -397,15 +412,7 @@ void ColorPicker::updateToolSelection(bool animated) {
|
||||
if (_toolButtons.size() < 4) {
|
||||
return;
|
||||
}
|
||||
const auto index = [&] {
|
||||
switch (_brush.tool) {
|
||||
case Brush::Tool::Pen: return 0;
|
||||
case Brush::Tool::Arrow: return 1;
|
||||
case Brush::Tool::Marker: return 2;
|
||||
case Brush::Tool::Eraser: return 3;
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
const auto index = ToolIndex(_brush.tool);
|
||||
if (index < 0 || index >= int(_toolButtons.size())) {
|
||||
return;
|
||||
}
|
||||
@@ -438,6 +445,28 @@ void ColorPicker::updateToolSelection(bool animated) {
|
||||
anim::easeOutCirc);
|
||||
}
|
||||
|
||||
void ColorPicker::setTool(Brush::Tool tool) {
|
||||
if (_brush.tool == tool) {
|
||||
return;
|
||||
}
|
||||
storeCurrentBrush();
|
||||
_brush = _toolBrushes[ToolIndex(tool)];
|
||||
_brush.tool = tool;
|
||||
updateSizeControlPositionFromRatio();
|
||||
if (_paletteVisible) {
|
||||
rebuildPalette();
|
||||
} else {
|
||||
_colorButton->update();
|
||||
}
|
||||
updateToolSelection(true);
|
||||
_sizeControl->update();
|
||||
_saveBrushRequests.fire_copy(_brush);
|
||||
}
|
||||
|
||||
void ColorPicker::storeCurrentBrush() {
|
||||
_toolBrushes[ToolIndex(_brush.tool)] = _brush;
|
||||
}
|
||||
|
||||
void ColorPicker::paintSizeControl(QPainter &p) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
|
||||
@@ -517,6 +546,7 @@ void ColorPicker::rebuildPalette() {
|
||||
button->setSelectionCutout(true);
|
||||
button->setClickedCallback([=] {
|
||||
_brush.color = c;
|
||||
storeCurrentBrush();
|
||||
rebuildPalette();
|
||||
_colorButton->update();
|
||||
_saveBrushRequests.fire_copy(_brush);
|
||||
@@ -550,6 +580,7 @@ void ColorPicker::rebuildPalette() {
|
||||
}, editor->lifetime());
|
||||
box->addButton(tr::lng_box_done(), [=] {
|
||||
_brush.color = state->color;
|
||||
storeCurrentBrush();
|
||||
rebuildPalette();
|
||||
_colorButton->update();
|
||||
_saveBrushRequests.fire_copy(_brush);
|
||||
@@ -667,6 +698,7 @@ void ColorPicker::updateSizeControlExpanded() {
|
||||
void ColorPicker::updateSizeControlMousePosition(int y) {
|
||||
_sizeDown.y = std::clamp(y, sizeControlTop(), sizeControlBottom());
|
||||
_brush.sizeRatio = sizeControlRatioFromY(_sizeDown.y);
|
||||
storeCurrentBrush();
|
||||
}
|
||||
|
||||
void ColorPicker::updateSizeControlPositionFromRatio() {
|
||||
|
||||
@@ -24,7 +24,8 @@ public:
|
||||
ColorPicker(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
const Brush &savedBrush);
|
||||
const std::array<Brush, 4> &savedBrushes,
|
||||
Brush::Tool savedTool);
|
||||
|
||||
void moveLine(const QPoint &position);
|
||||
void setCanvasRect(const QRect &rect);
|
||||
@@ -38,6 +39,8 @@ private:
|
||||
void rebuildPalette();
|
||||
void updateToolButtonsGeometry();
|
||||
void updateToolSelection(bool animated);
|
||||
void setTool(Brush::Tool tool);
|
||||
void storeCurrentBrush();
|
||||
void updatePaletteGeometry();
|
||||
void setPaletteVisible(bool visible);
|
||||
void moveSizeControl(const QSize &size);
|
||||
@@ -77,6 +80,7 @@ private:
|
||||
QPoint _colorButtonCenter;
|
||||
bool _paletteVisible = false;
|
||||
Brush _brush;
|
||||
std::array<Brush, 4> _toolBrushes;
|
||||
|
||||
Ui::Animations::Simple _sizeControlAnimation;
|
||||
Ui::Animations::Simple _toolSelectionAnimation;
|
||||
|
||||
@@ -22,42 +22,162 @@ namespace Editor {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPrecision = 100000;
|
||||
constexpr auto kBrushesVersion = -1;
|
||||
constexpr auto kDefaultBrushSizeRatio = 0.9;
|
||||
|
||||
[[nodiscard]] QByteArray Serialize(const Brush &brush) {
|
||||
[[nodiscard]] int ToolIndex(Brush::Tool tool) {
|
||||
switch (tool) {
|
||||
case Brush::Tool::Pen: return 0;
|
||||
case Brush::Tool::Arrow: return 1;
|
||||
case Brush::Tool::Marker: return 2;
|
||||
case Brush::Tool::Eraser: return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] Brush::Tool ToolFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0: return Brush::Tool::Pen;
|
||||
case 1: return Brush::Tool::Arrow;
|
||||
case 2: return Brush::Tool::Marker;
|
||||
case 3: return Brush::Tool::Eraser;
|
||||
}
|
||||
return Brush::Tool::Pen;
|
||||
}
|
||||
|
||||
[[nodiscard]] Brush::Tool ToolFromSerialized(qint32 value) {
|
||||
switch (value) {
|
||||
case int(Brush::Tool::Pen): return Brush::Tool::Pen;
|
||||
case int(Brush::Tool::Arrow): return Brush::Tool::Arrow;
|
||||
case int(Brush::Tool::Marker): return Brush::Tool::Marker;
|
||||
case int(Brush::Tool::Eraser): return Brush::Tool::Eraser;
|
||||
}
|
||||
return Brush::Tool::Pen;
|
||||
}
|
||||
|
||||
[[nodiscard]] QColor DefaultBrushColor(Brush::Tool tool) {
|
||||
switch (tool) {
|
||||
case Brush::Tool::Pen: return QColor(234, 39, 57);
|
||||
case Brush::Tool::Arrow: return QColor(252, 150, 77);
|
||||
case Brush::Tool::Marker: return QColor(252, 222, 101);
|
||||
case Brush::Tool::Eraser: return QColor(234, 39, 57);
|
||||
}
|
||||
return QColor(234, 39, 57);
|
||||
}
|
||||
|
||||
[[nodiscard]] Brush DefaultBrush(Brush::Tool tool) {
|
||||
auto result = Brush();
|
||||
result.sizeRatio = kDefaultBrushSizeRatio;
|
||||
result.color = DefaultBrushColor(tool);
|
||||
result.tool = tool;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::array<Brush, 4> DefaultBrushes() {
|
||||
auto result = std::array<Brush, 4>();
|
||||
for (auto i = 0; i != int(result.size()); ++i) {
|
||||
const auto tool = ToolFromIndex(i);
|
||||
result[i] = DefaultBrush(tool);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct BrushState {
|
||||
std::array<Brush, 4> brushes = DefaultBrushes();
|
||||
Brush::Tool tool = Brush::Tool::Pen;
|
||||
};
|
||||
|
||||
[[nodiscard]] QByteArray Serialize(
|
||||
const std::array<Brush, 4> &brushes,
|
||||
Brush::Tool tool) {
|
||||
auto result = QByteArray();
|
||||
auto stream = QDataStream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_3);
|
||||
stream << qint32(brush.sizeRatio * kPrecision)
|
||||
<< brush.color
|
||||
<< qint32(int(brush.tool));
|
||||
stream
|
||||
<< qint32(kBrushesVersion)
|
||||
<< qint32(int(tool))
|
||||
<< qint32(brushes.size());
|
||||
for (auto i = 0; i != int(brushes.size()); ++i) {
|
||||
const auto tool = ToolFromIndex(i);
|
||||
const auto &brush = brushes[i];
|
||||
stream
|
||||
<< qint32(int(tool))
|
||||
<< qint32(brush.sizeRatio * kPrecision)
|
||||
<< brush.color;
|
||||
}
|
||||
stream.device()->close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Brush Deserialize(const QByteArray &data) {
|
||||
auto stream = QDataStream(data);
|
||||
auto result = Brush();
|
||||
auto size = qint32(0);
|
||||
auto tool = qint32(int(Brush::Tool::Pen));
|
||||
stream >> size >> result.color;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return Brush();
|
||||
[[nodiscard]] BrushState Deserialize(const QByteArray &data) {
|
||||
auto result = BrushState();
|
||||
if (data.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> tool;
|
||||
if (stream.status() == QDataStream::Ok) {
|
||||
switch (tool) {
|
||||
case int(Brush::Tool::Pen):
|
||||
case int(Brush::Tool::Arrow):
|
||||
case int(Brush::Tool::Marker):
|
||||
case int(Brush::Tool::Eraser):
|
||||
result.tool = Brush::Tool(tool);
|
||||
break;
|
||||
auto stream = QDataStream(data);
|
||||
auto head = qint32(0);
|
||||
stream >> head;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
if (head < 0) {
|
||||
auto toolValue = qint32(int(Brush::Tool::Pen));
|
||||
auto count = qint32(0);
|
||||
stream >> toolValue >> count;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
result.tool = ToolFromSerialized(toolValue);
|
||||
auto limit = int(count);
|
||||
if (limit < 0) {
|
||||
limit = 0;
|
||||
} else if (limit > int(result.brushes.size())) {
|
||||
limit = int(result.brushes.size());
|
||||
}
|
||||
for (auto i = 0; i != limit; ++i) {
|
||||
auto entryTool = qint32(int(Brush::Tool::Pen));
|
||||
auto size = qint32(0);
|
||||
auto color = QColor();
|
||||
stream >> entryTool >> size >> color;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
const auto tool = ToolFromSerialized(entryTool);
|
||||
const auto index = ToolIndex(tool);
|
||||
if (size > 0) {
|
||||
result.brushes[index].sizeRatio = size / float(kPrecision);
|
||||
}
|
||||
if (color.isValid()) {
|
||||
result.brushes[index].color = color;
|
||||
}
|
||||
result.brushes[index].tool = tool;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
auto size = head;
|
||||
auto color = QColor();
|
||||
stream >> color;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return result;
|
||||
}
|
||||
auto toolValue = qint32(int(Brush::Tool::Pen));
|
||||
if (!stream.atEnd()) {
|
||||
stream >> toolValue;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
toolValue = qint32(int(Brush::Tool::Pen));
|
||||
}
|
||||
}
|
||||
result.sizeRatio = size / float(kPrecision);
|
||||
const auto tool = ToolFromSerialized(toolValue);
|
||||
const auto index = ToolIndex(tool);
|
||||
if (size > 0) {
|
||||
result.brushes[index].sizeRatio = size / float(kPrecision);
|
||||
}
|
||||
if (color.isValid()) {
|
||||
result.brushes[index].color = color;
|
||||
}
|
||||
result.brushes[index].tool = tool;
|
||||
result.tool = tool;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -108,10 +228,13 @@ PhotoEditor::PhotoEditor(
|
||||
_controllers,
|
||||
_modifications,
|
||||
data))
|
||||
, _brushes(Deserialize(Core::App().settings().photoEditorBrush()).brushes)
|
||||
, _brushTool(Deserialize(Core::App().settings().photoEditorBrush()).tool)
|
||||
, _colorPicker(std::make_unique<ColorPicker>(
|
||||
this,
|
||||
std::move(show),
|
||||
Deserialize(Core::App().settings().photoEditorBrush()))) {
|
||||
_brushes,
|
||||
_brushTool)) {
|
||||
|
||||
sizeValue(
|
||||
) | rpl::on_next([=](const QSize &size) {
|
||||
@@ -211,7 +334,9 @@ PhotoEditor::PhotoEditor(
|
||||
) | rpl::on_next([=](const Brush &brush) {
|
||||
_content->applyBrush(brush);
|
||||
|
||||
const auto serialized = Serialize(brush);
|
||||
_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();
|
||||
|
||||
@@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "editor/photo_editor_common.h"
|
||||
#include "editor/photo_editor_inner_common.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Ui {
|
||||
class LayerWidget;
|
||||
class Show;
|
||||
@@ -62,6 +64,8 @@ private:
|
||||
|
||||
base::unique_qptr<PhotoEditorContent> _content;
|
||||
base::unique_qptr<PhotoEditorControls> _controls;
|
||||
std::array<Brush, 4> _brushes;
|
||||
Brush::Tool _brushTool = Brush::Tool::Pen;
|
||||
const std::unique_ptr<ColorPicker> _colorPicker;
|
||||
|
||||
rpl::variable<PhotoEditorMode> _mode = PhotoEditorMode{
|
||||
|
||||
Reference in New Issue
Block a user