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.
5 #include "mojo/services/public/cpp/view_manager/view.h"
7 #include "mojo/public/cpp/application/service_provider_impl.h"
8 #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
9 #include "mojo/services/public/cpp/view_manager/lib/view_private.h"
10 #include "mojo/services/public/cpp/view_manager/view_observer.h"
11 #include "ui/gfx/canvas.h"
17 void NotifyViewTreeChangeAtReceiver(
19 const ViewObserver::TreeChangeParams& params,
20 bool change_applied) {
21 ViewObserver::TreeChangeParams local_params = params;
22 local_params.receiver = receiver;
24 FOR_EACH_OBSERVER(ViewObserver,
25 *ViewPrivate(receiver).observers(),
26 OnTreeChanged(local_params));
28 FOR_EACH_OBSERVER(ViewObserver,
29 *ViewPrivate(receiver).observers(),
30 OnTreeChanging(local_params));
34 void NotifyViewTreeChangeUp(
36 const ViewObserver::TreeChangeParams& params,
37 bool change_applied) {
38 for (View* current = start_at; current; current = current->parent())
39 NotifyViewTreeChangeAtReceiver(current, params, change_applied);
42 void NotifyViewTreeChangeDown(
44 const ViewObserver::TreeChangeParams& params,
45 bool change_applied) {
46 NotifyViewTreeChangeAtReceiver(start_at, params, change_applied);
47 View::Children::const_iterator it = start_at->children().begin();
48 for (; it != start_at->children().end(); ++it)
49 NotifyViewTreeChangeDown(*it, params, change_applied);
52 void NotifyViewTreeChange(
53 const ViewObserver::TreeChangeParams& params,
54 bool change_applied) {
55 NotifyViewTreeChangeDown(params.target, params, change_applied);
56 if (params.old_parent)
57 NotifyViewTreeChangeUp(params.old_parent, params, change_applied);
58 if (params.new_parent)
59 NotifyViewTreeChangeUp(params.new_parent, params, change_applied);
62 class ScopedTreeNotifier {
64 ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) {
65 params_.target = target;
66 params_.old_parent = old_parent;
67 params_.new_parent = new_parent;
68 NotifyViewTreeChange(params_, false);
70 ~ScopedTreeNotifier() {
71 NotifyViewTreeChange(params_, true);
75 ViewObserver::TreeChangeParams params_;
77 DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
80 void RemoveChildImpl(View* child, View::Children* children) {
81 View::Children::iterator it =
82 std::find(children->begin(), children->end(), child);
83 if (it != children->end()) {
85 ViewPrivate(child).ClearParent();
89 class ScopedOrderChangedNotifier {
91 ScopedOrderChangedNotifier(View* view,
93 OrderDirection direction)
95 relative_view_(relative_view),
96 direction_(direction) {
97 FOR_EACH_OBSERVER(ViewObserver,
98 *ViewPrivate(view_).observers(),
99 OnViewReordering(view_, relative_view_, direction_));
101 ~ScopedOrderChangedNotifier() {
102 FOR_EACH_OBSERVER(ViewObserver,
103 *ViewPrivate(view_).observers(),
104 OnViewReordered(view_, relative_view_, direction_));
109 View* relative_view_;
110 OrderDirection direction_;
112 DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
115 // Returns true if the order actually changed.
116 bool ReorderImpl(View::Children* children,
119 OrderDirection direction) {
121 DCHECK_NE(view, relative);
122 DCHECK_EQ(view->parent(), relative->parent());
124 const size_t child_i =
125 std::find(children->begin(), children->end(), view) - children->begin();
126 const size_t target_i =
127 std::find(children->begin(), children->end(), relative) -
129 if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
130 (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
134 ScopedOrderChangedNotifier notifier(view, relative, direction);
136 const size_t dest_i = direction == ORDER_DIRECTION_ABOVE
137 ? (child_i < target_i ? target_i : target_i + 1)
138 : (child_i < target_i ? target_i - 1 : target_i);
139 children->erase(children->begin() + child_i);
140 children->insert(children->begin() + dest_i, view);
145 class ScopedSetBoundsNotifier {
147 ScopedSetBoundsNotifier(View* view,
148 const gfx::Rect& old_bounds,
149 const gfx::Rect& new_bounds)
151 old_bounds_(old_bounds),
152 new_bounds_(new_bounds) {
153 FOR_EACH_OBSERVER(ViewObserver,
154 *ViewPrivate(view_).observers(),
155 OnViewBoundsChanging(view_, old_bounds_, new_bounds_));
157 ~ScopedSetBoundsNotifier() {
158 FOR_EACH_OBSERVER(ViewObserver,
159 *ViewPrivate(view_).observers(),
160 OnViewBoundsChanged(view_, old_bounds_, new_bounds_));
165 const gfx::Rect old_bounds_;
166 const gfx::Rect new_bounds_;
168 DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
171 // Some operations are only permitted in the connection that created the view.
172 bool OwnsView(ViewManager* manager, View* view) {
174 static_cast<ViewManagerClientImpl*>(manager)->OwnsView(view->id());
179 ////////////////////////////////////////////////////////////////////////////////
183 View* View::Create(ViewManager* view_manager) {
184 View* view = new View(view_manager);
185 static_cast<ViewManagerClientImpl*>(view_manager)->AddView(view);
189 void View::Destroy() {
190 if (!OwnsView(manager_, this))
194 static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_);
195 while (!children_.empty()) {
196 View* child = children_.front();
197 if (!OwnsView(manager_, child)) {
198 ViewPrivate(child).ClearParent();
199 children_.erase(children_.begin());
202 DCHECK(std::find(children_.begin(), children_.end(), child) ==
209 void View::SetBounds(const gfx::Rect& bounds) {
210 if (!OwnsView(manager_, this))
214 static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds);
215 LocalSetBounds(bounds_, bounds);
218 void View::SetVisible(bool value) {
220 static_cast<ViewManagerClientImpl*>(manager_)->SetVisible(id_, value);
223 void View::AddObserver(ViewObserver* observer) {
224 observers_.AddObserver(observer);
227 void View::RemoveObserver(ViewObserver* observer) {
228 observers_.RemoveObserver(observer);
231 void View::AddChild(View* child) {
232 // TODO(beng): not necessarily valid to all connections, but possibly to the
233 // embeddee in an embedder-embeddee relationship.
235 CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
236 LocalAddChild(child);
238 static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_);
241 void View::RemoveChild(View* child) {
242 // TODO(beng): not necessarily valid to all connections, but possibly to the
243 // embeddee in an embedder-embeddee relationship.
245 CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
246 LocalRemoveChild(child);
248 static_cast<ViewManagerClientImpl*>(manager_)->RemoveChild(child->id(),
253 void View::MoveToFront() {
254 Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE);
257 void View::MoveToBack() {
258 Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW);
261 void View::Reorder(View* relative, OrderDirection direction) {
262 if (!LocalReorder(relative, direction))
265 static_cast<ViewManagerClientImpl*>(manager_)->Reorder(id_,
271 bool View::Contains(View* child) const {
273 CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
274 for (View* p = child->parent(); p; p = p->parent()) {
281 View* View::GetChildById(Id id) {
284 // TODO(beng): this could be improved depending on how we decide to own views.
285 Children::const_iterator it = children_.begin();
286 for (; it != children_.end(); ++it) {
287 View* view = (*it)->GetChildById(id);
294 void View::SetContents(const SkBitmap& contents) {
296 static_cast<ViewManagerClientImpl*>(manager_)->SetViewContents(id_,
301 void View::SetColor(SkColor color) {
302 gfx::Canvas canvas(bounds_.size(), 1.0f, true);
303 canvas.DrawColor(color);
304 SetContents(skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(true));
307 void View::SetFocus() {
309 static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_);
312 void View::Embed(const String& url) {
313 static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_);
316 scoped_ptr<ServiceProvider>
317 View::Embed(const String& url,
318 scoped_ptr<ServiceProviderImpl> exported_services) {
319 scoped_ptr<ServiceProvider> imported_services;
320 // BindToProxy() takes ownership of |exported_services|.
321 ServiceProviderImpl* registry = exported_services.release();
322 ServiceProviderPtr sp;
324 BindToProxy(registry, &sp);
325 imported_services.reset(registry->CreateRemoteServiceProvider());
327 static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_, sp.Pass());
328 return imported_services.Pass();
331 ////////////////////////////////////////////////////////////////////////////////
336 id_(static_cast<Id>(-1)),
340 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
342 parent_->LocalRemoveChild(this);
343 // TODO(beng): It'd be better to do this via a destruction observer in the
344 // ViewManagerClientImpl.
346 static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
347 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
350 ////////////////////////////////////////////////////////////////////////////////
353 View::View(ViewManager* manager)
355 id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateView()),
358 void View::LocalDestroy() {
362 void View::LocalAddChild(View* child) {
363 ScopedTreeNotifier notifier(child, child->parent(), this);
365 RemoveChildImpl(child, &child->parent_->children_);
366 children_.push_back(child);
367 child->parent_ = this;
370 void View::LocalRemoveChild(View* child) {
371 DCHECK_EQ(this, child->parent());
372 ScopedTreeNotifier notifier(child, this, NULL);
373 RemoveChildImpl(child, &children_);
376 bool View::LocalReorder(View* relative, OrderDirection direction) {
377 return ReorderImpl(&parent_->children_, this, relative, direction);
380 void View::LocalSetBounds(const gfx::Rect& old_bounds,
381 const gfx::Rect& new_bounds) {
382 DCHECK(old_bounds == bounds_);
383 ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
384 bounds_ = new_bounds;