diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-11 00:43:28 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-11 00:43:28 +0000 |
commit | 93c1c66878c5f8fc76a4bd095ecbf537c2ed01e8 (patch) | |
tree | a0b955582d8c272b1c6bc8eb8784cfc07c938274 /chrome/browser/cocoa | |
parent | ed3d763b1fd7dddebd6cb8391027fe309ccaabd8 (diff) | |
download | chromium_src-93c1c66878c5f8fc76a4bd095ecbf537c2ed01e8.zip chromium_src-93c1c66878c5f8fc76a4bd095ecbf537c2ed01e8.tar.gz chromium_src-93c1c66878c5f8fc76a4bd095ecbf537c2ed01e8.tar.bz2 |
Mac tabpose: Add favicons and titles.
BUG=50307
TEST=Activate tabpose. The (white) thumbnails should have favicons and titles drawn below them.
Review URL: http://codereview.chromium.org/3159001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55643 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r-- | chrome/browser/cocoa/tabpose_window.h | 3 | ||||
-rw-r--r-- | chrome/browser/cocoa/tabpose_window.mm | 179 |
2 files changed, 163 insertions, 19 deletions
diff --git a/chrome/browser/cocoa/tabpose_window.h b/chrome/browser/cocoa/tabpose_window.h index bbcbf62..6e5893e 100644 --- a/chrome/browser/cocoa/tabpose_window.h +++ b/chrome/browser/cocoa/tabpose_window.h @@ -61,6 +61,9 @@ class TabStripModel; // the tabstrip model. scoped_nsobject<NSArray> allThumbnailLayers_; + scoped_nsobject<NSArray> allFaviconLayers_; + scoped_nsobject<NSArray> allTitleLayers_; + // Manages the state of all layers. scoped_ptr<tabpose::TileSet> tileSet_; } diff --git a/chrome/browser/cocoa/tabpose_window.mm b/chrome/browser/cocoa/tabpose_window.mm index 7a7862f..5f71989 100644 --- a/chrome/browser/cocoa/tabpose_window.mm +++ b/chrome/browser/cocoa/tabpose_window.mm @@ -6,8 +6,14 @@ #import <QuartzCore/QuartzCore.h> +#include "app/resource_bundle.h" +#include "base/mac_util.h" +#include "base/sys_string_conversions.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/tab_strip_controller.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "grit/app_resources.h" +#include "skia/ext/skia_utils_mac.h" const int kTopGradientHeight = 15; @@ -109,6 +115,24 @@ class Tile { NSRect GetStartRectRelativeTo(const Tile& tile) const; NSRect thumb_rect() const { return thumb_rect_; } + NSRect favicon_rect() const { return favicon_rect_; } + SkBitmap favicon() const; + + // This changes |title_rect| and |favicon_rect| such that the favicon is on + // the font's baseline and that the minimum distance between thumb rect and + // favicon and title rects doesn't change. + // The view code + // 1. queries desired font size by calling |title_font_size()| + // 2. loads that font + // 3. calls |set_font_metrics()| which updates the title rect + // 4. receives the title rect and puts the title on it with the font from 2. + void set_font_metrics(CGFloat ascender, CGFloat descender); + CGFloat title_font_size() const { return title_font_size_; } + + NSRect title_rect() const { return title_rect_; } + + // Returns an unelided title. The view logic is responsible for eliding. + const string16& title() const { return contents_->GetTitle(); } private: friend class TileSet; @@ -116,6 +140,13 @@ class Tile { // and devtools. NSRect thumb_rect_; NSRect start_thumb_rect_; + + NSRect favicon_rect_; + + CGFloat title_font_size_; + NSRect title_rect_; + + TabContents* contents_; // weak }; NSRect Tile::GetStartRectRelativeTo(const Tile& tile) const { @@ -125,6 +156,31 @@ NSRect Tile::GetStartRectRelativeTo(const Tile& tile) const { return rect; } +SkBitmap Tile::favicon() const { + if (contents_->is_app()) { + SkBitmap* icon = contents_->GetExtensionAppIcon(); + if (icon) + return *icon; + } + return contents_->GetFavIcon(); +} + +// Changes |title_rect| and |favicon_rect| such that the favicon is on the +// font's baseline and that the minimum distance between thumb rect and +// favicon and title rects doesn't change. +void Tile::set_font_metrics(CGFloat ascender, CGFloat descender) { + title_rect_.origin.y -= ascender + descender - NSHeight(title_rect_); + title_rect_.size.height = ascender + descender; + + if (NSHeight(favicon_rect_) < ascender) { + // Move favicon down. + favicon_rect_.origin.y = title_rect_.origin.y + descender; + } else { + // Move title down. + title_rect_.origin.y = favicon_rect_.origin.y - descender; + } +} + // A tileset is responsible for owning and laying out all |Tile|s shown in a // tabpose window. class TileSet { @@ -139,11 +195,12 @@ class TileSet { void set_selected_index(int index); void ResetSelectedIndex() { selected_index_ = initial_index_; } - const Tile& selected_tile() const { return tiles_[selected_index()]; } - const Tile& tile_at(int index) const { return tiles_[index]; } + const Tile& selected_tile() const { return *tiles_[selected_index()]; } + Tile& tile_at(int index) { return *tiles_[index]; } + const Tile& tile_at(int index) const { return *tiles_[index]; } private: - std::vector<Tile> tiles_; // Doesn't change often, hence values are fine. + std::vector<Tile*> tiles_; int selected_index_; int initial_index_; @@ -152,6 +209,10 @@ class TileSet { void TileSet::Build(TabStripModel* source_model) { selected_index_ = initial_index_ = source_model->selected_index(); tiles_.resize(source_model->count()); + for (size_t i = 0; i < tiles_.size(); ++i) { + tiles_[i] = new Tile; + tiles_[i]->contents_ = source_model->GetTabContentsAt(i); + } } void TileSet::Layout(NSRect containing_rect) { @@ -163,9 +224,19 @@ void TileSet::Layout(NSRect containing_rect) { const int kSmallPaddingRight = 30; const int kSmallPaddingBottom = 30; + // Favicon / title area. + const int kThumbTitlePaddingY = 6; + const int kFaviconSize = 16; + const int kTitleHeight = 14; // Font size. + const int kTitleExtraHeight = kThumbTitlePaddingY + kTitleHeight; + const int kFaviconExtraHeight = kThumbTitlePaddingY + kFaviconSize; + const int kFaviconTitleDistanceX = 6; + const int kFooterExtraHeight = + std::max(kFaviconExtraHeight, kTitleExtraHeight); + // Room between the tiles. const int kSmallPaddingX = 15; - const int kSmallPaddingY = 13; + const int kSmallPaddingY = kFooterExtraHeight; // Aspect ratio of the containing rect. CGFloat aspect = NSWidth(containing_rect) / NSHeight(containing_rect); @@ -190,8 +261,9 @@ void TileSet::Layout(NSRect containing_rect) { // TODO(thakis): It might be good enough to choose |count_x| and |count_y| // such that count_x / count_y is roughly equal to |aspect|? double fny = FitNRectsWithAspectIntoBoundingSizeWithConstantPadding( - tile_count, aspect, container_width, container_height, - kSmallPaddingX, kSmallPaddingY); + tile_count, aspect, + container_width, container_height - kFooterExtraHeight, + kSmallPaddingX, kSmallPaddingY + kFooterExtraHeight); int count_y(roundf(fny)); int count_x(ceilf(tile_count / float(count_y))); int last_row_count_x = tile_count - count_x * (count_y - 1); @@ -205,7 +277,7 @@ void TileSet::Layout(NSRect containing_rect) { kSmallPaddingX); int small_height = floor((container_height + kSmallPaddingY) / float(count_y) - - kSmallPaddingY); + (kSmallPaddingY + kFooterExtraHeight)); // |small_width / small_height| has only roughly an aspect ratio of |aspect|. // Shrink the thumbnail rect to make the aspect ratio fit exactly, and add @@ -215,7 +287,8 @@ void TileSet::Layout(NSRect containing_rect) { if (aspect > small_width/float(small_height)) { small_height = small_width / aspect; CGFloat all_tiles_height = - (small_height + kSmallPaddingY) * count_y - kSmallPaddingY; + (small_height + kSmallPaddingY + kFooterExtraHeight) * count_y - + (kSmallPaddingY + kFooterExtraHeight); smallExtraPaddingTop = (container_height - all_tiles_height)/2; } else { small_width = small_height * aspect; @@ -227,7 +300,8 @@ void TileSet::Layout(NSRect containing_rect) { // Compute inter-tile padding in the zoomed-out view. CGFloat scale_small_to_big = NSWidth(containing_rect) / float(small_width); CGFloat big_padding_x = kSmallPaddingX * scale_small_to_big; - CGFloat big_padding_y = kSmallPaddingY * scale_small_to_big; + CGFloat big_padding_y = + (kSmallPaddingY + kFooterExtraHeight) * scale_small_to_big; // Now all dimensions are known. Lay out all tiles on a regular grid: // X X X X @@ -236,22 +310,40 @@ void TileSet::Layout(NSRect containing_rect) { for (int row = 0, i = 0; i < tile_count; ++row) { for (int col = 0; col < count_x && i < tile_count; ++col, ++i) { // Compute the smalled, zoomed-out thumbnail rect. - tiles_[i].thumb_rect_.size = NSMakeSize(small_width, small_height); + tiles_[i]->thumb_rect_.size = NSMakeSize(small_width, small_height); int small_x = col * (small_width + kSmallPaddingX) + kSmallPaddingLeft + smallExtraPaddingLeft; - int small_y = row * (small_height + kSmallPaddingY) + + int small_y = row * (small_height + kSmallPaddingY + kFooterExtraHeight) + kSmallPaddingTop + smallExtraPaddingTop; - tiles_[i].thumb_rect_.origin = NSMakePoint( + tiles_[i]->thumb_rect_.origin = NSMakePoint( small_x, NSHeight(containing_rect) - small_y - small_height); + tiles_[i]->favicon_rect_.size = NSMakeSize(kFaviconSize, kFaviconSize); + tiles_[i]->favicon_rect_.origin = NSMakePoint( + small_x, + NSHeight(containing_rect) - + (small_y + small_height + kFaviconExtraHeight)); + + // Align lower left corner of title rect with lower left corner of favicon + // for now. The final position is computed later by + // |Tile::set_font_metrics()|. + tiles_[i]->title_font_size_ = kTitleHeight; + tiles_[i]->title_rect_.origin = NSMakePoint( + NSMaxX(tiles_[i]->favicon_rect()) + kFaviconTitleDistanceX, + NSMinY(tiles_[i]->favicon_rect())); + tiles_[i]->title_rect_.size = NSMakeSize( + small_width - + NSWidth(tiles_[i]->favicon_rect()) - kFaviconTitleDistanceX, + kTitleHeight); + // Compute the big, pre-zoom thumbnail rect. - tiles_[i].start_thumb_rect_.size = containing_rect.size; + tiles_[i]->start_thumb_rect_.size = containing_rect.size; int big_x = col * (NSWidth(containing_rect) + big_padding_x); int big_y = row * (NSHeight(containing_rect) + big_padding_y); - tiles_[i].start_thumb_rect_.origin = NSMakePoint(big_x, -big_y); + tiles_[i]->start_thumb_rect_.origin = NSMakePoint(big_x, -big_y); } } @@ -265,8 +357,10 @@ void TileSet::Layout(NSRect containing_rect) { 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]->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; } } @@ -377,7 +471,7 @@ void AnimateCALayerFrameFromTo( ScopedCAActionDisabler disabler; const tabpose::Tile& tile = tileSet_->tile_at(newIndex); selectionHighlight_.frame = - NSRectToCGRect(NSInsetRect(tile.thumb_rect(), -6, -6)); + NSRectToCGRect(NSInsetRect(tile.thumb_rect(), -5, -5)); tileSet_->set_selected_index(newIndex); } @@ -419,9 +513,17 @@ void AnimateCALayerFrameFromTo( tileSet_->Build(tabStripModel_); tileSet_->Layout(bgLayerRect); + NSImage* defaultFavIcon = ResourceBundle::GetSharedInstance().GetNSImageNamed( + IDR_DEFAULT_FAVICON); + allThumbnailLayers_.reset( [[NSMutableArray alloc] initWithCapacity:tabStripModel_->count()]); + allFaviconLayers_.reset( + [[NSMutableArray alloc] initWithCapacity:tabStripModel_->count()]); + allTitleLayers_.reset( + [[NSMutableArray alloc] initWithCapacity:tabStripModel_->count()]); for (int i = 0; i < tabStripModel_->count(); ++i) { + const tabpose::Tile& tile = tileSet_->tile_at(i); CALayer* layer = [CALayer layer]; // Background color as placeholder for now. @@ -429,8 +531,8 @@ void AnimateCALayerFrameFromTo( AnimateCALayerFrameFromTo( layer, - tileSet_->tile_at(i).GetStartRectRelativeTo(tileSet_->selected_tile()), - tileSet_->tile_at(i).thumb_rect(), + tile.GetStartRectRelativeTo(tileSet_->selected_tile()), + tile.thumb_rect(), kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1), i == tileSet_->selected_index() ? self : nil); @@ -447,6 +549,37 @@ void AnimateCALayerFrameFromTo( [bgLayer_ addSublayer:layer]; [allThumbnailLayers_ addObject:layer]; + + // Favicon and title. + NSFont* font = [NSFont systemFontOfSize:tile.title_font_size()]; + tileSet_->tile_at(i).set_font_metrics([font ascender], -[font descender]); + + NSImage* ns_favicon = gfx::SkBitmapToNSImage(tile.favicon()); + // Either we don't have a valid favicon or there was some issue converting + // it from an SkBitmap. Either way, just show the default. + if (!ns_favicon) + ns_favicon = defaultFavIcon; + scoped_cftyperef<CGImageRef> favicon( + mac_util::CopyNSImageToCGImage(ns_favicon)); + + CALayer* faviconLayer = [CALayer layer]; + faviconLayer.frame = NSRectToCGRect(tile.favicon_rect()); + faviconLayer.contents = (id)favicon.get(); + faviconLayer.zPosition = 1; // On top of the thumb shadow. + faviconLayer.hidden = YES; + [bgLayer_ addSublayer:faviconLayer]; + [allFaviconLayers_ addObject:faviconLayer]; + + CATextLayer* titleLayer = [CATextLayer layer]; + titleLayer.frame = NSRectToCGRect(tile.title_rect()); + titleLayer.string = base::SysUTF16ToNSString(tile.title()); + titleLayer.fontSize = [font pointSize]; + titleLayer.truncationMode = kCATruncationEnd; + titleLayer.font = font; + titleLayer.zPosition = 1; // On top of the thumb shadow. + titleLayer.hidden = YES; + [bgLayer_ addSublayer:titleLayer]; + [allTitleLayers_ addObject:titleLayer]; } [self selectTileAtIndex:tileSet_->selected_index()]; } @@ -538,6 +671,10 @@ void AnimateCALayerFrameFromTo( [self selectedLayer].zPosition = 1; selectionHighlight_.hidden = YES; + for (CALayer* layer in allFaviconLayers_.get()) + layer.hidden = YES; + for (CALayer* layer in allTitleLayers_.get()) + layer.hidden = YES; // Running animations with shadows is slow, so turn shadows off before // running the exit animation. @@ -579,6 +716,10 @@ void AnimateCALayerFrameFromTo( state_ = tabpose::kFadedIn; selectionHighlight_.hidden = NO; + for (CALayer* layer in allFaviconLayers_.get()) + layer.hidden = NO; + for (CALayer* layer in allTitleLayers_.get()) + layer.hidden = NO; // Running animations with shadows is slow, so turn shadows on only after // the animation is done. |