format previously misses .cc files

This commit is contained in:
Shelley Vohr 2018-04-17 21:56:12 -04:00
parent 284aca68c0
commit 49c8c31220
No known key found for this signature in database
GPG key ID: F13993A75599653C
36 changed files with 1279 additions and 1411 deletions

View file

@ -13,393 +13,405 @@ using std::shared_ptr;
namespace brightray {
HBITMAP CopyBitmap(HBITMAP bitmap) {
HBITMAP ret = NULL;
HBITMAP ret = NULL;
BITMAP bm;
if (bitmap && GetObject(bitmap, sizeof(bm), &bm)) {
HDC hdc_screen = GetDC(NULL);
ret = CreateCompatibleBitmap(hdc_screen, bm.bmWidth, bm.bmHeight);
ReleaseDC(NULL, hdc_screen);
BITMAP bm;
if (bitmap && GetObject(bitmap, sizeof(bm), &bm)) {
HDC hdc_screen = GetDC(NULL);
ret = CreateCompatibleBitmap(hdc_screen, bm.bmWidth, bm.bmHeight);
ReleaseDC(NULL, hdc_screen);
if (ret) {
HDC hdc_src = CreateCompatibleDC(NULL);
HDC hdc_dst = CreateCompatibleDC(NULL);
SelectBitmap(hdc_src, bitmap);
SelectBitmap(hdc_dst, ret);
BitBlt(hdc_dst, 0, 0, bm.bmWidth, bm.bmHeight,
hdc_src, 0, 0, SRCCOPY);
DeleteDC(hdc_dst);
DeleteDC(hdc_src);
}
if (ret) {
HDC hdc_src = CreateCompatibleDC(NULL);
HDC hdc_dst = CreateCompatibleDC(NULL);
SelectBitmap(hdc_src, bitmap);
SelectBitmap(hdc_dst, ret);
BitBlt(hdc_dst, 0, 0, bm.bmWidth, bm.bmHeight, hdc_src, 0, 0, SRCCOPY);
DeleteDC(hdc_dst);
DeleteDC(hdc_src);
}
}
return ret;
return ret;
}
HINSTANCE DesktopNotificationController::RegisterWndClasses() {
// We keep a static `module` variable which serves a dual purpose:
// 1. Stores the HINSTANCE where the window classes are registered,
// which can be passed to `CreateWindow`
// 2. Indicates whether we already attempted the registration so that
// we don't do it twice (we don't retry even if registration fails,
// as there is no point).
static HMODULE module = NULL;
// We keep a static `module` variable which serves a dual purpose:
// 1. Stores the HINSTANCE where the window classes are registered,
// which can be passed to `CreateWindow`
// 2. Indicates whether we already attempted the registration so that
// we don't do it twice (we don't retry even if registration fails,
// as there is no point).
static HMODULE module = NULL;
if (!module) {
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&RegisterWndClasses),
&module)) {
Toast::Register(module);
if (!module) {
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&RegisterWndClasses),
&module)) {
Toast::Register(module);
WNDCLASSEX wc = { sizeof(wc) };
wc.lpfnWndProc = &WndProc;
wc.lpszClassName = class_name_;
wc.cbWndExtra = sizeof(DesktopNotificationController*);
wc.hInstance = module;
WNDCLASSEX wc = {sizeof(wc)};
wc.lpfnWndProc = &WndProc;
wc.lpszClassName = class_name_;
wc.cbWndExtra = sizeof(DesktopNotificationController*);
wc.hInstance = module;
RegisterClassEx(&wc);
}
RegisterClassEx(&wc);
}
}
return module;
return module;
}
DesktopNotificationController::DesktopNotificationController(
unsigned maximum_toasts) {
instances_.reserve(maximum_toasts);
instances_.reserve(maximum_toasts);
}
DesktopNotificationController::~DesktopNotificationController() {
for (auto&& inst : instances_) DestroyToast(inst);
if (hwnd_controller_) DestroyWindow(hwnd_controller_);
ClearAssets();
for (auto&& inst : instances_)
DestroyToast(inst);
if (hwnd_controller_)
DestroyWindow(hwnd_controller_);
ClearAssets();
}
LRESULT CALLBACK DesktopNotificationController::WndProc(
HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
switch (message) {
case WM_CREATE:
{
auto& cs = reinterpret_cast<const CREATESTRUCT*&>(lparam);
SetWindowLongPtr(hwnd, 0, (LONG_PTR)cs->lpCreateParams);
}
break;
LRESULT CALLBACK DesktopNotificationController::WndProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
switch (message) {
case WM_CREATE: {
auto& cs = reinterpret_cast<const CREATESTRUCT*&>(lparam);
SetWindowLongPtr(hwnd, 0, (LONG_PTR)cs->lpCreateParams);
} break;
case WM_TIMER:
if (wparam == TimerID_Animate) {
Get(hwnd)->AnimateAll();
}
return 0;
if (wparam == TimerID_Animate) {
Get(hwnd)->AnimateAll();
}
return 0;
case WM_DISPLAYCHANGE:
{
auto inst = Get(hwnd);
inst->ClearAssets();
inst->AnimateAll();
}
break;
case WM_DISPLAYCHANGE: {
auto inst = Get(hwnd);
inst->ClearAssets();
inst->AnimateAll();
} break;
case WM_SETTINGCHANGE:
if (wparam == SPI_SETWORKAREA) {
Get(hwnd)->AnimateAll();
}
break;
}
if (wparam == SPI_SETWORKAREA) {
Get(hwnd)->AnimateAll();
}
break;
}
return DefWindowProc(hwnd, message, wparam, lparam);
return DefWindowProc(hwnd, message, wparam, lparam);
}
void DesktopNotificationController::StartAnimation() {
_ASSERT(hwnd_controller_);
_ASSERT(hwnd_controller_);
if (!is_animating_ && hwnd_controller_) {
// NOTE: 15ms is shorter than what we'd need for 60 fps, but since
// the timer is not accurate we must request a higher frame rate
// to get at least 60
if (!is_animating_ && hwnd_controller_) {
// NOTE: 15ms is shorter than what we'd need for 60 fps, but since
// the timer is not accurate we must request a higher frame rate
// to get at least 60
SetTimer(hwnd_controller_, TimerID_Animate, 15, nullptr);
is_animating_ = true;
}
SetTimer(hwnd_controller_, TimerID_Animate, 15, nullptr);
is_animating_ = true;
}
}
HFONT DesktopNotificationController::GetCaptionFont() {
InitializeFonts();
return caption_font_;
InitializeFonts();
return caption_font_;
}
HFONT DesktopNotificationController::GetBodyFont() {
InitializeFonts();
return body_font_;
InitializeFonts();
return body_font_;
}
void DesktopNotificationController::InitializeFonts() {
if (!body_font_) {
NONCLIENTMETRICS metrics = { sizeof(metrics) };
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) {
auto base_height = metrics.lfMessageFont.lfHeight;
if (!body_font_) {
NONCLIENTMETRICS metrics = {sizeof(metrics)};
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) {
auto base_height = metrics.lfMessageFont.lfHeight;
HDC hdc = GetDC(NULL);
auto base_dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
HDC hdc = GetDC(NULL);
auto base_dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
ScreenMetrics scr;
ScreenMetrics scr;
metrics.lfMessageFont.lfHeight =
(LONG)ScaleForDpi(base_height * 1.1f, scr.dpi_y, base_dpi_y);
body_font_ = CreateFontIndirect(&metrics.lfMessageFont);
metrics.lfMessageFont.lfHeight =
(LONG)ScaleForDpi(base_height * 1.1f, scr.dpi_y, base_dpi_y);
body_font_ = CreateFontIndirect(&metrics.lfMessageFont);
if (caption_font_) DeleteFont(caption_font_);
metrics.lfMessageFont.lfHeight =
(LONG)ScaleForDpi(base_height * 1.4f, scr.dpi_y, base_dpi_y);
caption_font_ = CreateFontIndirect(&metrics.lfMessageFont);
}
if (caption_font_)
DeleteFont(caption_font_);
metrics.lfMessageFont.lfHeight =
(LONG)ScaleForDpi(base_height * 1.4f, scr.dpi_y, base_dpi_y);
caption_font_ = CreateFontIndirect(&metrics.lfMessageFont);
}
}
}
void DesktopNotificationController::ClearAssets() {
if (caption_font_) { DeleteFont(caption_font_); caption_font_ = NULL; }
if (body_font_) { DeleteFont(body_font_); body_font_ = NULL; }
if (caption_font_) {
DeleteFont(caption_font_);
caption_font_ = NULL;
}
if (body_font_) {
DeleteFont(body_font_);
body_font_ = NULL;
}
}
void DesktopNotificationController::AnimateAll() {
// NOTE: This function refreshes position and size of all toasts according
// to all current conditions. Animation time is only one of the variables
// influencing them. Screen resolution is another.
// NOTE: This function refreshes position and size of all toasts according
// to all current conditions. Animation time is only one of the variables
// influencing them. Screen resolution is another.
bool keep_animating = false;
bool keep_animating = false;
if (!instances_.empty()) {
RECT work_area;
if (SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0)) {
ScreenMetrics metrics;
POINT origin = { work_area.right,
work_area.bottom - metrics.Y(toast_margin_) };
if (!instances_.empty()) {
RECT work_area;
if (SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0)) {
ScreenMetrics metrics;
POINT origin = {work_area.right,
work_area.bottom - metrics.Y(toast_margin_)};
auto hdwp =
BeginDeferWindowPos(static_cast<int>(instances_.size()));
auto hdwp = BeginDeferWindowPos(static_cast<int>(instances_.size()));
for (auto&& inst : instances_) {
if (!inst.hwnd) continue;
for (auto&& inst : instances_) {
if (!inst.hwnd)
continue;
auto notification = Toast::Get(inst.hwnd);
hdwp = notification->Animate(hdwp, origin);
if (!hdwp) break;
keep_animating |= notification->IsAnimationActive();
}
auto notification = Toast::Get(inst.hwnd);
hdwp = notification->Animate(hdwp, origin);
if (!hdwp)
break;
keep_animating |= notification->IsAnimationActive();
}
if (hdwp) EndDeferWindowPos(hdwp);
}
if (hdwp)
EndDeferWindowPos(hdwp);
}
}
if (!keep_animating) {
_ASSERT(hwnd_controller_);
if (hwnd_controller_) KillTimer(hwnd_controller_, TimerID_Animate);
is_animating_ = false;
if (!keep_animating) {
_ASSERT(hwnd_controller_);
if (hwnd_controller_)
KillTimer(hwnd_controller_, TimerID_Animate);
is_animating_ = false;
}
// Purge dismissed notifications and collapse the stack between
// items which are highlighted
if (!instances_.empty()) {
auto is_alive = [](ToastInstance& inst) {
return inst.hwnd && IsWindowVisible(inst.hwnd);
};
auto is_highlighted = [](ToastInstance& inst) {
return inst.hwnd && Toast::Get(inst.hwnd)->IsHighlighted();
};
for (auto it = instances_.begin();; ++it) {
// find next highlighted item
auto it2 = find_if(it, instances_.end(), is_highlighted);
// collapse the stack in front of the highlighted item
it = stable_partition(it, it2, is_alive);
// purge the dead items
for_each(it, it2, [this](auto&& inst) { DestroyToast(inst); });
if (it2 == instances_.end()) {
instances_.erase(it, it2);
break;
}
it = move(it2);
}
}
// Purge dismissed notifications and collapse the stack between
// items which are highlighted
if (!instances_.empty()) {
auto is_alive = [](ToastInstance& inst) {
return inst.hwnd && IsWindowVisible(inst.hwnd);
};
// Set new toast positions
if (!instances_.empty()) {
ScreenMetrics metrics;
auto margin = metrics.Y(toast_margin_);
auto is_highlighted = [](ToastInstance& inst) {
return inst.hwnd && Toast::Get(inst.hwnd)->IsHighlighted();
};
int target_pos = 0;
for (auto&& inst : instances_) {
if (inst.hwnd) {
auto toast = Toast::Get(inst.hwnd);
for (auto it = instances_.begin();; ++it) {
// find next highlighted item
auto it2 = find_if(it, instances_.end(), is_highlighted);
if (toast->IsHighlighted())
target_pos = toast->GetVerticalPosition();
else
toast->SetVerticalPosition(target_pos);
// collapse the stack in front of the highlighted item
it = stable_partition(it, it2, is_alive);
// purge the dead items
for_each(it, it2, [this](auto&& inst) { DestroyToast(inst); });
if (it2 == instances_.end()) {
instances_.erase(it, it2);
break;
}
it = move(it2);
}
target_pos += toast->GetHeight() + margin;
}
}
}
// Set new toast positions
if (!instances_.empty()) {
ScreenMetrics metrics;
auto margin = metrics.Y(toast_margin_);
int target_pos = 0;
for (auto&& inst : instances_) {
if (inst.hwnd) {
auto toast = Toast::Get(inst.hwnd);
if (toast->IsHighlighted())
target_pos = toast->GetVerticalPosition();
else
toast->SetVerticalPosition(target_pos);
target_pos += toast->GetHeight() + margin;
}
}
}
// Create new toasts from the queue
CheckQueue();
// Create new toasts from the queue
CheckQueue();
}
DesktopNotificationController::Notification
DesktopNotificationController::AddNotification(
std::wstring caption, std::wstring body_text, HBITMAP image) {
NotificationLink data(this);
DesktopNotificationController::AddNotification(std::wstring caption,
std::wstring body_text,
HBITMAP image) {
NotificationLink data(this);
data->caption = move(caption);
data->body_text = move(body_text);
data->image = CopyBitmap(image);
data->caption = move(caption);
data->body_text = move(body_text);
data->image = CopyBitmap(image);
// Enqueue new notification
Notification ret { *queue_.insert(queue_.end(), move(data)) };
CheckQueue();
return ret;
// Enqueue new notification
Notification ret{*queue_.insert(queue_.end(), move(data))};
CheckQueue();
return ret;
}
void DesktopNotificationController::CloseNotification(
Notification& notification) {
// Remove it from the queue
auto it = find(queue_.begin(), queue_.end(), notification.data_);
if (it != queue_.end()) {
queue_.erase(it);
this->OnNotificationClosed(notification);
return;
}
// Remove it from the queue
auto it = find(queue_.begin(), queue_.end(), notification.data_);
if (it != queue_.end()) {
queue_.erase(it);
this->OnNotificationClosed(notification);
return;
}
// Dismiss active toast
auto hwnd = GetToast(notification.data_.get());
if (hwnd) {
auto toast = Toast::Get(hwnd);
toast->Dismiss();
}
// Dismiss active toast
auto hwnd = GetToast(notification.data_.get());
if (hwnd) {
auto toast = Toast::Get(hwnd);
toast->Dismiss();
}
}
void DesktopNotificationController::CheckQueue() {
while (instances_.size() < instances_.capacity() && !queue_.empty()) {
CreateToast(move(queue_.front()));
queue_.pop_front();
}
while (instances_.size() < instances_.capacity() && !queue_.empty()) {
CreateToast(move(queue_.front()));
queue_.pop_front();
}
}
void DesktopNotificationController::CreateToast(NotificationLink&& data) {
auto hinstance = RegisterWndClasses();
auto hwnd = Toast::Create(hinstance, data);
if (hwnd) {
int toast_pos = 0;
if (!instances_.empty()) {
auto& item = instances_.back();
_ASSERT(item.hwnd);
auto hinstance = RegisterWndClasses();
auto hwnd = Toast::Create(hinstance, data);
if (hwnd) {
int toast_pos = 0;
if (!instances_.empty()) {
auto& item = instances_.back();
_ASSERT(item.hwnd);
ScreenMetrics scr;
auto toast = Toast::Get(item.hwnd);
toast_pos = toast->GetVerticalPosition() +
toast->GetHeight() +
scr.Y(toast_margin_);
}
instances_.push_back({ hwnd, move(data) });
if (!hwnd_controller_) {
// NOTE: We cannot use a message-only window because we need to
// receive system notifications
hwnd_controller_ = CreateWindow(class_name_, nullptr, 0,
0, 0, 0, 0,
NULL, NULL, hinstance, this);
}
auto toast = Toast::Get(hwnd);
toast->PopUp(toast_pos);
ScreenMetrics scr;
auto toast = Toast::Get(item.hwnd);
toast_pos = toast->GetVerticalPosition() + toast->GetHeight() +
scr.Y(toast_margin_);
}
instances_.push_back({hwnd, move(data)});
if (!hwnd_controller_) {
// NOTE: We cannot use a message-only window because we need to
// receive system notifications
hwnd_controller_ = CreateWindow(class_name_, nullptr, 0, 0, 0, 0, 0, NULL,
NULL, hinstance, this);
}
auto toast = Toast::Get(hwnd);
toast->PopUp(toast_pos);
}
}
HWND DesktopNotificationController::GetToast(
const NotificationData* data) const {
auto it = find_if(instances_.cbegin(), instances_.cend(),
[data](auto&& inst) {
if (!inst.hwnd) return false;
auto toast = Toast::Get(inst.hwnd);
return data == toast->GetNotification().get();
});
auto it =
find_if(instances_.cbegin(), instances_.cend(), [data](auto&& inst) {
if (!inst.hwnd)
return false;
auto toast = Toast::Get(inst.hwnd);
return data == toast->GetNotification().get();
});
return (it != instances_.cend()) ? it->hwnd : NULL;
return (it != instances_.cend()) ? it->hwnd : NULL;
}
void DesktopNotificationController::DestroyToast(ToastInstance& inst) {
if (inst.hwnd) {
auto data = Toast::Get(inst.hwnd)->GetNotification();
if (inst.hwnd) {
auto data = Toast::Get(inst.hwnd)->GetNotification();
DestroyWindow(inst.hwnd);
inst.hwnd = NULL;
DestroyWindow(inst.hwnd);
inst.hwnd = NULL;
Notification notification(data);
OnNotificationClosed(notification);
}
Notification notification(data);
OnNotificationClosed(notification);
}
}
DesktopNotificationController::Notification::Notification(
const shared_ptr<NotificationData>& data) :
data_(data) {
_ASSERT(data != nullptr);
const shared_ptr<NotificationData>& data)
: data_(data) {
_ASSERT(data != nullptr);
}
bool DesktopNotificationController::Notification::operator==(
const Notification& other) const {
return data_ == other.data_;
return data_ == other.data_;
}
void DesktopNotificationController::Notification::Close() {
// No business calling this when not pointing to a valid instance
_ASSERT(data_);
// No business calling this when not pointing to a valid instance
_ASSERT(data_);
if (data_->controller)
data_->controller->CloseNotification(*this);
if (data_->controller)
data_->controller->CloseNotification(*this);
}
void DesktopNotificationController::Notification::Set(
std::wstring caption, std::wstring body_text, HBITMAP image) {
// No business calling this when not pointing to a valid instance
_ASSERT(data_);
void DesktopNotificationController::Notification::Set(std::wstring caption,
std::wstring body_text,
HBITMAP image) {
// No business calling this when not pointing to a valid instance
_ASSERT(data_);
// Do nothing when the notification has been closed
if (!data_->controller)
return;
// Do nothing when the notification has been closed
if (!data_->controller)
return;
if (data_->image) DeleteBitmap(data_->image);
if (data_->image)
DeleteBitmap(data_->image);
data_->caption = move(caption);
data_->body_text = move(body_text);
data_->image = CopyBitmap(image);
data_->caption = move(caption);
data_->body_text = move(body_text);
data_->image = CopyBitmap(image);
auto hwnd = data_->controller->GetToast(data_.get());
if (hwnd) {
auto toast = Toast::Get(hwnd);
toast->ResetContents();
}
auto hwnd = data_->controller->GetToast(data_.get());
if (hwnd) {
auto toast = Toast::Get(hwnd);
toast->ResetContents();
}
// Change of contents can affect size and position of all toasts
data_->controller->StartAnimation();
// Change of contents can affect size and position of all toasts
data_->controller->StartAnimation();
}
DesktopNotificationController::NotificationLink::NotificationLink(
DesktopNotificationController* controller) :
shared_ptr(make_shared<NotificationData>()) {
get()->controller = controller;
DesktopNotificationController* controller)
: shared_ptr(make_shared<NotificationData>()) {
get()->controller = controller;
}
DesktopNotificationController::NotificationLink::~NotificationLink() {
auto p = get();
if (p) p->controller = nullptr;
auto p = get();
if (p)
p->controller = nullptr;
}
} // namespace brightray
} // namespace brightray

File diff suppressed because it is too large Load diff