opaque_browser_frame_view_layout_unittest.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/tab_icon_view.h" 11#include "chrome/browser/ui/views/tabs/tab.h" 12#include "chrome/common/chrome_switches.h" 13#include "ui/gfx/image/image_skia.h" 14#include "ui/gfx/image/image_skia_rep.h" 15#include "ui/gfx/text_constants.h" 16#include "ui/views/controls/button/image_button.h" 17#include "ui/views/controls/button/menu_button.h" 18#include "ui/views/controls/label.h" 19#include "ui/views/test/views_test_base.h" 20 21using views::Widget; 22 23namespace { 24 25const int kWidth = 500; 26 27class TestLayoutDelegate : public OpaqueBrowserFrameViewLayoutDelegate { 28 public: 29 enum WindowState { 30 STATE_NORMAL, 31 STATE_MAXIMIZED, 32 STATE_MINIMIZED, 33 STATE_FULLSCREEN 34 }; 35 36 TestLayoutDelegate() 37 : show_avatar_(false), 38 window_state_(STATE_NORMAL) { 39 } 40 41 virtual ~TestLayoutDelegate() {} 42 43 void SetWindowTitle(const base::string16& title) { 44 window_title_ = title; 45 } 46 47 void SetShouldShowAvatar(bool show_avatar) { 48 show_avatar_ = show_avatar; 49 } 50 51 void SetWindowState(WindowState state) { 52 window_state_ = state; 53 } 54 55 virtual bool ShouldShowWindowIcon() const OVERRIDE { 56 return !window_title_.empty(); 57 } 58 59 virtual bool ShouldShowWindowTitle() const OVERRIDE { 60 return !window_title_.empty(); 61 } 62 63 virtual base::string16 GetWindowTitle() const OVERRIDE { 64 return window_title_; 65 } 66 67 virtual int GetIconSize() const OVERRIDE { 68 // The value on linux_aura and non-aura windows. 69 return 17; 70 } 71 72 virtual bool ShouldLeaveOffsetNearTopBorder() const OVERRIDE { 73 return !IsMaximized(); 74 } 75 76 virtual gfx::Size GetBrowserViewMinimumSize() const OVERRIDE { 77 // Taken from a calculation in BrowserViewLayout. 78 return gfx::Size(168, 64); 79 } 80 81 virtual bool ShouldShowAvatar() const OVERRIDE { 82 return show_avatar_; 83 } 84 85 virtual bool IsRegularOrGuestSession() const OVERRIDE { 86 return true; 87 } 88 89 virtual gfx::ImageSkia GetOTRAvatarIcon() const OVERRIDE { 90 // The calculations depend on the size of the OTR resource, and chromeos 91 // uses a different sized image, so hard code the size of the current 92 // windows/linux one. 93 gfx::ImageSkiaRep rep(gfx::Size(40, 29), 1.0f); 94 gfx::ImageSkia image(rep); 95 return image; 96 } 97 98 virtual bool IsMaximized() const OVERRIDE { 99 return window_state_ == STATE_MAXIMIZED; 100 } 101 102 virtual bool IsMinimized() const OVERRIDE { 103 return window_state_ == STATE_MINIMIZED; 104 } 105 106 virtual bool IsFullscreen() const OVERRIDE { 107 return window_state_ == STATE_FULLSCREEN; 108 } 109 110 virtual bool IsTabStripVisible() const OVERRIDE { 111 return window_title_.empty(); 112 } 113 114 virtual int GetTabStripHeight() const OVERRIDE { 115 return IsTabStripVisible() ? Tab::GetMinimumUnselectedSize().height() : 0; 116 } 117 118 virtual int GetAdditionalReservedSpaceInTabStrip() const OVERRIDE { 119 return 0; 120 } 121 122 virtual gfx::Size GetTabstripPreferredSize() const OVERRIDE { 123 // Measured from Tabstrip::GetPreferredSize(). 124 return IsTabStripVisible() ? gfx::Size(78, 29) : gfx::Size(0, 0); 125 } 126 127 private: 128 base::string16 window_title_; 129 bool show_avatar_; 130 WindowState window_state_; 131 132 DISALLOW_COPY_AND_ASSIGN(TestLayoutDelegate); 133}; 134 135} // namespace 136 137class OpaqueBrowserFrameViewLayoutTest : public views::ViewsTestBase { 138 public: 139 OpaqueBrowserFrameViewLayoutTest() {} 140 virtual ~OpaqueBrowserFrameViewLayoutTest() {} 141 142 virtual void SetUp() OVERRIDE { 143 views::ViewsTestBase::SetUp(); 144 145 delegate_.reset(new TestLayoutDelegate); 146 layout_manager_ = new OpaqueBrowserFrameViewLayout(delegate_.get()); 147 layout_manager_->set_extra_caption_y(0); 148 layout_manager_->set_window_caption_spacing(0); 149 widget_ = new Widget; 150 widget_->Init(CreateParams(Widget::InitParams::TYPE_POPUP)); 151 root_view_ = widget_->GetRootView(); 152 root_view_->SetSize(gfx::Size(kWidth, kWidth)); 153 root_view_->SetLayoutManager(layout_manager_); 154 155 // Add the caption buttons. We use fake images because we're modeling the 156 // Windows assets here, while the linux version uses differently sized 157 // assets. 158 // 159 // TODO(erg): In a follow up patch, separate these sizes out into virtual 160 // accessors so we can test both the windows and linux behaviours once we 161 // start modifying the code. 162 minimize_button_ = InitWindowCaptionButton( 163 VIEW_ID_MINIMIZE_BUTTON, gfx::Size(26, 18)); 164 maximize_button_ = InitWindowCaptionButton( 165 VIEW_ID_MAXIMIZE_BUTTON, gfx::Size(25, 18)); 166 restore_button_ = InitWindowCaptionButton( 167 VIEW_ID_RESTORE_BUTTON, gfx::Size(25, 18)); 168 close_button_ = InitWindowCaptionButton( 169 VIEW_ID_CLOSE_BUTTON, gfx::Size(43, 18)); 170 } 171 172 virtual void TearDown() OVERRIDE { 173 widget_->CloseNow(); 174 175 views::ViewsTestBase::TearDown(); 176 } 177 178 protected: 179 views::ImageButton* InitWindowCaptionButton(ViewID view_id, 180 const gfx::Size& size) { 181 views::ImageButton* button = new views::ImageButton(NULL); 182 gfx::ImageSkiaRep rep(size, 1.0f); 183 gfx::ImageSkia image(rep); 184 button->SetImage(views::CustomButton::STATE_NORMAL, &image); 185 button->set_id(view_id); 186 root_view_->AddChildView(button); 187 return button; 188 } 189 190 void AddWindowTitleIcons() { 191 tab_icon_view_ = new TabIconView(NULL); 192 tab_icon_view_->set_is_light(true); 193 tab_icon_view_->set_id(VIEW_ID_WINDOW_ICON); 194 root_view_->AddChildView(tab_icon_view_); 195 196 window_title_ = new views::Label(delegate_->GetWindowTitle(), 197 default_font_); 198 window_title_->SetVisible(delegate_->ShouldShowWindowTitle()); 199 window_title_->SetEnabledColor(SK_ColorWHITE); 200 window_title_->SetBackgroundColor(0x00000000); 201 window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 202 window_title_->set_id(VIEW_ID_WINDOW_TITLE); 203 root_view_->AddChildView(window_title_); 204 } 205 206 void AddAvatarButton() { 207 menu_button_ = new views::MenuButton(NULL, string16(), NULL, false); 208 menu_button_->set_id(VIEW_ID_AVATAR_BUTTON); 209 delegate_->SetShouldShowAvatar(true); 210 root_view_->AddChildView(menu_button_); 211 } 212 213 void AddAvatarLabel() { 214 avatar_label_ = new views::MenuButton(NULL, string16(), NULL, false); 215 avatar_label_->set_id(VIEW_ID_AVATAR_LABEL); 216 root_view_->AddChildView(avatar_label_); 217 218 // The avatar label should only be used together with the avatar button. 219 AddAvatarButton(); 220 } 221 222 void AddNewAvatarButton() { 223 new_avatar_button_ = new views::MenuButton(NULL, string16(), NULL, false); 224 new_avatar_button_->set_id(VIEW_ID_NEW_AVATAR_BUTTON); 225 root_view_->AddChildView(new_avatar_button_); 226 } 227 228 void ExpectBasicWindowBounds() { 229 EXPECT_EQ("428,1 25x18", maximize_button_->bounds().ToString()); 230 EXPECT_EQ("402,1 26x18", minimize_button_->bounds().ToString()); 231 EXPECT_EQ("0,0 0x0", restore_button_->bounds().ToString()); 232 EXPECT_EQ("453,1 43x18", close_button_->bounds().ToString()); 233 } 234 235 gfx::Font default_font_; 236 237 Widget* widget_; 238 views::View* root_view_; 239 OpaqueBrowserFrameViewLayout* layout_manager_; 240 scoped_ptr<TestLayoutDelegate> delegate_; 241 242 // Widgets: 243 views::ImageButton* minimize_button_; 244 views::ImageButton* maximize_button_; 245 views::ImageButton* restore_button_; 246 views::ImageButton* close_button_; 247 248 TabIconView* tab_icon_view_; 249 views::Label* window_title_; 250 251 views::MenuButton* menu_button_; 252 views::MenuButton* avatar_label_; 253 views::MenuButton* new_avatar_button_; 254 255 DISALLOW_COPY_AND_ASSIGN(OpaqueBrowserFrameViewLayoutTest); 256}; 257 258TEST_F(OpaqueBrowserFrameViewLayoutTest, BasicWindow) { 259 // Tests the layout of a default chrome window with no avatars, no window 260 // titles, and a tabstrip. 261 root_view_->Layout(); 262 263 ExpectBasicWindowBounds(); 264 265 // After some visual inspection, it really does look like the tabstrip is 266 // initally positioned out of our view. 267 EXPECT_EQ("-1,13 398x29", 268 layout_manager_->GetBoundsForTabStrip( 269 delegate_->GetTabstripPreferredSize(), kWidth).ToString()); 270 EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString()); 271 272 // A normal window with no window icon still produces icon bounds for 273 // Windows, which has a hidden icon that a user can double click on to close 274 // the window. 275 EXPECT_EQ("6,4 17x17", layout_manager_->IconBounds().ToString()); 276} 277 278TEST_F(OpaqueBrowserFrameViewLayoutTest, BasicWindowMaximized) { 279 // Tests the layout of a default chrome window with no avatars, no window 280 // titles, and a tabstrip, but maximized this time. 281 delegate_->SetWindowState(TestLayoutDelegate::STATE_MAXIMIZED); 282 root_view_->Layout(); 283 284 // Note how the bonds start at the exact top of the window while maximized 285 // while they start 1 pixel below when unmaximized. 286 EXPECT_EQ("0,0 0x0", maximize_button_->bounds().ToString()); 287 EXPECT_EQ("403,0 26x18", minimize_button_->bounds().ToString()); 288 EXPECT_EQ("429,0 25x18", restore_button_->bounds().ToString()); 289 EXPECT_EQ("454,0 46x18", close_button_->bounds().ToString()); 290 291 EXPECT_EQ("-5,-3 392x29", 292 layout_manager_->GetBoundsForTabStrip( 293 delegate_->GetTabstripPreferredSize(), kWidth).ToString()); 294 EXPECT_EQ("262x61", layout_manager_->GetMinimumSize(kWidth).ToString()); 295 296 // In the maximized case, OpaqueBrowserFrameView::NonClientHitTest() uses 297 // this rect, extended to the top left corner of the window. 298 EXPECT_EQ("2,0 17x17", layout_manager_->IconBounds().ToString()); 299} 300 301TEST_F(OpaqueBrowserFrameViewLayoutTest, WithWindowTitleAndIcon) { 302 // Tests the layout of pop up windows. 303 delegate_->SetWindowTitle(ASCIIToUTF16("Window Title")); 304 AddWindowTitleIcons(); 305 root_view_->Layout(); 306 307 // We should have the right hand side should match the BasicWindow case. 308 ExpectBasicWindowBounds(); 309 310 // Check the location of the tab icon and window title. 311 EXPECT_EQ("6,3 17x17", tab_icon_view_->bounds().ToString()); 312 EXPECT_EQ("27,3 370x17", window_title_->bounds().ToString()); 313} 314 315TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatar) { 316 // Tests a normal tabstrip window with an avatar icon. 317 AddAvatarButton(); 318 root_view_->Layout(); 319 320 ExpectBasicWindowBounds(); 321 322 // Check the location of the avatar 323 EXPECT_EQ("7,11 40x29", menu_button_->bounds().ToString()); 324 EXPECT_EQ("45,13 352x29", 325 layout_manager_->GetBoundsForTabStrip( 326 delegate_->GetTabstripPreferredSize(), kWidth).ToString()); 327 EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString()); 328} 329 330 331TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithNewAvatar) { 332 CommandLine::ForCurrentProcess()->AppendSwitch( 333 switches::kNewProfileManagement); 334 335 // Tests a normal tabstrip window with the new style avatar icon. 336 AddNewAvatarButton(); 337 root_view_->Layout(); 338 339 ExpectBasicWindowBounds(); 340 341 // Check the location of the caption button 342 EXPECT_EQ("385,1 12x20", new_avatar_button_->bounds().ToString()); 343 // The basic window bounds are (-1, 13 398x29). There should not be an icon 344 // avatar in the left, and the new avatar button has an offset of 5 to its 345 // next control. 346 EXPECT_EQ("-1,13 381x29", 347 layout_manager_->GetBoundsForTabStrip( 348 delegate_->GetTabstripPreferredSize(), kWidth).ToString()); 349 EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString()); 350} 351 352TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatarLabelAndButton) { 353 AddAvatarLabel(); 354 root_view_->Layout(); 355 356 ExpectBasicWindowBounds(); 357 358 // Check the location of the avatar label relative to the avatar button. 359 // The label height and width depends on the font size and the text displayed. 360 // This may possibly change, so we don't test it here. 361 EXPECT_EQ(menu_button_->bounds().x() - 2, avatar_label_->bounds().x()); 362 EXPECT_EQ( 363 menu_button_->bounds().bottom() - 3 - avatar_label_->bounds().height(), 364 avatar_label_->bounds().y()); 365} 366