blob: 29ee3b80bbe397403ea26b833701ceeee131db05 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/tabs/tab_strip_layout.h"
#include <stddef.h>
#include <algorithm>
#include "base/logging.h"
#include "ui/gfx/geometry/rect.h"
namespace {
// Calculates the size for normal tabs.
// |is_active_tab_normal| is true if the active tab is a normal tab, if false
// the active tab is not in the set of normal tabs.
// |normal_width| is the available width for normal tabs.
void CalculateNormalTabWidths(const TabSizeInfo& tab_size_info,
bool is_active_tab_normal,
int num_normal_tabs,
int normal_width,
int* active_width,
int* inactive_width) {
DCHECK_NE(0, num_normal_tabs);
*inactive_width = tab_size_info.min_inactive_width;
*active_width = tab_size_info.min_active_width;
// Calculate the desired tab widths by dividing the available space into equal
// portions. Don't let tabs get larger than the "standard width" or smaller
// than the minimum width for each type, respectively.
const int total_overlap = tab_size_info.tab_overlap * (num_normal_tabs - 1);
const int desired_tab_width =
std::min((normal_width + total_overlap) / num_normal_tabs,
tab_size_info.max_size.width());
*inactive_width =
std::max(desired_tab_width, tab_size_info.min_inactive_width);
*active_width = std::max(desired_tab_width, tab_size_info.min_active_width);
// |desired_tab_width| was calculated assuming the active and inactive tabs
// get the same width. If this isn't the case, then we need to recalculate
// the size for inactive tabs based on how big the active tab is.
if (*inactive_width != *active_width && is_active_tab_normal &&
num_normal_tabs > 1) {
*inactive_width = std::max(
(normal_width + total_overlap - *active_width) / (num_normal_tabs - 1),
tab_size_info.min_inactive_width);
}
}
} // namespace
void CalculateBoundsForPinnedTabs(const TabSizeInfo& tab_size_info,
int num_pinned_tabs,
int num_tabs,
std::vector<gfx::Rect>* tabs_bounds) {
DCHECK_EQ(static_cast<size_t>(num_tabs), tabs_bounds->size());
int index = 0;
int next_x = 0;
for (; index < num_pinned_tabs; ++index) {
(*tabs_bounds)[index].SetRect(next_x, 0, tab_size_info.pinned_tab_width,
tab_size_info.max_size.height());
next_x += tab_size_info.pinned_tab_width - tab_size_info.tab_overlap;
}
if (index > 0 && index < num_tabs) {
(*tabs_bounds)[index].set_x(next_x + tab_size_info.pinned_to_normal_offset);
}
}
std::vector<gfx::Rect> CalculateBounds(const TabSizeInfo& tab_size_info,
int num_pinned_tabs,
int num_tabs,
int active_index,
int width,
int* active_width,
int* inactive_width) {
DCHECK_NE(0, num_tabs);
std::vector<gfx::Rect> tabs_bounds(num_tabs);
*active_width = *inactive_width = tab_size_info.max_size.width();
int next_x = 0;
if (num_pinned_tabs) {
CalculateBoundsForPinnedTabs(tab_size_info, num_pinned_tabs, num_tabs,
&tabs_bounds);
if (num_pinned_tabs == num_tabs)
return tabs_bounds;
// CalculateBoundsForPinnedTabs() sets the x location of the first normal
// tab.
width -= tabs_bounds[num_pinned_tabs].x();
next_x = tabs_bounds[num_pinned_tabs].x();
}
const bool is_active_tab_normal = active_index >= num_pinned_tabs;
const int num_normal_tabs = num_tabs - num_pinned_tabs;
CalculateNormalTabWidths(tab_size_info, is_active_tab_normal, num_normal_tabs,
width, active_width, inactive_width);
// As CalculateNormalTabWidths() calculates sizes using ints there may be a
// bit of extra space (due to the available width not being an integer
// multiple of these sizes). Give the extra space to the first tabs, and only
// give extra space to the active tab if it is the same size as the inactive
// tabs (the active tab may already be bigger).
int expand_width_count = 0;
bool give_extra_space_to_active = false;
if (*inactive_width != tab_size_info.max_size.width()) {
give_extra_space_to_active = *active_width == *inactive_width;
expand_width_count =
width -
((*inactive_width - tab_size_info.tab_overlap) * (num_normal_tabs - 1));
expand_width_count -=
(is_active_tab_normal ? *active_width : *inactive_width);
}
// Set the ideal bounds of the normal tabs.
// GenerateIdealBoundsForPinnedTabs() set the ideal bounds of the pinned tabs.
for (int i = num_pinned_tabs; i < num_tabs; ++i) {
const bool is_active = i == active_index;
int width = is_active ? *active_width : *inactive_width;
if (expand_width_count > 0 && (!is_active || give_extra_space_to_active)) {
++width;
--expand_width_count;
}
tabs_bounds[i].SetRect(next_x, 0, width, tab_size_info.max_size.height());
next_x += tabs_bounds[i].width() - tab_size_info.tab_overlap;
}
return tabs_bounds;
}