OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/cocoa/tabpose_window.h" | 5 #import "chrome/browser/cocoa/tabpose_window.h" |
6 | 6 |
7 #import <QuartzCore/QuartzCore.h> | 7 #import <QuartzCore/QuartzCore.h> |
8 | 8 |
| 9 #include "base/mac_util.h" |
| 10 #include "base/scoped_cftyperef.h" |
| 11 #include "base/task.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #import "chrome/browser/cocoa/browser_window_controller.h" |
| 14 #import "chrome/browser/cocoa/fast_resize_view.h" |
| 15 #import "chrome/browser/cocoa/tab_strip_controller.h" |
| 16 #include "chrome/browser/renderer_host/backing_store_mac.h" |
| 17 #include "chrome/browser/renderer_host/render_widget_host_view.h" |
| 18 #include "chrome/browser/renderer_host/render_view_host.h" |
| 19 #include "chrome/browser/tab_contents/tab_contents.h" |
| 20 #include "chrome/browser/tab_contents/thumbnail_generator.h" |
| 21 #include "third_party/skia/include/utils/mac/SkCGUtils.h" |
| 22 |
| 23 // FIXME: Need to listen for tab creation / destruction and react to that |
| 24 // FIXME: would be nice if at least the selected tab would be "live" (i.e. |
| 25 // videos play etc) |
| 26 // FIXME (related?): crashes if invoked while stuff is still loading |
| 27 |
| 28 const float S = 1.03; |
| 29 |
9 const int kTopGradientHeight = 15; | 30 const int kTopGradientHeight = 15; |
10 | 31 |
11 // CAGradientLayer is 10.6-only -- roll our own. | 32 // CAGradientLayer is 10.6-only -- roll our own. |
12 @interface DarkGradientLayer : CALayer | 33 @interface DarkGradientLayer : CALayer |
13 - (void)drawInContext:(CGContextRef)context; | 34 - (void)drawInContext:(CGContextRef)context; |
14 @end | 35 @end |
15 | 36 |
| 37 // CAGradientLayer is 10.6-only -- roll our own |
| 38 @interface GradientLayer : CALayer |
| 39 - (void)drawInContext:(CGContextRef)context; |
| 40 @end |
| 41 |
| 42 @implementation GradientLayer |
| 43 - (void)drawInContext:(CGContextRef)context { |
| 44 scoped_cftyperef<CGColorSpaceRef> grayColorspace( |
| 45 CGColorSpaceCreateWithName(kCGColorSpaceGenericGray)); |
| 46 NSLog(@"count: %d", CGColorSpaceGetNumberOfComponents(grayColorspace)); |
| 47 CGFloat grays[] = { 0.1, 0.39 }; |
| 48 CGFloat locations[] = { 0, 1 }; |
| 49 scoped_cftyperef<CGGradientRef> gradient(CGGradientCreateWithColorComponents( |
| 50 grayColorspace, grays, locations, arraysize(grays))); |
| 51 |
| 52 CGPoint midY = CGPointMake(0.0, 15); |
| 53 CGContextDrawLinearGradient(context, gradient, midY, CGPointZero, 0); |
| 54 } |
| 55 @end |
| 56 |
| 57 // A CALayer that draws a CGLayerRef (or a CGBitmap). |
| 58 @interface BackingStoreLayer : CALayer { |
| 59 BackingStoreMac* backing_store_; |
| 60 } |
| 61 - (id)initWithBackingStore:(BackingStoreMac*)store; |
| 62 - (void)drawInContext:(CGContextRef)context; |
| 63 @end |
| 64 |
16 @implementation DarkGradientLayer | 65 @implementation DarkGradientLayer |
17 - (void)drawInContext:(CGContextRef)context { | 66 - (void)drawInContext:(CGContextRef)context { |
18 scoped_cftyperef<CGColorSpaceRef> grayColorSpace( | 67 scoped_cftyperef<CGColorSpaceRef> grayColorSpace( |
19 CGColorSpaceCreateWithName(kCGColorSpaceGenericGray)); | 68 CGColorSpaceCreateWithName(kCGColorSpaceGenericGray)); |
20 CGFloat grays[] = { 0.277, 1.0, 0.39, 1.0 }; | 69 CGFloat grays[] = { 0.277, 1.0, 0.39, 1.0 }; |
21 CGFloat locations[] = { 0, 1 }; | 70 CGFloat locations[] = { 0, 1 }; |
22 scoped_cftyperef<CGGradientRef> gradient(CGGradientCreateWithColorComponents( | 71 scoped_cftyperef<CGGradientRef> gradient(CGGradientCreateWithColorComponents( |
23 grayColorSpace.get(), grays, locations, arraysize(locations))); | 72 grayColorSpace.get(), grays, locations, arraysize(locations))); |
24 CGPoint topLeft = CGPointMake(0.0, kTopGradientHeight); | 73 CGPoint topLeft = CGPointMake(0.0, kTopGradientHeight); |
25 CGContextDrawLinearGradient(context, gradient.get(), topLeft, CGPointZero, 0); | 74 CGContextDrawLinearGradient(context, gradient.get(), topLeft, CGPointZero, 0); |
26 } | 75 } |
27 @end | 76 @end |
28 | 77 |
| 78 @implementation BackingStoreLayer |
| 79 - (id)initWithBackingStore:(BackingStoreMac*)store { |
| 80 CHECK(store); |
| 81 if ((self = [super init])) { |
| 82 backing_store_ = store; |
| 83 } |
| 84 return self; |
| 85 } |
| 86 |
| 87 - (void)drawInContext:(CGContextRef)context { |
| 88 // Hrm, looks like I need to use the drawing delegate functionality of |
| 89 // a layer here |
| 90 if (backing_store_->cg_layer()) { |
| 91 // TODO: add clipping to dirtyRect if it improves drawing performance. |
| 92 CGContextDrawLayerInRect(context, [self bounds], |
| 93 backing_store_->cg_layer()); |
| 94 } else { |
| 95 NSLog(@"no layer!"); |
| 96 // if we haven't created a layer yet, draw the cached bitmap into |
| 97 // the window. The CGLayer will be created the next time the renderer |
| 98 // paints. |
| 99 scoped_cftyperef<CGImageRef> image( |
| 100 CGBitmapContextCreateImage(backing_store_->cg_bitmap())); |
| 101 CGContextDrawImage(context, [self bounds], image); |
| 102 } |
| 103 } |
| 104 @end |
| 105 |
| 106 // FIXME comment |
| 107 class TabposeCallback { |
| 108 public: |
| 109 TabposeCallback(CALayer* layer) |
| 110 : layer_(layer) {} |
| 111 |
| 112 void DidReceiveBitmap(const SkBitmap& bitmap) { |
| 113 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 114 NSLog(@"receiving bitmap %d %d", bitmap.width(), bitmap.height()); |
| 115 // FIXME: too many conversions |
| 116 scoped_cftyperef<CGImageRef> cgimage(SkCreateCGImageRef(bitmap)); |
| 117 layer_.contents = (id)cgimage.get(); |
| 118 layer_.backgroundColor = nil; // clear out bg color to default value |
| 119 } |
| 120 private: |
| 121 CALayer* layer_; |
| 122 }; |
| 123 |
| 124 // FIXME: look at DISABLE_RUNNABLE_METHOD_REFCOUNT |
| 125 // FIXME: look at URLFetcher's inner class for thread switching |
| 126 |
| 127 // FIMXE comment is wrong |
| 128 // A helper class used to interface with chrome's c++ code. Used to dispatch |
| 129 // stuff to threads; to listen for things; etc. |
| 130 class TabposeHelper : public base::RefCountedThreadSafe<TabposeHelper> { |
| 131 public: |
| 132 TabposeHelper(TabposeWindow* window) |
| 133 : window_(window)/*, factory_(this)*/ {} |
| 134 |
| 135 void LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size& s)
; |
| 136 private: |
| 137 friend class base::RefCountedThreadSafe<TabposeHelper>; |
| 138 ~TabposeHelper() {} |
| 139 void LoadThumbnailIO(RenderWidgetHost* rwh, CALayer* layer2, const gfx::Size&
s, const gfx::Size& fullSize); |
| 140 |
| 141 TabposeWindow* window_; |
| 142 // ScopedRunnableMethodFactory<TabposeHelper> factory_; |
| 143 }; |
| 144 |
| 145 void TabposeHelper::LoadThumbnail(RenderWidgetHost* rwh, CALayer* layer2, const
gfx::Size& s) { |
| 146 // rwh->set_painting_observer(NULL); // FIXME |
| 147 |
| 148 NSSize nsFullSize = [[window_->browser_ tabContentArea] frame].size; |
| 149 gfx::Size fullSize(NSSizeToCGSize(nsFullSize)); |
| 150 |
| 151 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 152 // FIXME: is the hop to the IO thread really necessary? |
| 153 ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, |
| 154 NewRunnableMethod(this, &TabposeHelper::LoadThumbnailIO, rwh, layer2, s, f
ullSize)); |
| 155 } |
| 156 |
| 157 void TabposeHelper::LoadThumbnailIO(RenderWidgetHost* rwh, CALayer* layer2, cons
t gfx::Size& s, const gfx::Size& fullSize) { |
| 158 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 159 ThumbnailGenerator* generator = g_browser_process->GetThumbnailGenerator(); |
| 160 |
| 161 // FIXME: Internally, this allocs memory for both transport dib and then |
| 162 // an SkBitmap (which _then_ needs to be converted to a CGImageRef). |
| 163 // Reduce the amount of copying; possibly by sidestepping |
| 164 // ThumbnailGenerator. |
| 165 TabposeCallback* callback = new TabposeCallback(layer2); |
| 166 window_->callbacks_.push_back(callback); // takes ownership |
| 167 |
| 168 // |fullSize| is slightly larger (view bounds doesn't include scroll bars?) |
| 169 // gfx::Size page_size(rwh->view()->GetViewBounds().size()); // Logical size t
he renderer renders at |
| 170 gfx::Size page_size(fullSize); // Logical size the renderer renders at |
| 171 |
| 172 // This is a bit tricky: We really only need a bitmap at size |s|, but when |
| 173 // the user clicks a thumbnail and it zooms large, this single thumbnail |
| 174 // should have a pixel size close to the view bounds, so that the image doesnt |
| 175 // get blurry. One possible idea would be to get all thumbs at size |s| and |
| 176 // request the one currently below the mouse at full size, but then it might |
| 177 // not be ready when the click happens. For now, KISS, and request everything |
| 178 // at the full resolution. |
| 179 // gfx::Size desired_size(s); // physical pixel size the image is rendered at |
| 180 gfx::Size desired_size(page_size); // physical pixel size the image is render
ed at |
| 181 NSLog(@"scheduling snapshot request"); |
| 182 |
| 183 // FIXME: check that observer is currently NULL |
| 184 // FIXME: this lets us receive straggling requests from an old tabpose window.
leads to crashes. |
| 185 rwh->set_painting_observer(generator); |
| 186 generator->AskForSnapshot(rwh, /*prefer_backing_store=*/false, |
| 187 NewCallback(callback, &TabposeCallback::DidReceiveBi
tmap), |
| 188 page_size, desired_size); |
| 189 // FIXME: needs to be cancelled somehow if we die before renderer returns |
| 190 } |
| 191 |
29 @interface TabposeWindow (Private) | 192 @interface TabposeWindow (Private) |
30 - (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo; | 193 - (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo |
| 194 tempBWC:(BrowserWindowController*)bwc; |
31 - (void)setUpLayers:(NSRect)bgLayerRect; | 195 - (void)setUpLayers:(NSRect)bgLayerRect; |
| 196 - (void)fadeAway:(BOOL)slomo; |
32 @end | 197 @end |
33 | 198 |
34 @implementation TabposeWindow | 199 @implementation TabposeWindow |
35 | 200 |
36 + (id)openTabposeFor:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo { | 201 + (id)openTabposeFor:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo |
| 202 tempBWC:(BrowserWindowController*)bwc { |
37 // Releases itself when closed. | 203 // Releases itself when closed. |
38 return [[TabposeWindow alloc] initForWindow:parent rect:rect slomo:slomo]; | 204 return [[TabposeWindow alloc] initForWindow:parent rect:rect slomo:slomo tempB
WC:bwc]; |
39 } | 205 } |
40 | 206 |
41 - (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo { | 207 - (id)initForWindow:(NSWindow*)parent rect:(NSRect)rect slomo:(BOOL)slomo |
| 208 tempBWC:(BrowserWindowController*)bwc { |
42 NSRect frame = [parent frame]; | 209 NSRect frame = [parent frame]; |
43 if ((self = [super initWithContentRect:frame | 210 if ((self = [super initWithContentRect:frame |
44 styleMask:NSBorderlessWindowMask | 211 styleMask:NSBorderlessWindowMask |
45 backing:NSBackingStoreBuffered | 212 backing:NSBackingStoreBuffered |
46 defer:NO])) { | 213 defer:NO])) { |
47 [self setReleasedWhenClosed:YES]; | 214 [self setReleasedWhenClosed:YES]; |
48 [self setOpaque:NO]; | 215 [self setOpaque:NO]; |
49 [self setBackgroundColor:[NSColor clearColor]]; | 216 [self setBackgroundColor:[NSColor clearColor]]; |
50 [self setUpLayers:rect]; | |
51 [parent addChildWindow:self ordered:NSWindowAbove]; | 217 [parent addChildWindow:self ordered:NSWindowAbove]; |
52 [self makeKeyAndOrderFront:self]; | 218 [self makeKeyAndOrderFront:self]; |
| 219 |
| 220 //NSImage* img = [NSImage imageNamed:@"contents1"]; |
| 221 |
| 222 // [self makeFirstResponder:self]; |
| 223 |
| 224 helper_ = new TabposeHelper(self); |
| 225 |
| 226 state_ = kFadingIn; |
| 227 |
| 228 browser_ = bwc; |
| 229 |
| 230 NSView* contentArea = [browser_ tabContentArea] ; // FIXME |
| 231 NSRect areaFrame = [contentArea frame]; |
| 232 |
| 233 [self setUpLayers:rect]; |
| 234 |
| 235 // Configure hand cursor |
| 236 NSTrackingArea* trackingArea = [[NSTrackingArea alloc] |
| 237 initWithRect:[[self contentView] frame] |
| 238 options:NSTrackingCursorUpdate|NSTrackingActiveInKeyWindow |
| 239 owner:self |
| 240 userInfo:nil]; |
| 241 [[self contentView] addTrackingArea:[trackingArea autorelease]]; |
| 242 |
| 243 [self disableCursorRects]; |
| 244 if (NSPointInRect([NSEvent mouseLocation], [self frame])) |
| 245 [[NSCursor pointingHandCursor] set]; |
| 246 |
| 247 // Needs to be called after -disableCursorRects. |
| 248 [self setAcceptsMouseMovedEvents:YES]; |
| 249 |
| 250 |
| 251 // set up perspective |
| 252 CATransform3D t = CATransform3DIdentity; |
| 253 t.m34 = 1.0 / 1850; |
| 254 bgLayer_.sublayerTransform = t; |
| 255 |
| 256 #if 0 |
| 257 // Animate in. |
| 258 // FIXME: probably want to use a CABasicAnimation instead |
| 259 CATransition* animation = [CATransition animation]; |
| 260 //animation.type = kCATransitionPush; |
| 261 //animation.type = kCATransitionMoveIn; |
| 262 //animation.type = kCATransitionFade; |
| 263 animation.type = kCATransitionReveal; |
| 264 //animation.subtype = kCATransitionFromTop; |
| 265 //animation.delegate = self; |
| 266 animation.duration = 1.f * (slomo ? 4 : 1); // half for in, half for out |
| 267 //[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMe
diaTimingFunctionEaseInEaseOut]]; |
| 268 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMedi
aTimingFunctionEaseOut]]; |
| 269 [bgLayer_ addAnimation:animation forKey:nil]; |
| 270 #endif |
| 271 |
| 272 // Count tabs. |
| 273 // Browser* browser = browserController->browser_.get(); |
| 274 TabStripController* tabStripController = browser_->tabStripController_.get()
; |
| 275 TabStripModel* tabStripModel = tabStripController->tabStripModel_; |
| 276 NSArray* arrays = tabStripController->tabArray_.get(); |
| 277 NSSet* closing = tabStripController->closingControllers_.get(); |
| 278 |
| 279 selectedLayer_ = nil; |
| 280 initiallySelectedIndex_ = selectedIndex_ = tabStripModel->selected_index(); |
| 281 |
| 282 |
| 283 int n = [arrays count] - [closing count]; |
| 284 |
| 285 // We want to have the small rects have the same aspect ratio a as a full |
| 286 // tab. Let w, h be the size of a small rect, and w_c, h_c the size of the |
| 287 // container. dx, dy are the distances between small rects in x, y direction
. |
| 288 |
| 289 // Geometry yields: |
| 290 // w_c = nx * (w + dx) - dx <=> w = (w_c + d_x) / nx - d_x |
| 291 // h_c = ny * (h + dy) - dy <=> h = (h_c + d_y) / ny - d_t |
| 292 // Plugging this into |
| 293 // a := tab_width / tab_height = w / h |
| 294 // yields |
| 295 // a = ((w_c - (nx - 1)*d_x)*ny) / (nx*(h_c - (ny - 1)*d_y)) |
| 296 // Plugging in nx = n/ny and pen and paper (or wolfram alpha: |
| 297 // 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) |
| 298 // 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) |
| 299 // ) gives us nx and ny (but the wrong root -- s/-sqrt(FOO)/sqrt(FOO)/. |
| 300 |
| 301 int row = 0, col = 0, i = 0; |
| 302 |
| 303 CGFloat a = NSWidth([contentArea frame]) / NSHeight([contentArea frame]); |
| 304 |
| 305 // FIXME: need to audit everything to check where i want rects with and with
out bookmarks bar |
| 306 // (right now, detached bookmark bar screws this up a bit) |
| 307 int kOffsetTop = 30 + NSHeight([[browser_ window] frame]) - NSHeight([[brows
er_ tabContentArea] frame]); |
| 308 // if (browser_->bookmarkBarController_.get()->visualState_ == bookmarks::kDe
tachedState) // FIXME: check for NULL? |
| 309 // kOffsetTop -= NSHeight([[browser_->bookmarkBarController_ view] frame]); |
| 310 int kOffsetLeft = 30; |
| 311 int kOffsetRight = 30; |
| 312 int kOffsetBottom = 30; |
| 313 int kPaddingX = 15; |
| 314 int kPaddingY = 10; |
| 315 |
| 316 int extraOffsetX = 0; |
| 317 int extraOffsetY = 0; |
| 318 double w_c = NSWidth(frame) - kOffsetLeft - kOffsetRight; |
| 319 double h_c = NSHeight(frame) - kOffsetTop - kOffsetBottom; |
| 320 dx = kPaddingX; |
| 321 dy = kPaddingY; |
| 322 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)); |
| 323 ny = int(roundf(fny)); |
| 324 nx = int(ceilf(n / float(ny))); |
| 325 last_nx = n - nx * (ny - 1); |
| 326 w = floor((w_c + dx)/float(nx) - dx); |
| 327 h = floor((h_c + dy)/float(ny) - dy); |
| 328 if (a > w/float(h)) { |
| 329 h = w / a; |
| 330 extraOffsetY = (h_c - ((h + dy)*ny - dy))/2; |
| 331 } else { |
| 332 w = h * a; |
| 333 extraOffsetX = (w_c - ((w + dx)*nx - dx))/2; |
| 334 } |
| 335 |
| 336 NSMutableArray* allLayers = [[NSMutableArray alloc] initWithCapacity:n]; |
| 337 allLayers_.reset(allLayers); |
| 338 // for (TabController* tab in arrays) { |
| 339 for (int ci = 0; ci < n; ++ci) { |
| 340 #if 0 |
| 341 if ([closing containsObject:tab]) |
| 342 continue; |
| 343 |
| 344 NSView* tabView = [tab view]; |
| 345 |
| 346 CALayer* layer = [CALayer layer]; |
| 347 layer.backgroundColor = blue; |
| 348 layer.frame = NSRectToCGRect([[self contentView] convertRect:[tabView boun
ds] fromView:tabView]); |
| 349 #endif |
| 350 #if 0 |
| 351 // Get tab image. FIXME: on bg thread? |
| 352 #if 0 |
| 353 // XXX: Doesn't work?! |
| 354 NSBitmapImageRep* imageRep = |
| 355 [tabView bitmapImageRepForCachingDisplayInRect:[tabView bounds]]; |
| 356 if ([imageRep bitmapData]) |
| 357 bzero([imageRep bitmapData], |
| 358 [imageRep bytesPerRow] * [imageRep pixelsHigh]); |
| 359 [tabView cacheDisplayInRect:[tabView bounds] toBitmapImageRep:imageRep]; |
| 360 CGImageRef image = CGImageCreateCopy([imageRep CGImage]); |
| 361 layer.contents = (id)image; |
| 362 #elif 0 |
| 363 [tabView lockFocus]; |
| 364 NSBitmapImageRep* imageRep = |
| 365 [[[NSBitmapImageRep alloc] initWithFocusedViewRect:[tabView bounds]] a
utorelease]; |
| 366 [tabView unlockFocus]; |
| 367 CGImageRef image = CGImageCreateCopy([imageRep CGImage]); |
| 368 layer.contents = (id)image; |
| 369 #else |
| 370 NSImage* nsi = [NSImage imageNamed:@"NSUserGroup"]; |
| 371 layer.contents = nsi; |
| 372 #endif |
| 373 // CFRelease(image); |
| 374 // layer.contents = imageRep; |
| 375 |
| 376 // NSImage *image = [[[NSImage alloc] initWithSize:[imageRep size]] autorel
ease]; |
| 377 // [image addRepresentation:imageRep]; |
| 378 // layer.contents = image; |
| 379 #endif |
| 380 |
| 381 // [rootLayer addSublayer:layer]; |
| 382 |
| 383 CALayer* layer2; |
| 384 // FIXME: on bg thread; also call the other method; ; also do |
| 385 // CGLayer->CGImage conversion directly without going through skia, etc. |
| 386 TabContents* contents = tabStripModel->GetTabContentsAt(i); |
| 387 DCHECK(contents); |
| 388 RenderViewHost* rvh = contents->render_view_host(); |
| 389 RenderWidgetHost* rwh = rvh; |
| 390 DCHECK(rwh); |
| 391 |
| 392 BackingStoreMac* backing_store = (BackingStoreMac*)rwh->GetBackingStore(fa
lse); |
| 393 if (backing_store) { |
| 394 // FIXME: looks like the backing store can go away while we're running :
-/ |
| 395 layer2 = [[[BackingStoreLayer alloc] initWithBackingStore:backing_store]
autorelease]; |
| 396 [layer2 setNeedsDisplay]; |
| 397 } else { |
| 398 layer2 = [CALayer layer]; |
| 399 // Set placeholder |
| 400 #if 0 |
| 401 // Icon as placeholder (FIXME: load only once) |
| 402 int m = MIN(w, h); |
| 403 // Can't use nsimage_cache::ImageNamed(), because that uses |
| 404 // initWithContentsOfFile, which creates a cached representation at |
| 405 // 16x16 and then throws away the vector data. |
| 406 NSString* path = [mac_util::MainAppBundle() pathForImageResource:@"nav.p
df"]; |
| 407 NSPDFImageRep* pdfRep = [NSPDFImageRep imageRepWithContentsOfFile:path]; |
| 408 NSImage* pdfImage = [[[NSImage alloc] initWithSize:NSMakeSize(m, m)] aut
orelease]; |
| 409 [pdfImage addRepresentation:pdfRep]; |
| 410 // FIXME: if we don't leak this, the image rep frees its cgimage soon |
| 411 // and the CALayer crashes when it wants to draw. |
| 412 NSBitmapImageRep* bitmap = [[NSBitmapImageRep imageRepWithData:[pdfImage
TIFFRepresentation]] retain]; // XXX |
| 413 layer2.contents = (id)[bitmap CGImage]; |
| 414 #else |
| 415 // background color as placeholder |
| 416 layer2.backgroundColor = CGColorGetConstantColor(kCGColorWhite); |
| 417 #endif |
| 418 helper_->LoadThumbnail(rwh, layer2, gfx::Size(w, h)); |
| 419 } |
| 420 layer2.contentsGravity = kCAGravityResizeAspect; // for placeholder |
| 421 |
| 422 // layer2.backgroundColor = blue; |
| 423 int x = kOffsetLeft + col*(w + kPaddingX) + extraOffsetX; |
| 424 if (row == ny - 1) { |
| 425 // last row |
| 426 x += (nx - last_nx) * (w + kPaddingX) / 2; |
| 427 } |
| 428 int y = kOffsetTop + row*(h + kPaddingY) + extraOffsetY; |
| 429 layer2.anchorPoint = CGPointMake(0.5, 0.5); |
| 430 |
| 431 // NSBitmapImageRep* rep = (NSBitmapImageRep*)[img bestRepresentationForDev
ice:nil]; |
| 432 // layer2.contents = (id)[rep CGImage]; |
| 433 // layer2.contentsGravity = kCAGravityResizeAspect; |
| 434 |
| 435 // layer2.shadowOpacity = 0.5; // FIXME: Slows things down, even if disabl
ed before animation runs |
| 436 layer2.shadowRadius = 10; |
| 437 layer2.shadowOffset = CGSizeMake(0, -10); |
| 438 // layer2.borderColor = blue; |
| 439 |
| 440 |
| 441 if (i == selectedIndex_) { |
| 442 // http://developer.apple.com/mac/library/qa/qa2008/qa1620.html |
| 443 // Prepare the animation from the old size to the new size |
| 444 CGRect oldBounds = NSRectToCGRect([contentArea frame]); |
| 445 CGRect newBounds = oldBounds; |
| 446 newBounds.size = CGSizeMake(w, h); |
| 447 CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"b
ounds"]; |
| 448 animation.fromValue = [NSValue valueWithRect:NSRectFromCGRect(oldBounds)
]; |
| 449 animation.toValue = [NSValue valueWithRect:NSRectFromCGRect(newBounds)]; |
| 450 animation.duration = .25f * (slomo ? 4 : 1); |
| 451 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMe
diaTimingFunctionEaseOut]; |
| 452 |
| 453 // Update the layer's bounds so the layer doesn't snap back when the ani
mation completes. |
| 454 layer2.bounds = newBounds; |
| 455 |
| 456 // Add the animation, overriding the implicit animation. |
| 457 [layer2 addAnimation:animation forKey:@"bounds"]; |
| 458 |
| 459 // Prepare the animation from the current position to the new position |
| 460 NSPoint t = areaFrame.origin; |
| 461 |
| 462 CGPoint opoint = CGPointMake(t.x + NSWidth(areaFrame)/2, t.y + NSHeight(
areaFrame)/2); |
| 463 CGPoint point = CGPointMake(x + w/2, NSHeight(frame) - y - h + h/2); |
| 464 animation = [CABasicAnimation animationWithKeyPath:@"position"]; |
| 465 animation.fromValue = [NSValue valueWithPoint:NSPointFromCGPoint(opoint)
]; |
| 466 animation.toValue = [NSValue valueWithPoint:NSPointFromCGPoint(point)]; |
| 467 animation.duration = .25f * (slomo ? 4 : 1); |
| 468 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMe
diaTimingFunctionEaseOut]; |
| 469 |
| 470 // Update the layer's position so that the layer doesn't snap back when
the animation completes. |
| 471 layer2.position = point; |
| 472 |
| 473 // Add the animation, overriding the implicit animation. |
| 474 [layer2 addAnimation:animation forKey:@"position"]; |
| 475 |
| 476 [rootLayer_ insertSublayer:layer2 above:bgLayer_]; |
| 477 selectedLayer_ = layer2; |
| 478 |
| 479 // selectedLayer_.borderWidth = 5; |
| 480 // [selectedLayer_ setValue:[NSNumber numberWithFloat:S] forKey:@"transfo
rm.scale"]; |
| 481 [CATransaction begin]; |
| 482 [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActio
ns]; |
| 483 selectedLayer_.transform = CATransform3DMakeScale(S, S, 1); |
| 484 [CATransaction commit]; |
| 485 } else { |
| 486 #if 0 |
| 487 CABasicAnimation* zoom = [CABasicAnimation animationWithKeyPath:@"transfo
rm.translation.z"]; |
| 488 [layer2 setValue:[NSNumber numberWithFloat:0] forKey:@"transform.translat
ion.z"]; |
| 489 zoom.fromValue = [NSNumber numberWithFloat:1000]; |
| 490 zoom.toValue = [NSNumber numberWithFloat:0]; |
| 491 zoom.duration = .25f * (slomo ? 4 : 1); |
| 492 zoom.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTim
ingFunctionLinear]; |
| 493 [layer2 addAnimation:zoom forKey:@"transform"]; |
| 494 #else |
| 495 int sel_col = selectedIndex_ % nx; |
| 496 int sel_row = selectedIndex_ / nx; |
| 497 CGFloat r = NSWidth([contentArea frame]) / float(w); |
| 498 int ox = (NSWidth([contentArea frame]) + dx*r) * (col - sel_col); |
| 499 if (row == ny - 1) { |
| 500 // last row |
| 501 ox += (nx - last_nx) * (NSWidth([contentArea frame]) + dx*r) / 2; |
| 502 } |
| 503 if (sel_row == ny - 1) { |
| 504 // last row |
| 505 ox -= (nx - last_nx) * (NSWidth([contentArea frame]) + dx*r) / 2; |
| 506 } |
| 507 int oy = (NSHeight([contentArea frame]) + dy*r) * (row - sel_row); |
| 508 |
| 509 CGRect oldBounds = NSRectToCGRect([contentArea frame]); |
| 510 CGRect newBounds = oldBounds; |
| 511 newBounds.size = CGSizeMake(w, h); |
| 512 CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"bo
unds"]; |
| 513 animation.fromValue = [NSValue valueWithRect:NSRectFromCGRect(oldBounds)]
; |
| 514 animation.toValue = [NSValue valueWithRect:NSRectFromCGRect(newBounds)]; |
| 515 animation.duration = .25f * (slomo ? 4 : 1); |
| 516 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMed
iaTimingFunctionEaseOut]; |
| 517 |
| 518 // Update the layer's bounds so the layer doesn't snap back when the anim
ation completes. |
| 519 layer2.bounds = newBounds; |
| 520 |
| 521 // Add the animation, overriding the implicit animation. |
| 522 [layer2 addAnimation:animation forKey:@"bounds"]; |
| 523 |
| 524 // Prepare the animation from the current position to the new position |
| 525 NSPoint t = [contentArea frame].origin; |
| 526 CGPoint opoint = CGPointMake(ox + t.x + NSWidth(areaFrame)/2, /*NSHeight(
frame) - h*/- oy + t.y + NSHeight(areaFrame)/2); |
| 527 |
| 528 CGPoint point = CGPointMake(x + w/2, NSHeight(frame) - y - h + h/2); |
| 529 animation = [CABasicAnimation animationWithKeyPath:@"position"]; |
| 530 animation.fromValue = [NSValue valueWithPoint:NSPointFromCGPoint(opoint)]
; |
| 531 animation.toValue = [NSValue valueWithPoint:NSPointFromCGPoint(point)]; |
| 532 animation.duration = .25f * (slomo ? 4 : 1); |
| 533 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMed
iaTimingFunctionEaseOut]; |
| 534 |
| 535 // Update the layer's position so that the layer doesn't snap back when t
he animation completes. |
| 536 layer2.position = point; |
| 537 |
| 538 // Add the animation, overriding the implicit animation. |
| 539 [layer2 addAnimation:animation forKey:@"position"]; |
| 540 #endif |
| 541 |
| 542 [bgLayer_ addSublayer:layer2]; |
| 543 } |
| 544 [allLayers addObject:layer2]; |
| 545 |
| 546 |
| 547 ++col; |
| 548 if (col == nx) { |
| 549 col = 0; |
| 550 ++row; |
| 551 } |
| 552 ++i; |
| 553 } |
53 } | 554 } |
54 return self; | 555 return self; |
55 } | 556 } |
56 | 557 |
57 - (void)setUpLayers:(NSRect)bgLayerRect { | 558 - (void)setUpLayers:(NSRect)bgLayerRect { |
58 // Root layer -- covers whole window. | 559 // Root layer -- covers whole window. |
59 rootLayer_ = [CALayer layer]; | 560 rootLayer_ = [CALayer layer]; |
60 [[self contentView] setLayer:rootLayer_]; | 561 [[self contentView] setLayer:rootLayer_]; |
61 [[self contentView] setWantsLayer:YES]; | 562 [[self contentView] setWantsLayer:YES]; |
62 | 563 |
(...skipping 18 matching lines...) Expand all Loading... |
81 | 582 |
82 - (BOOL)canBecomeKeyWindow { | 583 - (BOOL)canBecomeKeyWindow { |
83 return YES; | 584 return YES; |
84 } | 585 } |
85 | 586 |
86 - (void)keyDown:(NSEvent*)event { | 587 - (void)keyDown:(NSEvent*)event { |
87 // Overridden to prevent beeps. | 588 // Overridden to prevent beeps. |
88 } | 589 } |
89 | 590 |
90 - (void)keyUp:(NSEvent*)event { | 591 - (void)keyUp:(NSEvent*)event { |
| 592 if (state_ == kFadingOut) |
| 593 return; |
| 594 |
91 NSString* characters = [event characters]; | 595 NSString* characters = [event characters]; |
92 if ([characters length] < 1) | 596 if ([characters length] < 1) |
93 return; | 597 return; |
94 | 598 |
95 unichar character = [characters characterAtIndex:0]; | 599 unichar character = [characters characterAtIndex:0]; |
96 switch (character) { | 600 switch (character) { |
97 case NSEnterCharacter: | 601 case NSEnterCharacter: |
98 case NSNewlineCharacter: | 602 case NSNewlineCharacter: |
99 case NSCarriageReturnCharacter: | 603 case NSCarriageReturnCharacter: |
100 case ' ': | 604 case ' ': |
| 605 [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
| 606 break; |
101 case '\e': // Escape | 607 case '\e': // Escape |
102 [self close]; | 608 selectedIndex_ = initiallySelectedIndex_; |
| 609 selectedLayer_ = [allLayers_ objectAtIndex:selectedIndex_]; |
| 610 [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
103 break; | 611 break; |
104 } | 612 } |
105 } | 613 } |
106 | 614 |
| 615 - (void)mouseMoved:(NSEvent*)event { |
| 616 int i = 0; |
| 617 CALayer* newSelectedLayer = nil; |
| 618 int newIndex = -1; |
| 619 CGPoint p = NSPointToCGPoint([event locationInWindow]); |
| 620 for (CALayer* layer in allLayers_.get()) { |
| 621 CGPoint lp = [layer convertPoint:p fromLayer:rootLayer_]; |
| 622 if ([layer containsPoint:lp]) { |
| 623 newSelectedLayer = layer; |
| 624 newIndex = i; |
| 625 } |
| 626 ++i; |
| 627 } |
| 628 |
| 629 if (newSelectedLayer && newSelectedLayer != selectedLayer_) { |
| 630 CALayer* oldSelectedLayer = selectedLayer_; |
| 631 |
| 632 [CATransaction begin]; |
| 633 [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActio
ns]; |
| 634 if (selectedLayer_) |
| 635 [bgLayer_ addSublayer:selectedLayer_]; |
| 636 selectedLayer_ = newSelectedLayer; |
| 637 [selectedLayer_ removeFromSuperlayer]; |
| 638 [rootLayer_ insertSublayer:selectedLayer_ above:bgLayer_]; |
| 639 selectedIndex_ = newIndex; |
| 640 [CATransaction commit]; |
| 641 |
| 642 if (oldSelectedLayer) |
| 643 oldSelectedLayer.transform = CATransform3DIdentity; |
| 644 if (newSelectedLayer) |
| 645 newSelectedLayer.transform = CATransform3DMakeScale(S, S, 1); |
| 646 } |
| 647 } |
| 648 |
| 649 // FIXME: do this on long hover, too |
107 - (void)mouseDown:(NSEvent*)event { | 650 - (void)mouseDown:(NSEvent*)event { |
108 [self close]; | 651 [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
109 } | 652 } |
110 | 653 |
111 - (void)swipeWithEvent:(NSEvent*)event { | 654 - (void)swipeWithEvent:(NSEvent*)event { |
112 if ([event deltaY] > 0.5) // Swipe up | 655 if ([event deltaY] > 0.5) // Swipe up |
113 [self close]; | 656 [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
114 } | 657 } |
115 | 658 |
116 - (void)close { | 659 - (void)close { |
117 // Prevent parent window from disappearing. | 660 // Prevent parent window from disappearing. |
118 [[self parentWindow] removeChildWindow:self]; | 661 [[self parentWindow] removeChildWindow:self]; |
119 [super close]; | 662 [super close]; |
120 } | 663 } |
121 | 664 |
122 - (void)commandDispatch:(id)sender { | 665 - (void)commandDispatch:(id)sender { |
123 // Without this, -validateUserInterfaceItem: is not called. | 666 // Without this, -validateUserInterfaceItem: is not called. |
124 } | 667 } |
125 | 668 |
126 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { | 669 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
127 // Disable all browser-related menu items. | 670 // Disable all browser-related menu items. |
128 return NO; | 671 return NO; |
129 } | 672 } |
130 | 673 |
| 674 |
| 675 - (void)cursorUpdate:(NSEvent*)event { |
| 676 [[NSCursor pointingHandCursor] set]; |
| 677 } |
| 678 |
| 679 - (void)fadeAway:(BOOL)slomo { |
| 680 if (state_ == kFadingOut) |
| 681 return; |
| 682 |
| 683 [[NSCursor arrowCursor] set]; |
| 684 |
| 685 state_ = kFadingOut; |
| 686 [self setAcceptsMouseMovedEvents:NO]; |
| 687 |
| 688 // Clean out observers, so that eventual pending thumbnail requests don't |
| 689 // resolve against dead objects. |
| 690 TabStripController* tabStripController = browser_->tabStripController_.get(); |
| 691 TabStripModel* tabStripModel = tabStripController->tabStripModel_; |
| 692 for (int i = 0; i < tabStripModel->count(); ++i) { |
| 693 TabContents* contents = tabStripModel->GetTabContentsAt(i); |
| 694 contents->render_view_host()->set_painting_observer(NULL); |
| 695 } |
| 696 |
| 697 // Select chosen tab (FIXME: don't on ESC) |
| 698 if (selectedIndex_ >= 0) |
| 699 tabStripModel->SelectTabContentsAt(selectedIndex_, /*user_gesture=*/true); |
| 700 |
| 701 #if 1 |
| 702 // FIXME: this is only here so that we get a notification after 4 / 1 seconds. |
| 703 // Do that in a simpler way. |
| 704 CABasicAnimation* fade = [CABasicAnimation animationWithKeyPath:@"opacity"]; |
| 705 bgLayer_.opacity = 0.99; // make it stick, change model value |
| 706 fade.fromValue = [NSNumber numberWithFloat:1]; |
| 707 fade.toValue = [NSNumber numberWithFloat:0.99]; |
| 708 fade.duration = .5f * (slomo ? 4 : 1); |
| 709 fade.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFu
nctionEaseOut]; |
| 710 fade.delegate = self; |
| 711 #endif |
| 712 |
| 713 if (selectedIndex_ >= 0) { |
| 714 CALayer* layer = selectedLayer_; //[[bgLayer_ sublayers] objectAtIndex:selec
tedIndex_]; |
| 715 |
| 716 [CATransaction begin]; |
| 717 [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActio
ns]; |
| 718 // layer.contentsGravity = kCAGravityResize; |
| 719 // layer.opaque = YES; |
| 720 // layer.compositingFilter = [CIFilter filterWithName:@"CISourceInCompositing
"]; |
| 721 selectedLayer_.borderWidth = 0; |
| 722 |
| 723 // Animating large shadows is slow -- don't. |
| 724 layer.shadowOpacity = 0; |
| 725 layer.transform = CATransform3DIdentity; |
| 726 |
| 727 [layer removeFromSuperlayer]; |
| 728 // [rootLayer_ insertSublayer:layer above:bgLayer_]; |
| 729 [bgLayer_ addSublayer:layer]; // move on top of all other layers |
| 730 |
| 731 [CATransaction commit]; |
| 732 |
| 733 |
| 734 |
| 735 [CATransaction begin]; |
| 736 if (slomo) { |
| 737 CGFloat f = [[CATransaction valueForKey:kCATransactionAnimationDuration] f
loatValue]; |
| 738 f = 0.5; // the above returns 0 :-P |
| 739 [CATransaction setValue:[NSNumber numberWithFloat:4*f] forKey:kCATransacti
onAnimationDuration]; |
| 740 } |
| 741 layer.frame = NSRectToCGRect([[browser_ tabContentArea] frame]); |
| 742 if ([layer isKindOfClass:[BackingStoreLayer class]]) |
| 743 [layer setNeedsDisplay]; // Redraw layer at big resolution, so that zoom-
in isn't blocky. |
| 744 // layer.opacity = 0; |
| 745 [CATransaction commit]; |
| 746 } |
| 747 |
| 748 int i = 0, col = 0, row = 0; |
| 749 for (CALayer* layer in allLayers_.get()) { |
| 750 // DCHECK_NE(layer, selectedLayer_); |
| 751 // if (layer == selectedLayer_) continue; |
| 752 if (i == selectedIndex_) { |
| 753 ++i; |
| 754 ++col; |
| 755 if (col == nx) { |
| 756 col = 0; |
| 757 ++row; |
| 758 } |
| 759 continue; |
| 760 } |
| 761 |
| 762 // Animating large shadows is slow -- don't. |
| 763 [CATransaction begin]; |
| 764 [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActio
ns]; |
| 765 layer.shadowOpacity = 0; |
| 766 [CATransaction commit]; |
| 767 |
| 768 #if 0 |
| 769 CABasicAnimation* zoom = [CABasicAnimation animationWithKeyPath:@"transform.
translation.z"]; |
| 770 // [layer setValue:[NSNumber numberWithFloat:1000] forKey:@"transform.transla
tion.z"]; |
| 771 zoom.fromValue = [NSNumber numberWithFloat:0]; |
| 772 zoom.toValue = [NSNumber numberWithFloat:-1000]; // negative Z to keep them
behind the selected layer |
| 773 zoom.duration = .5f * (slomo ? 4 : 1); |
| 774 zoom.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTiming
FunctionLinear]; |
| 775 [layer addAnimation:zoom forKey:@"transform"]; |
| 776 #else |
| 777 int sel_col = selectedIndex_ % nx; |
| 778 int sel_row = selectedIndex_ / nx; |
| 779 NSLog(@"%d %d %d %d %d", col, row, sel_col, sel_row, selectedIndex_); |
| 780 CGFloat r = NSWidth([[browser_ tabContentArea] frame]) / float(w); |
| 781 int ox = (NSWidth([[browser_ tabContentArea] frame]) + dx*r) * (col - sel_c
ol); |
| 782 if (row == ny - 1) { |
| 783 // last row |
| 784 ox += (nx - last_nx) * (NSWidth([[browser_ tabContentArea] frame]) + dx*r
) / 2; |
| 785 } |
| 786 if (sel_row == ny - 1) { |
| 787 // last row |
| 788 ox -= (nx - last_nx) * (NSWidth([[browser_ tabContentArea] frame]) + dx*r
) / 2; |
| 789 } |
| 790 int oy = (NSHeight([[browser_ tabContentArea] frame]) + dy*r) * (row - sel_
row); |
| 791 |
| 792 CGRect newFrame = NSRectToCGRect([[browser_ tabContentArea] frame]); |
| 793 newFrame.origin.x += ox; |
| 794 newFrame.origin.y -= oy; |
| 795 |
| 796 [CATransaction begin]; |
| 797 if (slomo) { |
| 798 CGFloat f = 0.5; |
| 799 [CATransaction setValue:[NSNumber numberWithFloat:4*f] forKey:kCATransact
ionAnimationDuration]; |
| 800 } |
| 801 layer.frame = newFrame; |
| 802 [CATransaction commit]; |
| 803 |
| 804 ++col; |
| 805 if (col == nx) { |
| 806 col = 0; |
| 807 ++row; |
| 808 } |
| 809 #endif |
| 810 ++i; |
| 811 } |
| 812 |
| 813 [bgLayer_ addAnimation:fade forKey:@"opacity"]; // override any implicit anim
ation |
| 814 } |
| 815 |
| 816 - (BOOL)windowShouldClose:(id)window { |
| 817 [self fadeAway:NO]; |
| 818 return NO; |
| 819 } |
| 820 |
| 821 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag { |
| 822 [self close]; |
| 823 } |
| 824 |
131 @end | 825 @end |
OLD | NEW |