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; |
| } |
| } |