Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Unified Diff: chrome/browser/cocoa/tabpose_window.mm

Issue 3163003: Mac tabpose: Add thumbnails (Closed)
Patch Set: observer stuff Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/cocoa/tabpose_window.mm
diff --git a/chrome/browser/cocoa/tabpose_window.mm b/chrome/browser/cocoa/tabpose_window.mm
index f96d0402bb5a05d53fc23272536713a9ed94b1f2..798b43a7df8f5d22cf56e0cf14ba480ca860a96b 100644
--- a/chrome/browser/cocoa/tabpose_window.mm
+++ b/chrome/browser/cocoa/tabpose_window.mm
@@ -8,12 +8,18 @@
#include "app/resource_bundle.h"
#include "base/mac_util.h"
+#include "base/scoped_callback_factory.h"
#include "base/sys_string_conversions.h"
+#include "chrome/browser/browser_process.h"
#import "chrome/browser/cocoa/browser_window_controller.h"
#import "chrome/browser/cocoa/tab_strip_controller.h"
+#include "chrome/browser/renderer_host/backing_store_mac.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/thumbnail_generator.h"
#include "grit/app_resources.h"
#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/utils/mac/SkCGUtils.h"
const int kTopGradientHeight = 15;
@@ -42,6 +48,155 @@ const CGFloat kSlomoFactor = 4;
}
@end
+// FIXME: this needs to be cleaned up a lot
+
+// FIXME: look at URLFetcher's inner class for thread switching
+
+// FIMXE comment is wrong
+// A helper class used to interface with chrome's c++ code. Used to dispatch
+// stuff to threads; to listen for things; etc.
+class TabposeHelper : public base::RefCountedThreadSafe<TabposeHelper> {
+ public:
+ TabposeHelper(/*TabposeWindow* window*/NSSize fullS)
+ : nsFullSize(fullS) /*window_(window)*//*, factory_(this)*/ , layer_(nil), factory_(this) {}
+
+ void LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s);
+ private:
+ friend class base::RefCountedThreadSafe<TabposeHelper>;
+ ~TabposeHelper() {}
+
+ void DidReceiveBitmap(const SkBitmap& bitmap) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+NSLog(@"receiving bitmap %d %d", bitmap.width(), bitmap.height());
+ // FIXME: too many conversions (SkCreateCGImageRef() actually shares memory,
+ // so it's not too bad)
+ cgimage.reset(SkCreateCGImageRef(bitmap));
+ layer_.contents = (id)cgimage.get();
+ }
+
+scoped_cftyperef<CGImageRef> cgimage;
+NSSize nsFullSize;
+ CALayer* layer_;
+ base::ScopedCallbackFactory<TabposeHelper> factory_;
+};
+
+void TabposeHelper::LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const
+gfx::Size& s) {
+ gfx::Size fullSize(NSSizeToCGSize(nsFullSize));
+
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ // Don't need to go on IO thread, as this just sends an IPC anyway
+ // (and ScopedCallbackFactory isn't threadsafe :-P)
+ ThumbnailGenerator* generator = g_browser_process->GetThumbnailGenerator();
+
+ // FIXME: Internally, this allocs memory for both transport dib and then
+ // an SkBitmap (which _then_ needs to be converted to a CGImageRef).
+ // Reduce the amount of copying; possibly by sidestepping
+ // ThumbnailGenerator.
+ CHECK(!layer_);
+ layer_ = layer2;
+
+ // |fullSize| is slightly larger (view bounds doesn't include scroll bars?)
+ gfx::Size page_size(fullSize); // Logical size the renderer renders at
+
+ // This is a bit tricky: We really only need a bitmap at size |s|, but when
+ // the user clicks a thumbnail and it zooms large, this single thumbnail
+ // should have a pixel size close to the view bounds, so that the image doesnt
+ // get blurry. One possible idea would be to get all thumbs at size |s| and
+ // request the one currently below the mouse at full size, but then it might
+ // not be ready when the click happens. For now, KISS, and request everything
+ // at the full resolution.
+ // FIXME: even better plan: call model->selectTab before starting the bg animation.
+ // that makes sure the target tab has a backing store. then call setNeedsDisplay on the
+ // layer, and it'll be redrawn from the backing store -- and all bg thumbs can stay small.
+ // yay! (is this true?) => no :-/, it takes too long for the backing store to load.
+ //gfx::Size desired_size(s); // physical pixel size the image is rendered at
+ gfx::Size desired_size(fullSize); // physical pixel size the image is rendered at
+NSLog(@"scheduling snapshot request");
+
+ // FIXME: check that observer is currently NULL
+ // FIXME: set observer back to NULL at some point
+ rwh->set_painting_observer(generator);
+ generator->AskForSnapshot(rwh, /*prefer_backing_store=*/false,
+ factory_.NewCallback(&TabposeHelper::DidReceiveBitmap),
+ page_size, desired_size);
+}
+
+
+
+// A CALayer that draws a CGLayerRef (or a CGBitmap).
+@interface BackingStoreLayer : CALayer {
+ TabContents* contents_; // should really have a tabpose::Tile*?
+ NSSize smallSize;
+ NSSize fullSize;
+ scoped_refptr<TabposeHelper> helper_;
+ BOOL didSendLoad_;
+}
+- (id)initWithTabContents:(TabContents*)contents smallSize:(NSSize)s fullSize:(NSSize)s2;
+- (void)drawInContext:(CGContextRef)context;
+@end
+
+@implementation BackingStoreLayer
+- (id)initWithTabContents:(TabContents*)contents smallSize:(NSSize)s fullSize:(NSSize)s2 {
+ CHECK(contents);
+ if ((self = [super init])) {
+ contents_ = contents;
+ smallSize = s;
+ fullSize = s2;
+ }
+ return self;
+}
+
+- (void)drawInContext:(CGContextRef)context {
+ RenderWidgetHost* rwh = contents_->render_view_host();
+ BackingStoreMac* backing_store_ = (BackingStoreMac*)rwh->GetBackingStore(false);
+ if (!backing_store_) {
+ // Either the tab was never visible, or its backing store got evicted.
+
+NSLog(@"drawing layer without backingstore %d %@", didSendLoad_, self.contents);
+ // do this only if we haven't already sent a thumbnail request before
+ // else this is done on hide and way too much in general
+ if (!didSendLoad_) {
+ helper_ = new TabposeHelper(NSSizeFromCGSize(NSSizeToCGSize(fullSize)));
+ helper_->LoadThumbnail(rwh, self, gfx::Size(NSSizeToCGSize(smallSize)));
+ didSendLoad_ = YES;
+
+ // Overwrites |contents|, so do this only once, to fill with bg color.
+ [super drawInContext:context];
+ }
+
+ if ([self contents]) {
+ CGContextDrawImage(context, [self bounds], (CGImageRef)[self contents]);
+ }
+
+ return;
+ }
+
+ // TODO(thakis): re-send PaintAt ipc when tabstripmodelobserver informs us
+ // that a tab has changed (reload favicon & title, too)
+
+ // TODO(thakis): backing store might have wrong size (?)
+
+ // TODO(thakis): composited tabs might not be rendered correctly by this
+ // (once this is in BWC, let it add a sublayer for all IOSurfaces)
+
+ // TODO(thakis): Draw only into rect not occupied by info bars, detached
+ // bookmarks bar, devtool
+ if (backing_store_->cg_layer()) {
+ CGContextDrawLayerInRect(context, [self bounds],
+ backing_store_->cg_layer());
+ } else {
+NSLog(@"no layer!");
+ scoped_cftyperef<CGImageRef> image(
+ CGBitmapContextCreateImage(backing_store_->cg_bitmap()));
+ CGContextDrawImage(context, [self bounds], image);
+ }
+}
+@end
+
+
+
namespace {
class ScopedCAActionDisabler {
@@ -133,6 +288,8 @@ class Tile {
// Returns an unelided title. The view logic is responsible for eliding.
const string16& title() const { return contents_->GetTitle(); }
+
+ TabContents* tab_contents() const { return contents_; }
private:
friend class TileSet;
@@ -183,8 +340,9 @@ void Tile::set_font_metrics(CGFloat ascender, CGFloat descender) {
// A tileset is responsible for owning and laying out all |Tile|s shown in a
// tabpose window.
-class TileSet {
+class TileSet : public TabStripModelObserver {
public:
+ virtual ~TileSet() { source_model_->RemoveObserver(this); } // FIXME: crashes if Build() wasn't called.
// Fills in |tiles_|.
void Build(TabStripModel* source_model);
@@ -199,11 +357,29 @@ class TileSet {
Tile& tile_at(int index) { return *tiles_[index]; }
const Tile& tile_at(int index) const { return *tiles_[index]; }
+ // TabStripModelObserver
+ virtual void TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground) { CHECK(false); }
+ virtual void TabClosingAt(TabContents* contents, int index) { CHECK(false); }
+ virtual void TabDetachedAt(TabContents* contents, int index) { CHECK(false); }
+ virtual void TabMoved(TabContents* contents,
+ int from_index,
+ int to_index) { CHECK(false); }
+ virtual void TabStripModelDeleted() { CHECK(false); }
+ virtual void TabChangedAt(TabContents* contents, int index,
+ TabChangeType change_type) {
+ // FIXME: update tile with |contents|, reload title, favicon, thumb
+ // depending on |change_type|.
+ }
+
private:
ScopedVector<Tile> tiles_;
int selected_index_;
int initial_index_;
+
+ TabStripModel* source_model_;
};
void TileSet::Build(TabStripModel* source_model) {
@@ -213,6 +389,9 @@ void TileSet::Build(TabStripModel* source_model) {
tiles_[i] = new Tile;
tiles_[i]->contents_ = source_model->GetTabContentsAt(i);
}
+
+ source_model->AddObserver(this);
+ source_model_ = source_model;
}
void TileSet::Layout(NSRect containing_rect) {
@@ -479,36 +658,37 @@ void AnimateCALayerFrameFromTo(
- (void)setUpLayers:(NSRect)bgLayerRect slomo:(BOOL)slomo {
// Root layer -- covers whole window.
rootLayer_ = [CALayer layer];
- [[self contentView] setLayer:rootLayer_];
- [[self contentView] setWantsLayer:YES];
-
- // Background layer -- the visible part of the window.
- gray_.reset(CGColorCreateGenericGray(0.39, 1.0));
- bgLayer_ = [CALayer layer];
- bgLayer_.backgroundColor = gray_;
- bgLayer_.frame = NSRectToCGRect(bgLayerRect);
- bgLayer_.masksToBounds = YES;
- [rootLayer_ addSublayer:bgLayer_];
-
- // Selection highlight layer.
- darkBlue_.reset(CGColorCreateGenericRGB(0.25, 0.34, 0.86, 1.0));
- selectionHighlight_ = [CALayer layer];
- selectionHighlight_.backgroundColor = darkBlue_;
- selectionHighlight_.cornerRadius = 5.0;
- selectionHighlight_.zPosition = -1; // Behind other layers.
- selectionHighlight_.hidden = YES;
- [bgLayer_ addSublayer:selectionHighlight_];
-
- // Top gradient.
- CALayer* gradientLayer = [DarkGradientLayer layer];
- gradientLayer.frame = CGRectMake(
- 0,
- NSHeight(bgLayerRect) - kTopGradientHeight,
- NSWidth(bgLayerRect),
- kTopGradientHeight);
- [gradientLayer setNeedsDisplay]; // Draw once.
- [bgLayer_ addSublayer:gradientLayer];
+ // In a block so that the layers don't fade in.
+ {
+ ScopedCAActionDisabler disabler;
+ // Background layer -- the visible part of the window.
+ gray_.reset(CGColorCreateGenericGray(0.39, 1.0));
+ bgLayer_ = [CALayer layer];
+ bgLayer_.backgroundColor = gray_;
+ bgLayer_.frame = NSRectToCGRect(bgLayerRect);
+ bgLayer_.masksToBounds = YES;
+ [rootLayer_ addSublayer:bgLayer_];
+
+ // Selection highlight layer.
+ darkBlue_.reset(CGColorCreateGenericRGB(0.25, 0.34, 0.86, 1.0));
+ selectionHighlight_ = [CALayer layer];
+ selectionHighlight_.backgroundColor = darkBlue_;
+ selectionHighlight_.cornerRadius = 5.0;
+ selectionHighlight_.zPosition = -1; // Behind other layers.
+ selectionHighlight_.hidden = YES;
+ [bgLayer_ addSublayer:selectionHighlight_];
+
+ // Top gradient.
+ CALayer* gradientLayer = [DarkGradientLayer layer];
+ gradientLayer.frame = CGRectMake(
+ 0,
+ NSHeight(bgLayerRect) - kTopGradientHeight,
+ NSWidth(bgLayerRect),
+ kTopGradientHeight);
+ [gradientLayer setNeedsDisplay]; // Draw once.
+ [bgLayer_ addSublayer:gradientLayer];
+ }
// Layers for the tab thumbnails.
tileSet_->Build(tabStripModel_);
tileSet_->Layout(bgLayerRect);
@@ -524,7 +704,11 @@ void AnimateCALayerFrameFromTo(
[[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];
+
+
+// FIXME: BrowserWindowController should create this layer
+ CALayer* layer = [[[BackingStoreLayer alloc] initWithTabContents:tile.tab_contents() smallSize:tile.thumb_rect().size fullSize:tile.GetStartRectRelativeTo(tileSet_->selected_tile()).size] autorelease];
+ [layer setNeedsDisplay];
// Background color as placeholder for now.
layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
@@ -582,6 +766,12 @@ void AnimateCALayerFrameFromTo(
[allTitleLayers_ addObject:titleLayer];
}
[self selectTileAtIndex:tileSet_->selected_index()];
+
+ // Needs to happen after all layers have been added to |rootLayer_|, else
+ // there's a one frame flash of grey at the beginning of the animation
+ // (|bgLayer_| showing through with none of its children visible yet).
+ [[self contentView] setLayer:rootLayer_];
+ [[self contentView] setWantsLayer:YES];
}
- (BOOL)canBecomeKeyWindow {
@@ -683,7 +873,7 @@ void AnimateCALayerFrameFromTo(
}
// Animate layers out, all in one transaction.
- CGFloat duration = kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1);
+ CGFloat duration = 2 * kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1);
ScopedCAActionSetDuration durationSetter(duration);
for (NSUInteger i = 0; i < [allThumbnailLayers_ count]; ++i) {
CALayer* layer = [allThumbnailLayers_ objectAtIndex:i];
@@ -703,6 +893,11 @@ void AnimateCALayerFrameFromTo(
}
layer.frame = newFrame;
+
+ if (static_cast<int>(i) == tileSet_->selected_index()) {
+ // FIXME: only do this if the layer has a backing store (?)
+ [layer setNeedsDisplay]; // Redraw layer at big resolution, so that zoom-in isn't blocky.
+ }
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698