• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/ui/views/avatar_menu_button.h"
11 #include "chrome/browser/ui/views/tab_icon_view.h"
12 #include "chrome/browser/ui/views/tabs/tab.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "ui/gfx/image/image_skia.h"
15 #include "ui/gfx/image/image_skia_rep.h"
16 #include "ui/gfx/text_constants.h"
17 #include "ui/views/controls/button/image_button.h"
18 #include "ui/views/controls/button/menu_button.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/test/views_test_base.h"
21 
22 using views::Widget;
23 
24 namespace {
25 
26 const int kWidth = 500;
27 
28 class TestLayoutDelegate : public OpaqueBrowserFrameViewLayoutDelegate {
29  public:
30   enum WindowState {
31     STATE_NORMAL,
32     STATE_MAXIMIZED,
33     STATE_MINIMIZED,
34     STATE_FULLSCREEN
35   };
36 
TestLayoutDelegate()37   TestLayoutDelegate()
38       : show_avatar_(false),
39         show_caption_buttons_(true),
40         window_state_(STATE_NORMAL) {
41   }
42 
~TestLayoutDelegate()43   virtual ~TestLayoutDelegate() {}
44 
SetWindowTitle(const base::string16 & title)45   void SetWindowTitle(const base::string16& title) {
46     window_title_ = title;
47   }
48 
SetShouldShowAvatar(bool show_avatar)49   void SetShouldShowAvatar(bool show_avatar) {
50     show_avatar_ = show_avatar;
51   }
52 
SetShouldShowCaptionButtons(bool show_caption_buttons)53   void SetShouldShowCaptionButtons(bool show_caption_buttons) {
54     show_caption_buttons_ = show_caption_buttons;
55   }
56 
SetWindowState(WindowState state)57   void SetWindowState(WindowState state) {
58     window_state_ = state;
59   }
60 
61   // OpaqueBrowserFrameViewLayoutDelegate overrides:
62 
ShouldShowWindowIcon() const63   virtual bool ShouldShowWindowIcon() const OVERRIDE {
64     return !window_title_.empty();
65   }
66 
ShouldShowWindowTitle() const67   virtual bool ShouldShowWindowTitle() const OVERRIDE {
68     return !window_title_.empty();
69   }
70 
GetWindowTitle() const71   virtual base::string16 GetWindowTitle() const OVERRIDE {
72     return window_title_;
73   }
74 
GetIconSize() const75   virtual int GetIconSize() const OVERRIDE {
76     // The value on linux_aura and non-aura windows.
77     return 17;
78   }
79 
ShouldLeaveOffsetNearTopBorder() const80   virtual bool ShouldLeaveOffsetNearTopBorder() const OVERRIDE {
81     return !IsMaximized();
82   }
83 
GetBrowserViewMinimumSize() const84   virtual gfx::Size GetBrowserViewMinimumSize() const OVERRIDE {
85     // Taken from a calculation in BrowserViewLayout.
86     return gfx::Size(168, 64);
87   }
88 
ShouldShowCaptionButtons() const89   virtual bool ShouldShowCaptionButtons() const OVERRIDE {
90     return show_caption_buttons_;
91   }
92 
ShouldShowAvatar() const93   virtual bool ShouldShowAvatar() const OVERRIDE {
94     return show_avatar_;
95   }
96 
IsRegularOrGuestSession() const97   virtual bool IsRegularOrGuestSession() const OVERRIDE {
98     return true;
99   }
100 
GetOTRAvatarIcon() const101   virtual gfx::ImageSkia GetOTRAvatarIcon() const OVERRIDE {
102     // The calculations depend on the size of the OTR resource, and chromeos
103     // uses a different sized image, so hard code the size of the current
104     // windows/linux one.
105     gfx::ImageSkiaRep rep(gfx::Size(40, 29), 1.0f);
106     gfx::ImageSkia image(rep);
107     return image;
108   }
109 
IsMaximized() const110   virtual bool IsMaximized() const OVERRIDE {
111     return window_state_ == STATE_MAXIMIZED;
112   }
113 
IsMinimized() const114   virtual bool IsMinimized() const OVERRIDE {
115     return window_state_ == STATE_MINIMIZED;
116   }
117 
IsFullscreen() const118   virtual bool IsFullscreen() const OVERRIDE {
119     return window_state_ == STATE_FULLSCREEN;
120   }
121 
IsTabStripVisible() const122   virtual bool IsTabStripVisible() const OVERRIDE {
123     return window_title_.empty();
124   }
125 
GetTabStripHeight() const126   virtual int GetTabStripHeight() const OVERRIDE {
127     return IsTabStripVisible() ? Tab::GetMinimumUnselectedSize().height() : 0;
128   }
129 
GetAdditionalReservedSpaceInTabStrip() const130   virtual int GetAdditionalReservedSpaceInTabStrip() const OVERRIDE {
131     return 0;
132   }
133 
GetTabstripPreferredSize() const134   virtual gfx::Size GetTabstripPreferredSize() const OVERRIDE {
135     // Measured from Tabstrip::GetPreferredSize().
136     return IsTabStripVisible() ? gfx::Size(78, 29) : gfx::Size(0, 0);
137   }
138 
139  private:
140   base::string16 window_title_;
141   bool show_avatar_;
142   bool show_caption_buttons_;
143   WindowState window_state_;
144 
145   DISALLOW_COPY_AND_ASSIGN(TestLayoutDelegate);
146 };
147 
148 }  // namespace
149 
150 class OpaqueBrowserFrameViewLayoutTest : public views::ViewsTestBase {
151  public:
OpaqueBrowserFrameViewLayoutTest()152   OpaqueBrowserFrameViewLayoutTest() {}
~OpaqueBrowserFrameViewLayoutTest()153   virtual ~OpaqueBrowserFrameViewLayoutTest() {}
154 
SetUp()155   virtual void SetUp() OVERRIDE {
156     views::ViewsTestBase::SetUp();
157 
158     delegate_.reset(new TestLayoutDelegate);
159     layout_manager_ = new OpaqueBrowserFrameViewLayout(delegate_.get());
160     layout_manager_->set_extra_caption_y(0);
161     layout_manager_->set_window_caption_spacing(0);
162     widget_ = new Widget;
163     widget_->Init(CreateParams(Widget::InitParams::TYPE_POPUP));
164     root_view_ = widget_->GetRootView();
165     root_view_->SetSize(gfx::Size(kWidth, kWidth));
166     root_view_->SetLayoutManager(layout_manager_);
167 
168     // Add the caption buttons. We use fake images because we're modeling the
169     // Windows assets here, while the linux version uses differently sized
170     // assets.
171     //
172     // TODO(erg): In a follow up patch, separate these sizes out into virtual
173     // accessors so we can test both the windows and linux behaviours once we
174     // start modifying the code.
175     minimize_button_ = InitWindowCaptionButton(
176         VIEW_ID_MINIMIZE_BUTTON, gfx::Size(26, 18));
177     maximize_button_ = InitWindowCaptionButton(
178         VIEW_ID_MAXIMIZE_BUTTON, gfx::Size(25, 18));
179     restore_button_ = InitWindowCaptionButton(
180         VIEW_ID_RESTORE_BUTTON, gfx::Size(25, 18));
181     close_button_ = InitWindowCaptionButton(
182         VIEW_ID_CLOSE_BUTTON, gfx::Size(43, 18));
183   }
184 
TearDown()185   virtual void TearDown() OVERRIDE {
186     widget_->CloseNow();
187 
188     views::ViewsTestBase::TearDown();
189   }
190 
191  protected:
InitWindowCaptionButton(ViewID view_id,const gfx::Size & size)192   views::ImageButton* InitWindowCaptionButton(ViewID view_id,
193                                               const gfx::Size& size) {
194     views::ImageButton* button = new views::ImageButton(NULL);
195     gfx::ImageSkiaRep rep(size, 1.0f);
196     gfx::ImageSkia image(rep);
197     button->SetImage(views::CustomButton::STATE_NORMAL, &image);
198     button->set_id(view_id);
199     root_view_->AddChildView(button);
200     return button;
201   }
202 
AddWindowTitleIcons()203   void AddWindowTitleIcons() {
204     tab_icon_view_ = new TabIconView(NULL);
205     tab_icon_view_->set_is_light(true);
206     tab_icon_view_->set_id(VIEW_ID_WINDOW_ICON);
207     root_view_->AddChildView(tab_icon_view_);
208 
209     window_title_ = new views::Label(delegate_->GetWindowTitle(),
210                                      default_font_);
211     window_title_->SetVisible(delegate_->ShouldShowWindowTitle());
212     window_title_->SetEnabledColor(SK_ColorWHITE);
213     window_title_->SetBackgroundColor(0x00000000);
214     window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
215     window_title_->set_id(VIEW_ID_WINDOW_TITLE);
216     root_view_->AddChildView(window_title_);
217   }
218 
AddAvatarButton()219   void AddAvatarButton() {
220     menu_button_ = new AvatarMenuButton(NULL, false);
221     menu_button_->set_id(VIEW_ID_AVATAR_BUTTON);
222     delegate_->SetShouldShowAvatar(true);
223     root_view_->AddChildView(menu_button_);
224   }
225 
AddAvatarLabel()226   void AddAvatarLabel() {
227     avatar_label_ = new views::MenuButton(NULL, base::string16(), NULL, false);
228     avatar_label_->set_id(VIEW_ID_AVATAR_LABEL);
229     root_view_->AddChildView(avatar_label_);
230 
231     // The avatar label should only be used together with the avatar button.
232     AddAvatarButton();
233   }
234 
AddNewAvatarButton()235   void AddNewAvatarButton() {
236    new_avatar_button_ =
237        new views::MenuButton(NULL, base::string16(), NULL, false);
238    new_avatar_button_->set_id(VIEW_ID_NEW_AVATAR_BUTTON);
239    root_view_->AddChildView(new_avatar_button_);
240   }
241 
ExpectBasicWindowBounds()242   void ExpectBasicWindowBounds() {
243     EXPECT_EQ("428,1 25x18", maximize_button_->bounds().ToString());
244     EXPECT_EQ("402,1 26x18", minimize_button_->bounds().ToString());
245     EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString());
246     EXPECT_EQ("453,1 43x18", close_button_->bounds().ToString());
247   }
248 
249   gfx::Font default_font_;
250 
251   Widget* widget_;
252   views::View* root_view_;
253   OpaqueBrowserFrameViewLayout* layout_manager_;
254   scoped_ptr<TestLayoutDelegate> delegate_;
255 
256   // Widgets:
257   views::ImageButton* minimize_button_;
258   views::ImageButton* maximize_button_;
259   views::ImageButton* restore_button_;
260   views::ImageButton* close_button_;
261 
262   TabIconView* tab_icon_view_;
263   views::Label* window_title_;
264 
265   AvatarMenuButton* menu_button_;
266   views::MenuButton* avatar_label_;
267   views::MenuButton* new_avatar_button_;
268 
269   DISALLOW_COPY_AND_ASSIGN(OpaqueBrowserFrameViewLayoutTest);
270 };
271 
TEST_F(OpaqueBrowserFrameViewLayoutTest,BasicWindow)272 TEST_F(OpaqueBrowserFrameViewLayoutTest, BasicWindow) {
273   // Tests the layout of a default chrome window with no avatars, no window
274   // titles, and a tabstrip.
275   root_view_->Layout();
276 
277   ExpectBasicWindowBounds();
278 
279   // After some visual inspection, it really does look like the tabstrip is
280   // initally positioned out of our view.
281   EXPECT_EQ("-1,13 398x29",
282             layout_manager_->GetBoundsForTabStrip(
283                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
284   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
285 
286   // A normal window with no window icon still produces icon bounds for
287   // Windows, which has a hidden icon that a user can double click on to close
288   // the window.
289   EXPECT_EQ("6,4 17x17", layout_manager_->IconBounds().ToString());
290 }
291 
TEST_F(OpaqueBrowserFrameViewLayoutTest,BasicWindowMaximized)292 TEST_F(OpaqueBrowserFrameViewLayoutTest, BasicWindowMaximized) {
293   // Tests the layout of a default chrome window with no avatars, no window
294   // titles, and a tabstrip, but maximized this time.
295   delegate_->SetWindowState(TestLayoutDelegate::STATE_MAXIMIZED);
296   root_view_->Layout();
297 
298   // Note how the bonds start at the exact top of the window while maximized
299   // while they start 1 pixel below when unmaximized.
300   EXPECT_EQ("0,0 0x0", maximize_button_->bounds().ToString());
301   EXPECT_EQ("403,0 26x18", minimize_button_->bounds().ToString());
302   EXPECT_EQ("429,0 25x18", restore_button_->bounds().ToString());
303   EXPECT_EQ("454,0 46x18", close_button_->bounds().ToString());
304 
305   EXPECT_EQ("-5,-3 392x29",
306             layout_manager_->GetBoundsForTabStrip(
307                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
308   EXPECT_EQ("262x61", layout_manager_->GetMinimumSize(kWidth).ToString());
309 
310   // In the maximized case, OpaqueBrowserFrameView::NonClientHitTest() uses
311   // this rect, extended to the top left corner of the window.
312   EXPECT_EQ("2,0 17x17", layout_manager_->IconBounds().ToString());
313 }
314 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowButtonsOnLeft)315 TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowButtonsOnLeft) {
316   // Tests the layout of a chrome window with caption buttons on the left.
317   std::vector<views::FrameButton> leading_buttons;
318   std::vector<views::FrameButton> trailing_buttons;
319   leading_buttons.push_back(views::FRAME_BUTTON_CLOSE);
320   leading_buttons.push_back(views::FRAME_BUTTON_MINIMIZE);
321   leading_buttons.push_back(views::FRAME_BUTTON_MAXIMIZE);
322   layout_manager_->SetButtonOrdering(leading_buttons, trailing_buttons);
323   root_view_->Layout();
324 
325   EXPECT_EQ("73,1 25x18", maximize_button_->bounds().ToString());
326   EXPECT_EQ("47,1 26x18", minimize_button_->bounds().ToString());
327   EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString());
328   EXPECT_EQ("4,1 43x18", close_button_->bounds().ToString());
329 
330   EXPECT_EQ("93,13 398x29",
331             layout_manager_->GetBoundsForTabStrip(
332                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
333   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
334 
335   // If the buttons are on the left, there should be no hidden icon for the user
336   // to double click.
337   EXPECT_EQ("0,0 0x0", layout_manager_->IconBounds().ToString());
338 }
339 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WithoutCaptionButtons)340 TEST_F(OpaqueBrowserFrameViewLayoutTest, WithoutCaptionButtons) {
341   // Tests the layout of a default chrome window with no caption buttons.
342   delegate_->SetShouldShowCaptionButtons(false);
343   root_view_->Layout();
344 
345   EXPECT_EQ("0,0 0x0", maximize_button_->bounds().ToString());
346   EXPECT_EQ("0,0 0x0", minimize_button_->bounds().ToString());
347   EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString());
348   EXPECT_EQ("0,0 0x0", close_button_->bounds().ToString());
349 
350   EXPECT_EQ("-1,13 492x29",
351             layout_manager_->GetBoundsForTabStrip(
352                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
353   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
354 
355   // A normal window with no window icon still produces icon bounds for
356   // Windows, which has a hidden icon that a user can double click on to close
357   // the window.
358   EXPECT_EQ("6,4 17x17", layout_manager_->IconBounds().ToString());
359 }
360 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WithWindowTitleAndIcon)361 TEST_F(OpaqueBrowserFrameViewLayoutTest, WithWindowTitleAndIcon) {
362   // Tests the layout of pop up windows.
363   delegate_->SetWindowTitle(ASCIIToUTF16("Window Title"));
364   AddWindowTitleIcons();
365   root_view_->Layout();
366 
367   // We should have the right hand side should match the BasicWindow case.
368   ExpectBasicWindowBounds();
369 
370   // Check the location of the tab icon and window title.
371   EXPECT_EQ("6,3 17x17", tab_icon_view_->bounds().ToString());
372   EXPECT_EQ("27,3 370x17", window_title_->bounds().ToString());
373 }
374 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowWithAvatar)375 TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatar) {
376   // Tests a normal tabstrip window with an avatar icon.
377   AddAvatarButton();
378   root_view_->Layout();
379 
380   ExpectBasicWindowBounds();
381 
382   // Check the location of the avatar
383   EXPECT_EQ("7,11 40x29", menu_button_->bounds().ToString());
384   EXPECT_EQ("45,13 352x29",
385             layout_manager_->GetBoundsForTabStrip(
386                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
387   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
388 }
389 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowWithAvatarWithButtonsOnLeft)390 TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatarWithButtonsOnLeft) {
391   // Tests the layout of a chrome window with an avatar icon and caption buttons
392   // on the left. The avatar icon should therefore be on the right.
393   AddAvatarButton();
394   std::vector<views::FrameButton> leading_buttons;
395   std::vector<views::FrameButton> trailing_buttons;
396   leading_buttons.push_back(views::FRAME_BUTTON_CLOSE);
397   leading_buttons.push_back(views::FRAME_BUTTON_MINIMIZE);
398   leading_buttons.push_back(views::FRAME_BUTTON_MAXIMIZE);
399   layout_manager_->SetButtonOrdering(leading_buttons, trailing_buttons);
400   root_view_->Layout();
401 
402   EXPECT_EQ("73,1 25x18", maximize_button_->bounds().ToString());
403   EXPECT_EQ("47,1 26x18", minimize_button_->bounds().ToString());
404   EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString());
405   EXPECT_EQ("4,1 43x18", close_button_->bounds().ToString());
406 
407   // Check the location of the avatar
408   EXPECT_EQ("454,11 40x29", menu_button_->bounds().ToString());
409   EXPECT_EQ("93,13 356x29",
410             layout_manager_->GetBoundsForTabStrip(
411                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
412   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
413 
414   // This means that the menu will pop out facing the left (if it were to face
415   // the right, it would go outside the window frame and be clipped).
416   EXPECT_TRUE(menu_button_->button_on_right());
417 
418   // If the buttons are on the left, there should be no hidden icon for the user
419   // to double click.
420   EXPECT_EQ("0,0 0x0", layout_manager_->IconBounds().ToString());
421 }
422 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowWithAvatarWithoutCaptionButtonsOnLeft)423 TEST_F(OpaqueBrowserFrameViewLayoutTest,
424        WindowWithAvatarWithoutCaptionButtonsOnLeft) {
425   // Tests the layout of a chrome window with an avatar icon and no caption
426   // buttons. However, the caption buttons *would* be on the left if they
427   // weren't hidden, and therefore, the avatar icon should be on the right.
428   AddAvatarButton();
429   std::vector<views::FrameButton> leading_buttons;
430   std::vector<views::FrameButton> trailing_buttons;
431   leading_buttons.push_back(views::FRAME_BUTTON_CLOSE);
432   leading_buttons.push_back(views::FRAME_BUTTON_MINIMIZE);
433   leading_buttons.push_back(views::FRAME_BUTTON_MAXIMIZE);
434   layout_manager_->SetButtonOrdering(leading_buttons, trailing_buttons);
435   delegate_->SetShouldShowCaptionButtons(false);
436   root_view_->Layout();
437 
438   EXPECT_EQ("0,0 0x0", maximize_button_->bounds().ToString());
439   EXPECT_EQ("0,0 0x0", minimize_button_->bounds().ToString());
440   EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString());
441   EXPECT_EQ("0,0 0x0", close_button_->bounds().ToString());
442 
443   // Check the location of the avatar
444   EXPECT_EQ("454,11 40x29", menu_button_->bounds().ToString());
445   EXPECT_EQ("-1,13 450x29",
446             layout_manager_->GetBoundsForTabStrip(
447                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
448   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
449 
450   // A normal window with no window icon still produces icon bounds for
451   // Windows, which has a hidden icon that a user can double click on to close
452   // the window.
453   EXPECT_EQ("6,4 17x17", layout_manager_->IconBounds().ToString());
454 }
455 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowWithNewAvatar)456 TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithNewAvatar) {
457   CommandLine::ForCurrentProcess()->AppendSwitch(
458       switches::kNewProfileManagement);
459 
460   // Tests a normal tabstrip window with the new style avatar icon.
461   AddNewAvatarButton();
462   root_view_->Layout();
463 
464   ExpectBasicWindowBounds();
465 
466   // Check the location of the caption button
467   EXPECT_EQ("385,1 12x20", new_avatar_button_->bounds().ToString());
468   // The basic window bounds are (-1, 13 398x29). There should not be an icon
469   // avatar in the left, and the new avatar button has an offset of 5 to its
470   // next control.
471   EXPECT_EQ("-1,13 381x29",
472             layout_manager_->GetBoundsForTabStrip(
473                 delegate_->GetTabstripPreferredSize(), kWidth).ToString());
474   EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString());
475 }
476 
TEST_F(OpaqueBrowserFrameViewLayoutTest,WindowWithAvatarLabelAndButton)477 TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatarLabelAndButton) {
478   AddAvatarLabel();
479   root_view_->Layout();
480 
481   ExpectBasicWindowBounds();
482 
483   // Check the location of the avatar label relative to the avatar button.
484   // The label height and width depends on the font size and the text displayed.
485   // This may possibly change, so we don't test it here.
486   EXPECT_EQ(menu_button_->bounds().x() - 2, avatar_label_->bounds().x());
487   EXPECT_EQ(
488       menu_button_->bounds().bottom() - 3 - avatar_label_->bounds().height(),
489       avatar_label_->bounds().y());
490 }
491