Use outbox bubble colors, adjust custom colors.
This commit is contained in:
parent
e4e5c4a1d2
commit
c318f57fc0
|
@ -914,6 +914,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
.theme = _theme.get(),
|
.theme = _theme.get(),
|
||||||
.visibleAreaTop = _visibleTop,
|
.visibleAreaTop = _visibleTop,
|
||||||
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
|
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
|
||||||
|
.visibleAreaWidth = width(),
|
||||||
.clip = clip,
|
.clip = clip,
|
||||||
});
|
});
|
||||||
if (_items.empty() && _upLoaded && _downLoaded) {
|
if (_items.empty() && _upLoaded && _downLoaded) {
|
||||||
|
|
|
@ -579,6 +579,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
.theme = _theme.get(),
|
.theme = _theme.get(),
|
||||||
.visibleAreaTop = _visibleAreaTop,
|
.visibleAreaTop = _visibleAreaTop,
|
||||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||||
|
.visibleAreaWidth = width(),
|
||||||
.clip = clip,
|
.clip = clip,
|
||||||
});
|
});
|
||||||
_pathGradient->startFrame(
|
_pathGradient->startFrame(
|
||||||
|
|
|
@ -1625,6 +1625,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
.theme = _delegate->listChatTheme(),
|
.theme = _delegate->listChatTheme(),
|
||||||
.visibleAreaTop = _visibleTop,
|
.visibleAreaTop = _visibleTop,
|
||||||
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
|
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
|
||||||
|
.visibleAreaWidth = width(),
|
||||||
.clip = clip,
|
.clip = clip,
|
||||||
}).translated(0, -top);
|
}).translated(0, -top);
|
||||||
p.translate(0, top);
|
p.translate(0, top);
|
||||||
|
|
|
@ -232,7 +232,7 @@ void Game::draw(Painter &p, const PaintContext &context) const {
|
||||||
tshift += _titleLines * lineHeight;
|
tshift += _titleLines * lineHeight;
|
||||||
}
|
}
|
||||||
if (_descriptionLines) {
|
if (_descriptionLines) {
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
p.setPen(stm->historyTextFg);
|
||||||
auto endskip = 0;
|
auto endskip = 0;
|
||||||
if (_description.hasSkipBlock()) {
|
if (_description.hasSkipBlock()) {
|
||||||
endskip = _parent->skipBlockWidth();
|
endskip = _parent->skipBlockWidth();
|
||||||
|
|
|
@ -233,7 +233,7 @@ void Invoice::draw(Painter &p, const PaintContext &context) const {
|
||||||
p.setTextPalette(stm->textPalette);
|
p.setTextPalette(stm->textPalette);
|
||||||
}
|
}
|
||||||
if (_descriptionHeight) {
|
if (_descriptionHeight) {
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
p.setPen(stm->historyTextFg);
|
||||||
_description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(context.selection));
|
_description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(context.selection));
|
||||||
tshift += _descriptionHeight;
|
tshift += _descriptionHeight;
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ void Invoice::draw(Painter &p, const PaintContext &context) const {
|
||||||
|
|
||||||
p.translate(-attachLeft, -attachTop);
|
p.translate(-attachLeft, -attachTop);
|
||||||
} else {
|
} else {
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
p.setPen(stm->historyTextFg);
|
||||||
_status.drawLeft(p, padding.left(), tshift + st::mediaInBubbleSkip, paintw, width());
|
_status.drawLeft(p, padding.left(), tshift + st::mediaInBubbleSkip, paintw, width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,13 +165,12 @@ void Location::draw(Painter &p, const PaintContext &context) const {
|
||||||
|
|
||||||
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
|
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
|
||||||
|
|
||||||
|
p.setPen(stm->historyTextFg);
|
||||||
if (!_title.isEmpty()) {
|
if (!_title.isEmpty()) {
|
||||||
p.setPen(stm->webPageTitleFg);
|
|
||||||
_title.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 2, style::al_left, 0, -1, 0, false, context.selection);
|
_title.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 2, style::al_left, 0, -1, 0, false, context.selection);
|
||||||
painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
|
painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
|
||||||
}
|
}
|
||||||
if (!_description.isEmpty()) {
|
if (!_description.isEmpty()) {
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
|
||||||
_description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(context.selection));
|
_description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(context.selection));
|
||||||
painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
|
painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -731,7 +731,7 @@ void Poll::draw(Painter &p, const PaintContext &context) const {
|
||||||
}
|
}
|
||||||
paintw -= padding.left() + padding.right();
|
paintw -= padding.left() + padding.right();
|
||||||
|
|
||||||
p.setPen(stm->webPageTitleFg);
|
p.setPen(stm->historyTextFg);
|
||||||
_question.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, context.selection);
|
_question.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, context.selection);
|
||||||
tshift += _question.countHeight(paintw) + st::historyPollSubtitleSkip;
|
tshift += _question.countHeight(paintw) + st::historyPollSubtitleSkip;
|
||||||
|
|
||||||
|
@ -1099,7 +1099,7 @@ int Poll::paintAnswer(
|
||||||
}
|
}
|
||||||
|
|
||||||
top += st::historyPollAnswerPadding.top();
|
top += st::historyPollAnswerPadding.top();
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
p.setPen(stm->historyTextFg);
|
||||||
answer.text.drawLeft(p, aleft, top, awidth, outerWidth);
|
answer.text.drawLeft(p, aleft, top, awidth, outerWidth);
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
@ -1182,7 +1182,7 @@ void Poll::paintPercent(
|
||||||
top += st::historyPollAnswerPadding.top();
|
top += st::historyPollAnswerPadding.top();
|
||||||
|
|
||||||
p.setFont(st::historyPollPercentFont);
|
p.setFont(st::historyPollPercentFont);
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
p.setPen(stm->historyTextFg);
|
||||||
const auto pleft = aleft - percentWidth - st::historyPollPercentSkip;
|
const auto pleft = aleft - percentWidth - st::historyPollPercentSkip;
|
||||||
p.drawTextLeft(pleft, top + st::historyPollPercentTop, outerWidth, percent, percentWidth);
|
p.drawTextLeft(pleft, top + st::historyPollPercentTop, outerWidth, percent, percentWidth);
|
||||||
}
|
}
|
||||||
|
|
|
@ -521,8 +521,8 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
||||||
_siteName.drawLeftElided(p, padding.left(), tshift, paintw, width(), _siteNameLines, style::al_left, 0, -1, endskip, false, context.selection);
|
_siteName.drawLeftElided(p, padding.left(), tshift, paintw, width(), _siteNameLines, style::al_left, 0, -1, endskip, false, context.selection);
|
||||||
tshift += lineHeight;
|
tshift += lineHeight;
|
||||||
}
|
}
|
||||||
|
p.setPen(stm->historyTextFg);
|
||||||
if (_titleLines) {
|
if (_titleLines) {
|
||||||
p.setPen(stm->webPageTitleFg);
|
|
||||||
auto endskip = 0;
|
auto endskip = 0;
|
||||||
if (_title.hasSkipBlock()) {
|
if (_title.hasSkipBlock()) {
|
||||||
endskip = _parent->skipBlockWidth();
|
endskip = _parent->skipBlockWidth();
|
||||||
|
@ -531,7 +531,6 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
||||||
tshift += _titleLines * lineHeight;
|
tshift += _titleLines * lineHeight;
|
||||||
}
|
}
|
||||||
if (_descriptionLines) {
|
if (_descriptionLines) {
|
||||||
p.setPen(stm->webPageDescriptionFg);
|
|
||||||
auto endskip = 0;
|
auto endskip = 0;
|
||||||
if (_description.hasSkipBlock()) {
|
if (_description.hasSkipBlock()) {
|
||||||
endskip = _parent->skipBlockWidth();
|
endskip = _parent->skipBlockWidth();
|
||||||
|
|
|
@ -690,10 +690,6 @@ webPageLeft: 10px;
|
||||||
webPageBar: 2px;
|
webPageBar: 2px;
|
||||||
webPageTitleFont: semiboldFont;
|
webPageTitleFont: semiboldFont;
|
||||||
webPageTitleStyle: semiboldTextStyle;
|
webPageTitleStyle: semiboldTextStyle;
|
||||||
webPageTitleOutFg: historyTextOutFg;
|
|
||||||
webPageTitleInFg: historyTextInFg;
|
|
||||||
webPageDescriptionOutFg: historyTextOutFg;
|
|
||||||
webPageDescriptionInFg: historyTextInFg;
|
|
||||||
webPageDescriptionFont: normalFont;
|
webPageDescriptionFont: normalFont;
|
||||||
webPageDescriptionStyle: defaultTextStyle;
|
webPageDescriptionStyle: defaultTextStyle;
|
||||||
webPagePhotoSize: 100px;
|
webPagePhotoSize: 100px;
|
||||||
|
|
|
@ -211,18 +211,6 @@ ChatStyle::ChatStyle() {
|
||||||
st::mediaInFgSelected,
|
st::mediaInFgSelected,
|
||||||
st::mediaOutFg,
|
st::mediaOutFg,
|
||||||
st::mediaOutFgSelected);
|
st::mediaOutFgSelected);
|
||||||
make(
|
|
||||||
&MessageStyle::webPageTitleFg,
|
|
||||||
st::webPageTitleInFg,
|
|
||||||
st::webPageTitleInFg,
|
|
||||||
st::webPageTitleOutFg,
|
|
||||||
st::webPageTitleOutFg);
|
|
||||||
make(
|
|
||||||
&MessageStyle::webPageDescriptionFg,
|
|
||||||
st::webPageDescriptionInFg,
|
|
||||||
st::webPageDescriptionInFg,
|
|
||||||
st::webPageDescriptionOutFg,
|
|
||||||
st::webPageDescriptionOutFg);
|
|
||||||
make(
|
make(
|
||||||
&MessageStyle::textPalette,
|
&MessageStyle::textPalette,
|
||||||
st::inTextPalette,
|
st::inTextPalette,
|
||||||
|
|
|
@ -38,8 +38,6 @@ struct MessageStyle {
|
||||||
style::color historyFileNameFg;
|
style::color historyFileNameFg;
|
||||||
style::color historyFileRadialFg;
|
style::color historyFileRadialFg;
|
||||||
style::color mediaFg;
|
style::color mediaFg;
|
||||||
style::color webPageTitleFg;
|
|
||||||
style::color webPageDescriptionFg;
|
|
||||||
style::TextPalette textPalette;
|
style::TextPalette textPalette;
|
||||||
style::TextPalette semiboldPalette;
|
style::TextPalette semiboldPalette;
|
||||||
style::TextPalette fwdTextPalette;
|
style::TextPalette fwdTextPalette;
|
||||||
|
|
|
@ -124,6 +124,15 @@ constexpr auto kMaxSize = 2960;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QImage PrepareBubblesBackground(
|
||||||
|
const ChatThemeBubblesData &data) {
|
||||||
|
if (data.colors.size() < 2) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
constexpr auto kSize = 512;
|
||||||
|
return Images::GenerateLinearGradient(QSize(kSize, kSize), data.colors);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b) {
|
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b) {
|
||||||
|
@ -167,11 +176,72 @@ ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor)
|
||||||
: _id(descriptor.id)
|
: _id(descriptor.id)
|
||||||
, _palette(std::make_unique<style::palette>()) {
|
, _palette(std::make_unique<style::palette>()) {
|
||||||
descriptor.preparePalette(*_palette);
|
descriptor.preparePalette(*_palette);
|
||||||
setBackground(descriptor.prepareBackground());
|
setBackground(PrepareBackgroundImage(descriptor.backgroundData));
|
||||||
|
setBubblesBackground(PrepareBubblesBackground(descriptor.bubblesData));
|
||||||
|
adjustPalette(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatTheme::~ChatTheme() = default;
|
ChatTheme::~ChatTheme() = default;
|
||||||
|
|
||||||
|
void ChatTheme::adjustPalette(const ChatThemeDescriptor &descriptor) {
|
||||||
|
auto &p = *_palette;
|
||||||
|
const auto overrideOutBg = (descriptor.bubblesData.colors.size() == 1);
|
||||||
|
if (overrideOutBg) {
|
||||||
|
set(p.msgOutBg(), descriptor.bubblesData.colors.front());
|
||||||
|
}
|
||||||
|
const auto &background = descriptor.backgroundData.colors;
|
||||||
|
if (!background.empty()) {
|
||||||
|
const auto average = CountAverageColor(background);
|
||||||
|
adjust(p.msgServiceBg(), average);
|
||||||
|
adjust(p.msgServiceBgSelected(), average);
|
||||||
|
adjust(p.historyScrollBg(), average);
|
||||||
|
adjust(p.historyScrollBgOver(), average);
|
||||||
|
adjust(p.historyScrollBarBg(), average);
|
||||||
|
adjust(p.historyScrollBarBgOver(), average);
|
||||||
|
}
|
||||||
|
const auto bubblesAccent = descriptor.bubblesData.accent
|
||||||
|
? descriptor.bubblesData.accent
|
||||||
|
: !descriptor.bubblesData.colors.empty()
|
||||||
|
? ThemeAdjustedColor(
|
||||||
|
p.msgOutReplyBarColor()->c,
|
||||||
|
CountAverageColor(descriptor.bubblesData.colors))
|
||||||
|
: std::optional<QColor>();
|
||||||
|
if (bubblesAccent) {
|
||||||
|
const auto by = *bubblesAccent;
|
||||||
|
if (!overrideOutBg) {
|
||||||
|
adjust(p.msgOutBg(), by);
|
||||||
|
}
|
||||||
|
adjust(p.msgOutShadow(), by);
|
||||||
|
adjust(p.msgOutServiceFg(), by);
|
||||||
|
adjust(p.msgOutDateFg(), by);
|
||||||
|
adjust(p.msgFileThumbLinkOutFg(), by);
|
||||||
|
adjust(p.msgFileOutBg(), by);
|
||||||
|
adjust(p.msgOutReplyBarColor(), by);
|
||||||
|
adjust(p.msgWaveformOutActive(), by);
|
||||||
|
adjust(p.msgWaveformOutInactive(), by);
|
||||||
|
adjust(p.historyTextOutFg(), by);
|
||||||
|
adjust(p.historyFileNameOutFg(), by);
|
||||||
|
adjust(p.historyFileOutRadialFg(), by);
|
||||||
|
adjust(p.mediaOutFg(), by);
|
||||||
|
|
||||||
|
adjust(p.historyLinkOutFg(), by);
|
||||||
|
adjust(p.msgOutMonoFg(), by);
|
||||||
|
adjust(p.historyOutIconFg(), by);
|
||||||
|
adjust(p.historyCallArrowOutFg(), by);
|
||||||
|
adjust(p.historyFileOutIconFg(), by);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTheme::set(const style::color &my, const QColor &color) {
|
||||||
|
auto r = 0, g = 0, b = 0, a = 0;
|
||||||
|
color.getRgb(&r, &g, &b, &a);
|
||||||
|
my.set(uchar(r), uchar(g), uchar(b), uchar(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTheme::adjust(const style::color &my, const QColor &by) {
|
||||||
|
set(my, ThemeAdjustedColor(my->c, by));
|
||||||
|
}
|
||||||
|
|
||||||
void ChatTheme::setBackground(ChatThemeBackground &&background) {
|
void ChatTheme::setBackground(ChatThemeBackground &&background) {
|
||||||
_mutableBackground = std::move(background);
|
_mutableBackground = std::move(background);
|
||||||
_backgroundState = {};
|
_backgroundState = {};
|
||||||
|
@ -200,15 +270,23 @@ uint64 ChatTheme::key() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatTheme::setBubblesBackground(QImage image) {
|
void ChatTheme::setBubblesBackground(QImage image) {
|
||||||
_bubblesBackgroundPrepared = std::move(image);
|
if (image.isNull() && _bubblesBackgroundPrepared.isNull()) {
|
||||||
if (!_bubblesBackground.area.isEmpty()) {
|
return;
|
||||||
_bubblesBackground = CacheBackground({
|
|
||||||
.background = {
|
|
||||||
.prepared = _bubblesBackgroundPrepared,
|
|
||||||
},
|
|
||||||
.area = _bubblesBackground.area,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
_bubblesBackgroundPrepared = std::move(image);
|
||||||
|
if (_bubblesBackgroundPrepared.isNull()) {
|
||||||
|
_bubblesBackgroundPattern = nullptr;
|
||||||
|
_repaintBackgroundRequests.fire({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_bubblesBackground = CacheBackground({
|
||||||
|
.background = {
|
||||||
|
.prepared = _bubblesBackgroundPrepared,
|
||||||
|
},
|
||||||
|
.area = (_bubblesBackground.area.isEmpty()
|
||||||
|
? _bubblesBackgroundPrepared.size()
|
||||||
|
: _bubblesBackground.area),
|
||||||
|
});
|
||||||
if (!_bubblesBackgroundPattern) {
|
if (!_bubblesBackgroundPattern) {
|
||||||
_bubblesBackgroundPattern = PrepareBubblePattern(palette());
|
_bubblesBackgroundPattern = PrepareBubblePattern(palette());
|
||||||
}
|
}
|
||||||
|
@ -471,8 +549,29 @@ QColor CountAverageColor(const QImage &image) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (size) {
|
if (size) {
|
||||||
for (auto i = 0; i != 3; ++i) {
|
for (auto &component : components) {
|
||||||
components[i] /= size;
|
component /= size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QColor(components[0], components[1], components[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor CountAverageColor(const std::vector<QColor> &colors) {
|
||||||
|
Expects(colors.size() < (std::numeric_limits<int>::max() / 256));
|
||||||
|
|
||||||
|
int components[3] = { 0 };
|
||||||
|
auto r = 0;
|
||||||
|
auto g = 0;
|
||||||
|
auto b = 0;
|
||||||
|
for (const auto &color : colors) {
|
||||||
|
color.getRgb(&r, &g, &b);
|
||||||
|
components[0] += r;
|
||||||
|
components[1] += g;
|
||||||
|
components[2] += b;
|
||||||
|
}
|
||||||
|
if (const auto size = colors.size()) {
|
||||||
|
for (auto &component : components) {
|
||||||
|
component /= size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QColor(components[0], components[1], components[2]);
|
return QColor(components[0], components[1], components[2]);
|
||||||
|
@ -652,46 +751,46 @@ QImage GenerateDitheredGradient(
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatThemeBackground PrepareBackgroundImage(
|
ChatThemeBackground PrepareBackgroundImage(
|
||||||
const QString &path,
|
const ChatThemeBackgroundData &data) {
|
||||||
const QByteArray &bytes,
|
auto prepared = (data.isPattern || data.colors.empty())
|
||||||
bool gzipSvg,
|
? PreprocessBackgroundImage(
|
||||||
const std::vector<QColor> &colors,
|
ReadBackgroundImage(data.path, data.bytes, data.gzipSvg))
|
||||||
bool isPattern,
|
|
||||||
float64 patternOpacity,
|
|
||||||
bool isBlurred) {
|
|
||||||
auto prepared = (isPattern || colors.empty())
|
|
||||||
? PreprocessBackgroundImage(ReadBackgroundImage(path, bytes, gzipSvg))
|
|
||||||
: QImage();
|
: QImage();
|
||||||
if (isPattern && !prepared.isNull()) {
|
if (data.isPattern && !prepared.isNull()) {
|
||||||
if (colors.size() < 2) {
|
if (data.colors.size() < 2) {
|
||||||
const auto gradientRotation = 0; // No gradient here.
|
const auto gradientRotation = 0; // No gradient here.
|
||||||
prepared = PreparePatternImage(
|
prepared = PreparePatternImage(
|
||||||
std::move(prepared),
|
std::move(prepared),
|
||||||
colors,
|
data.colors,
|
||||||
gradientRotation,
|
data.gradientRotation,
|
||||||
patternOpacity);
|
data.patternOpacity);
|
||||||
}
|
}
|
||||||
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
} else if (colors.empty()) {
|
} else if (data.colors.empty()) {
|
||||||
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
}
|
}
|
||||||
const auto imageMonoColor = (colors.size() < 2)
|
const auto imageMonoColor = (data.colors.size() < 2)
|
||||||
? CalculateImageMonoColor(prepared)
|
? CalculateImageMonoColor(prepared)
|
||||||
: std::nullopt;
|
: std::nullopt;
|
||||||
if (!prepared.isNull() && !isPattern && isBlurred) {
|
if (!prepared.isNull() && !data.isPattern && data.isBlurred) {
|
||||||
prepared = PrepareBlurredBackground(std::move(prepared));
|
prepared = PrepareBlurredBackground(std::move(prepared));
|
||||||
}
|
}
|
||||||
|
auto gradientForFill = (data.generateGradient && data.colors.size() > 1)
|
||||||
|
? Ui::GenerateDitheredGradient(data.colors, data.gradientRotation)
|
||||||
|
: QImage();
|
||||||
return ChatThemeBackground{
|
return ChatThemeBackground{
|
||||||
.prepared = prepared,
|
.prepared = prepared,
|
||||||
.preparedForTiled = PrepareImageForTiled(prepared),
|
.preparedForTiled = PrepareImageForTiled(prepared),
|
||||||
|
.gradientForFill = std::move(gradientForFill),
|
||||||
.colorForFill = (!prepared.isNull()
|
.colorForFill = (!prepared.isNull()
|
||||||
? imageMonoColor
|
? imageMonoColor
|
||||||
: (colors.size() > 1 || colors.empty())
|
: (data.colors.size() > 1 || data.colors.empty())
|
||||||
? std::nullopt
|
? std::nullopt
|
||||||
: std::make_optional(colors.front())),
|
: std::make_optional(data.colors.front())),
|
||||||
.colors = colors,
|
.colors = data.colors,
|
||||||
.patternOpacity = patternOpacity,
|
.patternOpacity = data.patternOpacity,
|
||||||
.isPattern = isPattern,
|
.gradientRotation = data.generateGradient ? data.gradientRotation : 0,
|
||||||
|
.isPattern = data.isPattern,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,23 @@ struct ChatThemeBackground {
|
||||||
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
||||||
bool operator!=(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
bool operator!=(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
||||||
|
|
||||||
|
struct ChatThemeBackgroundData {
|
||||||
|
QString path;
|
||||||
|
QByteArray bytes;
|
||||||
|
bool gzipSvg = false;
|
||||||
|
std::vector<QColor> colors;
|
||||||
|
bool isPattern = false;
|
||||||
|
float64 patternOpacity = 0.;
|
||||||
|
bool isBlurred = false;
|
||||||
|
bool generateGradient = false;
|
||||||
|
int gradientRotation = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChatThemeBubblesData {
|
||||||
|
std::vector<QColor> colors;
|
||||||
|
std::optional<QColor> accent;
|
||||||
|
};
|
||||||
|
|
||||||
struct CacheBackgroundRequest {
|
struct CacheBackgroundRequest {
|
||||||
ChatThemeBackground background;
|
ChatThemeBackground background;
|
||||||
QSize area;
|
QSize area;
|
||||||
|
@ -82,7 +99,8 @@ struct BackgroundState {
|
||||||
struct ChatThemeDescriptor {
|
struct ChatThemeDescriptor {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
Fn<void(style::palette&)> preparePalette;
|
Fn<void(style::palette&)> preparePalette;
|
||||||
Fn<ChatThemeBackground()> prepareBackground;
|
ChatThemeBackgroundData backgroundData;
|
||||||
|
ChatThemeBubblesData bubblesData;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChatTheme final : public base::has_weak_ptr {
|
class ChatTheme final : public base::has_weak_ptr {
|
||||||
|
@ -131,6 +149,10 @@ private:
|
||||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||||
void generateNextBackgroundRotation();
|
void generateNextBackgroundRotation();
|
||||||
|
|
||||||
|
void adjustPalette(const ChatThemeDescriptor &descriptor);
|
||||||
|
void set(const style::color &my, const QColor &color);
|
||||||
|
void adjust(const style::color &my, const QColor &by);
|
||||||
|
|
||||||
uint64 _id = 0;
|
uint64 _id = 0;
|
||||||
std::unique_ptr<style::palette> _palette;
|
std::unique_ptr<style::palette> _palette;
|
||||||
ChatThemeBackground _mutableBackground;
|
ChatThemeBackground _mutableBackground;
|
||||||
|
@ -160,6 +182,7 @@ struct ChatBackgroundRects {
|
||||||
QSize imageSize);
|
QSize imageSize);
|
||||||
|
|
||||||
[[nodiscard]] QColor CountAverageColor(const QImage &image);
|
[[nodiscard]] QColor CountAverageColor(const QImage &image);
|
||||||
|
[[nodiscard]] QColor CountAverageColor(const std::vector<QColor> &colors);
|
||||||
[[nodiscard]] QColor ThemeAdjustedColor(QColor original, QColor background);
|
[[nodiscard]] QColor ThemeAdjustedColor(QColor original, QColor background);
|
||||||
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
|
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
|
||||||
[[nodiscard]] std::optional<QColor> CalculateImageMonoColor(
|
[[nodiscard]] std::optional<QColor> CalculateImageMonoColor(
|
||||||
|
@ -185,14 +208,7 @@ struct ChatBackgroundRects {
|
||||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||||
const std::vector<QColor> &colors,
|
const std::vector<QColor> &colors,
|
||||||
int rotation);
|
int rotation);
|
||||||
|
|
||||||
[[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
|
[[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
|
||||||
const QString &path,
|
const ChatThemeBackgroundData &data);
|
||||||
const QByteArray &bytes,
|
|
||||||
bool gzipSvg,
|
|
||||||
const std::vector<QColor> &colors,
|
|
||||||
bool isPattern,
|
|
||||||
float64 patternOpacity,
|
|
||||||
bool isBlurred);
|
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -752,12 +752,11 @@ void ChatBackground::setPrepared(
|
||||||
prepared = Ui::PrepareBlurredBackground(std::move(prepared));
|
prepared = Ui::PrepareBlurredBackground(std::move(prepared));
|
||||||
}
|
}
|
||||||
if (adjustPaletteRequired()) {
|
if (adjustPaletteRequired()) {
|
||||||
if (!gradient.isNull()) {
|
if ((prepared.isNull() || _paper.isPattern())
|
||||||
adjustPaletteUsingBackground(gradient);
|
&& !_paper.backgroundColors().empty()) {
|
||||||
|
adjustPaletteUsingColors(_paper.backgroundColors());
|
||||||
} else if (!prepared.isNull()) {
|
} else if (!prepared.isNull()) {
|
||||||
adjustPaletteUsingBackground(prepared);
|
adjustPaletteUsingBackground(prepared);
|
||||||
} else if (!_paper.backgroundColors().empty()) {
|
|
||||||
adjustPaletteUsingColor(_paper.backgroundColors().front());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,6 +818,11 @@ void ChatBackground::adjustPaletteUsingBackground(const QImage &image) {
|
||||||
adjustPaletteUsingColor(Ui::CountAverageColor(image));
|
adjustPaletteUsingColor(Ui::CountAverageColor(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatBackground::adjustPaletteUsingColors(
|
||||||
|
const std::vector<QColor> &colors) {
|
||||||
|
adjustPaletteUsingColor(Ui::CountAverageColor(colors));
|
||||||
|
}
|
||||||
|
|
||||||
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
||||||
const auto prepared = color.toHsl();
|
const auto prepared = color.toHsl();
|
||||||
for (const auto &adjustable : _adjustableColors) {
|
for (const auto &adjustable : _adjustableColors) {
|
||||||
|
|
|
@ -219,6 +219,7 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] bool adjustPaletteRequired();
|
[[nodiscard]] bool adjustPaletteRequired();
|
||||||
void adjustPaletteUsingBackground(const QImage &image);
|
void adjustPaletteUsingBackground(const QImage &image);
|
||||||
|
void adjustPaletteUsingColors(const std::vector<QColor> &colors);
|
||||||
void adjustPaletteUsingColor(QColor color);
|
void adjustPaletteUsingColor(QColor color);
|
||||||
void restoreAdjustableColors();
|
void restoreAdjustableColors();
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,14 @@ constexpr auto kMaxChatEntryHistorySize = 50;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData(
|
||||||
|
const Data::CloudTheme &theme) {
|
||||||
|
return {
|
||||||
|
.colors = theme.outgoingMessagesColors,
|
||||||
|
.accent = theme.outgoingAccentColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ActivateWindow(not_null<SessionController*> controller) {
|
void ActivateWindow(not_null<SessionController*> controller) {
|
||||||
|
@ -1442,7 +1450,8 @@ void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
|
||||||
.preparePalette = PreparePaletteCallback(
|
.preparePalette = PreparePaletteCallback(
|
||||||
data.basedOnDark,
|
data.basedOnDark,
|
||||||
data.accentColor),
|
data.accentColor),
|
||||||
.prepareBackground = backgroundGenerator(theme),
|
.backgroundData = backgroundData(theme),
|
||||||
|
.bubblesData = PrepareBubblesData(data),
|
||||||
};
|
};
|
||||||
crl::async([
|
crl::async([
|
||||||
this,
|
this,
|
||||||
|
@ -1500,8 +1509,11 @@ void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
|
||||||
}
|
}
|
||||||
const auto key = theme.theme->key();
|
const auto key = theme.theme->key();
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
crl::async([=, generator = backgroundGenerator(theme, false)] {
|
crl::async([=, data = backgroundData(theme, false)] {
|
||||||
crl::on_main(weak, [=, result = generator()]() mutable {
|
crl::on_main(weak, [
|
||||||
|
=,
|
||||||
|
result = Ui::PrepareBackgroundImage(data)
|
||||||
|
]() mutable {
|
||||||
const auto i = _customChatThemes.find(key);
|
const auto i = _customChatThemes.find(key);
|
||||||
if (i != end(_customChatThemes)) {
|
if (i != end(_customChatThemes)) {
|
||||||
i->second.theme->updateBackgroundImageFrom(std::move(result));
|
i->second.theme->updateBackgroundImageFrom(std::move(result));
|
||||||
|
@ -1510,9 +1522,9 @@ void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Fn<Ui::ChatThemeBackground()> SessionController::backgroundGenerator(
|
Ui::ChatThemeBackgroundData SessionController::backgroundData(
|
||||||
CachedTheme &theme,
|
CachedTheme &theme,
|
||||||
bool generateGradient) {
|
bool generateGradient) const {
|
||||||
const auto &paper = theme.paper;
|
const auto &paper = theme.paper;
|
||||||
const auto &media = theme.media;
|
const auto &media = theme.media;
|
||||||
const auto paperPath = media ? media->owner()->filepath() : QString();
|
const auto paperPath = media ? media->owner()->filepath() : QString();
|
||||||
|
@ -1523,22 +1535,16 @@ Fn<Ui::ChatThemeBackground()> SessionController::backgroundGenerator(
|
||||||
const auto patternOpacity = paper.patternOpacity();
|
const auto patternOpacity = paper.patternOpacity();
|
||||||
const auto isBlurred = paper.isBlurred();
|
const auto isBlurred = paper.isBlurred();
|
||||||
const auto gradientRotation = paper.gradientRotation();
|
const auto gradientRotation = paper.gradientRotation();
|
||||||
return [=] {
|
return {
|
||||||
auto result = Ui::PrepareBackgroundImage(
|
.path = paperPath,
|
||||||
paperPath,
|
.bytes = paperBytes,
|
||||||
paperBytes,
|
.gzipSvg = gzipSvg,
|
||||||
gzipSvg,
|
.colors = colors,
|
||||||
colors,
|
.isPattern = isPattern,
|
||||||
isPattern,
|
.patternOpacity = patternOpacity,
|
||||||
patternOpacity,
|
.isBlurred = isBlurred,
|
||||||
isBlurred);
|
.generateGradient = generateGradient,
|
||||||
if (generateGradient) {
|
.gradientRotation = gradientRotation,
|
||||||
result.gradientForFill = (colors.size() > 1)
|
|
||||||
? Ui::GenerateDitheredGradient(colors, gradientRotation)
|
|
||||||
: QImage();
|
|
||||||
result.gradientRotation = gradientRotation;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ class ChatStyle;
|
||||||
class ChatTheme;
|
class ChatTheme;
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
struct ChatThemeBackground;
|
struct ChatThemeBackground;
|
||||||
|
struct ChatThemeBackgroundData;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -455,9 +456,9 @@ private:
|
||||||
void cacheChatTheme(const Data::CloudTheme &data);
|
void cacheChatTheme(const Data::CloudTheme &data);
|
||||||
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
|
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
|
||||||
void updateCustomThemeBackground(CachedTheme &theme);
|
void updateCustomThemeBackground(CachedTheme &theme);
|
||||||
[[nodiscard]] Fn<Ui::ChatThemeBackground()> backgroundGenerator(
|
[[nodiscard]] Ui::ChatThemeBackgroundData backgroundData(
|
||||||
CachedTheme &theme,
|
CachedTheme &theme,
|
||||||
bool generateGradient = true);
|
bool generateGradient = true) const;
|
||||||
|
|
||||||
const not_null<Controller*> _window;
|
const not_null<Controller*> _window;
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit af1429cb87d765be7e70308a318ee20d2478ae74
|
Subproject commit db1b4b65c7c70001412ce1ca357d82f3590008f3
|
Loading…
Reference in New Issue