2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-31 06:35:14 +00:00

Move ChatTheme to td_ui.

This commit is contained in:
John Preston
2021-08-26 18:02:21 +03:00
parent 3cd0f9d189
commit 0a1e84ddb2
37 changed files with 353 additions and 244 deletions

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "ui/ui_utility.h"
#include "ui/chat/chat_theme.h"
#include "data/data_peer.h"
#include "data/data_changes.h"
#include "data/data_session.h"
@@ -18,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_slide_animation.h"
#include "window/window_session_controller.h"
#include "window/themes/window_theme.h"
#include "window/themes/window_chat_theme.h"
#include <rpl/range.h>
@@ -64,11 +64,11 @@ namespace {
[[nodiscard]] auto ChatThemeValueFromPeer(
not_null<SessionController*> controller,
not_null<PeerData*> peer)
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
return MaybeCloudThemeValueFromPeer(
peer
) | rpl::map([=](std::optional<Data::CloudTheme> theme)
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
if (!theme) {
return rpl::single(controller->defaultChatTheme());
}
@@ -94,7 +94,7 @@ AbstractSectionWidget::AbstractSectionWidget(
return ChatThemeValueFromPeer(
controller,
peer
) | rpl::map([](const std::shared_ptr<Theme::ChatTheme> &theme) {
) | rpl::map([](const std::shared_ptr<Ui::ChatTheme> &theme) {
return rpl::single(
rpl::empty_value()
) | rpl::then(
@@ -185,21 +185,21 @@ QPixmap SectionWidget::grabForShowAnimation(
void SectionWidget::PaintBackground(
not_null<Window::SessionController*> controller,
not_null<Window::Theme::ChatTheme*> theme,
not_null<Ui::ChatTheme*> theme,
not_null<QWidget*> widget,
QRect clip) {
Painter p(widget);
const auto background = Window::Theme::Background();
if (const auto color = background->colorForFill()) {
p.fillRect(clip, *color);
const auto &background = theme->background();
if (background.colorForFill) {
p.fillRect(clip, *background.colorForFill);
return;
}
const auto gradient = background->gradientForFill();
const auto &gradient = background.gradientForFill;
const auto fill = QSize(widget->width(), controller->content()->height());
auto fromy = controller->content()->backgroundFromY();
auto state = theme->backgroundState(fill);
const auto paintCache = [&](const Theme::CachedBackground &cache) {
const auto paintCache = [&](const Ui::CachedBackground &cache) {
const auto to = QRect(
QPoint(cache.x, fromy + cache.y),
cache.pixmap.size() / cIntRetinaFactor());
@@ -233,10 +233,10 @@ void SectionWidget::PaintBackground(
paintCache(state.now);
return;
}
const auto &prepared = background->prepared();
const auto &prepared = background.prepared;
if (prepared.isNull()) {
return;
} else if (background->paper().isPattern()) {
} else if (background.isPattern) {
const auto w = prepared.width() * fill.height() / prepared.height();
const auto cx = qCeil(fill.width() / float64(w));
const auto cols = (cx / 2) * 2 + 1;
@@ -247,8 +247,8 @@ void SectionWidget::PaintBackground(
prepared,
QRect(QPoint(), prepared.size()));
}
} else if (background->tile()) {
const auto &tiled = background->preparedForTiled();
} else if (background.tile) {
const auto &tiled = background.preparedForTiled;
const auto left = clip.left();
const auto top = clip.top();
const auto right = clip.left() + clip.width();
@@ -266,7 +266,7 @@ void SectionWidget::PaintBackground(
}
} else {
const auto hq = PainterHighQualityEnabler(p);
const auto rects = Window::Theme::ComputeBackgroundRects(
const auto rects = Ui::ComputeChatBackgroundRects(
fill,
prepared.size());
auto to = rects.to;

View File

@@ -22,11 +22,8 @@ class Session;
namespace Ui {
class LayerWidget;
} // namespace Ui
namespace Window::Theme {
class ChatTheme;
} // namespace Window::Theme
} // namespace Ui
namespace Window {
@@ -164,7 +161,7 @@ public:
static void PaintBackground(
not_null<SessionController*> controller,
not_null<Window::Theme::ChatTheme*> theme,
not_null<Ui::ChatTheme*> theme,
not_null<QWidget*> widget,
QRect clip);

View File

@@ -1,390 +0,0 @@
/*
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 "window/themes/window_chat_theme.h"
#include "window/themes/window_theme.h"
#include "ui/image/image_prepare.h"
#include "ui/ui_utility.h"
#include "ui/chat/message_bubble.h"
#include "history/view/history_view_element.h"
#include <QtGui/QGuiApplication>
namespace Window::Theme {
namespace {
constexpr auto kMaxChatEntryHistorySize = 50;
constexpr auto kCacheBackgroundTimeout = 3 * crl::time(1000);
constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
constexpr auto kBackgroundFadeDuration = crl::time(200);
[[nodiscard]] CacheBackgroundResult CacheBackground(
const CacheBackgroundRequest &request) {
const auto gradient = request.gradient.isNull()
? QImage()
: request.recreateGradient
? Images::GenerateGradient(
request.gradient.size(),
request.gradientColors,
request.gradientRotation)
: request.gradient;
if (request.isPattern || request.tile || request.prepared.isNull()) {
auto result = gradient.isNull()
? QImage(
request.area * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied)
: gradient.scaled(
request.area * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
if (!request.prepared.isNull()) {
QPainter p(&result);
if (!gradient.isNull()) {
if (request.patternOpacity >= 0.) {
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(request.patternOpacity);
} else {
p.setCompositionMode(
QPainter::CompositionMode_DestinationIn);
}
}
const auto tiled = request.isPattern
? request.prepared.scaled(
request.area.height() * cIntRetinaFactor(),
request.area.height() * cIntRetinaFactor(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation)
: request.preparedForTiled;
const auto w = tiled.width() / cRetinaFactor();
const auto h = tiled.height() / cRetinaFactor();
const auto cx = qCeil(request.area.width() / w);
const auto cy = qCeil(request.area.height() / h);
const auto rows = cy;
const auto cols = request.isPattern ? (((cx / 2) * 2) + 1) : cx;
const auto xshift = request.isPattern
? (request.area.width() - cols * w) / 2
: 0;
for (auto y = 0; y != rows; ++y) {
for (auto x = 0; x != cols; ++x) {
p.drawImage(QPointF(xshift + x * w, y * h), tiled);
}
}
if (!gradient.isNull()
&& request.patternOpacity < 0.
&& request.patternOpacity > -1.) {
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setOpacity(1. + request.patternOpacity);
p.fillRect(QRect(QPoint(), request.area), Qt::black);
}
}
return {
.image = std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied),
.gradient = gradient,
.area = request.area,
};
} else {
const auto rects = ComputeBackgroundRects(
request.area,
request.prepared.size());
auto result = request.prepared.copy(rects.from).scaled(
rects.to.width() * cIntRetinaFactor(),
rects.to.height() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
return {
.image = std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied),
.gradient = gradient,
.area = request.area,
.x = rects.to.x(),
.y = rects.to.y(),
};
}
}
} // namespace
bool operator==(
const CacheBackgroundRequest &a,
const CacheBackgroundRequest &b) {
return (a.prepared.cacheKey() == b.prepared.cacheKey())
&& (a.area == b.area)
&& (a.gradientRotation == b.gradientRotation)
&& (a.tile == b.tile)
&& (a.recreateGradient == b.recreateGradient)
&& (a.gradient.cacheKey() == b.gradient.cacheKey())
&& (a.gradientProgress == b.gradientProgress)
&& (a.patternOpacity == b.patternOpacity);
}
bool operator!=(
const CacheBackgroundRequest &a,
const CacheBackgroundRequest &b) {
return !(a == b);
}
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
: pixmap(Ui::PixmapFromImage(std::move(result.image)))
, area(result.area)
, x(result.x)
, y(result.y) {
}
ChatTheme::ChatTheme() {
Background()->updates(
) | rpl::start_with_next([=](const BackgroundUpdate &update) {
if (update.type == BackgroundUpdate::Type::New
|| update.type == BackgroundUpdate::Type::Changed) {
clearCachedBackground();
}
}, _lifetime);
}
// Runs from background thread.
ChatTheme::ChatTheme(const Data::CloudTheme &theme)
: _id(theme.id)
, _palette(std::make_unique<style::palette>()) {
}
uint64 ChatTheme::key() const {
return _id;
}
void ChatTheme::setBubblesBackground(QImage image) {
_bubblesBackgroundPrepared = std::move(image);
if (!_bubblesBackground.area.isEmpty()) {
_bubblesBackground = CacheBackground({
.prepared = _bubblesBackgroundPrepared,
.area = _bubblesBackground.area,
});
}
if (!_bubblesBackgroundPattern) {
_bubblesBackgroundPattern = Ui::PrepareBubblePattern();
}
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
_repaintBackgroundRequests.fire({});
}
HistoryView::PaintContext ChatTheme::preparePaintContext(
QRect viewport,
QRect clip) {
_bubblesBackground.area = viewport.size();
//if (!_bubblesBackgroundPrepared.isNull()
// && _bubblesBackground.area != viewport.size()
// && !viewport.isEmpty()) {
// // #TODO bubbles delayed caching
// _bubblesBackground = CacheBackground({
// .prepared = _bubblesBackgroundPrepared,
// .area = viewport.size(),
// });
// _bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
//}
return {
.st = _palette ? _palette.get() : style::main_palette::get(),
.bubblesPattern = _bubblesBackgroundPattern.get(),
.viewport = viewport,
.clip = clip,
.now = crl::now(),
};
}
const BackgroundState &ChatTheme::backgroundState(QSize area) {
if (!_cacheBackgroundTimer) {
_cacheBackgroundTimer.emplace([=] { cacheBackground(); });
}
_backgroundState.shown = _backgroundFade.value(1.);
if (_backgroundState.now.pixmap.isNull()
&& !Background()->gradientForFill().isNull()) {
// We don't support direct painting of patterned gradients.
// So we need to sync-generate cache image here.
setCachedBackground(CacheBackground(currentCacheRequest(area)));
_cacheBackgroundTimer->cancel();
} else if (_backgroundState.now.area != area) {
if (_willCacheForArea != area
|| (!_cacheBackgroundTimer->isActive()
&& !_backgroundCachingRequest)) {
_willCacheForArea = area;
_lastAreaChangeTime = crl::now();
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
}
}
generateNextBackgroundRotation();
return _backgroundState;
}
bool ChatTheme::readyForBackgroundRotation() const {
Expects(_cacheBackgroundTimer.has_value());
return !anim::Disabled()
&& !_backgroundFade.animating()
&& !_cacheBackgroundTimer->isActive()
&& !_backgroundState.now.pixmap.isNull();
}
void ChatTheme::generateNextBackgroundRotation() {
if (_backgroundCachingRequest
|| !_backgroundNext.image.isNull()
|| !readyForBackgroundRotation()) {
return;
}
const auto background = Background();
if (background->paper().backgroundColors().size() < 3) {
return;
}
constexpr auto kAddRotation = 315;
const auto request = currentCacheRequest(
_backgroundState.now.area,
kAddRotation);
if (!request) {
return;
}
cacheBackgroundAsync(request, [=](CacheBackgroundResult &&result) {
const auto forRequest = base::take(_backgroundCachingRequest);
if (!readyForBackgroundRotation()) {
return;
}
const auto request = currentCacheRequest(
_backgroundState.now.area,
kAddRotation);
if (forRequest == request) {
_backgroundAddRotation
= (_backgroundAddRotation + kAddRotation) % 360;
_backgroundNext = std::move(result);
}
});
}
auto ChatTheme::currentCacheRequest(QSize area, int addRotation) const
-> CacheBackgroundRequest {
const auto background = Background();
if (background->colorForFill()) {
return {};
}
const auto rotation = background->paper().gradientRotation();
const auto gradient = background->gradientForFill();
return {
.prepared = background->prepared(),
.preparedForTiled = background->preparedForTiled(),
.area = area,
.gradientRotation = (rotation
+ _backgroundAddRotation
+ addRotation) % 360,
.tile = background->tile(),
.isPattern = background->paper().isPattern(),
.recreateGradient = (addRotation != 0),
.gradient = gradient,
.gradientColors = (gradient.isNull()
? std::vector<QColor>()
: background->paper().backgroundColors()),
.gradientProgress = 1.,
.patternOpacity = background->paper().patternOpacity(),
};
}
void ChatTheme::cacheBackground() {
Expects(_cacheBackgroundTimer.has_value());
const auto now = crl::now();
if (now - _lastAreaChangeTime < kCacheBackgroundTimeout
&& QGuiApplication::mouseButtons() != 0) {
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
return;
}
cacheBackgroundNow();
}
void ChatTheme::cacheBackgroundNow() {
if (!_backgroundCachingRequest) {
if (const auto request = currentCacheRequest(_willCacheForArea)) {
cacheBackgroundAsync(request);
}
}
}
void ChatTheme::cacheBackgroundAsync(
const CacheBackgroundRequest &request,
Fn<void(CacheBackgroundResult&&)> done) {
_backgroundCachingRequest = request;
const auto weak = base::make_weak(this);
crl::async([=] {
if (!weak) {
return;
}
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
if (done) {
done(std::move(result));
} else if (const auto request = currentCacheRequest(
_willCacheForArea)) {
if (_backgroundCachingRequest != request) {
cacheBackgroundAsync(request);
} else {
_backgroundCachingRequest = {};
setCachedBackground(std::move(result));
}
}
});
});
}
void ChatTheme::setCachedBackground(CacheBackgroundResult &&cached) {
_backgroundNext = {};
const auto background = Background();
if (background->gradientForFill().isNull()
|| _backgroundState.now.pixmap.isNull()
|| anim::Disabled()) {
_backgroundFade.stop();
_backgroundState.shown = 1.;
_backgroundState.now = std::move(cached);
return;
}
// #TODO themes compose several transitions.
_backgroundState.was = std::move(_backgroundState.now);
_backgroundState.now = std::move(cached);
_backgroundState.shown = 0.;
const auto callback = [=] {
if (!_backgroundFade.animating()) {
_backgroundState.was = {};
_backgroundState.shown = 1.;
}
_repaintBackgroundRequests.fire({});
};
_backgroundFade.start(
callback,
0.,
1.,
kBackgroundFadeDuration);
}
void ChatTheme::clearCachedBackground() {
_backgroundState = {};
_backgroundAddRotation = 0;
_backgroundNext = {};
_backgroundFade.stop();
if (_cacheBackgroundTimer) {
_cacheBackgroundTimer->cancel();
}
_repaintBackgroundRequests.fire({});
}
rpl::producer<> ChatTheme::repaintBackgroundRequests() const {
return _repaintBackgroundRequests.events();
}
void ChatTheme::rotateComplexGradientBackground() {
if (!_backgroundFade.animating() && !_backgroundNext.image.isNull()) {
Background()->recacheGradientForFill(
std::move(_backgroundNext.gradient));
setCachedBackground(base::take(_backgroundNext));
}
}
} // namespace Window::Theme

View File

@@ -1,149 +0,0 @@
/*
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/effects/animations.h"
#include "base/timer.h"
#include "base/weak_ptr.h"
namespace Data {
struct CloudTheme;
} // namespace Data
namespace HistoryView {
struct PaintContext;
} // namespace HistoryView
namespace Ui {
struct BubblePattern;
} // namespace Ui
namespace Window::Theme {
struct CacheBackgroundRequest {
QImage prepared;
QImage preparedForTiled;
QSize area;
int gradientRotation = 0;
bool tile = false;
bool isPattern = false;
bool recreateGradient = false;
QImage gradient;
std::vector<QColor> gradientColors;
float64 gradientProgress = 1.;
float64 patternOpacity = 1.;
explicit operator bool() const {
return !prepared.isNull() || !gradient.isNull();
}
};
bool operator==(
const CacheBackgroundRequest &a,
const CacheBackgroundRequest &b);
bool operator!=(
const CacheBackgroundRequest &a,
const CacheBackgroundRequest &b);
struct CacheBackgroundResult {
QImage image;
QImage gradient;
QSize area;
int x = 0;
int y = 0;
};
struct CachedBackground {
CachedBackground() = default;
CachedBackground(CacheBackgroundResult &&result);
QPixmap pixmap;
QSize area;
int x = 0;
int y = 0;
};
struct BackgroundState {
CachedBackground was;
CachedBackground now;
float64 shown = 1.;
};
struct ChatThemeBackground {
QImage prepared;
QImage gradientForFill;
std::optional<QColor> colorForFill;
};
struct ChatThemeDescriptor {
Fn<void(style::palette&)> preparePalette;
Fn<ChatThemeBackground()> prepareBackground;
std::vector<QColor> backgroundColors;
};
class ChatTheme final : public base::has_weak_ptr {
public:
ChatTheme();
// Runs from background thread.
ChatTheme(const Data::CloudTheme &theme);
[[nodiscard]] uint64 key() const;
[[nodiscard]] not_null<const style::palette*> palette() const {
return _palette.get();
}
void setBackground(ChatThemeBackground);
void setBubblesBackground(QImage image);
const Ui::BubblePattern *bubblesBackgroundPattern() const {
return _bubblesBackgroundPattern.get();
}
[[nodiscard]] HistoryView::PaintContext preparePaintContext(
QRect viewport,
QRect clip);
[[nodiscard]] const BackgroundState &backgroundState(QSize area);
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
void rotateComplexGradientBackground();
private:
void cacheBackground();
void cacheBackgroundNow();
void cacheBackgroundAsync(
const CacheBackgroundRequest &request,
Fn<void(CacheBackgroundResult&&)> done = nullptr);
void clearCachedBackground();
void setCachedBackground(CacheBackgroundResult &&cached);
[[nodiscard]] CacheBackgroundRequest currentCacheRequest(
QSize area,
int addRotation = 0) const;
[[nodiscard]] bool readyForBackgroundRotation() const;
void generateNextBackgroundRotation();
uint64 _id = 0;
std::unique_ptr<style::palette> _palette;
BackgroundState _backgroundState;
Ui::Animations::Simple _backgroundFade;
CacheBackgroundRequest _backgroundCachingRequest;
CacheBackgroundResult _backgroundNext;
int _backgroundAddRotation = 0;
QSize _willCacheForArea;
crl::time _lastAreaChangeTime = 0;
std::optional<base::Timer> _cacheBackgroundTimer;
CachedBackground _bubblesBackground;
QImage _bubblesBackgroundPrepared;
std::unique_ptr<Ui::BubblePattern> _bubblesBackgroundPattern;
rpl::event_stream<> _repaintBackgroundRequests;
rpl::lifetime _lifetime;
};
} // namespace Window::Theme

View File

@@ -1448,10 +1448,22 @@ bool LoadFromFile(
const QString &path,
not_null<Instance*> out,
Cached *outCache,
not_null<QByteArray*> outContent) {
*outContent = readThemeContent(path);
QByteArray *outContent) {
const auto colorizer = ColorizerForTheme(path);
return LoadTheme(*outContent, colorizer, std::nullopt, outCache, out);
return LoadFromFile(path, out, outCache, outContent, colorizer);
}
bool LoadFromFile(
const QString &path,
not_null<Instance*> out,
Cached *outCache,
QByteArray *outContent,
const Colorizer &colorizer) {
const auto content = readThemeContent(path);
if (outContent) {
*outContent = content;
}
return LoadTheme(content, colorizer, std::nullopt, outCache, out);
}
bool LoadFromContent(
@@ -1523,34 +1535,6 @@ QImage PreprocessBackgroundImage(QImage image) {
return image;
}
BackgroundRects ComputeBackgroundRects(QSize fillSize, QSize imageSize) {
if (uint64(imageSize.width()) * fillSize.height() > uint64(imageSize.height()) * fillSize.width()) {
float64 pxsize = fillSize.height() / float64(imageSize.height());
int takewidth = qCeil(fillSize.width() / pxsize);
if (takewidth > imageSize.width()) {
takewidth = imageSize.width();
} else if ((imageSize.width() % 2) != (takewidth % 2)) {
++takewidth;
}
return {
.from = QRect((imageSize.width() - takewidth) / 2, 0, takewidth, imageSize.height()),
.to = QRect(int((fillSize.width() - takewidth * pxsize) / 2.), 0, qCeil(takewidth * pxsize), fillSize.height()),
};
} else {
float64 pxsize = fillSize.width() / float64(imageSize.width());
int takeheight = qCeil(fillSize.height() / pxsize);
if (takeheight > imageSize.height()) {
takeheight = imageSize.height();
} else if ((imageSize.height() % 2) != (takeheight % 2)) {
++takeheight;
}
return {
.from = QRect(0, (imageSize.height() - takeheight) / 2, imageSize.width(), takeheight),
.to = QRect(0, int((fillSize.height() - takeheight * pxsize) / 2.), fillSize.width(), qCeil(takeheight * pxsize)),
};
}
}
bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QLatin1String value)> callback) {
if (content.size() > kThemeSchemeSizeLimit) {
LOG(("Theme Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size()));

View File

@@ -25,6 +25,7 @@ inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
struct ParsedTheme;
struct Colorizer;
[[nodiscard]] bool IsEmbeddedTheme(const QString &path);
@@ -90,11 +91,18 @@ void Revert();
[[nodiscard]] QString EditingPalettePath();
// NB! This method looks to Core::App().settings() to get colorizer by 'file'.
bool LoadFromFile(
const QString &file,
const QString &path,
not_null<Instance*> out,
Cached *outCache,
not_null<QByteArray*> outContent);
QByteArray *outContent);
bool LoadFromFile(
const QString &path,
not_null<Instance*> out,
Cached *outCache,
QByteArray *outContent,
const Colorizer &colorizer);
bool LoadFromContent(
const QByteArray &content,
not_null<Instance*> out,
@@ -272,14 +280,6 @@ private:
[[nodiscard]] ChatBackground *Background();
struct BackgroundRects {
QRect from;
QRect to;
};
[[nodiscard]] BackgroundRects ComputeBackgroundRects(
QSize fillSize,
QSize imageSize);
bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QLatin1String value)> callback);
} // namespace Theme

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_options.h"
#include "ui/image/image_prepare.h"
#include "ui/emoji_config.h"
#include "ui/chat/chat_theme.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
#include "styles/style_media_view.h"
@@ -456,7 +457,9 @@ void Generator::paintHistoryBackground() {
PainterHighQualityEnabler hq(*_p);
auto fill = QSize(_topBar.width(), _body.height());
const auto rects = ComputeBackgroundRects(fill, background.size());
const auto rects = Ui::ComputeChatBackgroundRects(
fill,
background.size());
auto to = rects.to;
to.moveTop(to.top() + fromy);
to.moveTopLeft(to.topLeft() + _history.topLeft());

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_theme.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
#include "ui/chat/chat_theme.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/menu/menu.h"
@@ -990,7 +991,7 @@ void MainMenu::refreshBackground() {
const auto &paper = background->paper();
const auto &prepared = background->prepared();
const auto rects = Window::Theme::ComputeBackgroundRects(
const auto rects = Ui::ComputeChatBackgroundRects(
fill,
prepared.size());

View File

@@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "window/main_window.h"
#include "window/window_filters_menu.h"
#include "window/themes/window_chat_theme.h"
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "history/history.h"
@@ -43,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h"
#include "ui/delayed_activation.h"
#include "ui/chat/message_bubble.h"
#include "ui/chat/chat_theme.h"
#include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "calls/calls_instance.h" // Core::App().calls().inCall().
@@ -468,9 +468,18 @@ SessionController::SessionController(
_window->widget(),
this))
, _invitePeekTimer([=] { checkInvitePeek(); })
, _defaultChatTheme(std::make_shared<Theme::ChatTheme>()) {
, _defaultChatTheme(std::make_shared<Ui::ChatTheme>()) {
init();
pushDefaultChatBackground();
Theme::Background()->updates(
) | rpl::start_with_next([=](const Theme::BackgroundUpdate &update) {
if (update.type == Theme::BackgroundUpdate::Type::New
|| update.type == Theme::BackgroundUpdate::Type::Changed) {
pushDefaultChatBackground();
}
}, _lifetime);
if (Media::Player::instance()->pauseGifByRoundVideo()) {
enableGifPauseReason(GifPauseReason::RoundPlaying);
}
@@ -1310,7 +1319,7 @@ void SessionController::openDocument(
auto SessionController::cachedChatThemeValue(
const Data::CloudTheme &data)
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
const auto key = data.id;
if (!key || !data.paper || data.paper->backgroundColors().empty()) {
return rpl::single(_defaultChatTheme);
@@ -1326,23 +1335,75 @@ auto SessionController::cachedChatThemeValue(
return rpl::single(
_defaultChatTheme
) | rpl::then(_cachedThemesStream.events(
) | rpl::filter([=](const std::shared_ptr<Theme::ChatTheme> &theme) {
) | rpl::filter([=](const std::shared_ptr<Ui::ChatTheme> &theme) {
return (theme->key() == key);
}) | rpl::take(1));
}
void SessionController::pushDefaultChatBackground() {
const auto background = Theme::Background();
const auto &paper = background->paper();
_defaultChatTheme->setBackground({
.prepared = background->prepared(),
.preparedForTiled = background->preparedForTiled(),
.gradientForFill = background->gradientForFill(),
.colorForFill = background->colorForFill(),
.colors = paper.backgroundColors(),
.patternOpacity = paper.patternOpacity(),
.gradientRotation = paper.gradientRotation(),
.isPattern = paper.isPattern(),
.tile = background->tile(),
});
}
void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
Expects(data.id != 0);
using namespace Theme;
const auto key = data.id;
const auto dark = data.basedOnDark;
const auto accent = data.accentColor;
if (data.paper) {
const auto document = data.paper->document();
}
crl::async([this, data, weak = base::make_weak(this)] {
const auto preparePalette = [=](style::palette &palette) {
if (dark) {
const auto &embedded = Theme::EmbeddedThemes();
const auto i = ranges::find(
embedded,
EmbeddedType::Night,
&EmbeddedScheme::type);
Assert(i != end(embedded));
auto instance = Instance();
const auto loaded = LoadFromFile(
i->path,
&instance,
nullptr,
nullptr,
accent ? ColorizerFrom(*i, *accent) : Colorizer());
Assert(loaded);
palette = instance.palette;
} else {
// #TODO themes apply accent color to classic theme
}
};
const auto prepareBackground = [] {
return Ui::ChatThemeBackground{
};
};
auto descriptor = Ui::ChatThemeDescriptor{
.id = key,
.preparePalette = preparePalette,
.prepareBackground = prepareBackground,
};
crl::async([this, descriptor = std::move(descriptor), weak = base::make_weak(this)] {
crl::on_main(weak, [
this,
result = std::make_shared<Theme::ChatTheme>(data)
result = std::make_shared<Ui::ChatTheme>(std::move(descriptor))
]() mutable {
_customChatThemes.emplace(result->key(), result);
_cachedThemesStream.fire(std::move(result));

View File

@@ -25,10 +25,6 @@ namespace Adaptive {
enum class WindowLayout;
} // namespace Adaptive
namespace HistoryView {
struct PaintContext;
} // namespace HistoryView
namespace ChatHelpers {
class TabbedSelector;
} // namespace ChatHelpers
@@ -49,11 +45,9 @@ class FormController;
namespace Ui {
class LayerWidget;
enum class ReportReason;
} // namespace Ui
namespace Window::Theme {
class ChatTheme;
} // namespace Window::Theme
struct ChatPaintContext;
} // namespace Ui
namespace Data {
struct CloudTheme;
@@ -403,21 +397,21 @@ public:
[[nodiscard]] rpl::producer<> filtersMenuChanged() const;
[[nodiscard]] auto defaultChatTheme() const
-> const std::shared_ptr<Theme::ChatTheme> & {
-> const std::shared_ptr<Ui::ChatTheme> & {
return _defaultChatTheme;
}
[[nodiscard]] auto cachedChatThemeValue(
const Data::CloudTheme &data)
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>>;
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>>;
struct PaintContextArgs {
not_null<Theme::ChatTheme*> theme;
not_null<Ui::ChatTheme*> theme;
int visibleAreaTop = 0;
int visibleAreaTopGlobal = 0;
int visibleAreaWidth = 0;
QRect clip;
};
[[nodiscard]] HistoryView::PaintContext preparePaintContext(
[[nodiscard]] Ui::ChatPaintContext preparePaintContext(
PaintContextArgs &&args);
rpl::lifetime &lifetime() {
@@ -449,6 +443,7 @@ private:
void checkInvitePeek();
void pushDefaultChatBackground();
void cacheChatTheme(const Data::CloudTheme &data);
const not_null<Controller*> _window;
@@ -478,11 +473,9 @@ private:
rpl::event_stream<> _filtersMenuChanged;
std::shared_ptr<Theme::ChatTheme> _defaultChatTheme;
base::flat_map<
uint64,
std::shared_ptr<Theme::ChatTheme>> _customChatThemes;
rpl::event_stream<std::shared_ptr<Theme::ChatTheme>> _cachedThemesStream;
std::shared_ptr<Ui::ChatTheme> _defaultChatTheme;
base::flat_map<uint64, std::shared_ptr<Ui::ChatTheme>> _customChatThemes;
rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream;
rpl::lifetime _lifetime;