| 
									
										
										
										
											2018-10-17 20:01:11 +02:00
										 |  |  | // Copyright (c) 2015 GitHub, Inc.
 | 
					
						
							|  |  |  | // Use of this source code is governed by the MIT license that can be
 | 
					
						
							|  |  |  | // found in the LICENSE file.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:00:50 +02:00
										 |  |  | #ifndef NOMINMAX
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | #define NOMINMAX
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:00:50 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/notifications/win/win32_desktop_notifications/toast.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-12 16:18:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <combaseapi.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <UIAutomation.h>
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | #include <uxtheme.h>
 | 
					
						
							|  |  |  | #include <windowsx.h>
 | 
					
						
							| 
									
										
										
										
											2017-04-03 10:48:18 +02:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2019-03-22 01:09:12 -07:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2018-09-12 19:25:56 -05:00
										 |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2018-09-12 16:18:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:01:26 -07:00
										 |  |  | #include "base/logging.h"
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/notifications/win/win32_desktop_notifications/common.h"
 | 
					
						
							|  |  |  | #include "shell/browser/notifications/win/win32_desktop_notifications/toast_uia.h"
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #pragma comment(lib, "msimg32.lib")
 | 
					
						
							|  |  |  | #pragma comment(lib, "uxtheme.lib")
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 11:23:36 +02:00
										 |  |  | using std::min; | 
					
						
							|  |  |  | using std::shared_ptr; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | namespace electron { | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | static COLORREF GetAccentColor() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   bool success = false; | 
					
						
							|  |  |  |   if (IsAppThemed()) { | 
					
						
							|  |  |  |     HKEY hkey; | 
					
						
							|  |  |  |     if (RegOpenKeyEx(HKEY_CURRENT_USER, | 
					
						
							|  |  |  |                      TEXT("SOFTWARE\\Microsoft\\Windows\\DWM"), 0, | 
					
						
							|  |  |  |                      KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { | 
					
						
							|  |  |  |       COLORREF color; | 
					
						
							|  |  |  |       DWORD type, size; | 
					
						
							|  |  |  |       if (RegQueryValueEx(hkey, TEXT("AccentColor"), nullptr, &type, | 
					
						
							|  |  |  |                           reinterpret_cast<BYTE*>(&color), | 
					
						
							|  |  |  |                           &(size = sizeof(color))) == ERROR_SUCCESS && | 
					
						
							|  |  |  |           type == REG_DWORD) { | 
					
						
							|  |  |  |         // convert from RGBA
 | 
					
						
							|  |  |  |         color = RGB(GetRValue(color), GetGValue(color), GetBValue(color)); | 
					
						
							|  |  |  |         success = true; | 
					
						
							|  |  |  |       } else if (RegQueryValueEx(hkey, TEXT("ColorizationColor"), nullptr, | 
					
						
							|  |  |  |                                  &type, reinterpret_cast<BYTE*>(&color), | 
					
						
							|  |  |  |                                  &(size = sizeof(color))) == ERROR_SUCCESS && | 
					
						
							|  |  |  |                  type == REG_DWORD) { | 
					
						
							|  |  |  |         // convert from BGRA
 | 
					
						
							|  |  |  |         color = RGB(GetBValue(color), GetGValue(color), GetRValue(color)); | 
					
						
							|  |  |  |         success = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       RegCloseKey(hkey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (success) | 
					
						
							|  |  |  |         return color; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return GetSysColor(COLOR_ACTIVECAPTION); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stretches a bitmap to the specified size, preserves alpha channel
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | static HBITMAP StretchBitmap(HBITMAP bitmap, unsigned width, unsigned height) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // We use StretchBlt for the scaling, but that discards the alpha channel.
 | 
					
						
							|  |  |  |   // So we first create a separate grayscale bitmap from the alpha channel,
 | 
					
						
							|  |  |  |   // scale that separately, and copy it back to the scaled color bitmap.
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   BITMAP bm; | 
					
						
							|  |  |  |   if (!GetObject(bitmap, sizeof(bm), &bm)) | 
					
						
							|  |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (width == 0 || height == 0) | 
					
						
							|  |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   HBITMAP result_bitmap = NULL; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   HDC hdc_screen = GetDC(NULL); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   HBITMAP alpha_src_bitmap; | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     BITMAPINFOHEADER bmi = {sizeof(BITMAPINFOHEADER)}; | 
					
						
							|  |  |  |     bmi.biWidth = bm.bmWidth; | 
					
						
							|  |  |  |     bmi.biHeight = bm.bmHeight; | 
					
						
							|  |  |  |     bmi.biPlanes = bm.bmPlanes; | 
					
						
							|  |  |  |     bmi.biBitCount = bm.bmBitsPixel; | 
					
						
							|  |  |  |     bmi.biCompression = BI_RGB; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void* alpha_src_bits; | 
					
						
							|  |  |  |     alpha_src_bitmap = | 
					
						
							|  |  |  |         CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&bmi), | 
					
						
							|  |  |  |                          DIB_RGB_COLORS, &alpha_src_bits, NULL, 0); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 14:14:23 +02:00
										 |  |  |     if (alpha_src_bitmap) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (GetDIBits(hdc_screen, bitmap, 0, 0, 0, | 
					
						
							|  |  |  |                     reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS) && | 
					
						
							|  |  |  |           bmi.biSizeImage > 0 && (bmi.biSizeImage % 4) == 0) { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |         auto* buf = reinterpret_cast<BYTE*>( | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |             _aligned_malloc(bmi.biSizeImage, sizeof(DWORD))); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |         if (buf) { | 
					
						
							|  |  |  |           GetDIBits(hdc_screen, bitmap, 0, bm.bmHeight, buf, | 
					
						
							|  |  |  |                     reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           const DWORD* src = reinterpret_cast<DWORD*>(buf); | 
					
						
							|  |  |  |           const DWORD* end = reinterpret_cast<DWORD*>(buf + bmi.biSizeImage); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           BYTE* dest = reinterpret_cast<BYTE*>(alpha_src_bits); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           for (; src != end; ++src, ++dest) { | 
					
						
							|  |  |  |             BYTE a = *src >> 24; | 
					
						
							|  |  |  |             *dest++ = a; | 
					
						
							|  |  |  |             *dest++ = a; | 
					
						
							|  |  |  |             *dest++ = a; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           _aligned_free(buf); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (alpha_src_bitmap) { | 
					
						
							|  |  |  |     BITMAPINFOHEADER bmi = {sizeof(BITMAPINFOHEADER)}; | 
					
						
							|  |  |  |     bmi.biWidth = width; | 
					
						
							|  |  |  |     bmi.biHeight = height; | 
					
						
							|  |  |  |     bmi.biPlanes = 1; | 
					
						
							|  |  |  |     bmi.biBitCount = 32; | 
					
						
							|  |  |  |     bmi.biCompression = BI_RGB; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void* color_bits; | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |     auto* color_bitmap = | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |         CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&bmi), | 
					
						
							|  |  |  |                          DIB_RGB_COLORS, &color_bits, NULL, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void* alpha_bits; | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |     auto* alpha_bitmap = | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |         CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&bmi), | 
					
						
							|  |  |  |                          DIB_RGB_COLORS, &alpha_bits, NULL, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HDC hdc = CreateCompatibleDC(NULL); | 
					
						
							|  |  |  |     HDC hdc_src = CreateCompatibleDC(NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (color_bitmap && alpha_bitmap && hdc && hdc_src) { | 
					
						
							|  |  |  |       SetStretchBltMode(hdc, HALFTONE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // resize color channels
 | 
					
						
							|  |  |  |       SelectObject(hdc, color_bitmap); | 
					
						
							|  |  |  |       SelectObject(hdc_src, bitmap); | 
					
						
							|  |  |  |       StretchBlt(hdc, 0, 0, width, height, hdc_src, 0, 0, bm.bmWidth, | 
					
						
							|  |  |  |                  bm.bmHeight, SRCCOPY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // resize alpha channel
 | 
					
						
							|  |  |  |       SelectObject(hdc, alpha_bitmap); | 
					
						
							|  |  |  |       SelectObject(hdc_src, alpha_src_bitmap); | 
					
						
							|  |  |  |       StretchBlt(hdc, 0, 0, width, height, hdc_src, 0, 0, bm.bmWidth, | 
					
						
							|  |  |  |                  bm.bmHeight, SRCCOPY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // flush before touching the bits
 | 
					
						
							|  |  |  |       GdiFlush(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // apply the alpha channel
 | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* dest = reinterpret_cast<BYTE*>(color_bits); | 
					
						
							|  |  |  |       auto* src = reinterpret_cast<const BYTE*>(alpha_bits); | 
					
						
							|  |  |  |       auto* end = src + (width * height * 4); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       while (src != end) { | 
					
						
							|  |  |  |         dest[3] = src[0]; | 
					
						
							|  |  |  |         dest += 4; | 
					
						
							|  |  |  |         src += 4; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // create the resulting bitmap
 | 
					
						
							|  |  |  |       result_bitmap = | 
					
						
							|  |  |  |           CreateDIBitmap(hdc_screen, &bmi, CBM_INIT, color_bits, | 
					
						
							|  |  |  |                          reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     if (hdc_src) | 
					
						
							|  |  |  |       DeleteDC(hdc_src); | 
					
						
							|  |  |  |     if (hdc) | 
					
						
							|  |  |  |       DeleteDC(hdc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (alpha_bitmap) | 
					
						
							|  |  |  |       DeleteObject(alpha_bitmap); | 
					
						
							|  |  |  |     if (color_bitmap) | 
					
						
							|  |  |  |       DeleteObject(color_bitmap); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     DeleteObject(alpha_src_bitmap); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ReleaseDC(NULL, hdc_screen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return result_bitmap; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-28 14:25:26 -07:00
										 |  |  | const TCHAR DesktopNotificationController::Toast::class_name_[] = | 
					
						
							|  |  |  |     TEXT("DesktopNotificationToast"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | DesktopNotificationController::Toast::Toast(HWND hwnd, | 
					
						
							|  |  |  |                                             shared_ptr<NotificationData>* data) | 
					
						
							|  |  |  |     : hwnd_(hwnd), data_(*data) { | 
					
						
							|  |  |  |   HDC hdc_screen = GetDC(NULL); | 
					
						
							|  |  |  |   hdc_ = CreateCompatibleDC(hdc_screen); | 
					
						
							|  |  |  |   ReleaseDC(NULL, hdc_screen); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | DesktopNotificationController::Toast::~Toast() { | 
					
						
							| 
									
										
										
										
											2018-09-12 16:18:35 +02:00
										 |  |  |   if (uia_) { | 
					
						
							|  |  |  |     auto* UiaDisconnectProvider = | 
					
						
							|  |  |  |         reinterpret_cast<decltype(&::UiaDisconnectProvider)>(GetProcAddress( | 
					
						
							|  |  |  |             GetModuleHandle(L"uiautomationcore.dll"), "UiaDisconnectProvider")); | 
					
						
							|  |  |  |     // first detach from the toast, then call UiaDisconnectProvider;
 | 
					
						
							|  |  |  |     // UiaDisconnectProvider may call WM_GETOBJECT and we don't want
 | 
					
						
							|  |  |  |     // it to return the object that we're disconnecting
 | 
					
						
							|  |  |  |     uia_->DetachToast(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (UiaDisconnectProvider) | 
					
						
							|  |  |  |       UiaDisconnectProvider(uia_); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uia_->Release(); | 
					
						
							|  |  |  |     uia_ = nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   DeleteDC(hdc_); | 
					
						
							|  |  |  |   if (bitmap_) | 
					
						
							|  |  |  |     DeleteBitmap(bitmap_); | 
					
						
							|  |  |  |   if (scaled_image_) | 
					
						
							|  |  |  |     DeleteBitmap(scaled_image_); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 14:14:23 +02:00
										 |  |  | void DesktopNotificationController::Toast::Register(HINSTANCE hinstance) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   WNDCLASSEX wc = {sizeof(wc)}; | 
					
						
							|  |  |  |   wc.lpfnWndProc = &Toast::WndProc; | 
					
						
							|  |  |  |   wc.lpszClassName = class_name_; | 
					
						
							|  |  |  |   wc.cbWndExtra = sizeof(Toast*); | 
					
						
							|  |  |  |   wc.hInstance = hinstance; | 
					
						
							|  |  |  |   wc.hCursor = LoadCursor(NULL, IDC_ARROW); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   RegisterClassEx(&wc); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | LRESULT DesktopNotificationController::Toast::WndProc(HWND hwnd, | 
					
						
							|  |  |  |                                                       UINT message, | 
					
						
							|  |  |  |                                                       WPARAM wparam, | 
					
						
							|  |  |  |                                                       LPARAM lparam) { | 
					
						
							|  |  |  |   switch (message) { | 
					
						
							|  |  |  |     case WM_CREATE: { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto*& cs = reinterpret_cast<const CREATESTRUCT*&>(lparam); | 
					
						
							|  |  |  |       auto* data = | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           static_cast<shared_ptr<NotificationData>*>(cs->lpCreateParams); | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* inst = new Toast(hwnd, data); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       SetWindowLongPtr(hwnd, 0, (LONG_PTR)inst); | 
					
						
							|  |  |  |     } break; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     case WM_NCDESTROY: | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       delete Get(hwnd); | 
					
						
							|  |  |  |       SetWindowLongPtr(hwnd, 0, 0); | 
					
						
							|  |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-12 16:18:35 +02:00
										 |  |  |     case WM_DESTROY: | 
					
						
							|  |  |  |       if (Get(hwnd)->uia_) { | 
					
						
							|  |  |  |         // free UI Automation resources associated with this window
 | 
					
						
							|  |  |  |         UiaReturnRawElementProvider(hwnd, 0, 0, nullptr); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     case WM_MOUSEACTIVATE: | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       return MA_NOACTIVATE; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-12 01:39:25 +02:00
										 |  |  |     case WM_TIMER: { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (wparam == TimerID_AutoDismiss) { | 
					
						
							| 
									
										
										
										
											2019-06-12 01:39:25 +02:00
										 |  |  |         auto* inst = Get(hwnd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Notification notification(inst->data_); | 
					
						
							|  |  |  |         inst->data_->controller->OnNotificationDismissed(notification); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inst->AutoDismiss(); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-06-12 01:39:25 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     case WM_LBUTTONDOWN: { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* inst = Get(hwnd); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       inst->Dismiss(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       Notification notification(inst->data_); | 
					
						
							|  |  |  |       if (inst->is_close_hot_) | 
					
						
							|  |  |  |         inst->data_->controller->OnNotificationDismissed(notification); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         inst->data_->controller->OnNotificationClicked(notification); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     case WM_MOUSEMOVE: { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* inst = Get(hwnd); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (!inst->is_highlighted_) { | 
					
						
							|  |  |  |         inst->is_highlighted_ = true; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |         TRACKMOUSEEVENT tme = {sizeof(tme), TME_LEAVE, hwnd}; | 
					
						
							|  |  |  |         TrackMouseEvent(&tme); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       POINT cursor = {GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)}; | 
					
						
							|  |  |  |       inst->is_close_hot_ = | 
					
						
							|  |  |  |           (PtInRect(&inst->close_button_rect_, cursor) != FALSE); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (!inst->is_non_interactive_) | 
					
						
							|  |  |  |         inst->CancelDismiss(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       inst->UpdateContents(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     case WM_MOUSELEAVE: { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* inst = Get(hwnd); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       inst->is_highlighted_ = false; | 
					
						
							|  |  |  |       inst->is_close_hot_ = false; | 
					
						
							|  |  |  |       inst->UpdateContents(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (!inst->ease_out_active_ && inst->ease_in_pos_ == 1.0f) | 
					
						
							|  |  |  |         inst->ScheduleDismissal(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       // Make sure stack collapse happens if needed
 | 
					
						
							|  |  |  |       inst->data_->controller->StartAnimation(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case WM_WINDOWPOSCHANGED: { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto*& wp = reinterpret_cast<WINDOWPOS*&>(lparam); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       if (wp->flags & SWP_HIDEWINDOW) { | 
					
						
							|  |  |  |         if (!IsWindowVisible(hwnd)) | 
					
						
							|  |  |  |           Get(hwnd)->is_highlighted_ = false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } break; | 
					
						
							| 
									
										
										
										
											2018-09-12 16:18:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     case WM_GETOBJECT: | 
					
						
							|  |  |  |       if (lparam == UiaRootObjectId) { | 
					
						
							|  |  |  |         auto* inst = Get(hwnd); | 
					
						
							|  |  |  |         if (!inst->uia_) { | 
					
						
							|  |  |  |           inst->uia_ = new UIAutomationInterface(inst); | 
					
						
							|  |  |  |           inst->uia_->AddRef(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // don't return the interface if it's being disconnected
 | 
					
						
							|  |  |  |         if (!inst->uia_->IsDetached()) { | 
					
						
							|  |  |  |           return UiaReturnRawElementProvider(hwnd, wparam, lparam, inst->uia_); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return DefWindowProc(hwnd, message, wparam, lparam); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 10:38:21 +02:00
										 |  |  | HWND DesktopNotificationController::Toast::Create( | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     HINSTANCE hinstance, | 
					
						
							| 
									
										
										
										
											2018-10-17 20:01:11 +02:00
										 |  |  |     shared_ptr<NotificationData> data) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return CreateWindowEx(WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOPMOST, | 
					
						
							|  |  |  |                         class_name_, nullptr, WS_POPUP, 0, 0, 0, 0, NULL, NULL, | 
					
						
							|  |  |  |                         hinstance, &data); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::Draw() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   const COLORREF accent = GetAccentColor(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   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
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |     auto* brush = CreateSolidBrush(back_color); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     RECT rc = {0, 0, toast_size_.cx, toast_size_.cy}; | 
					
						
							|  |  |  |     FillRect(hdc_, &rc, brush); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     DeleteBrush(brush); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SetBkMode(hdc_, TRANSPARENT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const auto close = L'\x2715'; | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |   auto* caption_font = data_->controller->GetCaptionFont(); | 
					
						
							|  |  |  |   auto* body_font = data_->controller->GetBodyFont(); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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}; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 14:14:23 +02:00
										 |  |  |     SelectFont(hdc_, caption_font); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     SetTextColor(hdc_, fore_color); | 
					
						
							|  |  |  |     DrawText(hdc_, data_->caption.data(), (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_, data_->body_text.data(), (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); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   is_content_updated_ = true; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::Invalidate() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   is_content_updated_ = false; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | bool DesktopNotificationController::Toast::IsRedrawNeeded() const { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return !is_content_updated_; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::UpdateBufferSize() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (hdc_) { | 
					
						
							|  |  |  |     SIZE new_size; | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       TEXTMETRIC tm_cap = {}; | 
					
						
							|  |  |  |       HFONT font = data_->controller->GetCaptionFont(); | 
					
						
							|  |  |  |       if (font) { | 
					
						
							|  |  |  |         SelectFont(hdc_, font); | 
					
						
							|  |  |  |         if (!GetTextMetrics(hdc_, &tm_cap)) | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       TEXTMETRIC tm_body = {}; | 
					
						
							|  |  |  |       font = data_->controller->GetBodyFont(); | 
					
						
							|  |  |  |       if (font) { | 
					
						
							|  |  |  |         SelectFont(hdc_, font); | 
					
						
							|  |  |  |         if (!GetTextMetrics(hdc_, &tm_body)) | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this->margin_ = {tm_cap.tmAveCharWidth * 2, tm_cap.tmAscent / 2}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       new_size.cx = margin_.cx + (32 * tm_cap.tmAveCharWidth) + margin_.cx; | 
					
						
							|  |  |  |       new_size.cy = margin_.cy + (tm_cap.tmHeight) + margin_.cy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!data_->body_text.empty()) | 
					
						
							|  |  |  |         new_size.cy += margin_.cy + (3 * tm_body.tmHeight); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (data_->image) { | 
					
						
							|  |  |  |         BITMAP bm; | 
					
						
							|  |  |  |         if (GetObject(data_->image, sizeof(bm), &bm)) { | 
					
						
							|  |  |  |           // cap the image size
 | 
					
						
							|  |  |  |           const int max_dim_size = 80; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           auto width = bm.bmWidth; | 
					
						
							|  |  |  |           auto height = bm.bmHeight; | 
					
						
							|  |  |  |           if (width < height) { | 
					
						
							|  |  |  |             if (height > max_dim_size) { | 
					
						
							|  |  |  |               width = width * max_dim_size / height; | 
					
						
							|  |  |  |               height = max_dim_size; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           } else { | 
					
						
							|  |  |  |             if (width > max_dim_size) { | 
					
						
							|  |  |  |               height = height * max_dim_size / width; | 
					
						
							|  |  |  |               width = max_dim_size; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           ScreenMetrics scr; | 
					
						
							|  |  |  |           SIZE image_draw_size = {scr.X(width), scr.Y(height)}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           new_size.cx += image_draw_size.cx + margin_.cx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           auto height_with_image = | 
					
						
							|  |  |  |               margin_.cy + (image_draw_size.cy) + margin_.cy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (new_size.cy < height_with_image) | 
					
						
							|  |  |  |             new_size.cy = height_with_image; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           UpdateScaledImage(image_draw_size); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     if (new_size.cx != this->toast_size_.cx || | 
					
						
							|  |  |  |         new_size.cy != this->toast_size_.cy) { | 
					
						
							|  |  |  |       HDC hdc_screen = GetDC(NULL); | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |       auto* new_bitmap = | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           CreateCompatibleBitmap(hdc_screen, new_size.cx, new_size.cy); | 
					
						
							|  |  |  |       ReleaseDC(NULL, hdc_screen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (new_bitmap) { | 
					
						
							|  |  |  |         if (SelectBitmap(hdc_, new_bitmap)) { | 
					
						
							|  |  |  |           RECT dirty1 = {}, dirty2 = {}; | 
					
						
							|  |  |  |           if (toast_size_.cx < new_size.cx) { | 
					
						
							|  |  |  |             dirty1 = {toast_size_.cx, 0, new_size.cx, toast_size_.cy}; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (toast_size_.cy < new_size.cy) { | 
					
						
							|  |  |  |             dirty2 = {0, toast_size_.cy, new_size.cx, new_size.cy}; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (this->bitmap_) | 
					
						
							|  |  |  |             DeleteBitmap(this->bitmap_); | 
					
						
							|  |  |  |           this->bitmap_ = new_bitmap; | 
					
						
							|  |  |  |           this->toast_size_ = new_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           Invalidate(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // Resize also the DWM buffer to prevent flicker during
 | 
					
						
							|  |  |  |           // window resizing. Make sure any existing data is not
 | 
					
						
							|  |  |  |           // overwritten by marking the dirty region.
 | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             POINT origin = {0, 0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             UPDATELAYEREDWINDOWINFO ulw; | 
					
						
							|  |  |  |             ulw.cbSize = sizeof(ulw); | 
					
						
							|  |  |  |             ulw.hdcDst = NULL; | 
					
						
							|  |  |  |             ulw.pptDst = nullptr; | 
					
						
							|  |  |  |             ulw.psize = &toast_size_; | 
					
						
							|  |  |  |             ulw.hdcSrc = hdc_; | 
					
						
							|  |  |  |             ulw.pptSrc = &origin; | 
					
						
							|  |  |  |             ulw.crKey = 0; | 
					
						
							|  |  |  |             ulw.pblend = nullptr; | 
					
						
							|  |  |  |             ulw.dwFlags = 0; | 
					
						
							|  |  |  |             ulw.prcDirty = &dirty1; | 
					
						
							|  |  |  |             auto b1 = UpdateLayeredWindowIndirect(hwnd_, &ulw); | 
					
						
							|  |  |  |             ulw.prcDirty = &dirty2; | 
					
						
							|  |  |  |             auto b2 = UpdateLayeredWindowIndirect(hwnd_, &ulw); | 
					
						
							| 
									
										
										
										
											2018-07-10 08:01:26 -07:00
										 |  |  |             DCHECK(b1 && b2); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         DeleteBitmap(new_bitmap); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::UpdateScaledImage(const SIZE& size) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   BITMAP bm; | 
					
						
							|  |  |  |   if (!GetObject(scaled_image_, sizeof(bm), &bm) || bm.bmWidth != size.cx || | 
					
						
							|  |  |  |       bm.bmHeight != size.cy) { | 
					
						
							|  |  |  |     if (scaled_image_) | 
					
						
							|  |  |  |       DeleteBitmap(scaled_image_); | 
					
						
							|  |  |  |     scaled_image_ = StretchBitmap(data_->image, size.cx, size.cy); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::UpdateContents() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   Draw(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (IsWindowVisible(hwnd_)) { | 
					
						
							|  |  |  |     RECT rc; | 
					
						
							|  |  |  |     GetWindowRect(hwnd_, &rc); | 
					
						
							|  |  |  |     POINT origin = {0, 0}; | 
					
						
							|  |  |  |     SIZE size = {rc.right - rc.left, rc.bottom - rc.top}; | 
					
						
							|  |  |  |     UpdateLayeredWindow(hwnd_, NULL, nullptr, &size, hdc_, &origin, 0, nullptr, | 
					
						
							|  |  |  |                         0); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::Dismiss() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (!is_non_interactive_) { | 
					
						
							|  |  |  |     // Set a flag to prevent further interaction. We don't disable the HWND
 | 
					
						
							|  |  |  |     // because we still want to receive mouse move messages in order to keep
 | 
					
						
							|  |  |  |     // the toast under the cursor and not collapse it while dismissing.
 | 
					
						
							|  |  |  |     is_non_interactive_ = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     AutoDismiss(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::AutoDismiss() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   KillTimer(hwnd_, TimerID_AutoDismiss); | 
					
						
							|  |  |  |   StartEaseOut(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::CancelDismiss() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   KillTimer(hwnd_, TimerID_AutoDismiss); | 
					
						
							|  |  |  |   ease_out_active_ = false; | 
					
						
							|  |  |  |   ease_out_pos_ = 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::ScheduleDismissal() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   ULONG duration; | 
					
						
							|  |  |  |   if (!SystemParametersInfo(SPI_GETMESSAGEDURATION, 0, &duration, 0)) { | 
					
						
							|  |  |  |     duration = 5; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   SetTimer(hwnd_, TimerID_AutoDismiss, duration * 1000, nullptr); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::ResetContents() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (scaled_image_) { | 
					
						
							|  |  |  |     DeleteBitmap(scaled_image_); | 
					
						
							|  |  |  |     scaled_image_ = NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   Invalidate(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::PopUp(int y) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   vertical_pos_target_ = vertical_pos_ = y; | 
					
						
							|  |  |  |   StartEaseIn(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::SetVerticalPosition(int y) { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // Don't restart animation if current target is the same
 | 
					
						
							|  |  |  |   if (y == vertical_pos_target_) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Make sure the new animation's origin is at the current position
 | 
					
						
							|  |  |  |   vertical_pos_ += static_cast<int>((vertical_pos_target_ - vertical_pos_) * | 
					
						
							|  |  |  |                                     stack_collapse_pos_); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Set new target position and start the animation
 | 
					
						
							|  |  |  |   vertical_pos_target_ = y; | 
					
						
							|  |  |  |   stack_collapse_start_ = GetTickCount(); | 
					
						
							|  |  |  |   data_->controller->StartAnimation(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  | HDWP DesktopNotificationController::Toast::Animate(HDWP hdwp, | 
					
						
							|  |  |  |                                                    const POINT& origin) { | 
					
						
							|  |  |  |   UpdateBufferSize(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (IsRedrawNeeded()) | 
					
						
							|  |  |  |     Draw(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   POINT src_origin = {0, 0}; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   UPDATELAYEREDWINDOWINFO ulw; | 
					
						
							|  |  |  |   ulw.cbSize = sizeof(ulw); | 
					
						
							|  |  |  |   ulw.hdcDst = NULL; | 
					
						
							|  |  |  |   ulw.pptDst = nullptr; | 
					
						
							|  |  |  |   ulw.psize = nullptr; | 
					
						
							|  |  |  |   ulw.hdcSrc = hdc_; | 
					
						
							|  |  |  |   ulw.pptSrc = &src_origin; | 
					
						
							|  |  |  |   ulw.crKey = 0; | 
					
						
							|  |  |  |   ulw.pblend = nullptr; | 
					
						
							|  |  |  |   ulw.dwFlags = 0; | 
					
						
							|  |  |  |   ulw.prcDirty = nullptr; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   POINT pt = {0, 0}; | 
					
						
							|  |  |  |   SIZE size = {0, 0}; | 
					
						
							|  |  |  |   BLENDFUNCTION blend; | 
					
						
							|  |  |  |   UINT dwpFlags = | 
					
						
							|  |  |  |       SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOCOPYBITS; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   auto ease_in_pos = AnimateEaseIn(); | 
					
						
							|  |  |  |   auto ease_out_pos = AnimateEaseOut(); | 
					
						
							|  |  |  |   auto stack_collapse_pos = AnimateStackCollapse(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   auto y_offset = (vertical_pos_target_ - vertical_pos_) * stack_collapse_pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   size.cx = static_cast<int>(toast_size_.cx * ease_in_pos); | 
					
						
							|  |  |  |   size.cy = toast_size_.cy; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   pt.x = origin.x - size.cx; | 
					
						
							|  |  |  |   pt.y = static_cast<int>(origin.y - vertical_pos_ - y_offset - size.cy); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   ulw.pptDst = &pt; | 
					
						
							|  |  |  |   ulw.psize = &size; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (ease_in_active_ && ease_in_pos == 1.0f) { | 
					
						
							|  |  |  |     ease_in_active_ = false; | 
					
						
							|  |  |  |     ScheduleDismissal(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   this->ease_in_pos_ = ease_in_pos; | 
					
						
							|  |  |  |   this->stack_collapse_pos_ = stack_collapse_pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (ease_out_pos != this->ease_out_pos_) { | 
					
						
							|  |  |  |     blend.BlendOp = AC_SRC_OVER; | 
					
						
							|  |  |  |     blend.BlendFlags = 0; | 
					
						
							|  |  |  |     blend.SourceConstantAlpha = (BYTE)(255 * (1.0f - ease_out_pos)); | 
					
						
							|  |  |  |     blend.AlphaFormat = 0; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     ulw.pblend = &blend; | 
					
						
							|  |  |  |     ulw.dwFlags = ULW_ALPHA; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     this->ease_out_pos_ = ease_out_pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |     if (ease_out_pos == 1.0f) { | 
					
						
							|  |  |  |       ease_out_active_ = false; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |       dwpFlags &= ~SWP_SHOWWINDOW; | 
					
						
							|  |  |  |       dwpFlags |= SWP_HIDEWINDOW; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (stack_collapse_pos == 1.0f) { | 
					
						
							|  |  |  |     vertical_pos_ = vertical_pos_target_; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // `UpdateLayeredWindowIndirect` updates position, size, and transparency.
 | 
					
						
							|  |  |  |   // `DeferWindowPos` updates z-order, and also position and size in case
 | 
					
						
							|  |  |  |   // ULWI fails, which can happen when one of the dimensions is zero (e.g.
 | 
					
						
							|  |  |  |   // at the beginning of ease-in).
 | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 16:45:45 -07:00
										 |  |  |   UpdateLayeredWindowIndirect(hwnd_, &ulw); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   hdwp = DeferWindowPos(hdwp, hwnd_, HWND_TOPMOST, pt.x, pt.y, size.cx, size.cy, | 
					
						
							|  |  |  |                         dwpFlags); | 
					
						
							|  |  |  |   return hdwp; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::StartEaseIn() { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:01:26 -07:00
										 |  |  |   DCHECK(!ease_in_active_); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   ease_in_start_ = GetTickCount(); | 
					
						
							|  |  |  |   ease_in_active_ = true; | 
					
						
							|  |  |  |   data_->controller->StartAnimation(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | void DesktopNotificationController::Toast::StartEaseOut() { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:01:26 -07:00
										 |  |  |   DCHECK(!ease_out_active_); | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   ease_out_start_ = GetTickCount(); | 
					
						
							|  |  |  |   ease_out_active_ = true; | 
					
						
							|  |  |  |   data_->controller->StartAnimation(); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | bool DesktopNotificationController::Toast::IsStackCollapseActive() const { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return (vertical_pos_ != vertical_pos_target_); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | float DesktopNotificationController::Toast::AnimateEaseIn() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (!ease_in_active_) | 
					
						
							|  |  |  |     return ease_in_pos_; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   constexpr DWORD duration = 500; | 
					
						
							|  |  |  |   auto elapsed = GetTickCount() - ease_in_start_; | 
					
						
							|  |  |  |   float time = std::min(duration, elapsed) / static_cast<float>(duration); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // decelerating exponential ease
 | 
					
						
							|  |  |  |   const float a = -8.0f; | 
					
						
							|  |  |  |   auto pos = (std::exp(a * time) - 1.0f) / (std::exp(a) - 1.0f); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | float DesktopNotificationController::Toast::AnimateEaseOut() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (!ease_out_active_) | 
					
						
							|  |  |  |     return ease_out_pos_; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   constexpr DWORD duration = 120; | 
					
						
							|  |  |  |   auto elapsed = GetTickCount() - ease_out_start_; | 
					
						
							|  |  |  |   float time = std::min(duration, elapsed) / static_cast<float>(duration); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // accelerating circle ease
 | 
					
						
							|  |  |  |   auto pos = 1.0f - std::sqrt(1 - time * time); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-03 12:11:39 +02:00
										 |  |  | float DesktopNotificationController::Toast::AnimateStackCollapse() { | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   if (!IsStackCollapseActive()) | 
					
						
							|  |  |  |     return stack_collapse_pos_; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   constexpr DWORD duration = 500; | 
					
						
							|  |  |  |   auto elapsed = GetTickCount() - stack_collapse_start_; | 
					
						
							|  |  |  |   float time = std::min(duration, elapsed) / static_cast<float>(duration); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   // decelerating exponential ease
 | 
					
						
							|  |  |  |   const float a = -8.0f; | 
					
						
							|  |  |  |   auto pos = (std::exp(a * time) - 1.0f) / (std::exp(a) - 1.0f); | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-17 21:56:12 -04:00
										 |  |  |   return pos; | 
					
						
							| 
									
										
										
										
											2017-03-15 13:56:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | }  // namespace electron
 |