Chromium Code Reviews| Index: chrome/browser/cocoa/tabpose_window.mm |
| diff --git a/chrome/browser/cocoa/tabpose_window.mm b/chrome/browser/cocoa/tabpose_window.mm |
| index 390815fb115cefa2c04b4cabf666253d8ce5aee6..68c809cb2c9cde6e73e3186a3e52ec5093db0543 100644 |
| --- a/chrome/browser/cocoa/tabpose_window.mm |
| +++ b/chrome/browser/cocoa/tabpose_window.mm |
| @@ -10,6 +10,7 @@ |
| #include "base/mac_util.h" |
| #include "base/scoped_callback_factory.h" |
| #include "base/sys_string_conversions.h" |
| +#include "chrome/app/chrome_dll_resource.h" |
| #include "chrome/browser/browser_process.h" |
| #import "chrome/browser/cocoa/bookmark_bar_constants.h" |
| #import "chrome/browser/cocoa/browser_window_controller.h" |
| @@ -462,6 +463,17 @@ class TileSet { |
| Tile& tile_at(int index) { return *tiles_[index]; } |
| const Tile& tile_at(int index) const { return *tiles_[index]; } |
| + // These return which index needs to be selected when the user presses |
| + // up, down, left, or right respectively. |
| + int up_index() const; |
| + int down_index() const; |
| + int left_index() const; |
| + int right_index() const; |
| + |
| + // These return which index needs to be selected on tab / shift-tab. |
| + int next_index() const; |
| + int previous_index() const; |
| + |
| // Inserts a new Tile object containing |contents| at |index|. Does no |
| // relayout. |
| void InsertTileAt(int index, TabContents* contents); |
| @@ -476,8 +488,29 @@ class TileSet { |
| void MoveTileFromTo(int from_index, int to_index); |
| private: |
| + int count_x() const { |
| + return ceilf(tiles_.size() / static_cast<float>(count_y_)); |
| + } |
| + int count_y() const { |
| + return count_y_; |
| + } |
| + int last_row_count_x() const { |
| + return tiles_.size() - count_x() * (count_y() - 1); |
| + } |
| + int tiles_in_row(int row) const { |
| + return row != count_y() - 1 ? count_x() : last_row_count_x(); |
| + } |
| + void index_to_tile_xy(int index, int* tile_x, int* tile_y) const { |
| + *tile_x = index % count_x(); |
| + *tile_y = index / count_x(); |
| + } |
| + int tile_xy_to_index(int tile_x, int tile_y) const { |
| + return tile_y * count_x() + tile_x; |
| + } |
| + |
| ScopedVector<Tile> tiles_; |
| int selected_index_; |
| + int count_y_; |
| DISALLOW_COPY_AND_ASSIGN(TileSet); |
| }; |
| @@ -542,19 +575,17 @@ void TileSet::Layout(NSRect containing_rect) { |
| 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); |
| + count_y_ = roundf(fny); |
| - // Now that |count_x| and |count_y| are known, it's straightforward to compute |
| - // thumbnail width/height. See comment in |
| + // Now that |count_x()| and |count_y_| are known, it's straightforward to |
| + // compute thumbnail width/height. See comment in |
| // |FitNRectsWithAspectIntoBoundingSizeWithConstantPadding| for the derivation |
| // of these two formulas. |
| int small_width = |
| - floor((container_width + kSmallPaddingX) / float(count_x) - |
| + floor((container_width + kSmallPaddingX) / static_cast<float>(count_x()) - |
| kSmallPaddingX); |
| int small_height = |
| - floor((container_height + kSmallPaddingY) / float(count_y) - |
| + floor((container_height + kSmallPaddingY) / static_cast<float>(count_y_) - |
| (kSmallPaddingY + kFooterExtraHeight)); |
| // |small_width / small_height| has only roughly an aspect ratio of |aspect|. |
| @@ -562,21 +593,22 @@ void TileSet::Layout(NSRect containing_rect) { |
| // the extra space won by shrinking to the outer padding. |
| int smallExtraPaddingLeft = 0; |
| int smallExtraPaddingTop = 0; |
| - if (aspect > small_width/float(small_height)) { |
| + if (aspect > small_width/static_cast<float>(small_height)) { |
| small_height = small_width / aspect; |
| CGFloat all_tiles_height = |
| - (small_height + kSmallPaddingY + kFooterExtraHeight) * count_y - |
| + (small_height + kSmallPaddingY + kFooterExtraHeight) * count_y() - |
| (kSmallPaddingY + kFooterExtraHeight); |
| smallExtraPaddingTop = (container_height - all_tiles_height)/2; |
| } else { |
| small_width = small_height * aspect; |
| CGFloat all_tiles_width = |
| - (small_width + kSmallPaddingX) * count_x - kSmallPaddingX; |
| + (small_width + kSmallPaddingX) * count_x() - kSmallPaddingX; |
| smallExtraPaddingLeft = (container_width - all_tiles_width)/2; |
| } |
| // Compute inter-tile padding in the zoomed-out view. |
| - CGFloat scale_small_to_big = NSWidth(containing_rect) / float(small_width); |
| + CGFloat scale_small_to_big = |
| + NSWidth(containing_rect) / static_cast<float>(small_width); |
| CGFloat big_padding_x = kSmallPaddingX * scale_small_to_big; |
| CGFloat big_padding_y = |
| (kSmallPaddingY + kFooterExtraHeight) * scale_small_to_big; |
| @@ -586,7 +618,7 @@ void TileSet::Layout(NSRect containing_rect) { |
| // X X X X |
| // X X |
| for (int row = 0, i = 0; i < tile_count; ++row) { |
| - for (int col = 0; col < count_x && i < tile_count; ++col, ++i) { |
| + 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); |
| @@ -629,12 +661,12 @@ void TileSet::Layout(NSRect containing_rect) { |
| // X X X X |
| // X X X X |
| // X X |
| - int last_row_empty_tiles_x = count_x - last_row_count_x; |
| + int last_row_empty_tiles_x = count_x() - last_row_count_x(); |
| CGFloat small_last_row_shift_x = |
| last_row_empty_tiles_x * (small_width + kSmallPaddingX) / 2; |
| 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) { |
| + 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]->favicon_rect_.origin.x += small_last_row_shift_x; |
| @@ -648,6 +680,84 @@ void TileSet::set_selected_index(int index) { |
| selected_index_ = index; |
| } |
| +// Given a |value| in [0, from_scale), map it into [0, to_scale) such that: |
| +// * [0, from_scale) ends up in the middle of [0, to_scale) if the latter is |
| +// a bigger range |
| +// * The middle of [0, from_scale) is mapped to [0, to_scale), and the parts |
| +// of the former that don't fit are mapped to 0 and to_scale - respectively |
| +// if the former is a bigger range. |
| +static int rescale(int value, int from_scale, int to_scale) { |
| + int left = (to_scale - from_scale) / 2; |
| + int result = value + left; |
| + if (result < 0) |
| + return 0; |
| + if (result >= to_scale) |
| + return to_scale - 1; |
| + return result; |
| +} |
| + |
| +int TileSet::up_index() const { |
| + int tile_x, tile_y; |
| + index_to_tile_xy(selected_index(), &tile_x, &tile_y); |
| + tile_y -= 1; |
| + if (tile_y == count_y() - 2) { |
| + // Transition from last row to second-to-last row. |
| + tile_x = rescale(tile_x, last_row_count_x(), count_x()); |
| + } else if (tile_y < 0) { |
| + // Transition from first row to last row. |
| + tile_x = rescale(tile_x, count_x(), last_row_count_x()); |
| + tile_y = count_y() - 1; |
| + } |
| + return tile_xy_to_index(tile_x, tile_y); |
| +} |
| + |
| +int TileSet::down_index() const { |
| + int tile_x, tile_y; |
| + index_to_tile_xy(selected_index(), &tile_x, &tile_y); |
| + tile_y += 1; |
| + if (tile_y == count_y() - 1) { |
| + // Transition from second-to-last row to last row. |
| + tile_x = rescale(tile_x, count_x(), last_row_count_x()); |
| + } else if (tile_y >= count_y()) { |
| + // Transition from last row to first row. |
| + tile_x = rescale(tile_x, last_row_count_x(), count_x()); |
| + tile_y = 0; |
| + } |
| + return tile_xy_to_index(tile_x, tile_y); |
| +} |
| + |
| +int TileSet::left_index() const { |
| + int tile_x, tile_y; |
| + index_to_tile_xy(selected_index(), &tile_x, &tile_y); |
| + tile_x -= 1; |
| + if (tile_x < 0) |
| + tile_x = tiles_in_row(tile_y) - 1; |
| + return tile_xy_to_index(tile_x, tile_y); |
| +} |
| + |
| +int TileSet::right_index() const { |
| + int tile_x, tile_y; |
| + index_to_tile_xy(selected_index(), &tile_x, &tile_y); |
| + tile_x += 1; |
| + if (tile_x >= tiles_in_row(tile_y)) |
| + tile_x = 0; |
| + return tile_xy_to_index(tile_x, tile_y); |
| +} |
| + |
| +int TileSet::next_index() const { |
| + int new_index = selected_index() + 1; |
| + if (new_index >= static_cast<int>(tiles_.size())) |
|
viettrungluu
2010/09/16 20:07:21
I think this would be a fine place to use ?:.
Nico
2010/09/16 20:08:54
but then i need to write "+1" twice!
|
| + new_index = 0; |
| + return new_index; |
| +} |
| + |
| +int TileSet::previous_index() const { |
| + int new_index = selected_index() - 1; |
| + if (new_index < 0) |
|
viettrungluu
2010/09/16 20:07:21
Ditto.
|
| + new_index = tiles_.size() - 1; |
| + return new_index; |
| +} |
| + |
| void TileSet::InsertTileAt(int index, TabContents* contents) { |
| tiles_.insert(tiles_.begin() + index, new Tile); |
| tiles_[index]->contents_ = contents; |
| @@ -935,14 +1045,44 @@ void AnimateCALayerFrameFromTo( |
| return YES; |
| } |
| +// Handle key events that should be executed repeatedly while the key is down. |
| - (void)keyDown:(NSEvent*)event { |
| - // Overridden to prevent beeps. |
| + if (state_ == tabpose::kFadingOut) |
| + return; |
| + NSString* characters = [event characters]; |
| + if ([characters length] < 1) |
| + return; |
| + |
| + unichar character = [characters characterAtIndex:0]; |
| + int newIndex = -1; |
| + switch (character) { |
| + case NSUpArrowFunctionKey: |
| + newIndex = tileSet_->up_index(); |
| + break; |
| + case NSDownArrowFunctionKey: |
| + newIndex = tileSet_->down_index(); |
| + break; |
| + case NSLeftArrowFunctionKey: |
| + newIndex = tileSet_->left_index(); |
| + break; |
| + case NSRightArrowFunctionKey: |
| + newIndex = tileSet_->right_index(); |
| + break; |
| + case NSTabCharacter: |
| + newIndex = tileSet_->next_index(); |
| + break; |
| + case NSBackTabCharacter: |
| + newIndex = tileSet_->previous_index(); |
| + break; |
| + } |
| + if (newIndex != -1) |
| + [self selectTileAtIndexWithoutAnimation:newIndex]; |
| } |
| +// Handle keyboard events that should be executed once when the key is released. |
| - (void)keyUp:(NSEvent*)event { |
| if (state_ == tabpose::kFadingOut) |
| return; |
| - |
| NSString* characters = [event characters]; |
| if ([characters length] < 1) |
| return; |
| @@ -959,10 +1099,31 @@ void AnimateCALayerFrameFromTo( |
| tileSet_->set_selected_index(tabStripModel_->selected_index()); |
| [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
| break; |
| - // TODO(thakis): Support moving the selection via arrow keys. |
| } |
| } |
| +// Handle keyboard events that contain cmd or ctrl. |
| +- (BOOL)performKeyEquivalent:(NSEvent*)event { |
| + if (state_ == tabpose::kFadingOut) |
| + return NO; |
| + NSString* characters = [event characters]; |
| + if ([characters length] < 1) |
| + return NO; |
| + unichar character = [characters characterAtIndex:0]; |
| + if ([event modifierFlags] & NSCommandKeyMask) { |
| + if (character >= '1' && character <= '9') { |
| + int index = |
| + character == '9' ? tabStripModel_->count() - 1 : character - '1'; |
| + if (index < tabStripModel_->count()) { |
| + tileSet_->set_selected_index(index); |
| + [self fadeAway:([event modifierFlags] & NSShiftKeyMask) != 0]; |
| + return YES; |
| + } |
| + } |
| + } |
| + return NO; |
| +} |
| + |
| - (void)mouseMoved:(NSEvent*)event { |
| int newIndex = -1; |
| CGPoint p = NSPointToCGPoint([event locationInWindow]); |
| @@ -997,12 +1158,15 @@ void AnimateCALayerFrameFromTo( |
| } |
| - (void)commandDispatch:(id)sender { |
| - // Without this, -validateUserInterfaceItem: is not called. |
| + if ([sender tag] == IDC_TABPOSE) |
| + [self fadeAway:NO]; |
| } |
| - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
| - // Disable all browser-related menu items. |
| - return NO; |
| + // Disable all browser-related menu items except the tab overview toggle. |
| + SEL action = [item action]; |
| + NSInteger tag = [item tag]; |
| + return action == @selector(commandDispatch:) && tag == IDC_TABPOSE; |
| } |
| - (void)fadeAway:(BOOL)slomo { |