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.
6 #include "base/macros.h"
7 #include "mojo/application/application_runner_chromium.h"
8 #include "mojo/examples/keyboard/keyboard.mojom.h"
9 #include "mojo/examples/window_manager/debug_panel.h"
10 #include "mojo/examples/window_manager/window_manager.mojom.h"
11 #include "mojo/public/c/system/main.h"
12 #include "mojo/public/cpp/application/application_connection.h"
13 #include "mojo/public/cpp/application/application_delegate.h"
14 #include "mojo/public/cpp/application/application_impl.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/navigation/navigation.mojom.h"
26 #include "mojo/services/window_manager/window_manager_app.h"
27 #include "mojo/views/views_init.h"
28 #include "ui/aura/window.h"
29 #include "ui/events/event.h"
30 #include "ui/events/event_constants.h"
31 #include "ui/gfx/geometry/size_conversions.h"
32 #include "ui/wm/core/focus_rules.h"
34 #if defined CreateWindow
45 const int kBorderInset
= 25;
46 const int kControlPanelWidth
= 200;
47 const int kTextfieldHeight
= 25;
49 class WMFocusRules
: public wm::FocusRules
{
51 WMFocusRules(mojo::WindowManagerApp
* window_manager_app
,
52 mojo::View
* window_container
)
53 : window_container_(window_container
),
54 window_manager_app_(window_manager_app
) {}
55 virtual ~WMFocusRules() {}
58 // Overridden from wm::FocusRules:
59 virtual bool IsToplevelWindow(aura::Window
* window
) const override
{
60 return mojo::WindowManagerApp::GetViewForWindow(window
)->parent() ==
63 virtual bool CanActivateWindow(aura::Window
* window
) const override
{
64 return mojo::WindowManagerApp::GetViewForWindow(window
)->parent() ==
67 virtual bool CanFocusWindow(aura::Window
* window
) const override
{
70 virtual aura::Window
* GetToplevelWindow(aura::Window
* window
) const override
{
71 mojo::View
* view
= mojo::WindowManagerApp::GetViewForWindow(window
);
72 while (view
->parent() != window_container_
) {
73 view
= view
->parent();
74 // Unparented hierarchy, there is no "top level" window.
79 return window_manager_app_
->GetWindowForViewId(view
->id());
81 virtual aura::Window
* GetActivatableWindow(
82 aura::Window
* window
) const override
{
83 return GetToplevelWindow(window
);
85 virtual aura::Window
* GetFocusableWindow(
86 aura::Window
* window
) const override
{
89 virtual aura::Window
* GetNextActivatableWindow(
90 aura::Window
* ignore
) const override
{
91 aura::Window
* activatable
= GetActivatableWindow(ignore
);
92 const aura::Window::Windows
& children
= activatable
->parent()->children();
93 for (aura::Window::Windows::const_reverse_iterator it
= children
.rbegin();
94 it
!= children
.rend(); ++it
) {
101 mojo::View
* window_container_
;
102 mojo::WindowManagerApp
* window_manager_app_
;
104 DISALLOW_COPY_AND_ASSIGN(WMFocusRules
);
109 class WindowManagerConnection
: public InterfaceImpl
<IWindowManager
> {
111 explicit WindowManagerConnection(WindowManager
* window_manager
)
112 : window_manager_(window_manager
) {}
113 virtual ~WindowManagerConnection() {}
116 // Overridden from IWindowManager:
117 virtual void CloseWindow(Id view_id
) override
;
118 virtual void ShowKeyboard(Id view_id
, RectPtr bounds
) override
;
119 virtual void HideKeyboard(Id view_id
) override
;
121 WindowManager
* window_manager_
;
123 DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection
);
126 class NavigatorHostImpl
: public InterfaceImpl
<NavigatorHost
> {
128 explicit NavigatorHostImpl(WindowManager
* window_manager
, Id view_id
)
129 : window_manager_(window_manager
), view_id_(view_id
) {}
130 virtual ~NavigatorHostImpl() {
134 virtual void DidNavigateLocally(const mojo::String
& url
) override
;
135 virtual void RequestNavigate(Target target
, URLRequestPtr request
) override
;
137 WindowManager
* window_manager_
;
140 DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl
);
143 class KeyboardManager
: public KeyboardClient
,
144 public ViewObserver
{
146 KeyboardManager() : view_manager_(NULL
), view_(NULL
) {
148 virtual ~KeyboardManager() {
150 view_
->parent()->RemoveObserver(this);
153 View
* view() { return view_
; }
155 void Init(ApplicationImpl
* application
,
156 ViewManager
* view_manager
,
158 const gfx::Rect
& bounds
) {
159 view_manager_
= view_manager
;
160 view_
= View::Create(view_manager
);
161 view_
->SetBounds(bounds
);
162 parent
->AddChild(view_
);
163 view_
->Embed("mojo:mojo_keyboard");
164 application
->ConnectToService("mojo:mojo_keyboard", &keyboard_service_
);
165 keyboard_service_
.set_client(this);
166 parent
->AddObserver(this);
169 void Show(Id view_id
, const gfx::Rect
& bounds
) {
170 keyboard_service_
->SetTarget(view_id
);
171 view_
->SetVisible(true);
174 void Hide(Id view_id
) {
175 keyboard_service_
->SetTarget(0);
176 view_
->SetVisible(false);
181 virtual void OnKeyboardEvent(Id view_id
,
183 int32_t flags
) override
{
184 View
* view
= view_manager_
->GetViewById(view_id
);
188 const bool is_char
= code
!= ui::VKEY_BACK
&& code
!= ui::VKEY_RETURN
;
190 const bool is_char
= false;
193 view_manager_
->DispatchEvent(
195 Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED
,
196 static_cast<ui::KeyboardCode
>(code
),
199 view_manager_
->DispatchEvent(
201 Event::From(ui::KeyEvent(static_cast<base::char16
>(code
),
202 static_cast<ui::KeyboardCode
>(code
),
205 view_manager_
->DispatchEvent(
207 Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED
,
208 static_cast<ui::KeyboardCode
>(code
),
212 // Overridden from ViewObserver:
213 virtual void OnViewBoundsChanged(View
* parent
,
214 const gfx::Rect
& old_bounds
,
215 const gfx::Rect
& new_bounds
) override
{
216 gfx::Rect
keyboard_bounds(view_
->bounds());
217 keyboard_bounds
.set_y(new_bounds
.bottom() - keyboard_bounds
.height());
218 keyboard_bounds
.set_width(keyboard_bounds
.width() +
219 new_bounds
.width() - old_bounds
.width());
220 view_
->SetBounds(keyboard_bounds
);
222 virtual void OnViewDestroyed(View
* parent
) override
{
223 DCHECK_EQ(parent
, view_
->parent());
224 parent
->RemoveObserver(this);
228 KeyboardServicePtr keyboard_service_
;
229 ViewManager
* view_manager_
;
231 // View the keyboard is attached to.
234 DISALLOW_COPY_AND_ASSIGN(KeyboardManager
);
237 class RootLayoutManager
: public ViewObserver
{
239 RootLayoutManager(ViewManager
* view_manager
,
242 Id launcher_ui_view_id
,
243 Id control_panel_view_id
)
245 view_manager_(view_manager
),
246 content_view_id_(content_view_id
),
247 launcher_ui_view_id_(launcher_ui_view_id
),
248 control_panel_view_id_(control_panel_view_id
) {}
249 virtual ~RootLayoutManager() {
251 root_
->RemoveObserver(this);
255 // Overridden from ViewObserver:
256 virtual void OnViewBoundsChanged(View
* view
,
257 const gfx::Rect
& old_bounds
,
258 const gfx::Rect
& new_bounds
) override
{
259 DCHECK_EQ(view
, root_
);
261 View
* content_view
= view_manager_
->GetViewById(content_view_id_
);
262 content_view
->SetBounds(new_bounds
);
264 int delta_width
= new_bounds
.width() - old_bounds
.width();
265 int delta_height
= new_bounds
.height() - old_bounds
.height();
267 View
* launcher_ui_view
=
268 view_manager_
->GetViewById(launcher_ui_view_id_
);
269 gfx::Rect
launcher_ui_bounds(launcher_ui_view
->bounds());
270 launcher_ui_bounds
.set_width(launcher_ui_bounds
.width() + delta_width
);
271 launcher_ui_view
->SetBounds(launcher_ui_bounds
);
273 View
* control_panel_view
=
274 view_manager_
->GetViewById(control_panel_view_id_
);
275 gfx::Rect
control_panel_bounds(control_panel_view
->bounds());
276 control_panel_bounds
.set_x(control_panel_bounds
.x() + delta_width
);
277 control_panel_view
->SetBounds(control_panel_bounds
);
279 const View::Children
& content_views
= content_view
->children();
280 View::Children::const_iterator iter
= content_views
.begin();
281 for(; iter
!= content_views
.end(); ++iter
) {
283 if (view
->id() == control_panel_view
->id() ||
284 view
->id() == launcher_ui_view
->id())
286 gfx::Rect
view_bounds(view
->bounds());
287 view_bounds
.set_width(view_bounds
.width() + delta_width
);
288 view_bounds
.set_height(view_bounds
.height() + delta_height
);
289 view
->SetBounds(view_bounds
);
292 virtual void OnViewDestroyed(View
* view
) override
{
293 DCHECK_EQ(view
, root_
);
294 root_
->RemoveObserver(this);
299 ViewManager
* view_manager_
;
300 const Id content_view_id_
;
301 const Id launcher_ui_view_id_
;
302 const Id control_panel_view_id_
;
304 DISALLOW_COPY_AND_ASSIGN(RootLayoutManager
);
307 class Window
: public InterfaceFactory
<NavigatorHost
> {
309 Window(WindowManager
* window_manager
, View
* view
)
310 : window_manager_(window_manager
), view_(view
) {}
314 View
* view() const { return view_
; }
316 void Embed(const std::string
& url
) {
317 scoped_ptr
<ServiceProviderImpl
> service_provider_impl(
318 new ServiceProviderImpl());
319 service_provider_impl
->AddService
<NavigatorHost
>(this);
320 view_
->Embed(url
, service_provider_impl
.Pass());
324 // InterfaceFactory<NavigatorHost>
325 virtual void Create(ApplicationConnection
* connection
,
326 InterfaceRequest
<NavigatorHost
> request
) override
{
327 BindToRequest(new NavigatorHostImpl(window_manager_
, view_
->id()),
331 WindowManager
* window_manager_
;
336 : public ApplicationDelegate
,
337 public DebugPanel::Delegate
,
338 public ViewManagerDelegate
,
339 public WindowManagerDelegate
,
340 public ui::EventHandler
{
344 window_manager_factory_(this),
347 window_manager_app_(new WindowManagerApp(this, this)),
350 virtual ~WindowManager() {
351 // host() may be destroyed by the time we get here.
352 // TODO: figure out a way to always cleanly remove handler.
353 if (window_manager_app_
->host())
354 window_manager_app_
->host()->window()->RemovePreTargetHandler(this);
357 void CloseWindow(Id view_id
) {
358 WindowVector::iterator iter
= GetWindowByViewId(view_id
);
359 DCHECK(iter
!= windows_
.end());
360 Window
* window
= *iter
;
361 windows_
.erase(iter
);
362 window
->view()->Destroy();
365 void ShowKeyboard(Id view_id
, const gfx::Rect
& bounds
) {
366 // TODO: this needs to validate |view_id|. That is, it shouldn't assume
367 // |view_id| is valid and it also needs to make sure the client that sent
368 // this really owns |view_id|.
369 // TODO: honor |bounds|.
370 if (!keyboard_manager_
) {
371 keyboard_manager_
.reset(new KeyboardManager
);
372 View
* parent
= view_manager_
->GetRoots().back();
373 int ideal_height
= 200;
374 // TODO(sky): 10 is a bit of a hack here. There is a bug that causes
375 // white strips to appear when 0 is used. Figure this out!
376 const gfx::Rect
keyboard_bounds(
377 10, parent
->bounds().height() - ideal_height
,
378 parent
->bounds().width() - 20, ideal_height
);
379 keyboard_manager_
->Init(app_
, view_manager_
, parent
, keyboard_bounds
);
381 keyboard_manager_
->Show(view_id
, bounds
);
384 void HideKeyboard(Id view_id
) {
385 // See comment in ShowKeyboard() about validating args.
386 if (keyboard_manager_
)
387 keyboard_manager_
->Hide(view_id
);
390 void DidNavigateLocally(uint32 source_view_id
, const mojo::String
& url
) {
391 LOG(ERROR
) << "DidNavigateLocally: source_view_id: " << source_view_id
392 << " url: " << url
.To
<std::string
>();
395 // Overridden from DebugPanel::Delegate:
396 virtual void CloseTopWindow() override
{
397 if (!windows_
.empty())
398 CloseWindow(windows_
.back()->view()->id());
401 virtual void RequestNavigate(uint32 source_view_id
,
403 URLRequestPtr request
) override
{
404 OnLaunch(source_view_id
, target
, request
->url
);
408 typedef std::vector
<Window
*> WindowVector
;
410 // Overridden from ApplicationDelegate:
411 virtual void Initialize(ApplicationImpl
* app
) override
{
412 shell_
= app
->shell();
414 views_init_
.reset(new ViewsInit
);
415 window_manager_app_
->Initialize(app
);
418 virtual bool ConfigureIncomingConnection(
419 ApplicationConnection
* connection
) override
{
420 connection
->AddService(&window_manager_factory_
);
421 window_manager_app_
->ConfigureIncomingConnection(connection
);
425 // Overridden from ViewManagerDelegate:
426 virtual void OnEmbed(ViewManager
* view_manager
,
428 ServiceProviderImpl
* exported_services
,
429 scoped_ptr
<ServiceProvider
> imported_services
) override
{
430 DCHECK(!view_manager_
);
431 view_manager_
= view_manager
;
433 View
* view
= View::Create(view_manager_
);
434 root
->AddChild(view
);
435 view
->SetBounds(gfx::Rect(root
->bounds().size()));
436 content_view_id_
= view
->id();
438 Id launcher_ui_id
= CreateLauncherUI();
439 Id control_panel_id
= CreateControlPanel(view
);
441 root_layout_manager_
.reset(
442 new RootLayoutManager(view_manager
, root
,
446 root
->AddObserver(root_layout_manager_
.get());
448 window_manager_app_
->host()->window()->AddPreTargetHandler(this);
450 window_manager_app_
->InitFocus(new WMFocusRules(window_manager_app_
.get(),
453 virtual void OnViewManagerDisconnected(ViewManager
* view_manager
) override
{
454 DCHECK_EQ(view_manager_
, view_manager
);
455 view_manager_
= NULL
;
456 base::MessageLoop::current()->Quit();
459 // Overridden from WindowManagerDelegate:
462 InterfaceRequest
<ServiceProvider
> service_provider
) override
{
463 const Id kInvalidSourceViewId
= 0;
464 OnLaunch(kInvalidSourceViewId
, TARGET_DEFAULT
, url
);
466 virtual void DispatchEvent(EventPtr event
) override
{}
468 // Overridden from ui::EventHandler:
469 virtual void OnEvent(ui::Event
* event
) override
{
470 View
* view
= WindowManagerApp::GetViewForWindow(
471 static_cast<aura::Window
*>(event
->target()));
472 if (event
->type() == ui::ET_MOUSE_PRESSED
&&
473 !IsDescendantOfKeyboard(view
)) {
478 void OnLaunch(uint32 source_view_id
,
479 Target requested_target
,
480 const mojo::String
& url
) {
481 Target target
= debug_panel_
->navigation_target();
482 if (target
== TARGET_DEFAULT
) {
483 if (requested_target
!= TARGET_DEFAULT
) {
484 target
= requested_target
;
486 // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
488 target
= TARGET_SOURCE_NODE
;
492 Window
* dest_view
= NULL
;
493 if (target
== TARGET_SOURCE_NODE
) {
494 WindowVector::iterator source_view
= GetWindowByViewId(source_view_id
);
495 bool app_initiated
= source_view
!= windows_
.end();
497 dest_view
= *source_view
;
498 else if (!windows_
.empty())
499 dest_view
= windows_
.back();
503 dest_view
= CreateWindow();
504 windows_
.push_back(dest_view
);
507 dest_view
->Embed(url
);
510 // TODO(beng): proper layout manager!!
511 Id
CreateLauncherUI() {
512 View
* view
= view_manager_
->GetViewById(content_view_id_
);
513 gfx::Rect bounds
= view
->bounds();
514 bounds
.Inset(kBorderInset
, kBorderInset
);
515 bounds
.set_height(kTextfieldHeight
);
516 launcher_ui_
= CreateWindow(bounds
);
517 launcher_ui_
->Embed("mojo:mojo_browser");
518 return launcher_ui_
->view()->id();
521 Window
* CreateWindow() {
522 View
* view
= view_manager_
->GetViewById(content_view_id_
);
523 gfx::Rect
bounds(kBorderInset
,
524 2 * kBorderInset
+ kTextfieldHeight
,
525 view
->bounds().width() - 3 * kBorderInset
-
527 view
->bounds().height() -
528 (3 * kBorderInset
+ kTextfieldHeight
));
529 if (!windows_
.empty()) {
530 gfx::Point position
= windows_
.back()->view()->bounds().origin();
531 position
.Offset(35, 35);
532 bounds
.set_origin(position
);
534 return CreateWindow(bounds
);
537 Window
* CreateWindow(const gfx::Rect
& bounds
) {
538 View
* content
= view_manager_
->GetViewById(content_view_id_
);
539 View
* view
= View::Create(view_manager_
);
540 content
->AddChild(view
);
541 view
->SetBounds(bounds
);
543 return new Window(this, view
);
546 bool IsDescendantOfKeyboard(View
* target
) {
547 return keyboard_manager_
.get() &&
548 keyboard_manager_
->view()->Contains(target
);
551 Id
CreateControlPanel(View
* root
) {
552 View
* view
= View::Create(view_manager_
);
553 root
->AddChild(view
);
555 gfx::Rect
bounds(root
->bounds().width() - kControlPanelWidth
-
557 kBorderInset
* 2 + kTextfieldHeight
,
559 root
->bounds().height() - kBorderInset
* 3 -
561 view
->SetBounds(bounds
);
563 debug_panel_
= new DebugPanel(this, shell_
, view
);
567 WindowVector::iterator
GetWindowByViewId(Id view_id
) {
568 for (std::vector
<Window
*>::iterator iter
= windows_
.begin();
569 iter
!= windows_
.end();
571 if ((*iter
)->view()->id() == view_id
) {
575 return windows_
.end();
580 InterfaceFactoryImplWithContext
<WindowManagerConnection
, WindowManager
>
581 window_manager_factory_
;
583 scoped_ptr
<ViewsInit
> views_init_
;
584 DebugPanel
* debug_panel_
;
585 Window
* launcher_ui_
;
586 WindowVector windows_
;
587 ViewManager
* view_manager_
;
588 scoped_ptr
<RootLayoutManager
> root_layout_manager_
;
590 scoped_ptr
<WindowManagerApp
> window_manager_app_
;
592 // Id of the view most content is added to. The keyboard is NOT added here.
595 scoped_ptr
<KeyboardManager
> keyboard_manager_
;
596 ApplicationImpl
* app_
;
598 DISALLOW_COPY_AND_ASSIGN(WindowManager
);
601 void WindowManagerConnection::CloseWindow(Id view_id
) {
602 window_manager_
->CloseWindow(view_id
);
605 void WindowManagerConnection::ShowKeyboard(Id view_id
, RectPtr bounds
) {
606 window_manager_
->ShowKeyboard(view_id
, bounds
.To
<gfx::Rect
>());
609 void WindowManagerConnection::HideKeyboard(Id view_id
) {
610 window_manager_
->HideKeyboard(view_id
);
613 void NavigatorHostImpl::DidNavigateLocally(const mojo::String
& url
) {
614 window_manager_
->DidNavigateLocally(view_id_
, url
);
617 void NavigatorHostImpl::RequestNavigate(Target target
, URLRequestPtr request
) {
618 window_manager_
->RequestNavigate(view_id_
, target
, request
.Pass());
621 } // namespace examples
624 MojoResult
MojoMain(MojoHandle shell_handle
) {
625 mojo::ApplicationRunnerChromium
runner(new mojo::examples::WindowManager
);
626 return runner
.Run(shell_handle
);