diff --git a/mozilla/camino/src/browser/BrowserContentViews.mm b/mozilla/camino/src/browser/BrowserContentViews.mm index 9ca4ef8176c..ff36ebbf6d9 100644 --- a/mozilla/camino/src/browser/BrowserContentViews.mm +++ b/mozilla/camino/src/browser/BrowserContentViews.mm @@ -203,6 +203,7 @@ static const float kMinBrowserViewHeight = 100.0; [mBrowserContainerView setHidden:YES]; [self addSubview:mTabThumbnailGridView]; + [[self window] makeFirstResponder:[mTabThumbnailGridView nextValidKeyView]]; } - (void)hideTabThumbnailGridView diff --git a/mozilla/camino/src/browser/BrowserWindowController.mm b/mozilla/camino/src/browser/BrowserWindowController.mm index da4aac45e93..133793b4157 100644 --- a/mozilla/camino/src/browser/BrowserWindowController.mm +++ b/mozilla/camino/src/browser/BrowserWindowController.mm @@ -5253,16 +5253,6 @@ public: { [mContentView toggleTabThumbnailGridView]; if ([mContentView tabThumbnailGridViewIsVisible]) { - // If either of the toolbar fields is in the middle of an edit, end the edit - // by focusing the window. - NSText* locationFieldEditor = [mURLBar fieldEditor]; - NSText* searchFieldEditor = [[self window] fieldEditor:NO forObject:mSearchBar]; - NSResponder* currentFirstResponder = [[self window] firstResponder]; - if ((locationFieldEditor && currentFirstResponder == locationFieldEditor) || - (searchFieldEditor && currentFirstResponder == searchFieldEditor)) - { - [[self window] makeFirstResponder:[self window]]; - } [mURLBar setEditable:NO]; [mSearchBar setEditable:NO]; } diff --git a/mozilla/camino/src/browser/TabThumbnailGridView.mm b/mozilla/camino/src/browser/TabThumbnailGridView.mm index fcce846970a..81c4e3bddcc 100644 --- a/mozilla/camino/src/browser/TabThumbnailGridView.mm +++ b/mozilla/camino/src/browser/TabThumbnailGridView.mm @@ -54,6 +54,7 @@ const int kHorizontalPadding = 25; @interface TabThumbnailGridView (Private) - (void)updateGridSizeFor:(int)num; - (void)layoutThumbnails; +- (void)setUpKeyViewLoopWithInitialIndex:(unsigned int)startIndex; - (void)createThumbnailViews; - (BOOL)validateTabExists:(ThumbnailView*)tabThumb; @end @@ -100,9 +101,6 @@ const int kHorizontalPadding = 25; NSMutableArray* thumbViewsToLoad = [NSMutableArray arrayWithCapacity:[openTabs count]]; - NSEnumerator* tabEnum = [openTabs objectEnumerator]; - BrowserTabViewItem* tabViewItem; - NSSize browserContentSize = [[bwc browserWrapper] frame].size; NSImage* placeholderThumb = [[[NSImage alloc] initWithSize:browserContentSize] autorelease]; [placeholderThumb lockFocus]; @@ -110,6 +108,8 @@ const int kHorizontalPadding = 25; [NSBezierPath fillRect:NSMakeRect(0, 0, browserContentSize.width, browserContentSize.height)]; [placeholderThumb unlockFocus]; + NSEnumerator* tabEnum = [openTabs objectEnumerator]; + BrowserTabViewItem* tabViewItem; while ((tabViewItem = [tabEnum nextObject])) { ThumbnailView* curThumbView = [[[ThumbnailView alloc] init] autorelease]; if (curThumbView) { @@ -123,6 +123,8 @@ const int kHorizontalPadding = 25; } [self layoutThumbnails]; + unsigned int selectedIndex = [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]; + [self setUpKeyViewLoopWithInitialIndex:selectedIndex]; // Start the tab thumbnailing process. We do this one tab at a time, scheduled // with the runloop, so that the grid view comes up immediately and the UI @@ -132,6 +134,32 @@ const int kHorizontalPadding = 25; afterDelay:0]; } +// +// Wires up a key loop that cycles through all the thumbnails, starting with +// the one at the given index. +// +// This assumes that there's nothing outside this view that needs to be part of +// the loop, so if that changes in the future this will need adjusting. +// +- (void)setUpKeyViewLoopWithInitialIndex:(unsigned int)startIndex +{ + // First make a self-contained tab loop. + NSArray* thumbViews = [self subviews]; + unsigned int thumbViewCount = [thumbViews count]; + for (unsigned int i = 0; i < thumbViewCount ; i++) { + unsigned int nextThumbIndex = (i + 1) % thumbViewCount; + ThumbnailView* thumbView = [thumbViews objectAtIndex:i]; + [thumbView setNextKeyView:[thumbViews objectAtIndex:nextThumbIndex]]; + } + + // Then, insert the grid view itself just before the selected tab, so that + // keyboard selection will start there. + [self setNextKeyView:[thumbViews objectAtIndex:startIndex]]; + unsigned int previousThumbIndex = + (startIndex + thumbViewCount - 1) % thumbViewCount; + [[thumbViews objectAtIndex:previousThumbIndex] setNextKeyView:self]; +} + - (void)loadNextThumbnailForViews:(NSMutableArray*)thumbViewsToLoad { // First, bail if the tab grid view has been dismissed. diff --git a/mozilla/camino/src/browser/ThumbnailView.m b/mozilla/camino/src/browser/ThumbnailView.m index af2ef14b3cf..7e6dd070532 100644 --- a/mozilla/camino/src/browser/ThumbnailView.m +++ b/mozilla/camino/src/browser/ThumbnailView.m @@ -38,12 +38,20 @@ #import "ThumbnailView.h" +#import + static const int kShadowX = 0; static const int kShadowY = -3; static const int kShadowRadius = 5; static const int kShadowPadding = 5; static const int kThumbnailTitleHeight = 20; +@interface ThumbnailView (Private) +// Causes the browser to select the tab corresponding to this thumbnail. +- (void)selectTab; +@end + + @implementation ThumbnailView - (id)initWithFrame:(NSRect)frame { @@ -95,6 +103,23 @@ static const int kThumbnailTitleHeight = 20; return mDelegate; } +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + [self setNeedsDisplay:YES]; + return YES; +} + +- (BOOL)resignFirstResponder +{ + [self setNeedsDisplay:YES]; + return YES; +} + - (void)drawRect:(NSRect)rect { NSShadow* shadow = [[[NSShadow alloc] init] autorelease]; @@ -120,10 +145,18 @@ static const int kThumbnailTitleHeight = 20; thumbnailImageRect.origin.x = ([self bounds].size.width - thumbnailImageRect.size.width) / 2.0; } - [mThumbnail drawInRect:NSInsetRect(thumbnailImageRect, kShadowPadding, kShadowPadding) + NSRect insetRect = NSInsetRect(thumbnailImageRect, kShadowPadding, kShadowPadding); + [mThumbnail drawInRect:insetRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1]; + if ([[self window] firstResponder] == self) { + CGRect focusRect = CGRectMake(insetRect.origin.x, insetRect.origin.y, + insetRect.size.width, insetRect.size.height); + HIThemeDrawFocusRect(&focusRect, true, + [[NSGraphicsContext currentContext] graphicsPort], + kHIThemeOrientationNormal); + } } if (mTitleCell) @@ -131,6 +164,24 @@ static const int kThumbnailTitleHeight = 20; } - (void)mouseUp:(NSEvent*)theEvent +{ + NSPoint location = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + if (NSPointInRect(location, [self bounds])) + [self selectTab]; +} + +- (void)keyDown:(NSEvent*)theEvent +{ + if (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == 0 && + [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) { + [self selectTab]; + return; + } + [super keyDown:theEvent]; +} + +- (void)selectTab { if ([mDelegate respondsToSelector:@selector(thumbnailViewWasSelected:)]) [mDelegate thumbnailViewWasSelected:self];