| Index: chrome/browser/cocoa/tabpose_window.mm
|
| diff --git a/chrome/browser/cocoa/tabpose_window.mm b/chrome/browser/cocoa/tabpose_window.mm
|
| index c69c5c16b91cb7594513afa03cf49713b00bb04a..6a12a3ced701d9fa2a3beafa49dc42ebfc95d975 100644
|
| --- a/chrome/browser/cocoa/tabpose_window.mm
|
| +++ b/chrome/browser/cocoa/tabpose_window.mm
|
| @@ -6,6 +6,27 @@
|
|
|
| #import <QuartzCore/QuartzCore.h>
|
|
|
| +#include "base/mac_util.h"
|
| +#include "base/scoped_cftyperef.h"
|
| +#include "base/task.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#import "chrome/browser/cocoa/browser_window_controller.h"
|
| +#import "chrome/browser/cocoa/fast_resize_view.h"
|
| +#import "chrome/browser/cocoa/tab_strip_controller.h"
|
| +#include "chrome/browser/renderer_host/backing_store_mac.h"
|
| +#include "chrome/browser/renderer_host/render_widget_host_view.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 "third_party/skia/include/utils/mac/SkCGUtils.h"
|
| +
|
| +// FIXME: Need to listen for tab creation / destruction and react to that
|
| +// FIXME: would be nice if at least the selected tab would be "live" (i.e.
|
| +// videos play etc)
|
| +// FIXME (related?): crashes if invoked while stuff is still loading
|
| +
|
| +const float S = 1.03;
|
| +
|
| const int kTopGradientHeight = 15;
|
|
|
| // CAGradientLayer is 10.6-only -- roll our own.
|
| @@ -13,6 +34,34 @@ const int kTopGradientHeight = 15;
|
| - (void)drawInContext:(CGContextRef)context;
|
| @end
|
|
|
| +// CAGradientLayer is 10.6-only -- roll our own
|
| +@interface GradientLayer : CALayer
|
| +- (void)drawInContext:(CGContextRef)context;
|
| +@end
|
| +
|
| +@implementation GradientLayer
|
| +- (void)drawInContext:(CGContextRef)context {
|
| + scoped_cftyperef<CGColorSpaceRef> grayColorspace(
|
| + CGColorSpaceCreateWithName(kCGColorSpaceGenericGray));
|
| +NSLog(@"count: %d", CGColorSpaceGetNumberOfComponents(grayColorspace));
|
| + CGFloat grays[] = { 0.1, 0.39 };
|
| + CGFloat locations[] = { 0, 1 };
|
| + scoped_cftyperef<CGGradientRef> gradient(CGGradientCreateWithColorComponents(
|
| + grayColorspace, grays, locations, arraysize(grays)));
|
| +
|
| + CGPoint midY = CGPointMake(0.0, 15);
|
| + CGContextDrawLinearGradient(context, gradient, midY, CGPointZero, 0);
|
| +}
|
| +@end
|
| +
|
| +// A CALayer that draws a CGLayerRef (or a CGBitmap).
|
| +@interface BackingStoreLayer : CALayer {
|
| + BackingStoreMac* backing_store_;
|
| +}
|
| +- (id)initWithBackingStore:(BackingStoreMac*)store;
|
| +- (void)drawInContext:(CGContextRef)context;
|
| +@end
|
| +
|
| @implementation DarkGradientLayer
|
| - (void)drawInContext:(CGContextRef)context {
|
| scoped_cftyperef<CGColorSpaceRef> grayColorSpace(
|
| @@ -26,19 +75,137 @@ const int kTopGradientHeight = 15;
|
| }
|
| @end
|
|
|
| +@implementation BackingStoreLayer
|
| +- (id)initWithBackingStore:(BackingStoreMac*)store {
|
| + CHECK(store);
|
| + if ((self = [super init])) {
|
| + backing_store_ = store;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)drawInContext:(CGContextRef)context {
|
| + // Hrm, looks like I need to use the drawing delegate functionality of
|
| + // a layer here
|
| + if (backing_store_->cg_layer()) {
|
| + // TODO: add clipping to dirtyRect if it improves drawing performance.
|
| + CGContextDrawLayerInRect(context, [self bounds],
|
| + backing_store_->cg_layer());
|
| + } else {
|
| +NSLog(@"no layer!");
|
| + // if we haven't created a layer yet, draw the cached bitmap into
|
| + // the window. The CGLayer will be created the next time the renderer
|
| + // paints.
|
| + scoped_cftyperef<CGImageRef> image(
|
| + CGBitmapContextCreateImage(backing_store_->cg_bitmap()));
|
| + CGContextDrawImage(context, [self bounds], image);
|
| + }
|
| +}
|
| +@end
|
| +
|
| +// FIXME comment
|
| +class TabposeCallback {
|
| + public:
|
| + TabposeCallback(CALayer* layer)
|
| + : layer_(layer) {}
|
| +
|
| + void DidReceiveBitmap(const SkBitmap& bitmap) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +NSLog(@"receiving bitmap %d %d", bitmap.width(), bitmap.height());
|
| + // FIXME: too many conversions
|
| + scoped_cftyperef<CGImageRef> cgimage(SkCreateCGImageRef(bitmap));
|
| + layer_.contents = (id)cgimage.get();
|
| + layer_.backgroundColor = nil; // clear out bg color to default value
|
| + }
|
| + private:
|
| + CALayer* layer_;
|
| +};
|
| +
|
| +// FIXME: look at DISABLE_RUNNABLE_METHOD_REFCOUNT
|
| +// 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)
|
| + : window_(window)/*, factory_(this)*/ {}
|
| +
|
| + void LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s);
|
| + private:
|
| + friend class base::RefCountedThreadSafe<TabposeHelper>;
|
| + ~TabposeHelper() {}
|
| + void LoadThumbnailIO(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s, const gfx::Size& fullSize);
|
| +
|
| + TabposeWindow* window_;
|
| +// ScopedRunnableMethodFactory<TabposeHelper> factory_;
|
| +};
|
| +
|
| +void TabposeHelper::LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s) {
|
| + // rwh->set_painting_observer(NULL); // FIXME
|
| +
|
| + NSSize nsFullSize = [[window_->browser_ tabContentArea] frame].size;
|
| + gfx::Size fullSize(NSSizeToCGSize(nsFullSize));
|
| +
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // FIXME: is the hop to the IO thread really necessary?
|
| + ChromeThread::PostTask(ChromeThread::IO, FROM_HERE,
|
| + NewRunnableMethod(this, &TabposeHelper::LoadThumbnailIO, rwh, layer2, s, fullSize));
|
| +}
|
| +
|
| +void TabposeHelper::LoadThumbnailIO(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s, const gfx::Size& fullSize) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + 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.
|
| + TabposeCallback* callback = new TabposeCallback(layer2);
|
| + window_->callbacks_.push_back(callback); // takes ownership
|
| +
|
| + // |fullSize| is slightly larger (view bounds doesn't include scroll bars?)
|
| +// gfx::Size page_size(rwh->view()->GetViewBounds().size()); // Logical size the renderer renders at
|
| + 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.
|
| +// gfx::Size desired_size(s); // physical pixel size the image is rendered at
|
| + gfx::Size desired_size(page_size); // physical pixel size the image is rendered at
|
| +NSLog(@"scheduling snapshot request");
|
| +
|
| + // FIXME: check that observer is currently NULL
|
| + // FIXME: this lets us receive straggling requests from an old tabpose window. leads to crashes.
|
| + rwh->set_painting_observer(generator);
|
| + generator->AskForSnapshot(rwh, /*prefer_backing_store=*/false,
|
| + NewCallback(callback, &TabposeCallback::DidReceiveBitmap),
|
| + page_size, desired_size);
|
| + // FIXME: needs to be cancelled somehow if we die before renderer returns
|
| +}
|
| +
|
| @interface TabposeWindow (Private)
|
| -- (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo;
|
| +- (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo
|
| + tempBWC:(BrowserWindowController*)bwc;
|
| - (void)setUpLayers:(NSRect)bgLayerRect;
|
| +- (void)fadeAway:(BOOL)slomo;
|
| @end
|
|
|
| @implementation TabposeWindow
|
|
|
| -+ (id)openTabposeFor:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo {
|
| ++ (id)openTabposeFor:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo
|
| + tempBWC:(BrowserWindowController*)bwc {
|
| // Releases itself when closed.
|
| - return [[TabposeWindow alloc] initForWindow:parent rect:rect slomo:slomo];
|
| + return [[TabposeWindow alloc] initForWindow:parent rect:rect slomo:slomo tempBWC:bwc];
|
| }
|
|
|
| -- (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo {
|
| +- (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo
|
| + tempBWC:(BrowserWindowController*)bwc {
|
| NSRect frame = [parent frame];
|
| if ((self = [super initWithContentRect:frame
|
| styleMask:NSBorderlessWindowMask
|
| @@ -47,9 +214,343 @@ const int kTopGradientHeight = 15;
|
| [self setReleasedWhenClosed:YES];
|
| [self setOpaque:NO];
|
| [self setBackgroundColor:[NSColor clearColor]];
|
| - [self setUpLayers:rect];
|
| [parent addChildWindow:self ordered:NSWindowAbove];
|
| [self makeKeyAndOrderFront:self];
|
| +
|
| + //NSImage* img = [NSImage imageNamed:@"contents1"];
|
| +
|
| +// [self makeFirstResponder:self];
|
| +
|
| + helper_ = new TabposeHelper(self);
|
| +
|
| + state_ = kFadingIn;
|
| +
|
| + browser_ = bwc;
|
| +
|
| + NSView* contentArea = [browser_ tabContentArea] ; // FIXME
|
| + NSRect areaFrame = [contentArea frame];
|
| +
|
| + [self setUpLayers:rect];
|
| +
|
| + // Configure hand cursor
|
| + NSTrackingArea* trackingArea = [[NSTrackingArea alloc]
|
| + initWithRect:[[self contentView] frame]
|
| + options:NSTrackingCursorUpdate|NSTrackingActiveInKeyWindow
|
| + owner:self
|
| + userInfo:nil];
|
| + [[self contentView] addTrackingArea:[trackingArea autorelease]];
|
| +
|
| + [self disableCursorRects];
|
| + if (NSPointInRect([NSEvent mouseLocation], [self frame]))
|
| + [[NSCursor pointingHandCursor] set];
|
| +
|
| + // Needs to be called after -disableCursorRects.
|
| + [self setAcceptsMouseMovedEvents:YES];
|
| +
|
| +
|
| + // set up perspective
|
| + CATransform3D t = CATransform3DIdentity;
|
| + t.m34 = 1.0 / 1850;
|
| + bgLayer_.sublayerTransform = t;
|
| +
|
| +#if 0
|
| + // Animate in.
|
| + // FIXME: probably want to use a CABasicAnimation instead
|
| + CATransition* animation = [CATransition animation];
|
| + //animation.type = kCATransitionPush;
|
| + //animation.type = kCATransitionMoveIn;
|
| + //animation.type = kCATransitionFade;
|
| + animation.type = kCATransitionReveal;
|
| + //animation.subtype = kCATransitionFromTop;
|
| + //animation.delegate = self;
|
| + animation.duration = 1.f * (slomo ? 4 : 1); // half for in, half for out
|
| + //[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
|
| + [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
|
| + [bgLayer_ addAnimation:animation forKey:nil];
|
| +#endif
|
| +
|
| + // Count tabs.
|
| +// Browser* browser = browserController->browser_.get();
|
| + TabStripController* tabStripController = browser_->tabStripController_.get();
|
| + TabStripModel* tabStripModel = tabStripController->tabStripModel_;
|
| + NSArray* arrays = tabStripController->tabArray_.get();
|
| + NSSet* closing = tabStripController->closingControllers_.get();
|
| +
|
| + selectedLayer_ = nil;
|
| + initiallySelectedIndex_ = selectedIndex_ = tabStripModel->selected_index();
|
| +
|
| +
|
| + int n = [arrays count] - [closing count];
|
| +
|
| + // We want to have the small rects have the same aspect ratio a as a full
|
| + // tab. Let w, h be the size of a small rect, and w_c, h_c the size of the
|
| + // container. dx, dy are the distances between small rects in x, y direction.
|
| +
|
| + // Geometry yields:
|
| + // w_c = nx * (w + dx) - dx <=> w = (w_c + d_x) / nx - d_x
|
| + // h_c = ny * (h + dy) - dy <=> h = (h_c + d_y) / ny - d_t
|
| + // Plugging this into
|
| + // a := tab_width / tab_height = w / h
|
| + // yields
|
| + // a = ((w_c - (nx - 1)*d_x)*ny) / (nx*(h_c - (ny - 1)*d_y))
|
| + // Plugging in nx = n/ny and pen and paper (or wolfram alpha:
|
| + // http://www.wolframalpha.com/input/?i=(-sqrt((d+n-a+f+n)^2-4+(a+f%2Ba+h)+(-d+n-n+w))%2Ba+f+n-d+n)/(2+a+(f%2Bh)) , (solution for nx)
|
| + // http://www.wolframalpha.com/input/?i=+(-sqrt((a+f+n-d+n)^2-4+(d%2Bw)+(-a+f+n-a+h+n))-a+f+n%2Bd+n)/(2+(d%2Bw)) , (solution for ny)
|
| + // ) gives us nx and ny (but the wrong root -- s/-sqrt(FOO)/sqrt(FOO)/.
|
| +
|
| + int row = 0, col = 0, i = 0;
|
| +
|
| + CGFloat a = NSWidth([contentArea frame]) / NSHeight([contentArea frame]);
|
| +
|
| + // FIXME: need to audit everything to check where i want rects with and without bookmarks bar
|
| + // (right now, detached bookmark bar screws this up a bit)
|
| + int kOffsetTop = 30 + NSHeight([[browser_ window] frame]) - NSHeight([[browser_ tabContentArea] frame]);
|
| +// if (browser_->bookmarkBarController_.get()->visualState_ == bookmarks::kDetachedState) // FIXME: check for NULL?
|
| +// kOffsetTop -= NSHeight([[browser_->bookmarkBarController_ view] frame]);
|
| + int kOffsetLeft = 30;
|
| + int kOffsetRight = 30;
|
| + int kOffsetBottom = 30;
|
| + int kPaddingX = 15;
|
| + int kPaddingY = 10;
|
| +
|
| + int extraOffsetX = 0;
|
| + int extraOffsetY = 0;
|
| + double w_c = NSWidth(frame) - kOffsetLeft - kOffsetRight;
|
| + double h_c = NSHeight(frame) - kOffsetTop - kOffsetBottom;
|
| + dx = kPaddingX;
|
| + dy = kPaddingY;
|
| + double fny = (sqrt(pow(n*(a*dy - dx), 2) + 4*n*a*(dx + w_c)*(dy + h_c)) - n*(a*dy - dx)) / (2*(dx + w_c));
|
| + ny = int(roundf(fny));
|
| + nx = int(ceilf(n / float(ny)));
|
| + last_nx = n - nx * (ny - 1);
|
| + w = floor((w_c + dx)/float(nx) - dx);
|
| + h = floor((h_c + dy)/float(ny) - dy);
|
| + if (a > w/float(h)) {
|
| + h = w / a;
|
| + extraOffsetY = (h_c - ((h + dy)*ny - dy))/2;
|
| + } else {
|
| + w = h * a;
|
| + extraOffsetX = (w_c - ((w + dx)*nx - dx))/2;
|
| + }
|
| +
|
| + NSMutableArray* allLayers = [[NSMutableArray alloc] initWithCapacity:n];
|
| + allLayers_.reset(allLayers);
|
| +// for (TabController* tab in arrays) {
|
| + for (int ci = 0; ci < n; ++ci) {
|
| +#if 0
|
| + if ([closing containsObject:tab])
|
| + continue;
|
| +
|
| + NSView* tabView = [tab view];
|
| +
|
| + CALayer* layer = [CALayer layer];
|
| + layer.backgroundColor = blue;
|
| + layer.frame = NSRectToCGRect([[self contentView] convertRect:[tabView bounds] fromView:tabView]);
|
| +#endif
|
| +#if 0
|
| + // Get tab image. FIXME: on bg thread?
|
| +#if 0
|
| + // XXX: Doesn't work?!
|
| + NSBitmapImageRep* imageRep =
|
| + [tabView bitmapImageRepForCachingDisplayInRect:[tabView bounds]];
|
| + if ([imageRep bitmapData])
|
| + bzero([imageRep bitmapData],
|
| + [imageRep bytesPerRow] * [imageRep pixelsHigh]);
|
| + [tabView cacheDisplayInRect:[tabView bounds] toBitmapImageRep:imageRep];
|
| + CGImageRef image = CGImageCreateCopy([imageRep CGImage]);
|
| + layer.contents = (id)image;
|
| +#elif 0
|
| + [tabView lockFocus];
|
| + NSBitmapImageRep* imageRep =
|
| + [[[NSBitmapImageRep alloc] initWithFocusedViewRect:[tabView bounds]] autorelease];
|
| + [tabView unlockFocus];
|
| + CGImageRef image = CGImageCreateCopy([imageRep CGImage]);
|
| + layer.contents = (id)image;
|
| +#else
|
| + NSImage* nsi = [NSImage imageNamed:@"NSUserGroup"];
|
| + layer.contents = nsi;
|
| +#endif
|
| +// CFRelease(image);
|
| +// layer.contents = imageRep;
|
| +
|
| +// NSImage *image = [[[NSImage alloc] initWithSize:[imageRep size]] autorelease];
|
| +// [image addRepresentation:imageRep];
|
| +// layer.contents = image;
|
| +#endif
|
| +
|
| +// [rootLayer addSublayer:layer];
|
| +
|
| + CALayer* layer2;
|
| + // FIXME: on bg thread; also call the other method; ; also do
|
| + // CGLayer->CGImage conversion directly without going through skia, etc.
|
| + TabContents* contents = tabStripModel->GetTabContentsAt(i);
|
| + DCHECK(contents);
|
| + RenderViewHost* rvh = contents->render_view_host();
|
| + RenderWidgetHost* rwh = rvh;
|
| + DCHECK(rwh);
|
| +
|
| + BackingStoreMac* backing_store = (BackingStoreMac*)rwh->GetBackingStore(false);
|
| + if (backing_store) {
|
| + // FIXME: looks like the backing store can go away while we're running :-/
|
| + layer2 = [[[BackingStoreLayer alloc] initWithBackingStore:backing_store] autorelease];
|
| + [layer2 setNeedsDisplay];
|
| + } else {
|
| + layer2 = [CALayer layer];
|
| + // Set placeholder
|
| +#if 0
|
| + // Icon as placeholder (FIXME: load only once)
|
| + int m = MIN(w, h);
|
| + // Can't use nsimage_cache::ImageNamed(), because that uses
|
| + // initWithContentsOfFile, which creates a cached representation at
|
| + // 16x16 and then throws away the vector data.
|
| + NSString* path = [mac_util::MainAppBundle() pathForImageResource:@"nav.pdf"];
|
| + NSPDFImageRep* pdfRep = [NSPDFImageRep imageRepWithContentsOfFile:path];
|
| + NSImage* pdfImage = [[[NSImage alloc] initWithSize:NSMakeSize(m, m)] autorelease];
|
| + [pdfImage addRepresentation:pdfRep];
|
| + // FIXME: if we don't leak this, the image rep frees its cgimage soon
|
| + // and the CALayer crashes when it wants to draw.
|
| + NSBitmapImageRep* bitmap = [[NSBitmapImageRep imageRepWithData:[pdfImage TIFFRepresentation]] retain]; // XXX
|
| + layer2.contents = (id)[bitmap CGImage];
|
| +#else
|
| + // background color as placeholder
|
| + layer2.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
|
| +#endif
|
| + helper_->LoadThumbnail(rwh, layer2, gfx::Size(w, h));
|
| + }
|
| + layer2.contentsGravity = kCAGravityResizeAspect; // for placeholder
|
| +
|
| +// layer2.backgroundColor = blue;
|
| + int x = kOffsetLeft + col*(w + kPaddingX) + extraOffsetX;
|
| + if (row == ny - 1) {
|
| + // last row
|
| + x += (nx - last_nx) * (w + kPaddingX) / 2;
|
| + }
|
| + int y = kOffsetTop + row*(h + kPaddingY) + extraOffsetY;
|
| + layer2.anchorPoint = CGPointMake(0.5, 0.5);
|
| +
|
| +// NSBitmapImageRep* rep = (NSBitmapImageRep*)[img bestRepresentationForDevice:nil];
|
| +// layer2.contents = (id)[rep CGImage];
|
| +// layer2.contentsGravity = kCAGravityResizeAspect;
|
| +
|
| +// layer2.shadowOpacity = 0.5; // FIXME: Slows things down, even if disabled before animation runs
|
| + layer2.shadowRadius = 10;
|
| + layer2.shadowOffset = CGSizeMake(0, -10);
|
| +// layer2.borderColor = blue;
|
| +
|
| +
|
| + if (i == selectedIndex_) {
|
| + // http://developer.apple.com/mac/library/qa/qa2008/qa1620.html
|
| + // Prepare the animation from the old size to the new size
|
| + CGRect oldBounds = NSRectToCGRect([contentArea frame]);
|
| + CGRect newBounds = oldBounds;
|
| + newBounds.size = CGSizeMake(w, h);
|
| + CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
|
| + animation.fromValue = [NSValue valueWithRect:NSRectFromCGRect(oldBounds)];
|
| + animation.toValue = [NSValue valueWithRect:NSRectFromCGRect(newBounds)];
|
| + animation.duration = .25f * (slomo ? 4 : 1);
|
| + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
| +
|
| + // Update the layer's bounds so the layer doesn't snap back when the animation completes.
|
| + layer2.bounds = newBounds;
|
| +
|
| + // Add the animation, overriding the implicit animation.
|
| + [layer2 addAnimation:animation forKey:@"bounds"];
|
| +
|
| + // Prepare the animation from the current position to the new position
|
| + NSPoint t = areaFrame.origin;
|
| +
|
| + CGPoint opoint = CGPointMake(t.x + NSWidth(areaFrame)/2, t.y + NSHeight(areaFrame)/2);
|
| + CGPoint point = CGPointMake(x + w/2, NSHeight(frame) - y - h + h/2);
|
| + animation = [CABasicAnimation animationWithKeyPath:@"position"];
|
| + animation.fromValue = [NSValue valueWithPoint:NSPointFromCGPoint(opoint)];
|
| + animation.toValue = [NSValue valueWithPoint:NSPointFromCGPoint(point)];
|
| + animation.duration = .25f * (slomo ? 4 : 1);
|
| + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
| +
|
| + // Update the layer's position so that the layer doesn't snap back when the animation completes.
|
| + layer2.position = point;
|
| +
|
| + // Add the animation, overriding the implicit animation.
|
| + [layer2 addAnimation:animation forKey:@"position"];
|
| +
|
| + [rootLayer_ insertSublayer:layer2 above:bgLayer_];
|
| + selectedLayer_ = layer2;
|
| +
|
| +// selectedLayer_.borderWidth = 5;
|
| +// [selectedLayer_ setValue:[NSNumber numberWithFloat:S] forKey:@"transform.scale"];
|
| + [CATransaction begin];
|
| + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
|
| + selectedLayer_.transform = CATransform3DMakeScale(S, S, 1);
|
| + [CATransaction commit];
|
| + } else {
|
| +#if 0
|
| + CABasicAnimation* zoom = [CABasicAnimation animationWithKeyPath:@"transform.translation.z"];
|
| + [layer2 setValue:[NSNumber numberWithFloat:0] forKey:@"transform.translation.z"];
|
| + zoom.fromValue = [NSNumber numberWithFloat:1000];
|
| + zoom.toValue = [NSNumber numberWithFloat:0];
|
| + zoom.duration = .25f * (slomo ? 4 : 1);
|
| + zoom.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
| + [layer2 addAnimation:zoom forKey:@"transform"];
|
| +#else
|
| + int sel_col = selectedIndex_ % nx;
|
| + int sel_row = selectedIndex_ / nx;
|
| + CGFloat r = NSWidth([contentArea frame]) / float(w);
|
| + int ox = (NSWidth([contentArea frame]) + dx*r) * (col - sel_col);
|
| + if (row == ny - 1) {
|
| + // last row
|
| + ox += (nx - last_nx) * (NSWidth([contentArea frame]) + dx*r) / 2;
|
| + }
|
| + if (sel_row == ny - 1) {
|
| + // last row
|
| + ox -= (nx - last_nx) * (NSWidth([contentArea frame]) + dx*r) / 2;
|
| + }
|
| + int oy = (NSHeight([contentArea frame]) + dy*r) * (row - sel_row);
|
| +
|
| + CGRect oldBounds = NSRectToCGRect([contentArea frame]);
|
| + CGRect newBounds = oldBounds;
|
| + newBounds.size = CGSizeMake(w, h);
|
| + CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
|
| + animation.fromValue = [NSValue valueWithRect:NSRectFromCGRect(oldBounds)];
|
| + animation.toValue = [NSValue valueWithRect:NSRectFromCGRect(newBounds)];
|
| + animation.duration = .25f * (slomo ? 4 : 1);
|
| + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
| +
|
| + // Update the layer's bounds so the layer doesn't snap back when the animation completes.
|
| + layer2.bounds = newBounds;
|
| +
|
| + // Add the animation, overriding the implicit animation.
|
| + [layer2 addAnimation:animation forKey:@"bounds"];
|
| +
|
| + // Prepare the animation from the current position to the new position
|
| + NSPoint t = [contentArea frame].origin;
|
| + CGPoint opoint = CGPointMake(ox + t.x + NSWidth(areaFrame)/2, /*NSHeight(frame) - h*/- oy + t.y + NSHeight(areaFrame)/2);
|
| +
|
| + CGPoint point = CGPointMake(x + w/2, NSHeight(frame) - y - h + h/2);
|
| + animation = [CABasicAnimation animationWithKeyPath:@"position"];
|
| + animation.fromValue = [NSValue valueWithPoint:NSPointFromCGPoint(opoint)];
|
| + animation.toValue = [NSValue valueWithPoint:NSPointFromCGPoint(point)];
|
| + animation.duration = .25f * (slomo ? 4 : 1);
|
| + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
| +
|
| + // Update the layer's position so that the layer doesn't snap back when the animation completes.
|
| + layer2.position = point;
|
| +
|
| + // Add the animation, overriding the implicit animation.
|
| + [layer2 addAnimation:animation forKey:@"position"];
|
| +#endif
|
| +
|
| + [bgLayer_ addSublayer:layer2];
|
| + }
|
| + [allLayers addObject:layer2];
|
| +
|
| +
|
| + ++col;
|
| + if (col == nx) {
|
| + col = 0;
|
| + ++row;
|
| + }
|
| + ++i;
|
| + }
|
| }
|
| return self;
|
| }
|
| @@ -88,6 +589,9 @@ const int kTopGradientHeight = 15;
|
| }
|
|
|
| - (void)keyUp:(NSEvent*)event {
|
| + if (state_ == kFadingOut)
|
| + return;
|
| +
|
| NSString* characters = [event characters];
|
| if ([characters length] < 1)
|
| return;
|
| @@ -98,19 +602,58 @@ const int kTopGradientHeight = 15;
|
| case NSNewlineCharacter:
|
| case NSCarriageReturnCharacter:
|
| case ' ':
|
| + [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| + break;
|
| case '\e': // Escape
|
| - [self close];
|
| + selectedIndex_ = initiallySelectedIndex_;
|
| + selectedLayer_ = [allLayers_ objectAtIndex:selectedIndex_];
|
| + [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| break;
|
| }
|
| }
|
|
|
| +- (void)mouseMoved:(NSEvent*)event {
|
| + int i = 0;
|
| + CALayer* newSelectedLayer = nil;
|
| + int newIndex = -1;
|
| + CGPoint p = NSPointToCGPoint([event locationInWindow]);
|
| + for (CALayer* layer in allLayers_.get()) {
|
| + CGPoint lp = [layer convertPoint:p fromLayer:rootLayer_];
|
| + if ([layer containsPoint:lp]) {
|
| + newSelectedLayer = layer;
|
| + newIndex = i;
|
| + }
|
| + ++i;
|
| + }
|
| +
|
| + if (newSelectedLayer && newSelectedLayer != selectedLayer_) {
|
| + CALayer* oldSelectedLayer = selectedLayer_;
|
| +
|
| + [CATransaction begin];
|
| + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
|
| + if (selectedLayer_)
|
| + [bgLayer_ addSublayer:selectedLayer_];
|
| + selectedLayer_ = newSelectedLayer;
|
| + [selectedLayer_ removeFromSuperlayer];
|
| + [rootLayer_ insertSublayer:selectedLayer_ above:bgLayer_];
|
| + selectedIndex_ = newIndex;
|
| + [CATransaction commit];
|
| +
|
| + if (oldSelectedLayer)
|
| + oldSelectedLayer.transform = CATransform3DIdentity;
|
| + if (newSelectedLayer)
|
| + newSelectedLayer.transform = CATransform3DMakeScale(S, S, 1);
|
| + }
|
| +}
|
| +
|
| +// FIXME: do this on long hover, too
|
| - (void)mouseDown:(NSEvent*)event {
|
| - [self close];
|
| + [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| }
|
|
|
| - (void)swipeWithEvent:(NSEvent*)event {
|
| if ([event deltaY] > 0.5) // Swipe up
|
| - [self close];
|
| + [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0];
|
| }
|
|
|
| - (void)close {
|
| @@ -128,4 +671,155 @@ const int kTopGradientHeight = 15;
|
| return NO;
|
| }
|
|
|
| +
|
| +- (void)cursorUpdate:(NSEvent*)event {
|
| + [[NSCursor pointingHandCursor] set];
|
| +}
|
| +
|
| +- (void)fadeAway:(BOOL)slomo {
|
| + if (state_ == kFadingOut)
|
| + return;
|
| +
|
| + [[NSCursor arrowCursor] set];
|
| +
|
| + state_ = kFadingOut;
|
| + [self setAcceptsMouseMovedEvents:NO];
|
| +
|
| + // Clean out observers, so that eventual pending thumbnail requests don't
|
| + // resolve against dead objects.
|
| + TabStripController* tabStripController = browser_->tabStripController_.get();
|
| + TabStripModel* tabStripModel = tabStripController->tabStripModel_;
|
| + for (int i = 0; i < tabStripModel->count(); ++i) {
|
| + TabContents* contents = tabStripModel->GetTabContentsAt(i);
|
| + contents->render_view_host()->set_painting_observer(NULL);
|
| + }
|
| +
|
| + // Select chosen tab (FIXME: don't on ESC)
|
| + if (selectedIndex_ >= 0)
|
| + tabStripModel->SelectTabContentsAt(selectedIndex_, /*user_gesture=*/true);
|
| +
|
| +#if 1
|
| + // FIXME: this is only here so that we get a notification after 4 / 1 seconds.
|
| + // Do that in a simpler way.
|
| + CABasicAnimation* fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
| + bgLayer_.opacity = 0.99; // make it stick, change model value
|
| + fade.fromValue = [NSNumber numberWithFloat:1];
|
| + fade.toValue = [NSNumber numberWithFloat:0.99];
|
| + fade.duration = .5f * (slomo ? 4 : 1);
|
| + fade.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
| +fade.delegate = self;
|
| +#endif
|
| +
|
| + if (selectedIndex_ >= 0) {
|
| + CALayer* layer = selectedLayer_; //[[bgLayer_ sublayers] objectAtIndex:selectedIndex_];
|
| +
|
| + [CATransaction begin];
|
| + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
|
| +// layer.contentsGravity = kCAGravityResize;
|
| +// layer.opaque = YES;
|
| +// layer.compositingFilter = [CIFilter filterWithName:@"CISourceInCompositing"];
|
| + selectedLayer_.borderWidth = 0;
|
| +
|
| + // Animating large shadows is slow -- don't.
|
| + layer.shadowOpacity = 0;
|
| + layer.transform = CATransform3DIdentity;
|
| +
|
| + [layer removeFromSuperlayer];
|
| +// [rootLayer_ insertSublayer:layer above:bgLayer_];
|
| + [bgLayer_ addSublayer:layer]; // move on top of all other layers
|
| +
|
| + [CATransaction commit];
|
| +
|
| +
|
| +
|
| + [CATransaction begin];
|
| + if (slomo) {
|
| + CGFloat f = [[CATransaction valueForKey:kCATransactionAnimationDuration] floatValue];
|
| + f = 0.5; // the above returns 0 :-P
|
| + [CATransaction setValue:[NSNumber numberWithFloat:4*f] forKey:kCATransactionAnimationDuration];
|
| + }
|
| + layer.frame = NSRectToCGRect([[browser_ tabContentArea] frame]);
|
| + if ([layer isKindOfClass:[BackingStoreLayer class]])
|
| + [layer setNeedsDisplay]; // Redraw layer at big resolution, so that zoom-in isn't blocky.
|
| +// layer.opacity = 0;
|
| + [CATransaction commit];
|
| + }
|
| +
|
| + int i = 0, col = 0, row = 0;
|
| + for (CALayer* layer in allLayers_.get()) {
|
| +// DCHECK_NE(layer, selectedLayer_);
|
| +// if (layer == selectedLayer_) continue;
|
| + if (i == selectedIndex_) {
|
| + ++i;
|
| + ++col;
|
| + if (col == nx) {
|
| + col = 0;
|
| + ++row;
|
| + }
|
| + continue;
|
| + }
|
| +
|
| + // Animating large shadows is slow -- don't.
|
| + [CATransaction begin];
|
| + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
|
| + layer.shadowOpacity = 0;
|
| + [CATransaction commit];
|
| +
|
| +#if 0
|
| + CABasicAnimation* zoom = [CABasicAnimation animationWithKeyPath:@"transform.translation.z"];
|
| +// [layer setValue:[NSNumber numberWithFloat:1000] forKey:@"transform.translation.z"];
|
| + zoom.fromValue = [NSNumber numberWithFloat:0];
|
| + zoom.toValue = [NSNumber numberWithFloat:-1000]; // negative Z to keep them behind the selected layer
|
| + zoom.duration = .5f * (slomo ? 4 : 1);
|
| + zoom.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
| + [layer addAnimation:zoom forKey:@"transform"];
|
| +#else
|
| + int sel_col = selectedIndex_ % nx;
|
| + int sel_row = selectedIndex_ / nx;
|
| + NSLog(@"%d %d %d %d %d", col, row, sel_col, sel_row, selectedIndex_);
|
| + CGFloat r = NSWidth([[browser_ tabContentArea] frame]) / float(w);
|
| + int ox = (NSWidth([[browser_ tabContentArea] frame]) + dx*r) * (col - sel_col);
|
| + if (row == ny - 1) {
|
| + // last row
|
| + ox += (nx - last_nx) * (NSWidth([[browser_ tabContentArea] frame]) + dx*r) / 2;
|
| + }
|
| + if (sel_row == ny - 1) {
|
| + // last row
|
| + ox -= (nx - last_nx) * (NSWidth([[browser_ tabContentArea] frame]) + dx*r) / 2;
|
| + }
|
| + int oy = (NSHeight([[browser_ tabContentArea] frame]) + dy*r) * (row - sel_row);
|
| +
|
| + CGRect newFrame = NSRectToCGRect([[browser_ tabContentArea] frame]);
|
| + newFrame.origin.x += ox;
|
| + newFrame.origin.y -= oy;
|
| +
|
| + [CATransaction begin];
|
| + if (slomo) {
|
| + CGFloat f = 0.5;
|
| + [CATransaction setValue:[NSNumber numberWithFloat:4*f] forKey:kCATransactionAnimationDuration];
|
| + }
|
| + layer.frame = newFrame;
|
| + [CATransaction commit];
|
| +
|
| + ++col;
|
| + if (col == nx) {
|
| + col = 0;
|
| + ++row;
|
| + }
|
| +#endif
|
| + ++i;
|
| + }
|
| +
|
| + [bgLayer_ addAnimation:fade forKey:@"opacity"]; // override any implicit animation
|
| +}
|
| +
|
| +- (BOOL)windowShouldClose:(id)window {
|
| + [self fadeAway:NO];
|
| + return NO;
|
| +}
|
| +
|
| +- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag {
|
| + [self close];
|
| +}
|
| +
|
| @end
|
|
|