1 // Copyright 2014 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.
5 #import "chrome/browser/ui/cocoa/browser_window_layout.h"
10 #include "base/logging.h"
11 #include "base/mac/mac_util.h"
12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
16 // The height of the tab strip.
17 const CGFloat kTabStripHeight = 37;
23 // Insets for the location bar, used when the full toolbar is hidden.
24 // TODO(viettrungluu): We can argue about the "correct" insetting; I like the
25 // following best, though arguably 0 inset is better/more correct.
26 const CGFloat kLocBarLeftRightInset = 1;
27 const CGFloat kLocBarTopInset = 0;
28 const CGFloat kLocBarBottomInset = 1;
30 // Space between the incognito badge and the right edge of the window.
31 const CGFloat kAvatarRightOffset = 4;
33 // Space between the location bar and the right edge of the window, when there
34 // are no extension buttons present.
35 // When there is a fullscreen button to the right of the new style profile
36 // button, we align the profile button with the location bar (although it won't
37 // be aligned when there are extension buttons).
38 const CGFloat kLocationBarRightOffset = 35;
42 @interface BrowserWindowLayout ()
44 // Computes the y offset to use when laying out the tab strip in fullscreen
46 - (void)computeFullscreenYOffset;
48 // Computes the layout of the tab strip.
49 - (void)computeTabStripLayout;
51 // Computes the layout of the subviews of the content view.
52 - (void)computeContentViewLayout;
54 // Computes the height of the backing bar for the views in the omnibox area in
56 - (CGFloat)fullscreenBackingBarHeight;
60 @implementation BrowserWindowLayout
62 - (instancetype)init {
63 if ((self = [super init])) {
64 parameters_.isOSYosemiteOrLater = base::mac::IsOSYosemiteOrLater();
69 - (chrome::LayoutOutput)computeLayout {
70 memset(&output_, 0, sizeof(chrome::LayoutOutput));
72 [self computeFullscreenYOffset];
73 [self computeTabStripLayout];
74 [self computeContentViewLayout];
79 - (void)setContentViewSize:(NSSize)size {
80 parameters_.contentViewSize = size;
83 - (void)setWindowSize:(NSSize)size {
84 parameters_.windowSize = size;
87 - (void)setInAnyFullscreen:(BOOL)inAnyFullscreen {
88 parameters_.inAnyFullscreen = inAnyFullscreen;
91 - (void)setFullscreenSlidingStyle:(fullscreen_mac::SlidingStyle)slidingStyle {
92 parameters_.slidingStyle = slidingStyle;
95 - (void)setFullscreenMenubarOffset:(CGFloat)menubarOffset {
96 parameters_.menubarOffset = menubarOffset;
99 - (void)setFullscreenToolbarFraction:(CGFloat)toolbarFraction {
100 parameters_.toolbarFraction = toolbarFraction;
103 - (void)setHasTabStrip:(BOOL)hasTabStrip {
104 parameters_.hasTabStrip = hasTabStrip;
107 - (void)setFullscreenButtonFrame:(NSRect)frame {
108 parameters_.fullscreenButtonFrame = frame;
111 - (void)setShouldShowAvatar:(BOOL)shouldShowAvatar {
112 parameters_.shouldShowAvatar = shouldShowAvatar;
115 - (void)setShouldUseNewAvatar:(BOOL)shouldUseNewAvatar {
116 parameters_.shouldUseNewAvatar = shouldUseNewAvatar;
119 - (void)setAvatarSize:(NSSize)avatarSize {
120 parameters_.avatarSize = avatarSize;
123 - (void)setAvatarLineWidth:(CGFloat)avatarLineWidth {
124 parameters_.avatarLineWidth = avatarLineWidth;
127 - (void)setHasToolbar:(BOOL)hasToolbar {
128 parameters_.hasToolbar = hasToolbar;
131 - (void)setHasLocationBar:(BOOL)hasLocationBar {
132 parameters_.hasLocationBar = hasLocationBar;
135 - (void)setToolbarHeight:(CGFloat)toolbarHeight {
136 parameters_.toolbarHeight = toolbarHeight;
139 - (void)setBookmarkBarHidden:(BOOL)bookmarkBarHidden {
140 parameters_.bookmarkBarHidden = bookmarkBarHidden;
143 - (void)setPlaceBookmarkBarBelowInfoBar:(BOOL)placeBookmarkBarBelowInfoBar {
144 parameters_.placeBookmarkBarBelowInfoBar = placeBookmarkBarBelowInfoBar;
147 - (void)setBookmarkBarHeight:(CGFloat)bookmarkBarHeight {
148 parameters_.bookmarkBarHeight = bookmarkBarHeight;
151 - (void)setInfoBarHeight:(CGFloat)infoBarHeight {
152 parameters_.infoBarHeight = infoBarHeight;
155 - (void)setPageInfoBubblePointY:(CGFloat)pageInfoBubblePointY {
156 parameters_.pageInfoBubblePointY = pageInfoBubblePointY;
159 - (void)setHasDownloadShelf:(BOOL)hasDownloadShelf {
160 parameters_.hasDownloadShelf = hasDownloadShelf;
163 - (void)setDownloadShelfHeight:(CGFloat)downloadShelfHeight {
164 parameters_.downloadShelfHeight = downloadShelfHeight;
167 - (void)computeFullscreenYOffset {
169 if (parameters_.inAnyFullscreen) {
170 yOffset += parameters_.menubarOffset;
171 switch (parameters_.slidingStyle) {
172 case fullscreen_mac::OMNIBOX_TABS_PRESENT:
174 case fullscreen_mac::OMNIBOX_TABS_NONE:
175 case fullscreen_mac::OMNIBOX_TABS_HIDDEN:
176 // In presentation mode, |yOffset| accounts for the sliding position of
177 // the floating bar and the extra offset needed to dodge the menu bar.
178 yOffset += std::floor((1 - parameters_.toolbarFraction) *
179 [self fullscreenBackingBarHeight]);
183 fullscreenYOffset_ = yOffset;
186 - (void)computeTabStripLayout {
187 if (!parameters_.hasTabStrip) {
188 maxY_ = parameters_.contentViewSize.height + fullscreenYOffset_;
192 // Temporary variable to hold the output.
193 chrome::TabStripLayout layout = {};
195 // Lay out the tab strip.
196 maxY_ = parameters_.windowSize.height + fullscreenYOffset_;
197 CGFloat width = parameters_.contentViewSize.width;
198 layout.frame = NSMakeRect(
199 0, maxY_ - chrome::kTabStripHeight, width, chrome::kTabStripHeight);
200 maxY_ = NSMinY(layout.frame);
202 // In Yosemite, there is no longer an exit fullscreen button in the top-right
203 // corner of the OSX Menu Bar. Instead, a Cocoa application's toolbar is
204 // expected to have traffic lights. Chrome doesn't use an NSToolbar, so it
205 // needs to manually add the traffic lights to the tab strip.
206 layout.addCustomWindowControls =
207 parameters_.inAnyFullscreen && parameters_.isOSYosemiteOrLater;
209 // Set left indentation based on fullscreen mode status.
210 if (!parameters_.inAnyFullscreen || layout.addCustomWindowControls)
211 layout.leftIndent = [TabStripController defaultLeftIndentForControls];
213 // Lay out the icognito/avatar badge because calculating the indentation on
214 // the right depends on it.
215 if (parameters_.shouldShowAvatar) {
216 CGFloat badgeXOffset = -kAvatarRightOffset;
217 CGFloat badgeYOffset = 0;
218 CGFloat buttonHeight = parameters_.avatarSize.height;
220 if (parameters_.shouldUseNewAvatar) {
221 // The fullscreen icon (if present) is displayed to the right of the
222 // new style profile button.
223 if (!NSIsEmptyRect(parameters_.fullscreenButtonFrame))
224 badgeXOffset = -kLocationBarRightOffset;
226 // Center the button, but make sure that it's pixel aligned on non-retina
227 // displays. Use trunc() instead of round() to mimic the behavior of
228 // autoresizesSubviews.
229 badgeYOffset = trunc((chrome::kTabStripHeight - buttonHeight) / 2);
231 // Actually place the badge *above* |maxY|, by +2 to miss the divider.
232 badgeYOffset = 2 * parameters_.avatarLineWidth;
235 NSSize size = NSMakeSize(parameters_.avatarSize.width,
236 std::min(buttonHeight, chrome::kTabStripHeight));
238 NSMakePoint(width - parameters_.avatarSize.width + badgeXOffset,
239 maxY_ + badgeYOffset);
241 NSMakeRect(origin.x, origin.y, size.width, size.height);
244 // Calculate the right indentation.
245 // On 10.7 Lion to 10.9 Mavericks, there will be a fullscreen button when not
246 // in fullscreen mode.
247 // There may also be a profile button, which can be on the right of the
248 // fullscreen button (old style), or to its left (new style).
249 // The right indentation is calculated to prevent the tab strip from
250 // overlapping these buttons.
251 CGFloat maxX = width;
252 if (!NSIsEmptyRect(parameters_.fullscreenButtonFrame)) {
253 maxX = NSMinX(parameters_.fullscreenButtonFrame);
255 if (parameters_.shouldShowAvatar) {
256 maxX = std::min(maxX, NSMinX(layout.avatarFrame));
258 layout.rightIndent = width - maxX;
260 output_.tabStripLayout = layout;
263 - (void)computeContentViewLayout {
264 chrome::LayoutParameters parameters = parameters_;
265 CGFloat maxY = maxY_;
267 // Sanity-check |maxY|.
269 DCHECK_LE(maxY, parameters_.contentViewSize.height + fullscreenYOffset_);
271 CGFloat width = parameters_.contentViewSize.width;
273 // Lay out the toolbar.
274 if (parameters.hasToolbar) {
275 output_.toolbarFrame = NSMakeRect(
276 0, maxY - parameters_.toolbarHeight, width, parameters_.toolbarHeight);
277 maxY = NSMinY(output_.toolbarFrame);
278 } else if (parameters_.hasLocationBar) {
279 CGFloat toolbarX = kLocBarLeftRightInset;
280 CGFloat toolbarY = maxY - parameters_.toolbarHeight - kLocBarTopInset;
281 CGFloat toolbarWidth = width - 2 * kLocBarLeftRightInset;
282 output_.toolbarFrame =
283 NSMakeRect(toolbarX, toolbarY, toolbarWidth, parameters_.toolbarHeight);
284 maxY = NSMinY(output_.toolbarFrame) - kLocBarBottomInset;
287 // Lay out the bookmark bar, if it's above the info bar.
288 if (!parameters.bookmarkBarHidden &&
289 !parameters.placeBookmarkBarBelowInfoBar) {
290 output_.bookmarkFrame = NSMakeRect(0,
291 maxY - parameters.bookmarkBarHeight,
293 parameters.bookmarkBarHeight);
294 maxY = NSMinY(output_.bookmarkFrame);
297 // Lay out the backing bar in fullscreen mode.
298 if (parameters_.inAnyFullscreen) {
299 output_.fullscreenBackingBarFrame =
300 NSMakeRect(0, maxY, width, [self fullscreenBackingBarHeight]);
303 // Place the find bar immediately below the toolbar/attached bookmark bar.
304 output_.findBarMaxY = maxY;
305 output_.fullscreenExitButtonMaxY = maxY;
307 if (parameters_.inAnyFullscreen &&
308 parameters_.slidingStyle == fullscreen_mac::OMNIBOX_TABS_HIDDEN) {
309 // If in presentation mode, reset |maxY| to top of screen, so that the
310 // floating bar slides over the things which appear to be in the content
312 maxY = parameters_.windowSize.height;
315 // Lay out the info bar. It is never hidden.
316 if (parameters_.infoBarHeight != 0) {
317 CGFloat infoBarMaxY = maxY;
318 CGFloat infoBarMinY = maxY - parameters_.infoBarHeight;
320 // If there's a toolbar, then the frame needs to be high enough to
321 // accomodate the top arrow, which might stretch all the way to the page
323 if (parameters_.hasToolbar) {
325 NSMinY(output_.toolbarFrame) + parameters.pageInfoBubblePointY;
328 output_.infoBarFrame =
329 NSMakeRect(0, infoBarMinY, width, infoBarMaxY - infoBarMinY);
330 output_.infoBarMaxTopArrowHeight =
331 NSHeight(output_.infoBarFrame) - parameters_.infoBarHeight;
332 maxY = NSMinY(output_.infoBarFrame);
334 // The info bar has 0 height, but tests still expect it in the right
336 output_.infoBarFrame = NSMakeRect(0, maxY, width, 0);
339 // Lay out the bookmark bar when it is below the info bar.
340 if (!parameters.bookmarkBarHidden &&
341 parameters.placeBookmarkBarBelowInfoBar) {
342 output_.bookmarkFrame = NSMakeRect(0,
343 maxY - parameters.bookmarkBarHeight,
345 parameters.bookmarkBarHeight);
346 maxY = NSMinY(output_.bookmarkFrame);
349 // Layout the download shelf at the bottom of the content view.
351 if (parameters.hasDownloadShelf) {
352 output_.downloadShelfFrame =
353 NSMakeRect(0, 0, width, parameters.downloadShelfHeight);
354 minY = NSMaxY(output_.downloadShelfFrame);
357 if (parameters_.inAnyFullscreen &&
358 parameters_.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT) {
359 // If in Canonical Fullscreen, content should be shifted down by an amount
360 // equal to all the widgets and views at the top of the window. It should
361 // not be further shifted by the appearance/disappearance of the AppKit
363 maxY = parameters_.windowSize.height;
364 maxY -= NSHeight(output_.toolbarFrame) +
365 NSHeight(output_.tabStripLayout.frame) +
366 NSHeight(output_.bookmarkFrame) + parameters.infoBarHeight;
369 // All the remaining space becomes the frame of the content area.
370 output_.contentAreaFrame = NSMakeRect(0, minY, width, maxY - minY);
373 - (CGFloat)fullscreenBackingBarHeight {
374 if (!parameters_.inAnyFullscreen)
377 CGFloat totalHeight = 0;
378 if (parameters_.hasTabStrip)
379 totalHeight += chrome::kTabStripHeight;
381 if (parameters_.hasToolbar) {
382 totalHeight += parameters_.toolbarHeight;
383 } else if (parameters_.hasLocationBar) {
385 parameters_.toolbarHeight + kLocBarTopInset + kLocBarBottomInset;
388 if (!parameters_.bookmarkBarHidden &&
389 !parameters_.placeBookmarkBarBelowInfoBar)
390 totalHeight += parameters_.bookmarkBarHeight;
397 @implementation BrowserWindowLayout (ExposedForTesting)
399 - (void)setOSYosemiteOrLater:(BOOL)osYosemiteOrLater {
400 parameters_.isOSYosemiteOrLater = osYosemiteOrLater;