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/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"
33 #if defined CreateWindow
44 const int kBorderInset = 25;
45 const int kControlPanelWidth = 200;
46 const int kTextfieldHeight = 25;
50 class WindowManagerConnection : public InterfaceImpl<IWindowManager> {
52 explicit WindowManagerConnection(WindowManager* window_manager)
53 : window_manager_(window_manager) {}
54 virtual ~WindowManagerConnection() {}
57 // Overridden from IWindowManager:
58 virtual void CloseWindow(Id view_id) OVERRIDE;
59 virtual void ShowKeyboard(Id view_id, RectPtr bounds) OVERRIDE;
60 virtual void HideKeyboard(Id view_id) OVERRIDE;
62 WindowManager* window_manager_;
64 DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection);
67 class NavigatorHostImpl : public InterfaceImpl<NavigatorHost> {
69 explicit NavigatorHostImpl(WindowManager* window_manager, Id view_id)
70 : window_manager_(window_manager), view_id_(view_id) {}
71 virtual ~NavigatorHostImpl() {
75 virtual void DidNavigateLocally(const mojo::String& url) OVERRIDE;
76 virtual void RequestNavigate(Target target, URLRequestPtr request) OVERRIDE;
78 WindowManager* window_manager_;
81 DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl);
84 class KeyboardManager : public KeyboardClient,
87 KeyboardManager() : view_manager_(NULL), view_(NULL) {
89 virtual ~KeyboardManager() {
91 view_->parent()->RemoveObserver(this);
94 View* view() { return view_; }
96 void Init(ApplicationImpl* application,
97 ViewManager* view_manager,
99 const gfx::Rect& bounds) {
100 view_manager_ = view_manager;
101 view_ = View::Create(view_manager);
102 view_->SetBounds(bounds);
103 parent->AddChild(view_);
104 view_->Embed("mojo:mojo_keyboard");
105 application->ConnectToService("mojo:mojo_keyboard", &keyboard_service_);
106 keyboard_service_.set_client(this);
107 parent->AddObserver(this);
110 void Show(Id view_id, const gfx::Rect& bounds) {
111 keyboard_service_->SetTarget(view_id);
112 view_->SetVisible(true);
115 void Hide(Id view_id) {
116 keyboard_service_->SetTarget(0);
117 view_->SetVisible(false);
122 virtual void OnKeyboardEvent(Id view_id,
124 int32_t flags) OVERRIDE {
125 View* view = view_manager_->GetViewById(view_id);
129 const bool is_char = code != ui::VKEY_BACK && code != ui::VKEY_RETURN;
131 const bool is_char = false;
134 view_manager_->DispatchEvent(
136 Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED,
137 static_cast<ui::KeyboardCode>(code),
140 view_manager_->DispatchEvent(
142 Event::From(ui::KeyEvent(static_cast<base::char16>(code),
143 static_cast<ui::KeyboardCode>(code),
146 view_manager_->DispatchEvent(
148 Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED,
149 static_cast<ui::KeyboardCode>(code),
153 // Overridden from ViewObserver:
154 virtual void OnViewBoundsChanged(View* parent,
155 const gfx::Rect& old_bounds,
156 const gfx::Rect& new_bounds) OVERRIDE {
157 gfx::Rect keyboard_bounds(view_->bounds());
158 keyboard_bounds.set_y(new_bounds.bottom() - keyboard_bounds.height());
159 keyboard_bounds.set_width(keyboard_bounds.width() +
160 new_bounds.width() - old_bounds.width());
161 view_->SetBounds(keyboard_bounds);
163 virtual void OnViewDestroyed(View* parent) OVERRIDE {
164 DCHECK_EQ(parent, view_->parent());
165 parent->RemoveObserver(this);
169 KeyboardServicePtr keyboard_service_;
170 ViewManager* view_manager_;
172 // View the keyboard is attached to.
175 DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
178 class RootLayoutManager : public ViewObserver {
180 RootLayoutManager(ViewManager* view_manager,
183 Id launcher_ui_view_id,
184 Id control_panel_view_id)
186 view_manager_(view_manager),
187 content_view_id_(content_view_id),
188 launcher_ui_view_id_(launcher_ui_view_id),
189 control_panel_view_id_(control_panel_view_id) {}
190 virtual ~RootLayoutManager() {
192 root_->RemoveObserver(this);
196 // Overridden from ViewObserver:
197 virtual void OnViewBoundsChanged(View* view,
198 const gfx::Rect& old_bounds,
199 const gfx::Rect& new_bounds) OVERRIDE {
200 DCHECK_EQ(view, root_);
202 View* content_view = view_manager_->GetViewById(content_view_id_);
203 content_view->SetBounds(new_bounds);
205 int delta_width = new_bounds.width() - old_bounds.width();
206 int delta_height = new_bounds.height() - old_bounds.height();
208 View* launcher_ui_view =
209 view_manager_->GetViewById(launcher_ui_view_id_);
210 gfx::Rect launcher_ui_bounds(launcher_ui_view->bounds());
211 launcher_ui_bounds.set_width(launcher_ui_bounds.width() + delta_width);
212 launcher_ui_view->SetBounds(launcher_ui_bounds);
214 View* control_panel_view =
215 view_manager_->GetViewById(control_panel_view_id_);
216 gfx::Rect control_panel_bounds(control_panel_view->bounds());
217 control_panel_bounds.set_x(control_panel_bounds.x() + delta_width);
218 control_panel_view->SetBounds(control_panel_bounds);
220 const View::Children& content_views = content_view->children();
221 View::Children::const_iterator iter = content_views.begin();
222 for(; iter != content_views.end(); ++iter) {
224 if (view->id() == control_panel_view->id() ||
225 view->id() == launcher_ui_view->id())
227 gfx::Rect view_bounds(view->bounds());
228 view_bounds.set_width(view_bounds.width() + delta_width);
229 view_bounds.set_height(view_bounds.height() + delta_height);
230 view->SetBounds(view_bounds);
233 virtual void OnViewDestroyed(View* view) OVERRIDE {
234 DCHECK_EQ(view, root_);
235 root_->RemoveObserver(this);
240 ViewManager* view_manager_;
241 const Id content_view_id_;
242 const Id launcher_ui_view_id_;
243 const Id control_panel_view_id_;
245 DISALLOW_COPY_AND_ASSIGN(RootLayoutManager);
248 class Window : public InterfaceFactory<NavigatorHost> {
250 Window(WindowManager* window_manager, View* view)
251 : window_manager_(window_manager), view_(view) {}
255 View* view() const { return view_; }
257 void Embed(const std::string& url) {
258 scoped_ptr<ServiceProviderImpl> service_provider_impl(
259 new ServiceProviderImpl());
260 service_provider_impl->AddService<NavigatorHost>(this);
261 view_->Embed(url, service_provider_impl.Pass());
265 // InterfaceFactory<NavigatorHost>
266 virtual void Create(ApplicationConnection* connection,
267 InterfaceRequest<NavigatorHost> request) OVERRIDE {
268 BindToRequest(new NavigatorHostImpl(window_manager_, view_->id()),
272 WindowManager* window_manager_;
277 : public ApplicationDelegate,
278 public DebugPanel::Delegate,
279 public ViewManagerDelegate,
280 public WindowManagerDelegate,
281 public ui::EventHandler {
284 : window_manager_factory_(this),
287 window_manager_app_(new WindowManagerApp(this, this)),
290 virtual ~WindowManager() {
291 // host() may be destroyed by the time we get here.
292 // TODO: figure out a way to always cleanly remove handler.
293 if (window_manager_app_->host())
294 window_manager_app_->host()->window()->RemovePreTargetHandler(this);
297 void CloseWindow(Id view_id) {
298 WindowVector::iterator iter = GetWindowByViewId(view_id);
299 DCHECK(iter != windows_.end());
300 Window* window = *iter;
301 windows_.erase(iter);
302 window->view()->Destroy();
305 void ShowKeyboard(Id view_id, const gfx::Rect& bounds) {
306 // TODO: this needs to validate |view_id|. That is, it shouldn't assume
307 // |view_id| is valid and it also needs to make sure the client that sent
308 // this really owns |view_id|.
309 // TODO: honor |bounds|.
310 if (!keyboard_manager_) {
311 keyboard_manager_.reset(new KeyboardManager);
312 View* parent = view_manager_->GetRoots().back();
313 int ideal_height = 200;
314 // TODO(sky): 10 is a bit of a hack here. There is a bug that causes
315 // white strips to appear when 0 is used. Figure this out!
316 const gfx::Rect keyboard_bounds(
317 10, parent->bounds().height() - ideal_height,
318 parent->bounds().width() - 20, ideal_height);
319 keyboard_manager_->Init(app_, view_manager_, parent, keyboard_bounds);
321 keyboard_manager_->Show(view_id, bounds);
324 void HideKeyboard(Id view_id) {
325 // See comment in ShowKeyboard() about validating args.
326 if (keyboard_manager_)
327 keyboard_manager_->Hide(view_id);
330 void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) {
331 LOG(ERROR) << "DidNavigateLocally: source_view_id: " << source_view_id
332 << " url: " << url.To<std::string>();
335 // Overridden from DebugPanel::Delegate:
336 virtual void CloseTopWindow() OVERRIDE {
337 if (!windows_.empty())
338 CloseWindow(windows_.back()->view()->id());
341 virtual void RequestNavigate(uint32 source_view_id,
343 URLRequestPtr request) OVERRIDE {
344 OnLaunch(source_view_id, target, request->url);
348 typedef std::vector<Window*> WindowVector;
350 // Overridden from ApplicationDelegate:
351 virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
353 views_init_.reset(new ViewsInit);
354 window_manager_app_->Initialize(app);
357 virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
359 connection->AddService(&window_manager_factory_);
360 window_manager_app_->ConfigureIncomingConnection(connection);
364 // Overridden from ViewManagerDelegate:
365 virtual void OnEmbed(ViewManager* view_manager,
367 ServiceProviderImpl* exported_services,
368 scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
369 DCHECK(!view_manager_);
370 view_manager_ = view_manager;
372 View* view = View::Create(view_manager_);
373 root->AddChild(view);
374 view->SetBounds(gfx::Rect(root->bounds().size()));
375 content_view_id_ = view->id();
377 Id launcher_ui_id = CreateLauncherUI();
378 Id control_panel_id = CreateControlPanel(view);
380 root_layout_manager_.reset(
381 new RootLayoutManager(view_manager, root,
385 root->AddObserver(root_layout_manager_.get());
387 window_manager_app_->host()->window()->AddPreTargetHandler(this);
389 virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
390 DCHECK_EQ(view_manager_, view_manager);
391 view_manager_ = NULL;
392 base::MessageLoop::current()->Quit();
395 // Overridden from WindowManagerDelegate:
398 InterfaceRequest<ServiceProvider> service_provider) OVERRIDE {
399 const Id kInvalidSourceViewId = 0;
400 OnLaunch(kInvalidSourceViewId, TARGET_DEFAULT, url);
402 virtual void DispatchEvent(EventPtr event) MOJO_OVERRIDE {}
404 // Overridden from ui::EventHandler:
405 virtual void OnEvent(ui::Event* event) OVERRIDE {
406 View* view = WindowManagerApp::GetViewForWindow(
407 static_cast<aura::Window*>(event->target()));
408 if (event->type() == ui::ET_MOUSE_PRESSED &&
409 !IsDescendantOfKeyboard(view)) {
414 void OnLaunch(uint32 source_view_id,
415 Target requested_target,
416 const mojo::String& url) {
417 Target target = debug_panel_->navigation_target();
418 if (target == TARGET_DEFAULT) {
419 if (requested_target != TARGET_DEFAULT) {
420 target = requested_target;
422 // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
424 target = TARGET_SOURCE_NODE;
428 Window* dest_view = NULL;
429 if (target == TARGET_SOURCE_NODE) {
430 WindowVector::iterator source_view = GetWindowByViewId(source_view_id);
431 bool app_initiated = source_view != windows_.end();
433 dest_view = *source_view;
434 else if (!windows_.empty())
435 dest_view = windows_.back();
439 dest_view = CreateWindow();
440 windows_.push_back(dest_view);
443 dest_view->Embed(url);
446 // TODO(beng): proper layout manager!!
447 Id CreateLauncherUI() {
448 View* view = view_manager_->GetViewById(content_view_id_);
449 gfx::Rect bounds = view->bounds();
450 bounds.Inset(kBorderInset, kBorderInset);
451 bounds.set_height(kTextfieldHeight);
452 launcher_ui_ = CreateWindow(bounds);
453 launcher_ui_->Embed("mojo:mojo_browser");
454 return launcher_ui_->view()->id();
457 Window* CreateWindow() {
458 View* view = view_manager_->GetViewById(content_view_id_);
459 gfx::Rect bounds(kBorderInset,
460 2 * kBorderInset + kTextfieldHeight,
461 view->bounds().width() - 3 * kBorderInset -
463 view->bounds().height() -
464 (3 * kBorderInset + kTextfieldHeight));
465 if (!windows_.empty()) {
466 gfx::Point position = windows_.back()->view()->bounds().origin();
467 position.Offset(35, 35);
468 bounds.set_origin(position);
470 return CreateWindow(bounds);
473 Window* CreateWindow(const gfx::Rect& bounds) {
474 View* content = view_manager_->GetViewById(content_view_id_);
475 View* view = View::Create(view_manager_);
476 content->AddChild(view);
477 view->SetBounds(bounds);
479 return new Window(this, view);
482 bool IsDescendantOfKeyboard(View* target) {
483 return keyboard_manager_.get() &&
484 keyboard_manager_->view()->Contains(target);
487 Id CreateControlPanel(View* root) {
488 View* view = View::Create(view_manager_);
489 root->AddChild(view);
491 gfx::Rect bounds(root->bounds().width() - kControlPanelWidth -
493 kBorderInset * 2 + kTextfieldHeight,
495 root->bounds().height() - kBorderInset * 3 -
497 view->SetBounds(bounds);
499 debug_panel_ = new DebugPanel(this, view);
503 WindowVector::iterator GetWindowByViewId(Id view_id) {
504 for (std::vector<Window*>::iterator iter = windows_.begin();
505 iter != windows_.end();
507 if ((*iter)->view()->id() == view_id) {
511 return windows_.end();
514 InterfaceFactoryImplWithContext<WindowManagerConnection, WindowManager>
515 window_manager_factory_;
517 scoped_ptr<ViewsInit> views_init_;
518 DebugPanel* debug_panel_;
519 Window* launcher_ui_;
520 WindowVector windows_;
521 ViewManager* view_manager_;
522 scoped_ptr<RootLayoutManager> root_layout_manager_;
524 scoped_ptr<WindowManagerApp> window_manager_app_;
526 // Id of the view most content is added to. The keyboard is NOT added here.
529 scoped_ptr<KeyboardManager> keyboard_manager_;
530 ApplicationImpl* app_;
532 DISALLOW_COPY_AND_ASSIGN(WindowManager);
535 void WindowManagerConnection::CloseWindow(Id view_id) {
536 window_manager_->CloseWindow(view_id);
539 void WindowManagerConnection::ShowKeyboard(Id view_id, RectPtr bounds) {
540 window_manager_->ShowKeyboard(view_id, bounds.To<gfx::Rect>());
543 void WindowManagerConnection::HideKeyboard(Id view_id) {
544 window_manager_->HideKeyboard(view_id);
547 void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) {
548 window_manager_->DidNavigateLocally(view_id_, url);
551 void NavigatorHostImpl::RequestNavigate(Target target, URLRequestPtr request) {
552 window_manager_->RequestNavigate(view_id_, target, request.Pass());
555 } // namespace examples
558 MojoResult MojoMain(MojoHandle shell_handle) {
559 mojo::ApplicationRunnerChromium runner(new mojo::examples::WindowManager);
560 return runner.Run(shell_handle);