| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "components/mus/public/cpp/window.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <set> | |
| 11 #include <string> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/macros.h" | |
| 15 #include "components/mus/common/transient_window_utils.h" | |
| 16 #include "components/mus/public/cpp/lib/window_private.h" | |
| 17 #include "components/mus/public/cpp/property_type_converters.h" | |
| 18 #include "components/mus/public/cpp/window_observer.h" | |
| 19 #include "components/mus/public/cpp/window_property.h" | |
| 20 #include "components/mus/public/cpp/window_surface.h" | |
| 21 #include "components/mus/public/cpp/window_tracker.h" | |
| 22 #include "components/mus/public/cpp/window_tree_client.h" | |
| 23 #include "components/mus/public/interfaces/window_manager.mojom.h" | |
| 24 #include "ui/display/display.h" | |
| 25 #include "ui/gfx/geometry/rect.h" | |
| 26 #include "ui/gfx/geometry/size.h" | |
| 27 | |
| 28 namespace mus { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 void NotifyWindowTreeChangeAtReceiver( | |
| 33 Window* receiver, | |
| 34 const WindowObserver::TreeChangeParams& params, | |
| 35 bool change_applied) { | |
| 36 WindowObserver::TreeChangeParams local_params = params; | |
| 37 local_params.receiver = receiver; | |
| 38 if (change_applied) { | |
| 39 FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(), | |
| 40 OnTreeChanged(local_params)); | |
| 41 } else { | |
| 42 FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(), | |
| 43 OnTreeChanging(local_params)); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 void NotifyWindowTreeChangeUp(Window* start_at, | |
| 48 const WindowObserver::TreeChangeParams& params, | |
| 49 bool change_applied) { | |
| 50 for (Window* current = start_at; current; current = current->parent()) | |
| 51 NotifyWindowTreeChangeAtReceiver(current, params, change_applied); | |
| 52 } | |
| 53 | |
| 54 void NotifyWindowTreeChangeDown(Window* start_at, | |
| 55 const WindowObserver::TreeChangeParams& params, | |
| 56 bool change_applied) { | |
| 57 NotifyWindowTreeChangeAtReceiver(start_at, params, change_applied); | |
| 58 Window::Children::const_iterator it = start_at->children().begin(); | |
| 59 for (; it != start_at->children().end(); ++it) | |
| 60 NotifyWindowTreeChangeDown(*it, params, change_applied); | |
| 61 } | |
| 62 | |
| 63 void NotifyWindowTreeChange(const WindowObserver::TreeChangeParams& params, | |
| 64 bool change_applied) { | |
| 65 NotifyWindowTreeChangeDown(params.target, params, change_applied); | |
| 66 if (params.old_parent) | |
| 67 NotifyWindowTreeChangeUp(params.old_parent, params, change_applied); | |
| 68 if (params.new_parent) | |
| 69 NotifyWindowTreeChangeUp(params.new_parent, params, change_applied); | |
| 70 } | |
| 71 | |
| 72 class ScopedTreeNotifier { | |
| 73 public: | |
| 74 ScopedTreeNotifier(Window* target, Window* old_parent, Window* new_parent) { | |
| 75 params_.target = target; | |
| 76 params_.old_parent = old_parent; | |
| 77 params_.new_parent = new_parent; | |
| 78 NotifyWindowTreeChange(params_, false); | |
| 79 } | |
| 80 ~ScopedTreeNotifier() { NotifyWindowTreeChange(params_, true); } | |
| 81 | |
| 82 private: | |
| 83 WindowObserver::TreeChangeParams params_; | |
| 84 | |
| 85 DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); | |
| 86 }; | |
| 87 | |
| 88 void RemoveChildImpl(Window* child, Window::Children* children) { | |
| 89 Window::Children::iterator it = | |
| 90 std::find(children->begin(), children->end(), child); | |
| 91 if (it != children->end()) { | |
| 92 children->erase(it); | |
| 93 WindowPrivate(child).ClearParent(); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 class OrderChangedNotifier { | |
| 98 public: | |
| 99 OrderChangedNotifier(Window* window, | |
| 100 Window* relative_window, | |
| 101 mojom::OrderDirection direction) | |
| 102 : window_(window), | |
| 103 relative_window_(relative_window), | |
| 104 direction_(direction) {} | |
| 105 | |
| 106 ~OrderChangedNotifier() {} | |
| 107 | |
| 108 void NotifyWindowReordering() { | |
| 109 FOR_EACH_OBSERVER( | |
| 110 WindowObserver, *WindowPrivate(window_).observers(), | |
| 111 OnWindowReordering(window_, relative_window_, direction_)); | |
| 112 } | |
| 113 | |
| 114 void NotifyWindowReordered() { | |
| 115 FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(), | |
| 116 OnWindowReordered(window_, relative_window_, direction_)); | |
| 117 } | |
| 118 | |
| 119 private: | |
| 120 Window* window_; | |
| 121 Window* relative_window_; | |
| 122 mojom::OrderDirection direction_; | |
| 123 | |
| 124 DISALLOW_COPY_AND_ASSIGN(OrderChangedNotifier); | |
| 125 }; | |
| 126 | |
| 127 class ScopedSetBoundsNotifier { | |
| 128 public: | |
| 129 ScopedSetBoundsNotifier(Window* window, | |
| 130 const gfx::Rect& old_bounds, | |
| 131 const gfx::Rect& new_bounds) | |
| 132 : window_(window), old_bounds_(old_bounds), new_bounds_(new_bounds) { | |
| 133 FOR_EACH_OBSERVER( | |
| 134 WindowObserver, *WindowPrivate(window_).observers(), | |
| 135 OnWindowBoundsChanging(window_, old_bounds_, new_bounds_)); | |
| 136 } | |
| 137 ~ScopedSetBoundsNotifier() { | |
| 138 FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(), | |
| 139 OnWindowBoundsChanged(window_, old_bounds_, new_bounds_)); | |
| 140 } | |
| 141 | |
| 142 private: | |
| 143 Window* window_; | |
| 144 const gfx::Rect old_bounds_; | |
| 145 const gfx::Rect new_bounds_; | |
| 146 | |
| 147 DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); | |
| 148 }; | |
| 149 | |
| 150 // Some operations are only permitted in the client that created the window. | |
| 151 bool OwnsWindow(WindowTreeClient* client, Window* window) { | |
| 152 return !client || client->OwnsWindow(window); | |
| 153 } | |
| 154 | |
| 155 bool IsClientRoot(Window* window) { | |
| 156 return window->window_tree() && | |
| 157 window->window_tree()->GetRoots().count(window) > 0; | |
| 158 } | |
| 159 | |
| 160 bool OwnsWindowOrIsRoot(Window* window) { | |
| 161 return OwnsWindow(window->window_tree(), window) || IsClientRoot(window); | |
| 162 } | |
| 163 | |
| 164 void EmptyEmbedCallback(bool result) {} | |
| 165 | |
| 166 } // namespace | |
| 167 | |
| 168 //////////////////////////////////////////////////////////////////////////////// | |
| 169 // Window, public: | |
| 170 | |
| 171 void Window::Destroy() { | |
| 172 if (!OwnsWindowOrIsRoot(this)) | |
| 173 return; | |
| 174 | |
| 175 if (client_) | |
| 176 client_->DestroyWindow(this); | |
| 177 while (!children_.empty()) { | |
| 178 Window* child = children_.front(); | |
| 179 if (!OwnsWindow(client_, child)) { | |
| 180 WindowPrivate(child).ClearParent(); | |
| 181 children_.erase(children_.begin()); | |
| 182 } else { | |
| 183 child->Destroy(); | |
| 184 DCHECK(std::find(children_.begin(), children_.end(), child) == | |
| 185 children_.end()); | |
| 186 } | |
| 187 } | |
| 188 LocalDestroy(); | |
| 189 } | |
| 190 | |
| 191 void Window::SetBounds(const gfx::Rect& bounds) { | |
| 192 if (!OwnsWindowOrIsRoot(this)) | |
| 193 return; | |
| 194 if (bounds_ == bounds) | |
| 195 return; | |
| 196 if (client_) | |
| 197 client_->SetBounds(this, bounds_, bounds); | |
| 198 LocalSetBounds(bounds_, bounds); | |
| 199 } | |
| 200 | |
| 201 gfx::Rect Window::GetBoundsInRoot() const { | |
| 202 gfx::Vector2d offset; | |
| 203 for (const Window* w = parent(); w != nullptr; w = w->parent()) | |
| 204 offset += w->bounds().OffsetFromOrigin(); | |
| 205 return bounds() + offset; | |
| 206 } | |
| 207 | |
| 208 void Window::SetClientArea( | |
| 209 const gfx::Insets& client_area, | |
| 210 const std::vector<gfx::Rect>& additional_client_areas) { | |
| 211 if (!OwnsWindowOrIsRoot(this)) | |
| 212 return; | |
| 213 | |
| 214 if (client_) | |
| 215 client_->SetClientArea(server_id_, client_area, | |
| 216 additional_client_areas); | |
| 217 LocalSetClientArea(client_area, additional_client_areas); | |
| 218 } | |
| 219 | |
| 220 void Window::SetHitTestMask(const gfx::Rect& mask) { | |
| 221 if (!OwnsWindowOrIsRoot(this)) | |
| 222 return; | |
| 223 | |
| 224 if (hit_test_mask_ && *hit_test_mask_ == mask) | |
| 225 return; | |
| 226 | |
| 227 if (client_) | |
| 228 client_->SetHitTestMask(server_id_, mask); | |
| 229 hit_test_mask_.reset(new gfx::Rect(mask)); | |
| 230 } | |
| 231 | |
| 232 void Window::ClearHitTestMask() { | |
| 233 if (!OwnsWindowOrIsRoot(this)) | |
| 234 return; | |
| 235 | |
| 236 if (!hit_test_mask_) | |
| 237 return; | |
| 238 | |
| 239 if (client_) | |
| 240 client_->ClearHitTestMask(server_id_); | |
| 241 hit_test_mask_.reset(); | |
| 242 } | |
| 243 | |
| 244 void Window::SetVisible(bool value) { | |
| 245 if (visible_ == value) | |
| 246 return; | |
| 247 | |
| 248 if (client_) | |
| 249 client_->SetVisible(this, value); | |
| 250 LocalSetVisible(value); | |
| 251 } | |
| 252 | |
| 253 void Window::SetOpacity(float opacity) { | |
| 254 if (client_) | |
| 255 client_->SetOpacity(this, opacity); | |
| 256 LocalSetOpacity(opacity); | |
| 257 } | |
| 258 | |
| 259 void Window::SetPredefinedCursor(mus::mojom::Cursor cursor_id) { | |
| 260 if (cursor_id_ == cursor_id) | |
| 261 return; | |
| 262 | |
| 263 if (client_) | |
| 264 client_->SetPredefinedCursor(server_id_, cursor_id); | |
| 265 LocalSetPredefinedCursor(cursor_id); | |
| 266 } | |
| 267 | |
| 268 bool Window::IsDrawn() const { | |
| 269 if (!visible_) | |
| 270 return false; | |
| 271 return parent_ ? parent_->IsDrawn() : parent_drawn_; | |
| 272 } | |
| 273 | |
| 274 std::unique_ptr<WindowSurface> Window::RequestSurface(mojom::SurfaceType type) { | |
| 275 std::unique_ptr<WindowSurfaceBinding> surface_binding; | |
| 276 std::unique_ptr<WindowSurface> surface = | |
| 277 WindowSurface::Create(&surface_binding); | |
| 278 AttachSurface(type, std::move(surface_binding)); | |
| 279 return surface; | |
| 280 } | |
| 281 | |
| 282 void Window::AttachSurface( | |
| 283 mojom::SurfaceType type, | |
| 284 std::unique_ptr<WindowSurfaceBinding> surface_binding) { | |
| 285 window_tree()->AttachSurface( | |
| 286 server_id_, type, std::move(surface_binding->surface_request_), | |
| 287 mojo::MakeProxy(std::move(surface_binding->surface_client_))); | |
| 288 } | |
| 289 | |
| 290 void Window::ClearSharedProperty(const std::string& name) { | |
| 291 SetSharedPropertyInternal(name, nullptr); | |
| 292 } | |
| 293 | |
| 294 bool Window::HasSharedProperty(const std::string& name) const { | |
| 295 return properties_.count(name) > 0; | |
| 296 } | |
| 297 | |
| 298 void Window::AddObserver(WindowObserver* observer) { | |
| 299 observers_.AddObserver(observer); | |
| 300 } | |
| 301 | |
| 302 void Window::RemoveObserver(WindowObserver* observer) { | |
| 303 observers_.RemoveObserver(observer); | |
| 304 } | |
| 305 | |
| 306 const Window* Window::GetRoot() const { | |
| 307 const Window* root = this; | |
| 308 for (const Window* parent = this; parent; parent = parent->parent()) | |
| 309 root = parent; | |
| 310 return root; | |
| 311 } | |
| 312 | |
| 313 void Window::AddChild(Window* child) { | |
| 314 // TODO(beng): not necessarily valid to all clients, but possibly to the | |
| 315 // embeddee in an embedder-embeddee relationship. | |
| 316 if (client_) | |
| 317 CHECK_EQ(child->client_, client_); | |
| 318 // Roots can not be added as children of other windows. | |
| 319 if (window_tree() && window_tree()->IsRoot(child)) | |
| 320 return; | |
| 321 LocalAddChild(child); | |
| 322 if (client_) | |
| 323 client_->AddChild(this, child->server_id()); | |
| 324 } | |
| 325 | |
| 326 void Window::RemoveChild(Window* child) { | |
| 327 // TODO(beng): not necessarily valid to all clients, but possibly to the | |
| 328 // embeddee in an embedder-embeddee relationship. | |
| 329 if (client_) | |
| 330 CHECK_EQ(child->client_, client_); | |
| 331 LocalRemoveChild(child); | |
| 332 if (client_) | |
| 333 client_->RemoveChild(this, child->server_id()); | |
| 334 } | |
| 335 | |
| 336 void Window::Reorder(Window* relative, mojom::OrderDirection direction) { | |
| 337 if (!LocalReorder(relative, direction)) | |
| 338 return; | |
| 339 if (client_) | |
| 340 client_->Reorder(this, relative->server_id(), direction); | |
| 341 } | |
| 342 | |
| 343 void Window::MoveToFront() { | |
| 344 if (!parent_ || parent_->children_.back() == this) | |
| 345 return; | |
| 346 Reorder(parent_->children_.back(), mojom::OrderDirection::ABOVE); | |
| 347 } | |
| 348 | |
| 349 void Window::MoveToBack() { | |
| 350 if (!parent_ || parent_->children_.front() == this) | |
| 351 return; | |
| 352 Reorder(parent_->children_.front(), mojom::OrderDirection::BELOW); | |
| 353 } | |
| 354 | |
| 355 bool Window::Contains(const Window* child) const { | |
| 356 if (!child) | |
| 357 return false; | |
| 358 if (child == this) | |
| 359 return true; | |
| 360 if (client_) | |
| 361 CHECK_EQ(child->client_, client_); | |
| 362 for (const Window* p = child->parent(); p; p = p->parent()) { | |
| 363 if (p == this) | |
| 364 return true; | |
| 365 } | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 void Window::AddTransientWindow(Window* transient_window) { | |
| 370 // A system modal window cannot become a transient child. | |
| 371 DCHECK(!transient_window->is_modal() || transient_window->transient_parent()); | |
| 372 | |
| 373 if (client_) | |
| 374 CHECK_EQ(transient_window->client_, client_); | |
| 375 LocalAddTransientWindow(transient_window); | |
| 376 if (client_) | |
| 377 client_->AddTransientWindow(this, transient_window->server_id()); | |
| 378 } | |
| 379 | |
| 380 void Window::RemoveTransientWindow(Window* transient_window) { | |
| 381 if (client_) | |
| 382 CHECK_EQ(transient_window->window_tree(), client_); | |
| 383 LocalRemoveTransientWindow(transient_window); | |
| 384 if (client_) | |
| 385 client_->RemoveTransientWindowFromParent(transient_window); | |
| 386 } | |
| 387 | |
| 388 void Window::SetModal() { | |
| 389 if (is_modal_) | |
| 390 return; | |
| 391 | |
| 392 LocalSetModal(); | |
| 393 if (client_) | |
| 394 client_->SetModal(this); | |
| 395 } | |
| 396 | |
| 397 Window* Window::GetChildByLocalId(int id) { | |
| 398 if (id == local_id_) | |
| 399 return this; | |
| 400 // TODO(beng): this could be improved depending on how we decide to own | |
| 401 // windows. | |
| 402 for (Window* child : children_) { | |
| 403 Window* matching_child = child->GetChildByLocalId(id); | |
| 404 if (matching_child) | |
| 405 return matching_child; | |
| 406 } | |
| 407 return nullptr; | |
| 408 } | |
| 409 | |
| 410 void Window::SetTextInputState(mojo::TextInputStatePtr state) { | |
| 411 if (client_) | |
| 412 client_->SetWindowTextInputState(server_id_, std::move(state)); | |
| 413 } | |
| 414 | |
| 415 void Window::SetImeVisibility(bool visible, mojo::TextInputStatePtr state) { | |
| 416 // SetImeVisibility() shouldn't be used if the window is not editable. | |
| 417 DCHECK(state.is_null() || state->type != mojo::TextInputType::NONE); | |
| 418 if (client_) | |
| 419 client_->SetImeVisibility(server_id_, visible, std::move(state)); | |
| 420 } | |
| 421 | |
| 422 bool Window::HasCapture() const { | |
| 423 return client_ && client_->GetCaptureWindow() == this; | |
| 424 } | |
| 425 | |
| 426 void Window::SetCapture() { | |
| 427 if (client_) | |
| 428 client_->SetCapture(this); | |
| 429 } | |
| 430 | |
| 431 void Window::ReleaseCapture() { | |
| 432 if (client_) | |
| 433 client_->ReleaseCapture(this); | |
| 434 } | |
| 435 | |
| 436 void Window::SetFocus() { | |
| 437 if (client_ && IsDrawn()) | |
| 438 client_->SetFocus(this); | |
| 439 } | |
| 440 | |
| 441 bool Window::HasFocus() const { | |
| 442 return client_ && client_->GetFocusedWindow() == this; | |
| 443 } | |
| 444 | |
| 445 void Window::SetCanFocus(bool can_focus) { | |
| 446 if (client_) | |
| 447 client_->SetCanFocus(server_id_, can_focus); | |
| 448 } | |
| 449 | |
| 450 void Window::Embed(mus::mojom::WindowTreeClientPtr client, uint32_t flags) { | |
| 451 Embed(std::move(client), base::Bind(&EmptyEmbedCallback), flags); | |
| 452 } | |
| 453 | |
| 454 void Window::Embed(mus::mojom::WindowTreeClientPtr client, | |
| 455 const EmbedCallback& callback, | |
| 456 uint32_t flags) { | |
| 457 if (PrepareForEmbed()) | |
| 458 client_->Embed(server_id_, std::move(client), flags, callback); | |
| 459 else | |
| 460 callback.Run(false); | |
| 461 } | |
| 462 | |
| 463 void Window::RequestClose() { | |
| 464 if (client_) | |
| 465 client_->RequestClose(this); | |
| 466 } | |
| 467 | |
| 468 std::string Window::GetName() const { | |
| 469 if (HasSharedProperty(mojom::WindowManager::kName_Property)) | |
| 470 return GetSharedProperty<std::string>(mojom::WindowManager::kName_Property); | |
| 471 | |
| 472 return std::string(); | |
| 473 } | |
| 474 | |
| 475 //////////////////////////////////////////////////////////////////////////////// | |
| 476 // Window, protected: | |
| 477 | |
| 478 Window::Window() : Window(nullptr, static_cast<Id>(-1)) {} | |
| 479 | |
| 480 Window::~Window() { | |
| 481 FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this)); | |
| 482 if (client_) | |
| 483 client_->OnWindowDestroying(this); | |
| 484 | |
| 485 if (HasFocus()) { | |
| 486 // The focused window is being removed. When this happens the server | |
| 487 // advances focus. We don't want to randomly pick a Window to get focus, so | |
| 488 // we update local state only, and wait for the next focus change from the | |
| 489 // server. | |
| 490 client_->LocalSetFocus(nullptr); | |
| 491 } | |
| 492 | |
| 493 // Remove from transient parent. | |
| 494 if (transient_parent_) | |
| 495 transient_parent_->LocalRemoveTransientWindow(this); | |
| 496 | |
| 497 // Remove transient children. | |
| 498 while (!transient_children_.empty()) { | |
| 499 Window* transient_child = transient_children_.front(); | |
| 500 LocalRemoveTransientWindow(transient_child); | |
| 501 transient_child->LocalDestroy(); | |
| 502 DCHECK(transient_children_.empty() || | |
| 503 transient_children_.front() != transient_child); | |
| 504 } | |
| 505 | |
| 506 if (parent_) | |
| 507 parent_->LocalRemoveChild(this); | |
| 508 | |
| 509 // We may still have children. This can happen if the embedder destroys the | |
| 510 // root while we're still alive. | |
| 511 while (!children_.empty()) { | |
| 512 Window* child = children_.front(); | |
| 513 LocalRemoveChild(child); | |
| 514 DCHECK(children_.empty() || children_.front() != child); | |
| 515 } | |
| 516 | |
| 517 // Clear properties. | |
| 518 for (auto& pair : prop_map_) { | |
| 519 if (pair.second.deallocator) | |
| 520 (*pair.second.deallocator)(pair.second.value); | |
| 521 } | |
| 522 prop_map_.clear(); | |
| 523 | |
| 524 FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this)); | |
| 525 | |
| 526 // Invoke after observers so that can clean up any internal state observers | |
| 527 // may have changed. | |
| 528 if (window_tree()) | |
| 529 window_tree()->OnWindowDestroyed(this); | |
| 530 } | |
| 531 | |
| 532 //////////////////////////////////////////////////////////////////////////////// | |
| 533 // Window, private: | |
| 534 | |
| 535 Window::Window(WindowTreeClient* client, Id id) | |
| 536 : client_(client), | |
| 537 server_id_(id), | |
| 538 parent_(nullptr), | |
| 539 stacking_target_(nullptr), | |
| 540 transient_parent_(nullptr), | |
| 541 is_modal_(false), | |
| 542 // Matches aura, see aura::Window for details. | |
| 543 observers_(base::ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY), | |
| 544 input_event_handler_(nullptr), | |
| 545 visible_(false), | |
| 546 opacity_(1.0f), | |
| 547 display_id_(display::Display::kInvalidDisplayID), | |
| 548 cursor_id_(mojom::Cursor::CURSOR_NULL), | |
| 549 parent_drawn_(false) {} | |
| 550 | |
| 551 void Window::SetSharedPropertyInternal(const std::string& name, | |
| 552 const std::vector<uint8_t>* value) { | |
| 553 if (!OwnsWindowOrIsRoot(this)) | |
| 554 return; | |
| 555 | |
| 556 if (client_) { | |
| 557 mojo::Array<uint8_t> transport_value(nullptr); | |
| 558 if (value) { | |
| 559 transport_value.resize(value->size()); | |
| 560 if (value->size()) | |
| 561 memcpy(&transport_value.front(), &(value->front()), value->size()); | |
| 562 } | |
| 563 // TODO: add test coverage of this (450303). | |
| 564 client_->SetProperty(this, name, std::move(transport_value)); | |
| 565 } | |
| 566 LocalSetSharedProperty(name, value); | |
| 567 } | |
| 568 | |
| 569 int64_t Window::SetLocalPropertyInternal(const void* key, | |
| 570 const char* name, | |
| 571 PropertyDeallocator deallocator, | |
| 572 int64_t value, | |
| 573 int64_t default_value) { | |
| 574 int64_t old = GetLocalPropertyInternal(key, default_value); | |
| 575 if (value == default_value) { | |
| 576 prop_map_.erase(key); | |
| 577 } else { | |
| 578 Value prop_value; | |
| 579 prop_value.name = name; | |
| 580 prop_value.value = value; | |
| 581 prop_value.deallocator = deallocator; | |
| 582 prop_map_[key] = prop_value; | |
| 583 } | |
| 584 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 585 OnWindowLocalPropertyChanged(this, key, old)); | |
| 586 return old; | |
| 587 } | |
| 588 | |
| 589 int64_t Window::GetLocalPropertyInternal(const void* key, | |
| 590 int64_t default_value) const { | |
| 591 std::map<const void*, Value>::const_iterator iter = prop_map_.find(key); | |
| 592 if (iter == prop_map_.end()) | |
| 593 return default_value; | |
| 594 return iter->second.value; | |
| 595 } | |
| 596 | |
| 597 void Window::LocalDestroy() { | |
| 598 delete this; | |
| 599 } | |
| 600 | |
| 601 void Window::LocalAddChild(Window* child) { | |
| 602 ScopedTreeNotifier notifier(child, child->parent(), this); | |
| 603 if (child->parent()) | |
| 604 RemoveChildImpl(child, &child->parent_->children_); | |
| 605 children_.push_back(child); | |
| 606 child->parent_ = this; | |
| 607 child->display_id_ = display_id_; | |
| 608 } | |
| 609 | |
| 610 void Window::LocalRemoveChild(Window* child) { | |
| 611 DCHECK_EQ(this, child->parent()); | |
| 612 ScopedTreeNotifier notifier(child, this, nullptr); | |
| 613 RemoveChildImpl(child, &children_); | |
| 614 } | |
| 615 | |
| 616 void Window::LocalAddTransientWindow(Window* transient_window) { | |
| 617 if (transient_window->transient_parent()) | |
| 618 RemoveTransientWindowImpl(transient_window); | |
| 619 transient_children_.push_back(transient_window); | |
| 620 transient_window->transient_parent_ = this; | |
| 621 | |
| 622 // Restack |transient_window| properly above its transient parent, if they | |
| 623 // share the same parent. | |
| 624 if (transient_window->parent() == parent()) | |
| 625 RestackTransientDescendants(this, &GetStackingTarget, | |
| 626 &ReorderWithoutNotification); | |
| 627 | |
| 628 // TODO(fsamuel): We might want a notification here. | |
| 629 } | |
| 630 | |
| 631 void Window::LocalRemoveTransientWindow(Window* transient_window) { | |
| 632 DCHECK_EQ(this, transient_window->transient_parent()); | |
| 633 RemoveTransientWindowImpl(transient_window); | |
| 634 // TODO(fsamuel): We might want a notification here. | |
| 635 } | |
| 636 | |
| 637 void Window::LocalSetModal() { | |
| 638 is_modal_ = true; | |
| 639 } | |
| 640 | |
| 641 bool Window::LocalReorder(Window* relative, mojom::OrderDirection direction) { | |
| 642 OrderChangedNotifier notifier(this, relative, direction); | |
| 643 return ReorderImpl(this, relative, direction, ¬ifier); | |
| 644 } | |
| 645 | |
| 646 void Window::LocalSetBounds(const gfx::Rect& old_bounds, | |
| 647 const gfx::Rect& new_bounds) { | |
| 648 // If this client owns the window, then it should be the only one to change | |
| 649 // the bounds. | |
| 650 DCHECK(!OwnsWindow(client_, this) || old_bounds == bounds_); | |
| 651 ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); | |
| 652 bounds_ = new_bounds; | |
| 653 } | |
| 654 | |
| 655 void Window::LocalSetClientArea( | |
| 656 const gfx::Insets& new_client_area, | |
| 657 const std::vector<gfx::Rect>& additional_client_areas) { | |
| 658 const std::vector<gfx::Rect> old_additional_client_areas = | |
| 659 additional_client_areas_; | |
| 660 const gfx::Insets old_client_area = client_area_; | |
| 661 client_area_ = new_client_area; | |
| 662 additional_client_areas_ = additional_client_areas; | |
| 663 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 664 OnWindowClientAreaChanged(this, old_client_area, | |
| 665 old_additional_client_areas)); | |
| 666 } | |
| 667 | |
| 668 void Window::LocalSetDisplay(int64_t display_id) { | |
| 669 display_id_ = display_id; | |
| 670 // TODO(sad): Notify observers (of this window, and of the descendant windows) | |
| 671 // when a window moves from one display into another. https://crbug.com/614887 | |
| 672 } | |
| 673 | |
| 674 void Window::LocalSetParentDrawn(bool value) { | |
| 675 if (parent_drawn_ == value) | |
| 676 return; | |
| 677 | |
| 678 // As IsDrawn() is derived from |visible_| and |parent_drawn_|, only send | |
| 679 // drawn notification is the value of IsDrawn() is really changing. | |
| 680 if (IsDrawn() == value) { | |
| 681 parent_drawn_ = value; | |
| 682 return; | |
| 683 } | |
| 684 FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanging(this)); | |
| 685 parent_drawn_ = value; | |
| 686 FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanged(this)); | |
| 687 } | |
| 688 | |
| 689 void Window::LocalSetVisible(bool visible) { | |
| 690 if (visible_ == visible) | |
| 691 return; | |
| 692 | |
| 693 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 694 OnWindowVisibilityChanging(this)); | |
| 695 visible_ = visible; | |
| 696 NotifyWindowVisibilityChanged(this); | |
| 697 } | |
| 698 | |
| 699 void Window::LocalSetOpacity(float opacity) { | |
| 700 if (opacity_ == opacity) | |
| 701 return; | |
| 702 | |
| 703 float old_opacity = opacity_; | |
| 704 opacity_ = opacity; | |
| 705 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 706 OnWindowOpacityChanged(this, old_opacity, opacity_)); | |
| 707 } | |
| 708 | |
| 709 void Window::LocalSetPredefinedCursor(mojom::Cursor cursor_id) { | |
| 710 if (cursor_id_ == cursor_id) | |
| 711 return; | |
| 712 | |
| 713 cursor_id_ = cursor_id; | |
| 714 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 715 OnWindowPredefinedCursorChanged(this, cursor_id)); | |
| 716 } | |
| 717 | |
| 718 void Window::LocalSetSharedProperty(const std::string& name, | |
| 719 const std::vector<uint8_t>* value) { | |
| 720 std::vector<uint8_t> old_value; | |
| 721 std::vector<uint8_t>* old_value_ptr = nullptr; | |
| 722 auto it = properties_.find(name); | |
| 723 if (it != properties_.end()) { | |
| 724 old_value = it->second; | |
| 725 old_value_ptr = &old_value; | |
| 726 | |
| 727 if (value && old_value == *value) | |
| 728 return; | |
| 729 } else if (!value) { | |
| 730 // This property isn't set in |properties_| and |value| is nullptr, so | |
| 731 // there's no change. | |
| 732 return; | |
| 733 } | |
| 734 | |
| 735 if (value) { | |
| 736 properties_[name] = *value; | |
| 737 } else if (it != properties_.end()) { | |
| 738 properties_.erase(it); | |
| 739 } | |
| 740 | |
| 741 FOR_EACH_OBSERVER( | |
| 742 WindowObserver, observers_, | |
| 743 OnWindowSharedPropertyChanged(this, name, old_value_ptr, value)); | |
| 744 } | |
| 745 | |
| 746 void Window::NotifyWindowStackingChanged() { | |
| 747 if (stacking_target_) { | |
| 748 Children::const_iterator window_i = std::find( | |
| 749 parent()->children().begin(), parent()->children().end(), this); | |
| 750 DCHECK(window_i != parent()->children().end()); | |
| 751 if (window_i != parent()->children().begin() && | |
| 752 (*(window_i - 1) == stacking_target_)) | |
| 753 return; | |
| 754 } | |
| 755 RestackTransientDescendants(this, &GetStackingTarget, | |
| 756 &ReorderWithoutNotification); | |
| 757 } | |
| 758 | |
| 759 void Window::NotifyWindowVisibilityChanged(Window* target) { | |
| 760 if (!NotifyWindowVisibilityChangedDown(target)) { | |
| 761 return; // |this| has been deleted. | |
| 762 } | |
| 763 NotifyWindowVisibilityChangedUp(target); | |
| 764 } | |
| 765 | |
| 766 bool Window::NotifyWindowVisibilityChangedAtReceiver(Window* target) { | |
| 767 // |this| may be deleted during a call to OnWindowVisibilityChanged() on one | |
| 768 // of the observers. We create an local observer for that. In that case we | |
| 769 // exit without further access to any members. | |
| 770 WindowTracker tracker; | |
| 771 tracker.Add(this); | |
| 772 FOR_EACH_OBSERVER(WindowObserver, observers_, | |
| 773 OnWindowVisibilityChanged(target)); | |
| 774 return tracker.Contains(this); | |
| 775 } | |
| 776 | |
| 777 bool Window::NotifyWindowVisibilityChangedDown(Window* target) { | |
| 778 if (!NotifyWindowVisibilityChangedAtReceiver(target)) | |
| 779 return false; // |this| was deleted. | |
| 780 std::set<const Window*> child_already_processed; | |
| 781 bool child_destroyed = false; | |
| 782 do { | |
| 783 child_destroyed = false; | |
| 784 for (Window::Children::const_iterator it = children_.begin(); | |
| 785 it != children_.end(); ++it) { | |
| 786 if (!child_already_processed.insert(*it).second) | |
| 787 continue; | |
| 788 if (!(*it)->NotifyWindowVisibilityChangedDown(target)) { | |
| 789 // |*it| was deleted, |it| is invalid and |children_| has changed. We | |
| 790 // exit the current for-loop and enter a new one. | |
| 791 child_destroyed = true; | |
| 792 break; | |
| 793 } | |
| 794 } | |
| 795 } while (child_destroyed); | |
| 796 return true; | |
| 797 } | |
| 798 | |
| 799 void Window::NotifyWindowVisibilityChangedUp(Window* target) { | |
| 800 // Start with the parent as we already notified |this| | |
| 801 // in NotifyWindowVisibilityChangedDown. | |
| 802 for (Window* window = parent(); window; window = window->parent()) { | |
| 803 bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target); | |
| 804 DCHECK(ret); | |
| 805 } | |
| 806 } | |
| 807 | |
| 808 bool Window::PrepareForEmbed() { | |
| 809 if (!OwnsWindow(client_, this)) | |
| 810 return false; | |
| 811 | |
| 812 while (!children_.empty()) | |
| 813 RemoveChild(children_[0]); | |
| 814 return true; | |
| 815 } | |
| 816 | |
| 817 void Window::RemoveTransientWindowImpl(Window* transient_window) { | |
| 818 Window::Children::iterator it = std::find( | |
| 819 transient_children_.begin(), transient_children_.end(), transient_window); | |
| 820 if (it != transient_children_.end()) { | |
| 821 transient_children_.erase(it); | |
| 822 transient_window->transient_parent_ = nullptr; | |
| 823 } | |
| 824 // If |transient_window| and its former transient parent share the same | |
| 825 // parent, |transient_window| should be restacked properly so it is not among | |
| 826 // transient children of its former parent, anymore. | |
| 827 if (parent() == transient_window->parent()) | |
| 828 RestackTransientDescendants(this, &GetStackingTarget, | |
| 829 &ReorderWithoutNotification); | |
| 830 | |
| 831 // TOOD(fsamuel): We might want to notify observers here. | |
| 832 } | |
| 833 | |
| 834 // static | |
| 835 void Window::ReorderWithoutNotification(Window* window, | |
| 836 Window* relative, | |
| 837 mojom::OrderDirection direction) { | |
| 838 ReorderImpl(window, relative, direction, nullptr); | |
| 839 } | |
| 840 | |
| 841 // static | |
| 842 // Returns true if the order actually changed. | |
| 843 bool Window::ReorderImpl(Window* window, | |
| 844 Window* relative, | |
| 845 mojom::OrderDirection direction, | |
| 846 OrderChangedNotifier* notifier) { | |
| 847 DCHECK(relative); | |
| 848 DCHECK_NE(window, relative); | |
| 849 DCHECK_EQ(window->parent(), relative->parent()); | |
| 850 DCHECK(window->parent()); | |
| 851 | |
| 852 if (!AdjustStackingForTransientWindows(&window, &relative, &direction, | |
| 853 window->stacking_target_)) | |
| 854 return false; | |
| 855 | |
| 856 const size_t child_i = std::find(window->parent_->children_.begin(), | |
| 857 window->parent_->children_.end(), window) - | |
| 858 window->parent_->children_.begin(); | |
| 859 const size_t target_i = | |
| 860 std::find(window->parent_->children_.begin(), | |
| 861 window->parent_->children_.end(), relative) - | |
| 862 window->parent_->children_.begin(); | |
| 863 if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) || | |
| 864 (direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) { | |
| 865 return false; | |
| 866 } | |
| 867 | |
| 868 if (notifier) | |
| 869 notifier->NotifyWindowReordering(); | |
| 870 | |
| 871 const size_t dest_i = direction == mojom::OrderDirection::ABOVE | |
| 872 ? (child_i < target_i ? target_i : target_i + 1) | |
| 873 : (child_i < target_i ? target_i - 1 : target_i); | |
| 874 window->parent_->children_.erase(window->parent_->children_.begin() + | |
| 875 child_i); | |
| 876 window->parent_->children_.insert(window->parent_->children_.begin() + dest_i, | |
| 877 window); | |
| 878 | |
| 879 window->NotifyWindowStackingChanged(); | |
| 880 | |
| 881 if (notifier) | |
| 882 notifier->NotifyWindowReordered(); | |
| 883 | |
| 884 return true; | |
| 885 } | |
| 886 | |
| 887 // static | |
| 888 Window** Window::GetStackingTarget(Window* window) { | |
| 889 return &window->stacking_target_; | |
| 890 } | |
| 891 } // namespace mus | |
| OLD | NEW |