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.
4
5 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
6
7 #include <algorithm>
8
9 #include "ash/frame/caption_buttons/frame_caption_button.h"
10 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
11 #include "ash/frame/default_header_painter.h"
12 #include "ash/frame/frame_border_hit_test_controller.h"
13 #include "ash/frame/header_painter_util.h"
14 #include "ash/shell.h"
15 #include "chrome/app/chrome_command_ids.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/themes/theme_properties.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_commands.h"
20 #include "chrome/browser/ui/views/frame/browser_frame.h"
21 #include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
22 #include "chrome/browser/ui/views/frame/browser_view.h"
23 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
24 #include "chrome/browser/ui/views/profiles/avatar_label.h"
25 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
26 #include "chrome/browser/ui/views/tab_icon_view.h"
27 #include "chrome/browser/ui/views/tabs/tab_strip.h"
28 #include "content/public/browser/web_contents.h"
29 #include "grit/ash_resources.h"
30 #include "grit/theme_resources.h"
31 #include "ui/accessibility/ax_view_state.h"
32 #include "ui/aura/client/aura_constants.h"
33 #include "ui/aura/window.h"
34 #include "ui/base/hit_test.h"
35 #include "ui/base/layout.h"
36 #include "ui/base/resource/resource_bundle.h"
37 #include "ui/base/theme_provider.h"
38 #include "ui/compositor/layer_animator.h"
39 #include "ui/gfx/canvas.h"
40 #include "ui/gfx/image/image_skia.h"
41 #include "ui/gfx/rect_conversions.h"
42 #include "ui/views/controls/label.h"
43 #include "ui/views/layout/layout_constants.h"
44 #include "ui/views/widget/widget.h"
45 #include "ui/views/widget/widget_delegate.h"
46
47 namespace {
48
49 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
50 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
51 // user).
52 const int kAvatarBottomSpacing = 2;
53 // There are 2 px on each side of the avatar (between the frame border and
54 // it on the left, and between it and the tabstrip on the right).
55 const int kAvatarSideSpacing = 2;
56 // Space between left edge of window and tabstrip.
57 const int kTabstripLeftSpacing = 0;
58 // Space between right edge of tabstrip and maximize button.
59 const int kTabstripRightSpacing = 10;
60 // Height of the shadow of the content area, at the top of the toolbar.
61 const int kContentShadowHeight = 1;
62 // Space between top of window and top of tabstrip for tall headers, such as
63 // for restored windows, apps, etc.
64 const int kTabstripTopSpacingTall = 7;
65 // Space between top of window and top of tabstrip for short headers, such as
66 // for maximized windows, pop-ups, etc.
67 const int kTabstripTopSpacingShort = 0;
68 // Height of the shadow in the tab image, used to ensure clicks in the shadow
69 // area still drag restored windows. This keeps the clickable area large enough
70 // to hit easily.
71 const int kTabShadowHeight = 4;
72
73 // Combines View::ConvertPointToTarget() and View::HitTest() for a given
74 // |point|. Converts |point| from |src| to |dst| and hit tests it against |dst|.
ConvertedHitTest(views::View * src,views::View * dst,const gfx::Point & point)75 bool ConvertedHitTest(views::View* src,
76 views::View* dst,
77 const gfx::Point& point) {
78 DCHECK(src);
79 DCHECK(dst);
80 gfx::Point converted_point(point);
81 views::View::ConvertPointToTarget(src, dst, &converted_point);
82 return dst->HitTestPoint(converted_point);
83 }
84
85 } // namespace
86
87 ///////////////////////////////////////////////////////////////////////////////
88 // BrowserNonClientFrameViewAsh, public:
89
90 // static
91 const char BrowserNonClientFrameViewAsh::kViewClassName[] =
92 "BrowserNonClientFrameViewAsh";
93
BrowserNonClientFrameViewAsh(BrowserFrame * frame,BrowserView * browser_view)94 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
95 BrowserFrame* frame,
96 BrowserView* browser_view)
97 : BrowserNonClientFrameView(frame, browser_view),
98 caption_button_container_(NULL),
99 web_app_back_button_(NULL),
100 window_icon_(NULL),
101 frame_border_hit_test_controller_(
102 new ash::FrameBorderHitTestController(frame)) {
103 ash::Shell::GetInstance()->AddShellObserver(this);
104 }
105
~BrowserNonClientFrameViewAsh()106 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
107 ash::Shell::GetInstance()->RemoveShellObserver(this);
108 // browser_view() outlives the frame, as destruction of sibling views happens
109 // in the same order as creation - see BrowserView::CreateBrowserWindow.
110 chrome::RemoveCommandObserver(browser_view()->browser(), IDC_BACK, this);
111 }
112
Init()113 void BrowserNonClientFrameViewAsh::Init() {
114 caption_button_container_ = new ash::FrameCaptionButtonContainerView(frame(),
115 ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
116 caption_button_container_->UpdateSizeButtonVisibility();
117 AddChildView(caption_button_container_);
118
119 // Initializing the TabIconView is expensive, so only do it if we need to.
120 if (browser_view()->ShouldShowWindowIcon()) {
121 window_icon_ = new TabIconView(this, NULL);
122 window_icon_->set_is_light(true);
123 AddChildView(window_icon_);
124 window_icon_->Update();
125 }
126
127 // Create incognito icon if necessary.
128 UpdateAvatarInfo();
129
130 // HeaderPainter handles layout.
131 if (UsePackagedAppHeaderStyle()) {
132 ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
133 header_painter_.reset(header_painter);
134 header_painter->Init(frame(), this, caption_button_container_);
135 if (window_icon_) {
136 header_painter->UpdateLeftHeaderView(window_icon_);
137 }
138 } else if (UseWebAppHeaderStyle()) {
139 web_app_back_button_ =
140 new ash::FrameCaptionButton(this, ash::CAPTION_BUTTON_ICON_BACK);
141 web_app_back_button_->SetImages(ash::CAPTION_BUTTON_ICON_BACK,
142 ash::FrameCaptionButton::ANIMATE_NO,
143 IDR_AURA_WINDOW_CONTROL_ICON_BACK,
144 IDR_AURA_WINDOW_CONTROL_ICON_BACK_I,
145 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
146 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
147
148 UpdateBackButtonState(true);
149 chrome::AddCommandObserver(browser_view()->browser(), IDC_BACK, this);
150 AddChildView(web_app_back_button_);
151
152 ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
153 header_painter_.reset(header_painter);
154 header_painter->Init(frame(), this, caption_button_container_);
155 header_painter->UpdateLeftHeaderView(web_app_back_button_);
156 } else {
157 BrowserHeaderPainterAsh* header_painter = new BrowserHeaderPainterAsh;
158 header_painter_.reset(header_painter);
159 header_painter->Init(frame(), browser_view(), this, window_icon_,
160 caption_button_container_);
161 }
162 }
163
164 ///////////////////////////////////////////////////////////////////////////////
165 // BrowserNonClientFrameView:
166
GetBoundsForTabStrip(views::View * tabstrip) const167 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
168 views::View* tabstrip) const {
169 if (!tabstrip)
170 return gfx::Rect();
171
172 // When the tab strip is painted in the immersive fullscreen light bar style,
173 // the caption buttons and the avatar button are not visible. However, their
174 // bounds are still used to compute the tab strip bounds so that the tabs have
175 // the same horizontal position when the tab strip is painted in the immersive
176 // light bar style as when the top-of-window views are revealed.
177 int left_inset = GetTabStripLeftInset();
178 int right_inset = GetTabStripRightInset();
179 return gfx::Rect(left_inset,
180 GetTopInset(),
181 std::max(0, width() - left_inset - right_inset),
182 tabstrip->GetPreferredSize().height());
183 }
184
GetTopInset() const185 int BrowserNonClientFrameViewAsh::GetTopInset() const {
186 if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
187 return 0;
188
189 if (browser_view()->IsTabStripVisible()) {
190 if (frame()->IsMaximized() || frame()->IsFullscreen())
191 return kTabstripTopSpacingShort;
192 else
193 return kTabstripTopSpacingTall;
194 }
195
196 if (UsePackagedAppHeaderStyle() || UseWebAppHeaderStyle())
197 return header_painter_->GetHeaderHeightForPainting();
198
199 int caption_buttons_bottom = caption_button_container_->bounds().bottom();
200
201 // The toolbar partially overlaps the caption buttons.
202 if (browser_view()->IsToolbarVisible())
203 return caption_buttons_bottom - kContentShadowHeight;
204
205 return caption_buttons_bottom + kClientEdgeThickness;
206 }
207
GetThemeBackgroundXInset() const208 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
209 return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
210 }
211
UpdateThrobber(bool running)212 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
213 if (window_icon_)
214 window_icon_->Update();
215 }
216
217 ///////////////////////////////////////////////////////////////////////////////
218 // views::NonClientFrameView:
219
GetBoundsForClientView() const220 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
221 // The ClientView must be flush with the top edge of the widget so that the
222 // web contents can take up the entire screen in immersive fullscreen (with
223 // or without the top-of-window views revealed). When in immersive fullscreen
224 // and the top-of-window views are revealed, the TopContainerView paints the
225 // window header by redirecting paints from its background to
226 // BrowserNonClientFrameViewAsh.
227 return bounds();
228 }
229
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const230 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
231 const gfx::Rect& client_bounds) const {
232 return client_bounds;
233 }
234
NonClientHitTest(const gfx::Point & point)235 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
236 int hit_test = ash::FrameBorderHitTestController::NonClientHitTest(this,
237 caption_button_container_, point);
238
239 // See if the point is actually within the avatar menu button.
240 if (hit_test == HTCAPTION && avatar_button() &&
241 ConvertedHitTest(this, avatar_button(), point)) {
242 return HTCLIENT;
243 }
244
245 // See if the point is actually within the web app back button.
246 if (hit_test == HTCAPTION && web_app_back_button_ &&
247 ConvertedHitTest(this, web_app_back_button_, point)) {
248 return HTCLIENT;
249 }
250
251 // When the window is restored we want a large click target above the tabs
252 // to drag the window, so redirect clicks in the tab's shadow to caption.
253 if (hit_test == HTCLIENT &&
254 !(frame()->IsMaximized() || frame()->IsFullscreen())) {
255 // Convert point to client coordinates.
256 gfx::Point client_point(point);
257 View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
258 // Report hits in shadow at top of tabstrip as caption.
259 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
260 if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
261 hit_test = HTCAPTION;
262 }
263 return hit_test;
264 }
265
GetWindowMask(const gfx::Size & size,gfx::Path * window_mask)266 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
267 gfx::Path* window_mask) {
268 // Aura does not use window masks.
269 }
270
ResetWindowControls()271 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
272 // Hide the caption buttons in immersive fullscreen when the tab light bar
273 // is visible because it's confusing when the user hovers or clicks in the
274 // top-right of the screen and hits one.
275 bool button_visibility = !UseImmersiveLightbarHeaderStyle();
276 caption_button_container_->SetVisible(button_visibility);
277
278 caption_button_container_->ResetWindowControls();
279 }
280
UpdateWindowIcon()281 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
282 if (window_icon_)
283 window_icon_->SchedulePaint();
284 }
285
UpdateWindowTitle()286 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
287 if (!frame()->IsFullscreen())
288 header_painter_->SchedulePaintForTitle();
289 }
290
SizeConstraintsChanged()291 void BrowserNonClientFrameViewAsh::SizeConstraintsChanged() {
292 }
293
294 ///////////////////////////////////////////////////////////////////////////////
295 // views::View:
296
OnPaint(gfx::Canvas * canvas)297 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
298 if (!ShouldPaint())
299 return;
300
301 if (UseImmersiveLightbarHeaderStyle()) {
302 PaintImmersiveLightbarStyleHeader(canvas);
303 return;
304 }
305
306 caption_button_container_->SetPaintAsActive(ShouldPaintAsActive());
307 if (web_app_back_button_) {
308 // TODO(benwells): Check that the disabled and inactive states should be
309 // drawn in the same way.
310 web_app_back_button_->set_paint_as_active(
311 ShouldPaintAsActive() &&
312 chrome::IsCommandEnabled(browser_view()->browser(), IDC_BACK));
313 }
314
315 ash::HeaderPainter::Mode header_mode = ShouldPaintAsActive() ?
316 ash::HeaderPainter::MODE_ACTIVE : ash::HeaderPainter::MODE_INACTIVE;
317 header_painter_->PaintHeader(canvas, header_mode);
318 if (browser_view()->IsToolbarVisible())
319 PaintToolbarBackground(canvas);
320 else if (!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle())
321 PaintContentEdge(canvas);
322 }
323
Layout()324 void BrowserNonClientFrameViewAsh::Layout() {
325 // The header must be laid out before computing |painted_height| because the
326 // computation of |painted_height| for app and popup windows depends on the
327 // position of the window controls.
328 header_painter_->LayoutHeader();
329
330 int painted_height = 0;
331 if (browser_view()->IsTabStripVisible()) {
332 painted_height = GetTopInset() +
333 browser_view()->tabstrip()->GetPreferredSize().height();
334 } else if (browser_view()->IsToolbarVisible()) {
335 // Paint the header so that it overlaps with the top few pixels of the
336 // toolbar because the top few pixels of the toolbar are not opaque.
337 painted_height = GetTopInset() + kFrameShadowThickness * 2;
338 } else {
339 painted_height = GetTopInset();
340 }
341 header_painter_->SetHeaderHeightForPainting(painted_height);
342 if (avatar_button()) {
343 LayoutAvatar();
344 header_painter_->UpdateLeftViewXInset(avatar_button()->bounds().right());
345 } else {
346 header_painter_->UpdateLeftViewXInset(
347 ash::HeaderPainterUtil::GetDefaultLeftViewXInset());
348 }
349 BrowserNonClientFrameView::Layout();
350 }
351
GetClassName() const352 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
353 return kViewClassName;
354 }
355
GetAccessibleState(ui::AXViewState * state)356 void BrowserNonClientFrameViewAsh::GetAccessibleState(
357 ui::AXViewState* state) {
358 state->role = ui::AX_ROLE_TITLE_BAR;
359 }
360
GetMinimumSize() const361 gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() const {
362 gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize());
363 int min_width = std::max(header_painter_->GetMinimumHeaderWidth(),
364 min_client_view_size.width());
365 if (browser_view()->IsTabStripVisible()) {
366 // Ensure that the minimum width is enough to hold a minimum width tab strip
367 // at its usual insets.
368 int min_tabstrip_width =
369 browser_view()->tabstrip()->GetMinimumSize().width();
370 min_width = std::max(min_width,
371 min_tabstrip_width + GetTabStripLeftInset() + GetTabStripRightInset());
372 }
373 return gfx::Size(min_width, min_client_view_size.height());
374 }
375
376 void BrowserNonClientFrameViewAsh::
ChildPreferredSizeChanged(views::View * child)377 ChildPreferredSizeChanged(views::View* child) {
378 // FrameCaptionButtonContainerView animates the visibility changes in
379 // UpdateSizeButtonVisibility(false). Due to this a new size is not available
380 // until the completion of the animation. Layout in response to the preferred
381 // size changes.
382 if (child != caption_button_container_)
383 return;
384 frame()->GetRootView()->Layout();
385 }
386
387 ///////////////////////////////////////////////////////////////////////////////
388 // ash::ShellObserver:
389
OnMaximizeModeStarted()390 void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
391 caption_button_container_->UpdateSizeButtonVisibility();
392 InvalidateLayout();
393 frame()->client_view()->InvalidateLayout();
394 frame()->GetRootView()->Layout();
395 }
396
OnMaximizeModeEnded()397 void BrowserNonClientFrameViewAsh::OnMaximizeModeEnded() {
398 caption_button_container_->UpdateSizeButtonVisibility();
399 InvalidateLayout();
400 frame()->client_view()->InvalidateLayout();
401 frame()->GetRootView()->Layout();
402 }
403
404 ///////////////////////////////////////////////////////////////////////////////
405 // chrome::TabIconViewModel:
406
ShouldTabIconViewAnimate() const407 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
408 // This function is queried during the creation of the window as the
409 // TabIconView we host is initialized, so we need to NULL check the selected
410 // WebContents because in this condition there is not yet a selected tab.
411 content::WebContents* current_tab = browser_view()->GetActiveWebContents();
412 return current_tab ? current_tab->IsLoading() : false;
413 }
414
GetFaviconForTabIconView()415 gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
416 views::WidgetDelegate* delegate = frame()->widget_delegate();
417 if (!delegate)
418 return gfx::ImageSkia();
419 return delegate->GetWindowIcon();
420 }
421
422 ///////////////////////////////////////////////////////////////////////////////
423 // CommandObserver:
424
EnabledStateChangedForCommand(int id,bool enabled)425 void BrowserNonClientFrameViewAsh::EnabledStateChangedForCommand(int id,
426 bool enabled) {
427 DCHECK_EQ(IDC_BACK, id);
428 UpdateBackButtonState(enabled);
429 }
430
431 ///////////////////////////////////////////////////////////////////////////////
432 // views::ButtonListener:
433
ButtonPressed(views::Button * sender,const ui::Event & event)434 void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
435 const ui::Event& event) {
436 DCHECK_EQ(sender, web_app_back_button_);
437 chrome::ExecuteCommand(browser_view()->browser(), IDC_BACK);
438 }
439
440 ///////////////////////////////////////////////////////////////////////////////
441 // BrowserNonClientFrameViewAsh, private:
442
443 // views::NonClientFrameView:
DoesIntersectRect(const views::View * target,const gfx::Rect & rect) const444 bool BrowserNonClientFrameViewAsh::DoesIntersectRect(
445 const views::View* target,
446 const gfx::Rect& rect) const {
447 CHECK_EQ(target, this);
448 if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) {
449 // |rect| is outside BrowserNonClientFrameViewAsh's bounds.
450 return false;
451 }
452
453 TabStrip* tabstrip = browser_view()->tabstrip();
454 if (tabstrip && browser_view()->IsTabStripVisible()) {
455 // Claim |rect| only if it is above the bottom of the tabstrip in a non-tab
456 // portion.
457 gfx::RectF rect_in_tabstrip_coords_f(rect);
458 View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
459 gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
460 rect_in_tabstrip_coords_f);
461
462 if (rect_in_tabstrip_coords.y() > tabstrip->height())
463 return false;
464
465 return !tabstrip->HitTestRect(rect_in_tabstrip_coords) ||
466 tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
467 }
468
469 // Claim |rect| if it is above the top of the topmost view in the client area.
470 return rect.y() < GetTopInset();
471 }
472
GetTabStripLeftInset() const473 int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
474 return avatar_button() ? kAvatarSideSpacing +
475 browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
476 kTabstripLeftSpacing;
477 }
478
GetTabStripRightInset() const479 int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
480 return caption_button_container_->GetPreferredSize().width() +
481 kTabstripRightSpacing;
482 }
483
UseImmersiveLightbarHeaderStyle() const484 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
485 ImmersiveModeController* immersive_controller =
486 browser_view()->immersive_mode_controller();
487 return immersive_controller->IsEnabled() &&
488 !immersive_controller->IsRevealed() &&
489 browser_view()->IsTabStripVisible();
490 }
491
UsePackagedAppHeaderStyle() const492 bool BrowserNonClientFrameViewAsh::UsePackagedAppHeaderStyle() const {
493 // Use the packaged app style for apps that aren't using the newer WebApp
494 // style.
495 return browser_view()->browser()->is_app() && !UseWebAppHeaderStyle();
496 }
497
UseWebAppHeaderStyle() const498 bool BrowserNonClientFrameViewAsh::UseWebAppHeaderStyle() const {
499 // Use of the experimental WebApp header style is guarded with the
500 // streamlined hosted app style.
501 return browser_view()->browser()->is_app() &&
502 extensions::util::IsStreamlinedHostedAppsEnabled();
503 }
504
LayoutAvatar()505 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
506 DCHECK(avatar_button());
507 #if !defined(OS_CHROMEOS)
508 // ChromeOS shows avatar on V1 app.
509 DCHECK(browser_view()->IsTabStripVisible());
510 #endif
511 gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
512
513 int avatar_bottom = GetTopInset() +
514 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
515 int avatar_restored_y = avatar_bottom - incognito_icon.height();
516 int avatar_y =
517 (browser_view()->IsTabStripVisible() &&
518 (frame()->IsMaximized() || frame()->IsFullscreen())) ?
519 GetTopInset() + kContentShadowHeight : avatar_restored_y;
520
521 // Hide the incognito icon in immersive fullscreen when the tab light bar is
522 // visible because the header is too short for the icognito icon to be
523 // recognizable.
524 bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
525 int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
526
527 gfx::Rect avatar_bounds(kAvatarSideSpacing,
528 avatar_y,
529 incognito_icon.width(),
530 avatar_height);
531 avatar_button()->SetBoundsRect(avatar_bounds);
532 avatar_button()->SetVisible(avatar_visible);
533 }
534
ShouldPaint() const535 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
536 if (!frame()->IsFullscreen())
537 return true;
538
539 // We need to paint when in immersive fullscreen and either:
540 // - The top-of-window views are revealed.
541 // - The lightbar style tabstrip is visible.
542 ImmersiveModeController* immersive_mode_controller =
543 browser_view()->immersive_mode_controller();
544 return immersive_mode_controller->IsEnabled() &&
545 (immersive_mode_controller->IsRevealed() ||
546 UseImmersiveLightbarHeaderStyle());
547 }
548
PaintImmersiveLightbarStyleHeader(gfx::Canvas * canvas)549 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
550 gfx::Canvas* canvas) {
551 // The light bar header is not themed because theming it does not look good.
552 canvas->FillRect(
553 gfx::Rect(width(), header_painter_->GetHeaderHeightForPainting()),
554 SK_ColorBLACK);
555 }
556
PaintToolbarBackground(gfx::Canvas * canvas)557 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
558 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
559 if (toolbar_bounds.IsEmpty())
560 return;
561 gfx::Point toolbar_origin(toolbar_bounds.origin());
562 View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
563 toolbar_bounds.set_origin(toolbar_origin);
564
565 int x = toolbar_bounds.x();
566 int w = toolbar_bounds.width();
567 int y = toolbar_bounds.y();
568 int h = toolbar_bounds.height();
569
570 // Gross hack: We split the toolbar images into two pieces, since sometimes
571 // (popup mode) the toolbar isn't tall enough to show the whole image. The
572 // split happens between the top shadow section and the bottom gradient
573 // section so that we never break the gradient.
574 // NOTE(pkotwicz): If the computation for |bottom_y| is changed, Layout() must
575 // be changed as well.
576 int split_point = kFrameShadowThickness * 2;
577 int bottom_y = y + split_point;
578 ui::ThemeProvider* tp = GetThemeProvider();
579 int bottom_edge_height = h - split_point;
580
581 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
582 tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
583
584 // Paint the main toolbar image. Since this image is also used to draw the
585 // tab background, we must use the tab strip offset to compute the image
586 // source y position. If you have to debug this code use an image editor
587 // to paint a diagonal line through the toolbar image and ensure it lines up
588 // across the tab and toolbar.
589 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
590 canvas->TileImageInt(
591 *theme_toolbar,
592 x + GetThemeBackgroundXInset(),
593 bottom_y - GetTopInset(),
594 x, bottom_y,
595 w, theme_toolbar->height());
596
597 // The content area line has a shadow that extends a couple of pixels above
598 // the toolbar bounds.
599 const int kContentShadowHeight = 2;
600 gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
601 canvas->TileImageInt(*toolbar_top,
602 0, 0,
603 x, y - kContentShadowHeight,
604 w, split_point + kContentShadowHeight + 1);
605
606 // Draw the "lightening" shade line around the edges of the toolbar.
607 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
608 canvas->TileImageInt(*toolbar_left,
609 0, 0,
610 x + kClientEdgeThickness,
611 y + kClientEdgeThickness + kContentShadowHeight,
612 toolbar_left->width(), theme_toolbar->height());
613 gfx::ImageSkia* toolbar_right =
614 tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
615 canvas->TileImageInt(*toolbar_right,
616 0, 0,
617 w - toolbar_right->width() - 2 * kClientEdgeThickness,
618 y + kClientEdgeThickness + kContentShadowHeight,
619 toolbar_right->width(), theme_toolbar->height());
620
621 // Draw the content/toolbar separator.
622 canvas->FillRect(
623 gfx::Rect(x + kClientEdgeThickness,
624 toolbar_bounds.bottom() - kClientEdgeThickness,
625 w - (2 * kClientEdgeThickness),
626 kClientEdgeThickness),
627 ThemeProperties::GetDefaultColor(
628 ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
629 }
630
PaintContentEdge(gfx::Canvas * canvas)631 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
632 DCHECK(!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle());
633 canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(),
634 width(), kClientEdgeThickness),
635 ThemeProperties::GetDefaultColor(
636 ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
637 }
638
UpdateBackButtonState(bool enabled)639 void BrowserNonClientFrameViewAsh::UpdateBackButtonState(bool enabled) {
640 web_app_back_button_->SetState(enabled ? views::Button::STATE_NORMAL
641 : views::Button::STATE_DISABLED);
642 }
643