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 "base/basictypes.h"
7 #include "mojo/examples/keyboard/keyboard.mojom.h"
8 #include "mojo/examples/window_manager/debug_panel.h"
9 #include "mojo/examples/window_manager/window_manager.mojom.h"
10 #include "mojo/public/c/system/main.h"
11 #include "mojo/public/cpp/application/application_connection.h"
12 #include "mojo/public/cpp/application/application_delegate.h"
13 #include "mojo/public/cpp/application/application_impl.h"
14 #include "mojo/public/cpp/application/application_runner_chromium.h"
15 #include "mojo/public/cpp/application/interface_factory_impl.h"
16 #include "mojo/public/cpp/application/service_provider_impl.h"
17 #include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
18 #include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
19 #include "mojo/services/public/cpp/view_manager/view.h"
20 #include "mojo/services/public/cpp/view_manager/view_manager.h"
21 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
22 #include "mojo/services/public/cpp/view_manager/view_observer.h"
23 #include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
24 #include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
25 #include "mojo/services/public/interfaces/launcher/launcher.mojom.h"
26 #include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
27 #include "mojo/services/window_manager/window_manager_app.h"
28 #include "mojo/views/views_init.h"
29 #include "ui/aura/window.h"
30 #include "ui/events/event.h"
31 #include "ui/events/event_constants.h"
32 #include "ui/gfx/geometry/size_conversions.h"
34 #if defined CreateWindow
45 const int kBorderInset
= 25;
46 const int kControlPanelWidth
= 200;
47 const int kTextfieldHeight
= 25;
51 class WindowManagerConnection
: public InterfaceImpl
<IWindowManager
> {
53 explicit WindowManagerConnection(WindowManager
* window_manager
)
54 : window_manager_(window_manager
) {}
55 virtual ~WindowManagerConnection() {}
58 // Overridden from IWindowManager:
59 virtual void CloseWindow(Id view_id
) OVERRIDE
;
60 virtual void ShowKeyboard(Id view_id
, RectPtr bounds
) OVERRIDE
;
61 virtual void HideKeyboard(Id view_id
) OVERRIDE
;
63 WindowManager
* window_manager_
;
65 DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection
);
68 class NavigatorHostImpl
: public InterfaceImpl
<NavigatorHost
> {
70 explicit NavigatorHostImpl(WindowManager
* window_manager
, Id view_id
)
71 : window_manager_(window_manager
), view_id_(view_id
) {}
72 virtual ~NavigatorHostImpl() {
76 virtual void DidNavigateLocally(const mojo::String
& url
) OVERRIDE
;
77 virtual void RequestNavigate(
79 NavigationDetailsPtr nav_details
) OVERRIDE
;
81 WindowManager
* window_manager_
;
84 DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl
);
87 class KeyboardManager
: public KeyboardClient
,
90 KeyboardManager() : view_manager_(NULL
), view_(NULL
) {
92 virtual ~KeyboardManager() {
94 view_
->parent()->RemoveObserver(this);
97 View
* view() { return view_
; }
99 void Init(ApplicationImpl
* application
,
100 ViewManager
* view_manager
,
102 const gfx::Rect
& bounds
) {
103 view_manager_
= view_manager
;
104 view_
= View::Create(view_manager
);
105 view_
->SetBounds(bounds
);
106 parent
->AddChild(view_
);
107 view_
->Embed("mojo:mojo_keyboard");
108 application
->ConnectToService("mojo:mojo_keyboard", &keyboard_service_
);
109 keyboard_service_
.set_client(this);
110 parent
->AddObserver(this);
113 void Show(Id view_id
, const gfx::Rect
& bounds
) {
114 keyboard_service_
->SetTarget(view_id
);
115 view_
->SetVisible(true);
118 void Hide(Id view_id
) {
119 keyboard_service_
->SetTarget(0);
120 view_
->SetVisible(false);
125 virtual void OnKeyboardEvent(Id view_id
,
127 int32_t flags
) OVERRIDE
{
128 View
* view
= view_manager_
->GetViewById(view_id
);
132 const bool is_char
= code
!= ui::VKEY_BACK
&& code
!= ui::VKEY_RETURN
;
134 const bool is_char
= false;
137 view_manager_
->DispatchEvent(
139 Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED
,
140 static_cast<ui::KeyboardCode
>(code
),
143 view_manager_
->DispatchEvent(
145 Event::From(ui::KeyEvent(static_cast<base::char16
>(code
),
146 static_cast<ui::KeyboardCode
>(code
),
149 view_manager_
->DispatchEvent(
151 Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED
,
152 static_cast<ui::KeyboardCode
>(code
),
156 // Overridden from ViewObserver:
157 virtual void OnViewBoundsChanged(View
* parent
,
158 const gfx::Rect
& old_bounds
,
159 const gfx::Rect
& new_bounds
) OVERRIDE
{
160 gfx::Rect
keyboard_bounds(view_
->bounds());
161 keyboard_bounds
.set_y(new_bounds
.bottom() - keyboard_bounds
.height());
162 keyboard_bounds
.set_width(keyboard_bounds
.width() +
163 new_bounds
.width() - old_bounds
.width());
164 view_
->SetBounds(keyboard_bounds
);
166 virtual void OnViewDestroyed(View
* parent
) OVERRIDE
{
167 DCHECK_EQ(parent
, view_
->parent());
168 parent
->RemoveObserver(this);
172 KeyboardServicePtr keyboard_service_
;
173 ViewManager
* view_manager_
;
175 // View the keyboard is attached to.
178 DISALLOW_COPY_AND_ASSIGN(KeyboardManager
);
181 class RootLayoutManager
: public ViewObserver
{
183 RootLayoutManager(ViewManager
* view_manager
,
186 Id launcher_ui_view_id
,
187 Id control_panel_view_id
)
189 view_manager_(view_manager
),
190 content_view_id_(content_view_id
),
191 launcher_ui_view_id_(launcher_ui_view_id
),
192 control_panel_view_id_(control_panel_view_id
) {}
193 virtual ~RootLayoutManager() {
195 root_
->RemoveObserver(this);
199 // Overridden from ViewObserver:
200 virtual void OnViewBoundsChanged(View
* view
,
201 const gfx::Rect
& old_bounds
,
202 const gfx::Rect
& new_bounds
) OVERRIDE
{
203 DCHECK_EQ(view
, root_
);
205 View
* content_view
= view_manager_
->GetViewById(content_view_id_
);
206 content_view
->SetBounds(new_bounds
);
207 // Force the view's bitmap to be recreated
208 content_view
->SetColor(SK_ColorBLUE
);
210 int delta_width
= new_bounds
.width() - old_bounds
.width();
211 int delta_height
= new_bounds
.height() - old_bounds
.height();
213 View
* launcher_ui_view
=
214 view_manager_
->GetViewById(launcher_ui_view_id_
);
215 gfx::Rect
launcher_ui_bounds(launcher_ui_view
->bounds());
216 launcher_ui_bounds
.set_width(launcher_ui_bounds
.width() + delta_width
);
217 launcher_ui_view
->SetBounds(launcher_ui_bounds
);
219 View
* control_panel_view
=
220 view_manager_
->GetViewById(control_panel_view_id_
);
221 gfx::Rect
control_panel_bounds(control_panel_view
->bounds());
222 control_panel_bounds
.set_x(control_panel_bounds
.x() + delta_width
);
223 control_panel_view
->SetBounds(control_panel_bounds
);
225 const View::Children
& content_views
= content_view
->children();
226 View::Children::const_iterator iter
= content_views
.begin();
227 for(; iter
!= content_views
.end(); ++iter
) {
229 if (view
->id() == control_panel_view
->id() ||
230 view
->id() == launcher_ui_view
->id())
232 gfx::Rect
view_bounds(view
->bounds());
233 view_bounds
.set_width(view_bounds
.width() + delta_width
);
234 view_bounds
.set_height(view_bounds
.height() + delta_height
);
235 view
->SetBounds(view_bounds
);
238 virtual void OnViewDestroyed(View
* view
) OVERRIDE
{
239 DCHECK_EQ(view
, root_
);
240 root_
->RemoveObserver(this);
245 ViewManager
* view_manager_
;
246 const Id content_view_id_
;
247 const Id launcher_ui_view_id_
;
248 const Id control_panel_view_id_
;
250 DISALLOW_COPY_AND_ASSIGN(RootLayoutManager
);
253 class Window
: public InterfaceFactory
<NavigatorHost
> {
255 Window(WindowManager
* window_manager
, View
* view
)
256 : window_manager_(window_manager
), view_(view
) {}
260 View
* view() const { return view_
; }
262 void Embed(const std::string
& url
) {
263 scoped_ptr
<ServiceProviderImpl
> service_provider_impl(
264 new ServiceProviderImpl());
265 service_provider_impl
->AddService
<NavigatorHost
>(this);
266 view_
->Embed(url
, service_provider_impl
.Pass());
270 // InterfaceFactory<NavigatorHost>
271 virtual void Create(ApplicationConnection
* connection
,
272 InterfaceRequest
<NavigatorHost
> request
) OVERRIDE
{
273 BindToRequest(new NavigatorHostImpl(window_manager_
, view_
->id()),
277 WindowManager
* window_manager_
;
282 : public ApplicationDelegate
,
283 public DebugPanel::Delegate
,
284 public ViewManagerDelegate
,
285 public WindowManagerDelegate
,
286 public ui::EventHandler
{
289 : window_manager_factory_(this),
292 window_manager_app_(new WindowManagerApp(this, this)),
295 virtual ~WindowManager() {
296 // host() may be destroyed by the time we get here.
297 // TODO: figure out a way to always cleanly remove handler.
298 if (window_manager_app_
->host())
299 window_manager_app_
->host()->window()->RemovePreTargetHandler(this);
302 void CloseWindow(Id view_id
) {
303 WindowVector::iterator iter
= GetWindowByViewId(view_id
);
304 DCHECK(iter
!= windows_
.end());
305 windows_
.erase(iter
);
306 (*iter
)->view()->Destroy();
309 void ShowKeyboard(Id view_id
, const gfx::Rect
& bounds
) {
310 // TODO: this needs to validate |view_id|. That is, it shouldn't assume
311 // |view_id| is valid and it also needs to make sure the client that sent
312 // this really owns |view_id|.
313 // TODO: honor |bounds|.
314 if (!keyboard_manager_
) {
315 keyboard_manager_
.reset(new KeyboardManager
);
316 View
* parent
= view_manager_
->GetRoots().back();
317 int ideal_height
= 200;
318 // TODO(sky): 10 is a bit of a hack here. There is a bug that causes
319 // white strips to appear when 0 is used. Figure this out!
320 const gfx::Rect
keyboard_bounds(
321 10, parent
->bounds().height() - ideal_height
,
322 parent
->bounds().width() - 20, ideal_height
);
323 keyboard_manager_
->Init(app_
, view_manager_
, parent
, keyboard_bounds
);
325 keyboard_manager_
->Show(view_id
, bounds
);
328 void HideKeyboard(Id view_id
) {
329 // See comment in ShowKeyboard() about validating args.
330 if (keyboard_manager_
)
331 keyboard_manager_
->Hide(view_id
);
334 void DidNavigateLocally(uint32 source_view_id
, const mojo::String
& url
) {
335 LOG(ERROR
) << "DidNavigateLocally: source_view_id: " << source_view_id
336 << " url: " << url
.To
<std::string
>();
339 // Overridden from DebugPanel::Delegate:
340 virtual void CloseTopWindow() OVERRIDE
{
341 if (!windows_
.empty())
342 CloseWindow(windows_
.back()->view()->id());
345 virtual void RequestNavigate(
346 uint32 source_view_id
,
348 NavigationDetailsPtr nav_details
) OVERRIDE
{
349 OnLaunch(source_view_id
, target
, nav_details
->request
->url
);
353 typedef std::vector
<Window
*> WindowVector
;
355 // Overridden from ApplicationDelegate:
356 virtual void Initialize(ApplicationImpl
* app
) MOJO_OVERRIDE
{
358 views_init_
.reset(new ViewsInit
);
359 window_manager_app_
->Initialize(app
);
362 virtual bool ConfigureIncomingConnection(ApplicationConnection
* connection
)
364 connection
->AddService(&window_manager_factory_
);
365 window_manager_app_
->ConfigureIncomingConnection(connection
);
369 // Overridden from ViewManagerDelegate:
370 virtual void OnEmbed(ViewManager
* view_manager
,
372 ServiceProviderImpl
* exported_services
,
373 scoped_ptr
<ServiceProvider
> imported_services
) OVERRIDE
{
374 DCHECK(!view_manager_
);
375 view_manager_
= view_manager
;
377 View
* view
= View::Create(view_manager_
);
378 root
->AddChild(view
);
379 view
->SetBounds(gfx::Rect(root
->bounds().size()));
380 content_view_id_
= view
->id();
382 root
->SetColor(SK_ColorBLUE
);
384 Id launcher_ui_id
= CreateLauncherUI();
385 Id control_panel_id
= CreateControlPanel(view
);
387 root_layout_manager_
.reset(
388 new RootLayoutManager(view_manager
, root
,
392 root
->AddObserver(root_layout_manager_
.get());
394 window_manager_app_
->host()->window()->AddPreTargetHandler(this);
396 virtual void OnViewManagerDisconnected(ViewManager
* view_manager
) OVERRIDE
{
397 DCHECK_EQ(view_manager_
, view_manager
);
398 view_manager_
= NULL
;
399 base::MessageLoop::current()->Quit();
402 // Overridden from WindowManagerDelegate:
405 InterfaceRequest
<ServiceProvider
> service_provider
) OVERRIDE
{
406 const Id kInvalidSourceViewId
= 0;
407 OnLaunch(kInvalidSourceViewId
, TARGET_DEFAULT
, url
);
409 virtual void DispatchEvent(EventPtr event
) MOJO_OVERRIDE
{}
411 // Overridden from ui::EventHandler:
412 virtual void OnEvent(ui::Event
* event
) OVERRIDE
{
413 View
* view
= WindowManagerApp::GetViewForWindow(
414 static_cast<aura::Window
*>(event
->target()));
415 if (event
->type() == ui::ET_MOUSE_PRESSED
&&
416 !IsDescendantOfKeyboard(view
)) {
421 void OnLaunch(uint32 source_view_id
,
422 Target requested_target
,
423 const mojo::String
& url
) {
424 Target target
= debug_panel_
->navigation_target();
425 if (target
== TARGET_DEFAULT
) {
426 if (requested_target
!= TARGET_DEFAULT
) {
427 target
= requested_target
;
429 // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
431 target
= TARGET_SOURCE_NODE
;
435 Window
* dest_view
= NULL
;
436 if (target
== TARGET_SOURCE_NODE
) {
437 WindowVector::iterator source_view
= GetWindowByViewId(source_view_id
);
438 bool app_initiated
= source_view
!= windows_
.end();
440 dest_view
= *source_view
;
441 else if (!windows_
.empty())
442 dest_view
= windows_
.back();
446 dest_view
= CreateWindow();
447 windows_
.push_back(dest_view
);
450 dest_view
->Embed(url
);
453 // TODO(beng): proper layout manager!!
454 Id
CreateLauncherUI() {
455 NavigationDetailsPtr nav_details
;
456 ResponseDetailsPtr response
;
457 View
* view
= view_manager_
->GetViewById(content_view_id_
);
458 gfx::Rect bounds
= view
->bounds();
459 bounds
.Inset(kBorderInset
, kBorderInset
);
460 bounds
.set_height(kTextfieldHeight
);
461 launcher_ui_
= CreateWindow(bounds
);
462 launcher_ui_
->Embed("mojo:mojo_browser");
463 return launcher_ui_
->view()->id();
466 Window
* CreateWindow() {
467 View
* view
= view_manager_
->GetViewById(content_view_id_
);
468 gfx::Rect
bounds(kBorderInset
,
469 2 * kBorderInset
+ kTextfieldHeight
,
470 view
->bounds().width() - 3 * kBorderInset
-
472 view
->bounds().height() -
473 (3 * kBorderInset
+ kTextfieldHeight
));
474 if (!windows_
.empty()) {
475 gfx::Point position
= windows_
.back()->view()->bounds().origin();
476 position
.Offset(35, 35);
477 bounds
.set_origin(position
);
479 return CreateWindow(bounds
);
482 Window
* CreateWindow(const gfx::Rect
& bounds
) {
483 View
* content
= view_manager_
->GetViewById(content_view_id_
);
484 View
* view
= View::Create(view_manager_
);
485 content
->AddChild(view
);
486 view
->SetBounds(bounds
);
488 return new Window(this, view
);
491 bool IsDescendantOfKeyboard(View
* target
) {
492 return keyboard_manager_
.get() &&
493 keyboard_manager_
->view()->Contains(target
);
496 Id
CreateControlPanel(View
* root
) {
497 View
* view
= View::Create(view_manager_
);
498 root
->AddChild(view
);
500 gfx::Rect
bounds(root
->bounds().width() - kControlPanelWidth
-
502 kBorderInset
* 2 + kTextfieldHeight
,
504 root
->bounds().height() - kBorderInset
* 3 -
506 view
->SetBounds(bounds
);
508 debug_panel_
= new DebugPanel(this, view
);
512 WindowVector::iterator
GetWindowByViewId(Id view_id
) {
513 for (std::vector
<Window
*>::iterator iter
= windows_
.begin();
514 iter
!= windows_
.end();
516 if ((*iter
)->view()->id() == view_id
) {
520 return windows_
.end();
523 InterfaceFactoryImplWithContext
<WindowManagerConnection
, WindowManager
>
524 window_manager_factory_
;
526 scoped_ptr
<ViewsInit
> views_init_
;
527 DebugPanel
* debug_panel_
;
528 Window
* launcher_ui_
;
529 WindowVector windows_
;
530 ViewManager
* view_manager_
;
531 scoped_ptr
<RootLayoutManager
> root_layout_manager_
;
533 scoped_ptr
<WindowManagerApp
> window_manager_app_
;
535 // Id of the view most content is added to. The keyboard is NOT added here.
538 scoped_ptr
<KeyboardManager
> keyboard_manager_
;
539 ApplicationImpl
* app_
;
541 DISALLOW_COPY_AND_ASSIGN(WindowManager
);
544 void WindowManagerConnection::CloseWindow(Id view_id
) {
545 window_manager_
->CloseWindow(view_id
);
548 void WindowManagerConnection::ShowKeyboard(Id view_id
, RectPtr bounds
) {
549 window_manager_
->ShowKeyboard(view_id
, bounds
.To
<gfx::Rect
>());
552 void WindowManagerConnection::HideKeyboard(Id view_id
) {
553 window_manager_
->HideKeyboard(view_id
);
556 void NavigatorHostImpl::DidNavigateLocally(const mojo::String
& url
) {
557 window_manager_
->DidNavigateLocally(view_id_
, url
);
560 void NavigatorHostImpl::RequestNavigate(Target target
,
561 NavigationDetailsPtr nav_details
) {
562 window_manager_
->RequestNavigate(view_id_
, target
, nav_details
.Pass());
565 } // namespace examples
568 MojoResult
MojoMain(MojoHandle shell_handle
) {
569 mojo::ApplicationRunnerChromium
runner(new mojo::examples::WindowManager
);
570 return runner
.Run(shell_handle
);