init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
596
Telegram/SourceFiles/ui/effects/round_checkbox.cpp
Normal file
596
Telegram/SourceFiles/ui/effects/round_checkbox.cpp
Normal file
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
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/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>
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kAnimationTimerDelta = crl::time(7);
|
||||
constexpr auto kWideScale = 3;
|
||||
|
||||
[[nodiscard]] int CountFramesCount(const style::RoundCheckbox *st) {
|
||||
return (st->duration / kAnimationTimerDelta) + 1;
|
||||
}
|
||||
|
||||
class CheckCaches : public QObject {
|
||||
public:
|
||||
CheckCaches(QObject *parent) : QObject(parent) {
|
||||
Expects(parent != nullptr);
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::on_next([=] {
|
||||
_data.clear();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
QPixmap frame(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive,
|
||||
float64 progress);
|
||||
|
||||
private:
|
||||
struct Frames {
|
||||
bool displayInactive = false;
|
||||
std::vector<QPixmap> list;
|
||||
QPixmap outerWide;
|
||||
QPixmap inner;
|
||||
QPixmap check;
|
||||
};
|
||||
|
||||
Frames &framesForStyle(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive);
|
||||
void prepareFramesData(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive,
|
||||
Frames &frames);
|
||||
QPixmap paintFrame(
|
||||
const style::RoundCheckbox *st,
|
||||
const Frames &frames,
|
||||
float64 progress);
|
||||
|
||||
std::map<const style::RoundCheckbox *, Frames> _data;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
QPixmap PrepareOuterWide(const style::RoundCheckbox *st) {
|
||||
const auto size = st->size;
|
||||
const auto wideSize = size * kWideScale;
|
||||
auto result = QImage(
|
||||
QSize(wideSize, wideSize) * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
auto p = QPainter(&result);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st->border);
|
||||
const auto half = st->width / 2.;
|
||||
p.drawEllipse(QRectF(
|
||||
(wideSize - size) / 2 - half,
|
||||
(wideSize - size) / 2 - half,
|
||||
size + 2. * half,
|
||||
size + 2. * half));
|
||||
}
|
||||
return Ui::PixmapFromImage(std::move(result));
|
||||
}
|
||||
|
||||
QPixmap PrepareInner(const style::RoundCheckbox *st, bool displayInactive) {
|
||||
const auto size = st->size;
|
||||
auto result = QImage(
|
||||
QSize(size, size) * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
auto p = QPainter(&result);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st->bgActive);
|
||||
const auto half = st->width / 2.;
|
||||
p.drawEllipse(QRectF(
|
||||
displayInactive ? 0. : half,
|
||||
displayInactive ? 0. : half,
|
||||
size - (displayInactive ? 0. : 2. * half),
|
||||
size - (displayInactive ? 0. : 2. * half)));
|
||||
}
|
||||
return Ui::PixmapFromImage(std::move(result));
|
||||
}
|
||||
|
||||
QPixmap PrepareCheck(const style::RoundCheckbox *st) {
|
||||
const auto size = st->size;
|
||||
auto result = QImage(
|
||||
QSize(size, size) * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
auto p = QPainter(&result);
|
||||
st->check.paint(p, 0, 0, size);
|
||||
}
|
||||
return Ui::PixmapFromImage(std::move(result));
|
||||
}
|
||||
|
||||
QRect WideDestRect(
|
||||
const style::RoundCheckbox *st,
|
||||
int x,
|
||||
int y,
|
||||
float64 scale) {
|
||||
auto iconSizeFull = kWideScale * st->size;
|
||||
auto iconSize = qRound(iconSizeFull * scale);
|
||||
if (iconSize % 2 != iconSizeFull % 2) {
|
||||
++iconSize;
|
||||
}
|
||||
auto iconShift = (iconSizeFull - iconSize) / 2;
|
||||
auto iconLeft = x - (kWideScale - 1) * st->size / 2 + iconShift;
|
||||
auto iconTop = y - (kWideScale - 1) * st->size / 2 + iconShift;
|
||||
return QRect(iconLeft, iconTop, iconSize, iconSize);
|
||||
}
|
||||
|
||||
CheckCaches::Frames &CheckCaches::framesForStyle(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive) {
|
||||
auto i = _data.find(st);
|
||||
if (i == _data.end()) {
|
||||
i = _data.emplace(st, Frames()).first;
|
||||
prepareFramesData(st, displayInactive, i->second);
|
||||
} else if (i->second.displayInactive != displayInactive) {
|
||||
i->second = Frames();
|
||||
prepareFramesData(st, displayInactive, i->second);
|
||||
}
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void CheckCaches::prepareFramesData(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive,
|
||||
Frames &frames) {
|
||||
frames.list.resize(CountFramesCount(st));
|
||||
frames.displayInactive = displayInactive;
|
||||
|
||||
if (!frames.displayInactive) {
|
||||
frames.outerWide = PrepareOuterWide(st);
|
||||
}
|
||||
frames.inner = PrepareInner(st, frames.displayInactive);
|
||||
frames.check = PrepareCheck(st);
|
||||
}
|
||||
|
||||
QPixmap CheckCaches::frame(
|
||||
const style::RoundCheckbox *st,
|
||||
bool displayInactive,
|
||||
float64 progress) {
|
||||
auto &frames = framesForStyle(st, displayInactive);
|
||||
|
||||
const auto frameCount = int(frames.list.size());
|
||||
const auto frameIndex = int(base::SafeRound(progress * (frameCount - 1)));
|
||||
Assert(frameIndex >= 0 && frameIndex < frameCount);
|
||||
|
||||
if (!frames.list[frameIndex]) {
|
||||
const auto frameProgress = frameIndex / float64(frameCount - 1);
|
||||
frames.list[frameIndex] = paintFrame(st, frames, frameProgress);
|
||||
}
|
||||
return frames.list[frameIndex];
|
||||
}
|
||||
|
||||
QPixmap CheckCaches::paintFrame(
|
||||
const style::RoundCheckbox *st,
|
||||
const Frames &frames,
|
||||
float64 progress) {
|
||||
const auto size = st->size;
|
||||
const auto wideSize = size * kWideScale;
|
||||
const auto skip = (wideSize - size) / 2;
|
||||
auto result = QImage(wideSize * style::DevicePixelRatio(), wideSize * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
result.fill(Qt::transparent);
|
||||
|
||||
const auto roundProgress = (progress >= st->bgDuration)
|
||||
? 1.
|
||||
: (progress / st->bgDuration);
|
||||
const auto checkProgress = (1. - progress >= st->fgDuration)
|
||||
? 0.
|
||||
: (1. - (1. - progress) / st->fgDuration);
|
||||
{
|
||||
auto p = QPainter(&result);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
if (!frames.displayInactive) {
|
||||
const auto outerMaxScale = (size - st->width) / float64(size);
|
||||
const auto outerScale = roundProgress
|
||||
+ (1. - roundProgress) * outerMaxScale;
|
||||
const auto outerTo = WideDestRect(st, skip, skip, outerScale);
|
||||
const auto outerFrom = QRect(
|
||||
QPoint(0, 0),
|
||||
QSize(wideSize, wideSize) * style::DevicePixelRatio());
|
||||
p.drawPixmap(outerTo, frames.outerWide, outerFrom);
|
||||
}
|
||||
p.drawPixmap(skip, skip, frames.inner);
|
||||
|
||||
const auto divider = checkProgress * st->size;
|
||||
const auto checkTo = QRect(skip, skip, divider, st->size);
|
||||
const auto checkFrom = QRect(
|
||||
QPoint(0, 0),
|
||||
QSize(divider, st->size) * style::DevicePixelRatio());
|
||||
p.drawPixmap(checkTo, frames.check, checkFrom);
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::transparent);
|
||||
const auto remove = size * (1. - roundProgress);
|
||||
p.drawEllipse(QRectF(
|
||||
(wideSize - remove) / 2.,
|
||||
(wideSize - remove) / 2.,
|
||||
remove,
|
||||
remove));
|
||||
}
|
||||
return Ui::PixmapFromImage(std::move(result));
|
||||
}
|
||||
|
||||
CheckCaches *FrameCaches() {
|
||||
static QPointer<CheckCaches> Instance;
|
||||
|
||||
if (const auto instance = Instance.data()) {
|
||||
return instance;
|
||||
}
|
||||
const auto result = new CheckCaches(QCoreApplication::instance());
|
||||
Instance = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RoundCheckbox::RoundCheckbox(const style::RoundCheckbox &st, Fn<void()> updateCallback)
|
||||
: _st(st)
|
||||
, _updateCallback(updateCallback) {
|
||||
}
|
||||
|
||||
void RoundCheckbox::paint(QPainter &p, int x, int y, int outerWidth, float64 masterScale) const {
|
||||
if (!_st.size
|
||||
|| (!_checkedProgress.animating()
|
||||
&& !_checked
|
||||
&& !_displayInactive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cacheSize = kWideScale * _st.size * style::DevicePixelRatio();
|
||||
auto cacheFrom = QRect(0, 0, cacheSize, cacheSize);
|
||||
auto inactiveTo = WideDestRect(&_st, x, y, masterScale);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
if (!_inactiveCacheBg.isNull()) {
|
||||
p.drawPixmap(inactiveTo, _inactiveCacheBg, cacheFrom);
|
||||
}
|
||||
|
||||
const auto progress = _checkedProgress.value(_checked ? 1. : 0.);
|
||||
if (progress > 0.) {
|
||||
auto frame = FrameCaches()->frame(&_st, _displayInactive, progress);
|
||||
p.drawPixmap(inactiveTo, frame, cacheFrom);
|
||||
}
|
||||
|
||||
if (!_inactiveCacheFg.isNull()) {
|
||||
p.drawPixmap(inactiveTo, _inactiveCacheFg, cacheFrom);
|
||||
}
|
||||
}
|
||||
|
||||
void RoundCheckbox::setChecked(bool newChecked, anim::type animated) {
|
||||
if (_checked == newChecked) {
|
||||
if (animated == anim::type::instant) {
|
||||
_checkedProgress.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
_checked = newChecked;
|
||||
if (animated == anim::type::normal) {
|
||||
_checkedProgress.start(
|
||||
_updateCallback,
|
||||
_checked ? 0. : 1.,
|
||||
_checked ? 1. : 0.,
|
||||
_st.duration,
|
||||
anim::linear);
|
||||
} else {
|
||||
_checkedProgress.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void RoundCheckbox::finishAnimating() {
|
||||
_checkedProgress.stop();
|
||||
}
|
||||
|
||||
void RoundCheckbox::invalidateCache() {
|
||||
if (!_inactiveCacheBg.isNull() || !_inactiveCacheFg.isNull()) {
|
||||
prepareInactiveCache();
|
||||
}
|
||||
}
|
||||
|
||||
void RoundCheckbox::setDisplayInactive(bool displayInactive) {
|
||||
if (_displayInactive != displayInactive) {
|
||||
_displayInactive = displayInactive;
|
||||
if (_displayInactive) {
|
||||
prepareInactiveCache();
|
||||
} else {
|
||||
_inactiveCacheBg = _inactiveCacheFg = QPixmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RoundCheckbox::prepareInactiveCache() {
|
||||
auto wideSize = _st.size * kWideScale;
|
||||
auto ellipse = QRect((wideSize - _st.size) / 2, (wideSize - _st.size) / 2, _st.size, _st.size);
|
||||
|
||||
auto cacheBg = QImage(wideSize * style::DevicePixelRatio(), wideSize * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
|
||||
cacheBg.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
cacheBg.fill(Qt::transparent);
|
||||
auto cacheFg = cacheBg;
|
||||
if (_st.bgInactive) {
|
||||
auto p = QPainter(&cacheBg);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_st.bgInactive);
|
||||
p.drawEllipse(ellipse);
|
||||
}
|
||||
_inactiveCacheBg = Ui::PixmapFromImage(std::move(cacheBg));
|
||||
|
||||
{
|
||||
auto p = QPainter(&cacheFg);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
auto pen = _st.border->p;
|
||||
pen.setWidth(_st.width);
|
||||
p.setPen(pen);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawEllipse(ellipse);
|
||||
}
|
||||
_inactiveCacheFg = Ui::PixmapFromImage(std::move(cacheFg));
|
||||
}
|
||||
|
||||
RoundImageCheckbox::RoundImageCheckbox(
|
||||
const style::RoundImageCheckbox &st,
|
||||
Fn<void()> updateCallback,
|
||||
PaintRoundImage &&paintRoundImage,
|
||||
Fn<std::optional<int>(int size)> roundingRadius)
|
||||
: _st(st)
|
||||
, _updateCallback(updateCallback)
|
||||
, _paintRoundImage(std::move(paintRoundImage))
|
||||
, _roundingRadius(std::move(roundingRadius))
|
||||
, _check(_st.check, _updateCallback) {
|
||||
}
|
||||
|
||||
RoundImageCheckbox::RoundImageCheckbox(RoundImageCheckbox&&) = default;
|
||||
|
||||
RoundImageCheckbox::~RoundImageCheckbox() = default;
|
||||
|
||||
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 userpicShift = kWideScale * _st.imageRadius - userpicRadius;
|
||||
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());
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawPixmapLeft(to, outerWidth, _wideCache, from);
|
||||
} else {
|
||||
auto userpicRadius = checked() ? _st.imageSmallRadius : _st.imageRadius;
|
||||
auto userpicShift = _st.imageRadius - userpicRadius;
|
||||
auto userpicLeft = x + userpicShift;
|
||||
auto userpicTop = y + userpicShift;
|
||||
_paintRoundImage(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
}
|
||||
|
||||
if (selectionLevel > 0) {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setOpacity(std::clamp(selectionLevel, 0., 1.));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
const auto segments = int(_segments.size());
|
||||
const auto rect = style::rtlrect(
|
||||
x,
|
||||
y,
|
||||
_st.imageRadius * 2,
|
||||
_st.imageRadius * 2,
|
||||
outerWidth);
|
||||
const auto add = _st.selectExtendTwice / 2.;
|
||||
const auto outline = QRectF(rect).marginsAdded({
|
||||
add, add, add, add });
|
||||
if (segments < 2) {
|
||||
const auto radius = _roundingRadius
|
||||
? _roundingRadius(_st.imageRadius * 2)
|
||||
: std::optional<int>();
|
||||
const auto pen = QPen(
|
||||
segments ? _segments.front().brush : _st.selectFg->b,
|
||||
segments ? _segments.front().width : _st.selectWidth);
|
||||
p.setPen(pen);
|
||||
if (!radius) {
|
||||
p.drawEllipse(outline);
|
||||
} else {
|
||||
p.drawRoundedRect(outline, *radius, *radius);
|
||||
}
|
||||
} else {
|
||||
PaintOutlineSegments(p, outline, _segments);
|
||||
}
|
||||
|
||||
if (_liveBadge) {
|
||||
PaintLiveBadge(p, x, y, _st.imageRadius * 2);
|
||||
}
|
||||
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
if (_st.check.size > 0) {
|
||||
auto iconLeft = x + 2 * _st.imageRadius + _st.selectWidth - _st.check.size;
|
||||
auto iconTop = y + 2 * _st.imageRadius + _st.selectWidth - _st.check.size;
|
||||
_check.paint(p, iconLeft, iconTop, outerWidth);
|
||||
}
|
||||
}
|
||||
|
||||
float64 RoundImageCheckbox::checkedAnimationRatio() const {
|
||||
return std::clamp(_selection.value(checked() ? 1. : 0.), 0., 1.);
|
||||
}
|
||||
|
||||
void RoundImageCheckbox::setChecked(bool newChecked, anim::type animated) {
|
||||
auto changed = (checked() != newChecked);
|
||||
_check.setChecked(newChecked, animated);
|
||||
if (!changed) {
|
||||
if (animated == anim::type::instant) {
|
||||
_selection.stop();
|
||||
_wideCache = QPixmap();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (animated == anim::type::normal) {
|
||||
prepareWideCache();
|
||||
const auto from = checked() ? 0. : 1.;
|
||||
const auto to = checked() ? 1. : 0.;
|
||||
_selection.start(
|
||||
[=](float64 value) {
|
||||
if (_updateCallback) {
|
||||
_updateCallback();
|
||||
}
|
||||
if (value == to) {
|
||||
_wideCache = QPixmap();
|
||||
}
|
||||
},
|
||||
from,
|
||||
to,
|
||||
_st.selectDuration,
|
||||
anim::bumpy(1.25));
|
||||
} else {
|
||||
_selection.stop();
|
||||
_wideCache = QPixmap();
|
||||
}
|
||||
}
|
||||
|
||||
void RoundImageCheckbox::prepareWideCache() {
|
||||
if (_wideCache.isNull()) {
|
||||
auto size = _st.imageRadius * 2;
|
||||
auto wideSize = size * kWideScale;
|
||||
QImage cache(wideSize * style::DevicePixelRatio(), wideSize * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
|
||||
cache.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
{
|
||||
auto p = Painter(&cache);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(0, 0, wideSize, wideSize, Qt::transparent);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
_paintRoundImage(p, (wideSize - size) / 2, (wideSize - size) / 2, wideSize, size);
|
||||
}
|
||||
_wideCache = Ui::PixmapFromImage(std::move(cache));
|
||||
}
|
||||
}
|
||||
|
||||
void RoundImageCheckbox::setColorOverride(std::optional<QBrush> fg) {
|
||||
if (fg) {
|
||||
setCustomizedSegments({
|
||||
{ .brush = *fg, .width = float64(_st.selectWidth) }
|
||||
}, false);
|
||||
} else {
|
||||
setCustomizedSegments({}, false);
|
||||
}
|
||||
}
|
||||
|
||||
void RoundImageCheckbox::setCustomizedSegments(
|
||||
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
|
||||
Reference in New Issue
Block a user