1131 lines
		
	
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1131 lines
		
	
	
	
		
			31 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/chat/chat_style.h"
 | |
| 
 | |
| #include "ui/chat/chat_theme.h"
 | |
| #include "ui/image/image_prepare.h" // ImageRoundRadius
 | |
| #include "ui/text/text_custom_emoji.h"
 | |
| #include "ui/color_contrast.h"
 | |
| #include "ui/painter.h"
 | |
| #include "ui/ui_utility.h"
 | |
| #include "styles/style_chat.h"
 | |
| #include "styles/style_dialogs.h"
 | |
| #include "styles/style_widgets.h"
 | |
| 
 | |
| namespace Ui {
 | |
| namespace {
 | |
| 
 | |
| void EnsureCorners(
 | |
| 		CornersPixmaps &corners,
 | |
| 		int radius,
 | |
| 		const style::color &color,
 | |
| 		const style::color *shadow = nullptr) {
 | |
| 	if (corners.p[0].isNull()) {
 | |
| 		corners = PrepareCornerPixmaps(radius, color, shadow);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void EnsureBlockquoteCache(
 | |
| 		std::unique_ptr<Text::QuotePaintCache> &cache,
 | |
| 		Fn<ColorIndexValues()> values) {
 | |
| 	if (cache) {
 | |
| 		return;
 | |
| 	}
 | |
| 	cache = std::make_unique<Text::QuotePaintCache>();
 | |
| 	const auto &colors = values();
 | |
| 	cache->bg = colors.bg;
 | |
| 	cache->outlines = colors.outlines;
 | |
| 	cache->icon = colors.name;
 | |
| }
 | |
| 
 | |
| void EnsurePreCache(
 | |
| 		std::unique_ptr<Text::QuotePaintCache> &cache,
 | |
| 		const style::color &color,
 | |
| 		Fn<std::optional<QColor>()> bgOverride) {
 | |
| 	if (cache) {
 | |
| 		return;
 | |
| 	}
 | |
| 	cache = std::make_unique<Text::QuotePaintCache>();
 | |
| 	const auto bg = bgOverride();
 | |
| 	cache->bg = bg.value_or(color->c);
 | |
| 	if (!bg) {
 | |
| 		cache->bg.setAlpha(kDefaultBgOpacity * 255);
 | |
| 	}
 | |
| 	cache->outlines[0] = color->c;
 | |
| 	cache->outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
 | |
| 	cache->outlines[1] = cache->outlines[2] = QColor(0, 0, 0, 0);
 | |
| 	cache->header = color->c;
 | |
| 	cache->header.setAlpha(kDefaultOutline2Opacity * 255);
 | |
| 	cache->icon = cache->outlines[0];
 | |
| 	cache->icon.setAlpha(kDefaultOutline3Opacity * 255);
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| not_null<const MessageStyle*> ChatPaintContext::messageStyle() const {
 | |
| 	return &st->messageStyle(outbg, selected());
 | |
| }
 | |
| 
 | |
| not_null<const MessageImageStyle*> ChatPaintContext::imageStyle() const {
 | |
| 	return &st->imageStyle(selected());
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatPaintContext::quoteCache(
 | |
| 		uint8 colorIndex) const {
 | |
| 	return !outbg
 | |
| 		? st->coloredQuoteCache(selected(), colorIndex).get()
 | |
| 		: messageStyle()->quoteCache[
 | |
| 			st->colorPatternIndex(colorIndex)].get();
 | |
| }
 | |
| 
 | |
| int HistoryServiceMsgRadius() {
 | |
| 	static const auto result = [] {
 | |
| 		const auto minMessageHeight = st::msgServicePadding.top()
 | |
| 			+ st::msgServiceFont->height
 | |
| 			+ st::msgServicePadding.bottom();
 | |
| 		return minMessageHeight / 2;
 | |
| 	}();
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| int HistoryServiceMsgInvertedRadius() {
 | |
| 	static const auto result = [] {
 | |
| 		const auto minRowHeight = st::msgServiceFont->height;
 | |
| 		return minRowHeight - HistoryServiceMsgRadius();
 | |
| 	}();
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| int HistoryServiceMsgInvertedShrink() {
 | |
| 	static const auto result = [] {
 | |
| 		return (HistoryServiceMsgInvertedRadius() * 2) / 3;
 | |
| 	}();
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| ColorIndexValues SimpleColorIndexValues(QColor color, int patternIndex) {
 | |
| 	auto bg = color;
 | |
| 	bg.setAlpha(kDefaultBgOpacity * 255);
 | |
| 	auto result = ColorIndexValues{
 | |
| 		.name = color,
 | |
| 		.bg = bg,
 | |
| 	};
 | |
| 	result.outlines[0] = color;
 | |
| 	result.outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
 | |
| 	if (patternIndex > 1) {
 | |
| 		result.outlines[1] = result.outlines[0];
 | |
| 		result.outlines[1].setAlpha(kDefaultOutline2Opacity * 255);
 | |
| 		result.outlines[2] = result.outlines[0];
 | |
| 		result.outlines[2].setAlpha(kDefaultOutline3Opacity * 255);
 | |
| 	} else if (patternIndex > 0) {
 | |
| 		result.outlines[1] = result.outlines[0];
 | |
| 		result.outlines[1].setAlpha(kDefaultOutlineOpacitySecond * 255);
 | |
| 		result.outlines[2] = QColor(0, 0, 0, 0);
 | |
| 	} else {
 | |
| 		result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| int BackgroundEmojiData::CacheIndex(
 | |
| 		bool selected,
 | |
| 		bool outbg,
 | |
| 		bool inbubble,
 | |
| 		uint8 colorIndexPlusOne) {
 | |
| 	const auto base = colorIndexPlusOne
 | |
| 		? (colorIndexPlusOne - 1)
 | |
| 		: (kColorIndexCount + (!inbubble ? 0 : outbg ? 1 : 2));
 | |
| 	return (base * 2) + (selected ? 1 : 0);
 | |
| };
 | |
| 
 | |
| ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
 | |
| 	if (colorIndices) {
 | |
| 		_colorIndicesLifetime = std::move(
 | |
| 			colorIndices
 | |
| 		) | rpl::start_with_next([=](ColorIndicesCompressed &&indices) {
 | |
| 			_colorIndices = std::move(indices);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	finalize();
 | |
| 	make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
 | |
| 	make(_imgReplyTextPalette, st::imgReplyTextPalette);
 | |
| 	make(_serviceTextPalette, st::serviceTextPalette);
 | |
| 	make(_historyRepliesInvertedIcon, st::historyRepliesInvertedIcon);
 | |
| 	make(_historyViewsInvertedIcon, st::historyViewsInvertedIcon);
 | |
| 	make(_historyViewsSendingIcon, st::historyViewsSendingIcon);
 | |
| 	make(
 | |
| 		_historyViewsSendingInvertedIcon,
 | |
| 		st::historyViewsSendingInvertedIcon);
 | |
| 	make(_historyPinInvertedIcon, st::historyPinInvertedIcon);
 | |
| 	make(_historySendingIcon, st::historySendingIcon);
 | |
| 	make(_historySendingInvertedIcon, st::historySendingInvertedIcon);
 | |
| 	make(_historySentInvertedIcon, st::historySentInvertedIcon);
 | |
| 	make(_historyReceivedInvertedIcon, st::historyReceivedInvertedIcon);
 | |
| 	make(_msgBotKbUrlIcon, st::msgBotKbUrlIcon);
 | |
| 	make(_msgBotKbPaymentIcon, st::msgBotKbPaymentIcon);
 | |
| 	make(_msgBotKbSwitchPmIcon, st::msgBotKbSwitchPmIcon);
 | |
| 	make(_msgBotKbWebviewIcon, st::msgBotKbWebviewIcon);
 | |
| 	make(_historyFastCommentsIcon, st::historyFastCommentsIcon);
 | |
| 	make(_historyFastShareIcon, st::historyFastShareIcon);
 | |
| 	make(_historyFastTranscribeIcon, st::historyFastTranscribeIcon);
 | |
| 	make(_historyGoToOriginalIcon, st::historyGoToOriginalIcon);
 | |
| 	make(_historyMapPoint, st::historyMapPoint);
 | |
| 	make(_historyMapPointInner, st::historyMapPointInner);
 | |
| 	make(_youtubeIcon, st::youtubeIcon);
 | |
| 	make(_videoIcon, st::videoIcon);
 | |
| 	make(_historyPollChoiceRight, st::historyPollChoiceRight);
 | |
| 	make(_historyPollChoiceWrong, st::historyPollChoiceWrong);
 | |
| 	make(
 | |
| 		&MessageStyle::msgBg,
 | |
| 		st::msgInBg,
 | |
| 		st::msgInBgSelected,
 | |
| 		st::msgOutBg,
 | |
| 		st::msgOutBgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgShadow,
 | |
| 		st::msgInShadow,
 | |
| 		st::msgInShadowSelected,
 | |
| 		st::msgOutShadow,
 | |
| 		st::msgOutShadowSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgServiceFg,
 | |
| 		st::msgInServiceFg,
 | |
| 		st::msgInServiceFgSelected,
 | |
| 		st::msgOutServiceFg,
 | |
| 		st::msgOutServiceFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgDateFg,
 | |
| 		st::msgInDateFg,
 | |
| 		st::msgInDateFgSelected,
 | |
| 		st::msgOutDateFg,
 | |
| 		st::msgOutDateFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgFileThumbLinkFg,
 | |
| 		st::msgFileThumbLinkInFg,
 | |
| 		st::msgFileThumbLinkInFgSelected,
 | |
| 		st::msgFileThumbLinkOutFg,
 | |
| 		st::msgFileThumbLinkOutFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgFileBg,
 | |
| 		st::msgFileInBg,
 | |
| 		st::msgFileInBgSelected,
 | |
| 		st::msgFileOutBg,
 | |
| 		st::msgFileOutBgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgReplyBarColor,
 | |
| 		st::msgInReplyBarColor,
 | |
| 		st::msgInReplyBarSelColor,
 | |
| 		st::msgOutReplyBarColor,
 | |
| 		st::msgOutReplyBarSelColor);
 | |
| 	make(
 | |
| 		&MessageStyle::msgWaveformActive,
 | |
| 		st::msgWaveformInActive,
 | |
| 		st::msgWaveformInActiveSelected,
 | |
| 		st::msgWaveformOutActive,
 | |
| 		st::msgWaveformOutActiveSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::msgWaveformInactive,
 | |
| 		st::msgWaveformInInactive,
 | |
| 		st::msgWaveformInInactiveSelected,
 | |
| 		st::msgWaveformOutInactive,
 | |
| 		st::msgWaveformOutInactiveSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyTextFg,
 | |
| 		st::historyTextInFg,
 | |
| 		st::historyTextInFgSelected,
 | |
| 		st::historyTextOutFg,
 | |
| 		st::historyTextOutFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileNameFg,
 | |
| 		st::historyFileNameInFg,
 | |
| 		st::historyFileNameInFgSelected,
 | |
| 		st::historyFileNameOutFg,
 | |
| 		st::historyFileNameOutFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileRadialFg,
 | |
| 		st::historyFileInRadialFg,
 | |
| 		st::historyFileInRadialFgSelected,
 | |
| 		st::historyFileOutRadialFg,
 | |
| 		st::historyFileOutRadialFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::mediaFg,
 | |
| 		st::mediaInFg,
 | |
| 		st::mediaInFgSelected,
 | |
| 		st::mediaOutFg,
 | |
| 		st::mediaOutFgSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::textPalette,
 | |
| 		st::inTextPalette,
 | |
| 		st::inTextPaletteSelected,
 | |
| 		st::outTextPalette,
 | |
| 		st::outTextPaletteSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::semiboldPalette,
 | |
| 		st::inSemiboldPalette,
 | |
| 		st::inTextPaletteSelected,
 | |
| 		st::outSemiboldPalette,
 | |
| 		st::outTextPaletteSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::fwdTextPalette,
 | |
| 		st::inFwdTextPalette,
 | |
| 		st::inFwdTextPaletteSelected,
 | |
| 		st::outFwdTextPalette,
 | |
| 		st::outFwdTextPaletteSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::replyTextPalette,
 | |
| 		st::inReplyTextPalette,
 | |
| 		st::inReplyTextPaletteSelected,
 | |
| 		st::outReplyTextPalette,
 | |
| 		st::outReplyTextPaletteSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::tailLeft,
 | |
| 		st::historyBubbleTailInLeft,
 | |
| 		st::historyBubbleTailInLeftSelected,
 | |
| 		st::historyBubbleTailOutLeft,
 | |
| 		st::historyBubbleTailOutLeftSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::tailRight,
 | |
| 		st::historyBubbleTailInRight,
 | |
| 		st::historyBubbleTailInRightSelected,
 | |
| 		st::historyBubbleTailOutRight,
 | |
| 		st::historyBubbleTailOutRightSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyRepliesIcon,
 | |
| 		st::historyRepliesInIcon,
 | |
| 		st::historyRepliesInSelectedIcon,
 | |
| 		st::historyRepliesOutIcon,
 | |
| 		st::historyRepliesOutSelectedIcon);
 | |
| 	make(
 | |
| 		&MessageStyle::historyViewsIcon,
 | |
| 		st::historyViewsInIcon,
 | |
| 		st::historyViewsInSelectedIcon,
 | |
| 		st::historyViewsOutIcon,
 | |
| 		st::historyViewsOutSelectedIcon);
 | |
| 	make(
 | |
| 		&MessageStyle::historyPinIcon,
 | |
| 		st::historyPinInIcon,
 | |
| 		st::historyPinInSelectedIcon,
 | |
| 		st::historyPinOutIcon,
 | |
| 		st::historyPinOutSelectedIcon);
 | |
| 	make(
 | |
| 		&MessageStyle::historySentIcon,
 | |
| 		st::historySentIcon,
 | |
| 		st::historySentSelectedIcon,
 | |
| 		st::historySentIcon,
 | |
| 		st::historySentSelectedIcon);
 | |
| 	make(
 | |
| 		&MessageStyle::historyReceivedIcon,
 | |
| 		st::historyReceivedIcon,
 | |
| 		st::historyReceivedSelectedIcon,
 | |
| 		st::historyReceivedIcon,
 | |
| 		st::historyReceivedSelectedIcon);
 | |
| 	make(
 | |
| 		&MessageStyle::historyPsaIcon,
 | |
| 		st::historyPsaIconIn,
 | |
| 		st::historyPsaIconInSelected,
 | |
| 		st::historyPsaIconOut,
 | |
| 		st::historyPsaIconOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyCommentsOpen,
 | |
| 		st::historyCommentsOpenIn,
 | |
| 		st::historyCommentsOpenInSelected,
 | |
| 		st::historyCommentsOpenOut,
 | |
| 		st::historyCommentsOpenOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyComments,
 | |
| 		st::historyCommentsIn,
 | |
| 		st::historyCommentsInSelected,
 | |
| 		st::historyCommentsOut,
 | |
| 		st::historyCommentsOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyCallArrow,
 | |
| 		st::historyCallArrowIn,
 | |
| 		st::historyCallArrowInSelected,
 | |
| 		st::historyCallArrowOut,
 | |
| 		st::historyCallArrowOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyCallArrowMissed,
 | |
| 		st::historyCallArrowMissedIn,
 | |
| 		st::historyCallArrowMissedInSelected,
 | |
| 		st::historyCallArrowMissedIn,
 | |
| 		st::historyCallArrowMissedInSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyCallIcon,
 | |
| 		st::historyCallInIcon,
 | |
| 		st::historyCallInIconSelected,
 | |
| 		st::historyCallOutIcon,
 | |
| 		st::historyCallOutIconSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyCallCameraIcon,
 | |
| 		st::historyCallCameraInIcon,
 | |
| 		st::historyCallCameraInIconSelected,
 | |
| 		st::historyCallCameraOutIcon,
 | |
| 		st::historyCallCameraOutIconSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFilePlay,
 | |
| 		st::historyFileInPlay,
 | |
| 		st::historyFileInPlaySelected,
 | |
| 		st::historyFileOutPlay,
 | |
| 		st::historyFileOutPlaySelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileWaiting,
 | |
| 		st::historyFileInWaiting,
 | |
| 		st::historyFileInWaitingSelected,
 | |
| 		st::historyFileOutWaiting,
 | |
| 		st::historyFileOutWaitingSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileDownload,
 | |
| 		st::historyFileInDownload,
 | |
| 		st::historyFileInDownloadSelected,
 | |
| 		st::historyFileOutDownload,
 | |
| 		st::historyFileOutDownloadSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileCancel,
 | |
| 		st::historyFileInCancel,
 | |
| 		st::historyFileInCancelSelected,
 | |
| 		st::historyFileOutCancel,
 | |
| 		st::historyFileOutCancelSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFilePause,
 | |
| 		st::historyFileInPause,
 | |
| 		st::historyFileInPauseSelected,
 | |
| 		st::historyFileOutPause,
 | |
| 		st::historyFileOutPauseSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileImage,
 | |
| 		st::historyFileInImage,
 | |
| 		st::historyFileInImageSelected,
 | |
| 		st::historyFileOutImage,
 | |
| 		st::historyFileOutImageSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyFileDocument,
 | |
| 		st::historyFileInDocument,
 | |
| 		st::historyFileInDocumentSelected,
 | |
| 		st::historyFileOutDocument,
 | |
| 		st::historyFileOutDocumentSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyAudioDownload,
 | |
| 		st::historyAudioInDownload,
 | |
| 		st::historyAudioInDownloadSelected,
 | |
| 		st::historyAudioOutDownload,
 | |
| 		st::historyAudioOutDownloadSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyAudioCancel,
 | |
| 		st::historyAudioInCancel,
 | |
| 		st::historyAudioInCancelSelected,
 | |
| 		st::historyAudioOutCancel,
 | |
| 		st::historyAudioOutCancelSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyQuizTimer,
 | |
| 		st::historyQuizTimerIn,
 | |
| 		st::historyQuizTimerInSelected,
 | |
| 		st::historyQuizTimerOut,
 | |
| 		st::historyQuizTimerOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyQuizExplain,
 | |
| 		st::historyQuizExplainIn,
 | |
| 		st::historyQuizExplainInSelected,
 | |
| 		st::historyQuizExplainOut,
 | |
| 		st::historyQuizExplainOutSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyPollChosen,
 | |
| 		st::historyPollInChosen,
 | |
| 		st::historyPollInChosenSelected,
 | |
| 		st::historyPollOutChosen,
 | |
| 		st::historyPollOutChosenSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyPollChoiceRight,
 | |
| 		st::historyPollInChoiceRight,
 | |
| 		st::historyPollInChoiceRightSelected,
 | |
| 		st::historyPollOutChoiceRight,
 | |
| 		st::historyPollOutChoiceRightSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyTranscribeIcon,
 | |
| 		st::historyTranscribeInIcon,
 | |
| 		st::historyTranscribeInIconSelected,
 | |
| 		st::historyTranscribeOutIcon,
 | |
| 		st::historyTranscribeOutIconSelected);
 | |
| 	make(
 | |
| 		&MessageStyle::historyTranscribeHide,
 | |
| 		st::historyTranscribeInHide,
 | |
| 		st::historyTranscribeInHideSelected,
 | |
| 		st::historyTranscribeOutHide,
 | |
| 		st::historyTranscribeOutHideSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::msgDateImgBg,
 | |
| 		st::msgDateImgBg,
 | |
| 		st::msgDateImgBgSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::msgServiceBg,
 | |
| 		st::msgServiceBg,
 | |
| 		st::msgServiceBgSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::msgShadow,
 | |
| 		st::msgInShadow,
 | |
| 		st::msgInShadowSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbRadialFg,
 | |
| 		st::historyFileThumbRadialFg,
 | |
| 		st::historyFileThumbRadialFgSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbPlay,
 | |
| 		st::historyFileThumbPlay,
 | |
| 		st::historyFileThumbPlaySelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbWaiting,
 | |
| 		st::historyFileThumbWaiting,
 | |
| 		st::historyFileThumbWaitingSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbDownload,
 | |
| 		st::historyFileThumbDownload,
 | |
| 		st::historyFileThumbDownloadSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbCancel,
 | |
| 		st::historyFileThumbCancel,
 | |
| 		st::historyFileThumbCancelSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyFileThumbPause,
 | |
| 		st::historyFileThumbPause,
 | |
| 		st::historyFileThumbPauseSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyVideoDownload,
 | |
| 		st::historyVideoDownload,
 | |
| 		st::historyVideoDownloadSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyVideoCancel,
 | |
| 		st::historyVideoCancel,
 | |
| 		st::historyVideoCancelSelected);
 | |
| 	make(
 | |
| 		&MessageImageStyle::historyVideoMessageMute,
 | |
| 		st::historyVideoMessageMute,
 | |
| 		st::historyVideoMessageMuteSelected);
 | |
| 
 | |
| 	updateDarkValue();
 | |
| }
 | |
| 
 | |
| ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
 | |
| : ChatStyle(rpl::producer<ColorIndicesCompressed>()) {
 | |
| 	assignPalette(isolated);
 | |
| }
 | |
| 
 | |
| ChatStyle::~ChatStyle() = default;
 | |
| 
 | |
| void ChatStyle::apply(not_null<ChatTheme*> theme) {
 | |
| 	applyCustomPalette(theme->palette());
 | |
| }
 | |
| 
 | |
| void ChatStyle::updateDarkValue() {
 | |
| 	const auto withBg = [&](const QColor &color) {
 | |
| 		return CountContrast(windowBg()->c, color);
 | |
| 	};
 | |
| 	_dark = (withBg({ 0, 0, 0 }) < withBg({ 255, 255, 255 }));
 | |
| }
 | |
| 
 | |
| void ChatStyle::applyCustomPalette(const style::palette *palette) {
 | |
| 	assignPalette(palette ? palette : style::main_palette::get().get());
 | |
| 	if (palette) {
 | |
| 		_defaultPaletteChangeLifetime.destroy();
 | |
| 	} else {
 | |
| 		style::PaletteChanged(
 | |
| 		) | rpl::start_with_next([=] {
 | |
| 			assignPalette(style::main_palette::get());
 | |
| 		}, _defaultPaletteChangeLifetime);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) {
 | |
| 	auto r = 0, g = 0, b = 0, a = 0;
 | |
| 	serviceBg.getRgb(&r, &g, &b, &a);
 | |
| 	msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a));
 | |
| }
 | |
| 
 | |
| std::span<Text::SpecialColor> ChatStyle::highlightColors() const {
 | |
| 	if (_highlightColors.empty()) {
 | |
| 		const auto push = [&](const style::color &color) {
 | |
| 			_highlightColors.push_back({ &color->p, &color->p });
 | |
| 		};
 | |
| 
 | |
| 		// comment, block-comment, prolog, doctype, cdata
 | |
| 		push(statisticsChartLineLightblue());
 | |
| 
 | |
| 		// punctuation
 | |
| 		push(statisticsChartLineRed());
 | |
| 
 | |
| 		// property, tag, boolean, number,
 | |
| 		// constant, symbol, deleted
 | |
| 		push(statisticsChartLineRed());
 | |
| 
 | |
| 		// selector, attr-name, string, char, builtin, inserted
 | |
| 		push(statisticsChartLineOrange());
 | |
| 
 | |
| 		// operator, entity, url
 | |
| 		push(statisticsChartLineRed());
 | |
| 
 | |
| 		// atrule, attr-value, keyword, function
 | |
| 		push(statisticsChartLineBlue());
 | |
| 
 | |
| 		// class-name
 | |
| 		push(statisticsChartLinePurple());
 | |
| 
 | |
| 		//push(statisticsChartLineLightgreen());
 | |
| 		//push(statisticsChartLineGreen());
 | |
| 		//push(statisticsChartLineGolden());
 | |
| 	}
 | |
| 	return _highlightColors;
 | |
| }
 | |
| 
 | |
| void ChatStyle::clearColorIndexCaches() {
 | |
| 	for (auto &style : _messageStyles) {
 | |
| 		for (auto &cache : style.quoteCache) {
 | |
| 			cache = nullptr;
 | |
| 		}
 | |
| 		for (auto &cache : style.replyCache) {
 | |
| 			cache = nullptr;
 | |
| 		}
 | |
| 	}
 | |
| 	for (auto &values : _coloredValues) {
 | |
| 		values.reset();
 | |
| 	}
 | |
| 	for (auto &palette : _coloredTextPalettes) {
 | |
| 		palette.linkFg.reset();
 | |
| 	}
 | |
| 	for (auto &cache : _coloredReplyCaches) {
 | |
| 		cache = nullptr;
 | |
| 	}
 | |
| 	for (auto &cache : _coloredQuoteCaches) {
 | |
| 		cache = nullptr;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
 | |
| 	*static_cast<style::palette*>(this) = *palette;
 | |
| 	style::internal::resetIcons();
 | |
| 
 | |
| 	clearColorIndexCaches();
 | |
| 	for (auto &style : _messageStyles) {
 | |
| 		style.msgBgCornersSmall = {};
 | |
| 		style.msgBgCornersLarge = {};
 | |
| 		style.preCache = nullptr;
 | |
| 		style.textPalette.linkAlwaysActive
 | |
| 			= style.semiboldPalette.linkAlwaysActive
 | |
| 			= (style.textPalette.linkFg->c == style.historyTextFg->c);
 | |
| 	}
 | |
| 	for (auto &style : _imageStyles) {
 | |
| 		style.msgDateImgBgCorners = {};
 | |
| 		style.msgServiceBgCornersSmall = {};
 | |
| 		style.msgServiceBgCornersLarge = {};
 | |
| 		style.msgShadowCornersSmall = {};
 | |
| 		style.msgShadowCornersLarge = {};
 | |
| 	}
 | |
| 	_serviceBgCornersNormal = {};
 | |
| 	_serviceBgCornersInverted = {};
 | |
| 	_msgBotKbOverBgAddCornersSmall = {};
 | |
| 	_msgBotKbOverBgAddCornersLarge = {};
 | |
| 	for (auto &corners : _msgSelectOverlayCorners) {
 | |
| 		corners = {};
 | |
| 	}
 | |
| 	updateDarkValue();
 | |
| 
 | |
| 	_paletteChanged.fire({});
 | |
| }
 | |
| 
 | |
| const CornersPixmaps &ChatStyle::serviceBgCornersNormal() const {
 | |
| 	EnsureCorners(
 | |
| 		_serviceBgCornersNormal,
 | |
| 		HistoryServiceMsgRadius(),
 | |
| 		msgServiceBg());
 | |
| 	return _serviceBgCornersNormal;
 | |
| }
 | |
| 
 | |
| const CornersPixmaps &ChatStyle::serviceBgCornersInverted() const {
 | |
| 	if (_serviceBgCornersInverted.p[0].isNull()) {
 | |
| 		_serviceBgCornersInverted = PrepareInvertedCornerPixmaps(
 | |
| 			HistoryServiceMsgInvertedRadius(),
 | |
| 			msgServiceBg());
 | |
| 	}
 | |
| 	return _serviceBgCornersInverted;
 | |
| }
 | |
| 
 | |
| const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
 | |
| 	auto &result = messageStyleRaw(outbg, selected);
 | |
| 	EnsureCorners(
 | |
| 		result.msgBgCornersSmall,
 | |
| 		BubbleRadiusSmall(),
 | |
| 		result.msgBg,
 | |
| 		&result.msgShadow);
 | |
| 	EnsureCorners(
 | |
| 		result.msgBgCornersLarge,
 | |
| 		BubbleRadiusLarge(),
 | |
| 		result.msgBg,
 | |
| 		&result.msgShadow);
 | |
| 	const auto &replyBar = result.msgReplyBarColor->c;
 | |
| 	for (auto i = 0; i != kColorPatternsCount; ++i) {
 | |
| 		EnsureBlockquoteCache(
 | |
| 			result.replyCache[i],
 | |
| 			[&] { return SimpleColorIndexValues(replyBar, i); });
 | |
| 		if (!result.quoteCache[i]) {
 | |
| 			result.quoteCache[i] = std::make_unique<Text::QuotePaintCache>(
 | |
| 				*result.replyCache[i]);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	const auto preBgOverride = [&] {
 | |
| 		return _dark ? QColor(0, 0, 0, 192) : std::optional<QColor>();
 | |
| 	};
 | |
| 	EnsurePreCache(
 | |
| 		result.preCache,
 | |
| 		(selected
 | |
| 			? result.textPalette.selectMonoFg
 | |
| 			: result.textPalette.monoFg),
 | |
| 		preBgOverride);
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
 | |
| 	auto &result = imageStyleRaw(selected);
 | |
| 	EnsureCorners(
 | |
| 		result.msgDateImgBgCorners,
 | |
| 		(st::msgDateImgPadding.y() * 2 + st::normalFont->height) / 2,
 | |
| 		result.msgDateImgBg);
 | |
| 	EnsureCorners(
 | |
| 		result.msgServiceBgCornersSmall,
 | |
| 		BubbleRadiusSmall(),
 | |
| 		result.msgServiceBg);
 | |
| 	EnsureCorners(
 | |
| 		result.msgServiceBgCornersLarge,
 | |
| 		BubbleRadiusLarge(),
 | |
| 		result.msgServiceBg);
 | |
| 	EnsureCorners(
 | |
| 		result.msgShadowCornersSmall,
 | |
| 		BubbleRadiusSmall(),
 | |
| 		result.msgShadow);
 | |
| 	EnsureCorners(
 | |
| 		result.msgShadowCornersLarge,
 | |
| 		BubbleRadiusLarge(),
 | |
| 		result.msgShadow);
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| int ChatStyle::colorPatternIndex(uint8 colorIndex) const {
 | |
| 	Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
 | |
| 
 | |
| 	if (!_colorIndices.colors
 | |
| 		|| colorIndex < kSimpleColorIndexCount) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	auto &data = (*_colorIndices.colors)[colorIndex];
 | |
| 	auto &colors = _dark ? data.dark : data.light;
 | |
| 	return colors[2] ? 2 : colors[1] ? 1 : 0;
 | |
| }
 | |
| 
 | |
| ColorIndexValues ChatStyle::computeColorIndexValues(
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	if (!_colorIndices.colors) {
 | |
| 		colorIndex %= kSimpleColorIndexCount;
 | |
| 	}
 | |
| 	if (colorIndex < kSimpleColorIndexCount) {
 | |
| 		const auto list = std::array{
 | |
| 			&historyPeer1NameFg(),
 | |
| 			&historyPeer2NameFg(),
 | |
| 			&historyPeer3NameFg(),
 | |
| 			&historyPeer4NameFg(),
 | |
| 			&historyPeer5NameFg(),
 | |
| 			&historyPeer6NameFg(),
 | |
| 			&historyPeer7NameFg(),
 | |
| 			&historyPeer8NameFg(),
 | |
| 		};
 | |
| 		const auto listSelected = std::array{
 | |
| 			&historyPeer1NameFgSelected(),
 | |
| 			&historyPeer2NameFgSelected(),
 | |
| 			&historyPeer3NameFgSelected(),
 | |
| 			&historyPeer4NameFgSelected(),
 | |
| 			&historyPeer5NameFgSelected(),
 | |
| 			&historyPeer6NameFgSelected(),
 | |
| 			&historyPeer7NameFgSelected(),
 | |
| 			&historyPeer8NameFgSelected(),
 | |
| 		};
 | |
| 		const auto paletteIndex = ColorIndexToPaletteIndex(colorIndex);
 | |
| 		auto result = ColorIndexValues{
 | |
| 			.name = (*(selected ? listSelected : list)[paletteIndex])->c,
 | |
| 		};
 | |
| 		result.bg = result.name;
 | |
| 		result.bg.setAlpha(kDefaultBgOpacity * 255);
 | |
| 		result.outlines[0] = result.name;
 | |
| 		result.outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
 | |
| 		result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
 | |
| 		return result;
 | |
| 	}
 | |
| 	auto &data = (*_colorIndices.colors)[colorIndex];
 | |
| 	auto &colors = _dark ? data.dark : data.light;
 | |
| 	if (!colors[0]) {
 | |
| 		return computeColorIndexValues(
 | |
| 			selected,
 | |
| 			colorIndex % kSimpleColorIndexCount);
 | |
| 	}
 | |
| 	const auto color = [&](int index) {
 | |
| 		const auto v = colors[index];
 | |
| 		return v
 | |
| 			? QColor((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
 | |
| 			: QColor(0, 0, 0, 0);
 | |
| 	};
 | |
| 	auto result = ColorIndexValues{
 | |
| 		.outlines = { color(0), color(1), color(2) }
 | |
| 	};
 | |
| 	result.bg = result.outlines[0];
 | |
| 	result.bg.setAlpha(kDefaultBgOpacity * 255);
 | |
| 	result.name = result.outlines[0];
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache(
 | |
| 		bool twoColored) const {
 | |
| 	const auto index = (twoColored ? 1 : 0);
 | |
| 	const auto &service = msgServiceFg()->c;
 | |
| 	EnsureBlockquoteCache(
 | |
| 		_serviceQuoteCache[index],
 | |
| 		[&] { return SimpleColorIndexValues(service, twoColored); });
 | |
| 	return _serviceQuoteCache[index].get();
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatStyle::serviceReplyCache(
 | |
| 		bool twoColored) const {
 | |
| 	const auto index = (twoColored ? 1 : 0);
 | |
| 	const auto &service = msgServiceFg()->c;
 | |
| 	EnsureBlockquoteCache(
 | |
| 		_serviceReplyCache[index],
 | |
| 		[&] { return SimpleColorIndexValues(service, twoColored); });
 | |
| 	return _serviceReplyCache[index].get();
 | |
| }
 | |
| 
 | |
| const ColorIndexValues &ChatStyle::coloredValues(
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
 | |
| 
 | |
| 	const auto shift = (selected ? kColorIndexCount : 0);
 | |
| 	auto &result = _coloredValues[shift + colorIndex];
 | |
| 	if (!result) {
 | |
| 		result.emplace(computeColorIndexValues(selected, colorIndex));
 | |
| 	}
 | |
| 	return *result;
 | |
| }
 | |
| 
 | |
| const style::TextPalette &ChatStyle::coloredTextPalette(
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
 | |
| 
 | |
| 	const auto shift = (selected ? kColorIndexCount : 0);
 | |
| 	auto &result = _coloredTextPalettes[shift + colorIndex];
 | |
| 	if (!result.linkFg) {
 | |
| 		result.linkFg.emplace(coloredValues(selected, colorIndex).name);
 | |
| 		make(
 | |
| 			result.data,
 | |
| 			(selected
 | |
| 				? st::inReplyTextPaletteSelected
 | |
| 				: st::inReplyTextPalette));
 | |
| 		result.data.linkFg = result.linkFg->color();
 | |
| 		result.data.selectLinkFg = result.data.linkFg;
 | |
| 	}
 | |
| 	return result.data;
 | |
| }
 | |
| 
 | |
| not_null<BackgroundEmojiData*> ChatStyle::backgroundEmojiData(
 | |
| 		uint64 id) const {
 | |
| 	return &_backgroundEmojis[id];
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatStyle::coloredQuoteCache(
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	return coloredCache(_coloredQuoteCaches, selected, colorIndex);
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatStyle::coloredReplyCache(
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	return coloredCache(_coloredReplyCaches, selected, colorIndex);
 | |
| }
 | |
| 
 | |
| not_null<Text::QuotePaintCache*> ChatStyle::coloredCache(
 | |
| 		ColoredQuotePaintCaches &caches,
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) const {
 | |
| 	Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
 | |
| 
 | |
| 	const auto shift = (selected ? kColorIndexCount : 0);
 | |
| 	auto &cache = caches[shift + colorIndex];
 | |
| 	EnsureBlockquoteCache(cache, [&] {
 | |
| 		return coloredValues(selected, colorIndex);
 | |
| 	});
 | |
| 	return cache.get();
 | |
| }
 | |
| 
 | |
| const CornersPixmaps &ChatStyle::msgBotKbOverBgAddCornersSmall() const {
 | |
| 	EnsureCorners(
 | |
| 		_msgBotKbOverBgAddCornersSmall,
 | |
| 		BubbleRadiusSmall(),
 | |
| 		msgBotKbOverBgAdd());
 | |
| 	return _msgBotKbOverBgAddCornersSmall;
 | |
| }
 | |
| 
 | |
| const CornersPixmaps &ChatStyle::msgBotKbOverBgAddCornersLarge() const {
 | |
| 	EnsureCorners(
 | |
| 		_msgBotKbOverBgAddCornersLarge,
 | |
| 		BubbleRadiusLarge(),
 | |
| 		msgBotKbOverBgAdd());
 | |
| 	return _msgBotKbOverBgAddCornersLarge;
 | |
| }
 | |
| 
 | |
| const CornersPixmaps &ChatStyle::msgSelectOverlayCorners(
 | |
| 		CachedCornerRadius radius) const {
 | |
| 	const auto index = static_cast<int>(radius);
 | |
| 	Assert(index >= 0 && index < int(CachedCornerRadius::kCount));
 | |
| 
 | |
| 	EnsureCorners(
 | |
| 		_msgSelectOverlayCorners[index],
 | |
| 		CachedCornerRadiusValue(radius),
 | |
| 		msgSelectOverlay());
 | |
| 	return _msgSelectOverlayCorners[index];
 | |
| }
 | |
| 
 | |
| MessageStyle &ChatStyle::messageStyleRaw(bool outbg, bool selected) const {
 | |
| 	return _messageStyles[(outbg ? 2 : 0) + (selected ? 1 : 0)];
 | |
| }
 | |
| 
 | |
| MessageStyle &ChatStyle::messageIn() {
 | |
| 	return messageStyleRaw(false, false);
 | |
| }
 | |
| 
 | |
| MessageStyle &ChatStyle::messageInSelected() {
 | |
| 	return messageStyleRaw(false, true);
 | |
| }
 | |
| 
 | |
| MessageStyle &ChatStyle::messageOut() {
 | |
| 	return messageStyleRaw(true, false);
 | |
| }
 | |
| 
 | |
| MessageStyle &ChatStyle::messageOutSelected() {
 | |
| 	return messageStyleRaw(true, true);
 | |
| }
 | |
| 
 | |
| MessageImageStyle &ChatStyle::imageStyleRaw(bool selected) const {
 | |
| 	return _imageStyles[selected ? 1 : 0];
 | |
| }
 | |
| 
 | |
| MessageImageStyle &ChatStyle::image() {
 | |
| 	return imageStyleRaw(false);
 | |
| }
 | |
| 
 | |
| MessageImageStyle &ChatStyle::imageSelected() {
 | |
| 	return imageStyleRaw(true);
 | |
| }
 | |
| 
 | |
| void ChatStyle::make(style::color &my, const style::color &original) const {
 | |
| 	my = _colors[style::main_palette::indexOfColor(original)];
 | |
| }
 | |
| 
 | |
| void ChatStyle::make(style::icon &my, const style::icon &original) const {
 | |
| 	my = original.withPalette(*this);
 | |
| }
 | |
| 
 | |
| void ChatStyle::make(
 | |
| 		style::TextPalette &my,
 | |
| 		const style::TextPalette &original) const {
 | |
| 	my.linkAlwaysActive = original.linkAlwaysActive;
 | |
| 	make(my.linkFg, original.linkFg);
 | |
| 	make(my.monoFg, original.monoFg);
 | |
| 	make(my.spoilerFg, original.spoilerFg);
 | |
| 	make(my.selectBg, original.selectBg);
 | |
| 	make(my.selectFg, original.selectFg);
 | |
| 	make(my.selectLinkFg, original.selectLinkFg);
 | |
| 	make(my.selectMonoFg, original.selectMonoFg);
 | |
| 	make(my.selectSpoilerFg, original.selectSpoilerFg);
 | |
| 	make(my.selectOverlay, original.selectOverlay);
 | |
| }
 | |
| 
 | |
| void ChatStyle::make(
 | |
| 		style::TwoIconButton &my,
 | |
| 		const style::TwoIconButton &original) const {
 | |
| 	my = original;
 | |
| 	make(my.iconBelow, original.iconBelow);
 | |
| 	make(my.iconAbove, original.iconAbove);
 | |
| 	make(my.iconBelowOver, original.iconBelowOver);
 | |
| 	make(my.iconAboveOver, original.iconAboveOver);
 | |
| 	make(my.ripple.color, original.ripple.color);
 | |
| }
 | |
| 
 | |
| void ChatStyle::make(
 | |
| 		style::ScrollArea &my,
 | |
| 		const style::ScrollArea &original) const {
 | |
| 	my = original;
 | |
| 	make(my.bg, original.bg);
 | |
| 	make(my.bgOver, original.bgOver);
 | |
| 	make(my.barBg, original.barBg);
 | |
| 	make(my.barBgOver, original.barBgOver);
 | |
| 	make(my.shColor, original.shColor);
 | |
| }
 | |
| 
 | |
| template <typename Type>
 | |
| void ChatStyle::make(
 | |
| 		Type MessageStyle::*my,
 | |
| 		const Type &originalIn,
 | |
| 		const Type &originalInSelected,
 | |
| 		const Type &originalOut,
 | |
| 		const Type &originalOutSelected) {
 | |
| 	make(messageIn().*my, originalIn);
 | |
| 	make(messageInSelected().*my, originalInSelected);
 | |
| 	make(messageOut().*my, originalOut);
 | |
| 	make(messageOutSelected().*my, originalOutSelected);
 | |
| }
 | |
| 
 | |
| template <typename Type>
 | |
| void ChatStyle::make(
 | |
| 		Type MessageImageStyle::*my,
 | |
| 		const Type &original,
 | |
| 		const Type &originalSelected) {
 | |
| 	make(image().*my, original);
 | |
| 	make(imageSelected().*my, originalSelected);
 | |
| }
 | |
| 
 | |
| uint8 DecideColorIndex(uint64 id) {
 | |
| 	return id % kSimpleColorIndexCount;
 | |
| }
 | |
| 
 | |
| uint8 ColorIndexToPaletteIndex(uint8 colorIndex) {
 | |
| 	Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
 | |
| 
 | |
| 	const int8 map[] = { 0, 7, 4, 1, 6, 3, 5 };
 | |
| 	return map[colorIndex % kSimpleColorIndexCount];
 | |
| }
 | |
| 
 | |
| QColor FromNameFg(
 | |
| 		not_null<const ChatStyle*> st,
 | |
| 		bool selected,
 | |
| 		uint8 colorIndex) {
 | |
| 	return st->coloredValues(selected, colorIndex).name;
 | |
| }
 | |
| 
 | |
| void FillComplexOverlayRect(
 | |
| 		QPainter &p,
 | |
| 		QRect rect,
 | |
| 		const style::color &color,
 | |
| 		const CornersPixmaps &corners) {
 | |
| 	using namespace Images;
 | |
| 
 | |
| 	const auto pix = corners.p;
 | |
| 	const auto fillRect = [&](QRect rect) {
 | |
| 		p.fillRect(rect, color);
 | |
| 	};
 | |
| 	if (pix[kTopLeft].isNull()
 | |
| 		&& pix[kTopRight].isNull()
 | |
| 		&& pix[kBottomLeft].isNull()
 | |
| 		&& pix[kBottomRight].isNull()) {
 | |
| 		fillRect(rect);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	const auto ratio = style::DevicePixelRatio();
 | |
| 	const auto fillCorner = [&](int left, int top, int index) {
 | |
| 		p.drawPixmap(left, top, pix[index]);
 | |
| 	};
 | |
| 	const auto cornerSize = [&](int index) {
 | |
| 		const auto &p = pix[index];
 | |
| 		return p.isNull() ? 0 : p.width() / ratio;
 | |
| 	};
 | |
| 	const auto verticalSkip = [&](int left, int right) {
 | |
| 		return std::max(cornerSize(left), cornerSize(right));
 | |
| 	};
 | |
| 	const auto top = verticalSkip(kTopLeft, kTopRight);
 | |
| 	const auto bottom = verticalSkip(kBottomLeft, kBottomRight);
 | |
| 	if (top) {
 | |
| 		const auto left = cornerSize(kTopLeft);
 | |
| 		const auto right = cornerSize(kTopRight);
 | |
| 		if (left) {
 | |
| 			fillCorner(rect.left(), rect.top(), kTopLeft);
 | |
| 			if (const auto add = top - left) {
 | |
| 				fillRect({ rect.left(), rect.top() + left, left, add });
 | |
| 			}
 | |
| 		}
 | |
| 		if (const auto fill = rect.width() - left - right; fill > 0) {
 | |
| 			fillRect({ rect.left() + left, rect.top(), fill, top });
 | |
| 		}
 | |
| 		if (right) {
 | |
| 			fillCorner(
 | |
| 				rect.left() + rect.width() - right,
 | |
| 				rect.top(),
 | |
| 				kTopRight);
 | |
| 			if (const auto add = top - right) {
 | |
| 				fillRect({
 | |
| 					rect.left() + rect.width() - right,
 | |
| 					rect.top() + right,
 | |
| 					right,
 | |
| 					add,
 | |
| 				});
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if (const auto h = rect.height() - top - bottom; h > 0) {
 | |
| 		fillRect({ rect.left(), rect.top() + top, rect.width(), h });
 | |
| 	}
 | |
| 	if (bottom) {
 | |
| 		const auto left = cornerSize(kBottomLeft);
 | |
| 		const auto right = cornerSize(kBottomRight);
 | |
| 		if (left) {
 | |
| 			fillCorner(
 | |
| 				rect.left(),
 | |
| 				rect.top() + rect.height() - left,
 | |
| 				kBottomLeft);
 | |
| 			if (const auto add = bottom - left) {
 | |
| 				fillRect({
 | |
| 					rect.left(),
 | |
| 					rect.top() + rect.height() - bottom,
 | |
| 					left,
 | |
| 					add,
 | |
| 				});
 | |
| 			}
 | |
| 		}
 | |
| 		if (const auto fill = rect.width() - left - right; fill > 0) {
 | |
| 			fillRect({
 | |
| 				rect.left() + left,
 | |
| 				rect.top() + rect.height() - bottom,
 | |
| 				fill,
 | |
| 				bottom,
 | |
| 			});
 | |
| 		}
 | |
| 		if (right) {
 | |
| 			fillCorner(
 | |
| 				rect.left() + rect.width() - right,
 | |
| 				rect.top() + rect.height() - right,
 | |
| 				kBottomRight);
 | |
| 			if (const auto add = bottom - right) {
 | |
| 				fillRect({
 | |
| 					rect.left() + rect.width() - right,
 | |
| 					rect.top() + rect.height() - bottom,
 | |
| 					right,
 | |
| 					add,
 | |
| 				});
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FillComplexEllipse(
 | |
| 		QPainter &p,
 | |
| 		not_null<const ChatStyle*> st,
 | |
| 		QRect rect) {
 | |
| 	PainterHighQualityEnabler hq(p);
 | |
| 	p.setPen(Qt::NoPen);
 | |
| 	p.setBrush(st->msgSelectOverlay());
 | |
| 	p.drawEllipse(rect);
 | |
| }
 | |
| 
 | |
| } // namespace Ui
 | 
