diff --git a/tests/cefclient/browser/views_menu_bar.cc b/tests/cefclient/browser/views_menu_bar.cc index e2363993c..4640c5cd4 100644 --- a/tests/cefclient/browser/views_menu_bar.cc +++ b/tests/cefclient/browser/views_menu_bar.cc @@ -44,9 +44,12 @@ char16_t GetMnemonic(const std::u16string& title) { } // namespace -ViewsMenuBar::ViewsMenuBar(Delegate* delegate, int menu_id_start) +ViewsMenuBar::ViewsMenuBar(Delegate* delegate, + int menu_id_start, + bool use_bottom_controls) : delegate_(delegate), id_start_(menu_id_start), + use_bottom_controls_(use_bottom_controls), id_next_(menu_id_start), last_nav_with_keyboard_(false) { DCHECK(delegate_); @@ -164,12 +167,28 @@ void ViewsMenuBar::OnMenuButtonPressed( CefRefPtr button_pressed_lock) { CefRefPtr menu_model = GetMenuModel(menu_button->GetID()); + const auto button_bounds = menu_button->GetBoundsInScreen(); + // Adjust menu position to align with the button. CefPoint point = screen_point; if (CefIsRTL()) { - point.x += menu_button->GetBounds().width - 4; + point.x += button_bounds.width - 4; } else { - point.x -= menu_button->GetBounds().width - 4; + point.x -= button_bounds.width - 4; + } + + if (use_bottom_controls_) { + const auto display_bounds = + menu_button->GetWindow()->GetDisplay()->GetWorkArea(); + const int available_height = display_bounds.y + display_bounds.height - + button_bounds.y - button_bounds.height; + + // Approximation of the menu height. + const int menu_height = menu_model->GetCount() * button_bounds.height; + if (menu_height > available_height) { + // The menu will go upwards, so place it above the button. + point.y -= button_bounds.height - 8; + } } // Keep track of the current |last_nav_with_keyboard_| status and restore it @@ -230,6 +249,10 @@ void ViewsMenuBar::MouseOutsideMenu(CefRefPtr menu_model, CefRefPtr button = panel_->GetViewForID(id); CefRect button_bounds = button->GetBounds(); + + // Adjust for window coordinates. + button_bounds.y += panel_bounds.y; + if (CefIsRTL()) { // Adjust for right-to-left button layout. button_bounds.x = diff --git a/tests/cefclient/browser/views_menu_bar.h b/tests/cefclient/browser/views_menu_bar.h index 736fb9ac0..e1f6ec645 100644 --- a/tests/cefclient/browser/views_menu_bar.h +++ b/tests/cefclient/browser/views_menu_bar.h @@ -39,7 +39,7 @@ class ViewsMenuBar : public CefMenuButtonDelegate, public CefMenuModelDelegate { // |menu_id_start| is the ID for the first CefMenuButton in the bar. An ID // range starting with |menu_id_start| and extending for a reasonable distance // should be reserved in the client for MenuBar usage. - ViewsMenuBar(Delegate* delegate, int menu_id_start); + ViewsMenuBar(Delegate* delegate, int menu_id_start, bool use_bottom_controls); // Returns true if |menu_id| exists in the menu bar. bool HasMenuId(int menu_id) const; @@ -106,6 +106,7 @@ class ViewsMenuBar : public CefMenuButtonDelegate, public CefMenuModelDelegate { Delegate* delegate_; // Not owned by this object. const int id_start_; + const bool use_bottom_controls_; int id_next_; CefRefPtr panel_; std::vector> models_; diff --git a/tests/cefclient/browser/views_overlay_controls.cc b/tests/cefclient/browser/views_overlay_controls.cc index d84b19cdf..582f0e12a 100644 --- a/tests/cefclient/browser/views_overlay_controls.cc +++ b/tests/cefclient/browser/views_overlay_controls.cc @@ -47,25 +47,31 @@ std::array GetButtons() { #endif } -cef_docking_mode_t GetPanelDockingMode() { +cef_docking_mode_t GetPanelDockingMode(bool use_bottom_controls) { #if defined(OS_MAC) - return CEF_DOCKING_MODE_TOP_LEFT; + return use_bottom_controls ? CEF_DOCKING_MODE_BOTTOM_LEFT + : CEF_DOCKING_MODE_TOP_LEFT; #else - return CEF_DOCKING_MODE_TOP_RIGHT; + return use_bottom_controls ? CEF_DOCKING_MODE_BOTTOM_RIGHT + : CEF_DOCKING_MODE_TOP_RIGHT; #endif } -cef_docking_mode_t GetMenuDockingMode() { +cef_docking_mode_t GetMenuDockingMode(bool use_bottom_controls) { #if defined(OS_MAC) - return CEF_DOCKING_MODE_TOP_RIGHT; + return use_bottom_controls ? CEF_DOCKING_MODE_BOTTOM_RIGHT + : CEF_DOCKING_MODE_TOP_RIGHT; #else - return CEF_DOCKING_MODE_TOP_LEFT; + return use_bottom_controls ? CEF_DOCKING_MODE_BOTTOM_LEFT + : CEF_DOCKING_MODE_TOP_LEFT; #endif } } // namespace -ViewsOverlayControls::ViewsOverlayControls(bool with_window_buttons) - : with_window_buttons_(with_window_buttons) {} +ViewsOverlayControls::ViewsOverlayControls(bool with_window_buttons, + bool use_bottom_controls) + : with_window_buttons_(with_window_buttons), + use_bottom_controls_(use_bottom_controls) {} void ViewsOverlayControls::Initialize(CefRefPtr window, CefRefPtr menu_button, @@ -78,6 +84,13 @@ void ViewsOverlayControls::Initialize(CefRefPtr window, window_ = window; window_maximized_ = window_->IsMaximized(); + CefInsets insets; + if (use_bottom_controls_) { + insets.Set(0, kInsets, kInsets, kInsets); + } else { + insets.Set(kInsets, kInsets, 0, kInsets); + } + if (with_window_buttons_) { // Window control buttons. These controls are currently text which means // that we can't use a transparent background because subpixel text @@ -94,15 +107,17 @@ void ViewsOverlayControls::Initialize(CefRefPtr window, for (auto button : GetButtons()) { panel_->AddChildView(CreateButton(button)); } - panel_controller_ = window->AddOverlayView(panel_, GetPanelDockingMode()); - panel_controller_->SetInsets(CefInsets(kInsets, kInsets, 0, kInsets)); + panel_controller_ = window->AddOverlayView( + panel_, GetPanelDockingMode(use_bottom_controls_)); + panel_controller_->SetInsets(insets); panel_controller_->SetVisible(true); } // Menu button. menu_button->SetBackgroundColor(kBackgroundColor); - menu_controller_ = window_->AddOverlayView(menu_button, GetMenuDockingMode()); - menu_controller_->SetInsets(CefInsets(kInsets, kInsets, 0, kInsets)); + menu_controller_ = window_->AddOverlayView( + menu_button, GetMenuDockingMode(use_bottom_controls_)); + menu_controller_->SetInsets(insets); menu_controller_->SetVisible(true); // Location bar. Will be made visible in UpdateControls(). @@ -130,10 +145,11 @@ void ViewsOverlayControls::Destroy() { void ViewsOverlayControls::UpdateControls() { // Update location bar size, position and visibility. - auto bounds = window_->GetBounds(); + const auto window_bounds = window_->GetBounds(); + auto bounds = window_bounds; bounds.x = kLocationBarPadding; bounds.width -= kLocationBarPadding * 2; - bounds.y = kInsets; + if (is_chrome_toolbar_) { // Fit the standard Chrome toolbar. const auto preferred_size = location_bar_->GetPreferredSize(); @@ -142,6 +158,13 @@ void ViewsOverlayControls::UpdateControls() { } else { bounds.height = menu_controller_->GetSize().height; } + + if (use_bottom_controls_) { + bounds.y = window_bounds.height - bounds.height - kInsets; + } else { + bounds.y = kInsets; + } + if (bounds.width < kLocationBarPadding * 2) { // Not enough space. location_controller_->SetVisible(false); diff --git a/tests/cefclient/browser/views_overlay_controls.h b/tests/cefclient/browser/views_overlay_controls.h index 475fd90cd..0a794242b 100644 --- a/tests/cefclient/browser/views_overlay_controls.h +++ b/tests/cefclient/browser/views_overlay_controls.h @@ -25,7 +25,7 @@ class ViewsOverlayControls : public CefButtonDelegate { kClose, }; - explicit ViewsOverlayControls(bool with_window_buttons); + ViewsOverlayControls(bool with_window_buttons, bool use_bottom_controls); void Initialize(CefRefPtr window, CefRefPtr menu_button, @@ -54,6 +54,7 @@ class ViewsOverlayControls : public CefButtonDelegate { CefRefPtr panel_; CefRefPtr panel_controller_; const bool with_window_buttons_; + const bool use_bottom_controls_; // Location bar. CefRefPtr location_bar_; diff --git a/tests/cefclient/browser/views_window.cc b/tests/cefclient/browser/views_window.cc index 7b7df79e5..5faa96a1a 100644 --- a/tests/cefclient/browser/views_window.cc +++ b/tests/cefclient/browser/views_window.cc @@ -346,7 +346,7 @@ void ViewsWindow::TakeFocus(bool next) { } if (chrome_toolbar_type_ == CEF_CTT_NORMAL) { - top_toolbar_->RequestFocus(); + toolbar_->RequestFocus(); } else if (location_bar_) { // Give focus to the location bar. location_bar_->RequestFocus(); @@ -550,14 +550,30 @@ void ViewsWindow::OnMenuButtonPressed( DCHECK(with_controls_ || with_overlay_controls_); DCHECK_EQ(ID_MENU_BUTTON, id); + const auto button_bounds = menu_button->GetBoundsInScreen(); + 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; + point.x += button_bounds.width - 4; } else { - point.x -= button_width - 4; + point.x -= button_bounds.width - 4; + } + } + + if (use_bottom_controls_) { + const auto display_bounds = + menu_button->GetWindow()->GetDisplay()->GetWorkArea(); + const int available_height = display_bounds.y + display_bounds.height - + button_bounds.y - button_bounds.height; + + // Approximation of the menu height. + const int menu_height = + button_menu_model_->GetCount() * button_bounds.height; + if (menu_height > available_height) { + // The menu will go upwards, so place it above the button. + point.y -= button_bounds.height - 8; } } @@ -677,9 +693,9 @@ void ViewsWindow::OnWindowDestroyed(CefRefPtr window) { browser_view_ = nullptr; button_menu_model_ = nullptr; - if (top_menu_bar_) { - top_menu_bar_->Reset(); - top_menu_bar_ = nullptr; + if (menu_bar_) { + menu_bar_->Reset(); + menu_bar_ = nullptr; } extensions_panel_ = nullptr; menu_button_ = nullptr; @@ -704,10 +720,10 @@ void ViewsWindow::OnWindowBoundsChanged(CefRefPtr window, } #if defined(OS_MAC) - if (frameless_ && with_standard_buttons_ && top_toolbar_) { - auto insets = top_toolbar_->GetInsets(); + if (frameless_ && with_standard_buttons_ && toolbar_) { + auto insets = toolbar_->GetInsets(); insets.left = window->IsFullscreen() ? 0 : kWindowButtonsWidth; - top_toolbar_->SetInsets(insets); + toolbar_->SetInsets(insets); } #endif } @@ -846,8 +862,8 @@ bool ViewsWindow::OnKeyEvent(CefRefPtr window, return true; } - if (menu_has_focus_ && top_menu_bar_) { - return top_menu_bar_->OnKeyEvent(event); + if (menu_has_focus_ && menu_bar_) { + return menu_bar_->OnKeyEvent(event); } return false; @@ -882,14 +898,14 @@ void ViewsWindow::OnFocus(CefRefPtr view) { // 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))) { + (!menu_bar_ || !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)) { + if (menu_bar_) { + if (!menu_bar_->HasMenuId(view_id)) { SetMenuFocusable(false); } } else if (view_id != ID_MENU_BUTTON) { @@ -922,18 +938,25 @@ void ViewsWindow::OnWindowChanged(CefRefPtr view, bool added) { 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_ = + new ViewsOverlayControls(with_window_buttons, use_bottom_controls_); overlay_controls_->Initialize(window_, CreateMenuButton(), CreateLocationBar(), chrome_toolbar_type_ != CEF_CTT_NONE); } } else { + // Remove any controls that may include the Chrome toolbar before removing + // the BrowserView. 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; + } else if (use_bottom_controls_) { + if (toolbar_) { + window_->RemoveChildView(toolbar_); + toolbar_ = nullptr; + location_bar_ = nullptr; + } } } } @@ -1003,11 +1026,13 @@ ViewsWindow::ViewsWindow(WindowType type, chrome_toolbar_type_ = CalculateChromeToolbarType(toolbar_type, hide_toolbar, with_overlay_controls_); + use_bottom_controls_ = command_line->HasSwitch(switches::kUseBottomControls); + #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); + menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST, use_bottom_controls_); } #endif @@ -1033,10 +1058,10 @@ void ViewsWindow::CreateMenuModel() { AddTestMenuItems(test_menu); AddFileMenuItems(button_menu_model_); - if (top_menu_bar_) { + if (menu_bar_) { // Add the menus to the top menu bar. - AddFileMenuItems(top_menu_bar_->CreateMenuModel("&File", nullptr)); - AddTestMenuItems(top_menu_bar_->CreateMenuModel("&Tests", nullptr)); + AddFileMenuItems(menu_bar_->CreateMenuModel("&File", nullptr)); + AddTestMenuItems(menu_bar_->CreateMenuModel("&Tests", nullptr)); } } @@ -1110,20 +1135,20 @@ 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 top_menu_panel; - if (top_menu_bar_) { - top_menu_panel = top_menu_bar_->GetMenuPanel(); + CefRefPtr menu_panel; + if (menu_bar_) { + menu_panel = 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_); + toolbar_ = browser_view_->GetChromeToolbar(); + DCHECK(toolbar_); } - if (!top_toolbar_) { + if (!toolbar_) { // Create the browse buttons. browse_buttons.push_back(CreateBrowseButton("Back", ID_BACK_BUTTON)); browse_buttons.push_back(CreateBrowseButton("Forward", ID_FORWARD_BUTTON)); @@ -1133,48 +1158,56 @@ void ViewsWindow::AddControls() { CreateLocationBar(); CreateMenuButton(); - // Create the top panel. - CefRefPtr top_panel = CefPanel::CreatePanel(nullptr); + // Create the toolbar panel. + CefRefPtr panel = CefPanel::CreatePanel(nullptr); - // Use a horizontal box layout for |top_panel|. - CefBoxLayoutSettings top_panel_layout_settings; - top_panel_layout_settings.horizontal = true; - CefRefPtr top_panel_layout = - top_panel->SetToBoxLayout(top_panel_layout_settings); + // Use a horizontal box layout for |panel|. + CefBoxLayoutSettings panel_layout_settings; + panel_layout_settings.horizontal = true; + CefRefPtr panel_layout = + panel->SetToBoxLayout(panel_layout_settings); - // Add the buttons and URL textfield to |top_panel|. + // Add the buttons and URL textfield to |panel|. for (auto& browse_button : browse_buttons) { - top_panel->AddChildView(browse_button); + panel->AddChildView(browse_button); } - top_panel->AddChildView(location_bar_); + panel->AddChildView(location_bar_); UpdateExtensionControls(); DCHECK(extensions_panel_); - top_panel->AddChildView(extensions_panel_); + panel->AddChildView(extensions_panel_); - top_panel->AddChildView(menu_button_); - views_style::ApplyTo(top_panel); + panel->AddChildView(menu_button_); + views_style::ApplyTo(panel); // Allow |location| to grow and fill any remaining space. - top_panel_layout->SetFlexForView(location_bar_, 1); + panel_layout->SetFlexForView(location_bar_, 1); - top_toolbar_ = top_panel; + toolbar_ = panel; } #if defined(OS_MAC) if (frameless_ && with_standard_buttons_) { - auto insets = top_toolbar_->GetInsets(); + auto insets = toolbar_->GetInsets(); insets.left = kWindowButtonsWidth; - top_toolbar_->SetInsets(insets); + 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++); + if (use_bottom_controls_) { + // Add the panel at the bottom of |window|. + window_->AddChildView(toolbar_); + if (menu_panel) { + window_->AddChildView(menu_panel); + } + } else { + // Add the panel at the top of |window|. + int index = 0; + if (menu_panel) { + window_->AddChildViewAt(menu_panel, index++); + } + window_->AddChildViewAt(toolbar_, index); } - window_->AddChildViewAt(top_toolbar_, top_index); // Lay out |window| so we can get the default button sizes. window_->Layout(); @@ -1194,10 +1227,10 @@ void ViewsWindow::AddControls() { 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 height is the hight of the toolbar plus some extra. + int min_height = toolbar_->GetBounds().height + 100; + if (menu_panel) { + min_height += menu_panel->GetBounds().height; } minimum_window_size_ = CefSize(min_width, min_height); @@ -1217,8 +1250,8 @@ void ViewsWindow::SetMenuFocusable(bool focusable) { return; } - if (top_menu_bar_) { - top_menu_bar_->SetMenuFocusable(focusable); + if (menu_bar_) { + menu_bar_->SetMenuFocusable(focusable); } else { window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable); @@ -1249,10 +1282,10 @@ void ViewsWindow::ShowTopControls(bool show) { return; } - // Change the visibility of the top toolbar. - if (top_toolbar_->IsVisible() != show) { - top_toolbar_->SetVisible(show); - top_toolbar_->InvalidateLayout(); + // Change the visibility of the toolbar. + if (toolbar_->IsVisible() != show) { + toolbar_->SetVisible(show); + toolbar_->InvalidateLayout(); } } @@ -1266,11 +1299,11 @@ void ViewsWindow::UpdateExtensionControls() { 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 top_panel_layout = - extensions_panel_->SetToBoxLayout(top_panel_layout_settings); + // Use a horizontal box layout for |panel|. + CefBoxLayoutSettings panel_layout_settings; + panel_layout_settings.horizontal = true; + CefRefPtr panel_layout = + extensions_panel_->SetToBoxLayout(panel_layout_settings); } else { extensions_panel_->RemoveAllChildViews(); } diff --git a/tests/cefclient/browser/views_window.h b/tests/cefclient/browser/views_window.h index 311cc872c..317012bc1 100644 --- a/tests/cefclient/browser/views_window.h +++ b/tests/cefclient/browser/views_window.h @@ -253,11 +253,12 @@ class ViewsWindow : public CefBrowserViewDelegate, bool with_standard_buttons_; ChromeToolbarType chrome_toolbar_type_; bool use_window_modal_dialog_; + bool use_bottom_controls_; CefRefPtr window_; CefRefPtr button_menu_model_; - CefRefPtr top_menu_bar_; - CefRefPtr top_toolbar_; + CefRefPtr menu_bar_; + CefRefPtr toolbar_; CefRefPtr menu_button_; CefRefPtr location_bar_; bool menu_has_focus_; diff --git a/tests/shared/common/client_switches.cc b/tests/shared/common/client_switches.cc index 3753756c5..b07506a3d 100644 --- a/tests/shared/common/client_switches.cc +++ b/tests/shared/common/client_switches.cc @@ -55,6 +55,7 @@ const char kUseClientDialogs[] = "use-client-dialogs"; const char kUseTestHttpServer[] = "use-test-http-server"; const char kShowWindowButtons[] = "show-window-buttons"; const char kUseWindowModalDialog[] = "use-window-modal-dialog"; +const char kUseBottomControls[] = "use-bottom-controls"; } // namespace switches } // namespace client diff --git a/tests/shared/common/client_switches.h b/tests/shared/common/client_switches.h index cf1dc3127..a6d43c051 100644 --- a/tests/shared/common/client_switches.h +++ b/tests/shared/common/client_switches.h @@ -49,6 +49,7 @@ extern const char kUseClientDialogs[]; extern const char kUseTestHttpServer[]; extern const char kShowWindowButtons[]; extern const char kUseWindowModalDialog[]; +extern const char kUseBottomControls[]; } // namespace switches } // namespace client