Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / opaque_browser_frame_view.cc
blobf370c85a8039cadb8d6536f4986bf91686247f51
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/compiler_specific.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/profiles/profiles_state.h"
15 #include "chrome/browser/themes/theme_properties.h"
16 #include "chrome/browser/ui/views/avatar_label.h"
17 #include "chrome/browser/ui/views/avatar_menu_button.h"
18 #include "chrome/browser/ui/views/frame/browser_frame.h"
19 #include "chrome/browser/ui/views/frame/browser_view.h"
20 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
21 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h"
22 #include "chrome/browser/ui/views/new_avatar_button.h"
23 #include "chrome/browser/ui/views/tab_icon_view.h"
24 #include "chrome/browser/ui/views/tabs/tab_strip.h"
25 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/common/profile_management_switches.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/web_contents.h"
30 #include "grit/chromium_strings.h"
31 #include "grit/generated_resources.h"
32 #include "grit/theme_resources.h"
33 #include "grit/ui_resources.h"
34 #include "ui/base/accessibility/accessible_view_state.h"
35 #include "ui/base/hit_test.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/gfx/canvas.h"
40 #include "ui/gfx/font_list.h"
41 #include "ui/gfx/image/image.h"
42 #include "ui/gfx/image/image_skia.h"
43 #include "ui/gfx/path.h"
44 #include "ui/gfx/rect_conversions.h"
45 #include "ui/views/controls/button/image_button.h"
46 #include "ui/views/controls/image_view.h"
47 #include "ui/views/controls/label.h"
48 #include "ui/views/layout/layout_constants.h"
49 #include "ui/views/widget/root_view.h"
50 #include "ui/views/window/frame_background.h"
51 #include "ui/views/window/window_shape.h"
53 #if defined(OS_LINUX)
54 #include "ui/views/controls/menu/menu_runner.h"
55 #endif
57 using content::WebContents;
59 namespace {
61 // While resize areas on Windows are normally the same size as the window
62 // borders, our top area is shrunk by 1 px to make it easier to move the window
63 // around with our thinner top grabbable strip. (Incidentally, our side and
64 // bottom resize areas don't match the frame border thickness either -- they
65 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
66 const int kTopResizeAdjust = 1;
68 // In the window corners, the resize areas don't actually expand bigger, but the
69 // 16 px at the end of each edge triggers diagonal resizing.
70 const int kResizeAreaCornerSize = 16;
72 // The content left/right images have a shadow built into them.
73 const int kContentEdgeShadowThickness = 2;
75 // The icon never shrinks below 16 px on a side.
76 const int kIconMinimumSize = 16;
78 // The top 3 px of the tabstrip is shadow; in maximized mode we push this off
79 // the top of the screen so the tabs appear flush against the screen edge.
80 const int kTabstripTopShadowThickness = 3;
82 } // namespace
84 ///////////////////////////////////////////////////////////////////////////////
85 // OpaqueBrowserFrameView, public:
87 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
88 BrowserView* browser_view)
89 : BrowserNonClientFrameView(frame, browser_view),
90 layout_(new OpaqueBrowserFrameViewLayout(this)),
91 minimize_button_(NULL),
92 maximize_button_(NULL),
93 restore_button_(NULL),
94 close_button_(NULL),
95 window_icon_(NULL),
96 window_title_(NULL),
97 frame_background_(new views::FrameBackground()) {
98 SetLayoutManager(layout_);
100 if (OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons()) {
101 minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE,
102 IDR_MINIMIZE_H,
103 IDR_MINIMIZE_P,
104 IDR_MINIMIZE_BUTTON_MASK,
105 IDS_ACCNAME_MINIMIZE,
106 VIEW_ID_MINIMIZE_BUTTON);
107 maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE,
108 IDR_MAXIMIZE_H,
109 IDR_MAXIMIZE_P,
110 IDR_MAXIMIZE_BUTTON_MASK,
111 IDS_ACCNAME_MAXIMIZE,
112 VIEW_ID_MAXIMIZE_BUTTON);
113 restore_button_ = InitWindowCaptionButton(IDR_RESTORE,
114 IDR_RESTORE_H,
115 IDR_RESTORE_P,
116 IDR_RESTORE_BUTTON_MASK,
117 IDS_ACCNAME_RESTORE,
118 VIEW_ID_RESTORE_BUTTON);
119 close_button_ = InitWindowCaptionButton(IDR_CLOSE,
120 IDR_CLOSE_H,
121 IDR_CLOSE_P,
122 IDR_CLOSE_BUTTON_MASK,
123 IDS_ACCNAME_CLOSE,
124 VIEW_ID_CLOSE_BUTTON);
127 // Initializing the TabIconView is expensive, so only do it if we need to.
128 if (browser_view->ShouldShowWindowIcon()) {
129 window_icon_ = new TabIconView(this, this);
130 window_icon_->set_is_light(true);
131 window_icon_->set_id(VIEW_ID_WINDOW_ICON);
132 AddChildView(window_icon_);
133 window_icon_->Update();
136 window_title_ = new views::Label(browser_view->GetWindowTitle(),
137 gfx::FontList(BrowserFrame::GetTitleFont()));
138 window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
139 window_title_->SetEnabledColor(SK_ColorWHITE);
140 // TODO(msw): Use a transparent background color as a workaround to use the
141 // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts.
142 window_title_->SetBackgroundColor(0x00000000);
143 window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
144 window_title_->set_id(VIEW_ID_WINDOW_TITLE);
145 AddChildView(window_title_);
147 if (browser_view->IsRegularOrGuestSession() &&
148 switches::IsNewProfileManagement())
149 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON);
150 else
151 UpdateAvatarInfo();
153 if (!browser_view->IsOffTheRecord()) {
154 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
155 content::NotificationService::AllSources());
158 platform_observer_.reset(
159 OpaqueBrowserFrameViewPlatformSpecific::Create(this, layout_));
162 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
165 ///////////////////////////////////////////////////////////////////////////////
166 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
168 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
169 views::View* tabstrip) const {
170 if (!tabstrip)
171 return gfx::Rect();
173 return layout_->GetBoundsForTabStrip(tabstrip->GetPreferredSize(), width());
176 int OpaqueBrowserFrameView::GetTopInset() const {
177 return browser_view()->IsTabStripVisible() ?
178 layout_->GetTabStripInsetsTop(false) :
179 layout_->NonClientTopBorderHeight(false);
182 int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
183 return 0;
186 void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
187 if (window_icon_)
188 window_icon_->Update();
191 gfx::Size OpaqueBrowserFrameView::GetMinimumSize() {
192 return layout_->GetMinimumSize(width());
195 ///////////////////////////////////////////////////////////////////////////////
196 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
198 gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
199 return layout_->client_view_bounds();
202 gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
203 const gfx::Rect& client_bounds) const {
204 return layout_->GetWindowBoundsForClientBounds(client_bounds);
207 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
208 if (!bounds().Contains(point))
209 return HTNOWHERE;
211 // See if the point is within the avatar menu button or within the avatar
212 // label.
213 if ((avatar_button() &&
214 avatar_button()->GetMirroredBounds().Contains(point)) ||
215 (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point)) ||
216 (new_avatar_button() &&
217 new_avatar_button()->GetMirroredBounds().Contains(point)))
218 return HTCLIENT;
220 int frame_component = frame()->client_view()->NonClientHitTest(point);
222 // See if we're in the sysmenu region. We still have to check the tabstrip
223 // first so that clicks in a tab don't get treated as sysmenu clicks.
224 gfx::Rect sysmenu_rect(IconBounds());
225 // In maximized mode we extend the rect to the screen corner to take advantage
226 // of Fitts' Law.
227 if (frame()->IsMaximized())
228 sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
229 sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
230 if (sysmenu_rect.Contains(point))
231 return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
233 if (frame_component != HTNOWHERE)
234 return frame_component;
236 // Then see if the point is within any of the window controls.
237 if (close_button_ && close_button_->visible() &&
238 close_button_->GetMirroredBounds().Contains(point))
239 return HTCLOSE;
240 if (restore_button_ && restore_button_->visible() &&
241 restore_button_->GetMirroredBounds().Contains(point))
242 return HTMAXBUTTON;
243 if (maximize_button_ && maximize_button_->visible() &&
244 maximize_button_->GetMirroredBounds().Contains(point))
245 return HTMAXBUTTON;
246 if (minimize_button_ && minimize_button_->visible() &&
247 minimize_button_->GetMirroredBounds().Contains(point))
248 return HTMINBUTTON;
250 views::WidgetDelegate* delegate = frame()->widget_delegate();
251 if (!delegate) {
252 LOG(WARNING) << "delegate is NULL, returning safe default.";
253 return HTCAPTION;
255 int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
256 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
257 delegate->CanResize());
258 // Fall back to the caption if no other component matches.
259 return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
262 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
263 gfx::Path* window_mask) {
264 DCHECK(window_mask);
266 if (frame()->IsMaximized() || frame()->IsFullscreen())
267 return;
269 views::GetDefaultWindowMask(size, window_mask);
272 void OpaqueBrowserFrameView::ResetWindowControls() {
273 if (!OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons())
274 return;
275 restore_button_->SetState(views::CustomButton::STATE_NORMAL);
276 minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
277 maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
278 // The close button isn't affected by this constraint.
281 void OpaqueBrowserFrameView::UpdateWindowIcon() {
282 window_icon_->SchedulePaint();
285 void OpaqueBrowserFrameView::UpdateWindowTitle() {
286 if (!frame()->IsFullscreen())
287 window_title_->SchedulePaint();
290 ///////////////////////////////////////////////////////////////////////////////
291 // OpaqueBrowserFrameView, views::View overrides:
293 bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const {
294 if (!views::View::HitTestRect(rect)) {
295 // |rect| is outside OpaqueBrowserFrameView's bounds.
296 return false;
299 // If the rect is outside the bounds of the client area, claim it.
300 gfx::RectF rect_in_client_view_coords_f(rect);
301 View::ConvertRectToTarget(this, frame()->client_view(),
302 &rect_in_client_view_coords_f);
303 gfx::Rect rect_in_client_view_coords = gfx::ToEnclosingRect(
304 rect_in_client_view_coords_f);
305 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords))
306 return true;
308 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
309 // a non-tab portion.
310 TabStrip* tabstrip = browser_view()->tabstrip();
311 if (!tabstrip || !browser_view()->IsTabStripVisible())
312 return false;
314 gfx::RectF rect_in_tabstrip_coords_f(rect);
315 View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
316 gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
317 rect_in_tabstrip_coords_f);
318 if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) {
319 // |rect| is below the tabstrip.
320 return false;
323 if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) {
324 // Claim |rect| if it is in a non-tab portion of the tabstrip.
325 return tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
328 // The window switcher button is to the right of the tabstrip but is
329 // part of the client view.
330 views::View* window_switcher_button =
331 browser_view()->window_switcher_button();
332 if (window_switcher_button && window_switcher_button->visible()) {
333 gfx::RectF rect_in_window_switcher_coords_f(rect);
334 View::ConvertRectToTarget(this, window_switcher_button,
335 &rect_in_window_switcher_coords_f);
336 gfx::Rect rect_in_window_switcher_coords = gfx::ToEnclosingRect(
337 rect_in_window_switcher_coords_f);
339 if (window_switcher_button->HitTestRect(rect_in_window_switcher_coords))
340 return false;
343 // We claim |rect| because it is above the bottom of the tabstrip, but
344 // neither in the tabstrip nor in the window switcher button. In particular,
345 // the avatar label/button is left of the tabstrip and the window controls
346 // are right of the tabstrip.
347 return true;
350 void OpaqueBrowserFrameView::GetAccessibleState(
351 ui::AccessibleViewState* state) {
352 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
355 ///////////////////////////////////////////////////////////////////////////////
356 // OpaqueBrowserFrameView, views::ButtonListener implementation:
358 void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
359 const ui::Event& event) {
360 if (sender == minimize_button_)
361 frame()->Minimize();
362 else if (sender == maximize_button_)
363 frame()->Maximize();
364 else if (sender == restore_button_)
365 frame()->Restore();
366 else if (sender == close_button_)
367 frame()->Close();
368 else if (sender == new_avatar_button())
369 browser_view()->ShowAvatarBubbleFromAvatarButton();
372 void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View* source,
373 const gfx::Point& point) {
374 #if defined(OS_LINUX)
375 views::MenuRunner menu_runner(frame()->GetSystemMenuModel());
376 ignore_result(menu_runner.RunMenuAt(browser_view()->GetWidget(),
377 window_icon_,
378 window_icon_->GetBoundsInScreen(),
379 views::MenuItemView::TOPLEFT,
380 ui::MENU_SOURCE_MOUSE,
381 views::MenuRunner::HAS_MNEMONICS));
382 #endif
385 ///////////////////////////////////////////////////////////////////////////////
386 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
388 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
389 // This function is queried during the creation of the window as the
390 // TabIconView we host is initialized, so we need to NULL check the selected
391 // WebContents because in this condition there is not yet a selected tab.
392 WebContents* current_tab = browser_view()->GetActiveWebContents();
393 return current_tab ? current_tab->IsLoading() : false;
396 gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() {
397 views::WidgetDelegate* delegate = frame()->widget_delegate();
398 if (!delegate) {
399 LOG(WARNING) << "delegate is NULL, returning safe default.";
400 return gfx::ImageSkia();
402 return delegate->GetWindowIcon();
405 ///////////////////////////////////////////////////////////////////////////////
406 // OpaqueBrowserFrameView, protected:
408 void OpaqueBrowserFrameView::Observe(
409 int type,
410 const content::NotificationSource& source,
411 const content::NotificationDetails& details) {
412 switch (type) {
413 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED:
414 if (browser_view() ->IsRegularOrGuestSession() &&
415 switches::IsNewProfileManagement())
416 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON);
417 else
418 UpdateAvatarInfo();
419 break;
420 default:
421 NOTREACHED() << "Got a notification we didn't register for!";
422 break;
426 ///////////////////////////////////////////////////////////////////////////////
427 // OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
429 bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
430 views::WidgetDelegate* delegate = frame()->widget_delegate();
431 return platform_observer_->ShouldShowTitleBar() && delegate &&
432 delegate->ShouldShowWindowIcon();
435 bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const {
436 // |delegate| may be NULL if called from callback of InputMethodChanged while
437 // a window is being destroyed.
438 // See more discussion at http://crosbug.com/8958
439 views::WidgetDelegate* delegate = frame()->widget_delegate();
440 return platform_observer_->ShouldShowTitleBar() && delegate &&
441 delegate->ShouldShowWindowTitle();
444 base::string16 OpaqueBrowserFrameView::GetWindowTitle() const {
445 return frame()->widget_delegate()->GetWindowTitle();
448 int OpaqueBrowserFrameView::GetIconSize() const {
449 #if defined(OS_WIN)
450 // This metric scales up if either the titlebar height or the titlebar font
451 // size are increased.
452 return GetSystemMetrics(SM_CYSMICON);
453 #else
454 return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize);
455 #endif
458 bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const {
459 return frame()->ShouldLeaveOffsetNearTopBorder();
462 gfx::Size OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const {
463 return browser_view()->GetMinimumSize();
466 bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const {
467 if (!OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons())
468 return false;
469 return platform_observer_->ShouldShowCaptionButtons();
472 bool OpaqueBrowserFrameView::ShouldShowAvatar() const {
473 return browser_view()->ShouldShowAvatar();
476 bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const {
477 return browser_view()->IsRegularOrGuestSession();
480 gfx::ImageSkia OpaqueBrowserFrameView::GetOTRAvatarIcon() const {
481 return browser_view()->GetOTRAvatarIcon();
484 bool OpaqueBrowserFrameView::IsMaximized() const {
485 return frame()->IsMaximized();
488 bool OpaqueBrowserFrameView::IsMinimized() const {
489 return frame()->IsMinimized();
492 bool OpaqueBrowserFrameView::IsFullscreen() const {
493 return frame()->IsFullscreen();
496 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
497 return browser_view()->IsTabStripVisible();
500 int OpaqueBrowserFrameView::GetTabStripHeight() const {
501 return browser_view()->GetTabStripHeight();
504 int OpaqueBrowserFrameView::GetAdditionalReservedSpaceInTabStrip() const {
505 // We don't have the sysmenu buttons in Windows 8 metro mode. However there
506 // are buttons like the window switcher which are drawn in the non client
507 // are in the BrowserView. We need to ensure that the tab strip does not
508 // draw on the window switcher button.
509 views::View* button = browser_view()->window_switcher_button();
510 return button ? button->width() : 0;
513 gfx::Size OpaqueBrowserFrameView::GetTabstripPreferredSize() const {
514 gfx::Size s = browser_view()->tabstrip()->GetPreferredSize();
515 return s;
518 ///////////////////////////////////////////////////////////////////////////////
519 // OpaqueBrowserFrameView, views::View overrides:
521 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
522 if (frame()->IsFullscreen())
523 return; // Nothing is visible, so don't bother to paint.
525 if (frame()->IsMaximized())
526 PaintMaximizedFrameBorder(canvas);
527 else
528 PaintRestoredFrameBorder(canvas);
530 // The window icon and title are painted by their respective views.
531 /* TODO(pkasting): If this window is active, we should also draw a drop
532 * shadow on the title. This is tricky, because we don't want to hardcode a
533 * shadow color (since we want to work with various themes), but we can't
534 * alpha-blend either (since the Windows text APIs don't really do this).
535 * So we'd need to sample the background color at the right location and
536 * synthesize a good shadow color. */
538 if (browser_view()->IsToolbarVisible())
539 PaintToolbarBackground(canvas);
540 if (!frame()->IsMaximized())
541 PaintRestoredClientEdge(canvas);
544 ///////////////////////////////////////////////////////////////////////////////
545 // OpaqueBrowserFrameView, private:
547 views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton(
548 int normal_image_id,
549 int hot_image_id,
550 int pushed_image_id,
551 int mask_image_id,
552 int accessibility_string_id,
553 ViewID view_id) {
554 views::ImageButton* button = new views::ImageButton(this);
555 ui::ThemeProvider* tp = frame()->GetThemeProvider();
556 button->SetImage(views::CustomButton::STATE_NORMAL,
557 tp->GetImageSkiaNamed(normal_image_id));
558 button->SetImage(views::CustomButton::STATE_HOVERED,
559 tp->GetImageSkiaNamed(hot_image_id));
560 button->SetImage(views::CustomButton::STATE_PRESSED,
561 tp->GetImageSkiaNamed(pushed_image_id));
562 if (browser_view()->IsBrowserTypeNormal()) {
563 button->SetBackground(
564 tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND),
565 tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND),
566 tp->GetImageSkiaNamed(mask_image_id));
568 button->SetAccessibleName(
569 l10n_util::GetStringUTF16(accessibility_string_id));
570 button->set_id(view_id);
571 AddChildView(button);
572 return button;
575 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
576 return layout_->FrameBorderThickness(restored);
579 int OpaqueBrowserFrameView::TopResizeHeight() const {
580 return FrameBorderThickness(false) - kTopResizeAdjust;
583 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
584 return layout_->NonClientBorderThickness();
587 gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
588 return layout_->IconBounds();
591 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
592 frame_background_->set_frame_color(GetFrameColor());
593 frame_background_->set_theme_image(GetFrameImage());
594 frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
595 frame_background_->set_top_area_height(GetTopAreaHeight());
597 ui::ThemeProvider* tp = GetThemeProvider();
598 frame_background_->SetSideImages(
599 tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE),
600 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER),
601 tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE),
602 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER));
603 frame_background_->SetCornerImages(
604 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER),
605 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER),
606 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER),
607 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER));
608 frame_background_->PaintRestored(canvas, this);
610 // Note: When we don't have a toolbar, we need to draw some kind of bottom
611 // edge here. Because the App Window graphics we use for this have an
612 // attached client edge and their sizing algorithm is a little involved, we do
613 // all this in PaintRestoredClientEdge().
616 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
617 frame_background_->set_frame_color(GetFrameColor());
618 frame_background_->set_theme_image(GetFrameImage());
619 frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
620 frame_background_->set_top_area_height(GetTopAreaHeight());
622 // Theme frame must be aligned with the tabstrip as if we were
623 // in restored mode. Note that the top of the tabstrip is
624 // kTabstripTopShadowThickness px off the top of the screen.
625 int restored_tabstrip_top_inset = 0;
626 if (browser_view()->IsTabStripVisible())
627 restored_tabstrip_top_inset = layout_->GetTabStripInsetsTop(true);
628 frame_background_->set_theme_background_y(
629 -restored_tabstrip_top_inset - kTabstripTopShadowThickness);
631 frame_background_->PaintMaximized(canvas, this);
633 // TODO(jamescook): Migrate this into FrameBackground.
634 if (!browser_view()->IsToolbarVisible()) {
635 // There's no toolbar to edge the frame border, so we need to draw a bottom
636 // edge. The graphic we use for this has a built in client edge, so we clip
637 // it off the bottom.
638 gfx::ImageSkia* top_center =
639 GetThemeProvider()->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
640 int edge_height = top_center->height() - kClientEdgeThickness;
641 canvas->TileImageInt(*top_center, 0,
642 frame()->client_view()->y() - edge_height, width(), edge_height);
646 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
647 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
648 if (toolbar_bounds.IsEmpty())
649 return;
650 gfx::Point toolbar_origin(toolbar_bounds.origin());
651 ConvertPointToTarget(browser_view(), this, &toolbar_origin);
652 toolbar_bounds.set_origin(toolbar_origin);
654 int x = toolbar_bounds.x();
655 int w = toolbar_bounds.width();
656 int y = toolbar_bounds.y();
657 int h = toolbar_bounds.height();
659 // Gross hack: We split the toolbar images into two pieces, since sometimes
660 // (popup mode) the toolbar isn't tall enough to show the whole image. The
661 // split happens between the top shadow section and the bottom gradient
662 // section so that we never break the gradient.
663 int split_point = kFrameShadowThickness * 2;
664 int bottom_y = y + split_point;
665 ui::ThemeProvider* tp = GetThemeProvider();
666 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(
667 IDR_CONTENT_TOP_LEFT_CORNER);
668 int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
670 // Split our canvas out so we can mask out the corners of the toolbar
671 // without masking out the frame.
672 canvas->SaveLayerAlpha(
673 255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
674 h));
676 // Paint the bottom rect.
677 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
678 tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
680 // Tile the toolbar image starting at the frame edge on the left and where the
681 // horizontal tabstrip is (or would be) on the top.
682 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
683 canvas->TileImageInt(*theme_toolbar,
684 x + GetThemeBackgroundXInset(),
685 bottom_y - GetTopInset(),
686 x, bottom_y, w, theme_toolbar->height());
688 // Draw rounded corners for the tab.
689 gfx::ImageSkia* toolbar_left_mask =
690 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
691 gfx::ImageSkia* toolbar_right_mask =
692 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
694 // We mask out the corners by using the DestinationIn transfer mode,
695 // which keeps the RGB pixels from the destination and the alpha from
696 // the source.
697 SkPaint paint;
698 paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
700 // Mask the left edge.
701 int left_x = x - kContentEdgeShadowThickness;
702 canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
703 split_point, left_x, y, toolbar_left_mask->width(),
704 split_point, false, paint);
705 canvas->DrawImageInt(*toolbar_left_mask, 0,
706 toolbar_left_mask->height() - bottom_edge_height,
707 toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
708 toolbar_left_mask->width(), bottom_edge_height, false, paint);
710 // Mask the right edge.
711 int right_x =
712 x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
713 canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
714 split_point, right_x, y, toolbar_right_mask->width(),
715 split_point, false, paint);
716 canvas->DrawImageInt(*toolbar_right_mask, 0,
717 toolbar_right_mask->height() - bottom_edge_height,
718 toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
719 toolbar_right_mask->width(), bottom_edge_height, false, paint);
720 canvas->Restore();
722 canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
723 left_x, y, toolbar_left->width(), split_point, false);
724 canvas->DrawImageInt(*toolbar_left, 0,
725 toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
726 bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
727 bottom_edge_height, false);
729 gfx::ImageSkia* toolbar_center =
730 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
731 canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
732 y, right_x - (left_x + toolbar_left->width()),
733 split_point);
735 gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed(
736 IDR_CONTENT_TOP_RIGHT_CORNER);
737 canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(),
738 split_point, right_x, y, toolbar_right->width(), split_point, false);
739 canvas->DrawImageInt(*toolbar_right, 0,
740 toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
741 bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
742 bottom_edge_height, false);
744 // Draw the content/toolbar separator.
745 canvas->FillRect(
746 gfx::Rect(x + kClientEdgeThickness,
747 toolbar_bounds.bottom() - kClientEdgeThickness,
748 w - (2 * kClientEdgeThickness),
749 kClientEdgeThickness),
750 ThemeProperties::GetDefaultColor(
751 ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
754 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
755 ui::ThemeProvider* tp = GetThemeProvider();
756 int client_area_top = frame()->client_view()->y();
757 int image_top = client_area_top;
759 gfx::Rect client_area_bounds =
760 layout_->CalculateClientAreaBounds(width(), height());
761 SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
763 if (browser_view()->IsToolbarVisible()) {
764 // The client edge images always start below the toolbar corner images. The
765 // client edge filled rects start there or at the bottom of the toolbar,
766 // whichever is shorter.
767 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
768 image_top += toolbar_bounds.y() +
769 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height();
770 client_area_top = std::min(image_top,
771 client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
772 } else if (!browser_view()->IsTabStripVisible()) {
773 // The toolbar isn't going to draw a client edge for us, so draw one
774 // ourselves.
775 gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT);
776 gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
777 gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT);
778 int top_edge_y = client_area_top - top_center->height();
779 int height = client_area_top - top_edge_y;
781 canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height,
782 client_area_bounds.x() - top_left->width(), top_edge_y,
783 top_left->width(), height, false);
784 canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
785 client_area_bounds.width(), std::min(height, top_center->height()));
786 canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height,
787 client_area_bounds.right(), top_edge_y,
788 top_right->width(), height, false);
790 // Draw the toolbar color across the top edge.
791 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
792 client_area_top - kClientEdgeThickness,
793 client_area_bounds.width() + (2 * kClientEdgeThickness),
794 kClientEdgeThickness), toolbar_color);
797 int client_area_bottom =
798 std::max(client_area_top, height() - NonClientBorderThickness());
799 int image_height = client_area_bottom - image_top;
801 // Draw the client edge images.
802 gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE);
803 canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
804 right->width(), image_height);
805 canvas->DrawImageInt(
806 *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
807 client_area_bounds.right(), client_area_bottom);
808 gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER);
809 canvas->TileImageInt(*bottom, client_area_bounds.x(),
810 client_area_bottom, client_area_bounds.width(),
811 bottom->height());
812 gfx::ImageSkia* bottom_left =
813 tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
814 canvas->DrawImageInt(*bottom_left,
815 client_area_bounds.x() - bottom_left->width(), client_area_bottom);
816 gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE);
817 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
818 image_top, left->width(), image_height);
820 // Draw the toolbar color so that the client edges show the right color even
821 // where not covered by the toolbar image. NOTE: We do this after drawing the
822 // images because the images are meant to alpha-blend atop the frame whereas
823 // these rects are meant to be fully opaque, without anything overlaid.
824 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
825 client_area_top, kClientEdgeThickness,
826 client_area_bottom + kClientEdgeThickness - client_area_top),
827 toolbar_color);
828 canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom,
829 client_area_bounds.width(), kClientEdgeThickness),
830 toolbar_color);
831 canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top,
832 kClientEdgeThickness,
833 client_area_bottom + kClientEdgeThickness - client_area_top),
834 toolbar_color);
837 SkColor OpaqueBrowserFrameView::GetFrameColor() const {
838 bool is_incognito = browser_view()->IsOffTheRecord();
839 if (browser_view()->IsBrowserTypeNormal()) {
840 if (ShouldPaintAsActive()) {
841 return GetThemeProvider()->GetColor(is_incognito ?
842 ThemeProperties::COLOR_FRAME_INCOGNITO :
843 ThemeProperties::COLOR_FRAME);
845 return GetThemeProvider()->GetColor(is_incognito ?
846 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
847 ThemeProperties::COLOR_FRAME_INACTIVE);
849 // Never theme app and popup windows.
850 if (ShouldPaintAsActive()) {
851 return ThemeProperties::GetDefaultColor(is_incognito ?
852 ThemeProperties::COLOR_FRAME_INCOGNITO : ThemeProperties::COLOR_FRAME);
854 return ThemeProperties::GetDefaultColor(is_incognito ?
855 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
856 ThemeProperties::COLOR_FRAME_INACTIVE);
859 gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const {
860 bool is_incognito = browser_view()->IsOffTheRecord();
861 int resource_id;
862 if (browser_view()->IsBrowserTypeNormal()) {
863 if (ShouldPaintAsActive()) {
864 resource_id = is_incognito ?
865 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
866 } else {
867 resource_id = is_incognito ?
868 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
870 return GetThemeProvider()->GetImageSkiaNamed(resource_id);
872 // Never theme app and popup windows.
873 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
874 if (ShouldPaintAsActive()) {
875 resource_id = is_incognito ?
876 IDR_THEME_FRAME_INCOGNITO : IDR_FRAME;
877 } else {
878 resource_id = is_incognito ?
879 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
881 return rb.GetImageSkiaNamed(resource_id);
884 gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const {
885 ui::ThemeProvider* tp = GetThemeProvider();
886 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
887 browser_view()->IsBrowserTypeNormal() &&
888 !browser_view()->IsOffTheRecord()) {
889 return tp->GetImageSkiaNamed(ShouldPaintAsActive() ?
890 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
892 return NULL;
895 int OpaqueBrowserFrameView::GetTopAreaHeight() const {
896 gfx::ImageSkia* frame_image = GetFrameImage();
897 int top_area_height = frame_image->height();
898 if (browser_view()->IsTabStripVisible()) {
899 top_area_height = std::max(top_area_height,
900 GetBoundsForTabStrip(browser_view()->tabstrip()).bottom());
902 return top_area_height;