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
;