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. 4 5 #include "ui/wm/core/window_modality_controller.h" 6 7 #include <algorithm> 8 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" 21 22 namespace wm { 23 24 // Transient child's modal parent. 25 extern const aura::WindowProperty<aura::Window*>* const kModalParentKey; 26 DEFINE_WINDOW_PROPERTY_KEY(aura::Window*, kModalParentKey, NULL); 27 28 namespace { 29 30 bool HasAncestor(aura::Window* window, aura::Window* ancestor) { 31 if (!window) 32 return false; 33 if (window == ancestor) 34 return true; 35 return HasAncestor(window->parent(), ancestor); 36 } 37 38 bool TransientChildIsWindowModal(aura::Window* window) { 39 return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW; 40 } 41 42 bool TransientChildIsSystemModal(aura::Window* window) { 43 return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM; 44 } 45 46 bool TransientChildIsChildModal(aura::Window* window) { 47 return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_CHILD; 48 } 49 50 aura::Window* GetModalParent(aura::Window* window) { 51 return window->GetProperty(kModalParentKey); 52 } 53 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))))); 60 } 61 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(); 68 ++it) { 69 aura::Window* transient = *it; 70 if (IsModalTransientChild(transient, original)) { 71 return GetTransientChildren(transient).empty() ? 72 transient : GetModalTransientChild(transient, original); 73 } 74 } 75 return NULL; 76 } 77 78 } // namespace 79 80 void SetModalParent(aura::Window* child, aura::Window* parent) { 81 child->SetProperty(kModalParentKey, parent); 82 } 83 84 aura::Window* GetModalTransient(aura::Window* window) { 85 if (!window) 86 return NULL; 87 88 // We always want to check the for the transient child of the toplevel window. 89 aura::Window* toplevel = GetToplevelWindow(window); 90 if (!toplevel) 91 return NULL; 92 93 return GetModalTransientChild(toplevel, window); 94 } 95 96 //////////////////////////////////////////////////////////////////////////////// 97 // WindowModalityController, public: 98 99 WindowModalityController::WindowModalityController( 100 ui::EventTarget* event_target) 101 : event_target_(event_target) { 102 aura::Env::GetInstance()->AddObserver(this); 103 DCHECK(event_target->IsPreTargetListEmpty()); 104 event_target_->AddPreTargetHandler(this); 105 } 106 107 WindowModalityController::~WindowModalityController() { 108 event_target_->RemovePreTargetHandler(this); 109 aura::Env::GetInstance()->RemoveObserver(this); 110 for (size_t i = 0; i < windows_.size(); ++i) 111 windows_[i]->RemoveObserver(this); 112 } 113 114 //////////////////////////////////////////////////////////////////////////////// 115 // WindowModalityController, aura::EventFilter implementation: 116 117 void WindowModalityController::OnKeyEvent(ui::KeyEvent* event) { 118 aura::Window* target = static_cast<aura::Window*>(event->target()); 119 if (GetModalTransient(target)) 120 event->SetHandled(); 121 } 122 123 void WindowModalityController::OnMouseEvent(ui::MouseEvent* event) { 124 aura::Window* target = static_cast<aura::Window*>(event->target()); 125 if (ProcessLocatedEvent(target, event)) 126 event->SetHandled(); 127 } 128 129 void WindowModalityController::OnTouchEvent(ui::TouchEvent* event) { 130 aura::Window* target = static_cast<aura::Window*>(event->target()); 131 if (ProcessLocatedEvent(target, event)) 132 event->SetHandled(); 133 } 134 135 //////////////////////////////////////////////////////////////////////////////// 136 // WindowModalityController, aura::EnvObserver implementation: 137 138 void WindowModalityController::OnWindowInitialized(aura::Window* window) { 139 windows_.push_back(window); 140 window->AddObserver(this); 141 } 142 143 //////////////////////////////////////////////////////////////////////////////// 144 // WindowModalityController, aura::WindowObserver implementation: 145 146 void WindowModalityController::OnWindowPropertyChanged(aura::Window* window, 147 const void* key, 148 intptr_t old) { 149 // In tests, we sometimes create the modality relationship after a window is 150 // visible. 151 if (key == aura::client::kModalKey && 152 window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE && 153 window->IsVisible()) { 154 ActivateWindow(window); 155 ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window), 156 NULL); 157 } 158 } 159 160 void WindowModalityController::OnWindowVisibilityChanged( 161 aura::Window* window, 162 bool visible) { 163 if (visible && window->GetProperty(aura::client::kModalKey) != 164 ui::MODAL_TYPE_NONE) { 165 ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window), 166 NULL); 167 // Make sure no other window has capture, otherwise |window| won't get mouse 168 // events. 169 aura::Window* capture_window = aura::client::GetCaptureWindow(window); 170 if (capture_window) 171 capture_window->ReleaseCapture(); 172 } 173 } 174 175 void WindowModalityController::OnWindowDestroyed(aura::Window* window) { 176 windows_.erase(std::find(windows_.begin(), windows_.end(), window)); 177 window->RemoveObserver(this); 178 } 179 180 bool WindowModalityController::ProcessLocatedEvent(aura::Window* target, 181 ui::LocatedEvent* event) { 182 if (event->handled()) 183 return false; 184 aura::Window* modal_transient_child = GetModalTransient(target); 185 if (modal_transient_child && (event->type() == ui::ET_MOUSE_PRESSED || 186 event->type() == ui::ET_TOUCH_PRESSED)) { 187 // Activate top window if transient child window is window modal. 188 if (TransientChildIsWindowModal(modal_transient_child)) { 189 aura::Window* toplevel = GetToplevelWindow(target); 190 DCHECK(toplevel); 191 ActivateWindow(toplevel); 192 } 193 194 AnimateWindow(modal_transient_child, WINDOW_ANIMATION_TYPE_BOUNCE); 195 } 196 if (event->type() == ui::ET_TOUCH_CANCELLED) 197 return false; 198 return !!modal_transient_child; 199 } 200 201 } // namespace wm 202