| // Copyright (c) 2011 The Chromium 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 "chrome/browser/chromeos/frame/browser_view.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/chromeos/frame/panel_browser_view.h" |
| #include "chrome/browser/chromeos/status/input_method_menu_button.h" |
| #include "chrome/browser/chromeos/status/network_menu_button.h" |
| #include "chrome/browser/chromeos/status/status_area_button.h" |
| #include "chrome/browser/chromeos/status/status_area_view.h" |
| #include "chrome/browser/chromeos/view_ids.h" |
| #include "chrome/browser/chromeos/wm_ipc.h" |
| #include "chrome/browser/ui/gtk/gtk_util.h" |
| #include "chrome/browser/ui/views/frame/browser_frame_gtk.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/frame/browser_view_layout.h" |
| #include "chrome/browser/ui/views/tabs/tab.h" |
| #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| #include "chrome/browser/ui/views/theme_background.h" |
| #include "chrome/browser/ui/views/toolbar_view.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "grit/generated_resources.h" |
| #include "grit/theme_resources.h" |
| #include "third_party/cros/chromeos_wm_ipc_enums.h" |
| #include "ui/base/models/simple_menu_model.h" |
| #include "ui/base/theme_provider.h" |
| #include "ui/gfx/canvas.h" |
| #include "views/controls/button/button.h" |
| #include "views/controls/button/image_button.h" |
| #include "views/controls/menu/menu_2.h" |
| #include "views/screen.h" |
| #include "views/widget/root_view.h" |
| #include "views/window/hit_test.h" |
| #include "views/window/window.h" |
| |
| namespace { |
| |
| // Amount to offset the toolbar by when vertical tabs are enabled. |
| const int kVerticalTabStripToolbarOffset = 2; |
| // Amount to tweak the position of the status area to get it to look right. |
| const int kStatusAreaVerticalAdjustment = -1; |
| |
| // If a popup window is larger than this fraction of the screen, create a tab. |
| const float kPopupMaxWidthFactor = 0.5; |
| const float kPopupMaxHeightFactor = 0.6; |
| |
| } // namespace |
| |
| namespace chromeos { |
| |
| // LayoutManager for BrowserView, which layouts extra components such as |
| // the status views as follows: |
| // ____ __ __ |
| // / \ \ \ [StatusArea] |
| // |
| class BrowserViewLayout : public ::BrowserViewLayout { |
| public: |
| BrowserViewLayout() : ::BrowserViewLayout() {} |
| virtual ~BrowserViewLayout() {} |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // BrowserViewLayout overrides: |
| |
| void Installed(views::View* host) { |
| status_area_ = NULL; |
| ::BrowserViewLayout::Installed(host); |
| } |
| |
| void ViewAdded(views::View* host, |
| views::View* view) { |
| ::BrowserViewLayout::ViewAdded(host, view); |
| switch (view->GetID()) { |
| case VIEW_ID_STATUS_AREA: |
| status_area_ = static_cast<chromeos::StatusAreaView*>(view); |
| break; |
| } |
| } |
| |
| // In the normal and the compact navigation bar mode, ChromeOS |
| // layouts compact navigation buttons and status views in the title |
| // area. See Layout |
| virtual int LayoutTabStrip() { |
| if (browser_view_->IsFullscreen() || !browser_view_->IsTabStripVisible()) { |
| status_area_->SetVisible(false); |
| tabstrip_->SetVisible(false); |
| tabstrip_->SetBounds(0, 0, 0, 0); |
| return 0; |
| } |
| |
| gfx::Rect tabstrip_bounds( |
| browser_view_->frame()->GetBoundsForTabStrip(tabstrip_)); |
| gfx::Point tabstrip_origin = tabstrip_bounds.origin(); |
| views::View::ConvertPointToView(browser_view_->parent(), browser_view_, |
| &tabstrip_origin); |
| tabstrip_bounds.set_origin(tabstrip_origin); |
| return browser_view_->UseVerticalTabs() ? |
| LayoutTitlebarComponentsWithVerticalTabs(tabstrip_bounds) : |
| LayoutTitlebarComponents(tabstrip_bounds); |
| } |
| |
| virtual int LayoutToolbar(int top) { |
| if (!browser_view_->IsFullscreen() && browser_view_->IsTabStripVisible() && |
| browser_view_->UseVerticalTabs()) { |
| // For vertical tabs the toolbar is positioned in |
| // LayoutTitlebarComponentsWithVerticalTabs. |
| return top; |
| } |
| return ::BrowserViewLayout::LayoutToolbar(top); |
| } |
| |
| virtual bool IsPositionInWindowCaption(const gfx::Point& point) { |
| return ::BrowserViewLayout::IsPositionInWindowCaption(point) |
| && !IsPointInViewsInTitleArea(point); |
| } |
| |
| virtual int NonClientHitTest(const gfx::Point& point) { |
| gfx::Point point_in_browser_view_coords(point); |
| views::View::ConvertPointToView( |
| browser_view_->parent(), browser_view_, |
| &point_in_browser_view_coords); |
| return IsPointInViewsInTitleArea(point_in_browser_view_coords) ? |
| HTCLIENT : ::BrowserViewLayout::NonClientHitTest(point); |
| } |
| |
| private: |
| chromeos::BrowserView* chromeos_browser_view() { |
| return static_cast<chromeos::BrowserView*>(browser_view_); |
| } |
| |
| // Tests if the point is on one of views that are within the |
| // considered title bar area of client view. |
| bool IsPointInViewsInTitleArea(const gfx::Point& point) |
| const { |
| gfx::Point point_in_status_area_coords(point); |
| views::View::ConvertPointToView(browser_view_, status_area_, |
| &point_in_status_area_coords); |
| if (status_area_->HitTest(point_in_status_area_coords)) |
| return true; |
| |
| return false; |
| } |
| |
| // Positions the titlebar, toolbar and tabstrip. This is |
| // used when side tabs are enabled. |
| int LayoutTitlebarComponentsWithVerticalTabs(const gfx::Rect& bounds) { |
| if (bounds.IsEmpty()) |
| return 0; |
| |
| tabstrip_->SetVisible(true); |
| status_area_->SetVisible(true); |
| |
| gfx::Size status_size = status_area_->GetPreferredSize(); |
| int status_height = status_size.height(); |
| |
| int status_x = bounds.x(); |
| // Layout the status area. |
| status_area_->SetBounds(status_x, bounds.bottom() - status_height, |
| status_size.width(), status_height); |
| |
| // The tabstrip's width is the bigger of it's preferred width and the width |
| // the status area. |
| int tabstrip_w = std::max(status_x + status_size.width(), |
| tabstrip_->GetPreferredSize().width()); |
| tabstrip_->SetBounds(bounds.x(), bounds.y(), tabstrip_w, |
| bounds.height() - status_height); |
| |
| // The toolbar is promoted to the title for vertical tabs. |
| bool toolbar_visible = browser_view_->IsToolbarVisible(); |
| int toolbar_height = 0; |
| if (toolbar_) { |
| toolbar_->SetVisible(toolbar_visible); |
| if (toolbar_visible) |
| toolbar_height = toolbar_->GetPreferredSize().height(); |
| int tabstrip_max_x = tabstrip_->bounds().right(); |
| toolbar_->SetBounds(tabstrip_max_x, |
| bounds.y() - kVerticalTabStripToolbarOffset, |
| browser_view_->width() - tabstrip_max_x, |
| toolbar_height); |
| } |
| // Adjust the available bounds for other components. |
| gfx::Rect available_bounds = vertical_layout_rect(); |
| available_bounds.Inset(tabstrip_w, 0, 0, 0); |
| set_vertical_layout_rect(available_bounds); |
| return bounds.y() + toolbar_height; |
| } |
| |
| // Lays out tabstrip and status area in the title bar area (given by |
| // |bounds|). |
| int LayoutTitlebarComponents(const gfx::Rect& bounds) { |
| if (bounds.IsEmpty()) |
| return 0; |
| |
| tabstrip_->SetVisible(true); |
| status_area_->SetVisible(true); |
| |
| // Layout status area after tab strip. |
| gfx::Size status_size = status_area_->GetPreferredSize(); |
| status_area_->SetBounds( |
| bounds.right() - status_size.width(), |
| bounds.y() + kStatusAreaVerticalAdjustment, |
| status_size.width(), |
| status_size.height()); |
| tabstrip_->SetBounds(bounds.x(), bounds.y(), |
| std::max(0, status_area_->bounds().x() - bounds.x()), |
| bounds.height()); |
| return bounds.bottom(); |
| } |
| |
| chromeos::StatusAreaView* status_area_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserViewLayout); |
| }; |
| |
| BrowserView::BrowserView(Browser* browser) |
| : ::BrowserView(browser), |
| status_area_(NULL), |
| saved_focused_widget_(NULL) { |
| } |
| |
| BrowserView::~BrowserView() { |
| if (toolbar()) |
| toolbar()->RemoveMenuListener(this); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // BrowserView, ::BrowserView overrides: |
| |
| void BrowserView::Init() { |
| ::BrowserView::Init(); |
| status_area_ = new StatusAreaView(this); |
| status_area_->SetID(VIEW_ID_STATUS_AREA); |
| AddChildView(status_area_); |
| status_area_->Init(); |
| InitSystemMenu(); |
| |
| // The ContextMenuController has to be set to a NonClientView but |
| // not to a NonClientFrameView because a TabStrip is not a child of |
| // a NonClientFrameView even though visually a TabStrip is over a |
| // NonClientFrameView. |
| BrowserFrameGtk* gtk_frame = static_cast<BrowserFrameGtk*>(frame()); |
| gtk_frame->non_client_view()->SetContextMenuController(this); |
| |
| // Listen to wrench menu opens. |
| if (toolbar()) |
| toolbar()->AddMenuListener(this); |
| |
| // Make sure the window is set to the right type. |
| std::vector<int> params; |
| params.push_back(browser()->tab_count()); |
| params.push_back(browser()->active_index()); |
| params.push_back(gtk_get_current_event_time()); |
| WmIpc::instance()->SetWindowType( |
| GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()), |
| WM_IPC_WINDOW_CHROME_TOPLEVEL, |
| ¶ms); |
| } |
| |
| void BrowserView::Show() { |
| ShowInternal(true); |
| } |
| |
| void BrowserView::ShowInactive() { |
| ShowInternal(false); |
| } |
| |
| void BrowserView::ShowInternal(bool is_active) { |
| bool was_visible = frame()->GetWindow()->IsVisible(); |
| if (is_active) |
| ::BrowserView::Show(); |
| else |
| ::BrowserView::ShowInactive(); |
| if (!was_visible) { |
| // Have to update the tab count and selected index to reflect reality. |
| std::vector<int> params; |
| params.push_back(browser()->tab_count()); |
| params.push_back(browser()->active_index()); |
| WmIpc::instance()->SetWindowType( |
| GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()), |
| WM_IPC_WINDOW_CHROME_TOPLEVEL, |
| ¶ms); |
| } |
| } |
| |
| void BrowserView::FocusChromeOSStatus() { |
| SaveFocusedView(); |
| status_area_->SetPaneFocus(last_focused_view_storage_id(), NULL); |
| } |
| |
| views::LayoutManager* BrowserView::CreateLayoutManager() const { |
| return new BrowserViewLayout(); |
| } |
| |
| void BrowserView::ChildPreferredSizeChanged(View* child) { |
| Layout(); |
| } |
| |
| bool BrowserView::GetSavedWindowBounds(gfx::Rect* bounds) const { |
| if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeosFrame)) { |
| // Typically we don't request a full screen size. This means we'll request a |
| // non-full screen size, layout/paint at that size, then the window manager |
| // will snap us to full screen size. This results in an ugly |
| // resize/paint. To avoid this we always request a full screen size. |
| *bounds = views::Screen::GetMonitorWorkAreaNearestWindow( |
| GTK_WIDGET(GetWindow()->GetNativeWindow())); |
| return true; |
| } |
| return ::BrowserView::GetSavedWindowBounds(bounds); |
| } |
| |
| void BrowserView::Cut() { |
| gtk_util::DoCut(this); |
| } |
| |
| void BrowserView::Copy() { |
| gtk_util::DoCopy(this); |
| } |
| |
| void BrowserView::Paste() { |
| gtk_util::DoPaste(this); |
| } |
| |
| // views::ContextMenuController overrides. |
| void BrowserView::ShowContextMenuForView(views::View* source, |
| const gfx::Point& p, |
| bool is_mouse_gesture) { |
| // Only show context menu if point is in unobscured parts of browser, i.e. |
| // if NonClientHitTest returns : |
| // - HTCAPTION: in title bar or unobscured part of tabstrip |
| // - HTNOWHERE: as the name implies. |
| gfx::Point point_in_parent_coords(p); |
| views::View::ConvertPointToView(NULL, parent(), &point_in_parent_coords); |
| int hit_test = NonClientHitTest(point_in_parent_coords); |
| if (hit_test == HTCAPTION || hit_test == HTNOWHERE) |
| system_menu_menu_->RunMenuAt(p, views::Menu2::ALIGN_TOPLEFT); |
| } |
| |
| void BrowserView::OnMenuOpened() { |
| // Save the focused widget before wrench menu opens. |
| saved_focused_widget_ = gtk_window_get_focus(GetNativeHandle()); |
| } |
| |
| // StatusAreaHost overrides. |
| Profile* BrowserView::GetProfile() const { |
| return browser()->profile(); |
| } |
| |
| gfx::NativeWindow BrowserView::GetNativeWindow() const { |
| return GetWindow()->GetNativeWindow(); |
| } |
| |
| bool BrowserView::ShouldOpenButtonOptions( |
| const views::View* button_view) const { |
| return true; |
| } |
| |
| void BrowserView::ExecuteBrowserCommand(int id) const { |
| browser()->ExecuteCommand(id); |
| } |
| |
| void BrowserView::OpenButtonOptions(const views::View* button_view) { |
| if (button_view == status_area_->network_view()) { |
| browser()->OpenInternetOptionsDialog(); |
| } else if (button_view == status_area_->input_method_view()) { |
| browser()->OpenLanguageOptionsDialog(); |
| } else { |
| browser()->OpenSystemOptionsDialog(); |
| } |
| } |
| |
| StatusAreaHost::ScreenMode BrowserView::GetScreenMode() const { |
| return kBrowserMode; |
| } |
| |
| StatusAreaHost::TextStyle BrowserView::GetTextStyle() const { |
| ui::ThemeProvider* tp = GetThemeProvider(); |
| return tp->HasCustomImage(IDR_THEME_FRAME) ? |
| StatusAreaHost::kWhiteHaloed : (IsOffTheRecord() ? |
| StatusAreaHost::kWhitePlain : StatusAreaHost::kGrayEmbossed); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // BrowserView protected: |
| |
| void BrowserView::GetAccessiblePanes( |
| std::vector<AccessiblePaneView*>* panes) { |
| ::BrowserView::GetAccessiblePanes(panes); |
| panes->push_back(status_area_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // BrowserView private: |
| |
| void BrowserView::InitSystemMenu() { |
| system_menu_contents_.reset(new ui::SimpleMenuModel(this)); |
| system_menu_contents_->AddItemWithStringId(IDC_RESTORE_TAB, |
| IDS_RESTORE_TAB); |
| system_menu_contents_->AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); |
| system_menu_contents_->AddSeparator(); |
| system_menu_contents_->AddItemWithStringId(IDC_TASK_MANAGER, |
| IDS_TASK_MANAGER); |
| system_menu_menu_.reset(new views::Menu2(system_menu_contents_.get())); |
| } |
| |
| } // namespace chromeos |
| |
| // static |
| BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) { |
| // Create a browser view for chromeos. |
| BrowserView* view; |
| if (browser->type() & Browser::TYPE_POPUP) |
| view = new chromeos::PanelBrowserView(browser); |
| else |
| view = new chromeos::BrowserView(browser); |
| BrowserFrame::Create(view, browser->profile()); |
| return view; |
| } |