OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ui/views/controls/menu/menu_controller.h" | 5 #include "ui/views/controls/menu/menu_controller.h" |
6 | 6 |
7 #include "base/i18n/case_conversion.h" | 7 #include "base/i18n/case_conversion.h" |
8 #include "base/i18n/rtl.h" | 8 #include "base/i18n/rtl.h" |
9 #include "base/run_loop.h" | 9 #include "base/run_loop.h" |
10 #include "base/time.h" | 10 #include "base/time.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
43 // Period of the scroll timer (in milliseconds). | 43 // Period of the scroll timer (in milliseconds). |
44 static const int kScrollTimerMS = 30; | 44 static const int kScrollTimerMS = 30; |
45 | 45 |
46 // Delay, in ms, between when menus are selected are moused over and the menu | 46 // Delay, in ms, between when menus are selected are moused over and the menu |
47 // appears. | 47 // appears. |
48 static const int kShowDelay = 400; | 48 static const int kShowDelay = 400; |
49 | 49 |
50 // Amount of time from when the drop exits the menu and the menu is hidden. | 50 // Amount of time from when the drop exits the menu and the menu is hidden. |
51 static const int kCloseOnExitTime = 1200; | 51 static const int kCloseOnExitTime = 1200; |
52 | 52 |
53 // Border width of the menu shadow (used to align drop down position/bounds) | |
54 static const int kMenuBorderOffsetY = 2; | |
sky
2012/08/07 00:31:57
This should be moved into menu_config and you'll w
Harry McCleave
2012/08/07 01:26:36
Done.
| |
55 static const int kMenuBorderOffsetX = 1; | |
56 | |
53 namespace views { | 57 namespace views { |
54 | 58 |
55 namespace { | 59 namespace { |
56 | 60 |
57 // Returns true if the mnemonic of |menu| matches key. | 61 // Returns true if the mnemonic of |menu| matches key. |
58 bool MatchesMnemonic(MenuItemView* menu, char16 key) { | 62 bool MatchesMnemonic(MenuItemView* menu, char16 key) { |
59 return menu->GetMnemonic() == key; | 63 return menu->GetMnemonic() == key; |
60 } | 64 } |
61 | 65 |
62 // Returns true if |menu| doesn't have a mnemonic and first character of the its | 66 // Returns true if |menu| doesn't have a mnemonic and first character of the its |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 MenuItemView* MenuController::Run(Widget* parent, | 274 MenuItemView* MenuController::Run(Widget* parent, |
271 MenuButton* button, | 275 MenuButton* button, |
272 MenuItemView* root, | 276 MenuItemView* root, |
273 const gfx::Rect& bounds, | 277 const gfx::Rect& bounds, |
274 MenuItemView::AnchorPosition position, | 278 MenuItemView::AnchorPosition position, |
275 int* result_mouse_event_flags) { | 279 int* result_mouse_event_flags) { |
276 exit_type_ = EXIT_NONE; | 280 exit_type_ = EXIT_NONE; |
277 possible_drag_ = false; | 281 possible_drag_ = false; |
278 drag_in_progress_ = false; | 282 drag_in_progress_ = false; |
279 | 283 |
280 // We need to drop the first mouse release event when the menu has been | |
281 // layed out over the bounds. | |
282 drop_first_release_event_ = | |
283 root->GetRequestedMenuPosition() == MenuItemView::POSITION_OVER_BOUNDS; | |
284 | |
285 bool nested_menu = showing_; | 284 bool nested_menu = showing_; |
286 if (showing_) { | 285 if (showing_) { |
287 // Only support nesting of blocking_run menus, nesting of | 286 // Only support nesting of blocking_run menus, nesting of |
288 // blocking/non-blocking shouldn't be needed. | 287 // blocking/non-blocking shouldn't be needed. |
289 DCHECK(blocking_run_); | 288 DCHECK(blocking_run_); |
290 | 289 |
291 // We're already showing, push the current state. | 290 // We're already showing, push the current state. |
292 menu_stack_.push_back(state_); | 291 menu_stack_.push_back(state_); |
293 | 292 |
294 // The context menu should be owned by the same parent. | 293 // The context menu should be owned by the same parent. |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
466 ShowSiblingMenu(source, event.location()); | 465 ShowSiblingMenu(source, event.location()); |
467 } | 466 } |
468 UpdateActiveMouseView(source, event, mouse_menu); | 467 UpdateActiveMouseView(source, event, mouse_menu); |
469 } | 468 } |
470 | 469 |
471 void MenuController::OnMouseReleased(SubmenuView* source, | 470 void MenuController::OnMouseReleased(SubmenuView* source, |
472 const MouseEvent& event) { | 471 const MouseEvent& event) { |
473 if (!blocking_run_) | 472 if (!blocking_run_) |
474 return; | 473 return; |
475 | 474 |
476 // We must ignore the first release event when it occured within the original | |
477 // bounds. | |
478 if (drop_first_release_event_ && (event.flags() & ui::EF_LEFT_MOUSE_BUTTON)) { | |
479 drop_first_release_event_ = false; | |
480 gfx::Point loc(event.location()); | |
481 View::ConvertPointToScreen(source->GetScrollViewContainer(), &loc); | |
482 DCHECK(!state_.initial_bounds.IsEmpty()); | |
483 if (state_.initial_bounds.Contains(loc)) | |
484 return; | |
485 } | |
486 drop_first_release_event_ = false; | |
487 | |
488 DCHECK(state_.item); | 475 DCHECK(state_.item); |
489 possible_drag_ = false; | 476 possible_drag_ = false; |
490 DCHECK(blocking_run_); | 477 DCHECK(blocking_run_); |
491 MenuPart part = GetMenuPart(source, event.location()); | 478 MenuPart part = GetMenuPart(source, event.location()); |
492 if (event.IsRightMouseButton() && (part.type == MenuPart::MENU_ITEM && | 479 if (event.IsRightMouseButton() && (part.type == MenuPart::MENU_ITEM && |
493 part.menu)) { | 480 part.menu)) { |
494 if (ShowContextMenu(part.menu, source, event)) | 481 if (ShowContextMenu(part.menu, source, event)) |
495 return; | 482 return; |
496 } | 483 } |
497 | 484 |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
785 menu_item->GetType() != MenuItemView::SUBMENU)) { | 772 menu_item->GetType() != MenuItemView::SUBMENU)) { |
786 menu_item->GetWidget()->NotifyAccessibilityEvent( | 773 menu_item->GetWidget()->NotifyAccessibilityEvent( |
787 menu_item, ui::AccessibilityTypes::EVENT_FOCUS, true); | 774 menu_item, ui::AccessibilityTypes::EVENT_FOCUS, true); |
788 } | 775 } |
789 } | 776 } |
790 | 777 |
791 void MenuController::SetSelectionOnPointerDown(SubmenuView* source, | 778 void MenuController::SetSelectionOnPointerDown(SubmenuView* source, |
792 const LocatedEvent& event) { | 779 const LocatedEvent& event) { |
793 if (!blocking_run_) | 780 if (!blocking_run_) |
794 return; | 781 return; |
795 drop_first_release_event_ = false; | |
796 | 782 |
797 DCHECK(!active_mouse_view_); | 783 DCHECK(!active_mouse_view_); |
798 | 784 |
799 MenuPart part = GetMenuPart(source, event.location()); | 785 MenuPart part = GetMenuPart(source, event.location()); |
800 if (part.is_scroll()) | 786 if (part.is_scroll()) |
801 return; // Ignore presses on scroll buttons. | 787 return; // Ignore presses on scroll buttons. |
802 | 788 |
803 // When this menu is opened through a touch event, a simulated right-click | 789 // When this menu is opened through a touch event, a simulated right-click |
804 // is sent before the menu appears. Ignore it. | 790 // is sent before the menu appears. Ignore it. |
805 if ((event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) && | 791 if ((event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) && |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1042 default: | 1028 default: |
1043 break; | 1029 break; |
1044 } | 1030 } |
1045 return true; | 1031 return true; |
1046 } | 1032 } |
1047 | 1033 |
1048 MenuController::MenuController(bool blocking, | 1034 MenuController::MenuController(bool blocking, |
1049 internal::MenuControllerDelegate* delegate) | 1035 internal::MenuControllerDelegate* delegate) |
1050 : blocking_run_(blocking), | 1036 : blocking_run_(blocking), |
1051 showing_(false), | 1037 showing_(false), |
1052 drop_first_release_event_(false), | |
1053 exit_type_(EXIT_NONE), | 1038 exit_type_(EXIT_NONE), |
1054 did_capture_(false), | 1039 did_capture_(false), |
1055 result_(NULL), | 1040 result_(NULL), |
1056 result_mouse_event_flags_(0), | 1041 result_mouse_event_flags_(0), |
1057 drop_target_(NULL), | 1042 drop_target_(NULL), |
1058 drop_position_(MenuDelegate::DROP_UNKNOWN), | 1043 drop_position_(MenuDelegate::DROP_UNKNOWN), |
1059 owner_(NULL), | 1044 owner_(NULL), |
1060 #if defined(USE_AURA) | 1045 #if defined(USE_AURA) |
1061 root_window_(NULL), | 1046 root_window_(NULL), |
1062 #endif | 1047 #endif |
(...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1547 gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, | 1532 gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, |
1548 bool prefer_leading, | 1533 bool prefer_leading, |
1549 bool* is_leading) { | 1534 bool* is_leading) { |
1550 DCHECK(item); | 1535 DCHECK(item); |
1551 | 1536 |
1552 SubmenuView* submenu = item->GetSubmenu(); | 1537 SubmenuView* submenu = item->GetSubmenu(); |
1553 DCHECK(submenu); | 1538 DCHECK(submenu); |
1554 | 1539 |
1555 gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize(); | 1540 gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize(); |
1556 | 1541 |
1542 pref.set_width(pref.width() - (2 * kMenuBorderOffsetX)); | |
sky
2012/08/07 00:31:57
Move this into 1545.
Harry McCleave
2012/08/07 01:26:36
Done.
| |
1543 | |
1557 // Don't let the menu go too wide. | 1544 // Don't let the menu go too wide. |
1558 if (item->actual_menu_position() != MenuItemView::POSITION_OVER_BOUNDS) | 1545 pref.set_width(std::min(pref.width(), |
1559 pref.set_width(std::min(pref.width(), | |
1560 item->GetDelegate()->GetMaxWidthForMenu(item))); | 1546 item->GetDelegate()->GetMaxWidthForMenu(item))); |
1561 if (!state_.monitor_bounds.IsEmpty()) | 1547 if (!state_.monitor_bounds.IsEmpty()) |
1562 pref.set_width(std::min(pref.width(), state_.monitor_bounds.width())); | 1548 pref.set_width(std::min(pref.width(), state_.monitor_bounds.width())); |
1563 | 1549 |
1564 // Assume we can honor prefer_leading. | 1550 // Assume we can honor prefer_leading. |
1565 *is_leading = prefer_leading; | 1551 *is_leading = prefer_leading; |
1566 | 1552 |
1567 int x, y; | 1553 int x, y; |
1568 | 1554 |
1569 if (!item->GetParentMenuItem()) { | 1555 if (!item->GetParentMenuItem()) { |
1570 // First item, position relative to initial location. | 1556 // First item, position relative to initial location. |
1571 x = state_.initial_bounds.x(); | 1557 x = state_.initial_bounds.x() + kMenuBorderOffsetX; |
1572 if (item->actual_menu_position() == MenuItemView::POSITION_OVER_BOUNDS) | 1558 y = state_.initial_bounds.bottom() + kMenuBorderOffsetY; |
1573 y = state_.initial_bounds.y(); | |
1574 else | |
1575 y = state_.initial_bounds.bottom(); | |
1576 if (state_.anchor == MenuItemView::TOPRIGHT) | 1559 if (state_.anchor == MenuItemView::TOPRIGHT) |
1577 x = x + state_.initial_bounds.width() - pref.width(); | 1560 x = x + state_.initial_bounds.width() - pref.width(); |
1578 | 1561 |
1579 if (!state_.monitor_bounds.IsEmpty() && | 1562 if (!state_.monitor_bounds.IsEmpty() && |
1580 pref.height() > state_.monitor_bounds.height() && | 1563 y + pref.height() > state_.monitor_bounds.bottom()) { |
1581 item->actual_menu_position() == MenuItemView::POSITION_OVER_BOUNDS) { | |
1582 // Handle very tall menus. | |
1583 pref.set_height(state_.monitor_bounds.height()); | |
1584 y = state_.monitor_bounds.y(); | |
1585 } else if (!state_.monitor_bounds.IsEmpty() && | |
1586 y + pref.height() > state_.monitor_bounds.bottom() && | |
1587 item->actual_menu_position() != MenuItemView::POSITION_OVER_BOUNDS) { | |
1588 // The menu doesn't fit fully below the button on the screen. The menu | 1564 // The menu doesn't fit fully below the button on the screen. The menu |
1589 // position with respect to the bounds will be preserved if it has | 1565 // position with respect to the bounds will be preserved if it has |
1590 // already been drawn. When the requested positioning is below the bounds | 1566 // already been drawn. When the requested positioning is below the bounds |
1591 // it will shrink the menu to make it fit below. | 1567 // it will shrink the menu to make it fit below. |
1592 // If the requested positioning is best fit, it will first try to fit the | 1568 // If the requested positioning is best fit, it will first try to fit the |
1593 // menu below. If that does not fit it will try to place it above. If | 1569 // menu below. If that does not fit it will try to place it above. If |
1594 // that will not fit it will place it at the bottom of the work area and | 1570 // that will not fit it will place it at the bottom of the work area and |
1595 // moving it off the initial_bounds region to avoid overlap. | 1571 // moving it off the initial_bounds region to avoid overlap. |
1596 // In all other requested position styles it will be flipped above and | 1572 // In all other requested position styles it will be flipped above and |
1597 // the height will be shrunken to the usable height. | 1573 // the height will be shrunken to the usable height. |
1598 if (item->actual_menu_position() == MenuItemView::POSITION_BELOW_BOUNDS) { | 1574 if (item->menu_position() == MenuItemView::POSITION_BELOW_BOUNDS) { |
1599 pref.set_height(std::min(pref.height(), | 1575 pref.set_height(std::min(pref.height(), |
1600 state_.monitor_bounds.bottom() - y)); | 1576 state_.monitor_bounds.bottom() - y)); |
1601 } else if (item->actual_menu_position() == | 1577 } else if (item->menu_position() == |
1602 MenuItemView::POSITION_BEST_FIT) { | 1578 MenuItemView::POSITION_BEST_FIT) { |
1603 MenuItemView::MenuPosition orientation = | 1579 MenuItemView::MenuPosition orientation = |
1604 MenuItemView::POSITION_BELOW_BOUNDS; | 1580 MenuItemView::POSITION_BELOW_BOUNDS; |
1605 if (state_.monitor_bounds.height() < pref.height()) { | 1581 if (state_.monitor_bounds.height() < pref.height()) { |
1606 // Handle very tall menus. | 1582 // Handle very tall menus. |
1607 pref.set_height(state_.monitor_bounds.height()); | 1583 pref.set_height(state_.monitor_bounds.height()); |
1608 y = state_.monitor_bounds.y(); | 1584 y = state_.monitor_bounds.y(); |
1609 } else if (state_.monitor_bounds.y() + pref.height() < | 1585 } else if (state_.monitor_bounds.y() + pref.height() < |
1610 state_.initial_bounds.y()) { | 1586 state_.initial_bounds.y()) { |
1611 // Flipping upwards if there is enough space. | 1587 // Flipping upwards if there is enough space. |
(...skipping 18 matching lines...) Expand all Loading... | |
1630 } else { | 1606 } else { |
1631 // The menu should end with the same x coordinate as the owning | 1607 // The menu should end with the same x coordinate as the owning |
1632 // button. | 1608 // button. |
1633 if (state_.monitor_bounds.x() > | 1609 if (state_.monitor_bounds.x() > |
1634 state_.initial_bounds.x() - pref.width()) | 1610 state_.initial_bounds.x() - pref.width()) |
1635 x = state_.initial_bounds.right(); // Move right of the button. | 1611 x = state_.initial_bounds.right(); // Move right of the button. |
1636 else | 1612 else |
1637 x = state_.initial_bounds.x() - pref.width(); // Move left. | 1613 x = state_.initial_bounds.x() - pref.width(); // Move left. |
1638 } | 1614 } |
1639 } | 1615 } |
1640 item->set_actual_menu_position(orientation); | 1616 item->set_menu_position(orientation); |
1641 } else { | 1617 } else { |
1642 pref.set_height(std::min(pref.height(), | 1618 pref.set_height(std::min(pref.height(), |
1643 state_.initial_bounds.y() - state_.monitor_bounds.y())); | 1619 state_.initial_bounds.y() - state_.monitor_bounds.y())); |
1644 y = state_.initial_bounds.y() - pref.height(); | 1620 y = state_.initial_bounds.y() - pref.height(); |
1645 item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS); | 1621 item->set_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS); |
1646 } | 1622 } |
1647 } else if (item->actual_menu_position() == | 1623 } else if (item->menu_position() == |
1648 MenuItemView::POSITION_ABOVE_BOUNDS) { | 1624 MenuItemView::POSITION_ABOVE_BOUNDS) { |
1649 pref.set_height(std::min(pref.height(), | 1625 pref.set_height(std::min(pref.height(), |
1650 state_.initial_bounds.y() - state_.monitor_bounds.y())); | 1626 state_.initial_bounds.y() - state_.monitor_bounds.y())); |
1651 y = state_.initial_bounds.y() - pref.height(); | 1627 y = state_.initial_bounds.y() - pref.height(); |
1652 } else if (item->actual_menu_position() == | |
1653 MenuItemView::POSITION_OVER_BOUNDS) { | |
1654 // Center vertically assuming all items have the same height. | |
1655 int middle = state_.initial_bounds.y() - pref.height() / 2; | |
1656 if (submenu->GetMenuItemCount() > 0) | |
1657 middle += submenu->GetMenuItemAt(0)->GetPreferredSize().height() / 2; | |
1658 y = std::max(state_.monitor_bounds.y(), middle); | |
1659 if (y + pref.height() > state_.monitor_bounds.bottom()) | |
1660 y = state_.monitor_bounds.bottom() - pref.height(); | |
1661 } else { | 1628 } else { |
1662 item->set_actual_menu_position(MenuItemView::POSITION_BELOW_BOUNDS); | 1629 item->set_menu_position(MenuItemView::POSITION_BELOW_BOUNDS); |
1663 } | 1630 } |
1664 } else { | 1631 } else { |
1665 // Not the first menu; position it relative to the bounds of the menu | 1632 // Not the first menu; position it relative to the bounds of the menu |
1666 // item. | 1633 // item. |
1667 gfx::Point item_loc; | 1634 gfx::Point item_loc; |
1668 View::ConvertPointToScreen(item, &item_loc); | 1635 View::ConvertPointToScreen(item, &item_loc); |
1669 | 1636 |
1670 // We must make sure we take into account the UI layout. If the layout is | 1637 // We must make sure we take into account the UI layout. If the layout is |
1671 // RTL, then a 'leading' menu is positioned to the left of the parent menu | 1638 // RTL, then a 'leading' menu is positioned to the left of the parent menu |
1672 // item and not to the right. | 1639 // item and not to the right. |
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2140 | 2107 |
2141 #if defined(USE_AURA) | 2108 #if defined(USE_AURA) |
2142 void MenuController::OnWindowActivated(aura::Window* active, | 2109 void MenuController::OnWindowActivated(aura::Window* active, |
2143 aura::Window* old_active) { | 2110 aura::Window* old_active) { |
2144 if (!drag_in_progress_) | 2111 if (!drag_in_progress_) |
2145 Cancel(EXIT_ALL); | 2112 Cancel(EXIT_ALL); |
2146 } | 2113 } |
2147 #endif | 2114 #endif |
2148 | 2115 |
2149 } // namespace views | 2116 } // namespace views |
OLD | NEW |