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