Chromium Code Reviews| 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 #include "views/controls/menu/menu_controller.h" | 5 #include "views/controls/menu/menu_controller.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "app/os_exchange_data.h" | 8 #include "app/os_exchange_data.h" |
| 9 #include "base/i18n/rtl.h" | 9 #include "base/i18n/rtl.h" |
| 10 #include "base/keyboard_codes.h" | 10 #include "base/keyboard_codes.h" |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 44 // Amount to inset submenus. | 44 // Amount to inset submenus. |
| 45 static const int kSubmenuHorizontalInset = 3; | 45 static const int kSubmenuHorizontalInset = 3; |
| 46 | 46 |
| 47 namespace views { | 47 namespace views { |
| 48 | 48 |
| 49 // Convenience for scrolling the view such that the origin is visible. | 49 // Convenience for scrolling the view such that the origin is visible. |
| 50 static void ScrollToVisible(View* view) { | 50 static void ScrollToVisible(View* view) { |
| 51 view->ScrollRectToVisible(gfx::Rect(gfx::Point(), view->size())); | 51 view->ScrollRectToVisible(gfx::Rect(gfx::Point(), view->size())); |
| 52 } | 52 } |
| 53 | 53 |
| 54 // Returns the first descendant of |view| that is hot tracked. | |
| 55 static View* GetFirstHotTrackedView(View* view) { | |
| 56 if (!view) | |
| 57 return NULL; | |
| 58 | |
| 59 if (view->IsHotTracked()) | |
| 60 return view; | |
| 61 | |
| 62 for (int i = 0; i < view->GetChildViewCount(); ++i) { | |
| 63 View* hot_view = GetFirstHotTrackedView(view->GetChildViewAt(i)); | |
| 64 if (hot_view) | |
| 65 return hot_view; | |
| 66 } | |
| 67 return NULL; | |
| 68 } | |
| 69 | |
| 70 // Recurses through the child views of |view| returning the first view starting | |
| 71 // at |start| that is focusable. A value of -1 for |start| indicates to start at | |
| 72 // the first view (if |forward| is false, iterating starts at the last view). If | |
| 73 // |forward| is true the children are considered first to last, otherwise last | |
| 74 // to first. | |
| 75 static View* GetFirstFocusableView(View* view, int start, bool forward) { | |
| 76 if (forward) { | |
| 77 for (int i = start == -1 ? 0 : start; i < view->GetChildViewCount(); ++i) { | |
| 78 View* deepest = GetFirstFocusableView(view->GetChildViewAt(i), -1, | |
| 79 forward); | |
|
Jay Civelli
2010/06/09 16:03:47
Nit: you could use true instead of forward.
| |
| 80 if (deepest) | |
| 81 return deepest; | |
| 82 } | |
| 83 } else { | |
| 84 for (int i = start == -1 ? view->GetChildViewCount() - 1 : start; | |
| 85 i >= 0; --i) { | |
| 86 View* deepest = GetFirstFocusableView(view->GetChildViewAt(i), -1, | |
| 87 forward); | |
|
Jay Civelli
2010/06/09 16:03:47
Nit: you could use false instead of forward.
| |
| 88 if (deepest) | |
| 89 return deepest; | |
| 90 } | |
| 91 } | |
| 92 return view->IsFocusable() ? view : NULL; | |
| 93 } | |
| 94 | |
| 95 // Returns the first child of |start| that is focusable. | |
| 96 static View* GetInitialFocusableView(View* start, bool forward) { | |
| 97 return GetFirstFocusableView(start, -1, forward); | |
| 98 } | |
| 99 | |
| 100 // Returns the next view after |start_at| that is focusable. Returns NULL if | |
| 101 // there are no focusable children of |ancestor| after |start_at|. | |
| 102 static View* GetNextFocusableView(View* ancestor, | |
| 103 View* start_at, | |
| 104 bool forward) { | |
| 105 DCHECK(ancestor->IsParentOf(start_at)); | |
| 106 View* parent = start_at; | |
| 107 do { | |
| 108 View* new_parent = parent->GetParent(); | |
| 109 int index = new_parent->GetChildIndex(parent); | |
| 110 index += forward ? 1 : -1; | |
| 111 if (forward || index != -1) { | |
| 112 View* next = GetFirstFocusableView(new_parent, index, forward); | |
| 113 if (next) | |
| 114 return next; | |
| 115 } | |
| 116 parent = new_parent; | |
| 117 } while (parent != ancestor); | |
| 118 return NULL; | |
| 119 } | |
| 120 | |
| 54 // MenuScrollTask -------------------------------------------------------------- | 121 // MenuScrollTask -------------------------------------------------------------- |
| 55 | 122 |
| 56 // MenuScrollTask is used when the SubmenuView does not all fit on screen and | 123 // MenuScrollTask is used when the SubmenuView does not all fit on screen and |
| 57 // the mouse is over the scroll up/down buttons. MenuScrollTask schedules | 124 // the mouse is over the scroll up/down buttons. MenuScrollTask schedules |
| 58 // itself with a RepeatingTimer. When Run is invoked MenuScrollTask scrolls | 125 // itself with a RepeatingTimer. When Run is invoked MenuScrollTask scrolls |
| 59 // appropriately. | 126 // appropriately. |
| 60 | 127 |
| 61 class MenuController::MenuScrollTask { | 128 class MenuController::MenuScrollTask { |
| 62 public: | 129 public: |
| 63 MenuScrollTask() : submenu_(NULL) { | 130 MenuScrollTask() : submenu_(NULL) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 bool update_immediately) { | 335 bool update_immediately) { |
| 269 size_t paths_differ_at = 0; | 336 size_t paths_differ_at = 0; |
| 270 std::vector<MenuItemView*> current_path; | 337 std::vector<MenuItemView*> current_path; |
| 271 std::vector<MenuItemView*> new_path; | 338 std::vector<MenuItemView*> new_path; |
| 272 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, ¤t_path, | 339 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, ¤t_path, |
| 273 &new_path, &paths_differ_at); | 340 &new_path, &paths_differ_at); |
| 274 | 341 |
| 275 size_t current_size = current_path.size(); | 342 size_t current_size = current_path.size(); |
| 276 size_t new_size = new_path.size(); | 343 size_t new_size = new_path.size(); |
| 277 | 344 |
| 345 if (pending_state_.item != menu_item && pending_state_.item) { | |
| 346 View* current_hot_view = GetFirstHotTrackedView(pending_state_.item); | |
| 347 if (current_hot_view) | |
| 348 current_hot_view->SetHotTracked(false); | |
| 349 } | |
| 350 | |
| 278 // Notify the old path it isn't selected. | 351 // Notify the old path it isn't selected. |
| 279 for (size_t i = paths_differ_at; i < current_size; ++i) | 352 for (size_t i = paths_differ_at; i < current_size; ++i) |
| 280 current_path[i]->SetSelected(false); | 353 current_path[i]->SetSelected(false); |
| 281 | 354 |
| 282 // Notify the new path it is selected. | 355 // Notify the new path it is selected. |
| 283 for (size_t i = paths_differ_at; i < new_size; ++i) | 356 for (size_t i = paths_differ_at; i < new_size; ++i) |
| 284 new_path[i]->SetSelected(true); | 357 new_path[i]->SetSelected(true); |
| 285 | 358 |
| 286 if (menu_item && menu_item->GetDelegate()) | 359 if (menu_item && menu_item->GetDelegate()) |
| 287 menu_item->GetDelegate()->SelectionChanged(menu_item); | 360 menu_item->GetDelegate()->SelectionChanged(menu_item); |
| (...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 758 OpenSubmenuChangeSelectionIfCan(); | 831 OpenSubmenuChangeSelectionIfCan(); |
| 759 break; | 832 break; |
| 760 | 833 |
| 761 case base::VKEY_LEFT: | 834 case base::VKEY_LEFT: |
| 762 if (base::i18n::IsRTL()) | 835 if (base::i18n::IsRTL()) |
| 763 OpenSubmenuChangeSelectionIfCan(); | 836 OpenSubmenuChangeSelectionIfCan(); |
| 764 else | 837 else |
| 765 CloseSubmenu(); | 838 CloseSubmenu(); |
| 766 break; | 839 break; |
| 767 | 840 |
| 841 case base::VKEY_SPACE: | |
| 842 SendAcceleratorToHotTrackedView(); | |
| 843 break; | |
| 844 | |
| 768 case base::VKEY_RETURN: | 845 case base::VKEY_RETURN: |
| 769 if (pending_state_.item) { | 846 if (pending_state_.item) { |
| 770 if (pending_state_.item->HasSubmenu()) { | 847 if (pending_state_.item->HasSubmenu()) { |
| 771 OpenSubmenuChangeSelectionIfCan(); | 848 OpenSubmenuChangeSelectionIfCan(); |
| 772 } else if (pending_state_.item->IsEnabled()) { | 849 } else if (!SendAcceleratorToHotTrackedView() && |
| 850 pending_state_.item->IsEnabled()) { | |
| 773 Accept(pending_state_.item, 0); | 851 Accept(pending_state_.item, 0); |
| 774 return false; | 852 return false; |
| 775 } | 853 } |
| 776 } | 854 } |
| 777 break; | 855 break; |
| 778 | 856 |
| 779 case base::VKEY_ESCAPE: | 857 case base::VKEY_ESCAPE: |
| 780 if (!state_.item->GetParentMenuItem() || | 858 if (!state_.item->GetParentMenuItem() || |
| 781 (!state_.item->GetParentMenuItem()->GetParentMenuItem() && | 859 (!state_.item->GetParentMenuItem()->GetParentMenuItem() && |
| 782 (!state_.item->HasSubmenu() || | 860 (!state_.item->HasSubmenu() || |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 826 MenuController::~MenuController() { | 904 MenuController::~MenuController() { |
| 827 DCHECK(!showing_); | 905 DCHECK(!showing_); |
| 828 StopShowTimer(); | 906 StopShowTimer(); |
| 829 StopCancelAllTimer(); | 907 StopCancelAllTimer(); |
| 830 #ifdef DEBUG_MENU | 908 #ifdef DEBUG_MENU |
| 831 instance_count--; | 909 instance_count--; |
| 832 DLOG(INFO) << "destroyed MC, count=" << instance_count; | 910 DLOG(INFO) << "destroyed MC, count=" << instance_count; |
| 833 #endif | 911 #endif |
| 834 } | 912 } |
| 835 | 913 |
| 914 bool MenuController::SendAcceleratorToHotTrackedView() { | |
| 915 View* hot_view = GetFirstHotTrackedView(pending_state_.item); | |
| 916 if (!hot_view) | |
| 917 return false; | |
| 918 | |
| 919 Accelerator accelerator(base::VKEY_RETURN, false, false, false); | |
| 920 hot_view->AcceleratorPressed(accelerator); | |
| 921 hot_view->SetHotTracked(true); | |
| 922 return true; | |
| 923 } | |
| 924 | |
| 836 void MenuController::UpdateInitialLocation( | 925 void MenuController::UpdateInitialLocation( |
| 837 const gfx::Rect& bounds, | 926 const gfx::Rect& bounds, |
| 838 MenuItemView::AnchorPosition position) { | 927 MenuItemView::AnchorPosition position) { |
| 839 pending_state_.initial_bounds = bounds; | 928 pending_state_.initial_bounds = bounds; |
| 840 if (bounds.height() > 1) { | 929 if (bounds.height() > 1) { |
| 841 // Inset the bounds slightly, otherwise drag coordinates don't line up | 930 // Inset the bounds slightly, otherwise drag coordinates don't line up |
| 842 // nicely and menus close prematurely. | 931 // nicely and menus close prematurely. |
| 843 pending_state_.initial_bounds.Inset(0, 1); | 932 pending_state_.initial_bounds.Inset(0, 1); |
| 844 } | 933 } |
| 845 pending_state_.anchor = position; | 934 pending_state_.anchor = position; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 928 CloseMenu(item); | 1017 CloseMenu(item); |
| 929 last_item = item; | 1018 last_item = item; |
| 930 item = item->GetParentMenuItem(); | 1019 item = item->GetParentMenuItem(); |
| 931 } | 1020 } |
| 932 i->submenu_open = false; | 1021 i->submenu_open = false; |
| 933 i->item = last_item; | 1022 i->item = last_item; |
| 934 } | 1023 } |
| 935 } | 1024 } |
| 936 | 1025 |
| 937 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) { | 1026 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) { |
| 1027 // Walk the view hierarchy until we find a menu item (or the root). | |
| 938 View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y)); | 1028 View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y)); |
| 1029 while (child_under_mouse && | |
| 1030 child_under_mouse->GetID() != MenuItemView::kMenuItemViewID) { | |
| 1031 child_under_mouse = child_under_mouse->GetParent(); | |
| 1032 } | |
| 939 if (child_under_mouse && child_under_mouse->IsEnabled() && | 1033 if (child_under_mouse && child_under_mouse->IsEnabled() && |
| 940 child_under_mouse->GetID() == MenuItemView::kMenuItemViewID) { | 1034 child_under_mouse->GetID() == MenuItemView::kMenuItemViewID) { |
| 941 return static_cast<MenuItemView*>(child_under_mouse); | 1035 return static_cast<MenuItemView*>(child_under_mouse); |
| 942 } | 1036 } |
| 943 return NULL; | 1037 return NULL; |
| 944 } | 1038 } |
| 945 | 1039 |
| 946 MenuItemView* MenuController::GetEmptyMenuItemAt(View* source, int x, int y) { | 1040 MenuItemView* MenuController::GetEmptyMenuItemAt(View* source, int x, int y) { |
| 947 View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y)); | 1041 View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y)); |
| 948 if (child_under_mouse && | 1042 if (child_under_mouse && |
| (...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1317 void MenuController::IncrementSelection(int delta) { | 1411 void MenuController::IncrementSelection(int delta) { |
| 1318 MenuItemView* item = pending_state_.item; | 1412 MenuItemView* item = pending_state_.item; |
| 1319 DCHECK(item); | 1413 DCHECK(item); |
| 1320 if (pending_state_.submenu_open && item->HasSubmenu() && | 1414 if (pending_state_.submenu_open && item->HasSubmenu() && |
| 1321 item->GetSubmenu()->IsShowing()) { | 1415 item->GetSubmenu()->IsShowing()) { |
| 1322 // A menu is selected and open, but none of its children are selected, | 1416 // A menu is selected and open, but none of its children are selected, |
| 1323 // select the first menu item. | 1417 // select the first menu item. |
| 1324 if (item->GetSubmenu()->GetMenuItemCount()) { | 1418 if (item->GetSubmenu()->GetMenuItemCount()) { |
| 1325 SetSelection(item->GetSubmenu()->GetMenuItemAt(0), false, false); | 1419 SetSelection(item->GetSubmenu()->GetMenuItemAt(0), false, false); |
| 1326 ScrollToVisible(item->GetSubmenu()->GetMenuItemAt(0)); | 1420 ScrollToVisible(item->GetSubmenu()->GetMenuItemAt(0)); |
| 1327 return; // return so else case can fall through. | 1421 return; |
| 1328 } | 1422 } |
| 1329 } | 1423 } |
| 1424 | |
| 1425 if (item->GetChildViewCount()) { | |
| 1426 View* hot_view = GetFirstHotTrackedView(item); | |
| 1427 if (hot_view) { | |
| 1428 hot_view->SetHotTracked(false); | |
| 1429 View* to_make_hot = GetNextFocusableView(item, hot_view, delta == 1); | |
| 1430 if (to_make_hot) { | |
| 1431 to_make_hot->SetHotTracked(true); | |
| 1432 return; | |
| 1433 } | |
| 1434 } else { | |
| 1435 View* to_make_hot = GetInitialFocusableView(item, delta == 1); | |
| 1436 if (to_make_hot) { | |
| 1437 to_make_hot->SetHotTracked(true); | |
| 1438 return; | |
| 1439 } | |
| 1440 } | |
| 1441 } | |
| 1442 | |
| 1330 if (item->GetParentMenuItem()) { | 1443 if (item->GetParentMenuItem()) { |
| 1331 MenuItemView* parent = item->GetParentMenuItem(); | 1444 MenuItemView* parent = item->GetParentMenuItem(); |
| 1332 int parent_count = parent->GetSubmenu()->GetMenuItemCount(); | 1445 int parent_count = parent->GetSubmenu()->GetMenuItemCount(); |
| 1333 if (parent_count > 1) { | 1446 if (parent_count > 1) { |
| 1334 for (int i = 0; i < parent_count; ++i) { | 1447 for (int i = 0; i < parent_count; ++i) { |
| 1335 if (parent->GetSubmenu()->GetMenuItemAt(i) == item) { | 1448 if (parent->GetSubmenu()->GetMenuItemAt(i) == item) { |
| 1336 int next_index = (i + delta + parent_count) % parent_count; | 1449 int next_index = (i + delta + parent_count) % parent_count; |
| 1337 ScrollToVisible(parent->GetSubmenu()->GetMenuItemAt(next_index)); | 1450 ScrollToVisible(parent->GetSubmenu()->GetMenuItemAt(next_index)); |
| 1338 SetSelection(parent->GetSubmenu()->GetMenuItemAt(next_index), false, | 1451 MenuItemView* to_select = |
| 1339 false); | 1452 parent->GetSubmenu()->GetMenuItemAt(next_index); |
| 1453 SetSelection(to_select, false, false); | |
| 1454 View* to_make_hot = GetInitialFocusableView(to_select, delta == 1); | |
| 1455 if (to_make_hot) | |
| 1456 to_make_hot->SetHotTracked(true); | |
| 1340 break; | 1457 break; |
| 1341 } | 1458 } |
| 1342 } | 1459 } |
| 1343 } | 1460 } |
| 1344 } | 1461 } |
| 1345 } | 1462 } |
| 1346 | 1463 |
| 1347 void MenuController::OpenSubmenuChangeSelectionIfCan() { | 1464 void MenuController::OpenSubmenuChangeSelectionIfCan() { |
| 1348 MenuItemView* item = pending_state_.item; | 1465 MenuItemView* item = pending_state_.item; |
| 1349 if (item->HasSubmenu()) { | 1466 if (item->HasSubmenu()) { |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1518 if (!scroll_task_.get()) | 1635 if (!scroll_task_.get()) |
| 1519 scroll_task_.reset(new MenuScrollTask()); | 1636 scroll_task_.reset(new MenuScrollTask()); |
| 1520 scroll_task_->Update(part); | 1637 scroll_task_->Update(part); |
| 1521 } | 1638 } |
| 1522 | 1639 |
| 1523 void MenuController::StopScrolling() { | 1640 void MenuController::StopScrolling() { |
| 1524 scroll_task_.reset(NULL); | 1641 scroll_task_.reset(NULL); |
| 1525 } | 1642 } |
| 1526 | 1643 |
| 1527 } // namespace views | 1644 } // namespace views |
| OLD | NEW |