diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-12 21:03:02 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-12 21:03:02 +0000 |
commit | 2b2276608f079e2d59d2aeaf4321e1a2c0fa749c (patch) | |
tree | 22657c9c1924830843995bafca5507725aacc6bf /chrome/browser/ui/cocoa/tabpose_window.mm | |
parent | 5df02d2171c09e771800b6ece95fef6ff27d0436 (diff) | |
download | chromium_src-2b2276608f079e2d59d2aeaf4321e1a2c0fa749c.zip chromium_src-2b2276608f079e2d59d2aeaf4321e1a2c0fa749c.tar.gz chromium_src-2b2276608f079e2d59d2aeaf4321e1a2c0fa749c.tar.bz2 |
Make thumbnails closable in tabpose
Also make the thumbnails left-aligned. The reason for this is that when thumbnails are closed, the reshuffling algorithm looks a bit busy. We want to eventually slide thumbs out on the left side on the window and then slide them back in from the right.
BUG=50307
TEST=
* Hover a thumb. It should show a close button.
* Press opt. All thumbs should show a close button.
* Press a close button. Thumb should close, the rest of the tabs should reshuffle.
Review URL: http://codereview.chromium.org/5960008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71232 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa/tabpose_window.mm')
-rw-r--r-- | chrome/browser/ui/cocoa/tabpose_window.mm | 224 |
1 files changed, 165 insertions, 59 deletions
diff --git a/chrome/browser/ui/cocoa/tabpose_window.mm b/chrome/browser/ui/cocoa/tabpose_window.mm index 98d8611..70f499a 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 |