Added profile color support to calls panel.

This commit is contained in:
23rd
2025-12-09 02:19:48 +03:00
parent 52cc76636c
commit 4a43dfd091
5 changed files with 341 additions and 2 deletions
+2
View File
@@ -407,6 +407,8 @@ PRIVATE
calls/calls_instance.h
calls/calls_panel.cpp
calls/calls_panel.h
calls/calls_panel_background.cpp
calls/calls_panel_background.h
calls/calls_signal_bars.cpp
calls/calls_signal_bars.h
calls/calls_top_bar.cpp
+35 -2
View File
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_panel.h"
#include "boxes/peers/replace_boost_box.h" // CreateUserpicsWithMoreBadge
#include "calls/calls_panel_background.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
@@ -570,6 +571,13 @@ void Panel::reinitWithCall(Call *call) {
_user = _call->user();
_background = std::make_unique<PanelBackground>(
_user,
[=] {
updateTextColors();
widget()->update();
});
_call->confereceSupportedValue(
) | rpl::start_with_next([=](bool supported) {
_conferenceSupported = supported;
@@ -763,6 +771,7 @@ void Panel::reinitWithCall(Call *call) {
_name->setText(_user->name());
updateStatusText(_call->state());
updateTextColors();
_answerHangupRedial->raise();
_decline->raise();
@@ -1239,9 +1248,21 @@ void Panel::paint(QRect clip) {
if (!_incoming->widget()->isHidden()) {
region = region.subtracted(QRegion(_incoming->widget()->geometry()));
}
for (const auto &rect : region) {
p.fillRect(rect, st::callBgOpaque);
if (_background) {
_background->paint(
p,
widget()->size(),
_bodyTop,
_bodySt->photoTop,
_bodySt->photoSize,
region);
} else {
for (const auto &rect : region) {
p.fillRect(rect, st::callBgOpaque);
}
}
if (_incoming && _incoming->widget()->isHidden()) {
_call->videoIncoming()->markFrameShown();
}
@@ -1403,6 +1424,18 @@ void Panel::updateStatusText(State state) {
updateStatusGeometry();
}
void Panel::updateTextColors() {
if (!_background) {
_name->setTextColorOverride(std::nullopt);
_status->setTextColorOverride(std::nullopt);
return;
}
_name->setTextColorOverride(
_background->textColorOverride(st::callName.textFg));
_status->setTextColorOverride(
_background->textColorOverride(st::callStatus.textFg));
}
void Panel::startDurationUpdateTimer(crl::time currentDuration) {
auto msTillNextSecond = 1000 - (currentDuration % 1000);
_updateDurationTimer.callOnce(msTillNextSecond + 5);
+4
View File
@@ -64,6 +64,7 @@ class Window;
class Userpic;
class SignalBars;
class VideoBubble;
class PanelBackground;
struct DeviceSelection;
struct ConferencePanelMigration;
@@ -140,6 +141,7 @@ private:
void stateChanged(State state);
void showControls();
void updateStatusText(State state);
void updateTextColors();
void startDurationUpdateTimer(crl::time currentDuration);
void setIncomingSize(QSize size);
void refreshIncomingGeometry();
@@ -214,6 +216,8 @@ private:
rpl::event_stream<bool> _startOutgoingRequests;
std::unique_ptr<PanelBackground> _background;
rpl::lifetime _lifetime;
};
@@ -0,0 +1,225 @@
/*
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 "calls/calls_panel_background.h"
#include "apiwrap.h"
#include "api/api_peer_colors.h"
#include "data/data_changes.h"
#include "data/data_document.h"
#include "data/data_emoji_statuses.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h"
#include "ui/color_contrast.h"
#include "ui/top_background_gradient.h"
#include "styles/style_calls.h"
namespace Calls {
PanelBackground::PanelBackground(
not_null<PeerData*> peer,
Fn<void()> updateCallback)
: _peer(peer)
, _updateCallback(std::move(updateCallback)) {
updateColors();
updateEmojiId();
_peer->session().changes().peerFlagsValue(
_peer,
Data::PeerUpdate::Flag::ColorProfile
| Data::PeerUpdate::Flag::EmojiStatus
) | rpl::start_with_next([=] {
updateColors();
_brushSize = QSize();
if (_updateCallback) {
_updateCallback();
}
}, _lifetime);
_peer->session().changes().peerFlagsValue(
_peer,
Data::PeerUpdate::Flag::BackgroundEmoji
| Data::PeerUpdate::Flag::EmojiStatus
) | rpl::start_with_next([=] {
updateEmojiId();
if (_updateCallback) {
_updateCallback();
}
}, _lifetime);
}
void PanelBackground::paint(
QPainter &p,
QSize widgetSize,
int bodyTop,
int photoTop,
int photoSize,
const QRegion &region) {
if (!_colors || _colors->bg.empty()) {
for (const auto &rect : region) {
p.fillRect(rect, st::callBgOpaque);
}
return;
}
const auto &colors = _colors->bg;
if (colors.size() == 1) {
for (const auto &rect : region) {
p.fillRect(rect, colors.front());
}
} else {
const auto center = QPoint(
widgetSize.width() / 2,
bodyTop + photoTop + photoSize / 2);
const auto radius = std::max(
std::hypot(center.x(), center.y()),
std::hypot(widgetSize.width() - center.x(),
widgetSize.height() - center.y()));
if (_brushSize != widgetSize) {
updateBrush(widgetSize, center, radius, colors);
}
for (const auto &rect : region) {
p.fillRect(rect, _brush);
}
}
const auto emojiId = _currentEmojiId;
if (!emojiId) {
_emoji = nullptr;
_cachedImage = QImage();
_cachedEmojiId = 0;
return;
}
const auto userpicX = (widgetSize.width() - photoSize) / 2;
const auto userpicY = bodyTop + photoTop;
const auto padding = photoSize;
const auto patternRect = QRect(
userpicX - padding,
userpicY - padding / 2,
photoSize + padding * 2,
photoSize + padding);
if (!_emoji
|| _emoji->entityData()
!= Data::SerializeCustomEmojiId(emojiId)) {
const auto document = _peer->owner().document(emojiId);
_emoji = document->owner().customEmojiManager().create(
document,
[=] {
_cachedImage = QImage();
if (_updateCallback) {
_updateCallback();
}
},
Data::CustomEmojiSizeTag::Large);
_cachedImage = QImage();
_cachedEmojiId = 0;
}
if (_emoji && _emoji->ready()) {
if (_cachedImage.isNull()
|| _cachedRect != patternRect
|| _cachedEmojiId != emojiId) {
renderPattern(patternRect, emojiId);
}
p.drawImage(patternRect, _cachedImage);
}
}
void PanelBackground::updateBrush(
QSize widgetSize,
QPoint center,
float64 radius,
const std::vector<QColor> &colors) {
_brushSize = widgetSize;
auto gradient = QRadialGradient(center, radius);
const auto step = 1.0 / (colors.size() - 1);
for (auto i = 0; i < colors.size(); ++i) {
gradient.setColorAt(i * step, colors[colors.size() - 1 - i]);
}
_brush = QBrush(gradient);
}
void PanelBackground::renderPattern(const QRect &rect, DocumentId emojiId) {
const auto ratio = style::DevicePixelRatio();
_cachedImage = QImage(
rect.size() * ratio,
QImage::Format_ARGB32_Premultiplied);
_cachedImage.setDevicePixelRatio(ratio);
_cachedImage.fill(Qt::transparent);
auto painter = QPainter(&_cachedImage);
const auto patternColor = QColor(0, 0, 0, int(0.6 * 255));
const auto &points = Ui::PatternBgPoints();
const auto localRect = QRect(QPoint(), rect.size());
Ui::PaintBgPoints(
painter,
points,
_cache,
_emoji.get(),
patternColor,
localRect,
1.);
_cachedRect = rect;
_cachedEmojiId = emojiId;
}
void PanelBackground::updateColors() {
const auto collectible = _peer->emojiStatusId().collectible;
if (collectible && collectible->centerColor.isValid()) {
_colors = Data::ColorProfileSet{
.bg = { collectible->edgeColor, collectible->centerColor },
};
} else {
_colors = _peer->session().api().peerColors().colorProfileFor(_peer);
}
}
void PanelBackground::updateEmojiId() {
const auto collectible = _peer->emojiStatusId().collectible;
_currentEmojiId = (collectible && collectible->patternDocumentId)
? collectible->patternDocumentId
: _peer->profileBackgroundEmojiId();
}
std::optional<QColor> PanelBackground::edgeColor() const {
const auto collectible = _peer->emojiStatusId().collectible;
if (collectible && collectible->edgeColor.isValid()) {
return collectible->edgeColor;
}
if (_colors && !_colors->bg.empty()) {
return _colors->bg.front();
}
return std::nullopt;
}
std::optional<QColor> PanelBackground::textColorOverride(
const style::color &defaultColor) const {
const auto collectible = _peer->emojiStatusId().collectible;
if (collectible && collectible->textColor.isValid()) {
return collectible->textColor;
}
const auto edge = edgeColor();
if (!edge) {
return std::nullopt;
}
constexpr auto kMinContrast = 5.5;
if (kMinContrast > Ui::CountContrast(defaultColor->c, *edge)) {
return st::groupCallMembersFg->c;
}
return std::nullopt;
}
rpl::lifetime &PanelBackground::lifetime() {
return _lifetime;
}
} // namespace Calls
@@ -0,0 +1,75 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "data/data_peer_colors.h"
class DocumentData;
class PeerData;
namespace Data {
class CustomEmoji;
} // namespace Data
namespace Ui {
namespace Text {
class CustomEmoji;
} // namespace Text
} // namespace Ui
namespace Calls {
class PanelBackground final {
public:
explicit PanelBackground(
not_null<PeerData*> peer,
Fn<void()> updateCallback);
void paint(
QPainter &p,
QSize widgetSize,
int bodyTop,
int photoTop,
int photoSize,
const QRegion &region);
[[nodiscard]] rpl::lifetime &lifetime();
[[nodiscard]] std::optional<QColor> textColorOverride(
const style::color &defaultColor) const;
private:
void updateBrush(
QSize widgetSize,
QPoint center,
float64 radius,
const std::vector<QColor> &colors);
void renderPattern(const QRect &rect, DocumentId emojiId);
void updateColors();
void updateEmojiId();
[[nodiscard]] std::optional<QColor> edgeColor() const;
const not_null<PeerData*> _peer;
const Fn<void()> _updateCallback;
QBrush _brush;
QSize _brushSize;
std::optional<Data::ColorProfileSet> _colors;
std::unique_ptr<Ui::Text::CustomEmoji> _emoji;
base::flat_map<float64, QImage> _cache;
QImage _cachedImage;
QRect _cachedRect;
DocumentId _cachedEmojiId = 0;
DocumentId _currentEmojiId = 0;
rpl::lifetime _lifetime;
};
} // namespace Calls