Index: chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm |
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm |
index f39fffe9ece1525c06912e55f1dfdb738a2c9e9f..d9b9abf12f6209156f9dea7dcd8eeed1e5b2c84d 100644 |
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm |
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm |
@@ -6,6 +6,7 @@ |
#import <QuartzCore/QuartzCore.h> |
+#include <cmath> |
#include <limits> |
#include <string> |
@@ -851,6 +852,7 @@ private: |
// Initialize |nonMiniTabWidth| in case there aren't any non-mini-tabs; this |
// value shouldn't actually be used. |
CGFloat nonMiniTabWidth = kMaxTabWidth; |
+ CGFloat nonMiniTabWidthFraction = 0; |
const NSInteger numberOfOpenNonMiniTabs = [self numberOfOpenNonMiniTabs]; |
if (numberOfOpenNonMiniTabs) { |
// Find the width of a non-mini-tab. This only applies to horizontal |
@@ -862,6 +864,11 @@ private: |
// Clamp the width between the max and min. |
nonMiniTabWidth = MAX(MIN(nonMiniTabWidth, kMaxTabWidth), kMinTabWidth); |
+ |
+ // Separate integral and fractional parts. |
+ CGFloat integralPart = std::floor(nonMiniTabWidth); |
+ nonMiniTabWidthFraction = nonMiniTabWidth - integralPart; |
+ nonMiniTabWidth = integralPart; |
} |
BOOL visible = [[tabStripView_ window] isVisible]; |
@@ -870,6 +877,8 @@ private: |
bool hasPlaceholderGap = false; |
// Whether or not the last tab processed by the loop was a mini tab. |
BOOL isLastTabMini = NO; |
+ CGFloat tabWidthAccumulatedFraction = 0; |
+ NSInteger laidOutNonMiniTabs = 0; |
for (TabController* tab in tabArray_.get()) { |
// Ignore a tab that is going through a close animation. |
if ([closingControllers_ containsObject:tab]) |
@@ -920,8 +929,31 @@ private: |
// Set the width. Selected tabs are slightly wider when things get really |
// small and thus we enforce a different minimum width. |
BOOL isMini = [tab mini]; |
- tabFrame.size.width = isMini ? |
- ([tab app] ? kAppTabWidth : kMiniTabWidth) : nonMiniTabWidth; |
+ if (isMini) { |
+ tabFrame.size.width = [tab app] ? kAppTabWidth : kMiniTabWidth; |
+ } else { |
+ // Tabs have non-integer widths. Assign the integer part to the tab, and |
+ // keep an accumulation of the fractional parts. When the fractional |
+ // accumulation gets to be more than one pixel, assign that to the current |
+ // tab being laid out. This is vaguely inspired by Bresenham's line |
+ // algorithm. |
+ tabFrame.size.width = nonMiniTabWidth; |
+ tabWidthAccumulatedFraction += nonMiniTabWidthFraction; |
+ |
+ if (tabWidthAccumulatedFraction >= 1.0) { |
+ ++tabFrame.size.width; |
+ --tabWidthAccumulatedFraction; |
+ } |
+ |
+ // In case of rounding error, give any left over pixels to the last tab. |
+ if (laidOutNonMiniTabs == numberOfOpenNonMiniTabs - 1 && |
+ tabWidthAccumulatedFraction > 0.5) { |
+ ++tabFrame.size.width; |
+ } |
+ |
+ ++laidOutNonMiniTabs; |
+ } |
+ |
if ([tab selected]) |
tabFrame.size.width = MAX(tabFrame.size.width, kMinSelectedTabWidth); |