From 54af048f04c3194530081c56e10c567a14195339 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Sun, 2 Aug 2015 11:11:29 +0800 Subject: [PATCH] win: Add BrowserWindow.setThumbarButtons API. --- atom/browser/api/atom_api_window.cc | 34 +++++ atom/browser/api/atom_api_window.h | 8 ++ .../atom_desktop_window_tree_host_win.cc | 44 +++++++ .../atom_desktop_window_tree_host_win.h | 41 ++++++ atom/browser/native_window.h | 8 ++ atom/browser/native_window_views.cc | 22 ++++ atom/browser/native_window_views.h | 9 ++ atom/browser/ui/win/thumbar_host.cc | 122 ++++++++++++++++++ atom/browser/ui/win/thumbar_host.h | 51 ++++++++ filenames.gypi | 4 + 10 files changed, 343 insertions(+) create mode 100644 atom/browser/atom_desktop_window_tree_host_win.cc create mode 100644 atom/browser/atom_desktop_window_tree_host_win.h create mode 100644 atom/browser/ui/win/thumbar_host.cc create mode 100644 atom/browser/ui/win/thumbar_host.h diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 5f7c2503fced..bcdc57c3c704 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -20,6 +20,30 @@ #include "atom/common/node_includes.h" +#if defined(OS_WIN) +#include "atom/browser/ui/win/thumbar_host.h" +#endif + +namespace mate { + +#if defined(OS_WIN) +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Handle val, + atom::ThumbarHost::ThumbarButton* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + dict.Get("click", &(out->clicked_callback)); + dict.Get("tooltip", &(out->tooltip)); + dict.Get("flags", &out->flags); + return dict.Get("icon", &(out->icon)); + } +}; +#endif + +} // namespace mate + namespace atom { namespace api { @@ -413,6 +437,13 @@ void Window::SetOverlayIcon(const gfx::Image& overlay, window_->SetOverlayIcon(overlay, description); } +#if defined(OS_WIN) +void Window::SetThumbarButtons( + const std::vector& buttons) { + window_->SetThumbarButtons(buttons); +} +#endif + void Window::SetMenu(v8::Isolate* isolate, v8::Local value) { mate::Handle menu; if (value->IsObject() && @@ -538,6 +569,9 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("capturePage", &Window::CapturePage) .SetMethod("setProgressBar", &Window::SetProgressBar) .SetMethod("setOverlayIcon", &Window::SetOverlayIcon) +#if defined(OS_WIN) + .SetMethod("setThumbarButtons", &Window::SetThumbarButtons) +#endif .SetMethod("setMenu", &Window::SetMenu) .SetMethod("setAutoHideMenuBar", &Window::SetAutoHideMenuBar) .SetMethod("isMenuBarAutoHide", &Window::IsMenuBarAutoHide) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index cc2b3a64b5e9..fdb10825a6ca 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -14,6 +14,10 @@ #include "atom/browser/native_window_observer.h" #include "native_mate/handle.h" +#if defined(OS_WIN) +#include "atom/browser/ui/win/thumbar_host.h" +#endif + class GURL; namespace gfx { @@ -129,6 +133,10 @@ class Window : public mate::TrackableObject, void SetProgressBar(double progress); void SetOverlayIcon(const gfx::Image& overlay, const std::string& description); +#if defined(OS_WIN) + void SetThumbarButtons( + const std::vector& buttons); +#endif void SetMenu(v8::Isolate* isolate, v8::Local menu); void SetAutoHideMenuBar(bool auto_hide); bool IsMenuBarAutoHide(); diff --git a/atom/browser/atom_desktop_window_tree_host_win.cc b/atom/browser/atom_desktop_window_tree_host_win.cc new file mode 100644 index 000000000000..9c5ebf0f42c2 --- /dev/null +++ b/atom/browser/atom_desktop_window_tree_host_win.cc @@ -0,0 +1,44 @@ +// 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/atom_desktop_window_tree_host_win.h" +#include "atom/browser/ui/win/thumbar_host.h" + +namespace atom { + +AtomDesktopWindowTreeHostWin::AtomDesktopWindowTreeHostWin( + views::internal::NativeWidgetDelegate* native_widget_delegate, + views::DesktopNativeWidgetAura* desktop_native_widget_aura) + : views::DesktopWindowTreeHostWin(native_widget_delegate, + desktop_native_widget_aura) { +} + +AtomDesktopWindowTreeHostWin::~AtomDesktopWindowTreeHostWin() { +} + +bool AtomDesktopWindowTreeHostWin::SetThumbarButtons( + HWND window, + const std::vector& buttons) { + if (!thumbar_host_.get()) { + thumbar_host_.reset(new ThumbarHost(window)); + } + return thumbar_host_->SetThumbarButtons(buttons); +} + +bool AtomDesktopWindowTreeHostWin::PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + switch (message) { + case WM_COMMAND: { + int id = LOWORD(w_param); + if (thumbar_host_ && thumbar_host_->HandleThumbarButtonEvent(id)) + return true; + } + } + + return false; +} + +} // namespace atom diff --git a/atom/browser/atom_desktop_window_tree_host_win.h b/atom/browser/atom_desktop_window_tree_host_win.h new file mode 100644 index 000000000000..0271d06e6af2 --- /dev/null +++ b/atom/browser/atom_desktop_window_tree_host_win.h @@ -0,0 +1,41 @@ +// 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_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ +#define ATOM_BROWSER_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ + +#include + +#include + +#include "atom/browser/ui/win/thumbar_host.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" + +namespace atom { + +class AtomDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin { + public: + AtomDesktopWindowTreeHostWin( + views::internal::NativeWidgetDelegate* native_widget_delegate, + views::DesktopNativeWidgetAura* desktop_native_widget_aura); + ~AtomDesktopWindowTreeHostWin(); + + bool SetThumbarButtons( + HWND window, + const std::vector& buttons); + + protected: + bool PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) override; + + private: + scoped_ptr thumbar_host_; +}; + +} // namespace atom + +#endif // ATOM_BROWSER_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 8919d529df03..0ae22214f563 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -23,6 +23,10 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" +#if defined(OS_WIN) +#include "atom/browser/ui/win/thumbar_host.h" +#endif + class SkRegion; namespace base { @@ -144,6 +148,10 @@ class NativeWindow : public content::WebContentsObserver, const std::string& description) = 0; virtual void SetVisibleOnAllWorkspaces(bool visible) = 0; virtual bool IsVisibleOnAllWorkspaces() = 0; +#if defined(OS_WIN) + virtual bool SetThumbarButtons( + const std::vector& buttons) = 0; +#endif virtual bool IsClosed() const { return is_closed_; } diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 5f3472a7ffdb..e0221fbde597 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -27,6 +27,7 @@ #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/window/client_view.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/shadow_types.h" @@ -44,6 +45,7 @@ #include "ui/gfx/x/x11_types.h" #include "ui/views/window/native_frame_view.h" #elif defined(OS_WIN) +#include "atom/browser/atom_desktop_window_tree_host_win.h" #include "atom/browser/ui/views/win_frame_view.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" @@ -215,6 +217,14 @@ NativeWindowViews::NativeWindowViews( if (transparent()) params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; +#if defined(OS_WIN) + params.native_widget = + new views::DesktopNativeWidgetAura(window_.get()); + atom_desktop_window_tree_host_win_ = new AtomDesktopWindowTreeHostWin( + window_.get(), + static_cast(params.native_widget)); + params.desktop_window_tree_host = atom_desktop_window_tree_host_win_; +#endif #if defined(USE_X11) std::string name = Browser::Get()->GetName(); // Set WM_WINDOW_ROLE. @@ -707,6 +717,18 @@ bool NativeWindowViews::IsVisibleOnAllWorkspaces() { return false; } +#if defined(OS_WIN) +bool NativeWindowViews::SetThumbarButtons( + const std::vector& buttons) { + if (atom_desktop_window_tree_host_win_) { + return atom_desktop_window_tree_host_win_->SetThumbarButtons( + views::HWNDForNativeWindow(window_->GetNativeWindow()), + buttons); + } + return false; +} +#endif + gfx::AcceleratedWidget NativeWindowViews::GetAcceleratedWidget() { return GetNativeWindow()->GetHost()->GetAcceleratedWidget(); } diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index dcf1f603a4f4..94c0c04373f7 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -23,6 +23,9 @@ namespace atom { class GlobalMenuBarX11; class MenuBar; class WindowStateWatcher; +#if defined(OS_WIN) +class AtomDesktopWindowTreeHostWin; +#endif class NativeWindowViews : public NativeWindow, public views::WidgetDelegateView, @@ -79,6 +82,10 @@ class NativeWindowViews : public NativeWindow, bool IsMenuBarVisible() override; void SetVisibleOnAllWorkspaces(bool visible) override; bool IsVisibleOnAllWorkspaces() override; +#if defined(OS_WIN) + bool SetThumbarButtons( + const std::vector& buttons) override; +#endif gfx::AcceleratedWidget GetAcceleratedWidget(); @@ -154,6 +161,8 @@ class NativeWindowViews : public NativeWindow, // Handles window state events. scoped_ptr window_state_watcher_; #elif defined(OS_WIN) + // Weak ref. + AtomDesktopWindowTreeHostWin* atom_desktop_window_tree_host_win_; // Records window was whether restored from minimized state or maximized // state. bool is_minimized_; diff --git a/atom/browser/ui/win/thumbar_host.cc b/atom/browser/ui/win/thumbar_host.cc new file mode 100644 index 000000000000..22e9ba8c4169 --- /dev/null +++ b/atom/browser/ui/win/thumbar_host.cc @@ -0,0 +1,122 @@ +// 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/thumbar_host.h" + +#include + +#include "base/win/scoped_comptr.h" +#include "base/win/win_util.h" +#include "base/win/wrapped_window_proc.h" +#include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/icon_util.h" +#include "ui/gfx/win/hwnd_util.h" + +namespace atom { + +namespace { + +// The thumbnail toolbar has a maximum of seven buttons due to the limited room. +const int kMaxButtonsCount = 7; + +bool GetThumbarButtonFlags(const std::vector& flags, + THUMBBUTTONFLAGS* out) { + if (flags.empty()) { + *out = THBF_ENABLED; + return true; + } + THUMBBUTTONFLAGS result = static_cast(0); + for (const auto& flag : flags) { + if (flag == "enabled") { + result |= THBF_ENABLED; + } else 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 + +ThumbarHost::ThumbarHost(HWND window) : is_initialized_(false), + window_(window) { +} + +ThumbarHost::~ThumbarHost() { +} + +bool ThumbarHost::SetThumbarButtons( + const std::vector& buttons) { + THUMBBUTTON thumb_buttons[kMaxButtonsCount]; + thumbar_button_clicked_callback_map_.clear(); + + // Once a toolbar with a set of buttons is added to thumbnail, there is no way + // to remove it without re-creating the window. + // To achieve to re-set thumbar buttons, we initialize the buttons with + // HIDDEN state and only updated the caller's specified buttons. + // + // Initialize all thumb buttons with HIDDEN state. + for (int i = 0; i < kMaxButtonsCount; ++i) { + thumb_buttons[i].iId = i; + thumb_buttons[i].dwFlags = THBF_HIDDEN; + } + + // Update the callers' specified buttons. + for (size_t i = 0; i < buttons.size(); ++i) { + if (!GetThumbarButtonFlags(buttons[i].flags, &thumb_buttons[i].dwFlags)) + return false; + thumb_buttons[i].dwMask = THB_ICON | THB_FLAGS; + thumb_buttons[i].hIcon = IconUtil::CreateHICONFromSkBitmap( + buttons[i].icon.AsBitmap()); + if (!buttons[i].tooltip.empty()) { + thumb_buttons[i].dwMask |= THB_TOOLTIP; + wcscpy_s(thumb_buttons[i].szTip, + base::UTF8ToUTF16(buttons[i].tooltip).c_str()); + } + + thumbar_button_clicked_callback_map_[i] = + buttons[i].clicked_callback; + } + + base::win::ScopedComPtr taskbar; + if (FAILED(taskbar.CreateInstance(CLSID_TaskbarList, + nullptr, + CLSCTX_INPROC_SERVER)) || + FAILED(taskbar->HrInit())) { + return false; + } + if (!is_initialized_) { + is_initialized_ = true; + return taskbar->ThumbBarAddButtons( + window_, kMaxButtonsCount, thumb_buttons) == S_OK; + } + + return taskbar->ThumbBarUpdateButtons( + window_, kMaxButtonsCount, thumb_buttons) == S_OK; +} + +bool ThumbarHost::HandleThumbarButtonEvent(int button_id) { + if (thumbar_button_clicked_callback_map_.find(button_id) != + thumbar_button_clicked_callback_map_.end()) { + auto callback = thumbar_button_clicked_callback_map_[button_id]; + if (!callback.is_null()) + callback.Run(); + return true; + } + return false; +} + +} // namespace atom diff --git a/atom/browser/ui/win/thumbar_host.h b/atom/browser/ui/win/thumbar_host.h new file mode 100644 index 000000000000..2ab3b06a381d --- /dev/null +++ b/atom/browser/ui/win/thumbar_host.h @@ -0,0 +1,51 @@ +// 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_THUMBAR_HOST_H_ +#define ATOM_BROWSER_UI_WIN_THUMBAR_HOST_H_ + +#include + +#include +#include +#include + +#include "base/callback.h" +#include "ui/gfx/image/image.h" + +namespace atom { + +class ThumbarHost { + public: + using ThumbarButtonClickedCallback = base::Callback; + + struct ThumbarButton { + std::string tooltip; + gfx::Image icon; + std::vector flags; + ThumbarButtonClickedCallback clicked_callback; + }; + + explicit ThumbarHost(HWND window); + ~ThumbarHost(); + + bool SetThumbarButtons( + const std::vector& buttons); + bool HandleThumbarButtonEvent(int button_id); + + private: + using ThumbarButtonClickedCallbackMap = std::map< + int, ThumbarButtonClickedCallback>; + ThumbarButtonClickedCallbackMap thumbar_button_clicked_callback_map_; + + bool is_initialized_; + + HWND window_; + + DISALLOW_COPY_AND_ASSIGN(ThumbarHost); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_WIN_THUMBAR_HOST_H_ diff --git a/filenames.gypi b/filenames.gypi index 82f9ea95d078..3b3d3a4cc48f 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -119,6 +119,8 @@ 'atom/browser/atom_browser_main_parts.h', 'atom/browser/atom_browser_main_parts_linux.cc', 'atom/browser/atom_browser_main_parts_mac.mm', + "atom/browser/atom_desktop_window_tree_host_win.cc", + "atom/browser/atom_desktop_window_tree_host_win.h", 'atom/browser/atom_javascript_dialog_manager.cc', 'atom/browser/atom_javascript_dialog_manager.h', 'atom/browser/atom_quota_permission_context.cc', @@ -206,6 +208,8 @@ 'atom/browser/ui/win/notify_icon_host.h', 'atom/browser/ui/win/notify_icon.cc', 'atom/browser/ui/win/notify_icon.h', + 'atom/browser/ui/win/thumbar_host.cc', + 'atom/browser/ui/win/thumbar_host.h', 'atom/browser/ui/x/window_state_watcher.cc', 'atom/browser/ui/x/window_state_watcher.h', 'atom/browser/ui/x/x_window_utils.cc',