tdesktop/Telegram/SourceFiles/boxes/editcolorbox.cpp
John Preston 08167a6a91 Removed #include "stdafx.h" from all files.
Currently the build without implicitly included precompiled header
is not supported anyway (because Qt MOC source files do not include
stdafx.h, they include plain headers).

So when we decide to support building without implicitly included
precompiled headers we'll have to fix all the headers anyway.
2017-03-04 12:27:52 +03:00

907 lines
No EOL
27 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/editcolorbox.h"
#include "lang.h"
#include "styles/style_boxes.h"
#include "ui/widgets/shadow.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/input_fields.h"
class EditColorBox::Picker : public TWidget {
public:
Picker(QWidget *parent, QColor color);
float64 valueX() const {
return _x;
}
float64 valueY() const {
return _y;
}
base::Observable<void> &changed() {
return _changed;
}
void setHSV(int hue, int saturation, int brightness);
void setRGB(int red, int green, int blue);
protected:
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
private:
void setFromColor(QColor color);
QCursor generateCursor();
void preparePalette();
void updateCurrentPoint(QPoint localPosition);
QColor _topleft;
QColor _topright;
QColor _bottomleft;
QColor _bottomright;
QImage _palette;
bool _paletteInvalidated = false;
float64 _x = 0.;
float64 _y = 0.;
bool _choosing = false;
base::Observable<void> _changed;
};
QCursor EditColorBox::Picker::generateCursor() {
auto diameter = convertScale(16);
auto line = convertScale(1);
auto size = ((diameter + 2 * line) >= 32) ? 64 : 32;
auto cursor = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cursor.setDevicePixelRatio(cRetinaFactor());
cursor.fill(Qt::transparent);
{
Painter p(&cursor);
PainterHighQualityEnabler hq(p);
p.setBrush(Qt::NoBrush);
auto pen = QPen(Qt::white);
pen.setWidth(3 * line);
p.setPen(pen);
p.drawEllipse((size - diameter) / 2, (size - diameter) / 2, diameter, diameter);
pen = QPen(Qt::black);
pen.setWidth(line);
p.setPen(pen);
p.drawEllipse((size - diameter) / 2, (size - diameter) / 2, diameter, diameter);
}
return QCursor(QPixmap::fromImage(cursor));
}
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
setCursor(generateCursor());
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
resize(size);
_palette = QImage(size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
setFromColor(color);
}
void EditColorBox::Picker::paintEvent(QPaintEvent *e) {
Painter p(this);
preparePalette();
p.drawImage(0, 0, _palette);
auto left = anim::color(_topleft, _bottomleft, _y);
auto right = anim::color(_topright, _bottomright, _y);
auto color = anim::color(left, right, _x);
auto lightness = 0.2989 * color.redF() + 0.5870 * color.greenF() + 0.1140 * color.blueF();
auto pen = QPen((lightness > 0.6) ? QColor(0, 0, 0) : QColor(255, 255, 255));
pen.setWidth(st::colorPickerMarkLine);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
auto x = anim::interpolate(0, width() - 1, _x);
auto y = anim::interpolate(0, height() - 1, _y);
PainterHighQualityEnabler hq(p);
p.drawEllipse(QRect(x - st::colorPickerMarkRadius, y - st::colorPickerMarkRadius, 2 * st::colorPickerMarkRadius, 2 * st::colorPickerMarkRadius));
}
void EditColorBox::Picker::mousePressEvent(QMouseEvent *e) {
_choosing = true;
updateCurrentPoint(e->pos());
}
void EditColorBox::Picker::mouseMoveEvent(QMouseEvent *e) {
if (_choosing) {
updateCurrentPoint(e->pos());
}
}
void EditColorBox::Picker::mouseReleaseEvent(QMouseEvent *e) {
_choosing = false;
}
void EditColorBox::Picker::preparePalette() {
if (!_paletteInvalidated) return;
_paletteInvalidated = false;
auto size = _palette.width();
auto ints = reinterpret_cast<uint32*>(_palette.bits());
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
auto part = Large / size;
auto topleft = anim::shifted(_topleft);
auto topright = anim::shifted(_topright);
auto bottomleft = anim::shifted(_bottomleft);
auto bottomright = anim::shifted(_bottomright);
auto y_accumulated = 0;
for (auto y = 0; y != size; ++y, y_accumulated += part) {
auto y_ratio = y_accumulated >> (LargeBit - 8); // (y_accumulated * 256) / Large;
// 0 <= y_accumulated < Large
// 0 <= y_ratio < 256
auto top_ratio = 255 - y_ratio;
auto bottom_ratio = y_ratio;
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 255 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
}
ints += intsAddPerLine;
}
}
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
auto x = snap(localPosition.x(), 0, width()) / float64(width());
auto y = snap(localPosition.y(), 0, height()) / float64(height());
if (_x != x || _y != y) {
_x = x;
_y = y;
update();
_changed.notify();
}
}
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
_paletteInvalidated = true;
update();
_x = snap(saturation / 255., 0., 1.);
_y = 1. - snap(brightness / 255., 0., 1.);
}
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
setFromColor(QColor(red, green, blue));
}
void EditColorBox::Picker::setFromColor(QColor color) {
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
}
class EditColorBox::Slider : public TWidget {
public:
enum class Direction {
Horizontal,
Vertical,
};
enum class Type {
Hue,
Opacity,
};
Slider(QWidget *parent, Direction direction, Type type, QColor color);
base::Observable<void> &changed() {
return _changed;
}
float64 value() const {
return _value;
}
void setValue(float64 value) {
_value = snap(value, 0., 1.);
update();
}
void setHSV(int hue, int saturation, int brightness);
void setRGB(int red, int green, int blue);
void setAlpha(int alpha);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
private:
float64 valueFromColor(QColor color) const;
float64 valueFromHue(int hue) const;
bool isHorizontal() const {
return (_direction == Direction::Horizontal);
}
void colorUpdated();
void prepareMinSize();
void generatePixmap();
void updatePixmapFromMask();
void updateCurrentPoint(QPoint localPosition);
Direction _direction = Direction::Horizontal;
Type _type = Type::Hue;
QColor _color;
float64 _value = 0;
QImage _mask;
QPixmap _pixmap;
QBrush _transparent;
bool _choosing = false;
base::Observable<void> _changed;
};
EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QColor color) : TWidget(parent)
, _direction(direction)
, _type(type)
, _color(color.red(), color.green(), color.blue())
, _value(valueFromColor(color))
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
prepareMinSize();
}
void EditColorBox::Slider::prepareMinSize() {
auto minSize = st::colorSliderSkip + st::colorSliderWidth + st::colorSliderSkip;
resize(minSize, minSize);
}
void EditColorBox::Slider::paintEvent(QPaintEvent *e) {
Painter p(this);
auto to = rect().marginsRemoved(QMargins(st::colorSliderSkip, st::colorSliderSkip, st::colorSliderSkip, st::colorSliderSkip));
Ui::Shadow::paint(p, to, width(), st::defaultRoundShadow);
if (_type == Type::Opacity) {
p.fillRect(to, _transparent);
}
p.drawPixmap(to, _pixmap, _pixmap.rect());
if (isHorizontal()) {
auto x = st::colorSliderSkip + qRound(_value * to.width());
st::colorSliderArrowTop.paint(p, x - st::colorSliderArrowTop.width() / 2, 0, width());
st::colorSliderArrowBottom.paint(p, x - st::colorSliderArrowBottom.width() / 2, height() - st::colorSliderArrowBottom.height(), width());
} else {
auto y = st::colorSliderSkip + qRound(_value * to.height());
st::colorSliderArrowLeft.paint(p, 0, y - st::colorSliderArrowLeft.height() / 2, width());
st::colorSliderArrowRight.paint(p, width() - st::colorSliderArrowRight.width(), y - st::colorSliderArrowRight.height() / 2, width());
}
}
void EditColorBox::Slider::resizeEvent(QResizeEvent *e) {
generatePixmap();
update();
}
void EditColorBox::Slider::mousePressEvent(QMouseEvent *e) {
_choosing = true;
updateCurrentPoint(e->pos());
}
void EditColorBox::Slider::mouseMoveEvent(QMouseEvent *e) {
if (_choosing) {
updateCurrentPoint(e->pos());
}
}
void EditColorBox::Slider::mouseReleaseEvent(QMouseEvent *e) {
_choosing = false;
}
void EditColorBox::Slider::generatePixmap() {
auto size = (isHorizontal() ? width() : height()) * cIntRetinaFactor();
auto image = QImage(size, cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(cRetinaFactor());
auto ints = reinterpret_cast<uint32*>(image.bits());
auto intsPerLine = image.bytesPerLine() / sizeof(uint32);
auto intsPerLineAdded = intsPerLine - size;
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
auto part = Large / size;
if (_type == Type::Hue) {
QColor color;
for (auto x = 0; x != size; ++x) {
color.setHsv(x * 360 / size, 255, 255);
auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
++ints;
}
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
} else {
auto color = anim::shifted(QColor(255, 255, 255, 255));
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
auto x_ratio = x_accumulated >> (LargeBit - 8);
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
*ints++ = anim::unshifted(color * x_ratio + transparent * (255 - x_ratio));
}
ints += intsPerLineAdded;
}
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_mask = std::move(image);
updatePixmapFromMask();
}
}
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
if (_type == Type::Hue) {
// hue == 360 converts to 0 if done in general way
_value = valueFromHue(hue);
update();
} else {
_color.setHsv(hue, saturation, brightness);
colorUpdated();
}
}
void EditColorBox::Slider::setRGB(int red, int green, int blue) {
_color.setRgb(red, green, blue);
colorUpdated();
}
void EditColorBox::Slider::colorUpdated() {
if (_type == Type::Hue) {
_value = valueFromColor(_color);
} else if (!_mask.isNull()) {
updatePixmapFromMask();
}
update();
}
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
}
float64 EditColorBox::Slider::valueFromHue(int hue) const {
return (1. - snap(hue, 0, 360) / 360.);
}
void EditColorBox::Slider::setAlpha(int alpha) {
if (_type == Type::Opacity) {
_value = snap(alpha, 0, 255) / 255.;
update();
}
}
void EditColorBox::Slider::updatePixmapFromMask() {
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
}
void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {
auto coord = (isHorizontal() ? localPosition.x() : localPosition.y()) - st::colorSliderSkip;
auto maximum = (isHorizontal() ? width() : height()) - 2 * st::colorSliderSkip;
auto value = snap(coord, 0, maximum) / float64(maximum);
if (_value != value) {
_value = value;
update();
_changed.notify();
}
}
class EditColorBox::Field : public Ui::MaskedInputField {
public:
Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units = QString());
int value() const {
return getLastText().toInt();
}
void setTextWithFocus(const QString &text) {
setText(text);
if (hasFocus()) {
selectAll();
}
}
protected:
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
void paintAdditionalPlaceholder(Painter &p, TimeMs ms) override;
void wheelEvent(QWheelEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
private:
void changeValue(int delta);
QString _placeholder, _units;
int _limit = 0;
int _digitLimit = 1;
int _wheelDelta = 0;
};
EditColorBox::Field::Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units) : Ui::MaskedInputField(parent, st)
, _placeholder(placeholder)
, _units(units)
, _limit(limit)
, _digitLimit(QString::number(_limit).size()) {
}
void EditColorBox::Field::correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) {
QString newText;
int oldPos(nowCursor), newPos(-1), oldLen(now.length());
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
if (i == oldPos) {
newPos = newText.length();
}
QChar ch(now[i]);
if (ch.isDigit()) {
newText += ch;
}
if (newText.size() >= _digitLimit) {
break;
}
}
if (newPos < 0 || newPos > newText.size()) {
newPos = newText.size();
}
if (newText.toInt() > _limit) {
newText = QString::number(_limit);
newPos = newText.size();
}
if (newText != now) {
now = newText;
setText(now);
startPlaceholderAnimation();
nowCursor = -1;
}
if (newPos != nowCursor) {
nowCursor = newPos;
setCursorPosition(nowCursor);
}
}
void EditColorBox::Field::paintAdditionalPlaceholder(Painter &p, TimeMs ms) {
p.setFont(_st.font);
p.setPen(_st.placeholderFg);
auto inner = QRect(_st.textMargins.right(), _st.textMargins.top(), width() - 2 * _st.textMargins.right(), height() - _st.textMargins.top() - _st.textMargins.bottom());
p.drawText(inner, _placeholder, style::al_topleft);
if (!_units.isEmpty()) {
p.drawText(inner, _units, style::al_topright);
}
}
void EditColorBox::Field::wheelEvent(QWheelEvent *e) {
if (!hasFocus()) {
return;
}
auto deltaX = e->angleDelta().x(), deltaY = e->angleDelta().y();
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
deltaY *= -1;
} else {
deltaX *= -1;
}
_wheelDelta += (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY;
constexpr auto step = 5;
if (auto delta = _wheelDelta / step) {
_wheelDelta -= delta * step;
changeValue(delta);
}
}
void EditColorBox::Field::changeValue(int delta) {
auto currentValue = value();
auto newValue = snap(currentValue + delta, 0, _limit);
if (newValue != currentValue) {
setText(QString::number(newValue));
setFocus();
selectAll();
emit changed();
}
}
void EditColorBox::Field::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Up) {
changeValue(1);
} else if (e->key() == Qt::Key_Down) {
changeValue(-1);
} else {
MaskedInputField::keyPressEvent(e);
}
}
class EditColorBox::ResultField : public Ui::MaskedInputField {
public:
ResultField(QWidget *parent, const style::InputField &st);
void setTextWithFocus(const QString &text) {
setText(text);
if (hasFocus()) {
selectAll();
}
}
protected:
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
void paintAdditionalPlaceholder(Painter &p, TimeMs ms) override;
};
EditColorBox::ResultField::ResultField(QWidget *parent, const style::InputField &st) : Ui::MaskedInputField(parent, st) {
}
void EditColorBox::ResultField::correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) {
QString newText;
int oldPos(nowCursor), newPos(-1), oldLen(now.length());
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
if (i == oldPos) {
newPos = newText.length();
}
QChar ch(now[i]);
auto code = ch.unicode();
if ((code >= '0' && code <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
newText += ch;
}
if (newText.size() >= 8) {
break;
}
}
if (newPos < 0 || newPos > newText.size()) {
newPos = newText.size();
}
if (newText != now) {
now = newText;
setText(now);
startPlaceholderAnimation();
nowCursor = -1;
}
if (newPos != nowCursor) {
nowCursor = newPos;
setCursorPosition(nowCursor);
}
}
void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p, TimeMs ms) {
p.setFont(_st.font);
p.setPen(_st.placeholderFg);
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
}
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
, _title(title)
, _picker(this, current)
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
, _saturationField(this, st::colorValueInput, "S", 100, "%")
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
, _redField(this, st::colorValueInput, "R", 255)
, _greenField(this, st::colorValueInput, "G", 255)
, _blueField(this, st::colorValueInput, "B", 255)
, _result(this, st::colorResultInput)
, _transparent(style::transparentPlaceholderBrush())
, _current(current)
, _new(current) {
}
void EditColorBox::prepare() {
setTitle(_title);
connect(_hueField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_saturationField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_brightnessField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_redField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_greenField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_blueField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_result, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_hueField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_saturationField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_brightnessField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_redField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_greenField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_blueField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_result, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
addButton(lang(lng_settings_save), [this] { saveColor(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
auto height = st::colorEditSkip + st::colorPickerSize + st::colorEditSkip + st::colorSliderWidth + st::colorEditSkip;
setDimensions(st::colorEditWidth, height);
subscribe(_picker->changed(), [this] { updateFromControls(); });
subscribe(_hueSlider->changed(), [this] { updateFromControls(); });
subscribe(_opacitySlider->changed(), [this] { updateFromControls(); });
updateFromControls();
}
void EditColorBox::setInnerFocus() {
_result->setFocus();
_result->selectAll();
}
void EditColorBox::onFieldChanged() {
auto emitter = sender();
auto checkHSVSender = [this, emitter](QObject *field) {
if (emitter == field) {
updateFromHSVFields();
}
};
auto checkRGBSender = [this, emitter](QObject *field) {
if (emitter == field) {
updateFromRGBFields();
}
};
checkHSVSender(_hueField);
checkHSVSender(_saturationField);
checkHSVSender(_brightnessField);
checkRGBSender(_redField);
checkRGBSender(_greenField);
checkRGBSender(_blueField);
if (emitter == _result) {
updateFromResultField();
}
}
void EditColorBox::onFieldSubmitted() {
Ui::MaskedInputField *fields[] = {
_hueField,
_saturationField,
_brightnessField,
_redField,
_greenField,
_blueField,
_result
};
for (auto i = 0, count = int(base::array_size(fields)); i + 1 != count; ++i) {
if (fields[i]->hasFocus()) {
fields[i + 1]->setFocus();
fields[i + 1]->selectAll();
return;
}
}
if (_result->hasFocus()) {
saveColor();
}
}
void EditColorBox::saveColor() {
_cancelCallback = base::lambda<void()>();
if (_saveCallback) {
_saveCallback(_new.toRgb());
}
closeBox();
}
void EditColorBox::updateHSVFields() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
_hueField->setTextWithFocus(QString::number(hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
}
void EditColorBox::updateRGBFields() {
_redField->setTextWithFocus(QString::number(_new.red()));
_greenField->setTextWithFocus(QString::number(_new.green()));
_blueField->setTextWithFocus(QString::number(_new.blue()));
}
void EditColorBox::updateResultField() {
auto text = QString();
auto addHex = [&text](int value) {
if (value >= 0 && value <= 9) {
text.append('0' + value);
} else if (value >= 10 && value <= 15) {
text.append('a' + (value - 10));
}
};
auto addValue = [addHex](int value) {
addHex(value / 16);
addHex(value % 16);
};
addValue(_new.red());
addValue(_new.green());
addValue(_new.blue());
if (_new.alpha() != 255) {
addValue(_new.alpha());
}
_result->setTextWithFocus(text);
}
void EditColorBox::resizeEvent(QResizeEvent *e) {
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
auto left = (width() - fullwidth) / 2;
_picker->moveToLeft(left, st::colorEditSkip);
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
auto fieldWidth = st::colorSampleSize.width();
auto fieldHeight = _hueField->height();
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
_saturationField->setGeometryToLeft(fieldLeft, _hueField->y() + _hueField->height(), fieldWidth, fieldHeight);
_brightnessField->setGeometryToLeft(fieldLeft, _saturationField->y() + _saturationField->height(), fieldWidth, fieldHeight);
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
}
void EditColorBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
Ui::Shadow::paint(p, _picker->geometry(), width(), st::defaultRoundShadow);
Ui::Shadow::paint(p, QRect(_newRect.x(), _newRect.y(), _newRect.width(), _newRect.height() + _currentRect.height()), width(), st::defaultRoundShadow);
if (_new.alphaF() < 1.) {
p.fillRect(myrtlrect(_newRect), _transparent);
}
p.fillRect(myrtlrect(_newRect), _new);
if (_current.alphaF() < 1.) {
p.fillRect(myrtlrect(_currentRect), _transparent);
}
p.fillRect(myrtlrect(_currentRect), _current);
}
void EditColorBox::mousePressEvent(QMouseEvent *e) {
if (myrtlrect(_currentRect).contains(e->pos())) {
updateFromColor(_current);
}
}
void EditColorBox::updateFromColor(QColor color) {
_new = color;
updateControlsFromColor();
updateRGBFields();
updateHSVFields();
updateResultField();
update();
}
void EditColorBox::updateFromControls() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateHSVFields();
updateControlsFromHSV(hue, saturation, brightness);
}
void EditColorBox::updateFromHSVFields() {
auto hue = _hueField->value();
auto saturation = percentToByte(_saturationField->value());
auto brightness = percentToByte(_brightnessField->value());
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateControlsFromHSV(hue, saturation, brightness);
}
void EditColorBox::updateFromRGBFields() {
auto red = _redField->value();
auto blue = _blueField->value();
auto green = _greenField->value();
auto alpha = qRound(_opacitySlider->value() * 255);
setRGB(red, green, blue, alpha);
updateResultField();
}
void EditColorBox::updateFromResultField() {
auto text = _result->getLastText();
if (text.size() != 6 && text.size() != 8) {
return;
}
auto fromHex = [](QChar hex) {
auto code = hex.unicode();
if (code >= 'A' && code <= 'F') {
return (code - 'A' + 10);
} else if (code >= 'a' && code <= 'f') {
return (code - 'a' + 10);
}
return code - '0';
};
auto fromChars = [fromHex](QChar a, QChar b) {
return fromHex(a) * 0x10 + fromHex(b);
};
auto red = fromChars(text[0], text[1]);
auto green = fromChars(text[2], text[3]);
auto blue = fromChars(text[4], text[5]);
auto alpha = (text.size() == 8) ? fromChars(text[6], text[7]) : 255;
setRGB(red, green, blue, alpha);
updateRGBFields();
}
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
_picker->setHSV(hue, saturation, brightness);
_hueSlider->setHSV(hue, saturation, brightness);
_opacitySlider->setHSV(hue, saturation, brightness);
}
void EditColorBox::updateControlsFromColor() {
auto red = _new.red();
auto green = _new.green();
auto blue = _new.blue();
auto alpha = _new.alpha();
_picker->setRGB(red, green, blue);
_hueSlider->setRGB(red, green, blue);
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
}
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
_new.setHsv(hue, saturation, value, alpha);
updateRGBFields();
updateResultField();
update();
}
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
_new.setRgb(red, green, blue, alpha);
updateControlsFromColor();
updateHSVFields();
update();
}