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