electron/shell/browser/ui/win/notify_icon.cc

260 lines
8 KiB
C++
Raw Normal View History

// Copyright (c) 2014 GitHub, Inc.
2014-06-03 03:25:09 +00:00
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/ui/win/notify_icon.h"
2014-06-03 03:25:09 +00:00
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h"
#include "shell/browser/ui/win/notify_icon_host.h"
2014-06-03 03:25:09 +00:00
#include "third_party/skia/include/core/SkBitmap.h"
2016-07-04 06:08:55 +00:00
#include "ui/display/screen.h"
2016-07-05 00:31:29 +00:00
#include "ui/display/win/screen_win.h"
2016-08-26 22:30:02 +00:00
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
2014-07-11 11:42:53 +00:00
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/widget/widget.h"
2014-06-03 03:25:09 +00:00
namespace {
UINT ConvertIconType(electron::TrayIcon::IconType type) {
using IconType = electron::TrayIcon::IconType;
switch (type) {
case IconType::None:
return NIIF_NONE;
case IconType::Info:
return NIIF_INFO;
case IconType::Warning:
return NIIF_WARNING;
case IconType::Error:
return NIIF_ERROR;
case IconType::Custom:
return NIIF_USER;
default:
NOTREACHED() << "Invalid icon type";
}
}
} // namespace
namespace electron {
2014-06-03 03:25:09 +00:00
2018-04-18 01:55:30 +00:00
NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
: host_(host),
icon_id_(id),
window_(window),
message_id_(message),
weak_factory_(this) {
2014-06-03 03:25:09 +00:00
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
2015-07-24 15:02:14 +00:00
icon_data.uFlags |= NIF_MESSAGE;
2014-06-03 03:25:09 +00:00
icon_data.uCallbackMessage = message_id_;
BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
// This can happen if the explorer process isn't running when we try to
// create the icon for some reason (for example, at startup).
if (!result)
LOG(WARNING) << "Unable to create status tray icon.";
}
NotifyIcon::~NotifyIcon() {
// Remove our icon.
host_->Remove(this);
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
Shell_NotifyIcon(NIM_DELETE, &icon_data);
}
2015-12-02 11:58:10 +00:00
void NotifyIcon::HandleClickEvent(int modifiers,
2015-07-29 04:36:01 +00:00
bool left_mouse_click,
bool double_button_click) {
2016-06-21 06:49:22 +00:00
gfx::Rect bounds = GetBounds();
2014-06-03 03:25:09 +00:00
if (left_mouse_click) {
2015-07-29 04:36:01 +00:00
if (double_button_click) // double left click
2016-06-21 06:49:22 +00:00
NotifyDoubleClicked(bounds, modifiers);
2015-07-29 04:36:01 +00:00
else // single left click
2017-10-05 02:51:41 +00:00
NotifyClicked(bounds,
display::Screen::GetScreen()->GetCursorScreenPoint(),
modifiers);
2014-06-03 03:25:09 +00:00
return;
2015-07-29 04:36:01 +00:00
} else if (!double_button_click) { // single right click
if (menu_model_)
2015-12-02 11:58:10 +00:00
PopUpContextMenu(gfx::Point(), menu_model_);
else
2016-06-21 06:49:22 +00:00
NotifyRightClicked(bounds, modifiers);
2014-06-03 03:25:09 +00:00
}
}
void NotifyIcon::HandleMouseMoveEvent(int modifiers) {
gfx::Point cursorPos = display::Screen::GetScreen()->GetCursorScreenPoint();
// Omit event fired when tray icon is created but cursor is outside of it.
if (GetBounds().Contains(cursorPos))
NotifyMouseMoved(cursorPos, modifiers);
}
2014-06-03 03:25:09 +00:00
void NotifyIcon::ResetIcon() {
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
// Delete any previously existing icon.
Shell_NotifyIcon(NIM_DELETE, &icon_data);
InitIconData(&icon_data);
2015-07-24 15:02:14 +00:00
icon_data.uFlags |= NIF_MESSAGE;
2014-06-03 03:25:09 +00:00
icon_data.uCallbackMessage = message_id_;
2016-05-20 13:28:07 +00:00
icon_data.hIcon = icon_.get();
2014-06-03 03:25:09 +00:00
// If we have an image, then set the NIF_ICON flag, which tells
// Shell_NotifyIcon() to set the image for the status icon it creates.
if (icon_data.hIcon)
icon_data.uFlags |= NIF_ICON;
// Re-add our icon.
BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
if (!result)
LOG(WARNING) << "Unable to re-create status tray icon.";
}
2016-05-20 07:55:22 +00:00
void NotifyIcon::SetImage(HICON image) {
2016-05-20 13:28:07 +00:00
icon_ = base::win::ScopedHICON(CopyIcon(image));
2014-06-03 03:25:09 +00:00
// Create the icon.
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
2015-07-24 15:02:14 +00:00
icon_data.uFlags |= NIF_ICON;
2016-05-20 07:55:22 +00:00
icon_data.hIcon = image;
2014-06-03 03:25:09 +00:00
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
if (!result)
LOG(WARNING) << "Error setting status tray icon image";
}
2016-05-20 07:55:22 +00:00
void NotifyIcon::SetPressedImage(HICON image) {
2014-06-03 03:25:09 +00:00
// Ignore pressed images, since the standard on Windows is to not highlight
// pressed status icons.
}
void NotifyIcon::SetToolTip(const std::string& tool_tip) {
// Create the icon.
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
2015-07-24 15:02:14 +00:00
icon_data.uFlags |= NIF_TIP;
wcsncpy_s(icon_data.szTip, base::UTF8ToUTF16(tool_tip).c_str(), _TRUNCATE);
2014-06-03 03:25:09 +00:00
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
if (!result)
LOG(WARNING) << "Unable to set tooltip for status tray icon";
}
void NotifyIcon::DisplayBalloon(const BalloonOptions& options) {
2014-11-28 10:30:43 +00:00
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
2015-07-24 15:02:14 +00:00
icon_data.uFlags |= NIF_INFO;
wcsncpy_s(icon_data.szInfoTitle, options.title.c_str(), _TRUNCATE);
wcsncpy_s(icon_data.szInfo, options.content.c_str(), _TRUNCATE);
2014-11-28 10:30:43 +00:00
icon_data.uTimeout = 0;
icon_data.hBalloonIcon = options.icon;
icon_data.dwInfoFlags = ConvertIconType(options.icon_type);
if (options.large_icon)
icon_data.dwInfoFlags |= NIIF_LARGE_ICON;
if (options.no_sound)
icon_data.dwInfoFlags |= NIIF_NOSOUND;
if (options.respect_quiet_time)
icon_data.dwInfoFlags |= NIIF_RESPECT_QUIET_TIME;
2014-11-28 10:30:43 +00:00
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
if (!result)
LOG(WARNING) << "Unable to create status tray balloon.";
}
void NotifyIcon::RemoveBalloon() {
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
icon_data.uFlags |= NIF_INFO;
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
if (!result)
LOG(WARNING) << "Unable to remove status tray balloon.";
}
2019-08-09 14:43:48 +00:00
void NotifyIcon::Focus() {
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
BOOL result = Shell_NotifyIcon(NIM_SETFOCUS, &icon_data);
if (!result)
LOG(WARNING) << "Unable to focus tray icon.";
}
void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
AtomMenuModel* menu_model) {
// Returns if context menu isn't set.
2016-06-28 22:28:27 +00:00
if (menu_model == nullptr && menu_model_ == nullptr)
return;
// Set our window as the foreground window, so the context menu closes when
// we click away from it.
if (!SetForegroundWindow(window_))
return;
// Cancel current menu if there is one.
if (menu_runner_ && menu_runner_->IsRunning())
menu_runner_->Cancel();
2015-12-02 11:58:10 +00:00
// Show menu at mouse's position by default.
gfx::Rect rect(pos, gfx::Size());
if (pos.IsOrigin())
2016-07-04 06:08:55 +00:00
rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
2015-12-02 11:58:10 +00:00
// Create a widget for the menu, otherwise we get no keyboard events, which
// is required for accessibility.
widget_.reset(new views::Widget());
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
params.ownership =
views::Widget::InitParams::Ownership::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(0, 0, 0, 0);
params.force_software_compositing = true;
params.z_order = ui::ZOrderLevel::kFloatingUIElement;
widget_->Init(params);
widget_->Show();
widget_->Activate();
menu_runner_.reset(new views::MenuRunner(
2016-06-28 22:28:27 +00:00
menu_model != nullptr ? menu_model : menu_model_,
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS,
base::BindRepeating(&NotifyIcon::OnContextMenuClosed,
weak_factory_.GetWeakPtr())));
chore: bump chromium to 1e9f9a24aa12 (master) (#17880) * chore: bump chromium in DEPS to 1e9f9a24aa12bea9cf194a82a7e249bd1242ec4f * chore: update patches * Make WebContents' theme color a base::Optional<SkColor> https://chromium-review.googlesource.com/c/chromium/src/+/1540022 * update autofill patch for incorrect header includes * Move Shell messages to web_test and rename to BlinkTest. https://chromium-review.googlesource.com/c/chromium/src/+/1525181 * Make PlatformNotificationServiceImpl a KeyedService. https://chromium-review.googlesource.com/c/chromium/src/+/1336150 * Move MediaPlayerId to its own file. https://chromium-review.googlesource.com/c/chromium/src/+/1547057 * Remove net/base/completion_callback.h, which is no longer used https://chromium-review.googlesource.com/c/chromium/src/+/1552821 * AW NS: support file scheme cookies https://chromium-review.googlesource.com/c/chromium/src/+/1533486 * Remove SecurityInfo and adapt remaining consumers https://chromium-review.googlesource.com/c/chromium/src/+/1509455 * Remove deprecated type-specific number to string conversion functions https://chromium-review.googlesource.com/c/chromium/src/+/1545881 * DevTools: Adding new performance histograms for launch of top 4 tools https://chromium-review.googlesource.com/c/chromium/src/+/1506388 * Update include paths for //base/hash/hash.h https://chromium-review.googlesource.com/c/chromium/src/+/1544630 * build: Disable ensure_gn_version gclient hook for mac CI checkout * update patches * use maybe version of v8::String::NewFromTwoByte * bump appveyor image version * fix mac ci hopefully * Convert enum to enum class for MenuAnchorPosition https://chromium-review.googlesource.com/c/chromium/src/+/1530508 * use maybe version of ToObject * RenderViewHost::GetProcess is no longer const * Unrefcount AuthChallengeInfo https://chromium-review.googlesource.com/c/chromium/src/+/1550631 * MenuButtonController takes Button rather than MenuButton https://chromium-review.googlesource.com/c/chromium/src/+/1500935 * add //ui/views_bridge_mac to deps to fix link error * forward declare views::Button in atom::MenuDelegate * more v8 patches * base/{=> hash}/md5.h https://chromium-review.googlesource.com/c/chromium/src/+/1535124 * gfx::{PlatformFontWin => win}::* https://chromium-review.googlesource.com/c/chromium/src/+/1534178 * fix v8 patches * [base] Rename TaskScheduler to ThreadPool https://chromium-review.googlesource.com/c/chromium/src/+/1561552 * use internal_config_base for bytecode_builtins_list_generator avoids windows link errors * FIXME: temporarily disable v8/breakpad integration * FIXME: temporarily disable prevent-will-redirect test * FIXME: disable neon on aarch64 pending crbug.com/953815 * update to account for WebCursor refactor https://chromium-review.googlesource.com/c/chromium/src/+/1562755 * enable stack dumping on appveyor * Revert "FIXME: disable neon on aarch64 pending crbug.com/953815" This reverts commit 57f082026be3d83069f2a2814684abf4dc9e7b53. * fix: remove const qualifiers to match upstream * fix: remove const qualifiers to match upstream in cc files as well * don't throw an error when testing if an object is an object * use non-deprecated Buffer constructor * Remove net::CookieSameSite::DEFAULT_MODE enum value https://chromium-review.googlesource.com/c/chromium/src/+/1567955 * depend on modded dbus-native to work around buffer deprecation https://github.com/sidorares/dbus-native/pull/262 * revert clang roll to fix arm build on linux * fixup! depend on modded dbus-native to work around buffer deprecation need more coffee * update coffee-script * robustify verify-mksnapshot w.r.t. command-line parameters * Revert "robustify verify-mksnapshot w.r.t. command-line parameters" This reverts commit a49af01411f684f6025528d604895c3696e0bc57. * fix mksnapshot by matching args * update patches * TMP: enable rdp on appveyor * Changed ContentBrowserClient::CreateQuotaPermissionContext() to return scoped_refptr. https://chromium-review.googlesource.com/c/chromium/src/+/1569376 * Make content::ResourceType an enum class. https://chromium-review.googlesource.com/c/chromium/src/+/1569345 * fixup! Make content::ResourceType an enum class. * turn off rdp * use net::CompletionRepeatingCallback instead of base::Callback<void(int)> * remove disable_ensure_gn_version_gclient_hook.patch * copy repeating callback instead of std::move * fix lint * add completion_repeating_callback.h include
2019-04-20 17:20:37 +00:00
menu_runner_->RunMenuAt(widget_.get(), NULL, rect,
views::MenuAnchorPosition::kTopLeft,
2018-04-18 01:55:30 +00:00
ui::MENU_SOURCE_MOUSE);
}
void NotifyIcon::SetContextMenu(AtomMenuModel* menu_model) {
menu_model_ = menu_model;
2014-06-03 03:25:09 +00:00
}
2016-06-21 06:49:22 +00:00
gfx::Rect NotifyIcon::GetBounds() {
NOTIFYICONIDENTIFIER icon_id;
memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER));
icon_id.uID = icon_id_;
icon_id.hWnd = window_;
icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
2018-04-18 01:55:30 +00:00
RECT rect = {0};
2016-06-21 06:49:22 +00:00
Shell_NotifyIconGetRect(&icon_id, &rect);
2016-07-05 00:31:29 +00:00
return display::win::ScreenWin::ScreenToDIPRect(window_, gfx::Rect(rect));
2016-06-21 06:49:22 +00:00
}
2014-06-03 03:25:09 +00:00
void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
2014-11-28 10:59:51 +00:00
memset(icon_data, 0, sizeof(NOTIFYICONDATA));
icon_data->cbSize = sizeof(NOTIFYICONDATA);
2014-06-03 03:25:09 +00:00
icon_data->hWnd = window_;
icon_data->uID = icon_id_;
}
void NotifyIcon::OnContextMenuClosed() {
widget_->Close();
}
} // namespace electron