| Index: chrome/browser/ui/cocoa/tabpose_window.mm
|
| diff --git a/chrome/browser/ui/cocoa/tabpose_window.mm b/chrome/browser/ui/cocoa/tabpose_window.mm
|
| index 98d861172964984e94dbe28a2b1e02783753ee8f..70f499a929443f1e5ee998bf2806318a0dfa8713 100644
|
| --- a/chrome/browser/ui/cocoa/tabpose_window.mm
|
| +++ b/chrome/browser/ui/cocoa/tabpose_window.mm
|
| @@ -6,6 +6,8 @@
|
|
|
| #import <QuartzCore/QuartzCore.h>
|
|
|
| +#include <algorithm>
|
| +
|
| #include "app/resource_bundle.h"
|
| #include "base/mac/mac_util.h"
|
| #include "base/mac/scoped_cftyperef.h"
|
| @@ -29,6 +31,7 @@
|
| #include "chrome/common/pref_names.h"
|
| #include "gfx/scoped_cg_context_state_mac.h"
|
| #include "grit/app_resources.h"
|
| +#include "grit/theme_resources.h"
|
| #include "skia/ext/skia_utils_mac.h"
|
| #include "third_party/skia/include/utils/mac/SkCGUtils.h"
|
|
|
| @@ -40,7 +43,8 @@ NSString* const kAnimationIdFadeOut = @"FadeOut";
|
|
|
| const CGFloat kDefaultAnimationDuration = 0.25; // In seconds.
|
| const CGFloat kSlomoFactor = 4;
|
| -const CGFloat kObserverChangeAnimationDuration = 0.75; // In seconds.
|
| +const CGFloat kObserverChangeAnimationDuration = 0.25; // In seconds.
|
| +const CGFloat kSelectionInset = 5;
|
|
|
| // CAGradientLayer is 10.6-only -- roll our own.
|
| @interface DarkGradientLayer : CALayer
|
| @@ -703,22 +707,6 @@ void TileSet::Layout(NSRect containing_rect) {
|
| tiles_[i]->start_thumb_rect_.origin = NSMakePoint(big_x, -big_y);
|
| }
|
| }
|
| -
|
| - // Go through last row and center it:
|
| - // X X X X
|
| - // X X X X
|
| - // X X
|
| - int last_row_empty_tiles_x = count_x() - last_row_count_x();
|
| - CGFloat small_last_row_shift_x =
|
| - last_row_empty_tiles_x * (small_width + kSmallPaddingX) / 2;
|
| - CGFloat big_last_row_shift_x =
|
| - last_row_empty_tiles_x * (NSWidth(containing_rect) + big_padding_x) / 2;
|
| - for (int i = tile_count - last_row_count_x(); i < tile_count; ++i) {
|
| - tiles_[i]->thumb_rect_.origin.x += small_last_row_shift_x;
|
| - tiles_[i]->start_thumb_rect_.origin.x += big_last_row_shift_x;
|
| - tiles_[i]->favicon_rect_.origin.x += small_last_row_shift_x;
|
| - tiles_[i]->title_rect_.origin.x += small_last_row_shift_x;
|
| - }
|
| }
|
|
|
| void TileSet::set_selected_index(int index) {
|
| @@ -915,9 +903,21 @@ void AnimateCALayerOpacityFromTo(
|
| rect:(NSRect)rect
|
| slomo:(BOOL)slomo
|
| tabStripModel:(TabStripModel*)tabStripModel;
|
| +
|
| +// Creates and initializes the CALayer in the background and all the CALayers
|
| +// for the thumbnails, favicons, and titles.
|
| - (void)setUpLayersInSlomo:(BOOL)slomo;
|
| -- (void)fadeAway:(BOOL)slomo;
|
| -- (void)selectTileAtIndex:(int)newIndex;
|
| +
|
| +// Tells the browser to make the tab corresponding to currently selected
|
| +// thumbnail the current tab and starts the tabpose exit animmation.
|
| +- (void)fadeAwayInSlomo:(BOOL)slomo;
|
| +
|
| +// Returns the CALayer for the close button belonging to the thumbnail at
|
| +// index |index|.
|
| +- (CALayer*)closebuttonLayerAtIndex:(NSUInteger)index;
|
| +
|
| +// Updates the visibility of all closebutton layers.
|
| +- (void)updateClosebuttonLayersVisibility;
|
| @end
|
|
|
| @implementation TabposeWindow
|
| @@ -946,6 +946,10 @@ void AnimateCALayerOpacityFromTo(
|
| tileSet_.reset(new tabpose::TileSet);
|
| tabStripModelObserverBridge_.reset(
|
| new TabStripModelObserverBridge(tabStripModel_, self));
|
| + NSImage* nsCloseIcon =
|
| + ResourceBundle::GetSharedInstance().GetNativeImageNamed(
|
| + IDR_TABPOSE_CLOSE);
|
| + closeIcon_.reset(base::mac::CopyNSImageToCGImage(nsCloseIcon));
|
| [self setReleasedWhenClosed:YES];
|
| [self setOpaque:NO];
|
| [self setBackgroundColor:[NSColor clearColor]];
|
| @@ -961,16 +965,15 @@ void AnimateCALayerOpacityFromTo(
|
| return [allThumbnailLayers_ objectAtIndex:tileSet_->selected_index()];
|
| }
|
|
|
| -- (void)selectTileAtIndex:(int)newIndex {
|
| +- (void)selectTileAtIndexWithoutAnimation:(int)newIndex {
|
| + ScopedCAActionDisabler disabler;
|
| const tabpose::Tile& tile = tileSet_->tile_at(newIndex);
|
| selectionHighlight_.frame =
|
| - NSRectToCGRect(NSInsetRect(tile.thumb_rect(), -5, -5));
|
| + NSRectToCGRect(NSInsetRect(tile.thumb_rect(),
|
| + -kSelectionInset, -kSelectionInset));
|
| tileSet_->set_selected_index(newIndex);
|
| -}
|
|
|
| -- (void)selectTileAtIndexWithoutAnimation:(int)newIndex {
|
| - ScopedCAActionDisabler disabler;
|
| - [self selectTileAtIndex:newIndex];
|
| + [self updateClosebuttonLayersVisibility];
|
| }
|
|
|
| - (void)addLayersForTile:(tabpose::Tile&)tile
|
| @@ -1004,6 +1007,27 @@ void AnimateCALayerOpacityFromTo(
|
| if (state_ == tabpose::kFadedIn)
|
| layer.get().shadowOpacity = 0.5;
|
|
|
| + // Add a close button to the thumb layer.
|
| + CALayer* closeLayer = [CALayer layer];
|
| + closeLayer.contents = reinterpret_cast<id>(closeIcon_.get());
|
| + CGRect closeBounds = {};
|
| + closeBounds.size.width = CGImageGetWidth(closeIcon_);
|
| + closeBounds.size.height = CGImageGetHeight(closeIcon_);
|
| + closeLayer.bounds = closeBounds;
|
| + closeLayer.hidden = YES;
|
| +
|
| + [closeLayer addConstraint:
|
| + [CAConstraint constraintWithAttribute:kCAConstraintMidX
|
| + relativeTo:@"superlayer"
|
| + attribute:kCAConstraintMinX]];
|
| + [closeLayer addConstraint:
|
| + [CAConstraint constraintWithAttribute:kCAConstraintMidY
|
| + relativeTo:@"superlayer"
|
| + attribute:kCAConstraintMaxY]];
|
| +
|
| + layer.get().layoutManager = [CAConstraintLayoutManager layoutManager];
|
| + [layer.get() addSublayer:closeLayer];
|
| +
|
| [bgLayer_ addSublayer:layer];
|
| [allThumbnailLayers_ addObject:layer];
|
|
|
| @@ -1200,11 +1224,11 @@ void AnimateCALayerOpacityFromTo(
|
| case NSNewlineCharacter:
|
| case NSCarriageReturnCharacter:
|
| case ' ':
|
| - [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + [self fadeAwayInSlomo:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| break;
|
| case '\e': // Escape
|
| tileSet_->set_selected_index(tabStripModel_->selected_index());
|
| - [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + [self fadeAwayInSlomo:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| break;
|
| }
|
| }
|
| @@ -1223,7 +1247,7 @@ void AnimateCALayerOpacityFromTo(
|
| character == '9' ? tabStripModel_->count() - 1 : character - '1';
|
| if (index < tabStripModel_->count()) {
|
| tileSet_->set_selected_index(index);
|
| - [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + [self fadeAwayInSlomo:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| return YES;
|
| }
|
| }
|
| @@ -1231,7 +1255,12 @@ void AnimateCALayerOpacityFromTo(
|
| return NO;
|
| }
|
|
|
| --(void)selectTileFromMouseEvent:(NSEvent*)event {
|
| +- (void)flagsChanged:(NSEvent*)event {
|
| + showAllCloseLayers_ = ([event modifierFlags] & NSAlternateKeyMask) != 0;
|
| + [self updateClosebuttonLayersVisibility];
|
| +}
|
| +
|
| +- (void)selectTileFromMouseEvent:(NSEvent*)event {
|
| int newIndex = -1;
|
| CGPoint p = NSPointToCGPoint([event locationInWindow]);
|
| for (NSUInteger i = 0; i < [allThumbnailLayers_ count]; ++i) {
|
| @@ -1248,16 +1277,46 @@ void AnimateCALayerOpacityFromTo(
|
| [self selectTileFromMouseEvent:event];
|
| }
|
|
|
| +- (CALayer*)closebuttonLayerAtIndex:(NSUInteger)index {
|
| + CALayer* layer = [allThumbnailLayers_ objectAtIndex:index];
|
| + return [[layer sublayers] objectAtIndex:0];
|
| +}
|
| +
|
| +- (void)updateClosebuttonLayersVisibility {
|
| + for (NSUInteger i = 0; i < [allThumbnailLayers_ count]; ++i) {
|
| + CALayer* layer = [self closebuttonLayerAtIndex:i];
|
| + BOOL isSelectedTile = static_cast<int>(i) == tileSet_->selected_index();
|
| + BOOL isVisible = state_ == tabpose::kFadedIn &&
|
| + (isSelectedTile || showAllCloseLayers_);
|
| + layer.hidden = !isVisible;
|
| + }
|
| +}
|
| +
|
| - (void)mouseDown:(NSEvent*)event {
|
| // Just in case the user clicked without ever moving the mouse.
|
| [self selectTileFromMouseEvent:event];
|
|
|
| - [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + // If the click occurred in a close box, close that tab and don't do anything
|
| + // else.
|
| + CGPoint p = NSPointToCGPoint([event locationInWindow]);
|
| + for (NSUInteger i = 0; i < [allThumbnailLayers_ count]; ++i) {
|
| + CALayer* layer = [self closebuttonLayerAtIndex:i];
|
| + CGPoint lp = [layer convertPoint:p fromLayer:rootLayer_];
|
| + if ([static_cast<CALayer*>([layer presentationLayer]) containsPoint:lp] &&
|
| + !layer.hidden) {
|
| + tabStripModel_->CloseTabContentsAt(i,
|
| + TabStripModel::CLOSE_USER_GESTURE |
|
| + TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + [self fadeAwayInSlomo:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| }
|
|
|
| - (void)swipeWithEvent:(NSEvent*)event {
|
| if (abs([event deltaY]) > 0.5) // Swipe up or down.
|
| - [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + [self fadeAwayInSlomo:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| }
|
|
|
| - (void)close {
|
| @@ -1273,7 +1332,7 @@ void AnimateCALayerOpacityFromTo(
|
|
|
| - (void)commandDispatch:(id)sender {
|
| if ([sender tag] == IDC_TABPOSE)
|
| - [self fadeAway:NO];
|
| + [self fadeAwayInSlomo:NO];
|
| }
|
|
|
| - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
|
| @@ -1329,7 +1388,7 @@ void AnimateCALayerOpacityFromTo(
|
| titleLayer.opacity = 0;
|
| }
|
|
|
| -- (void)fadeAway:(BOOL)slomo {
|
| +- (void)fadeAwayInSlomo:(BOOL)slomo {
|
| if (state_ == tabpose::kFadingOut)
|
| return;
|
|
|
| @@ -1355,6 +1414,8 @@ void AnimateCALayerOpacityFromTo(
|
| // running the exit animation.
|
| for (CALayer* layer in allThumbnailLayers_.get())
|
| layer.shadowOpacity = 0.0;
|
| +
|
| + [self updateClosebuttonLayersVisibility];
|
| }
|
|
|
| // Animate layers out, all in one transaction.
|
| @@ -1380,6 +1441,8 @@ void AnimateCALayerOpacityFromTo(
|
| ScopedCAActionDisabler disableCAActions;
|
| for (CALayer* layer in allThumbnailLayers_.get())
|
| layer.shadowOpacity = 0.5;
|
| +
|
| + [self updateClosebuttonLayersVisibility];
|
| }
|
| } else if ([animationId isEqualToString:kAnimationIdFadeOut]) {
|
| DCHECK_EQ(tabpose::kFadingOut, state_);
|
| @@ -1400,12 +1463,50 @@ void AnimateCALayerOpacityFromTo(
|
| - (void)refreshLayerFramesAtIndex:(int)i {
|
| const tabpose::Tile& tile = tileSet_->tile_at(i);
|
|
|
| + CALayer* thumbLayer = [allThumbnailLayers_ objectAtIndex:i];
|
| +
|
| + if (i == tileSet_->selected_index()) {
|
| + AnimateCALayerFrameFromTo(
|
| + selectionHighlight_,
|
| + NSInsetRect(NSRectFromCGRect(thumbLayer.frame),
|
| + -kSelectionInset, -kSelectionInset),
|
| + NSInsetRect(tile.thumb_rect(),
|
| + -kSelectionInset, -kSelectionInset),
|
| + kObserverChangeAnimationDuration,
|
| + nil);
|
| + }
|
| +
|
| + // Repaint layer if necessary.
|
| + if (!NSEqualSizes(NSRectFromCGRect(thumbLayer.frame).size,
|
| + tile.thumb_rect().size)) {
|
| + [thumbLayer setNeedsDisplay];
|
| + }
|
| +
|
| + // Use AnimateCALayerFrameFromTo() instead of just setting |frame| to let
|
| + // the animation match the selection animation --
|
| + // |kCAMediaTimingFunctionDefault| is 10.6-only.
|
| + AnimateCALayerFrameFromTo(
|
| + thumbLayer,
|
| + NSRectFromCGRect(thumbLayer.frame),
|
| + tile.thumb_rect(),
|
| + kObserverChangeAnimationDuration,
|
| + nil);
|
| +
|
| CALayer* faviconLayer = [allFaviconLayers_ objectAtIndex:i];
|
| - faviconLayer.frame = NSRectToCGRect(tile.favicon_rect());
|
| + AnimateCALayerFrameFromTo(
|
| + faviconLayer,
|
| + NSRectFromCGRect(faviconLayer.frame),
|
| + tile.favicon_rect(),
|
| + kObserverChangeAnimationDuration,
|
| + nil);
|
| +
|
| CALayer* titleLayer = [allTitleLayers_ objectAtIndex:i];
|
| - titleLayer.frame = NSRectToCGRect(tile.title_rect());
|
| - CALayer* thumbLayer = [allThumbnailLayers_ objectAtIndex:i];
|
| - thumbLayer.frame = NSRectToCGRect(tile.thumb_rect());
|
| + AnimateCALayerFrameFromTo(
|
| + titleLayer,
|
| + NSRectFromCGRect(titleLayer.frame),
|
| + tile.title_rect(),
|
| + kObserverChangeAnimationDuration,
|
| + nil);
|
| }
|
|
|
| - (void)insertTabWithContents:(TabContentsWrapper*)contents
|
| @@ -1431,17 +1532,18 @@ void AnimateCALayerOpacityFromTo(
|
| DCHECK_EQ(tabStripModel_->count(),
|
| static_cast<int>([allFaviconLayers_ count]));
|
|
|
| + // Update selection.
|
| + int selectedIndex = tileSet_->selected_index();
|
| + if (selectedIndex >= index)
|
| + selectedIndex++;
|
| + [self selectTileAtIndexWithoutAnimation:selectedIndex];
|
| +
|
| + // Animate everything into its new place.
|
| for (int i = 0; i < tabStripModel_->count(); ++i) {
|
| if (i == index) // The new layer.
|
| continue;
|
| [self refreshLayerFramesAtIndex:i];
|
| }
|
| -
|
| - // Update selection.
|
| - int selectedIndex = tileSet_->selected_index();
|
| - if (selectedIndex >= index)
|
| - selectedIndex++;
|
| - [self selectTileAtIndex:selectedIndex];
|
| }
|
|
|
| - (void)tabClosingWithContents:(TabContentsWrapper*)contents
|
| @@ -1458,12 +1560,15 @@ void AnimateCALayerOpacityFromTo(
|
| tileSet_->RemoveTileAt(index);
|
| tileSet_->Layout(containingRect_);
|
|
|
| - [[allThumbnailLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| - [allThumbnailLayers_ removeObjectAtIndex:index];
|
| - [[allTitleLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| - [allTitleLayers_ removeObjectAtIndex:index];
|
| - [[allFaviconLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| - [allFaviconLayers_ removeObjectAtIndex:index];
|
| + {
|
| + ScopedCAActionDisabler disabler;
|
| + [[allThumbnailLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| + [allThumbnailLayers_ removeObjectAtIndex:index];
|
| + [[allTitleLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| + [allTitleLayers_ removeObjectAtIndex:index];
|
| + [[allFaviconLayers_ objectAtIndex:index] removeFromSuperlayer];
|
| + [allFaviconLayers_ removeObjectAtIndex:index];
|
| + }
|
|
|
| // Update old layers.
|
| DCHECK_EQ(tabStripModel_->count(),
|
| @@ -1476,15 +1581,16 @@ void AnimateCALayerOpacityFromTo(
|
| if (tabStripModel_->count() == 0)
|
| [self close];
|
|
|
| - for (int i = 0; i < tabStripModel_->count(); ++i)
|
| - [self refreshLayerFramesAtIndex:i];
|
| -
|
| // Update selection.
|
| int selectedIndex = tileSet_->selected_index();
|
| - if (selectedIndex >= index)
|
| + if (selectedIndex > index || selectedIndex >= tabStripModel_->count())
|
| selectedIndex--;
|
| if (selectedIndex >= 0)
|
| - [self selectTileAtIndex:selectedIndex];
|
| + [self selectTileAtIndexWithoutAnimation:selectedIndex];
|
| +
|
| + // Animate everything into its new place.
|
| + for (int i = 0; i < tabStripModel_->count(); ++i)
|
| + [self refreshLayerFramesAtIndex:i];
|
| }
|
|
|
| - (void)tabMovedWithContents:(TabContentsWrapper*)contents
|
| @@ -1509,10 +1615,6 @@ void AnimateCALayerOpacityFromTo(
|
| [allTitleLayers_ removeObjectAtIndex:from];
|
| [allTitleLayers_ insertObject:titleLayer.get() atIndex:to];
|
|
|
| - // Update frames of the layers.
|
| - for (int i = std::min(from, to); i <= std::max(from, to); ++i)
|
| - [self refreshLayerFramesAtIndex:i];
|
| -
|
| // Update selection.
|
| int selectedIndex = tileSet_->selected_index();
|
| if (from == selectedIndex)
|
| @@ -1521,7 +1623,11 @@ void AnimateCALayerOpacityFromTo(
|
| selectedIndex--;
|
| else if (to <= selectedIndex && selectedIndex < from)
|
| selectedIndex++;
|
| - [self selectTileAtIndex:selectedIndex];
|
| + [self selectTileAtIndexWithoutAnimation:selectedIndex];
|
| +
|
| + // Update frames of the layers.
|
| + for (int i = std::min(from, to); i <= std::max(from, to); ++i)
|
| + [self refreshLayerFramesAtIndex:i];
|
| }
|
|
|
| - (void)tabChangedWithContents:(TabContentsWrapper*)contents
|
|
|