Use outbox bubble colors, adjust custom colors.

This commit is contained in:
John Preston 2021-09-06 13:18:14 +03:00
parent e4e5c4a1d2
commit c318f57fc0
18 changed files with 208 additions and 98 deletions

View File

@ -914,6 +914,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
.theme = _theme.get(),
.visibleAreaTop = _visibleTop,
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
.visibleAreaWidth = width(),
.clip = clip,
});
if (_items.empty() && _upLoaded && _downLoaded) {

View File

@ -579,6 +579,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
.theme = _theme.get(),
.visibleAreaTop = _visibleAreaTop,
.visibleAreaTopGlobal = visibleAreaTopGlobal,
.visibleAreaWidth = width(),
.clip = clip,
});
_pathGradient->startFrame(

View File

@ -1625,6 +1625,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
.theme = _delegate->listChatTheme(),
.visibleAreaTop = _visibleTop,
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
.visibleAreaWidth = width(),
.clip = clip,
}).translated(0, -top);
p.translate(0, top);

View File

@ -232,7 +232,7 @@ void Game::draw(Painter &p, const PaintContext &context) const {
tshift += _titleLines * lineHeight;
}
if (_descriptionLines) {
p.setPen(stm->webPageDescriptionFg);
p.setPen(stm->historyTextFg);
auto endskip = 0;
if (_description.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();

View File

@ -233,7 +233,7 @@ void Invoice::draw(Painter &p, const PaintContext &context) const {
p.setTextPalette(stm->textPalette);
}
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));
tshift += _descriptionHeight;
}
@ -268,7 +268,7 @@ void Invoice::draw(Painter &p, const PaintContext &context) const {
p.translate(-attachLeft, -attachTop);
} else {
p.setPen(stm->webPageDescriptionFg);
p.setPen(stm->historyTextFg);
_status.drawLeft(p, padding.left(), tshift + st::mediaInBubbleSkip, paintw, width());
}
}

View File

@ -165,13 +165,12 @@ void Location::draw(Painter &p, const PaintContext &context) const {
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
p.setPen(stm->historyTextFg);
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);
painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
}
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));
painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}

View File

@ -731,7 +731,7 @@ void Poll::draw(Painter &p, const PaintContext &context) const {
}
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);
tshift += _question.countHeight(paintw) + st::historyPollSubtitleSkip;
@ -1099,7 +1099,7 @@ int Poll::paintAnswer(
}
top += st::historyPollAnswerPadding.top();
p.setPen(stm->webPageDescriptionFg);
p.setPen(stm->historyTextFg);
answer.text.drawLeft(p, aleft, top, awidth, outerWidth);
return height;
@ -1182,7 +1182,7 @@ void Poll::paintPercent(
top += st::historyPollAnswerPadding.top();
p.setFont(st::historyPollPercentFont);
p.setPen(stm->webPageDescriptionFg);
p.setPen(stm->historyTextFg);
const auto pleft = aleft - percentWidth - st::historyPollPercentSkip;
p.drawTextLeft(pleft, top + st::historyPollPercentTop, outerWidth, percent, percentWidth);
}

View File

@ -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);
tshift += lineHeight;
}
p.setPen(stm->historyTextFg);
if (_titleLines) {
p.setPen(stm->webPageTitleFg);
auto endskip = 0;
if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
@ -531,7 +531,6 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
tshift += _titleLines * lineHeight;
}
if (_descriptionLines) {
p.setPen(stm->webPageDescriptionFg);
auto endskip = 0;
if (_description.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();

View File

@ -690,10 +690,6 @@ webPageLeft: 10px;
webPageBar: 2px;
webPageTitleFont: semiboldFont;
webPageTitleStyle: semiboldTextStyle;
webPageTitleOutFg: historyTextOutFg;
webPageTitleInFg: historyTextInFg;
webPageDescriptionOutFg: historyTextOutFg;
webPageDescriptionInFg: historyTextInFg;
webPageDescriptionFont: normalFont;
webPageDescriptionStyle: defaultTextStyle;
webPagePhotoSize: 100px;

View File

@ -211,18 +211,6 @@ ChatStyle::ChatStyle() {
st::mediaInFgSelected,
st::mediaOutFg,
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(
&MessageStyle::textPalette,
st::inTextPalette,

View File

@ -38,8 +38,6 @@ struct MessageStyle {
style::color historyFileNameFg;
style::color historyFileRadialFg;
style::color mediaFg;
style::color webPageTitleFg;
style::color webPageDescriptionFg;
style::TextPalette textPalette;
style::TextPalette semiboldPalette;
style::TextPalette fwdTextPalette;

View File

@ -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
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b) {
@ -167,11 +176,72 @@ ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor)
: _id(descriptor.id)
, _palette(std::make_unique<style::palette>()) {
descriptor.preparePalette(*_palette);
setBackground(descriptor.prepareBackground());
setBackground(PrepareBackgroundImage(descriptor.backgroundData));
setBubblesBackground(PrepareBubblesBackground(descriptor.bubblesData));
adjustPalette(descriptor);
}
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) {
_mutableBackground = std::move(background);
_backgroundState = {};
@ -200,15 +270,23 @@ uint64 ChatTheme::key() const {
}
void ChatTheme::setBubblesBackground(QImage image) {
_bubblesBackgroundPrepared = std::move(image);
if (!_bubblesBackground.area.isEmpty()) {
_bubblesBackground = CacheBackground({
.background = {
.prepared = _bubblesBackgroundPrepared,
},
.area = _bubblesBackground.area,
});
if (image.isNull() && _bubblesBackgroundPrepared.isNull()) {
return;
}
_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) {
_bubblesBackgroundPattern = PrepareBubblePattern(palette());
}
@ -471,8 +549,29 @@ QColor CountAverageColor(const QImage &image) {
}
}
if (size) {
for (auto i = 0; i != 3; ++i) {
components[i] /= size;
for (auto &component : components) {
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]);
@ -652,46 +751,46 @@ QImage GenerateDitheredGradient(
}
ChatThemeBackground PrepareBackgroundImage(
const QString &path,
const QByteArray &bytes,
bool gzipSvg,
const std::vector<QColor> &colors,
bool isPattern,
float64 patternOpacity,
bool isBlurred) {
auto prepared = (isPattern || colors.empty())
? PreprocessBackgroundImage(ReadBackgroundImage(path, bytes, gzipSvg))
const ChatThemeBackgroundData &data) {
auto prepared = (data.isPattern || data.colors.empty())
? PreprocessBackgroundImage(
ReadBackgroundImage(data.path, data.bytes, data.gzipSvg))
: QImage();
if (isPattern && !prepared.isNull()) {
if (colors.size() < 2) {
if (data.isPattern && !prepared.isNull()) {
if (data.colors.size() < 2) {
const auto gradientRotation = 0; // No gradient here.
prepared = PreparePatternImage(
std::move(prepared),
colors,
gradientRotation,
patternOpacity);
data.colors,
data.gradientRotation,
data.patternOpacity);
}
prepared.setDevicePixelRatio(style::DevicePixelRatio());
} else if (colors.empty()) {
} else if (data.colors.empty()) {
prepared.setDevicePixelRatio(style::DevicePixelRatio());
}
const auto imageMonoColor = (colors.size() < 2)
const auto imageMonoColor = (data.colors.size() < 2)
? CalculateImageMonoColor(prepared)
: std::nullopt;
if (!prepared.isNull() && !isPattern && isBlurred) {
if (!prepared.isNull() && !data.isPattern && data.isBlurred) {
prepared = PrepareBlurredBackground(std::move(prepared));
}
auto gradientForFill = (data.generateGradient && data.colors.size() > 1)
? Ui::GenerateDitheredGradient(data.colors, data.gradientRotation)
: QImage();
return ChatThemeBackground{
.prepared = prepared,
.preparedForTiled = PrepareImageForTiled(prepared),
.gradientForFill = std::move(gradientForFill),
.colorForFill = (!prepared.isNull()
? imageMonoColor
: (colors.size() > 1 || colors.empty())
: (data.colors.size() > 1 || data.colors.empty())
? std::nullopt
: std::make_optional(colors.front())),
.colors = colors,
.patternOpacity = patternOpacity,
.isPattern = isPattern,
: std::make_optional(data.colors.front())),
.colors = data.colors,
.patternOpacity = data.patternOpacity,
.gradientRotation = data.generateGradient ? data.gradientRotation : 0,
.isPattern = data.isPattern,
};
}

View File

@ -36,6 +36,23 @@ struct ChatThemeBackground {
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 {
ChatThemeBackground background;
QSize area;
@ -82,7 +99,8 @@ struct BackgroundState {
struct ChatThemeDescriptor {
uint64 id = 0;
Fn<void(style::palette&)> preparePalette;
Fn<ChatThemeBackground()> prepareBackground;
ChatThemeBackgroundData backgroundData;
ChatThemeBubblesData bubblesData;
};
class ChatTheme final : public base::has_weak_ptr {
@ -131,6 +149,10 @@ private:
[[nodiscard]] bool readyForBackgroundRotation() const;
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;
std::unique_ptr<style::palette> _palette;
ChatThemeBackground _mutableBackground;
@ -160,6 +182,7 @@ struct ChatBackgroundRects {
QSize imageSize);
[[nodiscard]] QColor CountAverageColor(const QImage &image);
[[nodiscard]] QColor CountAverageColor(const std::vector<QColor> &colors);
[[nodiscard]] QColor ThemeAdjustedColor(QColor original, QColor background);
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
[[nodiscard]] std::optional<QColor> CalculateImageMonoColor(
@ -185,14 +208,7 @@ struct ChatBackgroundRects {
[[nodiscard]] QImage GenerateDitheredGradient(
const std::vector<QColor> &colors,
int rotation);
[[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
const QString &path,
const QByteArray &bytes,
bool gzipSvg,
const std::vector<QColor> &colors,
bool isPattern,
float64 patternOpacity,
bool isBlurred);
const ChatThemeBackgroundData &data);
} // namespace Ui

View File

@ -752,12 +752,11 @@ void ChatBackground::setPrepared(
prepared = Ui::PrepareBlurredBackground(std::move(prepared));
}
if (adjustPaletteRequired()) {
if (!gradient.isNull()) {
adjustPaletteUsingBackground(gradient);
if ((prepared.isNull() || _paper.isPattern())
&& !_paper.backgroundColors().empty()) {
adjustPaletteUsingColors(_paper.backgroundColors());
} else if (!prepared.isNull()) {
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));
}
void ChatBackground::adjustPaletteUsingColors(
const std::vector<QColor> &colors) {
adjustPaletteUsingColor(Ui::CountAverageColor(colors));
}
void ChatBackground::adjustPaletteUsingColor(QColor color) {
const auto prepared = color.toHsl();
for (const auto &adjustable : _adjustableColors) {

View File

@ -219,6 +219,7 @@ private:
[[nodiscard]] bool adjustPaletteRequired();
void adjustPaletteUsingBackground(const QImage &image);
void adjustPaletteUsingColors(const std::vector<QColor> &colors);
void adjustPaletteUsingColor(QColor color);
void restoreAdjustableColors();

View File

@ -109,6 +109,14 @@ constexpr auto kMaxChatEntryHistorySize = 50;
};
}
[[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData(
const Data::CloudTheme &theme) {
return {
.colors = theme.outgoingMessagesColors,
.accent = theme.outgoingAccentColor,
};
}
} // namespace
void ActivateWindow(not_null<SessionController*> controller) {
@ -1442,7 +1450,8 @@ void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
.preparePalette = PreparePaletteCallback(
data.basedOnDark,
data.accentColor),
.prepareBackground = backgroundGenerator(theme),
.backgroundData = backgroundData(theme),
.bubblesData = PrepareBubblesData(data),
};
crl::async([
this,
@ -1500,8 +1509,11 @@ void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
}
const auto key = theme.theme->key();
const auto weak = base::make_weak(this);
crl::async([=, generator = backgroundGenerator(theme, false)] {
crl::on_main(weak, [=, result = generator()]() mutable {
crl::async([=, data = backgroundData(theme, false)] {
crl::on_main(weak, [
=,
result = Ui::PrepareBackgroundImage(data)
]() mutable {
const auto i = _customChatThemes.find(key);
if (i != end(_customChatThemes)) {
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,
bool generateGradient) {
bool generateGradient) const {
const auto &paper = theme.paper;
const auto &media = theme.media;
const auto paperPath = media ? media->owner()->filepath() : QString();
@ -1523,22 +1535,16 @@ Fn<Ui::ChatThemeBackground()> SessionController::backgroundGenerator(
const auto patternOpacity = paper.patternOpacity();
const auto isBlurred = paper.isBlurred();
const auto gradientRotation = paper.gradientRotation();
return [=] {
auto result = Ui::PrepareBackgroundImage(
paperPath,
paperBytes,
gzipSvg,
colors,
isPattern,
patternOpacity,
isBlurred);
if (generateGradient) {
result.gradientForFill = (colors.size() > 1)
? Ui::GenerateDitheredGradient(colors, gradientRotation)
: QImage();
result.gradientRotation = gradientRotation;
}
return result;
return {
.path = paperPath,
.bytes = paperBytes,
.gzipSvg = gzipSvg,
.colors = colors,
.isPattern = isPattern,
.patternOpacity = patternOpacity,
.isBlurred = isBlurred,
.generateGradient = generateGradient,
.gradientRotation = gradientRotation,
};
}

View File

@ -49,6 +49,7 @@ class ChatStyle;
class ChatTheme;
struct ChatPaintContext;
struct ChatThemeBackground;
struct ChatThemeBackgroundData;
} // namespace Ui
namespace Data {
@ -455,9 +456,9 @@ private:
void cacheChatTheme(const Data::CloudTheme &data);
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
void updateCustomThemeBackground(CachedTheme &theme);
[[nodiscard]] Fn<Ui::ChatThemeBackground()> backgroundGenerator(
[[nodiscard]] Ui::ChatThemeBackgroundData backgroundData(
CachedTheme &theme,
bool generateGradient = true);
bool generateGradient = true) const;
const not_null<Controller*> _window;

@ -1 +1 @@
Subproject commit af1429cb87d765be7e70308a318ee20d2478ae74
Subproject commit db1b4b65c7c70001412ce1ca357d82f3590008f3