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
3 // found in the LICENSE file.
5 #include "ui/wm/core/window_modality_controller.h"
9 #include "ui/aura/client/aura_constants.h"
10 #include "ui/aura/client/capture_client.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_event_dispatcher.h"
14 #include "ui/aura/window_property.h"
15 #include "ui/base/ui_base_types.h"
16 #include "ui/events/event.h"
17 #include "ui/events/event_target.h"
18 #include "ui/events/gestures/gesture_recognizer.h"
19 #include "ui/wm/core/window_animations.h"
20 #include "ui/wm/core/window_util.h"
24 // Transient child's modal parent.
25 extern const aura::WindowProperty
<aura::Window
*>* const kModalParentKey
;
26 DEFINE_WINDOW_PROPERTY_KEY(aura::Window
*, kModalParentKey
, NULL
);
30 bool HasAncestor(aura::Window
* window
, aura::Window
* ancestor
) {
33 if (window
== ancestor
)
35 return HasAncestor(window
->parent(), ancestor
);
38 bool TransientChildIsWindowModal(aura::Window
* window
) {
39 return window
->GetProperty(aura::client::kModalKey
) == ui::MODAL_TYPE_WINDOW
;
42 bool TransientChildIsSystemModal(aura::Window
* window
) {
43 return window
->GetProperty(aura::client::kModalKey
) == ui::MODAL_TYPE_SYSTEM
;
46 bool TransientChildIsChildModal(aura::Window
* window
) {
47 return window
->GetProperty(aura::client::kModalKey
) == ui::MODAL_TYPE_CHILD
;
50 aura::Window
* GetModalParent(aura::Window
* window
) {
51 return window
->GetProperty(kModalParentKey
);
54 bool IsModalTransientChild(aura::Window
* transient
, aura::Window
* original
) {
55 return transient
->IsVisible() &&
56 (TransientChildIsWindowModal(transient
) ||
57 TransientChildIsSystemModal(transient
) ||
58 (TransientChildIsChildModal(transient
) &&
59 (HasAncestor(original
, GetModalParent(transient
)))));
62 aura::Window
* GetModalTransientChild(
63 aura::Window
* activatable
,
64 aura::Window
* original
) {
65 for (aura::Window::Windows::const_iterator it
=
66 GetTransientChildren(activatable
).begin();
67 it
!= GetTransientChildren(activatable
).end();
69 aura::Window
* transient
= *it
;
70 if (IsModalTransientChild(transient
, original
)) {
71 if (GetTransientChildren(transient
).empty())
74 aura::Window
* modal_child
= GetModalTransientChild(transient
, original
);
75 return modal_child
? modal_child
: transient
;
83 void SetModalParent(aura::Window
* child
, aura::Window
* parent
) {
84 child
->SetProperty(kModalParentKey
, parent
);
87 aura::Window
* GetModalTransient(aura::Window
* window
) {
91 // We always want to check the for the transient child of the toplevel window.
92 aura::Window
* toplevel
= GetToplevelWindow(window
);
96 return GetModalTransientChild(toplevel
, window
);
99 ////////////////////////////////////////////////////////////////////////////////
100 // WindowModalityController, public:
102 WindowModalityController::WindowModalityController(
103 ui::EventTarget
* event_target
)
104 : event_target_(event_target
) {
105 aura::Env::GetInstance()->AddObserver(this);
106 DCHECK(event_target
->IsPreTargetListEmpty());
107 event_target_
->AddPreTargetHandler(this);
110 WindowModalityController::~WindowModalityController() {
111 event_target_
->RemovePreTargetHandler(this);
112 aura::Env::GetInstance()->RemoveObserver(this);
113 for (size_t i
= 0; i
< windows_
.size(); ++i
)
114 windows_
[i
]->RemoveObserver(this);
117 ////////////////////////////////////////////////////////////////////////////////
118 // WindowModalityController, aura::EventFilter implementation:
120 void WindowModalityController::OnKeyEvent(ui::KeyEvent
* event
) {
121 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
122 if (GetModalTransient(target
))
126 void WindowModalityController::OnMouseEvent(ui::MouseEvent
* event
) {
127 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
128 if (ProcessLocatedEvent(target
, event
))
132 void WindowModalityController::OnTouchEvent(ui::TouchEvent
* event
) {
133 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
134 if (ProcessLocatedEvent(target
, event
))
138 ////////////////////////////////////////////////////////////////////////////////
139 // WindowModalityController, aura::EnvObserver implementation:
141 void WindowModalityController::OnWindowInitialized(aura::Window
* window
) {
142 windows_
.push_back(window
);
143 window
->AddObserver(this);
146 ////////////////////////////////////////////////////////////////////////////////
147 // WindowModalityController, aura::WindowObserver implementation:
149 void WindowModalityController::OnWindowPropertyChanged(aura::Window
* window
,
152 // In tests, we sometimes create the modality relationship after a window is
154 if (key
== aura::client::kModalKey
&&
155 window
->GetProperty(aura::client::kModalKey
) != ui::MODAL_TYPE_NONE
&&
156 window
->IsVisible()) {
157 ActivateWindow(window
);
158 ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
162 void WindowModalityController::OnWindowVisibilityChanged(
163 aura::Window
* window
,
165 if (visible
&& window
->GetProperty(aura::client::kModalKey
) !=
166 ui::MODAL_TYPE_NONE
) {
167 ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
168 // Make sure no other window has capture, otherwise |window| won't get mouse
170 aura::Window
* capture_window
= aura::client::GetCaptureWindow(window
);
172 capture_window
->ReleaseCapture();
176 void WindowModalityController::OnWindowDestroyed(aura::Window
* window
) {
177 windows_
.erase(std::find(windows_
.begin(), windows_
.end(), window
));
178 window
->RemoveObserver(this);
181 bool WindowModalityController::ProcessLocatedEvent(aura::Window
* target
,
182 ui::LocatedEvent
* event
) {
183 if (event
->handled())
185 aura::Window
* modal_transient_child
= GetModalTransient(target
);
186 if (modal_transient_child
&& (event
->type() == ui::ET_MOUSE_PRESSED
||
187 event
->type() == ui::ET_TOUCH_PRESSED
)) {
188 // Activate top window if transient child window is window modal.
189 if (TransientChildIsWindowModal(modal_transient_child
)) {
190 aura::Window
* toplevel
= GetToplevelWindow(target
);
192 ActivateWindow(toplevel
);
195 AnimateWindow(modal_transient_child
, WINDOW_ANIMATION_TYPE_BOUNCE
);
197 if (event
->type() == ui::ET_TOUCH_CANCELLED
)
199 return !!modal_transient_child
;