in shell/browser/notifications/win/win32_desktop_notifications/toast.cc [367:509]
void DesktopNotificationController::Toast::Draw() {
const COLORREF accent = GetAccentColor();
COLORREF back_color;
{
// base background color is 2/3 of accent
// highlighted adds a bit of intensity to every channel
int h = is_highlighted_ ? (0xff / 20) : 0;
back_color = RGB(min(0xff, (GetRValue(accent) * 2 / 3) + h),
min(0xff, (GetGValue(accent) * 2 / 3) + h),
min(0xff, (GetBValue(accent) * 2 / 3) + h));
}
const float back_luma = (GetRValue(back_color) * 0.299f / 255) +
(GetGValue(back_color) * 0.587f / 255) +
(GetBValue(back_color) * 0.114f / 255);
const struct {
float r, g, b;
} back_f = {
GetRValue(back_color) / 255.0f,
GetGValue(back_color) / 255.0f,
GetBValue(back_color) / 255.0f,
};
COLORREF fore_color, dimmed_color;
{
// based on the lightness of background, we draw foreground in light
// or dark shades of gray blended onto the background with slight
// transparency to avoid sharp contrast
constexpr float alpha = 0.9f;
constexpr float intensity_light[] = {(1.0f * alpha), (0.8f * alpha)};
constexpr float intensity_dark[] = {(0.1f * alpha), (0.3f * alpha)};
// select foreground intensity values (light or dark)
auto& i = (back_luma < 0.6f) ? intensity_light : intensity_dark;
float r, g, b;
r = i[0] + back_f.r * (1 - alpha);
g = i[0] + back_f.g * (1 - alpha);
b = i[0] + back_f.b * (1 - alpha);
fore_color = RGB(r * 0xff, g * 0xff, b * 0xff);
r = i[1] + back_f.r * (1 - alpha);
g = i[1] + back_f.g * (1 - alpha);
b = i[1] + back_f.b * (1 - alpha);
dimmed_color = RGB(r * 0xff, g * 0xff, b * 0xff);
}
// Draw background
{
auto* brush = CreateSolidBrush(back_color);
RECT rc = {0, 0, toast_size_.cx, toast_size_.cy};
FillRect(hdc_, &rc, brush);
DeleteBrush(brush);
}
SetBkMode(hdc_, TRANSPARENT);
const auto close = L'\x2715';
auto* caption_font = data_->controller->GetCaptionFont();
auto* body_font = data_->controller->GetBodyFont();
TEXTMETRIC tm_cap;
SelectFont(hdc_, caption_font);
GetTextMetrics(hdc_, &tm_cap);
auto text_offset_x = margin_.cx;
BITMAP image_info = {};
if (scaled_image_) {
GetObject(scaled_image_, sizeof(image_info), &image_info);
text_offset_x += margin_.cx + image_info.bmWidth;
}
// calculate close button rect
POINT close_pos;
{
SIZE extent = {};
GetTextExtentPoint32W(hdc_, &close, 1, &extent);
close_button_rect_.right = toast_size_.cx;
close_button_rect_.top = 0;
close_pos.x = close_button_rect_.right - margin_.cy - extent.cx;
close_pos.y = close_button_rect_.top + margin_.cy;
close_button_rect_.left = close_pos.x - margin_.cy;
close_button_rect_.bottom = close_pos.y + extent.cy + margin_.cy;
}
// image
if (scaled_image_) {
HDC hdc_image = CreateCompatibleDC(NULL);
SelectBitmap(hdc_image, scaled_image_);
BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
AlphaBlend(hdc_, margin_.cx, margin_.cy, image_info.bmWidth,
image_info.bmHeight, hdc_image, 0, 0, image_info.bmWidth,
image_info.bmHeight, blend);
DeleteDC(hdc_image);
}
// caption
{
RECT rc = {text_offset_x, margin_.cy, close_button_rect_.left,
toast_size_.cy};
SelectFont(hdc_, caption_font);
SetTextColor(hdc_, fore_color);
DrawText(hdc_, base::as_wcstr(data_->caption),
(UINT)data_->caption.length(), &rc,
DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX);
}
// body text
if (!data_->body_text.empty()) {
RECT rc = {text_offset_x, 2 * margin_.cy + tm_cap.tmAscent,
toast_size_.cx - margin_.cx, toast_size_.cy - margin_.cy};
SelectFont(hdc_, body_font);
SetTextColor(hdc_, dimmed_color);
DrawText(hdc_, base::as_wcstr(data_->body_text),
(UINT)data_->body_text.length(), &rc,
DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | DT_END_ELLIPSIS |
DT_EDITCONTROL);
}
// close button
{
SelectFont(hdc_, caption_font);
SetTextColor(hdc_, is_close_hot_ ? fore_color : dimmed_color);
ExtTextOut(hdc_, close_pos.x, close_pos.y, 0, nullptr, &close, 1, nullptr);
}
is_content_updated_ = true;
}