| Index: ui/views/controls/menu/menu_message_loop_aura.cc
|
| diff --git a/ui/views/controls/menu/menu_message_loop_aura.cc b/ui/views/controls/menu/menu_message_loop_aura.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..49049ee9f135fb6038739055796f2157f3a64c35
|
| --- /dev/null
|
| +++ b/ui/views/controls/menu/menu_message_loop_aura.cc
|
| @@ -0,0 +1,208 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ui/views/controls/menu/menu_message_loop_aura.h"
|
| +
|
| +#if defined(OS_WIN)
|
| +#include <windowsx.h>
|
| +#endif
|
| +
|
| +#include "base/run_loop.h"
|
| +#include "ui/aura/client/screen_position_client.h"
|
| +#include "ui/aura/env.h"
|
| +#include "ui/aura/window.h"
|
| +#include "ui/aura/window_event_dispatcher.h"
|
| +#include "ui/aura/window_tree_host.h"
|
| +#include "ui/events/event.h"
|
| +#include "ui/events/platform/platform_event_source.h"
|
| +#include "ui/events/platform/scoped_event_dispatcher.h"
|
| +#include "ui/views/controls/menu/menu_controller.h"
|
| +#include "ui/views/widget/widget.h"
|
| +#include "ui/wm/public/activation_change_observer.h"
|
| +#include "ui/wm/public/activation_client.h"
|
| +#include "ui/wm/public/dispatcher_client.h"
|
| +#include "ui/wm/public/drag_drop_client.h"
|
| +
|
| +#if defined(OS_WIN)
|
| +#include "ui/base/win/internal_constants.h"
|
| +#include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h"
|
| +#include "ui/views/win/hwnd_util.h"
|
| +#else
|
| +#include "ui/views/controls/menu/menu_event_dispatcher_linux.h"
|
| +#endif
|
| +
|
| +using aura::client::ScreenPositionClient;
|
| +
|
| +namespace views {
|
| +
|
| +namespace {
|
| +
|
| +aura::Window* GetOwnerRootWindow(views::Widget* owner) {
|
| + return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL;
|
| +}
|
| +
|
| +// ActivationChangeObserverImpl is used to observe activation changes and close
|
| +// the menu. Additionally it listens for the root window to be destroyed and
|
| +// cancel the menu as well.
|
| +class ActivationChangeObserverImpl
|
| + : public aura::client::ActivationChangeObserver,
|
| + public aura::WindowObserver,
|
| + public ui::EventHandler {
|
| + public:
|
| + ActivationChangeObserverImpl(MenuController* controller, aura::Window* root)
|
| + : controller_(controller), root_(root) {
|
| + aura::client::GetActivationClient(root_)->AddObserver(this);
|
| + root_->AddObserver(this);
|
| + root_->AddPreTargetHandler(this);
|
| + }
|
| +
|
| + virtual ~ActivationChangeObserverImpl() { Cleanup(); }
|
| +
|
| + // aura::client::ActivationChangeObserver:
|
| + virtual void OnWindowActivated(aura::Window* gained_active,
|
| + aura::Window* lost_active) OVERRIDE {
|
| + if (!controller_->drag_in_progress())
|
| + controller_->CancelAll();
|
| + }
|
| +
|
| + // aura::WindowObserver:
|
| + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { Cleanup(); }
|
| +
|
| + // ui::EventHandler:
|
| + virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE {
|
| + controller_->CancelAll();
|
| + }
|
| +
|
| + private:
|
| + void Cleanup() {
|
| + if (!root_)
|
| + return;
|
| + // The ActivationClient may have been destroyed by the time we get here.
|
| + aura::client::ActivationClient* client =
|
| + aura::client::GetActivationClient(root_);
|
| + if (client)
|
| + client->RemoveObserver(this);
|
| + root_->RemovePreTargetHandler(this);
|
| + root_->RemoveObserver(this);
|
| + root_ = NULL;
|
| + }
|
| +
|
| + MenuController* controller_;
|
| + aura::Window* root_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +MenuMessageLoop* MenuMessageLoop::Create() {
|
| + return new MenuMessageLoopAura;
|
| +}
|
| +
|
| +MenuMessageLoopAura::MenuMessageLoopAura()
|
| + : owner_(NULL),
|
| + message_loop_depth_(0) {
|
| +}
|
| +
|
| +MenuMessageLoopAura::~MenuMessageLoopAura() {}
|
| +
|
| +void MenuMessageLoopAura::RepostEventToWindow(const ui::LocatedEvent& event,
|
| + gfx::NativeWindow window,
|
| + gfx::Point screen_loc) {
|
| + aura::Window* root = window->GetRootWindow();
|
| + ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root);
|
| + if (!spc)
|
| + return;
|
| +
|
| + gfx::Point root_loc(screen_loc);
|
| + spc->ConvertPointFromScreen(root, &root_loc);
|
| +
|
| + ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event));
|
| + clone.set_location(root_loc);
|
| + clone.set_root_location(root_loc);
|
| + root->GetHost()->dispatcher()->RepostEvent(clone);
|
| +}
|
| +
|
| +void MenuMessageLoopAura::Run(MenuController* controller,
|
| + Widget* owner,
|
| + bool nested_menu) {
|
| +
|
| + // We need to turn on nestable tasks as in some situations (pressing alt-f for
|
| + // one) the menus are run from a task. If we don't do this and are invoked
|
| + // from a task none of the tasks we schedule are processed and the menu
|
| + // appears totally broken.
|
| + message_loop_depth_++;
|
| + DCHECK_LE(message_loop_depth_, 2);
|
| +
|
| + // |owner_| may be NULL.
|
| + owner_ = owner;
|
| + aura::Window* root = GetOwnerRootWindow(owner_);
|
| +
|
| +#if defined(OS_WIN)
|
| + internal::MenuMessagePumpDispatcher nested_dispatcher(controller);
|
| + if (root) {
|
| + scoped_ptr<ActivationChangeObserverImpl> observer;
|
| + if (!nested_menu)
|
| + observer.reset(new ActivationChangeObserverImpl(controller, root));
|
| + aura::client::GetDispatcherClient(root)
|
| + ->RunWithDispatcher(&nested_dispatcher);
|
| + } else {
|
| + base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
|
| + base::MessageLoop::ScopedNestableTaskAllower allow(loop);
|
| + base::RunLoop run_loop(&nested_dispatcher);
|
| + run_loop.Run();
|
| + }
|
| +#else
|
| + internal::MenuEventDispatcher event_dispatcher(controller);
|
| + scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
|
| + nested_dispatcher_.Pass();
|
| + if (ui::PlatformEventSource::GetInstance()) {
|
| + nested_dispatcher_ =
|
| + ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
|
| + &event_dispatcher);
|
| + }
|
| + if (root) {
|
| + scoped_ptr<ActivationChangeObserverImpl> observer;
|
| + if (!nested_menu)
|
| + observer.reset(new ActivationChangeObserverImpl(controller, root));
|
| + aura::client::GetDispatcherClient(root)->RunWithDispatcher(NULL);
|
| + } else {
|
| + base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
|
| + base::MessageLoop::ScopedNestableTaskAllower allow(loop);
|
| + base::RunLoop run_loop;
|
| + run_loop.Run();
|
| + }
|
| + nested_dispatcher_ = old_dispatcher.Pass();
|
| +#endif
|
| +
|
| + message_loop_depth_--;
|
| + if (message_loop_depth_ == 0)
|
| + owner_ = NULL;
|
| +}
|
| +
|
| +bool MenuMessageLoopAura::ShouldQuitNow() const {
|
| + aura::Window* root = GetOwnerRootWindow(owner_);
|
| + return !aura::client::GetDragDropClient(root) ||
|
| + !aura::client::GetDragDropClient(root)->IsDragDropInProgress();
|
| +}
|
| +
|
| +void MenuMessageLoopAura::QuitNow() {
|
| + if (owner_) {
|
| + // It's safe to invoke QuitNestedMessageLoop() multiple times, it only
|
| + // effects the current loop.
|
| + aura::Window* root = owner_->GetNativeWindow()->GetRootWindow();
|
| + aura::client::GetDispatcherClient(root)->QuitNestedMessageLoop();
|
| + } else {
|
| + base::MessageLoop::current()->QuitNow();
|
| + }
|
| + // Restore the previous dispatcher.
|
| + nested_dispatcher_.reset();
|
| +}
|
| +
|
| +int MenuMessageLoopAura::message_loop_depth() const OVERRIDE {
|
| + return message_loop_depth_;
|
| +}
|
| +
|
| +} // namespace views
|
|
|