browser_frame_win.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/views/frame/browser_frame_win.h" 6 7#include <dwmapi.h> 8#include <shellapi.h> 9 10#include <set> 11 12#include "app/win_util.h" 13#include "base/win_util.h" 14#include "chrome/browser/accessibility/browser_accessibility_state.h" 15#include "chrome/browser/profile.h" 16#include "chrome/browser/browser_list.h" 17#include "chrome/browser/themes/browser_theme_provider.h" 18#include "chrome/browser/views/frame/app_panel_browser_frame_view.h" 19#include "chrome/browser/views/frame/browser_non_client_frame_view.h" 20#include "chrome/browser/views/frame/browser_root_view.h" 21#include "chrome/browser/views/frame/browser_view.h" 22#include "chrome/browser/views/frame/glass_browser_frame_view.h" 23#include "chrome/browser/views/frame/opaque_browser_frame_view.h" 24#include "grit/theme_resources.h" 25#include "views/screen.h" 26#include "views/window/window_delegate.h" 27 28// static 29static const int kClientEdgeThickness = 3; 30static const int kTabDragWindowAlpha = 200; 31// We need to offset the DWMFrame into the toolbar so that the blackness 32// doesn't show up on our rounded corners. 33static const int kDWMFrameTopOffset = 3; 34 35// static (Factory method.) 36BrowserFrame* BrowserFrame::Create(BrowserView* browser_view, 37 Profile* profile) { 38 BrowserFrameWin* frame = new BrowserFrameWin(browser_view, profile); 39 frame->Init(); 40 return frame; 41} 42 43// static 44const gfx::Font& BrowserFrame::GetTitleFont() { 45 static gfx::Font* title_font = new gfx::Font(win_util::GetWindowTitleFont()); 46 return *title_font; 47} 48 49/////////////////////////////////////////////////////////////////////////////// 50// BrowserFrame, public: 51 52BrowserFrameWin::BrowserFrameWin(BrowserView* browser_view, Profile* profile) 53 : WindowWin(browser_view), 54 browser_view_(browser_view), 55 root_view_(NULL), 56 frame_initialized_(false), 57 profile_(profile) { 58 browser_view_->set_frame(this); 59 GetNonClientView()->SetFrameView(CreateFrameViewForWindow()); 60 // Don't focus anything on creation, selecting a tab will set the focus. 61 set_focus_on_creation(false); 62} 63 64void BrowserFrameWin::Init() { 65 WindowWin::Init(NULL, gfx::Rect()); 66} 67 68BrowserFrameWin::~BrowserFrameWin() { 69} 70 71views::Window* BrowserFrameWin::GetWindow() { 72 return this; 73} 74 75int BrowserFrameWin::GetMinimizeButtonOffset() const { 76 TITLEBARINFOEX titlebar_info; 77 titlebar_info.cbSize = sizeof(TITLEBARINFOEX); 78 SendMessage(GetNativeView(), WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info); 79 80 CPoint minimize_button_corner(titlebar_info.rgrect[2].left, 81 titlebar_info.rgrect[2].top); 82 MapWindowPoints(HWND_DESKTOP, GetNativeView(), &minimize_button_corner, 1); 83 84 return minimize_button_corner.x; 85} 86 87gfx::Rect BrowserFrameWin::GetBoundsForTabStrip(BaseTabStrip* tabstrip) const { 88 return browser_frame_view_->GetBoundsForTabStrip(tabstrip); 89} 90 91int BrowserFrameWin::GetHorizontalTabStripVerticalOffset(bool restored) const { 92 return browser_frame_view_->GetHorizontalTabStripVerticalOffset(restored); 93} 94 95void BrowserFrameWin::UpdateThrobber(bool running) { 96 browser_frame_view_->UpdateThrobber(running); 97} 98 99void BrowserFrameWin::ContinueDraggingDetachedTab() { 100 // Send the message directly, so that the window is positioned appropriately. 101 SendMessage(GetNativeWindow(), WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(0, 0)); 102} 103 104ThemeProvider* BrowserFrameWin::GetThemeProviderForFrame() const { 105 // This is implemented for a different interface than GetThemeProvider is, 106 // but they mean the same things. 107 return GetThemeProvider(); 108} 109 110bool BrowserFrameWin::AlwaysUseNativeFrame() const { 111 // App panel windows draw their own frame. 112 if (browser_view_->IsBrowserTypePanel()) 113 return false; 114 115 if (browser_view_->browser()->type() == Browser::TYPE_EXTENSION_APP) 116 return false; 117 118 // We don't theme popup or app windows, so regardless of whether or not a 119 // theme is active for normal browser windows, we don't want to use the custom 120 // frame for popups/apps. 121 if (!browser_view_->IsBrowserTypeNormal() && win_util::ShouldUseVistaFrame()) 122 return true; 123 124 // Otherwise, we use the native frame when we're told we should by the theme 125 // provider (e.g. no custom theme is active). 126 return GetThemeProvider()->ShouldUseNativeFrame(); 127} 128 129views::View* BrowserFrameWin::GetFrameView() const { 130 return browser_frame_view_; 131} 132 133void BrowserFrameWin::TabStripDisplayModeChanged() { 134 if (GetRootView()->GetChildViewCount() > 0) { 135 // Make sure the child of the root view gets Layout again. 136 GetRootView()->GetChildViewAt(0)->InvalidateLayout(); 137 } 138 GetRootView()->Layout(); 139 140 UpdateDWMFrame(); 141} 142 143/////////////////////////////////////////////////////////////////////////////// 144// BrowserFrame, views::WindowWin overrides: 145 146gfx::Insets BrowserFrameWin::GetClientAreaInsets() const { 147 // Use the default client insets for an opaque frame or a glass popup/app 148 // frame. 149 if (!GetNonClientView()->UseNativeFrame() || 150 !browser_view_->IsBrowserTypeNormal()) { 151 return WindowWin::GetClientAreaInsets(); 152 } 153 154 int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); 155 // In fullscreen mode, we have no frame. In restored mode, we draw our own 156 // client edge over part of the default frame. 157 if (IsFullscreen()) 158 border_thickness = 0; 159 else if (!IsMaximized()) 160 border_thickness -= kClientEdgeThickness; 161 return gfx::Insets(0, border_thickness, border_thickness, border_thickness); 162} 163 164bool BrowserFrameWin::GetAccelerator(int cmd_id, 165 menus::Accelerator* accelerator) { 166 return browser_view_->GetAccelerator(cmd_id, accelerator); 167} 168 169void BrowserFrameWin::OnEndSession(BOOL ending, UINT logoff) { 170 BrowserList::SessionEnding(); 171} 172 173void BrowserFrameWin::OnEnterSizeMove() { 174 browser_view_->WindowMoveOrResizeStarted(); 175} 176 177void BrowserFrameWin::OnExitSizeMove() { 178 WidgetWin::OnExitSizeMove(); 179} 180 181void BrowserFrameWin::OnInitMenuPopup(HMENU menu, UINT position, 182 BOOL is_system_menu) { 183 browser_view_->PrepareToRunSystemMenu(menu); 184} 185 186LRESULT BrowserFrameWin::OnMouseActivate(HWND window, UINT hittest_code, 187 UINT message) { 188 return browser_view_->ActivateAppModalDialog() ? MA_NOACTIVATEANDEAT 189 : MA_ACTIVATE; 190} 191 192void BrowserFrameWin::OnMove(const CPoint& point) { 193 browser_view_->WindowMoved(); 194} 195 196void BrowserFrameWin::OnMoving(UINT param, LPRECT new_bounds) { 197 browser_view_->WindowMoved(); 198} 199 200LRESULT BrowserFrameWin::OnNCActivate(BOOL active) { 201 if (browser_view_->ActivateAppModalDialog()) 202 return TRUE; 203 204 browser_view_->ActivationChanged(!!active); 205 return WindowWin::OnNCActivate(active); 206} 207 208LRESULT BrowserFrameWin::OnNCHitTest(const CPoint& pt) { 209 // Only do DWM hit-testing when we are using the native frame. 210 if (GetNonClientView()->UseNativeFrame()) { 211 LRESULT result; 212 if (DwmDefWindowProc(GetNativeView(), WM_NCHITTEST, 0, 213 MAKELPARAM(pt.x, pt.y), &result)) { 214 return result; 215 } 216 } 217 return WindowWin::OnNCHitTest(pt); 218} 219 220void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) { 221 WindowWin::OnWindowPosChanged(window_pos); 222 UpdateDWMFrame(); 223 224 // Windows lies to us about the position of the minimize button before a 225 // window is visible. We use this position to place the OTR avatar in RTL 226 // mode, so when the window is shown, we need to re-layout and schedule a 227 // paint for the non-client frame view so that the icon top has the correct 228 // position when the window becomes visible. This fixes bugs where the icon 229 // appears to overlay the minimize button. 230 // Note that we will call Layout every time SetWindowPos is called with 231 // SWP_SHOWWINDOW, however callers typically are careful about not specifying 232 // this flag unless necessary to avoid flicker. 233 if (window_pos->flags & SWP_SHOWWINDOW) { 234 GetNonClientView()->Layout(); 235 GetNonClientView()->SchedulePaint(); 236 } 237} 238 239ThemeProvider* BrowserFrameWin::GetThemeProvider() const { 240 return profile_->GetThemeProvider(); 241} 242 243ThemeProvider* BrowserFrameWin::GetDefaultThemeProvider() const { 244 return profile_->GetThemeProvider(); 245} 246 247void BrowserFrameWin::OnScreenReaderDetected() { 248 Singleton<BrowserAccessibilityState>()->OnScreenReaderDetected(); 249 WindowWin::OnScreenReaderDetected(); 250} 251 252/////////////////////////////////////////////////////////////////////////////// 253// BrowserFrame, views::CustomFrameWindow overrides: 254 255int BrowserFrameWin::GetShowState() const { 256 return browser_view_->GetShowState(); 257} 258 259void BrowserFrameWin::Activate() { 260 // When running under remote desktop, if the remote desktop client is not 261 // active on the users desktop, then none of the windows contained in the 262 // remote desktop will be activated. However, WindowWin::Activate will still 263 // bring this browser window to the foreground. We explicitly set ourselves 264 // as the last active browser window to ensure that we get treated as such by 265 // the rest of Chrome. 266 BrowserList::SetLastActive(browser_view_->browser()); 267 268 WindowWin::Activate(); 269} 270 271views::NonClientFrameView* BrowserFrameWin::CreateFrameViewForWindow() { 272 if (AlwaysUseNativeFrame()) 273 browser_frame_view_ = new GlassBrowserFrameView(this, browser_view_); 274 else if (browser_view_->IsBrowserTypePanel()) 275 browser_frame_view_ = new AppPanelBrowserFrameView(this, browser_view_); 276 else 277 browser_frame_view_ = new OpaqueBrowserFrameView(this, browser_view_); 278 return browser_frame_view_; 279} 280 281void BrowserFrameWin::UpdateFrameAfterFrameChange() { 282 // We need to update the glass region on or off before the base class adjusts 283 // the window region. 284 UpdateDWMFrame(); 285 WindowWin::UpdateFrameAfterFrameChange(); 286} 287 288views::RootView* BrowserFrameWin::CreateRootView() { 289 root_view_ = new BrowserRootView(browser_view_, this); 290 return root_view_; 291} 292 293/////////////////////////////////////////////////////////////////////////////// 294// BrowserFrame, private: 295 296void BrowserFrameWin::UpdateDWMFrame() { 297 // Nothing to do yet, or we're not showing a DWM frame. 298 if (!GetClientView() || !AlwaysUseNativeFrame()) 299 return; 300 301 MARGINS margins = { 0 }; 302 if (browser_view_->IsBrowserTypeNormal()) { 303 // In fullscreen mode, we don't extend glass into the client area at all, 304 // because the GDI-drawn text in the web content composited over it will 305 // become semi-transparent over any glass area. 306 if (!IsMaximized() && !IsFullscreen()) { 307 margins.cxLeftWidth = kClientEdgeThickness + 1; 308 margins.cxRightWidth = kClientEdgeThickness + 1; 309 margins.cyBottomHeight = kClientEdgeThickness + 1; 310 margins.cyTopHeight = kClientEdgeThickness + 1; 311 } 312 // In maximized mode, we only have a titlebar strip of glass, no side/bottom 313 // borders. 314 if (!browser_view_->IsFullscreen()) { 315 gfx::Rect tabstrip_bounds( 316 GetBoundsForTabStrip(browser_view_->tabstrip())); 317 margins.cyTopHeight = (browser_view_->UseVerticalTabs() ? 318 tabstrip_bounds.y() : tabstrip_bounds.bottom()) + kDWMFrameTopOffset; 319 } 320 } else { 321 // For popup and app windows we want to use the default margins. 322 } 323 DwmExtendFrameIntoClientArea(GetNativeView(), &margins); 324} 325