1 // Copyright (c) 2011 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/opaque_browser_frame_view.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/themes/theme_service.h"
10 #include "chrome/browser/ui/views/frame/browser_frame.h"
11 #include "chrome/browser/ui/views/frame/browser_view.h"
12 #include "chrome/browser/ui/views/tabs/tab_strip.h"
13 #include "chrome/browser/ui/views/toolbar_view.h"
14 #include "content/browser/tab_contents/tab_contents.h"
15 #include "grit/app_resources.h"
16 #include "grit/chromium_strings.h"
17 #include "grit/generated_resources.h"
18 #include "grit/theme_resources.h"
19 #include "ui/base/accessibility/accessible_view_state.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/base/theme_provider.h"
23 #include "ui/gfx/canvas_skia.h"
24 #include "ui/gfx/font.h"
25 #include "ui/gfx/path.h"
26 #include "views/controls/button/image_button.h"
27 #include "views/controls/image_view.h"
28 #include "views/widget/root_view.h"
29 #include "views/window/window.h"
30 #include "views/window/window_resources.h"
31 #include "views/window/window_shape.h"
32
33 #if defined(OS_LINUX)
34 #include "views/window/hit_test.h"
35 #endif
36
37 namespace {
38 // The frame border is only visible in restored mode and is hardcoded to 4 px on
39 // each side regardless of the system window border size.
40 const int kFrameBorderThickness = 4;
41 // Besides the frame border, there's another 11 px of empty space atop the
42 // window in restored mode, to use to drag the window around.
43 const int kNonClientRestoredExtraThickness = 11;
44 // While resize areas on Windows are normally the same size as the window
45 // borders, our top area is shrunk by 1 px to make it easier to move the window
46 // around with our thinner top grabbable strip. (Incidentally, our side and
47 // bottom resize areas don't match the frame border thickness either -- they
48 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
49 const int kTopResizeAdjust = 1;
50 // In the window corners, the resize areas don't actually expand bigger, but the
51 // 16 px at the end of each edge triggers diagonal resizing.
52 const int kResizeAreaCornerSize = 16;
53 // The titlebar never shrinks too short to show the caption button plus some
54 // padding below it.
55 const int kCaptionButtonHeightWithPadding = 19;
56 // The content left/right images have a shadow built into them.
57 const int kContentEdgeShadowThickness = 2;
58 // The titlebar has a 2 px 3D edge along the top and bottom.
59 const int kTitlebarTopAndBottomEdgeThickness = 2;
60 // The icon is inset 2 px from the left frame border.
61 const int kIconLeftSpacing = 2;
62 // The icon never shrinks below 16 px on a side.
63 const int kIconMinimumSize = 16;
64 // There is a 4 px gap between the icon and the title text.
65 const int kIconTitleSpacing = 4;
66 // There is a 5 px gap between the title text and the caption buttons.
67 const int kTitleLogoSpacing = 5;
68 // The OTR avatar ends 2 px above the bottom of the tabstrip (which, given the
69 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
70 // user).
71 const int kOTRBottomSpacing = 2;
72 // There are 2 px on each side of the OTR avatar (between the frame border and
73 // it on the left, and between it and the tabstrip on the right).
74 const int kOTRSideSpacing = 2;
75 // The top 1 px of the tabstrip is shadow; in maximized mode we push this off
76 // the top of the screen so the tabs appear flush against the screen edge.
77 const int kTabstripTopShadowThickness = 1;
78 // In restored mode, the New Tab button isn't at the same height as the caption
79 // buttons, but the space will look cluttered if it actually slides under them,
80 // so we stop it when the gap between the two is down to 5 px.
81 const int kNewTabCaptionRestoredSpacing = 5;
82 // In maximized mode, where the New Tab button and the caption buttons are at
83 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid
84 // looking too cluttered.
85 const int kNewTabCaptionMaximizedSpacing = 16;
86 // How far to indent the tabstrip from the left side of the screen when there
87 // is no OTR icon.
88 const int kTabStripIndent = 1;
89 // Inset from the top of the toolbar/tabstrip to the shadow. Used only for
90 // vertical tabs.
91 const int kVerticalTabBorderInset = 3;
92 }
93
94 ///////////////////////////////////////////////////////////////////////////////
95 // OpaqueBrowserFrameView, public:
96
OpaqueBrowserFrameView(BrowserFrame * frame,BrowserView * browser_view)97 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
98 BrowserView* browser_view)
99 : BrowserNonClientFrameView(),
100 ALLOW_THIS_IN_INITIALIZER_LIST(
101 minimize_button_(new views::ImageButton(this))),
102 ALLOW_THIS_IN_INITIALIZER_LIST(
103 maximize_button_(new views::ImageButton(this))),
104 ALLOW_THIS_IN_INITIALIZER_LIST(
105 restore_button_(new views::ImageButton(this))),
106 ALLOW_THIS_IN_INITIALIZER_LIST(
107 close_button_(new views::ImageButton(this))),
108 window_icon_(NULL),
109 frame_(frame),
110 browser_view_(browser_view) {
111 ui::ThemeProvider* tp = frame_->GetThemeProviderForFrame();
112 SkColor color = tp->GetColor(ThemeService::COLOR_BUTTON_BACKGROUND);
113 SkBitmap* background =
114 tp->GetBitmapNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND);
115 minimize_button_->SetImage(views::CustomButton::BS_NORMAL,
116 tp->GetBitmapNamed(IDR_MINIMIZE));
117 minimize_button_->SetImage(views::CustomButton::BS_HOT,
118 tp->GetBitmapNamed(IDR_MINIMIZE_H));
119 minimize_button_->SetImage(views::CustomButton::BS_PUSHED,
120 tp->GetBitmapNamed(IDR_MINIMIZE_P));
121 if (browser_view_->IsBrowserTypeNormal()) {
122 minimize_button_->SetBackground(color, background,
123 tp->GetBitmapNamed(IDR_MINIMIZE_BUTTON_MASK));
124 }
125 minimize_button_->SetAccessibleName(
126 l10n_util::GetStringUTF16(IDS_ACCNAME_MINIMIZE));
127 AddChildView(minimize_button_);
128
129 maximize_button_->SetImage(views::CustomButton::BS_NORMAL,
130 tp->GetBitmapNamed(IDR_MAXIMIZE));
131 maximize_button_->SetImage(views::CustomButton::BS_HOT,
132 tp->GetBitmapNamed(IDR_MAXIMIZE_H));
133 maximize_button_->SetImage(views::CustomButton::BS_PUSHED,
134 tp->GetBitmapNamed(IDR_MAXIMIZE_P));
135 if (browser_view_->IsBrowserTypeNormal()) {
136 maximize_button_->SetBackground(color, background,
137 tp->GetBitmapNamed(IDR_MAXIMIZE_BUTTON_MASK));
138 }
139 maximize_button_->SetAccessibleName(
140 l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
141 AddChildView(maximize_button_);
142
143 restore_button_->SetImage(views::CustomButton::BS_NORMAL,
144 tp->GetBitmapNamed(IDR_RESTORE));
145 restore_button_->SetImage(views::CustomButton::BS_HOT,
146 tp->GetBitmapNamed(IDR_RESTORE_H));
147 restore_button_->SetImage(views::CustomButton::BS_PUSHED,
148 tp->GetBitmapNamed(IDR_RESTORE_P));
149 if (browser_view_->IsBrowserTypeNormal()) {
150 restore_button_->SetBackground(color, background,
151 tp->GetBitmapNamed(IDR_RESTORE_BUTTON_MASK));
152 }
153 restore_button_->SetAccessibleName(
154 l10n_util::GetStringUTF16(IDS_ACCNAME_RESTORE));
155 AddChildView(restore_button_);
156
157 close_button_->SetImage(views::CustomButton::BS_NORMAL,
158 tp->GetBitmapNamed(IDR_CLOSE));
159 close_button_->SetImage(views::CustomButton::BS_HOT,
160 tp->GetBitmapNamed(IDR_CLOSE_H));
161 close_button_->SetImage(views::CustomButton::BS_PUSHED,
162 tp->GetBitmapNamed(IDR_CLOSE_P));
163 if (browser_view_->IsBrowserTypeNormal()) {
164 close_button_->SetBackground(color, background,
165 tp->GetBitmapNamed(IDR_CLOSE_BUTTON_MASK));
166 }
167 close_button_->SetAccessibleName(
168 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
169 AddChildView(close_button_);
170
171 // Initializing the TabIconView is expensive, so only do it if we need to.
172 if (browser_view_->ShouldShowWindowIcon()) {
173 window_icon_ = new TabIconView(this);
174 window_icon_->set_is_light(true);
175 AddChildView(window_icon_);
176 window_icon_->Update();
177 }
178 }
179
~OpaqueBrowserFrameView()180 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
181 }
182
183 ///////////////////////////////////////////////////////////////////////////////
184 // OpaqueBrowserFrameView, protected:
185
GetReservedHeight() const186 int OpaqueBrowserFrameView::GetReservedHeight() const {
187 return 0;
188 }
189
GetBoundsForReservedArea() const190 gfx::Rect OpaqueBrowserFrameView::GetBoundsForReservedArea() const {
191 gfx::Rect client_view_bounds = CalculateClientAreaBounds(width(), height());
192 return gfx::Rect(
193 client_view_bounds.x(),
194 client_view_bounds.y() + client_view_bounds.height(),
195 client_view_bounds.width(),
196 GetReservedHeight());
197 }
198
NonClientTopBorderHeight(bool restored,bool ignore_vertical_tabs) const199 int OpaqueBrowserFrameView::NonClientTopBorderHeight(
200 bool restored,
201 bool ignore_vertical_tabs) const {
202 views::Window* window = frame_->GetWindow();
203 views::WindowDelegate* delegate = window->window_delegate();
204 // |delegate| may be NULL if called from callback of InputMethodChanged while
205 // a window is being destroyed.
206 // See more discussion at http://crosbug.com/8958
207 if ((delegate && delegate->ShouldShowWindowTitle()) ||
208 (browser_view_->IsTabStripVisible() && !ignore_vertical_tabs &&
209 browser_view_->UseVerticalTabs())) {
210 return std::max(FrameBorderThickness(restored) + IconSize(),
211 CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) +
212 TitlebarBottomThickness(restored);
213 }
214
215 return FrameBorderThickness(restored) -
216 ((browser_view_->IsTabStripVisible() && !restored &&
217 window->IsMaximized()) ? kTabstripTopShadowThickness : 0);
218 }
219
220 ///////////////////////////////////////////////////////////////////////////////
221 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
222
GetBoundsForTabStrip(views::View * tabstrip) const223 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
224 views::View* tabstrip) const {
225 if (!tabstrip)
226 return gfx::Rect();
227
228 if (browser_view_->UseVerticalTabs()) {
229 gfx::Size ps = tabstrip->GetPreferredSize();
230 return gfx::Rect(NonClientBorderThickness(),
231 NonClientTopBorderHeight(false, false), ps.width(),
232 browser_view_->height());
233 }
234
235 int tabstrip_x = browser_view_->ShouldShowOffTheRecordAvatar() ?
236 (otr_avatar_bounds_.right() + kOTRSideSpacing) :
237 NonClientBorderThickness() + kTabStripIndent;
238
239 int tabstrip_width = minimize_button_->x() - tabstrip_x -
240 (frame_->GetWindow()->IsMaximized() ?
241 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing);
242 return gfx::Rect(tabstrip_x, GetHorizontalTabStripVerticalOffset(false),
243 std::max(0, tabstrip_width), tabstrip->GetPreferredSize().height());
244 }
245
GetHorizontalTabStripVerticalOffset(bool restored) const246 int OpaqueBrowserFrameView::GetHorizontalTabStripVerticalOffset(
247 bool restored) const {
248 return NonClientTopBorderHeight(restored, true) + ((!restored &&
249 (frame_->GetWindow()->IsMaximized() ||
250 frame_->GetWindow()->IsFullscreen())) ?
251 0 : kNonClientRestoredExtraThickness);
252 }
253
UpdateThrobber(bool running)254 void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
255 if (window_icon_)
256 window_icon_->Update();
257 }
258
GetMinimumSize()259 gfx::Size OpaqueBrowserFrameView::GetMinimumSize() {
260 gfx::Size min_size(browser_view_->GetMinimumSize());
261 int border_thickness = NonClientBorderThickness();
262 min_size.Enlarge(2 * border_thickness,
263 NonClientTopBorderHeight(false, false) + border_thickness);
264
265 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
266 int min_titlebar_width = (2 * FrameBorderThickness(false)) +
267 kIconLeftSpacing +
268 (delegate && delegate->ShouldShowWindowIcon() ?
269 (IconSize() + kTitleLogoSpacing) : 0);
270 #if !defined(OS_CHROMEOS)
271 min_titlebar_width +=
272 minimize_button_->GetMinimumSize().width() +
273 restore_button_->GetMinimumSize().width() +
274 close_button_->GetMinimumSize().width();
275 #endif
276 min_size.set_width(std::max(min_size.width(), min_titlebar_width));
277 return min_size;
278 }
279
280 ///////////////////////////////////////////////////////////////////////////////
281 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
282
GetBoundsForClientView() const283 gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
284 return client_view_bounds_;
285 }
286
AlwaysUseNativeFrame() const287 bool OpaqueBrowserFrameView::AlwaysUseNativeFrame() const {
288 return frame_->AlwaysUseNativeFrame();
289 }
290
AlwaysUseCustomFrame() const291 bool OpaqueBrowserFrameView::AlwaysUseCustomFrame() const {
292 return true;
293 }
294
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const295 gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
296 const gfx::Rect& client_bounds) const {
297 int top_height = NonClientTopBorderHeight(false, false);
298 int border_thickness = NonClientBorderThickness();
299 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
300 std::max(0, client_bounds.y() - top_height),
301 client_bounds.width() + (2 * border_thickness),
302 client_bounds.height() + top_height + border_thickness);
303 }
304
NonClientHitTest(const gfx::Point & point)305 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
306 if (!bounds().Contains(point))
307 return HTNOWHERE;
308
309 int frame_component =
310 frame_->GetWindow()->client_view()->NonClientHitTest(point);
311
312 // See if we're in the sysmenu region. We still have to check the tabstrip
313 // first so that clicks in a tab don't get treated as sysmenu clicks.
314 gfx::Rect sysmenu_rect(IconBounds());
315 // In maximized mode we extend the rect to the screen corner to take advantage
316 // of Fitts' Law.
317 if (frame_->GetWindow()->IsMaximized())
318 sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
319 sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
320 if (sysmenu_rect.Contains(point))
321 return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
322
323 if (frame_component != HTNOWHERE)
324 return frame_component;
325
326 // Then see if the point is within any of the window controls.
327 if (close_button_->IsVisible() &&
328 close_button_->GetMirroredBounds().Contains(point))
329 return HTCLOSE;
330 if (restore_button_->IsVisible() &&
331 restore_button_->GetMirroredBounds().Contains(point))
332 return HTMAXBUTTON;
333 if (maximize_button_->IsVisible() &&
334 maximize_button_->GetMirroredBounds().Contains(point))
335 return HTMAXBUTTON;
336 if (minimize_button_->IsVisible() &&
337 minimize_button_->GetMirroredBounds().Contains(point))
338 return HTMINBUTTON;
339
340 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
341 if (!delegate) {
342 LOG(WARNING) << "delegate is NULL, returning safe default.";
343 return HTCAPTION;
344 }
345 int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
346 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
347 delegate->CanResize());
348 // Fall back to the caption if no other component matches.
349 return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
350 }
351
GetWindowMask(const gfx::Size & size,gfx::Path * window_mask)352 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
353 gfx::Path* window_mask) {
354 DCHECK(window_mask);
355
356 if (frame_->GetWindow()->IsMaximized() || frame_->GetWindow()->IsFullscreen())
357 return;
358
359 views::GetDefaultWindowMask(size, window_mask);
360 }
361
EnableClose(bool enable)362 void OpaqueBrowserFrameView::EnableClose(bool enable) {
363 close_button_->SetEnabled(enable);
364 }
365
ResetWindowControls()366 void OpaqueBrowserFrameView::ResetWindowControls() {
367 restore_button_->SetState(views::CustomButton::BS_NORMAL);
368 minimize_button_->SetState(views::CustomButton::BS_NORMAL);
369 maximize_button_->SetState(views::CustomButton::BS_NORMAL);
370 // The close button isn't affected by this constraint.
371 }
372
UpdateWindowIcon()373 void OpaqueBrowserFrameView::UpdateWindowIcon() {
374 window_icon_->SchedulePaint();
375 }
376
377 ///////////////////////////////////////////////////////////////////////////////
378 // OpaqueBrowserFrameView, views::View overrides:
379
OnPaint(gfx::Canvas * canvas)380 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
381 views::Window* window = frame_->GetWindow();
382 if (window->IsFullscreen())
383 return; // Nothing is visible, so don't bother to paint.
384
385 if (window->IsMaximized())
386 PaintMaximizedFrameBorder(canvas);
387 else
388 PaintRestoredFrameBorder(canvas);
389 PaintTitleBar(canvas);
390 if (browser_view_->IsToolbarVisible())
391 PaintToolbarBackground(canvas);
392 if (browser_view_->ShouldShowOffTheRecordAvatar())
393 PaintOTRAvatar(canvas);
394 if (!window->IsMaximized())
395 PaintRestoredClientEdge(canvas);
396 }
397
Layout()398 void OpaqueBrowserFrameView::Layout() {
399 LayoutWindowControls();
400 LayoutTitleBar();
401 LayoutOTRAvatar();
402 client_view_bounds_ = CalculateClientAreaBounds(width(), height());
403 }
404
HitTest(const gfx::Point & l) const405 bool OpaqueBrowserFrameView::HitTest(const gfx::Point& l) const {
406 // If the point is outside the bounds of the client area, claim it.
407 bool in_nonclient = NonClientFrameView::HitTest(l);
408 if (in_nonclient)
409 return in_nonclient;
410
411 // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
412 if (!browser_view_->tabstrip())
413 return false;
414 gfx::Rect tabstrip_bounds(browser_view_->tabstrip()->bounds());
415 gfx::Point tabstrip_origin(tabstrip_bounds.origin());
416 View::ConvertPointToView(frame_->GetWindow()->client_view(),
417 this, &tabstrip_origin);
418 tabstrip_bounds.set_origin(tabstrip_origin);
419 if (browser_view_->UseVerticalTabs() ?
420 (l.x() > tabstrip_bounds.right()) : (l.y() > tabstrip_bounds.bottom()))
421 return false;
422
423 // We convert from our parent's coordinates since we assume we fill its bounds
424 // completely. We need to do this since we're not a parent of the tabstrip,
425 // meaning ConvertPointToView would otherwise return something bogus.
426 gfx::Point browser_view_point(l);
427 View::ConvertPointToView(parent(), browser_view_, &browser_view_point);
428 return browser_view_->IsPositionInWindowCaption(browser_view_point);
429 }
430
GetAccessibleState(ui::AccessibleViewState * state)431 void OpaqueBrowserFrameView::GetAccessibleState(
432 ui::AccessibleViewState* state) {
433 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
434 }
435
436 ///////////////////////////////////////////////////////////////////////////////
437 // OpaqueBrowserFrameView, views::ButtonListener implementation:
438
ButtonPressed(views::Button * sender,const views::Event & event)439 void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
440 const views::Event& event) {
441 views::Window* window = frame_->GetWindow();
442 if (sender == minimize_button_)
443 window->Minimize();
444 else if (sender == maximize_button_)
445 window->Maximize();
446 else if (sender == restore_button_)
447 window->Restore();
448 else if (sender == close_button_)
449 window->CloseWindow();
450 }
451
452 ///////////////////////////////////////////////////////////////////////////////
453 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
454
ShouldTabIconViewAnimate() const455 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
456 // This function is queried during the creation of the window as the
457 // TabIconView we host is initialized, so we need to NULL check the selected
458 // TabContents because in this condition there is not yet a selected tab.
459 TabContents* current_tab = browser_view_->GetSelectedTabContents();
460 return current_tab ? current_tab->is_loading() : false;
461 }
462
GetFaviconForTabIconView()463 SkBitmap OpaqueBrowserFrameView::GetFaviconForTabIconView() {
464 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
465 if (!delegate) {
466 LOG(WARNING) << "delegate is NULL, returning safe default.";
467 return SkBitmap();
468 }
469 return delegate->GetWindowIcon();
470 }
471
472 ///////////////////////////////////////////////////////////////////////////////
473 // OpaqueBrowserFrameView, private:
474
FrameBorderThickness(bool restored) const475 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
476 views::Window* window = frame_->GetWindow();
477 return (!restored && (window->IsMaximized() || window->IsFullscreen())) ?
478 0 : kFrameBorderThickness;
479 }
480
TopResizeHeight() const481 int OpaqueBrowserFrameView::TopResizeHeight() const {
482 return FrameBorderThickness(false) - kTopResizeAdjust;
483 }
484
NonClientBorderThickness() const485 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
486 // When we fill the screen, we don't show a client edge.
487 views::Window* window = frame_->GetWindow();
488 return FrameBorderThickness(false) +
489 ((window->IsMaximized() || window->IsFullscreen()) ?
490 0 : kClientEdgeThickness);
491 }
492
ModifyMaximizedFramePainting(int * theme_offset,SkBitmap ** left_corner,SkBitmap ** right_corner)493 void OpaqueBrowserFrameView::ModifyMaximizedFramePainting(
494 int* theme_offset, SkBitmap** left_corner, SkBitmap** right_corner) {
495 }
496
CaptionButtonY(bool restored) const497 int OpaqueBrowserFrameView::CaptionButtonY(bool restored) const {
498 // Maximized buttons start at window top so that even if their images aren't
499 // drawn flush with the screen edge, they still obey Fitts' Law.
500 return (!restored && frame_->GetWindow()->IsMaximized()) ?
501 FrameBorderThickness(false) : kFrameShadowThickness;
502 }
503
TitlebarBottomThickness(bool restored) const504 int OpaqueBrowserFrameView::TitlebarBottomThickness(bool restored) const {
505 return kTitlebarTopAndBottomEdgeThickness +
506 ((!restored && frame_->GetWindow()->IsMaximized()) ?
507 0 : kClientEdgeThickness);
508 }
509
IconSize() const510 int OpaqueBrowserFrameView::IconSize() const {
511 #if defined(OS_WIN)
512 // This metric scales up if either the titlebar height or the titlebar font
513 // size are increased.
514 return GetSystemMetrics(SM_CYSMICON);
515 #else
516 return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize);
517 #endif
518 }
519
IconBounds() const520 gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
521 int size = IconSize();
522 int frame_thickness = FrameBorderThickness(false);
523 int y;
524 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
525 if (delegate && (delegate->ShouldShowWindowIcon() ||
526 delegate->ShouldShowWindowTitle())) {
527 // Our frame border has a different "3D look" than Windows'. Theirs has a
528 // more complex gradient on the top that they push their icon/title below;
529 // then the maximized window cuts this off and the icon/title are centered
530 // in the remaining space. Because the apparent shape of our border is
531 // simpler, using the same positioning makes things look slightly uncentered
532 // with restored windows, so when the window is restored, instead of
533 // calculating the remaining space from below the frame border, we calculate
534 // from below the 3D edge.
535 int unavailable_px_at_top = frame_->GetWindow()->IsMaximized() ?
536 frame_thickness : kTitlebarTopAndBottomEdgeThickness;
537 // When the icon is shorter than the minimum space we reserve for the
538 // caption button, we vertically center it. We want to bias rounding to put
539 // extra space above the icon, since the 3D edge (+ client edge, for
540 // restored windows) below looks (to the eye) more like additional space
541 // than does the 3D edge (or nothing at all, for maximized windows) above;
542 // hence the +1.
543 y = unavailable_px_at_top + (NonClientTopBorderHeight(false, false) -
544 unavailable_px_at_top - size - TitlebarBottomThickness(false) + 1) / 2;
545 } else {
546 // For "browser mode" windows, we use the native positioning, which is just
547 // below the top frame border.
548 y = frame_thickness;
549 }
550 return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
551 }
552
PaintRestoredFrameBorder(gfx::Canvas * canvas)553 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
554 ui::ThemeProvider* tp = GetThemeProvider();
555
556 SkBitmap* top_left_corner = tp->GetBitmapNamed(IDR_WINDOW_TOP_LEFT_CORNER);
557 SkBitmap* top_right_corner =
558 tp->GetBitmapNamed(IDR_WINDOW_TOP_RIGHT_CORNER);
559 SkBitmap* top_edge = tp->GetBitmapNamed(IDR_WINDOW_TOP_CENTER);
560 SkBitmap* right_edge = tp->GetBitmapNamed(IDR_WINDOW_RIGHT_SIDE);
561 SkBitmap* left_edge = tp->GetBitmapNamed(IDR_WINDOW_LEFT_SIDE);
562 SkBitmap* bottom_left_corner =
563 tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER);
564 SkBitmap* bottom_right_corner =
565 tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER);
566 SkBitmap* bottom_edge = tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_CENTER);
567
568 // Window frame mode and color.
569 SkBitmap* theme_frame;
570 SkColor frame_color;
571 if (!browser_view_->IsBrowserTypeNormal()) {
572 // Never theme app and popup windows.
573 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
574 bool is_incognito = browser_view_->IsOffTheRecord();
575 if (ShouldPaintAsActive()) {
576 theme_frame = rb.GetBitmapNamed(is_incognito ?
577 IDR_THEME_FRAME_INCOGNITO : IDR_FRAME);
578 frame_color = is_incognito ?
579 ResourceBundle::frame_color_incognito :
580 ResourceBundle::frame_color;
581 } else {
582 theme_frame = rb.GetBitmapNamed(is_incognito ?
583 IDR_THEME_FRAME_INCOGNITO_INACTIVE :
584 IDR_THEME_FRAME_INACTIVE);
585 frame_color = is_incognito ?
586 ResourceBundle::frame_color_incognito_inactive :
587 ResourceBundle::frame_color_inactive;
588 }
589 } else if (!browser_view_->IsOffTheRecord()) {
590 if (ShouldPaintAsActive()) {
591 theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME);
592 frame_color = tp->GetColor(ThemeService::COLOR_FRAME);
593 } else {
594 theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INACTIVE);
595 frame_color = tp->GetColor(ThemeService::COLOR_FRAME_INACTIVE);
596 }
597 } else if (ShouldPaintAsActive()) {
598 theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INCOGNITO);
599 frame_color = tp->GetColor(ThemeService::COLOR_FRAME_INCOGNITO);
600 } else {
601 theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INCOGNITO_INACTIVE);
602 frame_color = tp->GetColor(
603 ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE);
604 }
605
606 // Fill with the frame color first so we have a constant background for
607 // areas not covered by the theme image.
608 canvas->FillRectInt(frame_color, 0, 0, width(), theme_frame->height());
609 // Now fill down the sides.
610 canvas->FillRectInt(frame_color, 0, theme_frame->height(), left_edge->width(),
611 height() - theme_frame->height());
612 canvas->FillRectInt(frame_color, width() - right_edge->width(),
613 theme_frame->height(), right_edge->width(),
614 height() - theme_frame->height());
615 // Now fill the bottom area.
616 canvas->FillRectInt(frame_color, left_edge->width(),
617 height() - bottom_edge->height(),
618 width() - left_edge->width() - right_edge->width(),
619 bottom_edge->height());
620
621 // Draw the theme frame.
622 canvas->TileImageInt(*theme_frame, 0, 0, width(), theme_frame->height());
623
624 // Draw the theme frame overlay.
625 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
626 browser_view_->IsBrowserTypeNormal() &&
627 !browser_view_->IsOffTheRecord()) {
628 canvas->DrawBitmapInt(*tp->GetBitmapNamed(ShouldPaintAsActive() ?
629 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE), 0, 0);
630 }
631
632 // Top.
633 int top_left_height = std::min(top_left_corner->height(),
634 height() - bottom_left_corner->height());
635 canvas->DrawBitmapInt(*top_left_corner, 0, 0, top_left_corner->width(),
636 top_left_height, 0, 0, top_left_corner->width(), top_left_height, false);
637 canvas->TileImageInt(*top_edge, top_left_corner->width(), 0,
638 width() - top_right_corner->width(), top_edge->height());
639 int top_right_height = std::min(top_right_corner->height(),
640 height() - bottom_right_corner->height());
641 canvas->DrawBitmapInt(*top_right_corner, 0, 0, top_right_corner->width(),
642 top_right_height, width() - top_right_corner->width(), 0,
643 top_right_corner->width(), top_right_height, false);
644 // Note: When we don't have a toolbar, we need to draw some kind of bottom
645 // edge here. Because the App Window graphics we use for this have an
646 // attached client edge and their sizing algorithm is a little involved, we do
647 // all this in PaintRestoredClientEdge().
648
649 // Right.
650 canvas->TileImageInt(*right_edge, width() - right_edge->width(),
651 top_right_height, right_edge->width(),
652 height() - top_right_height - bottom_right_corner->height());
653
654 // Bottom.
655 canvas->DrawBitmapInt(*bottom_right_corner,
656 width() - bottom_right_corner->width(),
657 height() - bottom_right_corner->height());
658 canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(),
659 height() - bottom_edge->height(),
660 width() - bottom_left_corner->width() - bottom_right_corner->width(),
661 bottom_edge->height());
662 canvas->DrawBitmapInt(*bottom_left_corner, 0,
663 height() - bottom_left_corner->height());
664
665 // Left.
666 canvas->TileImageInt(*left_edge, 0, top_left_height, left_edge->width(),
667 height() - top_left_height - bottom_left_corner->height());
668 }
669
PaintMaximizedFrameBorder(gfx::Canvas * canvas)670 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
671 ui::ThemeProvider* tp = GetThemeProvider();
672 views::Window* window = frame_->GetWindow();
673
674 // Window frame mode and color
675 SkBitmap* theme_frame;
676
677 // Allow customization of these attributes.
678 SkBitmap* left = NULL;
679 SkBitmap* right = NULL;
680 int top_offset = 0;
681 ModifyMaximizedFramePainting(&top_offset, &left, &right);
682
683 // Never theme app and popup windows.
684 if (!browser_view_->IsBrowserTypeNormal()) {
685 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
686 theme_frame = rb.GetBitmapNamed(ShouldPaintAsActive() ?
687 IDR_FRAME : IDR_FRAME_INACTIVE);
688 } else if (!browser_view_->IsOffTheRecord()) {
689 theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ?
690 IDR_THEME_FRAME : IDR_THEME_FRAME_INACTIVE);
691 } else {
692 theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ?
693 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME_INCOGNITO_INACTIVE);
694 }
695 // Draw the theme frame. It must be aligned with the tabstrip as if we were
696 // in restored mode. Note that the top of the tabstrip is
697 // kTabstripTopShadowThickness px off the top of the screen.
698 int theme_background_y = -(GetHorizontalTabStripVerticalOffset(true) +
699 kTabstripTopShadowThickness);
700 int left_offset = 0, right_offset = 0;
701
702 if (left || right) {
703 // If we have either a left or right we should have both.
704 DCHECK(left && right);
705 left_offset = left->width();
706 right_offset = right->width();
707 canvas->DrawBitmapInt(*left, 0, 0);
708 canvas->DrawBitmapInt(*right, width() - right_offset, 0);
709 }
710
711 canvas->TileImageInt(*theme_frame,
712 left_offset,
713 top_offset,
714 width() - (left_offset + right_offset),
715 theme_frame->height());
716 // Draw the theme frame overlay
717 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
718 browser_view_->IsBrowserTypeNormal()) {
719 SkBitmap* theme_overlay = tp->GetBitmapNamed(ShouldPaintAsActive() ?
720 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
721 canvas->DrawBitmapInt(*theme_overlay, 0, theme_background_y);
722 }
723
724 if (!browser_view_->IsToolbarVisible()) {
725 // There's no toolbar to edge the frame border, so we need to draw a bottom
726 // edge. The graphic we use for this has a built in client edge, so we clip
727 // it off the bottom.
728 SkBitmap* top_center =
729 tp->GetBitmapNamed(IDR_APP_TOP_CENTER);
730 int edge_height = top_center->height() - kClientEdgeThickness;
731 canvas->TileImageInt(*top_center, 0,
732 window->client_view()->y() - edge_height, width(), edge_height);
733 }
734 }
735
PaintTitleBar(gfx::Canvas * canvas)736 void OpaqueBrowserFrameView::PaintTitleBar(gfx::Canvas* canvas) {
737 // The window icon is painted by the TabIconView.
738 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
739 if (!delegate) {
740 LOG(WARNING) << "delegate is NULL";
741 return;
742 }
743 if (delegate->ShouldShowWindowTitle()) {
744 canvas->DrawStringInt(WideToUTF16Hack(delegate->GetWindowTitle()),
745 BrowserFrame::GetTitleFont(),
746 SK_ColorWHITE, GetMirroredXForRect(title_bounds_),
747 title_bounds_.y(), title_bounds_.width(), title_bounds_.height());
748 /* TODO(pkasting): If this window is active, we should also draw a drop
749 * shadow on the title. This is tricky, because we don't want to hardcode a
750 * shadow color (since we want to work with various themes), but we can't
751 * alpha-blend either (since the Windows text APIs don't really do this).
752 * So we'd need to sample the background color at the right location and
753 * synthesize a good shadow color. */
754 }
755 }
756
PaintToolbarBackground(gfx::Canvas * canvas)757 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
758 gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds());
759 if (toolbar_bounds.IsEmpty())
760 return;
761 gfx::Point toolbar_origin(toolbar_bounds.origin());
762 ConvertPointToView(browser_view_, this, &toolbar_origin);
763 toolbar_bounds.set_origin(toolbar_origin);
764
765 int x = toolbar_bounds.x();
766 int w = toolbar_bounds.width();
767 int y, h;
768 if (browser_view_->UseVerticalTabs()) {
769 gfx::Point tabstrip_origin(browser_view_->tabstrip()->bounds().origin());
770 ConvertPointToView(browser_view_, this, &tabstrip_origin);
771 y = tabstrip_origin.y() - kVerticalTabBorderInset;
772 h = toolbar_bounds.bottom() - y;
773 } else {
774 y = toolbar_bounds.y();
775 h = toolbar_bounds.bottom();
776 }
777
778 // Gross hack: We split the toolbar images into two pieces, since sometimes
779 // (popup mode) the toolbar isn't tall enough to show the whole image. The
780 // split happens between the top shadow section and the bottom gradient
781 // section so that we never break the gradient.
782 int split_point = kFrameShadowThickness * 2;
783 int bottom_y = y + split_point;
784 ui::ThemeProvider* tp = GetThemeProvider();
785 SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER);
786 int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
787
788 // Split our canvas out so we can mask out the corners of the toolbar
789 // without masking out the frame.
790 canvas->SaveLayerAlpha(
791 255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
792 h));
793 canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
794
795 SkColor theme_toolbar_color =
796 tp->GetColor(ThemeService::COLOR_TOOLBAR);
797 canvas->FillRectInt(theme_toolbar_color, x, bottom_y, w, bottom_edge_height);
798
799 // Tile the toolbar image starting at the frame edge on the left and where the
800 // horizontal tabstrip is (or would be) on the top.
801 SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR);
802 canvas->TileImageInt(*theme_toolbar, x,
803 bottom_y - GetHorizontalTabStripVerticalOffset(false), x,
804 bottom_y, w, theme_toolbar->height());
805
806 // Draw rounded corners for the tab.
807 SkBitmap* toolbar_left_mask =
808 tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
809 SkBitmap* toolbar_right_mask =
810 tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
811
812 // We mask out the corners by using the DestinationIn transfer mode,
813 // which keeps the RGB pixels from the destination and the alpha from
814 // the source.
815 SkPaint paint;
816 paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
817
818 // Mask the left edge.
819 int left_x = x - kContentEdgeShadowThickness;
820 canvas->DrawBitmapInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
821 split_point, left_x, y, toolbar_left_mask->width(),
822 split_point, false, paint);
823 canvas->DrawBitmapInt(*toolbar_left_mask, 0,
824 toolbar_left_mask->height() - bottom_edge_height,
825 toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
826 toolbar_left_mask->width(), bottom_edge_height, false, paint);
827
828 // Mask the right edge.
829 int right_x =
830 x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
831 canvas->DrawBitmapInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
832 split_point, right_x, y, toolbar_right_mask->width(),
833 split_point, false, paint);
834 canvas->DrawBitmapInt(*toolbar_right_mask, 0,
835 toolbar_right_mask->height() - bottom_edge_height,
836 toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
837 toolbar_right_mask->width(), bottom_edge_height, false, paint);
838 canvas->Restore();
839
840 canvas->DrawBitmapInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
841 left_x, y, toolbar_left->width(), split_point, false);
842 canvas->DrawBitmapInt(*toolbar_left, 0,
843 toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
844 bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
845 bottom_edge_height, false);
846
847 SkBitmap* toolbar_center =
848 tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER);
849 canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
850 y, right_x - (left_x + toolbar_left->width()),
851 split_point);
852
853 SkBitmap* toolbar_right = tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER);
854 canvas->DrawBitmapInt(*toolbar_right, 0, 0, toolbar_right->width(),
855 split_point, right_x, y, toolbar_right->width(), split_point, false);
856 canvas->DrawBitmapInt(*toolbar_right, 0,
857 toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
858 bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
859 bottom_edge_height, false);
860
861 // Draw the content/toolbar separator.
862 canvas->FillRectInt(ResourceBundle::toolbar_separator_color,
863 x + kClientEdgeThickness, toolbar_bounds.bottom() - kClientEdgeThickness,
864 w - (2 * kClientEdgeThickness), kClientEdgeThickness);
865 }
866
PaintOTRAvatar(gfx::Canvas * canvas)867 void OpaqueBrowserFrameView::PaintOTRAvatar(gfx::Canvas* canvas) {
868 // In RTL mode, the avatar icon should be looking the opposite direction.
869 canvas->Save();
870 if (base::i18n::IsRTL()) {
871 canvas->TranslateInt(width(), 0);
872 canvas->ScaleInt(-1, 1);
873 }
874
875 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon();
876 int w = otr_avatar_bounds_.width();
877 int h = otr_avatar_bounds_.height();
878 canvas->DrawBitmapInt(otr_avatar_icon, 0,
879 // Bias the rounding to select a region that's lower rather than higher,
880 // as the shadows at the image top mean the apparent center is below the
881 // real center.
882 ((otr_avatar_icon.height() - otr_avatar_bounds_.height()) + 1) / 2, w, h,
883 otr_avatar_bounds_.x(), otr_avatar_bounds_.y(), w, h, false);
884
885 canvas->Restore();
886 }
887
PaintRestoredClientEdge(gfx::Canvas * canvas)888 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
889 ui::ThemeProvider* tp = GetThemeProvider();
890 int client_area_top = frame_->GetWindow()->client_view()->y();
891 int image_top = client_area_top;
892
893 gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height());
894 SkColor toolbar_color = tp->GetColor(ThemeService::COLOR_TOOLBAR);
895
896 if (browser_view_->IsToolbarVisible()) {
897 // The client edge images always start below the toolbar corner images. The
898 // client edge filled rects start there or at the bottom of the tooolbar,
899 // whichever is shorter.
900 gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds());
901 image_top += toolbar_bounds.y() +
902 tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height();
903 client_area_top = std::min(image_top,
904 client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
905 if (browser_view_->UseVerticalTabs()) {
906 client_area_top -= kVerticalTabBorderInset;
907 image_top -= kVerticalTabBorderInset;
908 }
909 } else if (!browser_view_->IsTabStripVisible()) {
910 // The toolbar isn't going to draw a client edge for us, so draw one
911 // ourselves.
912 SkBitmap* top_left = tp->GetBitmapNamed(IDR_APP_TOP_LEFT);
913 SkBitmap* top_center = tp->GetBitmapNamed(IDR_APP_TOP_CENTER);
914 SkBitmap* top_right = tp->GetBitmapNamed(IDR_APP_TOP_RIGHT);
915 int top_edge_y = client_area_top - top_center->height();
916 int height = client_area_top - top_edge_y;
917
918 canvas->DrawBitmapInt(*top_left, 0, 0, top_left->width(), height,
919 client_area_bounds.x() - top_left->width(), top_edge_y,
920 top_left->width(), height, false);
921 canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
922 client_area_bounds.width(), std::min(height, top_center->height()));
923 canvas->DrawBitmapInt(*top_right, 0, 0, top_right->width(), height,
924 client_area_bounds.right(), top_edge_y,
925 top_right->width(), height, false);
926
927 // Draw the toolbar color across the top edge.
928 canvas->FillRectInt(toolbar_color,
929 client_area_bounds.x() - kClientEdgeThickness,
930 client_area_top - kClientEdgeThickness,
931 client_area_bounds.width() + (2 * kClientEdgeThickness),
932 kClientEdgeThickness);
933 }
934
935 int client_area_bottom =
936 std::max(client_area_top, height() - NonClientBorderThickness());
937 int image_height = client_area_bottom - image_top;
938
939 // Draw the client edge images.
940 // Draw the client edge images.
941 SkBitmap* right = tp->GetBitmapNamed(IDR_CONTENT_RIGHT_SIDE);
942 canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
943 right->width(), image_height);
944 canvas->DrawBitmapInt(
945 *tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
946 client_area_bounds.right(), client_area_bottom);
947 SkBitmap* bottom = tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_CENTER);
948 canvas->TileImageInt(*bottom, client_area_bounds.x(),
949 client_area_bottom, client_area_bounds.width(),
950 bottom->height());
951 SkBitmap* bottom_left =
952 tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
953 canvas->DrawBitmapInt(*bottom_left,
954 client_area_bounds.x() - bottom_left->width(), client_area_bottom);
955 SkBitmap* left = tp->GetBitmapNamed(IDR_CONTENT_LEFT_SIDE);
956 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
957 image_top, left->width(), image_height);
958
959 // Draw the toolbar color so that the client edges show the right color even
960 // where not covered by the toolbar image. NOTE: We do this after drawing the
961 // images because the images are meant to alpha-blend atop the frame whereas
962 // these rects are meant to be fully opaque, without anything overlaid.
963 canvas->FillRectInt(toolbar_color,
964 client_area_bounds.x() - kClientEdgeThickness, client_area_top,
965 kClientEdgeThickness,
966 client_area_bottom + kClientEdgeThickness - client_area_top);
967 canvas->FillRectInt(toolbar_color, client_area_bounds.x(), client_area_bottom,
968 client_area_bounds.width(), kClientEdgeThickness);
969 canvas->FillRectInt(toolbar_color, client_area_bounds.right(),
970 client_area_top, kClientEdgeThickness,
971 client_area_bottom + kClientEdgeThickness - client_area_top);
972 }
973
LayoutWindowControls()974 void OpaqueBrowserFrameView::LayoutWindowControls() {
975 bool is_maximized = frame_->GetWindow()->IsMaximized();
976 close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
977 views::ImageButton::ALIGN_BOTTOM);
978 int caption_y = CaptionButtonY(false);
979 // There should always be the same number of non-shadow pixels visible to the
980 // side of the caption buttons. In maximized mode we extend the rightmost
981 // button to the screen corner to obey Fitts' Law.
982 int right_extra_width = is_maximized ?
983 (kFrameBorderThickness - kFrameShadowThickness) : 0;
984 gfx::Size close_button_size = close_button_->GetPreferredSize();
985 close_button_->SetBounds(width() - FrameBorderThickness(false) -
986 right_extra_width - close_button_size.width(), caption_y,
987 close_button_size.width() + right_extra_width,
988 close_button_size.height());
989
990 #if defined(OS_CHROMEOS)
991 // LayoutWindowControls could be triggered from WindowGtk::UpdateWindowTitle,
992 // which could happen when user navigates in fullscreen mode. And because
993 // BrowserFrameChromeos::IsMaximized return false for fullscreen mode, we
994 // explicitly test fullscreen mode here and make it use the same code path
995 // as maximized mode.
996 // TODO(oshima): Optimize the relayout logic to defer the frame view's
997 // relayout until it is necessary, i.e when it becomes visible.
998 if (is_maximized || frame_->GetWindow()->IsFullscreen()) {
999 minimize_button_->SetVisible(false);
1000 restore_button_->SetVisible(false);
1001 maximize_button_->SetVisible(false);
1002
1003 if (browser_view_->browser()->type() == Browser::TYPE_DEVTOOLS) {
1004 close_button_->SetVisible(true);
1005 minimize_button_->SetBounds(close_button_->bounds().x(), 0, 0, 0);
1006 } else {
1007 close_button_->SetVisible(false);
1008 // Set the bounds of the minimize button so that we don't have to change
1009 // other places that rely on the bounds. Put it slightly to the right
1010 // of the edge of the view, so that when we remove the spacing it lines
1011 // up with the edge.
1012 minimize_button_->SetBounds(width() - FrameBorderThickness(false) +
1013 kNewTabCaptionMaximizedSpacing, 0, 0, 0);
1014 }
1015
1016 return;
1017 } else {
1018 close_button_->SetVisible(true);
1019 }
1020 #endif
1021
1022 // When the window is restored, we show a maximized button; otherwise, we show
1023 // a restore button.
1024 bool is_restored = !is_maximized && !frame_->GetWindow()->IsMinimized();
1025 views::ImageButton* invisible_button = is_restored ?
1026 restore_button_ : maximize_button_;
1027 invisible_button->SetVisible(false);
1028
1029 views::ImageButton* visible_button = is_restored ?
1030 maximize_button_ : restore_button_;
1031 visible_button->SetVisible(true);
1032 visible_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1033 views::ImageButton::ALIGN_BOTTOM);
1034 gfx::Size visible_button_size = visible_button->GetPreferredSize();
1035 visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
1036 caption_y, visible_button_size.width(),
1037 visible_button_size.height());
1038
1039 minimize_button_->SetVisible(true);
1040 minimize_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1041 views::ImageButton::ALIGN_BOTTOM);
1042 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
1043 minimize_button_->SetBounds(
1044 visible_button->x() - minimize_button_size.width(), caption_y,
1045 minimize_button_size.width(),
1046 minimize_button_size.height());
1047 }
1048
LayoutTitleBar()1049 void OpaqueBrowserFrameView::LayoutTitleBar() {
1050 // The window title is based on the calculated icon position, even when there
1051 // is no icon.
1052 gfx::Rect icon_bounds(IconBounds());
1053 views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
1054 if (delegate && delegate->ShouldShowWindowIcon())
1055 window_icon_->SetBoundsRect(icon_bounds);
1056
1057 // Size the title, if visible.
1058 if (delegate && delegate->ShouldShowWindowTitle()) {
1059 int title_x = delegate->ShouldShowWindowIcon() ?
1060 icon_bounds.right() + kIconTitleSpacing : icon_bounds.x();
1061 int title_height = BrowserFrame::GetTitleFont().GetHeight();
1062 // We bias the title position so that when the difference between the icon
1063 // and title heights is odd, the extra pixel of the title is above the
1064 // vertical midline rather than below. This compensates for how the icon is
1065 // already biased downwards (see IconBounds()) and helps prevent descenders
1066 // on the title from overlapping the 3D edge at the bottom of the titlebar.
1067 title_bounds_.SetRect(title_x,
1068 icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2),
1069 std::max(0, minimize_button_->x() - kTitleLogoSpacing - title_x),
1070 title_height);
1071 }
1072 }
1073
LayoutOTRAvatar()1074 void OpaqueBrowserFrameView::LayoutOTRAvatar() {
1075 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon();
1076 int otr_bottom, otr_restored_y;
1077 if (browser_view_->UseVerticalTabs()) {
1078 otr_bottom = NonClientTopBorderHeight(false, false) - kOTRBottomSpacing;
1079 otr_restored_y = kFrameShadowThickness;
1080 } else {
1081 otr_bottom = GetHorizontalTabStripVerticalOffset(false) +
1082 browser_view_->GetTabStripHeight() - kOTRBottomSpacing;
1083 otr_restored_y = otr_bottom - otr_avatar_icon.height();
1084 }
1085 int otr_y = frame_->GetWindow()->IsMaximized() ?
1086 (NonClientTopBorderHeight(false, true) + kTabstripTopShadowThickness) :
1087 otr_restored_y;
1088 otr_avatar_bounds_.SetRect(NonClientBorderThickness() + kOTRSideSpacing,
1089 otr_y, otr_avatar_icon.width(),
1090 browser_view_->ShouldShowOffTheRecordAvatar() ? (otr_bottom - otr_y) : 0);
1091 }
1092
CalculateClientAreaBounds(int width,int height) const1093 gfx::Rect OpaqueBrowserFrameView::CalculateClientAreaBounds(int width,
1094 int height) const {
1095 int top_height = NonClientTopBorderHeight(false, false);
1096 int border_thickness = NonClientBorderThickness();
1097 return gfx::Rect(border_thickness, top_height,
1098 std::max(0, width - (2 * border_thickness)),
1099 std::max(0, height - GetReservedHeight() -
1100 top_height - border_thickness));
1101 }
1102