mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-31 06:26:18 +00:00
Colorize bubbles according to a custom chat theme.
This commit is contained in:
@@ -18,19 +18,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
struct CornersPixmaps {
|
||||
QPixmap p[4];
|
||||
};
|
||||
std::vector<CornersPixmaps> Corners;
|
||||
base::flat_map<uint32, CornersPixmaps> CornersMap;
|
||||
QImage CornersMaskLarge[4], CornersMaskSmall[4];
|
||||
rpl::lifetime PaletteChangedLifetime;
|
||||
|
||||
void PrepareCorners(CachedRoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) {
|
||||
Expects(Corners.size() > index);
|
||||
|
||||
[[nodiscard]] std::array<QImage, 4> PrepareCorners(int32 radius, const QBrush &brush, const style::color *shadow = nullptr) {
|
||||
int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio();
|
||||
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4];
|
||||
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied);
|
||||
{
|
||||
Painter p(&rect);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
@@ -46,27 +41,31 @@ void PrepareCorners(CachedRoundCorners index, int32 radius, const QBrush &brush,
|
||||
p.setBrush(brush);
|
||||
p.drawRoundedRect(0, 0, r * 3, r * 3, r, r);
|
||||
}
|
||||
if (!cors) cors = localCors;
|
||||
cors[0] = rect.copy(0, 0, r, r);
|
||||
cors[1] = rect.copy(r * 2, 0, r, r);
|
||||
cors[2] = rect.copy(0, r * 2, r, r + (shadow ? s : 0));
|
||||
cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0));
|
||||
if (index != SmallMaskCorners && index != LargeMaskCorners) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Corners[index].p[i] = PixmapFromImage(std::move(cors[i]));
|
||||
Corners[index].p[i].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
auto result = std::array<QImage, 4>();
|
||||
result[0] = rect.copy(0, 0, r, r);
|
||||
result[1] = rect.copy(r * 2, 0, r, r);
|
||||
result[2] = rect.copy(0, r * 2, r, r + (shadow ? s : 0));
|
||||
result[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0));
|
||||
return result;
|
||||
}
|
||||
|
||||
void PrepareCorners(CachedRoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr) {
|
||||
Expects(index < Corners.size());
|
||||
|
||||
auto images = PrepareCorners(radius, brush, shadow);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Corners[index].p[i] = PixmapFromImage(std::move(images[i]));
|
||||
Corners[index].p[i].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMaskCorners() {
|
||||
QImage mask[4];
|
||||
PrepareCorners(SmallMaskCorners, st::roundRadiusSmall, QColor(255, 255, 255), nullptr, mask);
|
||||
auto mask = PrepareCorners(st::roundRadiusSmall, QColor(255, 255, 255), nullptr);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
CornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
CornersMaskSmall[i].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
PrepareCorners(LargeMaskCorners, st::historyMessageRadius, QColor(255, 255, 255), nullptr, mask);
|
||||
mask = PrepareCorners(st::historyMessageRadius, QColor(255, 255, 255), nullptr);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
CornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
CornersMaskLarge[i].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
@@ -231,23 +230,40 @@ void FillRoundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::colo
|
||||
}
|
||||
}
|
||||
|
||||
void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts) {
|
||||
auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24);
|
||||
auto i = CornersMap.find(colorKey);
|
||||
if (i == CornersMap.cend()) {
|
||||
QImage images[4];
|
||||
switch (radius) {
|
||||
case ImageRoundRadius::Small: PrepareCorners(SmallMaskCorners, st::roundRadiusSmall, bg, nullptr, images); break;
|
||||
case ImageRoundRadius::Large: PrepareCorners(LargeMaskCorners, st::historyMessageRadius, bg, nullptr, images); break;
|
||||
default: p.fillRect(x, y, w, h, bg); return;
|
||||
}
|
||||
CornersPixmaps PrepareCornerPixmaps(int32 radius, style::color bg, const style::color *sh) {
|
||||
auto images = PrepareCorners(radius, bg, sh);
|
||||
auto result = CornersPixmaps();
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
result.p[j] = PixmapFromImage(std::move(images[j]));
|
||||
result.p[j].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CornersPixmaps pixmaps;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
pixmaps.p[j] = PixmapFromImage(std::move(images[j]));
|
||||
pixmaps.p[j].setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
i = CornersMap.emplace(colorKey, pixmaps).first;
|
||||
CornersPixmaps PrepareCornerPixmaps(ImageRoundRadius radius, style::color bg, const style::color *sh) {
|
||||
switch (radius) {
|
||||
case ImageRoundRadius::Small:
|
||||
return PrepareCornerPixmaps(st::roundRadiusSmall, bg, sh);
|
||||
case ImageRoundRadius::Large:
|
||||
return PrepareCornerPixmaps(st::historyMessageRadius, bg, sh);
|
||||
}
|
||||
Unexpected("Image round radius in PrepareCornerPixmaps.");
|
||||
}
|
||||
|
||||
void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts) {
|
||||
if (radius == ImageRoundRadius::None) {
|
||||
p.fillRect(x, y, w, h, bg);
|
||||
return;
|
||||
}
|
||||
const auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24)
|
||||
| ((uint32(bg->c.red()) & 0xFF) << 16)
|
||||
| ((uint32(bg->c.green()) & 0xFF) << 8)
|
||||
| ((uint32(bg->c.blue()) & 0xFF));
|
||||
auto i = CornersMap.find(colorKey);
|
||||
if (i == end(CornersMap)) {
|
||||
i = CornersMap.emplace(
|
||||
colorKey,
|
||||
PrepareCornerPixmaps(radius, bg, nullptr)).first;
|
||||
}
|
||||
FillRoundRect(p, x, y, w, h, bg, i->second, nullptr, parts);
|
||||
}
|
||||
|
@@ -15,10 +15,11 @@ enum class ImageRoundRadius;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
enum CachedRoundCorners : int {
|
||||
SmallMaskCorners = 0x00, // for images
|
||||
LargeMaskCorners,
|
||||
struct CornersPixmaps {
|
||||
QPixmap p[4];
|
||||
};
|
||||
|
||||
enum CachedRoundCorners : int {
|
||||
BoxCorners,
|
||||
MenuCorners,
|
||||
BotKbOverCorners,
|
||||
@@ -69,6 +70,16 @@ inline void FillRoundRect(Painter &p, const QRect &rect, style::color bg, ImageR
|
||||
return FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts);
|
||||
}
|
||||
|
||||
[[nodiscard]] CornersPixmaps PrepareCornerPixmaps(
|
||||
int32 radius,
|
||||
style::color bg,
|
||||
const style::color *sh);
|
||||
[[nodiscard]] CornersPixmaps PrepareCornerPixmaps(
|
||||
ImageRoundRadius radius,
|
||||
style::color bg,
|
||||
const style::color *sh);
|
||||
void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow = nullptr, RectParts parts = RectPart::Full);
|
||||
|
||||
void StartCachedCorners();
|
||||
void FinishCachedCorners();
|
||||
|
||||
|
125
Telegram/SourceFiles/ui/chat/chat_style.cpp
Normal file
125
Telegram/SourceFiles/ui/chat/chat_style.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "ui/chat/chat_style.h"
|
||||
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
ChatStyle::ChatStyle() {
|
||||
finalize();
|
||||
messageIcon(
|
||||
&MessageStyle::tailLeft,
|
||||
st::historyBubbleTailInLeft,
|
||||
st::historyBubbleTailInLeftSelected,
|
||||
st::historyBubbleTailOutLeft,
|
||||
st::historyBubbleTailOutLeftSelected);
|
||||
messageIcon(
|
||||
&MessageStyle::tailRight,
|
||||
st::historyBubbleTailInRight,
|
||||
st::historyBubbleTailInRightSelected,
|
||||
st::historyBubbleTailOutRight,
|
||||
st::historyBubbleTailOutRightSelected);
|
||||
messageColor(
|
||||
&MessageStyle::msgBg,
|
||||
msgInBg(),
|
||||
msgInBgSelected(),
|
||||
msgOutBg(),
|
||||
msgOutBgSelected());
|
||||
messageColor(
|
||||
&MessageStyle::msgShadow,
|
||||
msgInShadow(),
|
||||
msgInShadowSelected(),
|
||||
msgOutShadow(),
|
||||
msgOutShadowSelected());
|
||||
}
|
||||
|
||||
void ChatStyle::apply(not_null<ChatTheme*> theme) {
|
||||
const auto themePalette = theme->palette();
|
||||
assignPalette(themePalette
|
||||
? themePalette
|
||||
: style::main_palette::get().get());
|
||||
if (themePalette) {
|
||||
_defaultPaletteChangeLifetime.destroy();
|
||||
} else {
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
assignPalette(style::main_palette::get());
|
||||
}, _defaultPaletteChangeLifetime);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
|
||||
*static_cast<style::palette*>(this) = *palette;
|
||||
style::internal::resetIcons();
|
||||
for (auto &style : _messageStyles) {
|
||||
style.corners = {};
|
||||
}
|
||||
}
|
||||
|
||||
const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
|
||||
auto &result = messageStyleRaw(outbg, selected);
|
||||
if (result.corners.p[0].isNull()) {
|
||||
result.corners = Ui::PrepareCornerPixmaps(
|
||||
st::historyMessageRadius,
|
||||
result.msgBg,
|
||||
&result.msgShadow);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessageStyle &ChatStyle::messageStyleRaw(bool outbg, bool selected) const {
|
||||
return _messageStyles[(outbg ? 2 : 0) + (selected ? 1 : 0)];
|
||||
}
|
||||
|
||||
void ChatStyle::icon(style::icon &my, const style::icon &original) {
|
||||
my = original.withPalette(*this);
|
||||
}
|
||||
|
||||
MessageStyle &ChatStyle::messageIn() {
|
||||
return messageStyleRaw(false, false);
|
||||
}
|
||||
|
||||
MessageStyle &ChatStyle::messageInSelected() {
|
||||
return messageStyleRaw(false, true);
|
||||
}
|
||||
|
||||
MessageStyle &ChatStyle::messageOut() {
|
||||
return messageStyleRaw(true, false);
|
||||
}
|
||||
|
||||
MessageStyle &ChatStyle::messageOutSelected() {
|
||||
return messageStyleRaw(true, true);
|
||||
}
|
||||
|
||||
void ChatStyle::messageIcon(
|
||||
style::icon MessageStyle::*my,
|
||||
const style::icon &originalIn,
|
||||
const style::icon &originalInSelected,
|
||||
const style::icon &originalOut,
|
||||
const style::icon &originalOutSelected) {
|
||||
icon(messageIn().*my, originalIn);
|
||||
icon(messageInSelected().*my, originalInSelected);
|
||||
icon(messageOut().*my, originalOut);
|
||||
icon(messageOutSelected().*my, originalOutSelected);
|
||||
}
|
||||
|
||||
void ChatStyle::messageColor(
|
||||
style::color MessageStyle::*my,
|
||||
const style::color &originalIn,
|
||||
const style::color &originalInSelected,
|
||||
const style::color &originalOut,
|
||||
const style::color &originalOutSelected) {
|
||||
messageIn().*my = originalIn;
|
||||
messageInSelected().*my = originalInSelected;
|
||||
messageOut().*my = originalOut;
|
||||
messageOutSelected().*my = originalOutSelected;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
65
Telegram/SourceFiles/ui/chat/chat_style.h
Normal file
65
Telegram/SourceFiles/ui/chat/chat_style.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/cached_round_corners.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class ChatTheme;
|
||||
|
||||
struct MessageStyle {
|
||||
CornersPixmaps corners;
|
||||
style::icon tailLeft = { Qt::Uninitialized };
|
||||
style::icon tailRight = { Qt::Uninitialized };
|
||||
style::color msgBg;
|
||||
style::color msgShadow;
|
||||
};
|
||||
|
||||
class ChatStyle final : public style::palette {
|
||||
public:
|
||||
ChatStyle();
|
||||
|
||||
void apply(not_null<ChatTheme*> theme);
|
||||
|
||||
[[nodiscard]] const MessageStyle &messageStyle(
|
||||
bool outbg,
|
||||
bool selected) const;
|
||||
|
||||
private:
|
||||
void assignPalette(not_null<const style::palette*> palette);
|
||||
|
||||
void icon(style::icon &my, const style::icon &original);
|
||||
|
||||
[[nodiscard]] MessageStyle &messageStyleRaw(
|
||||
bool outbg,
|
||||
bool selected) const;
|
||||
[[nodiscard]] MessageStyle &messageIn();
|
||||
[[nodiscard]] MessageStyle &messageInSelected();
|
||||
[[nodiscard]] MessageStyle &messageOut();
|
||||
[[nodiscard]] MessageStyle &messageOutSelected();
|
||||
void messageIcon(
|
||||
style::icon MessageStyle::*my,
|
||||
const style::icon &originalIn,
|
||||
const style::icon &originalInSelected,
|
||||
const style::icon &originalOut,
|
||||
const style::icon &originalOutSelected);
|
||||
void messageColor(
|
||||
style::color MessageStyle::*my,
|
||||
const style::color &originalIn,
|
||||
const style::color &originalInSelected,
|
||||
const style::color &originalOut,
|
||||
const style::color &originalOutSelected);
|
||||
|
||||
mutable std::array<MessageStyle, 4> _messageStyles;
|
||||
|
||||
rpl::lifetime _defaultPaletteChangeLifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
@@ -30,6 +30,8 @@ constexpr auto kMaxSize = 2960;
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request) {
|
||||
Expects(!request.area.isEmpty());
|
||||
|
||||
const auto gradient = request.background.gradientForFill.isNull()
|
||||
? QImage()
|
||||
: (request.gradientRotationAdd != 0)
|
||||
@@ -69,12 +71,14 @@ constexpr auto kMaxSize = 2960;
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation)
|
||||
: request.background.preparedForTiled;
|
||||
const auto w = tiled.width() / style::DevicePixelRatio();
|
||||
const auto h = tiled.height() / style::DevicePixelRatio();
|
||||
const auto w = tiled.width() / float(style::DevicePixelRatio());
|
||||
const auto h = tiled.height() / float(style::DevicePixelRatio());
|
||||
const auto cx = int(std::ceil(request.area.width() / w));
|
||||
const auto cy = int(std::ceil(request.area.height() / h));
|
||||
const auto rows = cy;
|
||||
const auto cols = request.background.isPattern ? (((cx / 2) * 2) + 1) : cx;
|
||||
const auto cols = request.background.isPattern
|
||||
? (((cx / 2) * 2) + 1)
|
||||
: cx;
|
||||
const auto xshift = request.background.isPattern
|
||||
? (request.area.width() - cols * w) / 2
|
||||
: 0;
|
||||
@@ -202,13 +206,14 @@ void ChatTheme::setBubblesBackground(QImage image) {
|
||||
});
|
||||
}
|
||||
if (!_bubblesBackgroundPattern) {
|
||||
_bubblesBackgroundPattern = PrepareBubblePattern();
|
||||
_bubblesBackgroundPattern = PrepareBubblePattern(palette());
|
||||
}
|
||||
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
_repaintBackgroundRequests.fire({});
|
||||
}
|
||||
|
||||
ChatPaintContext ChatTheme::preparePaintContext(
|
||||
not_null<const ChatStyle*> st,
|
||||
QRect viewport,
|
||||
QRect clip) {
|
||||
_bubblesBackground.area = viewport.size();
|
||||
@@ -223,7 +228,7 @@ ChatPaintContext ChatTheme::preparePaintContext(
|
||||
// _bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
//}
|
||||
return {
|
||||
.st = _palette ? _palette.get() : style::main_palette::get(),
|
||||
.st = st,
|
||||
.bubblesPattern = _bubblesBackgroundPattern.get(),
|
||||
.viewport = viewport,
|
||||
.clip = clip,
|
||||
@@ -240,6 +245,7 @@ const BackgroundState &ChatTheme::backgroundState(QSize area) {
|
||||
&& !background().gradientForFill.isNull()) {
|
||||
// We don't support direct painting of patterned gradients.
|
||||
// So we need to sync-generate cache image here.
|
||||
_willCacheForArea = area;
|
||||
setCachedBackground(CacheBackground(currentCacheRequest(area)));
|
||||
_cacheBackgroundTimer->cancel();
|
||||
} else if (_backgroundState.now.area != area) {
|
||||
|
@@ -13,10 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class ChatStyle;
|
||||
struct BubblePattern;
|
||||
|
||||
struct ChatPaintContext {
|
||||
not_null<const style::palette*> st;
|
||||
not_null<const ChatStyle*> st;
|
||||
const BubblePattern *bubblesPattern = nullptr;
|
||||
QRect viewport;
|
||||
QRect clip;
|
||||
@@ -112,22 +113,23 @@ public:
|
||||
ChatTheme(ChatThemeDescriptor &&descriptor);
|
||||
|
||||
[[nodiscard]] uint64 key() const;
|
||||
[[nodiscard]] not_null<const style::palette*> palette() const {
|
||||
[[nodiscard]] const style::palette *palette() const {
|
||||
return _palette.get();
|
||||
}
|
||||
|
||||
void setBackground(ChatThemeBackground &&background);
|
||||
void updateBackgroundImageFrom(ChatThemeBackground &&background);
|
||||
const ChatThemeBackground &background() const {
|
||||
[[nodiscard]] const ChatThemeBackground &background() const {
|
||||
return _mutableBackground;
|
||||
}
|
||||
|
||||
void setBubblesBackground(QImage image);
|
||||
const BubblePattern *bubblesBackgroundPattern() const {
|
||||
[[nodiscard]] const BubblePattern *bubblesBackgroundPattern() const {
|
||||
return _bubblesBackgroundPattern.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] ChatPaintContext preparePaintContext(
|
||||
not_null<const ChatStyle*> st,
|
||||
QRect viewport,
|
||||
QRect clip);
|
||||
[[nodiscard]] const BackgroundState &backgroundState(QSize area);
|
||||
|
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace Ui {
|
||||
@@ -79,12 +80,10 @@ void PaintBubbleGeneric(
|
||||
}
|
||||
|
||||
void PaintPatternBubble(Painter &p, const SimpleBubble &args) {
|
||||
const auto opacity = st::msgOutBg->c.alphaF();
|
||||
const auto shadowOpacity = opacity * st::msgOutShadow->c.alphaF();
|
||||
const auto opacity = args.st->msgOutBg()->c.alphaF();
|
||||
const auto shadowOpacity = opacity * args.st->msgOutShadow()->c.alphaF();
|
||||
const auto pattern = args.pattern;
|
||||
const auto sh = (args.skip & RectPart::Bottom)
|
||||
? nullptr
|
||||
: &st::msgOutShadow;
|
||||
const auto sh = !(args.skip & RectPart::Bottom);
|
||||
const auto &tail = (args.tailSide == RectPart::Right)
|
||||
? pattern->tailRight
|
||||
: pattern->tailLeft;
|
||||
@@ -221,30 +220,14 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) {
|
||||
}
|
||||
|
||||
void PaintSolidBubble(Painter &p, const SimpleBubble &args) {
|
||||
const auto &bg = args.selected
|
||||
? (args.outbg ? st::msgOutBgSelected : st::msgInBgSelected)
|
||||
: (args.outbg ? st::msgOutBg : st::msgInBg);
|
||||
const auto &st = args.st->messageStyle(args.outbg, args.selected);
|
||||
const auto &bg = st.msgBg;
|
||||
const auto sh = (args.skip & RectPart::Bottom)
|
||||
? nullptr
|
||||
: args.selected
|
||||
? &(args.outbg ? st::msgOutShadowSelected : st::msgInShadowSelected)
|
||||
: &(args.outbg ? st::msgOutShadow : st::msgInShadow);
|
||||
const auto corners = args.selected
|
||||
? (args.outbg
|
||||
? MessageOutSelectedCorners
|
||||
: MessageInSelectedCorners)
|
||||
: (args.outbg ? MessageOutCorners : MessageInCorners);
|
||||
: &st.msgShadow;
|
||||
const auto &tail = (args.tailSide == RectPart::Right)
|
||||
? (args.selected
|
||||
? st::historyBubbleTailOutRightSelected
|
||||
: st::historyBubbleTailOutRight)
|
||||
: args.selected
|
||||
? (args.outbg
|
||||
? st::historyBubbleTailOutLeftSelected
|
||||
: st::historyBubbleTailInLeftSelected)
|
||||
: (args.outbg
|
||||
? st::historyBubbleTailOutLeft
|
||||
: st::historyBubbleTailInLeft);
|
||||
? st.tailRight
|
||||
: st.tailLeft;
|
||||
const auto tailShift = (args.tailSide == RectPart::Right)
|
||||
? QPoint(0, tail.height())
|
||||
: QPoint(tail.width(), tail.height());
|
||||
@@ -253,7 +236,7 @@ void PaintSolidBubble(Painter &p, const SimpleBubble &args) {
|
||||
}, [&](const QRect &rect) {
|
||||
p.fillRect(rect, *sh);
|
||||
}, [&](const QRect &rect, RectParts parts) {
|
||||
Ui::FillRoundRect(p, rect, bg, corners, sh, parts);
|
||||
Ui::FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, st.corners, sh, parts);
|
||||
}, [&](const QPoint &bottomPosition) {
|
||||
tail.paint(p, bottomPosition - tailShift, args.outerWidth);
|
||||
return tail.width();
|
||||
@@ -262,7 +245,8 @@ void PaintSolidBubble(Painter &p, const SimpleBubble &args) {
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<BubblePattern> PrepareBubblePattern() {
|
||||
std::unique_ptr<BubblePattern> PrepareBubblePattern(
|
||||
not_null<const style::palette*> st) {
|
||||
auto result = std::make_unique<Ui::BubblePattern>();
|
||||
result->corners = Images::CornersMask(st::historyMessageRadius);
|
||||
const auto addShadow = [&](QImage &bottomCorner) {
|
||||
@@ -274,7 +258,7 @@ std::unique_ptr<BubblePattern> PrepareBubblePattern() {
|
||||
result.fill(Qt::transparent);
|
||||
result.setDevicePixelRatio(bottomCorner.devicePixelRatio());
|
||||
auto p = QPainter(&result);
|
||||
p.setOpacity(st::msgInShadow->c.alphaF());
|
||||
p.setOpacity(st->msgInShadow()->c.alphaF());
|
||||
p.drawImage(0, st::msgShadow, bottomCorner);
|
||||
p.setOpacity(1.);
|
||||
p.drawImage(0, 0, bottomCorner);
|
||||
|
@@ -13,6 +13,9 @@ class Painter;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class ChatTheme;
|
||||
class ChatStyle;
|
||||
|
||||
struct BubbleSelectionInterval {
|
||||
int top = 0;
|
||||
int height = 0;
|
||||
@@ -28,9 +31,11 @@ struct BubblePattern {
|
||||
mutable QImage tailCache;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<BubblePattern> PrepareBubblePattern();
|
||||
[[nodiscard]] std::unique_ptr<BubblePattern> PrepareBubblePattern(
|
||||
not_null<const style::palette*> st);
|
||||
|
||||
struct SimpleBubble {
|
||||
not_null<const ChatStyle*> st;
|
||||
QRect geometry;
|
||||
const BubblePattern *pattern = nullptr;
|
||||
QRect patternViewport;
|
||||
|
Reference in New Issue
Block a user