mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
A modal dialog is a child CefWindow that implements some special behaviors relative to a parent CefWindow. Like any CefWindow it can be framed with titlebar or frameless, and optionally contain draggable regions (subject to platform limitations described below). Modal dialogs are shown centered on the parent window (inside a single display) and always stay on top of the parent window in z-order. Sizing behavior and available window buttons are controlled via the usual CefWindowDelegate callbacks. For example, the dialog can have a preferred size with resize, minimize and maximize disabled (via GetPreferredSize, CanResize, CanMinimize and CanMaximize respectively). This change adds support for two modality modes. With window modality all controls in the parent window are disabled. With browser modality only the browser view in the parent window is disabled. Both modality modes require that a valid parent window be returned via GetParentWindow. For window modality return true from IsWindowModalDialog and call CefWindow::Show. For browser modality return false from IsWindowModalDialog (the default value) and call CefWindow::ShowAsBrowserModalDialog with a reference to the parent window's browser view. Window modal dialog behavior depends on the platform. On Windows and Linux these dialogs have a titlebar and can be moved independent of the parent window. On macOS these dialogs do not have a titlebar, move with the parent window, and do not support draggable regions (because they are implemented using sheets). On Linux disabling the parent window controls requires a window manager the supports _NET_WM_STATE_MODAL. Browser modal dialog behavior is similar on all platforms. The dialog will be automatically sized and positioned relative to the parent window's browser view. Closing the parent window or navigating the parent browser view will dismiss the dialog. The dialog can also be moved independent of the parent window though it will be recentered when the parent window itself is resized or redisplayed. On MacOS the dialog will move along with the parent window while on Windows and Linux the parent window can be moved independently. To test: Use the Tests > Dialog Window menu option in cefclient with Views enabled (`--use-views` or `--enable-chrome-runtime` command-line flag). Browser modal dialog is the default behavior. For window modal dialog add the `--use-window-modal-dialog` command-line flag.
1349 lines
38 KiB
C++
1349 lines
38 KiB
C++
// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
|
|
// reserved. Use of this source code is governed by a BSD-style license that
|
|
// can be found in the LICENSE file.
|
|
|
|
#include "tests/cefclient/browser/views_window.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "include/base/cef_build.h"
|
|
#include "include/base/cef_callback.h"
|
|
#include "include/cef_app.h"
|
|
#include "include/cef_i18n_util.h"
|
|
#include "include/views/cef_box_layout.h"
|
|
#include "include/wrapper/cef_helpers.h"
|
|
#include "tests/cefclient/browser/main_context.h"
|
|
#include "tests/cefclient/browser/resource.h"
|
|
#include "tests/cefclient/browser/views_style.h"
|
|
#include "tests/shared/browser/extension_util.h"
|
|
#include "tests/shared/common/client_switches.h"
|
|
|
|
#if !defined(OS_WIN)
|
|
#define VK_ESCAPE 0x1B
|
|
#define VK_RETURN 0x0D
|
|
#define VK_MENU 0x12 // ALT key.
|
|
#endif
|
|
|
|
namespace client {
|
|
|
|
namespace {
|
|
|
|
const char kDefaultExtensionIcon[] = "window_icon";
|
|
|
|
// Default window size.
|
|
constexpr int kDefaultWidth = 800;
|
|
constexpr int kDefaultHeight = 600;
|
|
|
|
#if defined(OS_MAC)
|
|
constexpr int kTitleBarHeight = 35;
|
|
constexpr int kWindowButtonsWidth = 80;
|
|
#endif
|
|
|
|
// Control IDs for Views in the top-level Window.
|
|
enum ControlIds {
|
|
ID_WINDOW = 1,
|
|
ID_BROWSER_VIEW,
|
|
ID_BACK_BUTTON,
|
|
ID_FORWARD_BUTTON,
|
|
ID_STOP_BUTTON,
|
|
ID_RELOAD_BUTTON,
|
|
ID_URL_TEXTFIELD,
|
|
ID_MENU_BUTTON,
|
|
|
|
// Reserved range of top menu button IDs.
|
|
ID_TOP_MENU_FIRST,
|
|
ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10,
|
|
|
|
// Reserved range of extension button IDs.
|
|
ID_EXTENSION_BUTTON_FIRST,
|
|
ID_EXTENSION_BUTTON_LAST = ID_EXTENSION_BUTTON_FIRST + 10,
|
|
};
|
|
|
|
typedef std::vector<CefRefPtr<CefLabelButton>> LabelButtons;
|
|
|
|
// Make all |buttons| the same size.
|
|
void MakeButtonsSameSize(const LabelButtons& buttons) {
|
|
CefSize size;
|
|
|
|
// Determine the largest button size.
|
|
for (size_t i = 0U; i < buttons.size(); ++i) {
|
|
const CefSize& button_size = buttons[i]->GetPreferredSize();
|
|
if (size.width < button_size.width) {
|
|
size.width = button_size.width;
|
|
}
|
|
if (size.height < button_size.height) {
|
|
size.height = button_size.height;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0U; i < buttons.size(); ++i) {
|
|
// Set the button's minimum size.
|
|
buttons[i]->SetMinimumSize(size);
|
|
|
|
// Re-layout the button and all parent Views.
|
|
buttons[i]->InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
void AddTestMenuItems(CefRefPtr<CefMenuModel> test_menu) {
|
|
test_menu->AddItem(ID_TESTS_GETSOURCE, "Get Source");
|
|
test_menu->AddItem(ID_TESTS_GETTEXT, "Get Text");
|
|
test_menu->AddItem(ID_TESTS_WINDOW_NEW, "New Window");
|
|
test_menu->AddItem(ID_TESTS_WINDOW_POPUP, "Popup Window");
|
|
test_menu->AddItem(ID_TESTS_WINDOW_DIALOG, "Dialog Window");
|
|
test_menu->AddItem(ID_TESTS_REQUEST, "Request");
|
|
test_menu->AddItem(ID_TESTS_ZOOM_IN, "Zoom In");
|
|
test_menu->AddItem(ID_TESTS_ZOOM_OUT, "Zoom Out");
|
|
test_menu->AddItem(ID_TESTS_ZOOM_RESET, "Zoom Reset");
|
|
test_menu->AddItem(ID_TESTS_TRACING_BEGIN, "Begin Tracing");
|
|
test_menu->AddItem(ID_TESTS_TRACING_END, "End Tracing");
|
|
test_menu->AddItem(ID_TESTS_PRINT, "Print");
|
|
test_menu->AddItem(ID_TESTS_PRINT_TO_PDF, "Print to PDF");
|
|
test_menu->AddItem(ID_TESTS_MUTE_AUDIO, "Mute Audio");
|
|
test_menu->AddItem(ID_TESTS_UNMUTE_AUDIO, "Unmute Audio");
|
|
test_menu->AddItem(ID_TESTS_OTHER_TESTS, "Other Tests");
|
|
}
|
|
|
|
void AddFileMenuItems(CefRefPtr<CefMenuModel> file_menu) {
|
|
file_menu->AddItem(ID_QUIT, "E&xit");
|
|
|
|
// Show the accelerator shortcut text in the menu.
|
|
file_menu->SetAcceleratorAt(file_menu->GetCount() - 1, 'X', false, false,
|
|
true);
|
|
}
|
|
|
|
CefBrowserViewDelegate::ChromeToolbarType CalculateChromeToolbarType(
|
|
const std::string& toolbar_type,
|
|
bool hide_toolbar,
|
|
bool with_overlay_controls) {
|
|
if (!MainContext::Get()->UseChromeRuntime() || toolbar_type == "none" ||
|
|
hide_toolbar) {
|
|
return CEF_CTT_NONE;
|
|
}
|
|
|
|
if (toolbar_type == "location") {
|
|
return CEF_CTT_LOCATION;
|
|
}
|
|
|
|
return with_overlay_controls ? CEF_CTT_LOCATION : CEF_CTT_NORMAL;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
CefRefPtr<ViewsWindow> ViewsWindow::Create(
|
|
WindowType type,
|
|
Delegate* delegate,
|
|
CefRefPtr<CefClient> client,
|
|
const CefString& url,
|
|
const CefBrowserSettings& settings,
|
|
CefRefPtr<CefRequestContext> request_context) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(delegate);
|
|
|
|
// Create a new ViewsWindow.
|
|
CefRefPtr<ViewsWindow> views_window =
|
|
new ViewsWindow(type, delegate, nullptr);
|
|
|
|
// Create a new BrowserView.
|
|
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
|
|
client, url, settings, nullptr, request_context, views_window);
|
|
|
|
// Associate the BrowserView with the ViewsWindow.
|
|
views_window->SetBrowserView(browser_view);
|
|
|
|
// Create a new top-level Window. It will show itself after creation.
|
|
CefWindow::CreateTopLevelWindow(views_window);
|
|
|
|
return views_window;
|
|
}
|
|
|
|
void ViewsWindow::Show() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
if (type_ == WindowType::DIALOG) {
|
|
if (use_window_modal_dialog_) {
|
|
// Show as a window modal dialog (IsWindowModalDialog() will return
|
|
// true).
|
|
window_->Show();
|
|
} else {
|
|
CefRefPtr<CefBrowserView> browser_view;
|
|
if (auto parent_window = delegate_->GetParentWindow()) {
|
|
if (auto view = parent_window->GetViewForID(ID_BROWSER_VIEW)) {
|
|
browser_view = view->AsBrowserView();
|
|
}
|
|
}
|
|
CHECK(browser_view);
|
|
|
|
// Show as a browser modal dialog (relative to |browser_view|).
|
|
window_->ShowAsBrowserModalDialog(browser_view);
|
|
}
|
|
} else {
|
|
window_->Show();
|
|
}
|
|
}
|
|
if (browser_view_ && !window_->IsMinimized()) {
|
|
// Give keyboard focus to the BrowserView.
|
|
browser_view_->RequestFocus();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::Hide() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->Hide();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::Minimize() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->Minimize();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::Maximize() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->Maximize();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetBounds(const CefRect& bounds) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->SetBounds(bounds);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetBrowserSize(const CefSize& size,
|
|
bool has_position,
|
|
const CefPoint& position) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (browser_view_) {
|
|
browser_view_->SetSize(size);
|
|
}
|
|
if (window_) {
|
|
window_->SizeToPreferredSize();
|
|
if (has_position) {
|
|
window_->SetPosition(position);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::Close(bool force) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (!browser_view_) {
|
|
return;
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (browser) {
|
|
// This will result in a call to CefWindow::Close() which will then call
|
|
// ViewsWindow::CanClose().
|
|
browser->GetHost()->CloseBrowser(force);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetAddress(const std::string& url) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (!window_) {
|
|
return;
|
|
}
|
|
|
|
// |location_bar_| may instead be a Chrome toolbar.
|
|
if (location_bar_ && location_bar_->AsTextfield()) {
|
|
location_bar_->AsTextfield()->SetText(url);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetTitle(const std::string& title) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->SetTitle(title);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetFavicon(CefRefPtr<CefImage> image) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Window icons should be 16 DIP in size.
|
|
DCHECK_EQ(std::max(image->GetWidth(), image->GetHeight()), 16U);
|
|
|
|
if (window_) {
|
|
window_->SetWindowIcon(image);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetFullscreen(bool fullscreen) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
// Hide the top controls while in full-screen mode.
|
|
if (with_controls_) {
|
|
ShowTopControls(!fullscreen);
|
|
}
|
|
|
|
window_->SetFullscreen(fullscreen);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetAlwaysOnTop(bool on_top) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (window_) {
|
|
window_->SetAlwaysOnTop(on_top);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetLoadingState(bool isLoading,
|
|
bool canGoBack,
|
|
bool canGoForward) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (!window_ || chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
return;
|
|
}
|
|
|
|
if (with_controls_) {
|
|
EnableView(ID_BACK_BUTTON, canGoBack);
|
|
EnableView(ID_FORWARD_BUTTON, canGoForward);
|
|
EnableView(ID_RELOAD_BUTTON, !isLoading);
|
|
EnableView(ID_STOP_BUTTON, isLoading);
|
|
}
|
|
if (location_bar_) {
|
|
EnableView(ID_URL_TEXTFIELD, true);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::SetDraggableRegions(
|
|
const std::vector<CefDraggableRegion>& regions) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_ || !browser_view_) {
|
|
return;
|
|
}
|
|
|
|
// Convert the regions from BrowserView to Window coordinates.
|
|
std::vector<CefDraggableRegion> window_regions = regions;
|
|
for (auto& region : window_regions) {
|
|
CefPoint origin = CefPoint(region.bounds.x, region.bounds.y);
|
|
browser_view_->ConvertPointToWindow(origin);
|
|
region.bounds.x = origin.x;
|
|
region.bounds.y = origin.y;
|
|
}
|
|
|
|
if (overlay_controls_) {
|
|
// Exclude all regions obscured by overlays.
|
|
overlay_controls_->UpdateDraggableRegions(window_regions);
|
|
}
|
|
|
|
window_->SetDraggableRegions(window_regions);
|
|
}
|
|
|
|
void ViewsWindow::TakeFocus(bool next) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_) {
|
|
return;
|
|
}
|
|
|
|
if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
top_toolbar_->RequestFocus();
|
|
} else if (location_bar_) {
|
|
// Give focus to the location bar.
|
|
location_bar_->RequestFocus();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
views_style::ApplyTo(model);
|
|
}
|
|
|
|
void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (extensions.empty()) {
|
|
if (!extensions_.empty()) {
|
|
extensions_.clear();
|
|
UpdateExtensionControls();
|
|
}
|
|
return;
|
|
}
|
|
|
|
ImageCache::ImageInfoSet image_set;
|
|
|
|
ExtensionSet::const_iterator it = extensions.begin();
|
|
for (; it != extensions.end(); ++it) {
|
|
CefRefPtr<CefExtension> extension = *it;
|
|
bool internal = false;
|
|
const std::string& icon_path =
|
|
extension_util::GetExtensionIconPath(extension, &internal);
|
|
if (!icon_path.empty()) {
|
|
// Load the extension icon.
|
|
image_set.push_back(
|
|
ImageCache::ImageInfo::Create1x(icon_path, icon_path, internal));
|
|
} else {
|
|
// Get a nullptr image and use the default icon.
|
|
image_set.push_back(ImageCache::ImageInfo::Empty());
|
|
}
|
|
}
|
|
|
|
delegate_->GetImageCache()->LoadImages(
|
|
image_set,
|
|
base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
|
|
}
|
|
|
|
bool ViewsWindow::GetWindowRestorePreferences(
|
|
cef_show_state_t& show_state,
|
|
std::optional<CefRect>& dip_bounds) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (!window_) {
|
|
return false;
|
|
}
|
|
|
|
show_state = CEF_SHOW_STATE_NORMAL;
|
|
if (window_->IsMinimized()) {
|
|
show_state = CEF_SHOW_STATE_MINIMIZED;
|
|
} else if (window_->IsMaximized()) {
|
|
show_state = CEF_SHOW_STATE_MAXIMIZED;
|
|
} else if (window_->IsFullscreen()) {
|
|
show_state = CEF_SHOW_STATE_FULLSCREEN;
|
|
}
|
|
|
|
if (show_state == CEF_SHOW_STATE_NORMAL) {
|
|
// Use the current visible bounds.
|
|
dip_bounds = window_->GetBoundsInScreen();
|
|
} else {
|
|
// Use the last known visible bounds.
|
|
dip_bounds = last_visible_bounds_;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ViewsWindow::SetTitlebarHeight(const std::optional<float>& height) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (height.has_value()) {
|
|
override_titlebar_height_ = height;
|
|
} else {
|
|
override_titlebar_height_ = default_titlebar_height_;
|
|
}
|
|
NudgeWindow();
|
|
}
|
|
|
|
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
const CefBrowserSettings& settings,
|
|
CefRefPtr<CefClient> client,
|
|
bool is_devtools) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// The popup browser client is created in CefLifeSpanHandler::OnBeforePopup()
|
|
// (e.g. via RootWindowViews::InitAsPopup()). The Delegate (RootWindowViews)
|
|
// knows the association between |client| and itself.
|
|
Delegate* popup_delegate = delegate_->GetDelegateForPopup(client);
|
|
|
|
// May be nullptr when using the default popup behavior.
|
|
if (!popup_delegate) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Should not be the same RootWindowViews that owns |this|.
|
|
DCHECK(popup_delegate != delegate_);
|
|
|
|
// Create a new ViewsWindow for the popup BrowserView.
|
|
return new ViewsWindow(WindowType::NORMAL, popup_delegate, nullptr);
|
|
}
|
|
|
|
bool ViewsWindow::OnPopupBrowserViewCreated(
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
CefRefPtr<CefBrowserView> popup_browser_view,
|
|
bool is_devtools) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Retrieve the ViewsWindow created in GetDelegateForPopupBrowserView.
|
|
CefRefPtr<ViewsWindow> popup_window =
|
|
static_cast<ViewsWindow*>(static_cast<CefBrowserViewDelegate*>(
|
|
popup_browser_view->GetDelegate().get()));
|
|
|
|
// May be nullptr when using the default popup behavior.
|
|
if (!popup_window) {
|
|
return false;
|
|
}
|
|
|
|
// Should not be the same ViewsWindow as |this|.
|
|
DCHECK(popup_window != this);
|
|
|
|
// Associate the ViewsWindow with the new popup browser.
|
|
popup_window->SetBrowserView(popup_browser_view);
|
|
|
|
// Create a new top-level Window for the popup. It will show itself after
|
|
// creation.
|
|
CefWindow::CreateTopLevelWindow(popup_window);
|
|
|
|
// We created the Window.
|
|
return true;
|
|
}
|
|
|
|
CefBrowserViewDelegate::ChromeToolbarType ViewsWindow::GetChromeToolbarType() {
|
|
return chrome_toolbar_type_;
|
|
}
|
|
|
|
void ViewsWindow::OnButtonPressed(CefRefPtr<CefButton> button) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(with_controls_);
|
|
|
|
if (!browser_view_) {
|
|
return;
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (!browser) {
|
|
return;
|
|
}
|
|
|
|
switch (button->GetID()) {
|
|
case ID_BACK_BUTTON:
|
|
browser->GoBack();
|
|
break;
|
|
case ID_FORWARD_BUTTON:
|
|
browser->GoForward();
|
|
break;
|
|
case ID_STOP_BUTTON:
|
|
browser->StopLoad();
|
|
break;
|
|
case ID_RELOAD_BUTTON:
|
|
browser->Reload();
|
|
break;
|
|
case ID_MENU_BUTTON:
|
|
break;
|
|
default:
|
|
NOTREACHED();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnMenuButtonPressed(
|
|
CefRefPtr<CefMenuButton> menu_button,
|
|
const CefPoint& screen_point,
|
|
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
const int id = menu_button->GetID();
|
|
if (id >= ID_EXTENSION_BUTTON_FIRST && id <= ID_EXTENSION_BUTTON_LAST) {
|
|
const size_t extension_idx = id - ID_EXTENSION_BUTTON_FIRST;
|
|
if (extension_idx >= extensions_.size()) {
|
|
LOG(ERROR) << "Invalid extension index " << extension_idx;
|
|
return;
|
|
}
|
|
|
|
// Keep the button pressed until the extension window is closed.
|
|
extension_button_pressed_lock_ = button_pressed_lock;
|
|
|
|
// Create a window for the extension.
|
|
delegate_->CreateExtensionWindow(
|
|
extensions_[extension_idx].extension_, menu_button->GetBoundsInScreen(),
|
|
window_, base::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
|
|
return;
|
|
}
|
|
|
|
DCHECK(with_controls_ || with_overlay_controls_);
|
|
DCHECK_EQ(ID_MENU_BUTTON, id);
|
|
|
|
auto point = screen_point;
|
|
if (with_overlay_controls_) {
|
|
// Align the menu correctly under the button.
|
|
const int button_width = menu_button->GetSize().width;
|
|
if (CefIsRTL()) {
|
|
point.x += button_width - 4;
|
|
} else {
|
|
point.x -= button_width - 4;
|
|
}
|
|
}
|
|
|
|
menu_button->ShowMenu(button_menu_model_, point,
|
|
with_overlay_controls_ ? CEF_MENU_ANCHOR_TOPLEFT
|
|
: CEF_MENU_ANCHOR_TOPRIGHT);
|
|
}
|
|
|
|
void ViewsWindow::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
|
|
int command_id,
|
|
cef_event_flags_t event_flags) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(with_controls_ || with_overlay_controls_);
|
|
|
|
if (command_id == ID_QUIT) {
|
|
delegate_->OnExit();
|
|
} else if (command_id >= ID_TESTS_FIRST && command_id <= ID_TESTS_LAST) {
|
|
delegate_->OnTest(command_id);
|
|
} else {
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
|
|
bool ViewsWindow::OnKeyEvent(CefRefPtr<CefTextfield> textfield,
|
|
const CefKeyEvent& event) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK_EQ(ID_URL_TEXTFIELD, textfield->GetID());
|
|
|
|
// Trigger when the return key is pressed.
|
|
if (window_ && browser_view_ && event.type == KEYEVENT_RAWKEYDOWN &&
|
|
event.windows_key_code == VK_RETURN) {
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (browser) {
|
|
const CefString& url = textfield->GetText();
|
|
if (!url.empty()) {
|
|
browser->GetMainFrame()->LoadURL(url);
|
|
}
|
|
}
|
|
|
|
// We handled the event.
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(browser_view_);
|
|
DCHECK(!window_);
|
|
DCHECK(window);
|
|
|
|
window_ = window;
|
|
window_->SetID(ID_WINDOW);
|
|
|
|
delegate_->OnViewsWindowCreated(this);
|
|
|
|
if (type_ == WindowType::NORMAL) {
|
|
const CefRect bounds = delegate_->GetInitialBounds();
|
|
if (bounds.IsEmpty()) {
|
|
// Size the Window and center it at the default size.
|
|
window_->CenterWindow(CefSize(kDefaultWidth, kDefaultHeight));
|
|
} else {
|
|
// Remember the bounds from the previous application run in case the user
|
|
// does not move or resize the window during this application run.
|
|
last_visible_bounds_ = bounds;
|
|
}
|
|
}
|
|
|
|
// Set the background color for regions that are not obscured by other Views.
|
|
views_style::ApplyTo(window_.get());
|
|
|
|
if (with_controls_ || with_overlay_controls_) {
|
|
// Create the MenuModel that will be displayed via the menu button.
|
|
CreateMenuModel();
|
|
}
|
|
|
|
if (with_controls_) {
|
|
// Add the BrowserView and other controls to the Window.
|
|
AddBrowserView();
|
|
|
|
// Add keyboard accelerators to the Window.
|
|
AddAccelerators();
|
|
|
|
// Hide the top controls while in full-screen mode.
|
|
if (delegate_->GetInitialShowState() == CEF_SHOW_STATE_FULLSCREEN) {
|
|
ShowTopControls(false);
|
|
}
|
|
} else {
|
|
// Add the BrowserView as the only child of the Window.
|
|
window_->AddChildView(browser_view_);
|
|
|
|
if (type_ != WindowType::EXTENSION) {
|
|
// Choose a reasonable minimum window size.
|
|
minimum_window_size_ = CefSize(100, 100);
|
|
}
|
|
}
|
|
|
|
if (!delegate_->InitiallyHidden()) {
|
|
// Show the Window.
|
|
Show();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnWindowClosing(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(window_);
|
|
|
|
delegate_->OnViewsWindowClosing(this);
|
|
}
|
|
|
|
void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(window_);
|
|
|
|
delegate_->OnViewsWindowDestroyed(this);
|
|
|
|
browser_view_ = nullptr;
|
|
button_menu_model_ = nullptr;
|
|
if (top_menu_bar_) {
|
|
top_menu_bar_->Reset();
|
|
top_menu_bar_ = nullptr;
|
|
}
|
|
extensions_panel_ = nullptr;
|
|
menu_button_ = nullptr;
|
|
window_ = nullptr;
|
|
}
|
|
|
|
void ViewsWindow::OnWindowActivationChanged(CefRefPtr<CefWindow> window,
|
|
bool active) {
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
delegate_->OnViewsWindowActivated(this);
|
|
}
|
|
|
|
void ViewsWindow::OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
|
|
const CefRect& new_bounds) {
|
|
if (type_ == WindowType::NORMAL && !window->IsMinimized() &&
|
|
!window->IsMaximized() && !window->IsFullscreen()) {
|
|
// Track the last visible bounds for window restore purposes.
|
|
last_visible_bounds_ = new_bounds;
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_ && top_toolbar_) {
|
|
auto insets = top_toolbar_->GetInsets();
|
|
insets.left = window->IsFullscreen() ? 0 : kWindowButtonsWidth;
|
|
top_toolbar_->SetInsets(insets);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Allow the window to close if the browser says it's OK.
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (browser) {
|
|
return browser->GetHost()->TryCloseBrowser();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window,
|
|
bool* is_menu,
|
|
bool* can_activate_menu) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
CefRefPtr<CefWindow> parent_window = delegate_->GetParentWindow();
|
|
if (parent_window) {
|
|
// Extension windows behave as a menu and allow activation.
|
|
if (type_ == WindowType::EXTENSION) {
|
|
*is_menu = true;
|
|
*can_activate_menu = true;
|
|
}
|
|
}
|
|
return parent_window;
|
|
}
|
|
|
|
bool ViewsWindow::IsWindowModalDialog(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(delegate_->GetParentWindow());
|
|
return use_window_modal_dialog_;
|
|
}
|
|
|
|
CefRect ViewsWindow::GetInitialBounds(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
const CefRect bounds = delegate_->GetInitialBounds();
|
|
if (frameless_ && bounds.IsEmpty()) {
|
|
// Need to provide a size for frameless windows that will be centered.
|
|
return CefRect(0, 0, kDefaultWidth, kDefaultHeight);
|
|
}
|
|
return bounds;
|
|
}
|
|
|
|
cef_show_state_t ViewsWindow::GetInitialShowState(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return delegate_->GetInitialShowState();
|
|
}
|
|
|
|
bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return frameless_;
|
|
}
|
|
|
|
bool ViewsWindow::WithStandardWindowButtons(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return with_standard_buttons_;
|
|
}
|
|
|
|
bool ViewsWindow::GetTitlebarHeight(CefRefPtr<CefWindow> window,
|
|
float* titlebar_height) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
#if defined(OS_MAC)
|
|
if (override_titlebar_height_.has_value()) {
|
|
*titlebar_height = override_titlebar_height_.value();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// Only allow resize of normal windows.
|
|
return type_ == WindowType::NORMAL;
|
|
}
|
|
|
|
bool ViewsWindow::CanMaximize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// Only allow maximize of normal windows.
|
|
return type_ == WindowType::NORMAL;
|
|
}
|
|
|
|
bool ViewsWindow::CanMinimize(CefRefPtr<CefWindow> window) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// Only allow minimize of normal windows.
|
|
return type_ == WindowType::NORMAL;
|
|
}
|
|
|
|
bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (command_id == ID_QUIT) {
|
|
delegate_->OnExit();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewsWindow::OnKeyEvent(CefRefPtr<CefWindow> window,
|
|
const CefKeyEvent& event) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_) {
|
|
return false;
|
|
}
|
|
|
|
if (type_ == WindowType::EXTENSION && event.type == KEYEVENT_RAWKEYDOWN &&
|
|
event.windows_key_code == VK_ESCAPE) {
|
|
// Close the extension window on escape.
|
|
Close(false);
|
|
return true;
|
|
}
|
|
|
|
if (!with_controls_) {
|
|
return false;
|
|
}
|
|
|
|
if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) {
|
|
// ALT key is pressed.
|
|
int last_focused_view = last_focused_view_;
|
|
bool menu_had_focus = menu_has_focus_;
|
|
|
|
// Toggle menu button focusable.
|
|
SetMenuFocusable(!menu_has_focus_);
|
|
|
|
if (menu_had_focus && last_focused_view != 0) {
|
|
// Restore focus to the view that was previously focused.
|
|
window_->GetViewForID(last_focused_view)->RequestFocus();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (menu_has_focus_ && top_menu_bar_) {
|
|
return top_menu_bar_->OnKeyEvent(event);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CefSize ViewsWindow::GetPreferredSize(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (view->GetID() == ID_WINDOW && type_ == WindowType::DIALOG) {
|
|
// Preferred size for a browser modal dialog. The dialog will be shrunk to
|
|
// fit inside the parent browser view if necessary.
|
|
return CefSize(kDefaultWidth, kDefaultHeight);
|
|
}
|
|
|
|
return CefSize();
|
|
}
|
|
|
|
CefSize ViewsWindow::GetMinimumSize(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (view->GetID() == ID_WINDOW) {
|
|
return minimum_window_size_;
|
|
}
|
|
|
|
return CefSize();
|
|
}
|
|
|
|
void ViewsWindow::OnFocus(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
const int view_id = view->GetID();
|
|
|
|
// Keep track of the non-menu view that was last focused.
|
|
if (last_focused_view_ != view_id &&
|
|
(!top_menu_bar_ || !top_menu_bar_->HasMenuId(view_id))) {
|
|
last_focused_view_ = view_id;
|
|
}
|
|
|
|
// When focus leaves the menu buttons make them unfocusable.
|
|
if (menu_has_focus_) {
|
|
if (top_menu_bar_) {
|
|
if (!top_menu_bar_->HasMenuId(view_id)) {
|
|
SetMenuFocusable(false);
|
|
}
|
|
} else if (view_id != ID_MENU_BUTTON) {
|
|
SetMenuFocusable(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnBlur(CefRefPtr<CefView> view) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
const int view_id = view->GetID();
|
|
if (view_id == ID_BROWSER_VIEW && type_ == WindowType::EXTENSION) {
|
|
// Close windows hosting extensions when the browser loses focus.
|
|
Close(false);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
|
|
const int view_id = view->GetID();
|
|
if (view_id != ID_BROWSER_VIEW) {
|
|
return;
|
|
}
|
|
|
|
if (added) {
|
|
if (with_controls_) {
|
|
AddControls();
|
|
}
|
|
|
|
if (with_overlay_controls_) {
|
|
// Add window buttons if we don't have standard ones
|
|
const bool with_window_buttons = !with_standard_buttons_;
|
|
overlay_controls_ = new ViewsOverlayControls(with_window_buttons);
|
|
overlay_controls_->Initialize(window_, CreateMenuButton(),
|
|
CreateLocationBar(),
|
|
chrome_toolbar_type_ != CEF_CTT_NONE);
|
|
}
|
|
} else {
|
|
if (overlay_controls_) {
|
|
// Overlay controls may include the Chrome toolbar, in which case they
|
|
// need to be removed before the BrowserView.
|
|
overlay_controls_->Destroy();
|
|
overlay_controls_ = nullptr;
|
|
location_bar_ = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnLayoutChanged(CefRefPtr<CefView> view,
|
|
const CefRect& new_bounds) {
|
|
const int view_id = view->GetID();
|
|
if (view_id != ID_BROWSER_VIEW) {
|
|
return;
|
|
}
|
|
|
|
if (overlay_controls_) {
|
|
overlay_controls_->UpdateControls();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
|
|
int command_id,
|
|
cef_event_flags_t event_flags) {
|
|
ExecuteCommand(menu_model, command_id, event_flags);
|
|
}
|
|
|
|
ViewsWindow::ViewsWindow(WindowType type,
|
|
Delegate* delegate,
|
|
CefRefPtr<CefBrowserView> browser_view)
|
|
: type_(type),
|
|
delegate_(delegate),
|
|
menu_has_focus_(false),
|
|
last_focused_view_(false) {
|
|
DCHECK(delegate_);
|
|
if (browser_view) {
|
|
SetBrowserView(browser_view);
|
|
}
|
|
|
|
CefRefPtr<CefCommandLine> command_line =
|
|
CefCommandLine::GetGlobalCommandLine();
|
|
|
|
const bool is_normal_type = type_ == WindowType::NORMAL;
|
|
|
|
with_controls_ = is_normal_type && delegate_->WithControls();
|
|
|
|
const bool hide_frame = command_line->HasSwitch(switches::kHideFrame);
|
|
const bool hide_overlays =
|
|
!is_normal_type || command_line->HasSwitch(switches::kHideOverlays);
|
|
const bool hide_toolbar = hide_overlays && !with_controls_;
|
|
const bool show_window_buttons =
|
|
command_line->HasSwitch(switches::kShowWindowButtons);
|
|
|
|
// Without a window frame.
|
|
frameless_ = hide_frame || type_ == WindowType::EXTENSION;
|
|
|
|
// With an overlay that mimics window controls.
|
|
with_overlay_controls_ = hide_frame && !hide_overlays && !with_controls_;
|
|
|
|
// If window has frame or flag passed explicitly
|
|
with_standard_buttons_ = !frameless_ || show_window_buttons;
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_) {
|
|
default_titlebar_height_ = kTitleBarHeight;
|
|
override_titlebar_height_ = kTitleBarHeight;
|
|
}
|
|
#endif
|
|
|
|
const std::string& toolbar_type =
|
|
command_line->GetSwitchValue(switches::kShowChromeToolbar);
|
|
chrome_toolbar_type_ = CalculateChromeToolbarType(toolbar_type, hide_toolbar,
|
|
with_overlay_controls_);
|
|
|
|
#if !defined(OS_MAC)
|
|
// On Mac we don't show a top menu on the window. The options are available in
|
|
// the app menu instead.
|
|
if (!command_line->HasSwitch(switches::kHideTopMenu)) {
|
|
top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST);
|
|
}
|
|
#endif
|
|
|
|
use_window_modal_dialog_ =
|
|
command_line->HasSwitch(switches::kUseWindowModalDialog);
|
|
}
|
|
|
|
void ViewsWindow::SetBrowserView(CefRefPtr<CefBrowserView> browser_view) {
|
|
DCHECK(!browser_view_);
|
|
DCHECK(browser_view);
|
|
DCHECK(browser_view->IsValid());
|
|
DCHECK(!browser_view->IsAttached());
|
|
browser_view_ = browser_view;
|
|
browser_view_->SetID(ID_BROWSER_VIEW);
|
|
}
|
|
|
|
void ViewsWindow::CreateMenuModel() {
|
|
// Create the menu button model.
|
|
button_menu_model_ = CefMenuModel::CreateMenuModel(this);
|
|
CefRefPtr<CefMenuModel> test_menu =
|
|
button_menu_model_->AddSubMenu(0, "&Tests");
|
|
views_style::ApplyTo(button_menu_model_);
|
|
AddTestMenuItems(test_menu);
|
|
AddFileMenuItems(button_menu_model_);
|
|
|
|
if (top_menu_bar_) {
|
|
// Add the menus to the top menu bar.
|
|
AddFileMenuItems(top_menu_bar_->CreateMenuModel("&File", nullptr));
|
|
AddTestMenuItems(top_menu_bar_->CreateMenuModel("&Tests", nullptr));
|
|
}
|
|
}
|
|
|
|
CefRefPtr<CefLabelButton> ViewsWindow::CreateBrowseButton(
|
|
const std::string& label,
|
|
int id) {
|
|
CefRefPtr<CefLabelButton> button =
|
|
CefLabelButton::CreateLabelButton(this, label);
|
|
button->SetID(id);
|
|
views_style::ApplyTo(button.get());
|
|
button->SetInkDropEnabled(true);
|
|
button->SetEnabled(false); // Disabled by default.
|
|
button->SetFocusable(false); // Don't give focus to the button.
|
|
|
|
return button;
|
|
}
|
|
|
|
CefRefPtr<CefMenuButton> ViewsWindow::CreateMenuButton() {
|
|
// Create the menu button.
|
|
DCHECK(!menu_button_);
|
|
menu_button_ = CefMenuButton::CreateMenuButton(this, CefString());
|
|
menu_button_->SetID(ID_MENU_BUTTON);
|
|
menu_button_->SetImage(
|
|
CEF_BUTTON_STATE_NORMAL,
|
|
delegate_->GetImageCache()->GetCachedImage("menu_icon"));
|
|
views_style::ApplyTo(menu_button_.get());
|
|
menu_button_->SetInkDropEnabled(true);
|
|
// Override the default minimum size.
|
|
menu_button_->SetMinimumSize(CefSize(0, 0));
|
|
return menu_button_;
|
|
}
|
|
|
|
CefRefPtr<CefView> ViewsWindow::CreateLocationBar() {
|
|
DCHECK(!location_bar_);
|
|
if (chrome_toolbar_type_ == CEF_CTT_LOCATION) {
|
|
// Chrome will provide a minimal location bar.
|
|
location_bar_ = browser_view_->GetChromeToolbar();
|
|
DCHECK(location_bar_);
|
|
views_style::ApplyBackgroundTo(location_bar_);
|
|
}
|
|
if (!location_bar_) {
|
|
// Create the URL textfield.
|
|
CefRefPtr<CefTextfield> url_textfield = CefTextfield::CreateTextfield(this);
|
|
url_textfield->SetID(ID_URL_TEXTFIELD);
|
|
url_textfield->SetEnabled(false); // Disabled by default.
|
|
views_style::ApplyTo(url_textfield);
|
|
location_bar_ = url_textfield;
|
|
}
|
|
return location_bar_;
|
|
}
|
|
|
|
void ViewsWindow::AddBrowserView() {
|
|
// Use a vertical box layout for |window|.
|
|
CefBoxLayoutSettings window_layout_settings;
|
|
window_layout_settings.horizontal = false;
|
|
window_layout_settings.between_child_spacing = 2;
|
|
CefRefPtr<CefBoxLayout> window_layout =
|
|
window_->SetToBoxLayout(window_layout_settings);
|
|
|
|
window_->AddChildView(browser_view_);
|
|
|
|
// Allow |browser_view_| to grow and fill any remaining space.
|
|
window_layout->SetFlexForView(browser_view_, 1);
|
|
|
|
// Remaining setup will be performed in OnWindowChanged after the BrowserView
|
|
// is added to the CefWindow. This is necessary because Chrome toolbars are
|
|
// only available after the BrowserView is added.
|
|
}
|
|
|
|
void ViewsWindow::AddControls() {
|
|
// Build the remainder of the UI now that the BrowserView has been added to
|
|
// the CefWindow. This is a requirement to use Chrome toolbars.
|
|
|
|
CefRefPtr<CefPanel> top_menu_panel;
|
|
if (top_menu_bar_) {
|
|
top_menu_panel = top_menu_bar_->GetMenuPanel();
|
|
}
|
|
|
|
LabelButtons browse_buttons;
|
|
|
|
if (chrome_toolbar_type_ == CEF_CTT_NORMAL) {
|
|
// Chrome will provide a normal toolbar with location, menu, etc.
|
|
top_toolbar_ = browser_view_->GetChromeToolbar();
|
|
DCHECK(top_toolbar_);
|
|
}
|
|
|
|
if (!top_toolbar_) {
|
|
// Create the browse buttons.
|
|
browse_buttons.push_back(CreateBrowseButton("Back", ID_BACK_BUTTON));
|
|
browse_buttons.push_back(CreateBrowseButton("Forward", ID_FORWARD_BUTTON));
|
|
browse_buttons.push_back(CreateBrowseButton("Reload", ID_RELOAD_BUTTON));
|
|
browse_buttons.push_back(CreateBrowseButton("Stop", ID_STOP_BUTTON));
|
|
|
|
CreateLocationBar();
|
|
CreateMenuButton();
|
|
|
|
// Create the top panel.
|
|
CefRefPtr<CefPanel> top_panel = CefPanel::CreatePanel(nullptr);
|
|
|
|
// Use a horizontal box layout for |top_panel|.
|
|
CefBoxLayoutSettings top_panel_layout_settings;
|
|
top_panel_layout_settings.horizontal = true;
|
|
CefRefPtr<CefBoxLayout> top_panel_layout =
|
|
top_panel->SetToBoxLayout(top_panel_layout_settings);
|
|
|
|
// Add the buttons and URL textfield to |top_panel|.
|
|
for (auto& browse_button : browse_buttons) {
|
|
top_panel->AddChildView(browse_button);
|
|
}
|
|
top_panel->AddChildView(location_bar_);
|
|
|
|
UpdateExtensionControls();
|
|
DCHECK(extensions_panel_);
|
|
top_panel->AddChildView(extensions_panel_);
|
|
|
|
top_panel->AddChildView(menu_button_);
|
|
views_style::ApplyTo(top_panel);
|
|
|
|
// Allow |location| to grow and fill any remaining space.
|
|
top_panel_layout->SetFlexForView(location_bar_, 1);
|
|
|
|
top_toolbar_ = top_panel;
|
|
}
|
|
|
|
#if defined(OS_MAC)
|
|
if (frameless_ && with_standard_buttons_) {
|
|
auto insets = top_toolbar_->GetInsets();
|
|
insets.left = kWindowButtonsWidth;
|
|
top_toolbar_->SetInsets(insets);
|
|
}
|
|
#endif
|
|
|
|
// Add the top panel and browser view to |window|.
|
|
int top_index = 0;
|
|
if (top_menu_panel) {
|
|
window_->AddChildViewAt(top_menu_panel, top_index++);
|
|
}
|
|
window_->AddChildViewAt(top_toolbar_, top_index);
|
|
|
|
// Lay out |window| so we can get the default button sizes.
|
|
window_->Layout();
|
|
|
|
int min_width = 200;
|
|
if (!browse_buttons.empty()) {
|
|
// Make all browse buttons the same size.
|
|
MakeButtonsSameSize(browse_buttons);
|
|
|
|
// Lay out |window| again with the new button sizes.
|
|
window_->Layout();
|
|
|
|
const int buttons_number = static_cast<int>(browse_buttons.size());
|
|
|
|
// Minimum window width is the size of all buttons plus some extra.
|
|
min_width = browse_buttons[0]->GetBounds().width * buttons_number +
|
|
menu_button_->GetBounds().width + 100;
|
|
}
|
|
|
|
// Minimum window height is the hight of the top toolbar plus some extra.
|
|
int min_height = top_toolbar_->GetBounds().height + 100;
|
|
if (top_menu_panel) {
|
|
min_height += top_menu_panel->GetBounds().height;
|
|
}
|
|
|
|
minimum_window_size_ = CefSize(min_width, min_height);
|
|
}
|
|
|
|
void ViewsWindow::AddAccelerators() {
|
|
// Trigger accelerators without first forwarding to web content.
|
|
browser_view_->SetPreferAccelerators(true);
|
|
|
|
// Specify the accelerators to handle. OnAccelerator will be called when the
|
|
// accelerator is triggered.
|
|
window_->SetAccelerator(ID_QUIT, 'X', false, false, true);
|
|
}
|
|
|
|
void ViewsWindow::SetMenuFocusable(bool focusable) {
|
|
if (!window_ || !with_controls_) {
|
|
return;
|
|
}
|
|
|
|
if (top_menu_bar_) {
|
|
top_menu_bar_->SetMenuFocusable(focusable);
|
|
} else {
|
|
window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable);
|
|
|
|
if (focusable) {
|
|
// Give focus to menu button.
|
|
window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus();
|
|
}
|
|
}
|
|
|
|
menu_has_focus_ = focusable;
|
|
}
|
|
|
|
void ViewsWindow::EnableView(int id, bool enable) {
|
|
if (!window_) {
|
|
return;
|
|
}
|
|
// Special handling for |location_bar_| which may be an overlay (e.g. not a
|
|
// child of this view).
|
|
CefRefPtr<CefView> view =
|
|
id == ID_URL_TEXTFIELD ? location_bar_ : window_->GetViewForID(id);
|
|
if (view) {
|
|
view->SetEnabled(enable);
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::ShowTopControls(bool show) {
|
|
if (!window_ || !with_controls_) {
|
|
return;
|
|
}
|
|
|
|
// Change the visibility of the top toolbar.
|
|
if (top_toolbar_->IsVisible() != show) {
|
|
top_toolbar_->SetVisible(show);
|
|
top_toolbar_->InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::UpdateExtensionControls() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!window_ || !with_controls_) {
|
|
return;
|
|
}
|
|
|
|
if (!extensions_panel_) {
|
|
extensions_panel_ = CefPanel::CreatePanel(nullptr);
|
|
|
|
// Use a horizontal box layout for |top_panel|.
|
|
CefBoxLayoutSettings top_panel_layout_settings;
|
|
top_panel_layout_settings.horizontal = true;
|
|
CefRefPtr<CefBoxLayout> top_panel_layout =
|
|
extensions_panel_->SetToBoxLayout(top_panel_layout_settings);
|
|
} else {
|
|
extensions_panel_->RemoveAllChildViews();
|
|
}
|
|
|
|
if (extensions_.size() >
|
|
ID_EXTENSION_BUTTON_LAST - ID_EXTENSION_BUTTON_FIRST) {
|
|
LOG(WARNING) << "Too many extensions loaded. Some will be ignored.";
|
|
}
|
|
|
|
ExtensionInfoSet::const_iterator it = extensions_.begin();
|
|
for (int id = ID_EXTENSION_BUTTON_FIRST;
|
|
it != extensions_.end() && id <= ID_EXTENSION_BUTTON_LAST; ++id, ++it) {
|
|
CefRefPtr<CefMenuButton> button =
|
|
CefMenuButton::CreateMenuButton(this, CefString());
|
|
button->SetID(id);
|
|
button->SetImage(CEF_BUTTON_STATE_NORMAL, (*it).image_);
|
|
views_style::ApplyTo(button.get());
|
|
button->SetInkDropEnabled(true);
|
|
// Override the default minimum size.
|
|
button->SetMinimumSize(CefSize(0, 0));
|
|
|
|
extensions_panel_->AddChildView(button);
|
|
}
|
|
|
|
CefRefPtr<CefView> parent_view = extensions_panel_->GetParentView();
|
|
if (parent_view) {
|
|
parent_view->InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
void ViewsWindow::OnExtensionIconsLoaded(const ExtensionSet& extensions,
|
|
const ImageCache::ImageSet& images) {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
// Execute this method on the UI thread.
|
|
CefPostTask(TID_UI, base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded,
|
|
this, extensions, images));
|
|
return;
|
|
}
|
|
|
|
DCHECK_EQ(extensions.size(), images.size());
|
|
|
|
extensions_.clear();
|
|
|
|
ExtensionSet::const_iterator it1 = extensions.begin();
|
|
ImageCache::ImageSet::const_iterator it2 = images.begin();
|
|
for (; it1 != extensions.end() && it2 != images.end(); ++it1, ++it2) {
|
|
CefRefPtr<CefImage> icon = *it2;
|
|
if (!icon) {
|
|
icon = delegate_->GetImageCache()->GetCachedImage(kDefaultExtensionIcon);
|
|
}
|
|
extensions_.push_back(ExtensionInfo(*it1, icon));
|
|
}
|
|
|
|
UpdateExtensionControls();
|
|
}
|
|
|
|
void ViewsWindow::OnExtensionWindowClosed() {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
// Execute this method on the UI thread.
|
|
CefPostTask(TID_UI,
|
|
base::BindOnce(&ViewsWindow::OnExtensionWindowClosed, this));
|
|
return;
|
|
}
|
|
|
|
// Restore the button state.
|
|
extension_button_pressed_lock_ = nullptr;
|
|
}
|
|
|
|
#if !defined(OS_MAC)
|
|
void ViewsWindow::NudgeWindow() {
|
|
NOTIMPLEMENTED();
|
|
}
|
|
#endif
|
|
|
|
} // namespace client
|