Add LIVE badge for outlined userpics.

This commit is contained in:
John Preston
2025-11-06 15:17:05 +04:00
parent 1e197ae66c
commit 0d11cb603f
9 changed files with 148 additions and 18 deletions
+1 -2
View File
@@ -1006,9 +1006,8 @@ contactsWithStories: PeerList(peerListBox) {
statusPosition: point(70px, 27px);
checkbox: RoundImageCheckbox(defaultPeerListCheckbox) {
selectExtendTwice: 1px;
imageRadius: 21px;
imageSmallRadius: 19px;
imageSmallRadius: 18px;
check: RoundCheckbox(defaultPeerListCheck) {
size: 0px;
}
+3 -2
View File
@@ -983,10 +983,11 @@ void PeerListRow::setCheckedInternal(bool checked, anim::type animated) {
}
void PeerListRow::setCustomizedCheckSegments(
std::vector<Ui::OutlineSegment> segments) {
std::vector<Ui::OutlineSegment> segments,
bool liveBadge) {
Expects(_checkbox != nullptr);
_checkbox->setCustomizedSegments(std::move(segments));
_checkbox->setCustomizedSegments(std::move(segments), liveBadge);
}
void PeerListRow::finishCheckedAnimation() {
+2 -1
View File
@@ -223,7 +223,8 @@ public:
setCheckedInternal(checked, animated);
}
void setCustomizedCheckSegments(
std::vector<Ui::OutlineSegment> segments);
std::vector<Ui::OutlineSegment> segments,
bool liveBadge);
void setHidden(bool hidden) {
_hidden = hidden;
}
@@ -626,7 +626,8 @@ void PeerListStories::applyForRow(
_delegate->peerListSetRowChecked(row, counts.count > 0);
if (counts.count > 0) {
row->setCustomizedCheckSegments(
PeerListStoriesSegments(counts, _unreadBrush));
PeerListStoriesSegments(counts, _unreadBrush),
counts.videoStream);
}
}
+3 -3
View File
@@ -1707,9 +1707,9 @@ groupCallMessageBadge: RoundButton(defaultActiveButton) {
textBg: attentionButtonFg;
textBgOver: attentionButtonFg;
width: -6px;
height: 12px;
radius: 6px;
textTop: 1px;
height: 11px;
radius: 5px;
textTop: 0px;
style: TextStyle(semiboldTextStyle) {
font: font(8px semibold);
}
@@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/credits_graphics.h"
#include "ui/effects/outline_segments.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/round_checkbox.h"
#include "ui/image/image_prepare.h"
#include "ui/text/custom_emoji_text_badge.h"
#include "ui/text/format_values.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
@@ -30,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "base/unixtime.h"
#include "styles/style_calls.h"
#include "styles/style_dialogs.h"
namespace Dialogs {
@@ -481,6 +484,10 @@ void Row::PaintCornerBadgeFrame(
} else {
Ui::PaintOutlineSegments(q, outline, segments);
}
if (data->storiesHasVideoStream) {
Ui::PaintLiveBadge(q, 0, 0, photoSize);
}
}
if (subscribed) {
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/event_filter.h"
#include "base/qt_signal_producer.h"
#include "lang/lang_keys.h"
#include "ui/effects/round_checkbox.h"
#include "ui/effects/outline_segments.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
@@ -642,6 +643,19 @@ void List::paint(
itemFull->element.thumbnail->image(size));
}
}
if (const auto full = single.itemFull) {
if (full->element.hasVideoStream && expandRatio > 0.) {
p.setOpacity(expandRatio);
const auto skip = std::ceil(line + lineRead);
Ui::PaintLiveBadge(
p,
std::ceil(userpic.x() - skip),
std::ceil(userpic.y() - skip),
std::ceil(userpic.width() + 2 * skip),
st::windowBg->c);
}
}
p.setOpacity(1.);
});
}
@@ -7,10 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/effects/round_checkbox.h"
#include "lang/lang_keys.h"
#include "ui/rp_widget.h"
#include "ui/ui_utility.h"
#include "ui/painter.h"
#include "ui/effects/outline_segments.h"
#include "styles/style_calls.h"
#include "styles/style_dialogs.h"
#include "styles/style_widgets.h"
#include <QtCore/QCoreApplication>
@@ -376,13 +379,50 @@ RoundImageCheckbox::RoundImageCheckbox(RoundImageCheckbox&&) = default;
RoundImageCheckbox::~RoundImageCheckbox() = default;
void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
void RoundImageCheckbox::paint(
Painter &p,
int x,
int y,
int outerWidth) const {
if (_liveBadge) {
const auto ratio = style::DevicePixelRatio();
const auto added = _st.selectWidth;
const auto cacheSize = (added + _st.imageRadius) * 2;
const auto fullCacheSize = cacheSize * ratio;
if (_liveBadgeCache.width() != fullCacheSize) {
_liveBadgeCache = QImage(
QSize(fullCacheSize, fullCacheSize),
QImage::Format_ARGB32_Premultiplied);
_liveBadgeCache.setDevicePixelRatio(ratio);
}
_liveBadgeCache.fill(Qt::transparent);
auto q = Painter(&_liveBadgeCache);
paintFrame(q, added, added, cacheSize);
q.end();
p.drawImage(x - added, y - added, _liveBadgeCache);
} else {
paintFrame(p, x, y, outerWidth);
}
}
void RoundImageCheckbox::paintFrame(
Painter &p,
int x,
int y,
int outerWidth) const {
auto selectionLevel = _selection.value(checked() ? 1. : 0.);
if (_selection.animating()) {
auto userpicRadius = qRound(kWideScale * (_st.imageRadius + (_st.imageSmallRadius - _st.imageRadius) * selectionLevel));
auto userpicRadius = qRound(kWideScale
* (_st.imageRadius + (_st.imageSmallRadius - _st.imageRadius)
* selectionLevel));
auto userpicShift = kWideScale * _st.imageRadius - userpicRadius;
auto userpicLeft = x - (kWideScale - 1) * _st.imageRadius + userpicShift;
auto userpicTop = y - (kWideScale - 1) * _st.imageRadius + userpicShift;
auto userpicLeft = x
- ((kWideScale - 1) * _st.imageRadius)
+ userpicShift;
auto userpicTop = y
- ((kWideScale - 1) * _st.imageRadius)
+ userpicShift;
auto to = QRect(userpicLeft, userpicTop, userpicRadius * 2, userpicRadius * 2);
auto from = QRect(QPoint(0, 0), _wideCache.size());
@@ -426,6 +466,11 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
} else {
PaintOutlineSegments(p, outline, _segments);
}
if (_liveBadge) {
PaintLiveBadge(p, x, y, _st.imageRadius * 2);
}
p.setOpacity(1.);
}
if (_st.check.size > 0) {
@@ -493,15 +538,59 @@ void RoundImageCheckbox::setColorOverride(std::optional<QBrush> fg) {
if (fg) {
setCustomizedSegments({
{ .brush = *fg, .width = float64(_st.selectWidth) }
});
}, false);
} else {
setCustomizedSegments({});
setCustomizedSegments({}, false);
}
}
void RoundImageCheckbox::setCustomizedSegments(
std::vector<Ui::OutlineSegment> segments) {
std::vector<Ui::OutlineSegment> segments,
bool liveBadge) {
_segments = std::move(segments);
_liveBadge = liveBadge;
}
void PaintLiveBadge(
QPainter &p,
int x,
int y,
int photoSize,
std::optional<QColor> outline) {
const auto &st = st::groupCallMessageBadge;
const auto text = tr::lng_video_stream_live(tr::now);
auto string = Ui::Text::String(st.style, text);
const auto size = QSize(string.maxWidth(), string.minHeight());
const auto full = QSize(
(st.width < 0) ? (size.width() - st.width) : st.width,
st.height);
const auto left = x + (photoSize - full.width()) / 2;
const auto top = y + photoSize - full.height();
const auto stroke = st::dialogsStories.lineTwice / 2.;
auto pen = QPen(outline.value_or(QColor(Qt::transparent)));
pen.setWidthF(stroke);
const auto half = stroke / 2.;
if (!outline) {
p.setCompositionMode(QPainter::CompositionMode_Source);
}
auto hq = PainterHighQualityEnabler(p);
p.setPen(pen);
p.setBrush(st.textBg);
const auto r = st.radius + half;
const auto rect = QRectF(left, top, full.width(), full.height());
const auto sub = QMarginsF(half, half, half, half);
p.drawRoundedRect(rect.marginsAdded(sub), r, r);
if (!outline) {
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
const auto textLeft = (full.width() - size.width()) / 2;
p.setPen(st.textFg);
string.draw(p, { .position = { left + textLeft, top + st.textTop } });
}
} // namespace Ui
@@ -54,7 +54,12 @@ private:
class RoundImageCheckbox {
public:
using PaintRoundImage = Fn<void(Painter &p, int x, int y, int outerWidth, int size)>;
using PaintRoundImage = Fn<void(
Painter &p,
int x,
int y,
int outerWidth,
int size)>;
RoundImageCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback,
@@ -67,7 +72,9 @@ public:
float64 checkedAnimationRatio() const;
void setColorOverride(std::optional<QBrush> fg);
void setCustomizedSegments(std::vector<OutlineSegment> segments);
void setCustomizedSegments(
std::vector<OutlineSegment> segments,
bool liveBadge);
bool checked() const {
return _check.checked();
@@ -82,6 +89,7 @@ public:
private:
void prepareWideCache();
void paintFrame(Painter &p, int x, int y, int outerWidth) const;
const style::RoundImageCheckbox &_st;
Fn<void()> _updateCallback;
@@ -96,6 +104,16 @@ private:
//std::optional<QBrush> _fgOverride;
std::vector<OutlineSegment> _segments;
mutable QImage _liveBadgeCache;
bool _liveBadge = false;
};
void PaintLiveBadge(
QPainter &p,
int x,
int y,
int photoSize,
std::optional<QColor> outline = std::nullopt);
} // namespace Ui