feat: allow GUID parameter to avoid systray demotion on Windows (#21891)
* fix: systray icon demotion Adding support for GUID parameter in Tray API. In combination with signed binaries this allows to maintain the position in the systray on Windows. * unit tests * make mac and linux compile
This commit is contained in:
parent
2955c67c4e
commit
89eb309d0b
13 changed files with 143 additions and 15 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "shell/browser/browser.h"
|
||||
#include "shell/common/api/atom_api_native_image.h"
|
||||
#include "shell/common/gin_converters/gfx_converter.h"
|
||||
#include "shell/common/gin_converters/guid_converter.h"
|
||||
#include "shell/common/gin_converters/image_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
|
@ -54,8 +55,10 @@ namespace electron {
|
|||
|
||||
namespace api {
|
||||
|
||||
Tray::Tray(gin::Handle<NativeImage> image, gin_helper::Arguments* args)
|
||||
: tray_icon_(TrayIcon::Create()) {
|
||||
Tray::Tray(gin::Handle<NativeImage> image,
|
||||
base::Optional<UUID> guid,
|
||||
gin_helper::Arguments* args)
|
||||
: tray_icon_(TrayIcon::Create(guid)) {
|
||||
SetImage(args->isolate(), image);
|
||||
tray_icon_->AddObserver(this);
|
||||
|
||||
|
@ -67,12 +70,21 @@ Tray::~Tray() = default;
|
|||
// static
|
||||
gin_helper::WrappableBase* Tray::New(gin_helper::ErrorThrower thrower,
|
||||
gin::Handle<NativeImage> image,
|
||||
base::Optional<UUID> guid,
|
||||
gin_helper::Arguments* args) {
|
||||
if (!Browser::Get()->is_ready()) {
|
||||
thrower.ThrowError("Cannot create Tray before app is ready");
|
||||
return nullptr;
|
||||
}
|
||||
return new Tray(image, args);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
if (!guid.has_value() && args->Length() > 1) {
|
||||
thrower.ThrowError("Invalid GUID format");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return new Tray(image, guid, args);
|
||||
}
|
||||
|
||||
void Tray::OnClicked(const gfx::Rect& bounds,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gin/handle.h"
|
||||
#include "shell/browser/ui/tray_icon.h"
|
||||
#include "shell/browser/ui/tray_icon_observer.h"
|
||||
#include "shell/common/gin_converters/guid_converter.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/trackable_object.h"
|
||||
|
||||
|
@ -36,13 +37,16 @@ class Tray : public gin_helper::TrackableObject<Tray>, public TrayIconObserver {
|
|||
public:
|
||||
static gin_helper::WrappableBase* New(gin_helper::ErrorThrower thrower,
|
||||
gin::Handle<NativeImage> image,
|
||||
base::Optional<UUID> guid,
|
||||
gin_helper::Arguments* args);
|
||||
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::FunctionTemplate> prototype);
|
||||
|
||||
protected:
|
||||
Tray(gin::Handle<NativeImage> image, gin_helper::Arguments* args);
|
||||
Tray(gin::Handle<NativeImage> image,
|
||||
base::Optional<UUID> guid,
|
||||
gin_helper::Arguments* args);
|
||||
~Tray() override;
|
||||
|
||||
// TrayIconObserver:
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
#include "base/observer_list.h"
|
||||
#include "shell/browser/ui/atom_menu_model.h"
|
||||
#include "shell/browser/ui/tray_icon_observer.h"
|
||||
#include "shell/common/gin_converters/guid_converter.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class TrayIcon {
|
||||
public:
|
||||
static TrayIcon* Create();
|
||||
static TrayIcon* Create(base::Optional<UUID> guid);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
using ImageType = HICON;
|
||||
|
|
|
@ -341,7 +341,7 @@ gfx::Rect TrayIconCocoa::GetBounds() {
|
|||
}
|
||||
|
||||
// static
|
||||
TrayIcon* TrayIcon::Create() {
|
||||
TrayIcon* TrayIcon::Create(base::Optional<UUID> guid) {
|
||||
return new TrayIconCocoa;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ bool TrayIconGtk::HasClickAction() {
|
|||
}
|
||||
|
||||
// static
|
||||
TrayIcon* TrayIcon::Create() {
|
||||
TrayIcon* TrayIcon::Create(base::Optional<UUID> guid) {
|
||||
return new TrayIconGtk;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
namespace electron {
|
||||
|
||||
// static
|
||||
TrayIcon* TrayIcon::Create() {
|
||||
TrayIcon* TrayIcon::Create(base::Optional<UUID> guid) {
|
||||
static NotifyIconHost host;
|
||||
return host.CreateNotifyIcon();
|
||||
return host.CreateNotifyIcon(guid);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "shell/browser/ui/win/notify_icon.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <utility>
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
@ -43,12 +44,18 @@ UINT ConvertIconType(electron::TrayIcon::IconType type) {
|
|||
|
||||
namespace electron {
|
||||
|
||||
NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
|
||||
NotifyIcon::NotifyIcon(NotifyIconHost* host,
|
||||
UINT id,
|
||||
HWND window,
|
||||
UINT message,
|
||||
GUID guid)
|
||||
: host_(host),
|
||||
icon_id_(id),
|
||||
window_(window),
|
||||
message_id_(message),
|
||||
weak_factory_(this) {
|
||||
guid_ = guid;
|
||||
is_using_guid_ = guid != GUID_DEFAULT;
|
||||
NOTIFYICONDATA icon_data;
|
||||
InitIconData(&icon_data);
|
||||
icon_data.uFlags |= NIF_MESSAGE;
|
||||
|
@ -246,6 +253,9 @@ gfx::Rect NotifyIcon::GetBounds() {
|
|||
icon_id.uID = icon_id_;
|
||||
icon_id.hWnd = window_;
|
||||
icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
|
||||
if (is_using_guid_) {
|
||||
icon_id.guidItem = guid_;
|
||||
}
|
||||
|
||||
RECT rect = {0};
|
||||
Shell_NotifyIconGetRect(&icon_id, &rect);
|
||||
|
@ -257,6 +267,10 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
|
|||
icon_data->cbSize = sizeof(NOTIFYICONDATA);
|
||||
icon_data->hWnd = window_;
|
||||
icon_data->uID = icon_id_;
|
||||
if (is_using_guid_) {
|
||||
icon_data->uFlags = NIF_GUID;
|
||||
icon_data->guidItem = guid_;
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyIcon::OnContextMenuClosed() {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "shell/browser/ui/tray_icon.h"
|
||||
#include "shell/browser/ui/win/notify_icon_host.h"
|
||||
|
||||
namespace gfx {
|
||||
class Point;
|
||||
|
@ -34,7 +35,11 @@ class NotifyIconHost;
|
|||
class NotifyIcon : public TrayIcon {
|
||||
public:
|
||||
// Constructor which provides this icon's unique ID and messaging window.
|
||||
NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message);
|
||||
NotifyIcon(NotifyIconHost* host,
|
||||
UINT id,
|
||||
HWND window,
|
||||
UINT message,
|
||||
GUID guid);
|
||||
~NotifyIcon() override;
|
||||
|
||||
// Handles a click event from the user - if |left_button_click| is true and
|
||||
|
@ -53,6 +58,7 @@ class NotifyIcon : public TrayIcon {
|
|||
UINT icon_id() const { return icon_id_; }
|
||||
HWND window() const { return window_; }
|
||||
UINT message_id() const { return message_id_; }
|
||||
GUID guid() const { return guid_; }
|
||||
|
||||
// Overridden from TrayIcon:
|
||||
void SetImage(HICON image) override;
|
||||
|
@ -89,6 +95,12 @@ class NotifyIcon : public TrayIcon {
|
|||
// The context menu.
|
||||
AtomMenuModel* menu_model_ = nullptr;
|
||||
|
||||
// An optional GUID used for identifying tray entries on Windows
|
||||
GUID guid_ = GUID_DEFAULT;
|
||||
|
||||
// indicates whether the tray entry is associated with a guid
|
||||
bool is_using_guid_ = false;
|
||||
|
||||
// Context menu associated with this icon (if any).
|
||||
std::unique_ptr<views::MenuRunner> menu_runner_;
|
||||
|
||||
|
|
|
@ -83,9 +83,22 @@ NotifyIconHost::~NotifyIconHost() {
|
|||
delete ptr;
|
||||
}
|
||||
|
||||
NotifyIcon* NotifyIconHost::CreateNotifyIcon() {
|
||||
NotifyIcon* NotifyIconHost::CreateNotifyIcon(base::Optional<UUID> guid) {
|
||||
if (guid.has_value()) {
|
||||
for (NotifyIcons::const_iterator i(notify_icons_.begin());
|
||||
i != notify_icons_.end(); ++i) {
|
||||
NotifyIcon* current_win_icon = static_cast<NotifyIcon*>(*i);
|
||||
if (current_win_icon->guid() == guid.value()) {
|
||||
LOG(WARNING)
|
||||
<< "Guid already in use. Existing tray entry will be replaced.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotifyIcon* notify_icon =
|
||||
new NotifyIcon(this, NextIconId(), window_, kNotifyIconMessage);
|
||||
new NotifyIcon(this, NextIconId(), window_, kNotifyIconMessage,
|
||||
guid.has_value() ? guid.value() : GUID_DEFAULT);
|
||||
|
||||
notify_icons_.push_back(notify_icon);
|
||||
return notify_icon;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/optional.h"
|
||||
#include "shell/common/gin_converters/guid_converter.h"
|
||||
|
||||
const GUID GUID_DEFAULT = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
|
||||
namespace electron {
|
||||
|
||||
|
@ -20,7 +24,7 @@ class NotifyIconHost {
|
|||
NotifyIconHost();
|
||||
~NotifyIconHost();
|
||||
|
||||
NotifyIcon* CreateNotifyIcon();
|
||||
NotifyIcon* CreateNotifyIcon(base::Optional<UUID> guid);
|
||||
void Remove(NotifyIcon* notify_icon);
|
||||
|
||||
private:
|
||||
|
|
55
shell/common/gin_converters/guid_converter.h
Normal file
55
shell/common/gin_converters/guid_converter.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2020 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_COMMON_GIN_CONVERTERS_GUID_CONVERTER_H_
|
||||
#define SHELL_COMMON_GIN_CONVERTERS_GUID_CONVERTER_H_
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <rpc.h>
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
#include "gin/converter.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef GUID UUID;
|
||||
#else
|
||||
typedef struct {
|
||||
} UUID;
|
||||
#endif
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <>
|
||||
struct Converter<UUID> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
UUID* out) {
|
||||
#if defined(OS_WIN)
|
||||
std::string guid;
|
||||
if (!gin::ConvertFromV8(isolate, val, &guid))
|
||||
return false;
|
||||
|
||||
UUID uid;
|
||||
|
||||
if (guid.length() > 0) {
|
||||
unsigned char* uid_cstr = (unsigned char*)guid.c_str();
|
||||
RPC_STATUS result = UuidFromStringA(uid_cstr, &uid);
|
||||
if (result == RPC_S_INVALID_STRING_UUID) {
|
||||
return false;
|
||||
} else {
|
||||
*out = uid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
#endif // SHELL_COMMON_GIN_CONVERTERS_GUID_CONVERTER_H_
|
Loading…
Add table
Add a link
Reference in a new issue