Chromium Code Reviews| Index: views/controls/menu/menu_controller.cc | 
| diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc | 
| index 9005fd6117afbe7705d3468340966c55406fee7e..723f8bb8eb320d3fa07bc79aa42ba661a4e1a694 100644 | 
| --- a/views/controls/menu/menu_controller.cc | 
| +++ b/views/controls/menu/menu_controller.cc | 
| @@ -51,6 +51,73 @@ static void ScrollToVisible(View* view) { | 
| view->ScrollRectToVisible(gfx::Rect(gfx::Point(), view->size())); | 
| } | 
| +// Returns the first descendant of |view| that is hot tracked. | 
| +static View* GetFirstHotTrackedView(View* view) { | 
| + if (!view) | 
| + return NULL; | 
| + | 
| + if (view->IsHotTracked()) | 
| + return view; | 
| + | 
| + for (int i = 0; i < view->GetChildViewCount(); ++i) { | 
| + View* hot_view = GetFirstHotTrackedView(view->GetChildViewAt(i)); | 
| + if (hot_view) | 
| + return hot_view; | 
| + } | 
| + return NULL; | 
| +} | 
| + | 
| +// Recurses through the child views of |view| returning the first view starting | 
| +// at |start| that is focusable. A value of -1 for |start| indicates to start at | 
| +// the first view (if |forward| is false, iterating starts at the last view). If | 
| +// |forward| is true the children are considered first to last, otherwise last | 
| +// to first. | 
| +static View* GetFirstFocusableView(View* view, int start, bool forward) { | 
| + if (forward) { | 
| + for (int i = start == -1 ? 0 : start; i < view->GetChildViewCount(); ++i) { | 
| + View* deepest = GetFirstFocusableView(view->GetChildViewAt(i), -1, | 
| + forward); | 
| 
 
Jay Civelli
2010/06/09 16:03:47
Nit: you could use true instead of forward.
 
 | 
| + if (deepest) | 
| + return deepest; | 
| + } | 
| + } else { | 
| + for (int i = start == -1 ? view->GetChildViewCount() - 1 : start; | 
| + i >= 0; --i) { | 
| + View* deepest = GetFirstFocusableView(view->GetChildViewAt(i), -1, | 
| + forward); | 
| 
 
Jay Civelli
2010/06/09 16:03:47
Nit: you could use false instead of forward.
 
 | 
| + if (deepest) | 
| + return deepest; | 
| + } | 
| + } | 
| + return view->IsFocusable() ? view : NULL; | 
| +} | 
| + | 
| +// Returns the first child of |start| that is focusable. | 
| +static View* GetInitialFocusableView(View* start, bool forward) { | 
| + return GetFirstFocusableView(start, -1, forward); | 
| +} | 
| + | 
| +// Returns the next view after |start_at| that is focusable. Returns NULL if | 
| +// there are no focusable children of |ancestor| after |start_at|. | 
| +static View* GetNextFocusableView(View* ancestor, | 
| + View* start_at, | 
| + bool forward) { | 
| + DCHECK(ancestor->IsParentOf(start_at)); | 
| + View* parent = start_at; | 
| + do { | 
| + View* new_parent = parent->GetParent(); | 
| + int index = new_parent->GetChildIndex(parent); | 
| + index += forward ? 1 : -1; | 
| + if (forward || index != -1) { | 
| + View* next = GetFirstFocusableView(new_parent, index, forward); | 
| + if (next) | 
| + return next; | 
| + } | 
| + parent = new_parent; | 
| + } while (parent != ancestor); | 
| + return NULL; | 
| +} | 
| + | 
| // MenuScrollTask -------------------------------------------------------------- | 
| // MenuScrollTask is used when the SubmenuView does not all fit on screen and | 
| @@ -275,6 +342,12 @@ void MenuController::SetSelection(MenuItemView* menu_item, | 
| size_t current_size = current_path.size(); | 
| size_t new_size = new_path.size(); | 
| + if (pending_state_.item != menu_item && pending_state_.item) { | 
| + View* current_hot_view = GetFirstHotTrackedView(pending_state_.item); | 
| + if (current_hot_view) | 
| + current_hot_view->SetHotTracked(false); | 
| + } | 
| + | 
| // Notify the old path it isn't selected. | 
| for (size_t i = paths_differ_at; i < current_size; ++i) | 
| current_path[i]->SetSelected(false); | 
| @@ -765,11 +838,16 @@ bool MenuController::OnKeyDown(int key_code | 
| CloseSubmenu(); | 
| break; | 
| + case base::VKEY_SPACE: | 
| + SendAcceleratorToHotTrackedView(); | 
| + break; | 
| + | 
| case base::VKEY_RETURN: | 
| if (pending_state_.item) { | 
| if (pending_state_.item->HasSubmenu()) { | 
| OpenSubmenuChangeSelectionIfCan(); | 
| - } else if (pending_state_.item->IsEnabled()) { | 
| + } else if (!SendAcceleratorToHotTrackedView() && | 
| + pending_state_.item->IsEnabled()) { | 
| Accept(pending_state_.item, 0); | 
| return false; | 
| } | 
| @@ -833,6 +911,17 @@ MenuController::~MenuController() { | 
| #endif | 
| } | 
| +bool MenuController::SendAcceleratorToHotTrackedView() { | 
| + View* hot_view = GetFirstHotTrackedView(pending_state_.item); | 
| + if (!hot_view) | 
| + return false; | 
| + | 
| + Accelerator accelerator(base::VKEY_RETURN, false, false, false); | 
| + hot_view->AcceleratorPressed(accelerator); | 
| + hot_view->SetHotTracked(true); | 
| + return true; | 
| +} | 
| + | 
| void MenuController::UpdateInitialLocation( | 
| const gfx::Rect& bounds, | 
| MenuItemView::AnchorPosition position) { | 
| @@ -935,7 +1024,12 @@ void MenuController::CloseAllNestedMenus() { | 
| } | 
| MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) { | 
| + // Walk the view hierarchy until we find a menu item (or the root). | 
| View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y)); | 
| + while (child_under_mouse && | 
| + child_under_mouse->GetID() != MenuItemView::kMenuItemViewID) { | 
| + child_under_mouse = child_under_mouse->GetParent(); | 
| + } | 
| if (child_under_mouse && child_under_mouse->IsEnabled() && | 
| child_under_mouse->GetID() == MenuItemView::kMenuItemViewID) { | 
| return static_cast<MenuItemView*>(child_under_mouse); | 
| @@ -1324,9 +1418,28 @@ void MenuController::IncrementSelection(int delta) { | 
| if (item->GetSubmenu()->GetMenuItemCount()) { | 
| SetSelection(item->GetSubmenu()->GetMenuItemAt(0), false, false); | 
| ScrollToVisible(item->GetSubmenu()->GetMenuItemAt(0)); | 
| - return; // return so else case can fall through. | 
| + return; | 
| } | 
| } | 
| + | 
| + if (item->GetChildViewCount()) { | 
| + View* hot_view = GetFirstHotTrackedView(item); | 
| + if (hot_view) { | 
| + hot_view->SetHotTracked(false); | 
| + View* to_make_hot = GetNextFocusableView(item, hot_view, delta == 1); | 
| + if (to_make_hot) { | 
| + to_make_hot->SetHotTracked(true); | 
| + return; | 
| + } | 
| + } else { | 
| + View* to_make_hot = GetInitialFocusableView(item, delta == 1); | 
| + if (to_make_hot) { | 
| + to_make_hot->SetHotTracked(true); | 
| + return; | 
| + } | 
| + } | 
| + } | 
| + | 
| if (item->GetParentMenuItem()) { | 
| MenuItemView* parent = item->GetParentMenuItem(); | 
| int parent_count = parent->GetSubmenu()->GetMenuItemCount(); | 
| @@ -1335,8 +1448,12 @@ void MenuController::IncrementSelection(int delta) { | 
| if (parent->GetSubmenu()->GetMenuItemAt(i) == item) { | 
| int next_index = (i + delta + parent_count) % parent_count; | 
| ScrollToVisible(parent->GetSubmenu()->GetMenuItemAt(next_index)); | 
| - SetSelection(parent->GetSubmenu()->GetMenuItemAt(next_index), false, | 
| - false); | 
| + MenuItemView* to_select = | 
| + parent->GetSubmenu()->GetMenuItemAt(next_index); | 
| + SetSelection(to_select, false, false); | 
| + View* to_make_hot = GetInitialFocusableView(to_select, delta == 1); | 
| + if (to_make_hot) | 
| + to_make_hot->SetHotTracked(true); | 
| break; | 
| } | 
| } |