1 // Copyright 2013 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_layout.h"
6
7 #include "chrome/browser/profiles/profiles_state.h"
8 #include "chrome/browser/ui/views/avatar_menu_button.h"
9 #include "ui/gfx/font.h"
10 #include "ui/views/controls/button/image_button.h"
11 #include "ui/views/controls/label.h"
12
13 #if defined(OS_WIN)
14 #include "win8/util/win8_util.h"
15 #endif // OS_WIN
16
17 namespace {
18
19 // Besides the frame border, there's another 9 px of empty space atop the
20 // window in restored mode, to use to drag the window around.
21 const int kNonClientRestoredExtraThickness = 9;
22
23 // The titlebar never shrinks too short to show the caption button plus some
24 // padding below it.
25 const int kCaptionButtonHeightWithPadding = 19;
26
27 // There is a 5 px gap between the title text and the caption buttons.
28 const int kTitleLogoSpacing = 5;
29
30 // The frame border is only visible in restored mode and is hardcoded to 4 px on
31 // each side regardless of the system window border size.
32 const int kFrameBorderThickness = 4;
33
34 // The titlebar has a 2 px 3D edge along the top and bottom.
35 const int kTitlebarTopAndBottomEdgeThickness = 2;
36
37 // The icon is inset 2 px from the left frame border.
38 const int kIconLeftSpacing = 2;
39
40 // There is a 4 px gap between the icon and the title text.
41 const int kIconTitleSpacing = 4;
42
43 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
44 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
45 // user).
46 const int kAvatarBottomSpacing = 2;
47
48 // Space between the frame border and the edge of the avatar.
49 const int kAvatarOuterSpacing = 2;
50
51 // Space between the edge of the avatar and the tabstrip.
52 const int kAvatarInnerSpacing = 4;
53
54 // Space between the trailing edge of the avatar label and the tabstrip.
55 const int kAvatarLabelInnerSpacing = 10;
56
57 // How far the new avatar button is from the closest caption button.
58 const int kNewAvatarButtonOffset = 5;
59
60 // In restored mode, the New Tab button isn't at the same height as the caption
61 // buttons, but the space will look cluttered if it actually slides under them,
62 // so we stop it when the gap between the two is down to 5 px.
63 const int kNewTabCaptionRestoredSpacing = 5;
64
65 // In maximized mode, where the New Tab button and the caption buttons are at
66 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid
67 // looking too cluttered.
68 const int kNewTabCaptionMaximizedSpacing = 16;
69
70 // The top 3 px of the tabstrip is shadow; in maximized mode we push this off
71 // the top of the screen so the tabs appear flush against the screen edge.
72 const int kTabstripTopShadowThickness = 3;
73
74 // How far to indent the tabstrip from the left side of the screen when there
75 // is no avatar icon.
76 const int kTabStripIndent = -6;
77
78 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
79 // Default extra space between the top of the frame and the top of the window
80 // caption buttons.
81 const int kExtraCaption = 2;
82
83 // Default extra spacing between individual window caption buttons.
84 const int kCaptionButtonSpacing = 2;
85 #else
86 const int kExtraCaption = 0;
87 const int kCaptionButtonSpacing = 0;
88 #endif
89
90 } // namespace
91
92 ///////////////////////////////////////////////////////////////////////////////
93 // OpaqueBrowserFrameView, public:
94
OpaqueBrowserFrameViewLayout(OpaqueBrowserFrameViewLayoutDelegate * delegate)95 OpaqueBrowserFrameViewLayout::OpaqueBrowserFrameViewLayout(
96 OpaqueBrowserFrameViewLayoutDelegate* delegate)
97 : delegate_(delegate),
98 leading_button_start_(0),
99 trailing_button_start_(0),
100 minimum_size_for_buttons_(0),
101 has_leading_buttons_(false),
102 has_trailing_buttons_(false),
103 extra_caption_y_(kExtraCaption),
104 window_caption_spacing_(kCaptionButtonSpacing),
105 minimize_button_(NULL),
106 maximize_button_(NULL),
107 restore_button_(NULL),
108 close_button_(NULL),
109 window_icon_(NULL),
110 window_title_(NULL),
111 avatar_label_(NULL),
112 avatar_button_(NULL),
113 new_avatar_button_(NULL) {
114 trailing_buttons_.push_back(views::FRAME_BUTTON_MINIMIZE);
115 trailing_buttons_.push_back(views::FRAME_BUTTON_MAXIMIZE);
116 trailing_buttons_.push_back(views::FRAME_BUTTON_CLOSE);
117 }
118
~OpaqueBrowserFrameViewLayout()119 OpaqueBrowserFrameViewLayout::~OpaqueBrowserFrameViewLayout() {}
120
121 // static
ShouldAddDefaultCaptionButtons()122 bool OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons() {
123 #if defined(OS_WIN)
124 return !win8::IsSingleWindowMetroMode();
125 #endif // OS_WIN
126 return true;
127 }
128
SetButtonOrdering(const std::vector<views::FrameButton> & leading_buttons,const std::vector<views::FrameButton> & trailing_buttons)129 void OpaqueBrowserFrameViewLayout::SetButtonOrdering(
130 const std::vector<views::FrameButton>& leading_buttons,
131 const std::vector<views::FrameButton>& trailing_buttons) {
132 leading_buttons_ = leading_buttons;
133 trailing_buttons_ = trailing_buttons;
134 }
135
GetBoundsForTabStrip(const gfx::Size & tabstrip_preferred_size,int available_width) const136 gfx::Rect OpaqueBrowserFrameViewLayout::GetBoundsForTabStrip(
137 const gfx::Size& tabstrip_preferred_size,
138 int available_width) const {
139 available_width -= trailing_button_start_;
140 available_width -= leading_button_start_;
141
142 if (delegate_->GetAdditionalReservedSpaceInTabStrip())
143 available_width -= delegate_->GetAdditionalReservedSpaceInTabStrip();
144
145 const int caption_spacing = delegate_->IsMaximized() ?
146 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing;
147 const int tabstrip_width = available_width - caption_spacing;
148 gfx::Rect bounds(leading_button_start_, GetTabStripInsetsTop(false),
149 std::max(0, tabstrip_width),
150 tabstrip_preferred_size.height());
151
152 int leading_tabstrip_indent = kTabStripIndent;
153 if (delegate_->ShouldShowAvatar() && !ShouldAvatarBeOnRight()) {
154 if (avatar_label_ && avatar_label_->bounds().width())
155 leading_tabstrip_indent += kAvatarLabelInnerSpacing;
156 else
157 leading_tabstrip_indent += kAvatarInnerSpacing;
158 }
159 bounds.Inset(leading_tabstrip_indent, 0, 0, 0);
160 return bounds;
161 }
162
GetMinimumSize(int available_width) const163 gfx::Size OpaqueBrowserFrameViewLayout::GetMinimumSize(
164 int available_width) const {
165 gfx::Size min_size = delegate_->GetBrowserViewMinimumSize();
166 int border_thickness = NonClientBorderThickness();
167 min_size.Enlarge(2 * border_thickness,
168 NonClientTopBorderHeight(false) + border_thickness);
169
170 // Ensure that we can, at minimum, hold our window controls and avatar icon.
171 min_size.set_width(std::max(min_size.width(), minimum_size_for_buttons_));
172
173 // Ensure that the minimum width is enough to hold a minimum width tab strip
174 // at its usual insets.
175 if (delegate_->IsTabStripVisible()) {
176 gfx::Size preferred_size = delegate_->GetTabstripPreferredSize();
177 const int min_tabstrip_width = preferred_size.width();
178 const int caption_spacing = delegate_->IsMaximized() ?
179 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing;
180 min_size.Enlarge(min_tabstrip_width + caption_spacing, 0);
181 }
182
183 return min_size;
184 }
185
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const186 gfx::Rect OpaqueBrowserFrameViewLayout::GetWindowBoundsForClientBounds(
187 const gfx::Rect& client_bounds) const {
188 int top_height = NonClientTopBorderHeight(false);
189 int border_thickness = NonClientBorderThickness();
190 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
191 std::max(0, client_bounds.y() - top_height),
192 client_bounds.width() + (2 * border_thickness),
193 client_bounds.height() + top_height + border_thickness);
194 }
195
FrameBorderThickness(bool restored) const196 int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const {
197 return (!restored && (delegate_->IsMaximized() ||
198 delegate_->IsFullscreen())) ?
199 0 : kFrameBorderThickness;
200 }
201
NonClientBorderThickness() const202 int OpaqueBrowserFrameViewLayout::NonClientBorderThickness() const {
203 // When we fill the screen, we don't show a client edge.
204 return FrameBorderThickness(false) +
205 ((delegate_->IsMaximized() || delegate_->IsFullscreen()) ?
206 0 : views::NonClientFrameView::kClientEdgeThickness);
207 }
208
NonClientTopBorderHeight(bool restored) const209 int OpaqueBrowserFrameViewLayout::NonClientTopBorderHeight(
210 bool restored) const {
211 if (delegate_->ShouldShowWindowTitle()) {
212 return std::max(FrameBorderThickness(restored) + delegate_->GetIconSize(),
213 CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) +
214 TitlebarBottomThickness(restored);
215 }
216
217 return FrameBorderThickness(restored) -
218 ((delegate_->IsTabStripVisible() &&
219 !restored && !delegate_->ShouldLeaveOffsetNearTopBorder())
220 ? kTabstripTopShadowThickness : 0);
221 }
222
GetTabStripInsetsTop(bool restored) const223 int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const {
224 return NonClientTopBorderHeight(restored) + ((!restored &&
225 (!delegate_->ShouldLeaveOffsetNearTopBorder() ||
226 delegate_->IsFullscreen())) ?
227 0 : kNonClientRestoredExtraThickness);
228 }
229
TitlebarBottomThickness(bool restored) const230 int OpaqueBrowserFrameViewLayout::TitlebarBottomThickness(bool restored) const {
231 return kTitlebarTopAndBottomEdgeThickness +
232 ((!restored && delegate_->IsMaximized()) ? 0 :
233 views::NonClientFrameView::kClientEdgeThickness);
234 }
235
CaptionButtonY(bool restored) const236 int OpaqueBrowserFrameViewLayout::CaptionButtonY(bool restored) const {
237 // Maximized buttons start at window top so that even if their images aren't
238 // drawn flush with the screen edge, they still obey Fitts' Law.
239 return ((!restored && delegate_->IsMaximized()) ?
240 FrameBorderThickness(false) :
241 views::NonClientFrameView::kFrameShadowThickness) + extra_caption_y_;
242 }
243
IconBounds() const244 gfx::Rect OpaqueBrowserFrameViewLayout::IconBounds() const {
245 return window_icon_bounds_;
246 }
247
CalculateClientAreaBounds(int width,int height) const248 gfx::Rect OpaqueBrowserFrameViewLayout::CalculateClientAreaBounds(
249 int width,
250 int height) const {
251 int top_height = NonClientTopBorderHeight(false);
252 int border_thickness = NonClientBorderThickness();
253 return gfx::Rect(border_thickness, top_height,
254 std::max(0, width - (2 * border_thickness)),
255 std::max(0, height - top_height - border_thickness));
256 }
257
258 ///////////////////////////////////////////////////////////////////////////////
259 // OpaqueBrowserFrameView, private:
260
ShouldAvatarBeOnRight() const261 bool OpaqueBrowserFrameViewLayout::ShouldAvatarBeOnRight() const {
262 // The avatar should be shown either on the end of the left or the beginning
263 // of the right depending on which side has fewer buttons.
264 return trailing_buttons_.size() < leading_buttons_.size();
265 }
266
LayoutWindowControls(views::View * host)267 void OpaqueBrowserFrameViewLayout::LayoutWindowControls(views::View* host) {
268 if (!ShouldAddDefaultCaptionButtons())
269 return;
270
271 int caption_y = CaptionButtonY(false);
272
273 // Keep a list of all buttons that we don't show.
274 std::vector<views::FrameButton> buttons_not_shown;
275 buttons_not_shown.push_back(views::FRAME_BUTTON_MAXIMIZE);
276 buttons_not_shown.push_back(views::FRAME_BUTTON_MINIMIZE);
277 buttons_not_shown.push_back(views::FRAME_BUTTON_CLOSE);
278
279 if (delegate_->ShouldShowCaptionButtons()) {
280 for (std::vector<views::FrameButton>::const_iterator it =
281 leading_buttons_.begin(); it != leading_buttons_.end(); ++it) {
282 ConfigureButton(host, *it, ALIGN_LEADING, caption_y);
283 buttons_not_shown.erase(
284 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it),
285 buttons_not_shown.end());
286 }
287
288 for (std::vector<views::FrameButton>::const_reverse_iterator it =
289 trailing_buttons_.rbegin(); it != trailing_buttons_.rend(); ++it) {
290 ConfigureButton(host, *it, ALIGN_TRAILING, caption_y);
291 buttons_not_shown.erase(
292 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it),
293 buttons_not_shown.end());
294 }
295 }
296
297 for (std::vector<views::FrameButton>::const_iterator it =
298 buttons_not_shown.begin(); it != buttons_not_shown.end(); ++it) {
299 HideButton(*it);
300 }
301 }
302
LayoutTitleBar(views::View * host)303 void OpaqueBrowserFrameViewLayout::LayoutTitleBar(views::View* host) {
304 bool use_hidden_icon_location = true;
305
306 int size = delegate_->GetIconSize();
307 int frame_thickness = FrameBorderThickness(false);
308 bool should_show_icon = delegate_->ShouldShowWindowIcon();
309 bool should_show_title = delegate_->ShouldShowWindowTitle();
310
311 if (should_show_icon || should_show_title) {
312 use_hidden_icon_location = false;
313
314 // Our frame border has a different "3D look" than Windows'. Theirs has
315 // a more complex gradient on the top that they push their icon/title
316 // below; then the maximized window cuts this off and the icon/title are
317 // centered in the remaining space. Because the apparent shape of our
318 // border is simpler, using the same positioning makes things look
319 // slightly uncentered with restored windows, so when the window is
320 // restored, instead of calculating the remaining space from below the
321 // frame border, we calculate from below the 3D edge.
322 int unavailable_px_at_top = delegate_->IsMaximized() ?
323 frame_thickness : kTitlebarTopAndBottomEdgeThickness;
324 // When the icon is shorter than the minimum space we reserve for the
325 // caption button, we vertically center it. We want to bias rounding to
326 // put extra space above the icon, since the 3D edge (+ client edge, for
327 // restored windows) below looks (to the eye) more like additional space
328 // than does the 3D edge (or nothing at all, for maximized windows)
329 // above; hence the +1.
330 int y = unavailable_px_at_top + (NonClientTopBorderHeight(false) -
331 unavailable_px_at_top - size -
332 TitlebarBottomThickness(false) + 1) / 2;
333
334 window_icon_bounds_ = gfx::Rect(leading_button_start_ + kIconLeftSpacing, y,
335 size, size);
336 leading_button_start_ += size + kIconLeftSpacing;
337 minimum_size_for_buttons_ += size + kIconLeftSpacing;
338 }
339
340 if (should_show_icon)
341 window_icon_->SetBoundsRect(window_icon_bounds_);
342
343 if (window_title_) {
344 window_title_->SetVisible(should_show_title);
345 if (should_show_title) {
346 window_title_->SetText(delegate_->GetWindowTitle());
347
348 int text_width = std::max(
349 0, host->width() - trailing_button_start_ - kTitleLogoSpacing -
350 leading_button_start_ - kIconTitleSpacing);
351 window_title_->SetBounds(leading_button_start_ + kIconTitleSpacing,
352 window_icon_bounds_.y(),
353 text_width, window_icon_bounds_.height());
354 leading_button_start_ += text_width + kIconTitleSpacing;
355 }
356 }
357
358 if (use_hidden_icon_location) {
359 if (has_leading_buttons_) {
360 // There are window button icons on the left. Don't size the hidden window
361 // icon that people can double click on to close the window.
362 window_icon_bounds_ = gfx::Rect();
363 } else {
364 // We set the icon bounds to a small rectangle in the top leading corner
365 // if there are no icons on the leading side.
366 window_icon_bounds_ = gfx::Rect(
367 frame_thickness + kIconLeftSpacing, frame_thickness, size, size);
368 }
369 }
370 }
371
LayoutNewStyleAvatar(views::View * host)372 void OpaqueBrowserFrameViewLayout::LayoutNewStyleAvatar(views::View* host) {
373 DCHECK(profiles::IsNewProfileManagementEnabled());
374 if (!new_avatar_button_)
375 return;
376
377 gfx::Size label_size = new_avatar_button_->GetPreferredSize();
378 int button_size_with_offset = kNewAvatarButtonOffset + label_size.width();
379
380 int button_x = host->width() - trailing_button_start_ -
381 button_size_with_offset;
382 int button_y = CaptionButtonY(false);
383
384 trailing_button_start_ += button_size_with_offset;
385 minimum_size_for_buttons_ += button_size_with_offset;
386
387 new_avatar_button_->SetBounds(
388 button_x,
389 button_y,
390 label_size.width(),
391 button_y + kCaptionButtonHeightWithPadding);
392 }
393
LayoutAvatar(views::View * host)394 void OpaqueBrowserFrameViewLayout::LayoutAvatar(views::View* host) {
395 // Even though the avatar is used for both incognito and profiles we always
396 // use the incognito icon to layout the avatar button. The profile icon
397 // can be customized so we can't depend on its size to perform layout.
398 gfx::ImageSkia incognito_icon = delegate_->GetOTRAvatarIcon();
399
400 bool avatar_on_right = ShouldAvatarBeOnRight();
401 int avatar_bottom = GetTabStripInsetsTop(false) +
402 delegate_->GetTabStripHeight() - kAvatarBottomSpacing;
403 int avatar_restored_y = avatar_bottom - incognito_icon.height();
404 int avatar_x = avatar_on_right ?
405 host->width() - trailing_button_start_ - kAvatarOuterSpacing -
406 incognito_icon.width() :
407 leading_button_start_ + kAvatarOuterSpacing;
408 int avatar_y = delegate_->IsMaximized() ?
409 (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) :
410 avatar_restored_y;
411 avatar_bounds_.SetRect(
412 avatar_x,
413 avatar_y,
414 incognito_icon.width(),
415 delegate_->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0);
416 if (avatar_button_) {
417 avatar_button_->set_button_on_right(avatar_on_right);
418 avatar_button_->SetBoundsRect(avatar_bounds_);
419
420 int edge_offset;
421 if (avatar_label_) {
422 // Space between the bottom of the avatar and the bottom of the avatar
423 // label.
424 const int kAvatarLabelBottomSpacing = 3;
425 gfx::Size label_size = avatar_label_->GetPreferredSize();
426 // The x-position of the avatar label should be slightly to the left of
427 // the avatar menu button. Therefore we use the |leading_button_start_|
428 // value directly.
429 gfx::Rect label_bounds(
430 leading_button_start_,
431 avatar_bottom - kAvatarLabelBottomSpacing - label_size.height(),
432 label_size.width(),
433 delegate_->ShouldShowAvatar() ? label_size.height() : 0);
434 avatar_label_->SetBoundsRect(label_bounds);
435 edge_offset = label_size.width();
436 } else {
437 edge_offset = kAvatarOuterSpacing + incognito_icon.width();
438 }
439 if (avatar_on_right)
440 trailing_button_start_ += edge_offset;
441 else
442 leading_button_start_ += edge_offset;
443
444 // We just add the avatar button size to the minimum size because clicking
445 // the avatar label does the same thing as clicking the avatar button.
446 minimum_size_for_buttons_ += kAvatarOuterSpacing + incognito_icon.width();
447 }
448 }
449
ConfigureButton(views::View * host,views::FrameButton button_id,ButtonAlignment alignment,int caption_y)450 void OpaqueBrowserFrameViewLayout::ConfigureButton(
451 views::View* host,
452 views::FrameButton button_id,
453 ButtonAlignment alignment,
454 int caption_y) {
455 switch (button_id) {
456 case views::FRAME_BUTTON_MINIMIZE: {
457 minimize_button_->SetVisible(true);
458 SetBoundsForButton(host, minimize_button_, alignment, caption_y);
459 break;
460 }
461 case views::FRAME_BUTTON_MAXIMIZE: {
462 // When the window is restored, we show a maximized button; otherwise, we
463 // show a restore button.
464 bool is_restored = !delegate_->IsMaximized() && !delegate_->IsMinimized();
465 views::ImageButton* invisible_button = is_restored ?
466 restore_button_ : maximize_button_;
467 invisible_button->SetVisible(false);
468
469 views::ImageButton* visible_button = is_restored ?
470 maximize_button_ : restore_button_;
471 visible_button->SetVisible(true);
472 SetBoundsForButton(host, visible_button, alignment, caption_y);
473 break;
474 }
475 case views::FRAME_BUTTON_CLOSE: {
476 close_button_->SetVisible(true);
477 SetBoundsForButton(host, close_button_, alignment, caption_y);
478 break;
479 }
480 }
481 }
482
HideButton(views::FrameButton button_id)483 void OpaqueBrowserFrameViewLayout::HideButton(views::FrameButton button_id) {
484 switch (button_id) {
485 case views::FRAME_BUTTON_MINIMIZE:
486 minimize_button_->SetVisible(false);
487 break;
488 case views::FRAME_BUTTON_MAXIMIZE:
489 restore_button_->SetVisible(false);
490 maximize_button_->SetVisible(false);
491 break;
492 case views::FRAME_BUTTON_CLOSE:
493 close_button_->SetVisible(false);
494 break;
495 }
496 }
497
SetBoundsForButton(views::View * host,views::ImageButton * button,ButtonAlignment alignment,int caption_y)498 void OpaqueBrowserFrameViewLayout::SetBoundsForButton(
499 views::View* host,
500 views::ImageButton* button,
501 ButtonAlignment alignment,
502 int caption_y) {
503 gfx::Size button_size = button->GetPreferredSize();
504
505 button->SetImageAlignment(
506 (alignment == ALIGN_LEADING) ?
507 views::ImageButton::ALIGN_RIGHT : views::ImageButton::ALIGN_LEFT,
508 views::ImageButton::ALIGN_BOTTOM);
509
510 // There should always be the same number of non-shadow pixels visible to the
511 // side of the caption buttons. In maximized mode we extend the rightmost
512 // button to the screen corner to obey Fitts' Law.
513 bool is_maximized = delegate_->IsMaximized();
514
515 // When we are the first button on the leading side and are the close
516 // button, we must flip ourselves, because the close button assets have
517 // a little notch to fit in the rounded frame.
518 button->SetDrawImageMirrored(alignment == ALIGN_LEADING &&
519 !has_leading_buttons_ &&
520 button == close_button_);
521
522 switch (alignment) {
523 case ALIGN_LEADING: {
524 if (has_leading_buttons_)
525 leading_button_start_ += window_caption_spacing_;
526
527 // If we're the first button on the left and maximized, add with to the
528 // right hand side of the screen.
529 int extra_width = (is_maximized && !has_leading_buttons_) ?
530 (kFrameBorderThickness -
531 views::NonClientFrameView::kFrameShadowThickness) : 0;
532
533 button->SetBounds(
534 leading_button_start_,
535 caption_y,
536 button_size.width() + extra_width,
537 button_size.height());
538
539 leading_button_start_ += extra_width + button_size.width();
540 minimum_size_for_buttons_ += extra_width + button_size.width();
541 has_leading_buttons_ = true;
542 break;
543 }
544 case ALIGN_TRAILING: {
545 if (has_trailing_buttons_)
546 trailing_button_start_ += window_caption_spacing_;
547
548 // If we're the first button on the right and maximized, add with to the
549 // right hand side of the screen.
550 int extra_width = (is_maximized && !has_trailing_buttons_) ?
551 (kFrameBorderThickness -
552 views::NonClientFrameView::kFrameShadowThickness) : 0;
553
554 button->SetBounds(
555 host->width() - trailing_button_start_ - extra_width -
556 button_size.width(),
557 caption_y,
558 button_size.width() + extra_width,
559 button_size.height());
560
561 trailing_button_start_ += extra_width + button_size.width();
562 minimum_size_for_buttons_ += extra_width + button_size.width();
563 has_trailing_buttons_ = true;
564 break;
565 }
566 }
567 }
568
SetView(int id,views::View * view)569 void OpaqueBrowserFrameViewLayout::SetView(int id, views::View* view) {
570 // Why do things this way instead of having an Init() method, where we're
571 // passed the views we'll handle? Because OpaqueBrowserFrameView doesn't own
572 // all the views which are part of it. The avatar stuff, for example, will be
573 // added and removed by the base class of OpaqueBrowserFrameView.
574 switch (id) {
575 case VIEW_ID_MINIMIZE_BUTTON:
576 if (view) {
577 DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
578 view->GetClassName());
579 }
580 minimize_button_ = static_cast<views::ImageButton*>(view);
581 break;
582 case VIEW_ID_MAXIMIZE_BUTTON:
583 if (view) {
584 DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
585 view->GetClassName());
586 }
587 maximize_button_ = static_cast<views::ImageButton*>(view);
588 break;
589 case VIEW_ID_RESTORE_BUTTON:
590 if (view) {
591 DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
592 view->GetClassName());
593 }
594 restore_button_ = static_cast<views::ImageButton*>(view);
595 break;
596 case VIEW_ID_CLOSE_BUTTON:
597 if (view) {
598 DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
599 view->GetClassName());
600 }
601 close_button_ = static_cast<views::ImageButton*>(view);
602 break;
603 case VIEW_ID_WINDOW_ICON:
604 window_icon_ = view;
605 break;
606 case VIEW_ID_WINDOW_TITLE:
607 if (view) {
608 DCHECK_EQ(std::string(views::Label::kViewClassName),
609 view->GetClassName());
610 }
611 window_title_ = static_cast<views::Label*>(view);
612 break;
613 case VIEW_ID_AVATAR_LABEL:
614 avatar_label_ = view;
615 break;
616 case VIEW_ID_AVATAR_BUTTON:
617 if (view) {
618 DCHECK_EQ(std::string(AvatarMenuButton::kViewClassName),
619 view->GetClassName());
620 }
621 avatar_button_ = static_cast<AvatarMenuButton*>(view);
622 break;
623 case VIEW_ID_NEW_AVATAR_BUTTON:
624 new_avatar_button_ = view;
625 break;
626 default:
627 NOTIMPLEMENTED() << "Unknown view id " << id;
628 break;
629 }
630 }
631
632 ///////////////////////////////////////////////////////////////////////////////
633 // OpaqueBrowserFrameView, views::LayoutManager:
634
Layout(views::View * host)635 void OpaqueBrowserFrameViewLayout::Layout(views::View* host) {
636 // Reset all our data so that everything is invisible.
637 int thickness = FrameBorderThickness(false);
638 leading_button_start_ = thickness;
639 trailing_button_start_ = thickness;
640 minimum_size_for_buttons_ = leading_button_start_ + trailing_button_start_;
641 has_leading_buttons_ = false;
642 has_trailing_buttons_ = false;
643
644 LayoutWindowControls(host);
645 LayoutTitleBar(host);
646
647 // We now add a single pixel to the leading spacing. We do this because the
648 // avatar and tab strip start one pixel inward compared to where things start
649 // on the trailing side.
650 leading_button_start_++;
651
652 if (delegate_->IsRegularOrGuestSession() &&
653 profiles::IsNewProfileManagementEnabled())
654 LayoutNewStyleAvatar(host);
655 else
656 LayoutAvatar(host);
657
658 client_view_bounds_ = CalculateClientAreaBounds(
659 host->width(), host->height());
660 }
661
GetPreferredSize(views::View * host)662 gfx::Size OpaqueBrowserFrameViewLayout::GetPreferredSize(views::View* host) {
663 // This is never used; NonClientView::GetPreferredSize() will be called
664 // instead.
665 NOTREACHED();
666 return gfx::Size();
667 }
668
ViewAdded(views::View * host,views::View * view)669 void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host,
670 views::View* view) {
671 SetView(view->id(), view);
672 }
673
ViewRemoved(views::View * host,views::View * view)674 void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host,
675 views::View* view) {
676 SetView(view->id(), NULL);
677 }
678