| 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/time.h" | 9 #include "base/time.h" |
| 10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| 11 #include "ui/base/dragdrop/os_exchange_data.h" | 11 #include "ui/base/dragdrop/os_exchange_data.h" |
| 12 #include "ui/base/events.h" | 12 #include "ui/base/events.h" |
| 13 #include "ui/base/keycodes/keyboard_codes.h" | 13 #include "ui/base/keycodes/keyboard_codes.h" |
| 14 #include "ui/base/l10n/l10n_util.h" | 14 #include "ui/base/l10n/l10n_util.h" |
| 15 #include "ui/gfx/canvas_skia.h" | 15 #include "ui/gfx/canvas_skia.h" |
| 16 #include "ui/gfx/screen.h" | 16 #include "ui/gfx/screen.h" |
| 17 #include "ui/views/controls/button/menu_button.h" | 17 #include "ui/views/controls/button/menu_button.h" |
| 18 #include "ui/views/controls/menu/menu_controller_delegate.h" | 18 #include "ui/views/controls/menu/menu_controller_delegate.h" |
| 19 #include "ui/views/controls/menu/menu_scroll_view_container.h" | 19 #include "ui/views/controls/menu/menu_scroll_view_container.h" |
| 20 #include "ui/views/controls/menu/submenu_view.h" | 20 #include "ui/views/controls/menu/submenu_view.h" |
| 21 #include "ui/views/drag_utils.h" | 21 #include "ui/views/drag_utils.h" |
| 22 #include "ui/views/view_constants.h" | 22 #include "ui/views/view_constants.h" |
| 23 #include "ui/views/views_delegate.h" | 23 #include "ui/views/views_delegate.h" |
| 24 #include "ui/views/widget/root_view.h" | 24 #include "ui/views/widget/root_view.h" |
| 25 #include "ui/views/widget/widget.h" | 25 #include "ui/views/widget/widget.h" |
| 26 | 26 |
| 27 #if defined(USE_AURA) | 27 #if defined(USE_AURA) |
| 28 #include "ui/aura/client/dispatcher_client.h" | 28 #include "ui/aura/client/dispatcher_client.h" |
| 29 #include "ui/aura/env.h" | 29 #include "ui/aura/env.h" |
| 30 #include "ui/aura/event.h" |
| 30 #include "ui/aura/root_window.h" | 31 #include "ui/aura/root_window.h" |
| 31 #elif defined(TOOLKIT_USES_GTK) | 32 #elif defined(TOOLKIT_USES_GTK) |
| 32 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" | 33 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" |
| 33 #endif | 34 #endif |
| 34 | 35 |
| 35 using base::Time; | 36 #if defined(USE_AURA) && defined(USE_X11) |
| 37 #include <X11/Xlib.h> |
| 38 #endif |
| 39 |
| 36 using base::TimeDelta; | 40 using base::TimeDelta; |
| 37 using ui::OSExchangeData; | 41 using ui::OSExchangeData; |
| 38 | 42 |
| 39 // Period of the scroll timer (in milliseconds). | 43 // Period of the scroll timer (in milliseconds). |
| 40 static const int kScrollTimerMS = 30; | 44 static const int kScrollTimerMS = 30; |
| 41 | 45 |
| 42 // 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 |
| 43 // appears. | 47 // appears. |
| 44 static const int kShowDelay = 400; | 48 static const int kShowDelay = 400; |
| 45 | 49 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 156 if (!part.is_scroll()) { | 160 if (!part.is_scroll()) { |
| 157 StopScrolling(); | 161 StopScrolling(); |
| 158 return; | 162 return; |
| 159 } | 163 } |
| 160 DCHECK(part.submenu); | 164 DCHECK(part.submenu); |
| 161 SubmenuView* new_menu = part.submenu; | 165 SubmenuView* new_menu = part.submenu; |
| 162 bool new_is_up = (part.type == MenuController::MenuPart::SCROLL_UP); | 166 bool new_is_up = (part.type == MenuController::MenuPart::SCROLL_UP); |
| 163 if (new_menu == submenu_ && is_scrolling_up_ == new_is_up) | 167 if (new_menu == submenu_ && is_scrolling_up_ == new_is_up) |
| 164 return; | 168 return; |
| 165 | 169 |
| 166 start_scroll_time_ = Time::Now(); | 170 start_scroll_time_ = base::Time::Now(); |
| 167 start_y_ = part.submenu->GetVisibleBounds().y(); | 171 start_y_ = part.submenu->GetVisibleBounds().y(); |
| 168 submenu_ = new_menu; | 172 submenu_ = new_menu; |
| 169 is_scrolling_up_ = new_is_up; | 173 is_scrolling_up_ = new_is_up; |
| 170 | 174 |
| 171 if (!scrolling_timer_.IsRunning()) { | 175 if (!scrolling_timer_.IsRunning()) { |
| 172 scrolling_timer_.Start(FROM_HERE, | 176 scrolling_timer_.Start(FROM_HERE, |
| 173 TimeDelta::FromMilliseconds(kScrollTimerMS), | 177 TimeDelta::FromMilliseconds(kScrollTimerMS), |
| 174 this, &MenuScrollTask::Run); | 178 this, &MenuScrollTask::Run); |
| 175 } | 179 } |
| 176 } | 180 } |
| 177 | 181 |
| 178 void StopScrolling() { | 182 void StopScrolling() { |
| 179 if (scrolling_timer_.IsRunning()) { | 183 if (scrolling_timer_.IsRunning()) { |
| 180 scrolling_timer_.Stop(); | 184 scrolling_timer_.Stop(); |
| 181 submenu_ = NULL; | 185 submenu_ = NULL; |
| 182 } | 186 } |
| 183 } | 187 } |
| 184 | 188 |
| 185 // The menu being scrolled. Returns null if not scrolling. | 189 // The menu being scrolled. Returns null if not scrolling. |
| 186 SubmenuView* submenu() const { return submenu_; } | 190 SubmenuView* submenu() const { return submenu_; } |
| 187 | 191 |
| 188 private: | 192 private: |
| 189 void Run() { | 193 void Run() { |
| 190 DCHECK(submenu_); | 194 DCHECK(submenu_); |
| 191 gfx::Rect vis_rect = submenu_->GetVisibleBounds(); | 195 gfx::Rect vis_rect = submenu_->GetVisibleBounds(); |
| 192 const int delta_y = static_cast<int>( | 196 const int delta_y = static_cast<int>( |
| 193 (Time::Now() - start_scroll_time_).InMilliseconds() * | 197 (base::Time::Now() - start_scroll_time_).InMilliseconds() * |
| 194 pixels_per_second_ / 1000); | 198 pixels_per_second_ / 1000); |
| 195 vis_rect.set_y(is_scrolling_up_ ? | 199 vis_rect.set_y(is_scrolling_up_ ? |
| 196 std::max(0, start_y_ - delta_y) : | 200 std::max(0, start_y_ - delta_y) : |
| 197 std::min(submenu_->height() - vis_rect.height(), start_y_ + delta_y)); | 201 std::min(submenu_->height() - vis_rect.height(), start_y_ + delta_y)); |
| 198 submenu_->ScrollRectToVisible(vis_rect); | 202 submenu_->ScrollRectToVisible(vis_rect); |
| 199 } | 203 } |
| 200 | 204 |
| 201 // SubmenuView being scrolled. | 205 // SubmenuView being scrolled. |
| 202 SubmenuView* submenu_; | 206 SubmenuView* submenu_; |
| 203 | 207 |
| 204 // Direction scrolling. | 208 // Direction scrolling. |
| 205 bool is_scrolling_up_; | 209 bool is_scrolling_up_; |
| 206 | 210 |
| 207 // Timer to periodically scroll. | 211 // Timer to periodically scroll. |
| 208 base::RepeatingTimer<MenuScrollTask> scrolling_timer_; | 212 base::RepeatingTimer<MenuScrollTask> scrolling_timer_; |
| 209 | 213 |
| 210 // Time we started scrolling at. | 214 // Time we started scrolling at. |
| 211 Time start_scroll_time_; | 215 base::Time start_scroll_time_; |
| 212 | 216 |
| 213 // How many pixels to scroll per second. | 217 // How many pixels to scroll per second. |
| 214 int pixels_per_second_; | 218 int pixels_per_second_; |
| 215 | 219 |
| 216 // Y-coordinate of submenu_view_ when scrolling started. | 220 // Y-coordinate of submenu_view_ when scrolling started. |
| 217 int start_y_; | 221 int start_y_; |
| 218 | 222 |
| 219 DISALLOW_COPY_AND_ASSIGN(MenuScrollTask); | 223 DISALLOW_COPY_AND_ASSIGN(MenuScrollTask); |
| 220 }; | 224 }; |
| 221 | 225 |
| (...skipping 556 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 778 return; // Ignore presses on scroll buttons. | 782 return; // Ignore presses on scroll buttons. |
| 779 | 783 |
| 780 if (part.type == MenuPart::NONE || | 784 if (part.type == MenuPart::NONE || |
| 781 (part.type == MenuPart::MENU_ITEM && part.menu && | 785 (part.type == MenuPart::MENU_ITEM && part.menu && |
| 782 part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) { | 786 part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) { |
| 783 // Mouse wasn't pressed over any menu, or the active menu, cancel. | 787 // Mouse wasn't pressed over any menu, or the active menu, cancel. |
| 784 | 788 |
| 785 // We're going to close and we own the mouse capture. We need to repost the | 789 // We're going to close and we own the mouse capture. We need to repost the |
| 786 // mouse down, otherwise the window the user clicked on won't get the | 790 // mouse down, otherwise the window the user clicked on won't get the |
| 787 // event. | 791 // event. |
| 788 #if defined(OS_WIN) && !defined(USE_AURA) | 792 #if defined(OS_WIN) || defined(USE_AURA) |
| 789 RepostEvent(source, event); | 793 RepostEvent(source, event); |
| 790 // NOTE: not reposting on linux seems fine. | 794 // NOTE: not reposting on linux seems fine. |
| 791 #endif | 795 #endif |
| 792 | 796 |
| 793 // And close. | 797 // And close. |
| 794 ExitType exit_type = EXIT_ALL; | 798 ExitType exit_type = EXIT_ALL; |
| 795 if (!menu_stack_.empty()) { | 799 if (!menu_stack_.empty()) { |
| 796 // We're running nested menus. Only exit all if the mouse wasn't over one | 800 // We're running nested menus. Only exit all if the mouse wasn't over one |
| 797 // of the menus from the last run. | 801 // of the menus from the last run. |
| 798 gfx::Point screen_loc(event.location()); | 802 gfx::Point screen_loc(event.location()); |
| (...skipping 1030 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1829 } | 1833 } |
| 1830 | 1834 |
| 1831 // If no mnemonics found, look at first character of titles. | 1835 // If no mnemonics found, look at first character of titles. |
| 1832 details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); | 1836 details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); |
| 1833 if (details.first_match != -1) | 1837 if (details.first_match != -1) |
| 1834 return AcceptOrSelect(item, details); | 1838 return AcceptOrSelect(item, details); |
| 1835 | 1839 |
| 1836 return false; | 1840 return false; |
| 1837 } | 1841 } |
| 1838 | 1842 |
| 1839 #if defined(OS_WIN) && !defined(USE_AURA) | 1843 #if defined(OS_WIN) |
| 1840 void MenuController::RepostEvent(SubmenuView* source, | 1844 void MenuController::RepostEvent(SubmenuView* source, |
| 1841 const LocatedEvent& event) { | 1845 const LocatedEvent& event) { |
| 1842 if (!state_.item) { | 1846 if (!state_.item) { |
| 1843 // We some times get an event after closing all the menus. Ignore it. | 1847 // We some times get an event after closing all the menus. Ignore it. |
| 1844 // Make sure the menu is in fact not visible. If the menu is visible, then | 1848 // Make sure the menu is in fact not visible. If the menu is visible, then |
| 1845 // we're in a bad state where we think the menu isn't visibile but it is. | 1849 // we're in a bad state where we think the menu isn't visibile but it is. |
| 1846 DCHECK(!source->GetWidget()->IsVisible()); | 1850 DCHECK(!source->GetWidget()->IsVisible()); |
| 1847 return; | 1851 return; |
| 1848 } | 1852 } |
| 1849 | 1853 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1897 if (in_client_area) { | 1901 if (in_client_area) { |
| 1898 PostMessage(window, event_type, event.native_event().wParam, | 1902 PostMessage(window, event_type, event.native_event().wParam, |
| 1899 MAKELPARAM(window_x, window_y)); | 1903 MAKELPARAM(window_x, window_y)); |
| 1900 } else { | 1904 } else { |
| 1901 PostMessage(window, event_type, nc_hit_result, | 1905 PostMessage(window, event_type, nc_hit_result, |
| 1902 MAKELPARAM(screen_loc.x(), screen_loc.y())); | 1906 MAKELPARAM(screen_loc.x(), screen_loc.y())); |
| 1903 } | 1907 } |
| 1904 } | 1908 } |
| 1905 } | 1909 } |
| 1906 } | 1910 } |
| 1907 #endif // defined(OS_WIN) | 1911 #elif defined(USE_AURA) |
| 1912 void MenuController::RepostEvent(SubmenuView* source, |
| 1913 const LocatedEvent& event) { |
| 1914 if (!state_.item) { |
| 1915 // We some times get an event after closing all the menus. Ignore it. |
| 1916 // Make sure the menu is in fact not visible. If the menu is visible, then |
| 1917 // we're in a bad state where we think the menu isn't visibile but it is. |
| 1918 DCHECK(!source->GetWidget()->IsVisible()); |
| 1919 return; |
| 1920 } |
| 1921 |
| 1922 // Release the capture. |
| 1923 SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu(); |
| 1924 submenu->ReleaseCapture(); |
| 1925 |
| 1926 gfx::Point screen_loc(event.location()); |
| 1927 View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc); |
| 1928 |
| 1929 XEvent xevent; |
| 1930 xevent.type = ButtonPress; |
| 1931 xevent.xbutton.x = screen_loc.x(); |
| 1932 xevent.xbutton.y = screen_loc.y(); |
| 1933 xevent.xbutton.state = 0; |
| 1934 xevent.xbutton.same_screen = True; |
| 1935 int flags = event.flags(); |
| 1936 if (flags & ui::EF_LEFT_MOUSE_BUTTON) |
| 1937 xevent.xbutton.button = 1; |
| 1938 else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) |
| 1939 xevent.xbutton.button = 2; |
| 1940 else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) |
| 1941 xevent.xbutton.button = 3; |
| 1942 else |
| 1943 return; |
| 1944 |
| 1945 aura::RootWindow::GetInstance()->PostNativeEvent(&xevent); |
| 1946 } |
| 1947 #endif // defined(USE_AURA) |
| 1908 | 1948 |
| 1909 void MenuController::SetDropMenuItem( | 1949 void MenuController::SetDropMenuItem( |
| 1910 MenuItemView* new_target, | 1950 MenuItemView* new_target, |
| 1911 MenuDelegate::DropPosition new_position) { | 1951 MenuDelegate::DropPosition new_position) { |
| 1912 if (new_target == drop_target_ && new_position == drop_position_) | 1952 if (new_target == drop_target_ && new_position == drop_position_) |
| 1913 return; | 1953 return; |
| 1914 | 1954 |
| 1915 if (drop_target_) { | 1955 if (drop_target_) { |
| 1916 drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem( | 1956 drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem( |
| 1917 NULL, MenuDelegate::DROP_NONE); | 1957 NULL, MenuDelegate::DROP_NONE); |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2044 (!pending_state_.item->HasSubmenu() || | 2084 (!pending_state_.item->HasSubmenu() || |
| 2045 !pending_state_.item->GetSubmenu()->IsShowing())) { | 2085 !pending_state_.item->GetSubmenu()->IsShowing())) { |
| 2046 // On exit if the user hasn't selected an item with a submenu, move the | 2086 // On exit if the user hasn't selected an item with a submenu, move the |
| 2047 // selection back to the parent menu item. | 2087 // selection back to the parent menu item. |
| 2048 SetSelection(pending_state_.item->GetParentMenuItem(), | 2088 SetSelection(pending_state_.item->GetParentMenuItem(), |
| 2049 SELECTION_OPEN_SUBMENU); | 2089 SELECTION_OPEN_SUBMENU); |
| 2050 } | 2090 } |
| 2051 } | 2091 } |
| 2052 | 2092 |
| 2053 } // namespace views | 2093 } // namespace views |
| OLD | NEW |