#include "ui/views/controls/menu/menu_controller.h"
#include <algorithm>
#include <set>
#include <utility>
#include "base/callback_list.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/owned_window_anchor.h"
#include "ui/base/ui_base_types.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller_delegate.h"
#include "ui/views/controls/menu/menu_host_root_view.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_pre_target_handler.h"
#include "ui/views/controls/menu/menu_scroll_view_container.h"
#include "ui/views/controls/menu/menu_types.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/drag_utils.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/mouse_constants.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_constants.h"
#include "ui/views/view_tracker.h"
#include "ui/views/view_utils.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(IS_WIN)
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/win/internal_constants.h"
#include "ui/display/win/screen_win.h"
#include "ui/views/win/hwnd_util.h"
#endif
#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#endif
#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif
OSExchangeData;
DEFINE_UI_CLASS_PROPERTY_TYPE(…)
namespace views {
namespace {
enum class MenuPartType { … };
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(…)
#if BUILDFLAG(IS_MAC)
bool AcceleratorShouldCancelMenu(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_CONTROL ||
accelerator.key_code() == ui::VKEY_MENU ||
accelerator.key_code() == ui::VKEY_COMMAND ||
accelerator.key_code() == ui::VKEY_SHIFT) {
return false;
}
return accelerator.IsCtrlDown() || accelerator.IsAltDown() ||
accelerator.IsCmdDown();
}
#endif
bool ShouldIgnoreScreenBoundsForMenus() { … }
base::TimeDelta menu_selection_hold_time = …;
constexpr int kCloseOnExitTime = …;
constexpr int kTouchYPadding = …;
constexpr int kBubbleTipSizeLeftRight = …;
constexpr int kBubbleTipSizeTopBottom = …;
constexpr float kMaximumLengthMovedToActivate = …;
constexpr base::TimeDelta kAlertAnimationThrobDuration = …;
bool MatchesMnemonic(MenuItemView* menu, char16_t key) { … }
bool TitleMatchesMnemonic(MenuItemView* menu, char16_t key) { … }
Button* GetFirstHotTrackedView(View* view) { … }
MenuPartType GetScrollButtonAt(SubmenuView* source,
const gfx::Point& location) { … }
gfx::Point ConvertFromScreen(const SubmenuView& submenu,
const gfx::Point& location) { … }
gfx::Point ConvertToScreen(const SubmenuView& submenu,
const gfx::Point& location) { … }
template <typename T>
T ConvertLocatedEventForRootView(const SubmenuView& submenu,
const MenuHostRootView& root_view,
const T& event) { … }
const SubmenuView& GetRootMenu(const SubmenuView& submenu) { … }
gfx::Point GetLocationInRootMenu(const SubmenuView& submenu,
const gfx::Point& location) { … }
bool Contains(const SubmenuView& submenu, const gfx::Point& location) { … }
View* GetFirstFocusableViewForward(View* view,
View::Views::const_iterator pos) { … }
View* GetFirstFocusableViewBackward(View* view,
View::Views::const_reverse_iterator pos) { … }
View* GetInitialFocusableView(View* start, bool forward) { … }
View* GetNextFocusableView(View* ancestor, View* start_at, bool forward) { … }
#if BUILDFLAG(IS_WIN)
static void RepostEventImpl(const ui::LocatedEvent* event,
const gfx::Point& screen_loc,
gfx::NativeView native_view,
gfx::NativeWindow window) {
if (!event->IsMouseEvent() && !event->IsTouchEvent()) {
DCHECK(event->IsGestureEvent());
return;
}
if (!native_view)
return;
gfx::Point screen_loc_pixels =
display::win::ScreenWin::DIPToScreenPoint(screen_loc);
HWND target_window = ::WindowFromPoint(screen_loc_pixels.ToPOINT());
if (!window) {
HWND parent = ::GetParent(target_window);
if (parent) {
aura::WindowTreeHost* host =
aura::WindowTreeHost::GetForAcceleratedWidget(parent);
if (host) {
target_window = parent;
window = host->window();
}
}
}
if (event->IsMouseEvent()) {
HWND source_window = HWNDForNativeView(native_view);
if (!target_window || !source_window ||
GetWindowThreadProcessId(source_window, nullptr) !=
GetWindowThreadProcessId(target_window, nullptr)) {
return;
}
LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y());
LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords);
const bool client_area = nc_hit_result == HTCLIENT;
int window_x = screen_loc_pixels.x();
int window_y = screen_loc_pixels.y();
if (client_area) {
POINT pt = {window_x, window_y};
ScreenToClient(target_window, &pt);
window_x = pt.x;
window_y = pt.y;
}
WPARAM target = client_area ? event->native_event().wParam
: static_cast<WPARAM>(nc_hit_result);
LPARAM window_coords = MAKELPARAM(window_x, window_y);
PostMessage(target_window, event->native_event().message, target,
window_coords);
return;
}
if (!window)
return;
aura::Window* root = window->GetRootWindow();
aura::client::ScreenPositionClient* spc =
aura::client::GetScreenPositionClient(root);
if (!spc)
return;
gfx::Point root_loc(screen_loc);
spc->ConvertPointFromScreen(root, &root_loc);
std::unique_ptr<ui::Event> clone = event->Clone();
std::unique_ptr<ui::LocatedEvent> located_event(
static_cast<ui::LocatedEvent*>(clone.release()));
located_event->set_location(root_loc);
located_event->set_root_location(root_loc);
root->GetHost()->dispatcher()->RepostEvent(located_event.get());
}
#endif
}
struct MenuController::MenuPart { … };
class MenuController::MenuScrollTask { … };
struct MenuController::SelectByCharDetails { … };
MenuController::State::State() = default;
MenuController::State::State(const State& other) = default;
MenuController::State::~State() = default;
MenuController* MenuController::active_instance_ = …;
MenuController* MenuController::GetActiveInstance() { … }
void MenuController::OnWidgetShowStateChanged(Widget* widget) { … }
void MenuController::Run(Widget* parent,
MenuButtonController* button_controller,
MenuItemView* root,
const gfx::Rect& anchor_bounds,
MenuAnchorPosition position,
bool context_menu,
bool is_nested_drag,
gfx::NativeView native_view_for_gestures) { … }
void MenuController::Cancel(ExitType type) { … }
void MenuController::AddNestedDelegate(
internal::MenuControllerDelegate* delegate) { … }
bool MenuController::IsCombobox() const { … }
bool MenuController::IsEditableCombobox() const { … }
bool MenuController::IsReadonlyCombobox() const { … }
bool MenuController::IsContextMenu() const { … }
void MenuController::SelectItemAndOpenSubmenu(MenuItemView* item) { … }
bool MenuController::OnMousePressed(SubmenuView* source,
const ui::MouseEvent& event) { … }
bool MenuController::OnMouseDragged(SubmenuView* source,
const ui::MouseEvent& event) { … }
void MenuController::OnMouseReleased(SubmenuView* source,
const ui::MouseEvent& event) { … }
void MenuController::OnMouseMoved(SubmenuView* source,
const ui::MouseEvent& event) { … }
void MenuController::OnMouseEntered(SubmenuView* source,
const ui::MouseEvent& event) { … }
bool MenuController::OnMouseWheel(SubmenuView* source,
const ui::MouseWheelEvent& event) { … }
void MenuController::OnGestureEvent(SubmenuView* source,
ui::GestureEvent* event) { … }
void MenuController::OnTouchEvent(SubmenuView* source, ui::TouchEvent* event) { … }
View* MenuController::GetTooltipHandlerForPoint(SubmenuView* source,
const gfx::Point& point) { … }
void MenuController::ViewHierarchyChanged(
SubmenuView* source,
const ViewHierarchyChangedDetails& details) { … }
bool MenuController::GetDropFormats(
SubmenuView* source,
int* formats,
std::set<ui::ClipboardFormatType>* format_types) { … }
bool MenuController::AreDropTypesRequired(SubmenuView* source) { … }
bool MenuController::CanDrop(SubmenuView* source, const OSExchangeData& data) { … }
void MenuController::OnDragEntered(SubmenuView* source,
const ui::DropTargetEvent& event) { … }
int MenuController::OnDragUpdated(SubmenuView* source,
const ui::DropTargetEvent& event) { … }
void MenuController::OnDragExited(SubmenuView* source) { … }
views::View::DropCallback MenuController::GetDropCallback(
SubmenuView* source,
const ui::DropTargetEvent& event) { … }
void MenuController::OnDragEnteredScrollButton(SubmenuView* source,
bool is_up) { … }
void MenuController::OnDragExitedScrollButton(SubmenuView* source) { … }
void MenuController::OnDragWillStart() { … }
void MenuController::OnDragComplete(bool should_close) { … }
ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent(
ui::KeyEvent* event) { … }
void MenuController::UpdateSubmenuSelection(SubmenuView* submenu) { … }
void MenuController::OnWidgetDestroying(Widget* widget) { … }
bool MenuController::IsCancelAllTimerRunningForTest() { … }
void MenuController::ClearStateForTest() { … }
void MenuController::TurnOffMenuSelectionHoldForTest() { … }
ui::ColorId MenuController::GetSeparatorColorId() const { … }
void MenuController::OnMenuItemDestroying(MenuItemView* menu_item) { … }
void MenuController::AnimationProgressed(const gfx::Animation* animation) { … }
void MenuController::SetSelection(MenuItemView* menu_item,
int selection_types) { … }
void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
const ui::LocatedEvent* event) { … }
void MenuController::StartDrag(SubmenuView* source,
const gfx::Point& location) { … }
bool MenuController::OnKeyPressed(const ui::KeyEvent& event) { … }
MenuController::MenuController(bool for_drop,
internal::MenuControllerDelegate* delegate)
: … { … }
MenuController::~MenuController() { … }
bool MenuController::SendAcceleratorToHotTrackedView(int event_flags) { … }
void MenuController::UpdateInitialLocation(const gfx::Rect& anchor_bounds,
MenuAnchorPosition position,
bool context_menu) { … }
MenuAnchorPosition MenuController::AdjustAnchorPositionForRtl(
MenuAnchorPosition position) { … }
void MenuController::Accept(MenuItemView* item, int event_flags) { … }
void MenuController::ReallyAccept() { … }
bool MenuController::ShowSiblingMenu(SubmenuView* source,
const gfx::Point& mouse_location) { … }
bool MenuController::ShowContextMenu(MenuItemView* menu_item,
const gfx::Point& screen_location,
ui::MenuSourceType source_type) { … }
void MenuController::CloseAllNestedMenus() { … }
MenuItemView* MenuController::GetMenuItemAt(View* source,
const gfx::Point& location) { … }
MenuController::MenuPart MenuController::GetMenuPart(
SubmenuView* source,
const gfx::Point& source_loc) { … }
MenuController::MenuPart MenuController::GetMenuPartByScreenCoordinateUsingMenu(
MenuItemView* item,
const gfx::Point& screen_loc) { … }
bool MenuController::GetMenuPartByScreenCoordinateImpl(
SubmenuView* menu,
const gfx::Point& screen_loc,
MenuPart* part) { … }
MenuHostRootView* MenuController::GetRootView(SubmenuView* submenu,
const gfx::Point& source_loc) { … }
bool MenuController::IsLocationOverSubmenuAreaOfActionableSubmenu(
MenuItemView* item,
const gfx::Point& screen_loc) const { … }
void MenuController::CommitPendingSelection() { … }
void MenuController::CloseMenu(MenuItemView* item) { … }
void MenuController::OpenMenu(MenuItemView* item) { … }
void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { … }
void MenuController::MenuChildrenChanged(MenuItemView* item) { … }
void MenuController::BuildPathsAndCalculateDiff(
MenuItemView* old_item,
MenuItemView* new_item,
std::vector<MenuItemView*>* old_path,
std::vector<MenuItemView*>* new_path,
size_t* first_diff_at) { … }
void MenuController::BuildMenuItemPath(MenuItemView* item,
std::vector<MenuItemView*>* path) { … }
void MenuController::StartShowTimer() { … }
void MenuController::StopShowTimer() { … }
void MenuController::StartCancelAllTimer() { … }
void MenuController::StopCancelAllTimer() { … }
gfx::Rect MenuController::CalculateMenuBounds(
MenuItemView* item,
MenuOpenDirection preferred_open_direction,
MenuOpenDirection* resulting_direction,
ui::OwnedWindowAnchor* anchor) { … }
gfx::Rect MenuController::CalculateBubbleMenuBounds(
MenuItemView* item,
MenuOpenDirection preferred_open_direction,
MenuOpenDirection* resulting_direction,
ui::OwnedWindowAnchor* anchor) { … }
size_t MenuController::MenuDepth(MenuItemView* item) { … }
void MenuController::IncrementSelection(
SelectionIncrementDirectionType direction) { … }
void MenuController::SetSelectionIndices(MenuItemView* parent) { … }
void MenuController::MoveSelectionToFirstOrLastItem(
SelectionIncrementDirectionType direction) { … }
MenuItemView* MenuController::FindInitialSelectableMenuItem(
MenuItemView* parent,
SelectionIncrementDirectionType direction) { … }
void MenuController::OpenSubmenuChangeSelectionIfCan() { … }
void MenuController::CloseSubmenu() { … }
MenuController::SelectByCharDetails MenuController::FindChildForMnemonic(
MenuItemView* parent,
char16_t key,
bool (*match_function)(MenuItemView* menu, char16_t mnemonic)) { … }
void MenuController::AcceptOrSelect(MenuItemView* parent,
const SelectByCharDetails& details) { … }
void MenuController::SelectByChar(char16_t character) { … }
void MenuController::RepostEventAndCancel(SubmenuView* source,
const ui::LocatedEvent* event) { … }
void MenuController::SetDropMenuItem(MenuItemView* new_target,
MenuDelegate::DropPosition new_position) { … }
void MenuController::UpdateScrolling(const MenuPart& part) { … }
void MenuController::StopScrollingViaButton() { … }
void MenuController::UpdateActiveMouseView(SubmenuView* event_source,
const ui::MouseEvent& event,
View* target_menu) { … }
void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source,
const ui::MouseEvent& event) { … }
void MenuController::SendMouseCaptureLostToActiveView() { … }
void MenuController::SetExitType(ExitType type) { … }
void MenuController::ExitMenu() { … }
MenuItemView* MenuController::ExitTopMostMenu() { … }
void MenuController::HandleMouseLocation(SubmenuView* source,
const gfx::Point& mouse_location) { … }
void MenuController::SetInitialHotTrackedView(
MenuItemView* item,
SelectionIncrementDirectionType direction) { … }
void MenuController::SetNextHotTrackedView(
MenuItemView* item,
SelectionIncrementDirectionType direction) { … }
void MenuController::SetHotTrackedButton(Button* new_hot_button) { … }
bool MenuController::ShouldContinuePrefixSelection() const { … }
void MenuController::RegisterAlertedItem(MenuItemView* item) { … }
void MenuController::UnregisterAlertedItem(MenuItemView* item) { … }
void MenuController::SetAnchorParametersForItem(MenuItemView* item,
const gfx::Point& item_loc,
ui::OwnedWindowAnchor* anchor) { … }
base::CallbackListSubscription MenuController::AddAnnotationCallback(
AnnotationCallback callback) { … }
bool MenuController::MaybeForwardToAnnotation(SubmenuView* source,
const ui::LocatedEvent& event) { … }
bool MenuController::CanProcessInputEvents() const { … }
MenuController::MenuOpenDirection
MenuController::GetChildMenuOpenDirectionAtDepth(size_t depth) const { … }
void MenuController::SetChildMenuOpenDirectionAtDepth(
size_t depth,
MenuOpenDirection direction) { … }
void MenuController::SetMenuRoundedCorners(
std::optional<gfx::RoundedCornersF> corners) { … }
}