refactor: rename the atom directory to shell
This commit is contained in:
parent
4575a4aae3
commit
d7f07e8a80
631 changed files with 0 additions and 0 deletions
59
shell/browser/ui/win/atom_desktop_native_widget_aura.cc
Normal file
59
shell/browser/ui/win/atom_desktop_native_widget_aura.cc
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/atom_desktop_native_widget_aura.h"
|
||||
|
||||
#include "atom/browser/ui/win/atom_desktop_window_tree_host_win.h"
|
||||
#include "ui/views/corewm/tooltip_controller.h"
|
||||
#include "ui/wm/public/tooltip_client.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomDesktopNativeWidgetAura::AtomDesktopNativeWidgetAura(
|
||||
NativeWindowViews* native_window_view)
|
||||
: views::DesktopNativeWidgetAura(native_window_view->widget()),
|
||||
native_window_view_(native_window_view) {
|
||||
GetNativeWindow()->SetName("AtomDesktopNativeWidgetAura");
|
||||
// This is to enable the override of OnWindowActivated
|
||||
wm::SetActivationChangeObserver(GetNativeWindow(), this);
|
||||
}
|
||||
|
||||
void AtomDesktopNativeWidgetAura::InitNativeWidget(
|
||||
const views::Widget::InitParams& params) {
|
||||
views::Widget::InitParams modified_params = params;
|
||||
desktop_window_tree_host_ = new AtomDesktopWindowTreeHostWin(
|
||||
native_window_view_,
|
||||
static_cast<views::DesktopNativeWidgetAura*>(params.native_widget));
|
||||
modified_params.desktop_window_tree_host = desktop_window_tree_host_;
|
||||
views::DesktopNativeWidgetAura::InitNativeWidget(modified_params);
|
||||
}
|
||||
|
||||
void AtomDesktopNativeWidgetAura::Activate() {
|
||||
// Activate can cause the focused window to be blurred so only
|
||||
// call when the window being activated is visible. This prevents
|
||||
// hidden windows from blurring the focused window when created.
|
||||
if (IsVisible())
|
||||
views::DesktopNativeWidgetAura::Activate();
|
||||
}
|
||||
|
||||
void AtomDesktopNativeWidgetAura::OnWindowActivated(
|
||||
wm::ActivationChangeObserver::ActivationReason reason,
|
||||
aura::Window* gained_active,
|
||||
aura::Window* lost_active) {
|
||||
views::DesktopNativeWidgetAura::OnWindowActivated(reason, gained_active,
|
||||
lost_active);
|
||||
if (lost_active != nullptr) {
|
||||
auto* tooltip_controller = static_cast<views::corewm::TooltipController*>(
|
||||
wm::GetTooltipClient(lost_active->GetRootWindow()));
|
||||
|
||||
// This will cause the tooltip to be hidden when a window is deactivated,
|
||||
// as it should be.
|
||||
// TODO(brenca): Remove this fix when the chromium issue is fixed.
|
||||
// crbug.com/724538
|
||||
if (tooltip_controller != nullptr)
|
||||
tooltip_controller->OnCancelMode(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace atom
|
42
shell/browser/ui/win/atom_desktop_native_widget_aura.h
Normal file
42
shell/browser/ui/win/atom_desktop_native_widget_aura.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_
|
||||
#define ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_
|
||||
|
||||
#include "atom/browser/native_window_views.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
|
||||
|
||||
namespace views {
|
||||
class DesktopWindowTreeHost;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura {
|
||||
public:
|
||||
explicit AtomDesktopNativeWidgetAura(NativeWindowViews* native_window_view);
|
||||
|
||||
// views::DesktopNativeWidgetAura:
|
||||
void InitNativeWidget(const views::Widget::InitParams& params) override;
|
||||
|
||||
// internal::NativeWidgetPrivate:
|
||||
void Activate() override;
|
||||
|
||||
private:
|
||||
void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
|
||||
aura::Window* gained_active,
|
||||
aura::Window* lost_active) override;
|
||||
|
||||
NativeWindowViews* native_window_view_;
|
||||
|
||||
// Owned by DesktopNativeWidgetAura.
|
||||
views::DesktopWindowTreeHost* desktop_window_tree_host_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomDesktopNativeWidgetAura);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_
|
32
shell/browser/ui/win/atom_desktop_window_tree_host_win.cc
Normal file
32
shell/browser/ui/win/atom_desktop_window_tree_host_win.cc
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/atom_desktop_window_tree_host_win.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomDesktopWindowTreeHostWin::AtomDesktopWindowTreeHostWin(
|
||||
NativeWindowViews* native_window_view,
|
||||
views::DesktopNativeWidgetAura* desktop_native_widget_aura)
|
||||
: views::DesktopWindowTreeHostWin(native_window_view->widget(),
|
||||
desktop_native_widget_aura),
|
||||
native_window_view_(native_window_view) {}
|
||||
|
||||
AtomDesktopWindowTreeHostWin::~AtomDesktopWindowTreeHostWin() {}
|
||||
|
||||
bool AtomDesktopWindowTreeHostWin::PreHandleMSG(UINT message,
|
||||
WPARAM w_param,
|
||||
LPARAM l_param,
|
||||
LRESULT* result) {
|
||||
return native_window_view_->PreHandleMSG(message, w_param, l_param, result);
|
||||
}
|
||||
|
||||
bool AtomDesktopWindowTreeHostWin::HasNativeFrame() const {
|
||||
// Since we never use chromium's titlebar implementation, we can just say
|
||||
// that we use a native titlebar. This will disable the repaint locking when
|
||||
// DWM composition is disabled.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace atom
|
37
shell/browser/ui/win/atom_desktop_window_tree_host_win.h
Normal file
37
shell/browser/ui/win/atom_desktop_window_tree_host_win.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_
|
||||
#define ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "atom/browser/native_window_views.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin {
|
||||
public:
|
||||
AtomDesktopWindowTreeHostWin(
|
||||
NativeWindowViews* native_window_view,
|
||||
views::DesktopNativeWidgetAura* desktop_native_widget_aura);
|
||||
~AtomDesktopWindowTreeHostWin() override;
|
||||
|
||||
protected:
|
||||
bool PreHandleMSG(UINT message,
|
||||
WPARAM w_param,
|
||||
LPARAM l_param,
|
||||
LRESULT* result) override;
|
||||
bool HasNativeFrame() const override;
|
||||
|
||||
private:
|
||||
NativeWindowViews* native_window_view_; // weak ref
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomDesktopWindowTreeHostWin);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_
|
350
shell/browser/ui/win/jump_list.cc
Normal file
350
shell/browser/ui/win/jump_list.cc
Normal file
|
@ -0,0 +1,350 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/jump_list.h"
|
||||
|
||||
#include <propkey.h> // for PKEY_* constants
|
||||
|
||||
#include "base/win/scoped_co_mem.h"
|
||||
#include "base/win/scoped_propvariant.h"
|
||||
#include "base/win/win_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using atom::JumpListCategory;
|
||||
using atom::JumpListItem;
|
||||
using atom::JumpListResult;
|
||||
|
||||
bool AppendTask(const JumpListItem& item, IObjectCollection* collection) {
|
||||
DCHECK(collection);
|
||||
|
||||
CComPtr<IShellLink> link;
|
||||
if (FAILED(link.CoCreateInstance(CLSID_ShellLink)) ||
|
||||
FAILED(link->SetPath(item.path.value().c_str())) ||
|
||||
FAILED(link->SetArguments(item.arguments.c_str())) ||
|
||||
FAILED(link->SetWorkingDirectory(item.working_dir.value().c_str())) ||
|
||||
FAILED(link->SetDescription(item.description.c_str())))
|
||||
return false;
|
||||
|
||||
if (!item.icon_path.empty() &&
|
||||
FAILED(link->SetIconLocation(item.icon_path.value().c_str(),
|
||||
item.icon_index)))
|
||||
return false;
|
||||
|
||||
CComQIPtr<IPropertyStore> property_store(link);
|
||||
if (!base::win::SetStringValueForPropertyStore(property_store, PKEY_Title,
|
||||
item.title.c_str()))
|
||||
return false;
|
||||
|
||||
return SUCCEEDED(collection->AddObject(link));
|
||||
}
|
||||
|
||||
bool AppendSeparator(IObjectCollection* collection) {
|
||||
DCHECK(collection);
|
||||
|
||||
CComPtr<IShellLink> shell_link;
|
||||
if (SUCCEEDED(shell_link.CoCreateInstance(CLSID_ShellLink))) {
|
||||
CComQIPtr<IPropertyStore> property_store(shell_link);
|
||||
if (base::win::SetBooleanValueForPropertyStore(
|
||||
property_store, PKEY_AppUserModel_IsDestListSeparator, true))
|
||||
return SUCCEEDED(collection->AddObject(shell_link));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppendFile(const JumpListItem& item, IObjectCollection* collection) {
|
||||
DCHECK(collection);
|
||||
|
||||
CComPtr<IShellItem> file;
|
||||
if (SUCCEEDED(SHCreateItemFromParsingName(item.path.value().c_str(), NULL,
|
||||
IID_PPV_ARGS(&file))))
|
||||
return SUCCEEDED(collection->AddObject(file));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetShellItemFileName(IShellItem* shell_item, base::FilePath* file_name) {
|
||||
DCHECK(shell_item);
|
||||
DCHECK(file_name);
|
||||
|
||||
base::win::ScopedCoMem<wchar_t> file_name_buffer;
|
||||
if (SUCCEEDED(
|
||||
shell_item->GetDisplayName(SIGDN_FILESYSPATH, &file_name_buffer))) {
|
||||
*file_name = base::FilePath(file_name_buffer.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConvertShellLinkToJumpListItem(IShellLink* shell_link,
|
||||
JumpListItem* item) {
|
||||
DCHECK(shell_link);
|
||||
DCHECK(item);
|
||||
|
||||
item->type = JumpListItem::Type::TASK;
|
||||
wchar_t path[MAX_PATH];
|
||||
if (FAILED(shell_link->GetPath(path, base::size(path), nullptr, 0)))
|
||||
return false;
|
||||
|
||||
item->path = base::FilePath(path);
|
||||
|
||||
CComQIPtr<IPropertyStore> property_store = shell_link;
|
||||
base::win::ScopedPropVariant prop;
|
||||
if (SUCCEEDED(
|
||||
property_store->GetValue(PKEY_Link_Arguments, prop.Receive())) &&
|
||||
(prop.get().vt == VT_LPWSTR)) {
|
||||
item->arguments = prop.get().pwszVal;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(property_store->GetValue(PKEY_Title, prop.Receive())) &&
|
||||
(prop.get().vt == VT_LPWSTR)) {
|
||||
item->title = prop.get().pwszVal;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(shell_link->GetWorkingDirectory(path, base::size(path))))
|
||||
item->working_dir = base::FilePath(path);
|
||||
|
||||
int icon_index;
|
||||
if (SUCCEEDED(
|
||||
shell_link->GetIconLocation(path, base::size(path), &icon_index))) {
|
||||
item->icon_path = base::FilePath(path);
|
||||
item->icon_index = icon_index;
|
||||
}
|
||||
|
||||
wchar_t item_desc[INFOTIPSIZE];
|
||||
if (SUCCEEDED(shell_link->GetDescription(item_desc, base::size(item_desc))))
|
||||
item->description = item_desc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert IObjectArray of IShellLink & IShellItem to std::vector.
|
||||
void ConvertRemovedJumpListItems(IObjectArray* in,
|
||||
std::vector<JumpListItem>* out) {
|
||||
DCHECK(in);
|
||||
DCHECK(out);
|
||||
|
||||
UINT removed_count;
|
||||
if (SUCCEEDED(in->GetCount(&removed_count) && (removed_count > 0))) {
|
||||
out->reserve(removed_count);
|
||||
JumpListItem item;
|
||||
IShellItem* shell_item;
|
||||
IShellLink* shell_link;
|
||||
for (UINT i = 0; i < removed_count; ++i) {
|
||||
if (SUCCEEDED(in->GetAt(i, IID_PPV_ARGS(&shell_item)))) {
|
||||
item.type = JumpListItem::Type::FILE;
|
||||
GetShellItemFileName(shell_item, &item.path);
|
||||
out->push_back(item);
|
||||
shell_item->Release();
|
||||
} else if (SUCCEEDED(in->GetAt(i, IID_PPV_ARGS(&shell_link)))) {
|
||||
if (ConvertShellLinkToJumpListItem(shell_link, &item))
|
||||
out->push_back(item);
|
||||
shell_link->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
JumpListItem::JumpListItem() = default;
|
||||
JumpListItem::JumpListItem(const JumpListItem&) = default;
|
||||
JumpListItem::~JumpListItem() = default;
|
||||
JumpListCategory::JumpListCategory() = default;
|
||||
JumpListCategory::JumpListCategory(const JumpListCategory&) = default;
|
||||
JumpListCategory::~JumpListCategory() = default;
|
||||
|
||||
JumpList::JumpList(const base::string16& app_id) : app_id_(app_id) {
|
||||
destinations_.CoCreateInstance(CLSID_DestinationList);
|
||||
}
|
||||
|
||||
JumpList::~JumpList() = default;
|
||||
|
||||
bool JumpList::Begin(int* min_items, std::vector<JumpListItem>* removed_items) {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return false;
|
||||
|
||||
if (FAILED(destinations_->SetAppID(app_id_.c_str())))
|
||||
return false;
|
||||
|
||||
UINT min_slots;
|
||||
CComPtr<IObjectArray> removed;
|
||||
if (FAILED(destinations_->BeginList(&min_slots, IID_PPV_ARGS(&removed))))
|
||||
return false;
|
||||
|
||||
if (min_items)
|
||||
*min_items = min_slots;
|
||||
|
||||
if (removed_items)
|
||||
ConvertRemovedJumpListItems(removed, removed_items);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JumpList::Abort() {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return false;
|
||||
|
||||
return SUCCEEDED(destinations_->AbortList());
|
||||
}
|
||||
|
||||
bool JumpList::Commit() {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return false;
|
||||
|
||||
return SUCCEEDED(destinations_->CommitList());
|
||||
}
|
||||
|
||||
bool JumpList::Delete() {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return false;
|
||||
|
||||
return SUCCEEDED(destinations_->DeleteList(app_id_.c_str()));
|
||||
}
|
||||
|
||||
// This method will attempt to append as many items to the Jump List as
|
||||
// possible, and will return a single error code even if multiple things
|
||||
// went wrong in the process. To get detailed information about what went
|
||||
// wrong enable runtime logging.
|
||||
JumpListResult JumpList::AppendCategory(const JumpListCategory& category) {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return JumpListResult::GENERIC_ERROR;
|
||||
|
||||
if (category.items.empty())
|
||||
return JumpListResult::SUCCESS;
|
||||
|
||||
CComPtr<IObjectCollection> collection;
|
||||
if (FAILED(collection.CoCreateInstance(CLSID_EnumerableObjectCollection))) {
|
||||
return JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
|
||||
auto result = JumpListResult::SUCCESS;
|
||||
// Keep track of how many items were actually appended to the category.
|
||||
int appended_count = 0;
|
||||
for (const auto& item : category.items) {
|
||||
switch (item.type) {
|
||||
case JumpListItem::Type::TASK:
|
||||
if (AppendTask(item, collection))
|
||||
++appended_count;
|
||||
else
|
||||
LOG(ERROR) << "Failed to append task '" << item.title
|
||||
<< "' "
|
||||
"to Jump List.";
|
||||
break;
|
||||
|
||||
case JumpListItem::Type::SEPARATOR:
|
||||
if (category.type == JumpListCategory::Type::TASKS) {
|
||||
if (AppendSeparator(collection))
|
||||
++appended_count;
|
||||
} else {
|
||||
LOG(ERROR) << "Can't append separator to Jump List category "
|
||||
<< "'" << category.name << "'. "
|
||||
<< "Separators are only allowed in the standard 'Tasks' "
|
||||
"Jump List category.";
|
||||
result = JumpListResult::CUSTOM_CATEGORY_SEPARATOR_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case JumpListItem::Type::FILE:
|
||||
if (AppendFile(item, collection))
|
||||
++appended_count;
|
||||
else
|
||||
LOG(ERROR) << "Failed to append '" << item.path.value()
|
||||
<< "' "
|
||||
"to Jump List.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (appended_count == 0)
|
||||
return result;
|
||||
|
||||
if ((static_cast<size_t>(appended_count) < category.items.size()) &&
|
||||
(result == JumpListResult::SUCCESS)) {
|
||||
result = JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
|
||||
CComQIPtr<IObjectArray> items(collection);
|
||||
|
||||
if (category.type == JumpListCategory::Type::TASKS) {
|
||||
if (FAILED(destinations_->AddUserTasks(items))) {
|
||||
LOG(ERROR) << "Failed to append items to the standard Tasks category.";
|
||||
if (result == JumpListResult::SUCCESS)
|
||||
result = JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
} else {
|
||||
HRESULT hr = destinations_->AppendCategory(category.name.c_str(), items);
|
||||
if (FAILED(hr)) {
|
||||
if (hr == static_cast<HRESULT>(0x80040F03)) {
|
||||
LOG(ERROR) << "Failed to append custom category "
|
||||
<< "'" << category.name << "' "
|
||||
<< "to Jump List due to missing file type registration.";
|
||||
result = JumpListResult::MISSING_FILE_TYPE_REGISTRATION_ERROR;
|
||||
} else if (hr == E_ACCESSDENIED) {
|
||||
LOG(ERROR) << "Failed to append custom category "
|
||||
<< "'" << category.name << "' "
|
||||
<< "to Jump List due to system privacy settings.";
|
||||
result = JumpListResult::CUSTOM_CATEGORY_ACCESS_DENIED_ERROR;
|
||||
} else {
|
||||
LOG(ERROR) << "Failed to append custom category "
|
||||
<< "'" << category.name << "' to Jump List.";
|
||||
if (result == JumpListResult::SUCCESS)
|
||||
result = JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method will attempt to append as many categories to the Jump List
|
||||
// as possible, and will return a single error code even if multiple things
|
||||
// went wrong in the process. To get detailed information about what went
|
||||
// wrong enable runtime logging.
|
||||
JumpListResult JumpList::AppendCategories(
|
||||
const std::vector<JumpListCategory>& categories) {
|
||||
DCHECK(destinations_);
|
||||
if (!destinations_)
|
||||
return JumpListResult::GENERIC_ERROR;
|
||||
|
||||
auto result = JumpListResult::SUCCESS;
|
||||
for (const auto& category : categories) {
|
||||
auto latestResult = JumpListResult::SUCCESS;
|
||||
switch (category.type) {
|
||||
case JumpListCategory::Type::TASKS:
|
||||
case JumpListCategory::Type::CUSTOM:
|
||||
latestResult = AppendCategory(category);
|
||||
break;
|
||||
|
||||
case JumpListCategory::Type::RECENT:
|
||||
if (FAILED(destinations_->AppendKnownCategory(KDC_RECENT))) {
|
||||
LOG(ERROR) << "Failed to append Recent category to Jump List.";
|
||||
latestResult = JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case JumpListCategory::Type::FREQUENT:
|
||||
if (FAILED(destinations_->AppendKnownCategory(KDC_FREQUENT))) {
|
||||
LOG(ERROR) << "Failed to append Frequent category to Jump List.";
|
||||
latestResult = JumpListResult::GENERIC_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Keep the first non-generic error code as only one can be returned from
|
||||
// the function (so try to make it the most useful one).
|
||||
if (((result == JumpListResult::SUCCESS) ||
|
||||
(result == JumpListResult::GENERIC_ERROR)) &&
|
||||
(latestResult != JumpListResult::SUCCESS))
|
||||
result = latestResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace atom
|
122
shell/browser/ui/win/jump_list.h
Normal file
122
shell/browser/ui/win/jump_list.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_JUMP_LIST_H_
|
||||
#define ATOM_BROWSER_UI_WIN_JUMP_LIST_H_
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <shobjidl.h>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
enum class JumpListResult : int {
|
||||
SUCCESS = 0,
|
||||
// In JS code this error will manifest as an exception.
|
||||
ARGUMENT_ERROR = 1,
|
||||
// Generic error, the runtime logs may provide some clues.
|
||||
GENERIC_ERROR = 2,
|
||||
// Custom categories can't contain separators.
|
||||
CUSTOM_CATEGORY_SEPARATOR_ERROR = 3,
|
||||
// The app isn't registered to handle a file type found in a custom category.
|
||||
MISSING_FILE_TYPE_REGISTRATION_ERROR = 4,
|
||||
// Custom categories can't be created due to user privacy settings.
|
||||
CUSTOM_CATEGORY_ACCESS_DENIED_ERROR = 5,
|
||||
};
|
||||
|
||||
struct JumpListItem {
|
||||
enum class Type {
|
||||
// A task will launch an app (usually the one that created the Jump List)
|
||||
// with specific arguments.
|
||||
TASK,
|
||||
// Separators can only be inserted between items in the standard Tasks
|
||||
// category, they can't appear in custom categories.
|
||||
SEPARATOR,
|
||||
// A file link will open a file using the app that created the Jump List,
|
||||
// for this to work the app must be registered as a handler for the file
|
||||
// type (though the app doesn't have to be the default handler).
|
||||
FILE
|
||||
};
|
||||
|
||||
Type type = Type::TASK;
|
||||
// For tasks this is the path to the program executable, for file links this
|
||||
// is the full filename.
|
||||
base::FilePath path;
|
||||
base::string16 arguments;
|
||||
base::string16 title;
|
||||
base::string16 description;
|
||||
base::FilePath working_dir;
|
||||
base::FilePath icon_path;
|
||||
int icon_index = 0;
|
||||
|
||||
JumpListItem();
|
||||
JumpListItem(const JumpListItem&);
|
||||
~JumpListItem();
|
||||
};
|
||||
|
||||
struct JumpListCategory {
|
||||
enum class Type {
|
||||
// A custom category can contain tasks and files, but not separators.
|
||||
CUSTOM,
|
||||
// Frequent/Recent categories are managed by the OS, their name and items
|
||||
// can't be set by the app (though items can be set indirectly).
|
||||
FREQUENT,
|
||||
RECENT,
|
||||
// The standard Tasks category can't be renamed by the app, but the app
|
||||
// can set the items that should appear in this category, and those items
|
||||
// can include tasks, files, and separators.
|
||||
TASKS
|
||||
};
|
||||
|
||||
Type type = Type::TASKS;
|
||||
base::string16 name;
|
||||
std::vector<JumpListItem> items;
|
||||
|
||||
JumpListCategory();
|
||||
JumpListCategory(const JumpListCategory&);
|
||||
~JumpListCategory();
|
||||
};
|
||||
|
||||
// Creates or removes a custom Jump List for an app.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/gg281362.aspx
|
||||
class JumpList {
|
||||
public:
|
||||
// |app_id| must be the Application User Model ID of the app for which the
|
||||
// custom Jump List should be created/removed, it's usually obtained by
|
||||
// calling GetCurrentProcessExplicitAppUserModelID().
|
||||
explicit JumpList(const base::string16& app_id);
|
||||
~JumpList();
|
||||
|
||||
// Starts a new transaction, must be called before appending any categories,
|
||||
// aborting or committing. After the method returns |min_items| will indicate
|
||||
// the minimum number of items that will be displayed in the Jump List, and
|
||||
// |removed_items| (if not null) will contain all the items the user has
|
||||
// unpinned from the Jump List. Both parameters are optional.
|
||||
bool Begin(int* min_items = nullptr,
|
||||
std::vector<JumpListItem>* removed_items = nullptr);
|
||||
// Abandons any changes queued up since Begin() was called.
|
||||
bool Abort();
|
||||
// Commits any changes queued up since Begin() was called.
|
||||
bool Commit();
|
||||
// Deletes the custom Jump List and restores the default Jump List.
|
||||
bool Delete();
|
||||
// Appends a category to the custom Jump List.
|
||||
JumpListResult AppendCategory(const JumpListCategory& category);
|
||||
// Appends categories to the custom Jump List.
|
||||
JumpListResult AppendCategories(
|
||||
const std::vector<JumpListCategory>& categories);
|
||||
|
||||
private:
|
||||
base::string16 app_id_;
|
||||
CComPtr<ICustomDestinationList> destinations_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JumpList);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_JUMP_LIST_H_
|
204
shell/browser/ui/win/notify_icon.cc
Normal file
204
shell/browser/ui/win/notify_icon.cc
Normal file
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/notify_icon.h"
|
||||
|
||||
#include "atom/browser/ui/win/notify_icon_host.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
#include "ui/views/controls/menu/menu_runner.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
|
||||
: host_(host),
|
||||
icon_id_(id),
|
||||
window_(window),
|
||||
message_id_(message),
|
||||
weak_factory_(this) {
|
||||
NOTIFYICONDATA icon_data;
|
||||
InitIconData(&icon_data);
|
||||
icon_data.uFlags |= NIF_MESSAGE;
|
||||
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);
|
||||
}
|
||||
|
||||
void NotifyIcon::HandleClickEvent(int modifiers,
|
||||
bool left_mouse_click,
|
||||
bool double_button_click) {
|
||||
gfx::Rect bounds = GetBounds();
|
||||
|
||||
if (left_mouse_click) {
|
||||
if (double_button_click) // double left click
|
||||
NotifyDoubleClicked(bounds, modifiers);
|
||||
else // single left click
|
||||
NotifyClicked(bounds,
|
||||
display::Screen::GetScreen()->GetCursorScreenPoint(),
|
||||
modifiers);
|
||||
return;
|
||||
} else if (!double_button_click) { // single right click
|
||||
if (menu_model_)
|
||||
PopUpContextMenu(gfx::Point(), menu_model_);
|
||||
else
|
||||
NotifyRightClicked(bounds, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyIcon::ResetIcon() {
|
||||
NOTIFYICONDATA icon_data;
|
||||
InitIconData(&icon_data);
|
||||
// Delete any previously existing icon.
|
||||
Shell_NotifyIcon(NIM_DELETE, &icon_data);
|
||||
InitIconData(&icon_data);
|
||||
icon_data.uFlags |= NIF_MESSAGE;
|
||||
icon_data.uCallbackMessage = message_id_;
|
||||
icon_data.hIcon = icon_.get();
|
||||
// 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.";
|
||||
}
|
||||
|
||||
void NotifyIcon::SetImage(HICON image) {
|
||||
icon_ = base::win::ScopedHICON(CopyIcon(image));
|
||||
|
||||
// Create the icon.
|
||||
NOTIFYICONDATA icon_data;
|
||||
InitIconData(&icon_data);
|
||||
icon_data.uFlags |= NIF_ICON;
|
||||
icon_data.hIcon = image;
|
||||
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
|
||||
if (!result)
|
||||
LOG(WARNING) << "Error setting status tray icon image";
|
||||
}
|
||||
|
||||
void NotifyIcon::SetPressedImage(HICON image) {
|
||||
// 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);
|
||||
icon_data.uFlags |= NIF_TIP;
|
||||
wcsncpy_s(icon_data.szTip, base::UTF8ToUTF16(tool_tip).c_str(), _TRUNCATE);
|
||||
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
|
||||
if (!result)
|
||||
LOG(WARNING) << "Unable to set tooltip for status tray icon";
|
||||
}
|
||||
|
||||
void NotifyIcon::DisplayBalloon(HICON icon,
|
||||
const base::string16& title,
|
||||
const base::string16& contents) {
|
||||
NOTIFYICONDATA icon_data;
|
||||
InitIconData(&icon_data);
|
||||
icon_data.uFlags |= NIF_INFO;
|
||||
icon_data.dwInfoFlags = NIIF_INFO;
|
||||
wcsncpy_s(icon_data.szInfoTitle, title.c_str(), _TRUNCATE);
|
||||
wcsncpy_s(icon_data.szInfo, contents.c_str(), _TRUNCATE);
|
||||
icon_data.uTimeout = 0;
|
||||
icon_data.hBalloonIcon = icon;
|
||||
icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
|
||||
|
||||
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
|
||||
if (!result)
|
||||
LOG(WARNING) << "Unable to create status tray balloon.";
|
||||
}
|
||||
|
||||
void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
|
||||
AtomMenuModel* menu_model) {
|
||||
// Returns if context menu isn't set.
|
||||
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();
|
||||
|
||||
// Show menu at mouse's position by default.
|
||||
gfx::Rect rect(pos, gfx::Size());
|
||||
if (pos.IsOrigin())
|
||||
rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
|
||||
|
||||
// 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;
|
||||
|
||||
widget_->Init(params);
|
||||
|
||||
widget_->Show();
|
||||
widget_->Activate();
|
||||
menu_runner_.reset(new views::MenuRunner(
|
||||
menu_model != nullptr ? menu_model : menu_model_,
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS,
|
||||
base::BindRepeating(&NotifyIcon::OnContextMenuClosed,
|
||||
weak_factory_.GetWeakPtr())));
|
||||
menu_runner_->RunMenuAt(widget_.get(), NULL, rect,
|
||||
views::MenuAnchorPosition::kTopLeft,
|
||||
ui::MENU_SOURCE_MOUSE);
|
||||
}
|
||||
|
||||
void NotifyIcon::SetContextMenu(AtomMenuModel* menu_model) {
|
||||
menu_model_ = menu_model;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
RECT rect = {0};
|
||||
Shell_NotifyIconGetRect(&icon_id, &rect);
|
||||
return display::win::ScreenWin::ScreenToDIPRect(window_, gfx::Rect(rect));
|
||||
}
|
||||
|
||||
void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
|
||||
memset(icon_data, 0, sizeof(NOTIFYICONDATA));
|
||||
icon_data->cbSize = sizeof(NOTIFYICONDATA);
|
||||
icon_data->hWnd = window_;
|
||||
icon_data->uID = icon_id_;
|
||||
}
|
||||
|
||||
void NotifyIcon::OnContextMenuClosed() {
|
||||
widget_->Close();
|
||||
}
|
||||
|
||||
} // namespace atom
|
102
shell/browser/ui/win/notify_icon.h
Normal file
102
shell/browser/ui/win/notify_icon.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_NOTIFY_ICON_H_
|
||||
#define ATOM_BROWSER_UI_WIN_NOTIFY_ICON_H_
|
||||
|
||||
#include <windows.h> // windows.h must be included first
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/ui/tray_icon.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
|
||||
namespace gfx {
|
||||
class Point;
|
||||
}
|
||||
|
||||
namespace views {
|
||||
class MenuRunner;
|
||||
class Widget;
|
||||
} // namespace views
|
||||
|
||||
namespace atom {
|
||||
|
||||
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() override;
|
||||
|
||||
// Handles a click event from the user - if |left_button_click| is true and
|
||||
// there is a registered observer, passes the click event to the observer,
|
||||
// otherwise displays the context menu if there is one.
|
||||
void HandleClickEvent(int modifiers,
|
||||
bool left_button_click,
|
||||
bool double_button_click);
|
||||
|
||||
// Re-creates the status tray icon now after the taskbar has been created.
|
||||
void ResetIcon();
|
||||
|
||||
UINT icon_id() const { return icon_id_; }
|
||||
HWND window() const { return window_; }
|
||||
UINT message_id() const { return message_id_; }
|
||||
|
||||
// Overridden from TrayIcon:
|
||||
void SetImage(HICON image) override;
|
||||
void SetPressedImage(HICON image) override;
|
||||
void SetToolTip(const std::string& tool_tip) override;
|
||||
void DisplayBalloon(HICON icon,
|
||||
const base::string16& title,
|
||||
const base::string16& contents) override;
|
||||
void PopUpContextMenu(const gfx::Point& pos,
|
||||
AtomMenuModel* menu_model) override;
|
||||
void SetContextMenu(AtomMenuModel* menu_model) override;
|
||||
gfx::Rect GetBounds() override;
|
||||
|
||||
private:
|
||||
void InitIconData(NOTIFYICONDATA* icon_data);
|
||||
void OnContextMenuClosed();
|
||||
|
||||
// The tray that owns us. Weak.
|
||||
NotifyIconHost* host_;
|
||||
|
||||
// The unique ID corresponding to this icon.
|
||||
UINT icon_id_;
|
||||
|
||||
// Window used for processing messages from this icon.
|
||||
HWND window_;
|
||||
|
||||
// The message identifier used for status icon messages.
|
||||
UINT message_id_;
|
||||
|
||||
// The currently-displayed icon for the window.
|
||||
base::win::ScopedHICON icon_;
|
||||
|
||||
// The context menu.
|
||||
AtomMenuModel* menu_model_ = nullptr;
|
||||
|
||||
// Context menu associated with this icon (if any).
|
||||
std::unique_ptr<views::MenuRunner> menu_runner_;
|
||||
|
||||
// Temporary widget for the context menu, needed for keyboard event capture.
|
||||
std::unique_ptr<views::Widget> widget_;
|
||||
|
||||
// WeakPtrFactory for CloseClosure safety.
|
||||
base::WeakPtrFactory<NotifyIcon> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NotifyIcon);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_NOTIFY_ICON_H_
|
183
shell/browser/ui/win/notify_icon_host.cc
Normal file
183
shell/browser/ui/win/notify_icon_host.cc
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/notify_icon_host.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
#include <winuser.h>
|
||||
|
||||
#include "atom/browser/ui/win/notify_icon.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/wrapped_window_proc.h"
|
||||
#include "ui/events/event_constants.h"
|
||||
#include "ui/events/win/system_event_state_lookup.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const UINT kNotifyIconMessage = WM_APP + 1;
|
||||
|
||||
// |kBaseIconId| is 2 to avoid conflicts with plugins that hard-code id 1.
|
||||
const UINT kBaseIconId = 2;
|
||||
|
||||
const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
|
||||
|
||||
bool IsWinPressed() {
|
||||
return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
|
||||
((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
|
||||
}
|
||||
|
||||
int GetKeyboardModifers() {
|
||||
int modifiers = ui::EF_NONE;
|
||||
if (ui::win::IsShiftPressed())
|
||||
modifiers |= ui::EF_SHIFT_DOWN;
|
||||
if (ui::win::IsCtrlPressed())
|
||||
modifiers |= ui::EF_CONTROL_DOWN;
|
||||
if (ui::win::IsAltPressed())
|
||||
modifiers |= ui::EF_ALT_DOWN;
|
||||
if (IsWinPressed())
|
||||
modifiers |= ui::EF_COMMAND_DOWN;
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NotifyIconHost::NotifyIconHost() {
|
||||
// Register our window class
|
||||
WNDCLASSEX window_class;
|
||||
base::win::InitializeWindowClass(
|
||||
kNotifyIconHostWindowClass,
|
||||
&base::win::WrappedWindowProc<NotifyIconHost::WndProcStatic>, 0, 0, 0,
|
||||
NULL, NULL, NULL, NULL, NULL, &window_class);
|
||||
instance_ = window_class.hInstance;
|
||||
atom_ = RegisterClassEx(&window_class);
|
||||
CHECK(atom_);
|
||||
|
||||
// If the taskbar is re-created after we start up, we have to rebuild all of
|
||||
// our icons.
|
||||
taskbar_created_message_ = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||
|
||||
// Create an offscreen window for handling messages for the status icons. We
|
||||
// create a hidden WS_POPUP window instead of an HWND_MESSAGE window, because
|
||||
// only top-level windows such as popups can receive broadcast messages like
|
||||
// "TaskbarCreated".
|
||||
window_ = CreateWindow(MAKEINTATOM(atom_), 0, WS_POPUP, 0, 0, 0, 0, 0, 0,
|
||||
instance_, 0);
|
||||
gfx::CheckWindowCreated(window_);
|
||||
gfx::SetWindowUserData(window_, this);
|
||||
}
|
||||
|
||||
NotifyIconHost::~NotifyIconHost() {
|
||||
if (window_)
|
||||
DestroyWindow(window_);
|
||||
|
||||
if (atom_)
|
||||
UnregisterClass(MAKEINTATOM(atom_), instance_);
|
||||
|
||||
for (NotifyIcon* ptr : notify_icons_)
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
NotifyIcon* NotifyIconHost::CreateNotifyIcon() {
|
||||
NotifyIcon* notify_icon =
|
||||
new NotifyIcon(this, NextIconId(), window_, kNotifyIconMessage);
|
||||
notify_icons_.push_back(notify_icon);
|
||||
return notify_icon;
|
||||
}
|
||||
|
||||
void NotifyIconHost::Remove(NotifyIcon* icon) {
|
||||
NotifyIcons::iterator i(
|
||||
std::find(notify_icons_.begin(), notify_icons_.end(), icon));
|
||||
|
||||
if (i == notify_icons_.end()) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
notify_icons_.erase(i);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK NotifyIconHost::WndProcStatic(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
NotifyIconHost* msg_wnd =
|
||||
reinterpret_cast<NotifyIconHost*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
if (msg_wnd)
|
||||
return msg_wnd->WndProc(hwnd, message, wparam, lparam);
|
||||
else
|
||||
return ::DefWindowProc(hwnd, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
if (message == taskbar_created_message_) {
|
||||
// We need to reset all of our icons because the taskbar went away.
|
||||
for (NotifyIcons::const_iterator i(notify_icons_.begin());
|
||||
i != notify_icons_.end(); ++i) {
|
||||
NotifyIcon* win_icon = static_cast<NotifyIcon*>(*i);
|
||||
win_icon->ResetIcon();
|
||||
}
|
||||
return TRUE;
|
||||
} else if (message == kNotifyIconMessage) {
|
||||
NotifyIcon* win_icon = NULL;
|
||||
|
||||
// Find the selected status icon.
|
||||
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->icon_id() == wparam) {
|
||||
win_icon = current_win_icon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// It is possible for this procedure to be called with an obsolete icon
|
||||
// id. In that case we should just return early before handling any
|
||||
// actions.
|
||||
if (!win_icon)
|
||||
return TRUE;
|
||||
|
||||
switch (lparam) {
|
||||
case TB_CHECKBUTTON:
|
||||
win_icon->NotifyBalloonShow();
|
||||
return TRUE;
|
||||
|
||||
case TB_INDETERMINATE:
|
||||
win_icon->NotifyBalloonClicked();
|
||||
return TRUE;
|
||||
|
||||
case TB_HIDEBUTTON:
|
||||
win_icon->NotifyBalloonClosed();
|
||||
return TRUE;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
case WM_CONTEXTMENU:
|
||||
// Walk our icons, find which one was clicked on, and invoke its
|
||||
// HandleClickEvent() method.
|
||||
win_icon->HandleClickEvent(
|
||||
GetKeyboardModifers(),
|
||||
(lparam == WM_LBUTTONDOWN || lparam == WM_LBUTTONDBLCLK),
|
||||
(lparam == WM_LBUTTONDBLCLK || lparam == WM_RBUTTONDBLCLK));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return ::DefWindowProc(hwnd, message, wparam, lparam);
|
||||
}
|
||||
|
||||
UINT NotifyIconHost::NextIconId() {
|
||||
UINT icon_id = next_icon_id_++;
|
||||
return kBaseIconId + icon_id;
|
||||
}
|
||||
|
||||
} // namespace atom
|
66
shell/browser/ui/win/notify_icon_host.h
Normal file
66
shell/browser/ui/win/notify_icon_host.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_NOTIFY_ICON_HOST_H_
|
||||
#define ATOM_BROWSER_UI_WIN_NOTIFY_ICON_HOST_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NotifyIcon;
|
||||
|
||||
class NotifyIconHost {
|
||||
public:
|
||||
NotifyIconHost();
|
||||
~NotifyIconHost();
|
||||
|
||||
NotifyIcon* CreateNotifyIcon();
|
||||
void Remove(NotifyIcon* notify_icon);
|
||||
|
||||
private:
|
||||
typedef std::vector<NotifyIcon*> NotifyIcons;
|
||||
|
||||
// Static callback invoked when a message comes in to our messaging window.
|
||||
static LRESULT CALLBACK WndProcStatic(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
UINT NextIconId();
|
||||
|
||||
// The unique icon ID we will assign to the next icon.
|
||||
UINT next_icon_id_ = 1;
|
||||
|
||||
// List containing all active NotifyIcons.
|
||||
NotifyIcons notify_icons_;
|
||||
|
||||
// The window class of |window_|.
|
||||
ATOM atom_ = 0;
|
||||
|
||||
// The handle of the module that contains the window procedure of |window_|.
|
||||
HMODULE instance_ = nullptr;
|
||||
|
||||
// The window used for processing events.
|
||||
HWND window_ = nullptr;
|
||||
|
||||
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
||||
// reset our status icons.
|
||||
UINT taskbar_created_message_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NotifyIconHost);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_NOTIFY_ICON_HOST_H_
|
226
shell/browser/ui/win/taskbar_host.cc
Normal file
226
shell/browser/ui/win/taskbar_host.cc
Normal file
|
@ -0,0 +1,226 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/win/taskbar_host.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/gfx/icon_util.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// From MSDN:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#thumbbars
|
||||
// The thumbnail toolbar has a maximum of seven buttons due to the limited room.
|
||||
const size_t kMaxButtonsCount = 7;
|
||||
|
||||
// The base id of Thumbar button.
|
||||
const int kButtonIdBase = 40001;
|
||||
|
||||
bool GetThumbarButtonFlags(const std::vector<std::string>& flags,
|
||||
THUMBBUTTONFLAGS* out) {
|
||||
THUMBBUTTONFLAGS result = THBF_ENABLED; // THBF_ENABLED == 0
|
||||
for (const auto& flag : flags) {
|
||||
if (flag == "disabled")
|
||||
result |= THBF_DISABLED;
|
||||
else if (flag == "dismissonclick")
|
||||
result |= THBF_DISMISSONCLICK;
|
||||
else if (flag == "nobackground")
|
||||
result |= THBF_NOBACKGROUND;
|
||||
else if (flag == "hidden")
|
||||
result |= THBF_HIDDEN;
|
||||
else if (flag == "noninteractive")
|
||||
result |= THBF_NONINTERACTIVE;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
*out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TaskbarHost::ThumbarButton::ThumbarButton() = default;
|
||||
TaskbarHost::ThumbarButton::ThumbarButton(const TaskbarHost::ThumbarButton&) =
|
||||
default;
|
||||
TaskbarHost::ThumbarButton::~ThumbarButton() = default;
|
||||
|
||||
TaskbarHost::TaskbarHost() {}
|
||||
|
||||
TaskbarHost::~TaskbarHost() {}
|
||||
|
||||
bool TaskbarHost::SetThumbarButtons(HWND window,
|
||||
const std::vector<ThumbarButton>& buttons) {
|
||||
if (buttons.size() > kMaxButtonsCount || !InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
callback_map_.clear();
|
||||
|
||||
// The number of buttons in thumbar can not be changed once it is created,
|
||||
// so we have to claim kMaxButtonsCount buttons initialy in case users add
|
||||
// more buttons later.
|
||||
base::win::ScopedHICON icons[kMaxButtonsCount] = {};
|
||||
THUMBBUTTON thumb_buttons[kMaxButtonsCount] = {};
|
||||
|
||||
for (size_t i = 0; i < kMaxButtonsCount; ++i) {
|
||||
THUMBBUTTON& thumb_button = thumb_buttons[i];
|
||||
|
||||
// Set ID.
|
||||
thumb_button.iId = kButtonIdBase + i;
|
||||
thumb_button.dwMask = THB_FLAGS;
|
||||
|
||||
if (i >= buttons.size()) {
|
||||
// This button is used to occupy the place in toolbar, and it does not
|
||||
// show.
|
||||
thumb_button.dwFlags = THBF_HIDDEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
// This button is user's button.
|
||||
const ThumbarButton& button = buttons[i];
|
||||
|
||||
// Generate flags.
|
||||
thumb_button.dwFlags = THBF_ENABLED;
|
||||
if (!GetThumbarButtonFlags(button.flags, &thumb_button.dwFlags))
|
||||
return false;
|
||||
|
||||
// Set icon.
|
||||
if (!button.icon.IsEmpty()) {
|
||||
thumb_button.dwMask |= THB_ICON;
|
||||
icons[i] = IconUtil::CreateHICONFromSkBitmap(button.icon.AsBitmap());
|
||||
thumb_button.hIcon = icons[i].get();
|
||||
}
|
||||
|
||||
// Set tooltip.
|
||||
if (!button.tooltip.empty()) {
|
||||
thumb_button.dwMask |= THB_TOOLTIP;
|
||||
wcsncpy_s(thumb_button.szTip, base::UTF8ToUTF16(button.tooltip).c_str(),
|
||||
_TRUNCATE);
|
||||
}
|
||||
|
||||
// Save callback.
|
||||
callback_map_[thumb_button.iId] = button.clicked_callback;
|
||||
}
|
||||
|
||||
// Finally add them to taskbar.
|
||||
HRESULT r;
|
||||
if (thumbar_buttons_added_)
|
||||
r = taskbar_->ThumbBarUpdateButtons(window, kMaxButtonsCount,
|
||||
thumb_buttons);
|
||||
else
|
||||
r = taskbar_->ThumbBarAddButtons(window, kMaxButtonsCount, thumb_buttons);
|
||||
|
||||
thumbar_buttons_added_ = true;
|
||||
last_buttons_ = buttons;
|
||||
return SUCCEEDED(r);
|
||||
}
|
||||
|
||||
void TaskbarHost::RestoreThumbarButtons(HWND window) {
|
||||
if (thumbar_buttons_added_) {
|
||||
thumbar_buttons_added_ = false;
|
||||
SetThumbarButtons(window, last_buttons_);
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskbarHost::SetProgressBar(HWND window,
|
||||
double value,
|
||||
const NativeWindow::ProgressState state) {
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
bool success;
|
||||
if (value > 1.0 || state == NativeWindow::ProgressState::kIndeterminate) {
|
||||
success = SUCCEEDED(taskbar_->SetProgressState(window, TBPF_INDETERMINATE));
|
||||
} else if (value < 0 || state == NativeWindow::ProgressState::kNone) {
|
||||
success = SUCCEEDED(taskbar_->SetProgressState(window, TBPF_NOPROGRESS));
|
||||
} else {
|
||||
// Unless SetProgressState set a blocking state (TBPF_ERROR, TBPF_PAUSED)
|
||||
// for the window, a call to SetProgressValue assumes the TBPF_NORMAL
|
||||
// state even if it is not explicitly set.
|
||||
// SetProgressValue overrides and clears the TBPF_INDETERMINATE state.
|
||||
if (state == NativeWindow::ProgressState::kError) {
|
||||
success = SUCCEEDED(taskbar_->SetProgressState(window, TBPF_ERROR));
|
||||
} else if (state == NativeWindow::ProgressState::kPaused) {
|
||||
success = SUCCEEDED(taskbar_->SetProgressState(window, TBPF_PAUSED));
|
||||
} else {
|
||||
success = SUCCEEDED(taskbar_->SetProgressState(window, TBPF_NORMAL));
|
||||
}
|
||||
|
||||
if (success) {
|
||||
int val = static_cast<int>(value * 100);
|
||||
success = SUCCEEDED(taskbar_->SetProgressValue(window, val, 100));
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool TaskbarHost::SetOverlayIcon(HWND window,
|
||||
const gfx::Image& overlay,
|
||||
const std::string& text) {
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
base::win::ScopedHICON icon(
|
||||
IconUtil::CreateHICONFromSkBitmap(overlay.AsBitmap()));
|
||||
return SUCCEEDED(taskbar_->SetOverlayIcon(window, icon.get(),
|
||||
base::UTF8ToUTF16(text).c_str()));
|
||||
}
|
||||
|
||||
bool TaskbarHost::SetThumbnailClip(HWND window, const gfx::Rect& region) {
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
if (region.IsEmpty()) {
|
||||
return SUCCEEDED(taskbar_->SetThumbnailClip(window, NULL));
|
||||
} else {
|
||||
RECT rect =
|
||||
display::win::ScreenWin::DIPToScreenRect(window, region).ToRECT();
|
||||
return SUCCEEDED(taskbar_->SetThumbnailClip(window, &rect));
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskbarHost::SetThumbnailToolTip(HWND window, const std::string& tooltip) {
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
return SUCCEEDED(taskbar_->SetThumbnailTooltip(
|
||||
window, base::UTF8ToUTF16(tooltip).c_str()));
|
||||
}
|
||||
|
||||
bool TaskbarHost::HandleThumbarButtonEvent(int button_id) {
|
||||
if (ContainsKey(callback_map_, button_id)) {
|
||||
auto callback = callback_map_[button_id];
|
||||
if (!callback.is_null())
|
||||
callback.Run();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaskbarHost::InitializeTaskbar() {
|
||||
if (taskbar_)
|
||||
return true;
|
||||
|
||||
if (FAILED(::CoCreateInstance(CLSID_TaskbarList, nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&taskbar_))) ||
|
||||
FAILED(taskbar_->HrInit())) {
|
||||
taskbar_.Reset();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace atom
|
83
shell/browser/ui/win/taskbar_host.h
Normal file
83
shell/browser/ui/win/taskbar_host.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_WIN_TASKBAR_HOST_H_
|
||||
#define ATOM_BROWSER_UI_WIN_TASKBAR_HOST_H_
|
||||
|
||||
#include <shobjidl.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class TaskbarHost {
|
||||
public:
|
||||
struct ThumbarButton {
|
||||
std::string tooltip;
|
||||
gfx::Image icon;
|
||||
std::vector<std::string> flags;
|
||||
base::Closure clicked_callback;
|
||||
|
||||
ThumbarButton();
|
||||
ThumbarButton(const ThumbarButton&);
|
||||
~ThumbarButton();
|
||||
};
|
||||
|
||||
TaskbarHost();
|
||||
virtual ~TaskbarHost();
|
||||
|
||||
// Add or update the buttons in thumbar.
|
||||
bool SetThumbarButtons(HWND window,
|
||||
const std::vector<ThumbarButton>& buttons);
|
||||
|
||||
void RestoreThumbarButtons(HWND window);
|
||||
|
||||
// Set the progress state in taskbar.
|
||||
bool SetProgressBar(HWND window,
|
||||
double value,
|
||||
const NativeWindow::ProgressState state);
|
||||
|
||||
// Set the overlay icon in taskbar.
|
||||
bool SetOverlayIcon(HWND window,
|
||||
const gfx::Image& overlay,
|
||||
const std::string& text);
|
||||
|
||||
// Set the region of the window to show as a thumbnail in taskbar.
|
||||
bool SetThumbnailClip(HWND window, const gfx::Rect& region);
|
||||
|
||||
// Set the tooltip for the thumbnail in taskbar.
|
||||
bool SetThumbnailToolTip(HWND window, const std::string& tooltip);
|
||||
|
||||
// Called by the window that there is a button in thumbar clicked.
|
||||
bool HandleThumbarButtonEvent(int button_id);
|
||||
|
||||
private:
|
||||
// Initialize the taskbar object.
|
||||
bool InitializeTaskbar();
|
||||
|
||||
using CallbackMap = std::map<int, base::Closure>;
|
||||
CallbackMap callback_map_;
|
||||
|
||||
std::vector<ThumbarButton> last_buttons_;
|
||||
|
||||
// The COM object of taskbar.
|
||||
Microsoft::WRL::ComPtr<ITaskbarList3> taskbar_;
|
||||
|
||||
// Whether we have already added the buttons to thumbar.
|
||||
bool thumbar_buttons_added_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TaskbarHost);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_WIN_TASKBAR_HOST_H_
|
Loading…
Add table
Add a link
Reference in a new issue