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 |