183 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
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/unread_badge_paint.h"
 | 
						|
 | 
						|
#include "ui/ui_utility.h"
 | 
						|
#include "styles/style_dialogs.h"
 | 
						|
 | 
						|
namespace Ui {
 | 
						|
namespace {
 | 
						|
 | 
						|
struct UnreadBadgeSizeData {
 | 
						|
	QImage circle;
 | 
						|
	QPixmap left[6], right[6];
 | 
						|
};
 | 
						|
class UnreadBadgeStyleData {
 | 
						|
public:
 | 
						|
	UnreadBadgeStyleData();
 | 
						|
 | 
						|
	UnreadBadgeSizeData sizes[static_cast<int>(UnreadBadgeSize::kCount)];
 | 
						|
	style::color bg[6] = {
 | 
						|
		st::dialogsUnreadBg,
 | 
						|
		st::dialogsUnreadBgOver,
 | 
						|
		st::dialogsUnreadBgActive,
 | 
						|
		st::dialogsUnreadBgMuted,
 | 
						|
		st::dialogsUnreadBgMutedOver,
 | 
						|
		st::dialogsUnreadBgMutedActive
 | 
						|
	};
 | 
						|
	style::color reactionBg[6] = {
 | 
						|
		st::dialogsDraftFg,
 | 
						|
		st::dialogsDraftFgOver,
 | 
						|
		st::dialogsDraftFgActive,
 | 
						|
		st::dialogsUnreadBgMuted,
 | 
						|
		st::dialogsUnreadBgMutedOver,
 | 
						|
		st::dialogsUnreadBgMutedActive
 | 
						|
	};
 | 
						|
	rpl::lifetime lifetime;
 | 
						|
};
 | 
						|
 | 
						|
UnreadBadgeStyleData::UnreadBadgeStyleData() {
 | 
						|
	style::PaletteChanged(
 | 
						|
	) | rpl::start_with_next([=] {
 | 
						|
		for (auto &data : sizes) {
 | 
						|
			for (auto &left : data.left) {
 | 
						|
				left = QPixmap();
 | 
						|
			}
 | 
						|
			for (auto &right : data.right) {
 | 
						|
				right = QPixmap();
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}, lifetime);
 | 
						|
}
 | 
						|
 | 
						|
UnreadBadgeStyleData &UnreadBadgeStyles() {
 | 
						|
	static auto result = UnreadBadgeStyleData();
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void CreateCircleMask(UnreadBadgeSizeData *data, int size) {
 | 
						|
	if (!data->circle.isNull()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	data->circle = style::createCircleMask(size);
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] QImage ColorizeCircleHalf(
 | 
						|
		UnreadBadgeSizeData *data,
 | 
						|
		int size,
 | 
						|
		int half,
 | 
						|
		int xoffset,
 | 
						|
		style::color color) {
 | 
						|
	auto result = style::colorizeImage(data->circle, color, QRect(xoffset, 0, half, size));
 | 
						|
	result.setDevicePixelRatio(style::DevicePixelRatio());
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] QString ComputeUnreadBadgeText(
 | 
						|
	const QString &unreadCount,
 | 
						|
	int allowDigits) {
 | 
						|
	return (allowDigits > 0) && (unreadCount.size() > allowDigits + 1)
 | 
						|
		? u".."_q + unreadCount.mid(unreadCount.size() - allowDigits)
 | 
						|
		: unreadCount;
 | 
						|
}
 | 
						|
 | 
						|
void PaintUnreadBadge(QPainter &p, const QRect &rect, const UnreadBadgeStyle &st) {
 | 
						|
	Assert(rect.height() == st.size);
 | 
						|
 | 
						|
	int index = (st.muted ? 0x03 : 0x00) + (st.active ? 0x02 : (st.selected ? 0x01 : 0x00));
 | 
						|
	int size = st.size, sizehalf = size / 2;
 | 
						|
 | 
						|
	auto &styles = UnreadBadgeStyles();
 | 
						|
	auto badgeData = styles.sizes;
 | 
						|
	if (st.sizeId > UnreadBadgeSize()) {
 | 
						|
		Assert(st.sizeId < UnreadBadgeSize::kCount);
 | 
						|
		badgeData = &styles.sizes[static_cast<int>(st.sizeId)];
 | 
						|
	}
 | 
						|
	const auto bg = (st.sizeId == UnreadBadgeSize::ReactionInDialogs)
 | 
						|
		? styles.reactionBg[index]
 | 
						|
		: styles.bg[index];
 | 
						|
	if (badgeData->left[index].isNull()) {
 | 
						|
		const auto ratio = style::DevicePixelRatio();
 | 
						|
		int imgsize = size * ratio, imgsizehalf = sizehalf * ratio;
 | 
						|
		CreateCircleMask(badgeData, size);
 | 
						|
		badgeData->left[index] = PixmapFromImage(
 | 
						|
			ColorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, bg));
 | 
						|
		badgeData->right[index] = PixmapFromImage(ColorizeCircleHalf(
 | 
						|
			badgeData,
 | 
						|
			imgsize,
 | 
						|
			imgsizehalf,
 | 
						|
			imgsize - imgsizehalf,
 | 
						|
			bg));
 | 
						|
	}
 | 
						|
 | 
						|
	int bar = rect.width() - 2 * sizehalf;
 | 
						|
	p.drawPixmap(rect.x(), rect.y(), badgeData->left[index]);
 | 
						|
	if (bar) {
 | 
						|
		p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), bg);
 | 
						|
	}
 | 
						|
	p.drawPixmap(rect.x() + sizehalf + bar, rect.y(), badgeData->right[index]);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
UnreadBadgeStyle::UnreadBadgeStyle()
 | 
						|
: size(st::dialogsUnreadHeight)
 | 
						|
, padding(st::dialogsUnreadPadding)
 | 
						|
, font(st::dialogsUnreadFont) {
 | 
						|
}
 | 
						|
 | 
						|
QSize CountUnreadBadgeSize(
 | 
						|
		const QString &unreadCount,
 | 
						|
		const UnreadBadgeStyle &st,
 | 
						|
		int allowDigits) {
 | 
						|
	const auto text = ComputeUnreadBadgeText(unreadCount, allowDigits);
 | 
						|
	const auto unreadRectHeight = st.size;
 | 
						|
	const auto unreadWidth = st.font->width(text);
 | 
						|
	return {
 | 
						|
		std::max(unreadWidth + 2 * st.padding, unreadRectHeight),
 | 
						|
		unreadRectHeight,
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
QRect PaintUnreadBadge(
 | 
						|
		QPainter &p,
 | 
						|
		const QString &unreadCount,
 | 
						|
		int x,
 | 
						|
		int y,
 | 
						|
		const UnreadBadgeStyle &st,
 | 
						|
		int allowDigits) {
 | 
						|
	const auto text = ComputeUnreadBadgeText(unreadCount, allowDigits);
 | 
						|
	const auto unreadRectHeight = st.size;
 | 
						|
	const auto unreadWidth = st.font->width(text);
 | 
						|
	const auto unreadRectWidth = std::max(
 | 
						|
		unreadWidth + 2 * st.padding,
 | 
						|
		unreadRectHeight);
 | 
						|
 | 
						|
	const auto unreadRectLeft = ((st.align & Qt::AlignHorizontal_Mask) & style::al_center)
 | 
						|
		? (x - unreadRectWidth) / 2
 | 
						|
		: ((st.align & Qt::AlignHorizontal_Mask) & style::al_right)
 | 
						|
		? (x - unreadRectWidth)
 | 
						|
		: x;
 | 
						|
	const auto unreadRectTop = y;
 | 
						|
 | 
						|
	const auto badge = QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight);
 | 
						|
	PaintUnreadBadge(p, badge, st);
 | 
						|
 | 
						|
	const auto textTop = st.textTop ? st.textTop : (unreadRectHeight - st.font->height) / 2;
 | 
						|
	p.setFont(st.font);
 | 
						|
	p.setPen(st.active
 | 
						|
		? st::dialogsUnreadFgActive
 | 
						|
		: st.selected
 | 
						|
		? st::dialogsUnreadFgOver
 | 
						|
		: st::dialogsUnreadFg);
 | 
						|
	p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st.font->ascent, text);
 | 
						|
 | 
						|
	return badge;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Ui
 |