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.
4
5#include "base/basictypes.h"
6#include "base/bind.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
33#if defined CreateWindow
34#undef CreateWindow
35#endif
36
37namespace mojo {
38namespace examples {
39
40class WindowManager;
41
42namespace {
43
44const int kBorderInset = 25;
45const int kControlPanelWidth = 200;
46const int kTextfieldHeight = 25;
47
48}  // namespace
49
50class WindowManagerConnection : public InterfaceImpl<IWindowManager> {
51 public:
52  explicit WindowManagerConnection(WindowManager* window_manager)
53      : window_manager_(window_manager) {}
54  virtual ~WindowManagerConnection() {}
55
56 private:
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;
61
62  WindowManager* window_manager_;
63
64  DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection);
65};
66
67class NavigatorHostImpl : public InterfaceImpl<NavigatorHost> {
68 public:
69  explicit NavigatorHostImpl(WindowManager* window_manager, Id view_id)
70      : window_manager_(window_manager), view_id_(view_id) {}
71  virtual ~NavigatorHostImpl() {
72  }
73
74 private:
75  virtual void DidNavigateLocally(const mojo::String& url) OVERRIDE;
76  virtual void RequestNavigate(Target target, URLRequestPtr request) OVERRIDE;
77
78  WindowManager* window_manager_;
79  Id view_id_;
80
81  DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl);
82};
83
84class KeyboardManager : public KeyboardClient,
85                        public ViewObserver {
86 public:
87  KeyboardManager() : view_manager_(NULL), view_(NULL) {
88  }
89  virtual ~KeyboardManager() {
90    if (view_)
91      view_->parent()->RemoveObserver(this);
92  }
93
94  View* view() { return view_; }
95
96  void Init(ApplicationImpl* application,
97            ViewManager* view_manager,
98            View* parent,
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);
108  }
109
110  void Show(Id view_id, const gfx::Rect& bounds) {
111    keyboard_service_->SetTarget(view_id);
112    view_->SetVisible(true);
113  }
114
115  void Hide(Id view_id) {
116    keyboard_service_->SetTarget(0);
117    view_->SetVisible(false);
118  }
119
120 private:
121  // KeyboardClient:
122  virtual void OnKeyboardEvent(Id view_id,
123                               int32_t code,
124                               int32_t flags) OVERRIDE {
125    View* view = view_manager_->GetViewById(view_id);
126    if (!view)
127      return;
128#if defined(OS_WIN)
129    const bool is_char = code != ui::VKEY_BACK && code != ui::VKEY_RETURN;
130#else
131    const bool is_char = false;
132#endif
133    if (is_char) {
134      view_manager_->DispatchEvent(
135          view,
136          Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED,
137                                   static_cast<ui::KeyboardCode>(code),
138                                   flags)));
139    } else {
140      view_manager_->DispatchEvent(
141          view,
142          Event::From(ui::KeyEvent(static_cast<base::char16>(code),
143                                   static_cast<ui::KeyboardCode>(code),
144                                   flags)));
145    }
146    view_manager_->DispatchEvent(
147        view,
148        Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED,
149                                 static_cast<ui::KeyboardCode>(code),
150                                 flags)));
151  }
152
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);
162  }
163  virtual void OnViewDestroyed(View* parent) OVERRIDE {
164    DCHECK_EQ(parent, view_->parent());
165    parent->RemoveObserver(this);
166    view_ = NULL;
167  }
168
169  KeyboardServicePtr keyboard_service_;
170  ViewManager* view_manager_;
171
172  // View the keyboard is attached to.
173  View* view_;
174
175  DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
176};
177
178class RootLayoutManager : public ViewObserver {
179 public:
180  RootLayoutManager(ViewManager* view_manager,
181                    View* root,
182                    Id content_view_id,
183                    Id launcher_ui_view_id,
184                    Id control_panel_view_id)
185      : root_(root),
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() {
191    if (root_)
192      root_->RemoveObserver(this);
193  }
194
195 private:
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_);
201
202    View* content_view = view_manager_->GetViewById(content_view_id_);
203    content_view->SetBounds(new_bounds);
204
205    int delta_width = new_bounds.width() - old_bounds.width();
206    int delta_height = new_bounds.height() - old_bounds.height();
207
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);
213
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);
219
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) {
223      View* view = *iter;
224      if (view->id() == control_panel_view->id() ||
225          view->id() == launcher_ui_view->id())
226        continue;
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);
231    }
232  }
233  virtual void OnViewDestroyed(View* view) OVERRIDE {
234    DCHECK_EQ(view, root_);
235    root_->RemoveObserver(this);
236    root_ = NULL;
237  }
238
239  View* root_;
240  ViewManager* view_manager_;
241  const Id content_view_id_;
242  const Id launcher_ui_view_id_;
243  const Id control_panel_view_id_;
244
245  DISALLOW_COPY_AND_ASSIGN(RootLayoutManager);
246};
247
248class Window : public InterfaceFactory<NavigatorHost> {
249 public:
250  Window(WindowManager* window_manager, View* view)
251      : window_manager_(window_manager), view_(view) {}
252
253  virtual ~Window() {}
254
255  View* view() const { return view_; }
256
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());
262  }
263
264 private:
265  // InterfaceFactory<NavigatorHost>
266  virtual void Create(ApplicationConnection* connection,
267                      InterfaceRequest<NavigatorHost> request) OVERRIDE {
268    BindToRequest(new NavigatorHostImpl(window_manager_, view_->id()),
269                  &request);
270  }
271
272  WindowManager* window_manager_;
273  View* view_;
274};
275
276class WindowManager
277    : public ApplicationDelegate,
278      public DebugPanel::Delegate,
279      public ViewManagerDelegate,
280      public WindowManagerDelegate,
281      public ui::EventHandler {
282 public:
283  WindowManager()
284      : window_manager_factory_(this),
285        launcher_ui_(NULL),
286        view_manager_(NULL),
287        window_manager_app_(new WindowManagerApp(this, this)),
288        app_(NULL) {}
289
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);
295  }
296
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();
303  }
304
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);
320    }
321    keyboard_manager_->Show(view_id, bounds);
322  }
323
324  void HideKeyboard(Id view_id) {
325    // See comment in ShowKeyboard() about validating args.
326    if (keyboard_manager_)
327      keyboard_manager_->Hide(view_id);
328  }
329
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>();
333  }
334
335  // Overridden from DebugPanel::Delegate:
336  virtual void CloseTopWindow() OVERRIDE {
337    if (!windows_.empty())
338      CloseWindow(windows_.back()->view()->id());
339  }
340
341  virtual void RequestNavigate(uint32 source_view_id,
342                               Target target,
343                               URLRequestPtr request) OVERRIDE {
344    OnLaunch(source_view_id, target, request->url);
345  }
346
347 private:
348  typedef std::vector<Window*> WindowVector;
349
350  // Overridden from ApplicationDelegate:
351  virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
352    app_ = app;
353    views_init_.reset(new ViewsInit);
354    window_manager_app_->Initialize(app);
355  }
356
357  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
358      MOJO_OVERRIDE {
359    connection->AddService(&window_manager_factory_);
360    window_manager_app_->ConfigureIncomingConnection(connection);
361    return true;
362  }
363
364  // Overridden from ViewManagerDelegate:
365  virtual void OnEmbed(ViewManager* view_manager,
366                       View* root,
367                       ServiceProviderImpl* exported_services,
368                       scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
369    DCHECK(!view_manager_);
370    view_manager_ = view_manager;
371
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();
376
377    Id launcher_ui_id = CreateLauncherUI();
378    Id control_panel_id = CreateControlPanel(view);
379
380    root_layout_manager_.reset(
381        new RootLayoutManager(view_manager, root,
382                              content_view_id_,
383                              launcher_ui_id,
384                              control_panel_id));
385    root->AddObserver(root_layout_manager_.get());
386
387    window_manager_app_->host()->window()->AddPreTargetHandler(this);
388  }
389  virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
390    DCHECK_EQ(view_manager_, view_manager);
391    view_manager_ = NULL;
392    base::MessageLoop::current()->Quit();
393  }
394
395  // Overridden from WindowManagerDelegate:
396  virtual void Embed(
397      const String& url,
398      InterfaceRequest<ServiceProvider> service_provider) OVERRIDE {
399    const Id kInvalidSourceViewId = 0;
400    OnLaunch(kInvalidSourceViewId, TARGET_DEFAULT, url);
401  }
402  virtual void DispatchEvent(EventPtr event) MOJO_OVERRIDE {}
403
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)) {
410      view->SetFocus();
411    }
412  }
413
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;
421      } else {
422        // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
423        // are different?
424        target = TARGET_SOURCE_NODE;
425      }
426    }
427
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();
432      if (app_initiated)
433        dest_view = *source_view;
434      else if (!windows_.empty())
435        dest_view = windows_.back();
436    }
437
438    if (!dest_view) {
439      dest_view = CreateWindow();
440      windows_.push_back(dest_view);
441    }
442
443    dest_view->Embed(url);
444  }
445
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();
455  }
456
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 -
462                         kControlPanelWidth,
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);
469    }
470    return CreateWindow(bounds);
471  }
472
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);
478    view->SetFocus();
479    return new Window(this, view);
480  }
481
482  bool IsDescendantOfKeyboard(View* target) {
483    return keyboard_manager_.get() &&
484        keyboard_manager_->view()->Contains(target);
485  }
486
487  Id CreateControlPanel(View* root) {
488    View* view = View::Create(view_manager_);
489    root->AddChild(view);
490
491    gfx::Rect bounds(root->bounds().width() - kControlPanelWidth -
492                         kBorderInset,
493                     kBorderInset * 2 + kTextfieldHeight,
494                     kControlPanelWidth,
495                     root->bounds().height() - kBorderInset * 3 -
496                         kTextfieldHeight);
497    view->SetBounds(bounds);
498
499    debug_panel_ = new DebugPanel(this, view);
500    return view->id();
501  }
502
503  WindowVector::iterator GetWindowByViewId(Id view_id) {
504    for (std::vector<Window*>::iterator iter = windows_.begin();
505         iter != windows_.end();
506         ++iter) {
507      if ((*iter)->view()->id() == view_id) {
508        return iter;
509      }
510    }
511    return windows_.end();
512  }
513
514  InterfaceFactoryImplWithContext<WindowManagerConnection, WindowManager>
515      window_manager_factory_;
516
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_;
523
524  scoped_ptr<WindowManagerApp> window_manager_app_;
525
526  // Id of the view most content is added to. The keyboard is NOT added here.
527  Id content_view_id_;
528
529  scoped_ptr<KeyboardManager> keyboard_manager_;
530  ApplicationImpl* app_;
531
532  DISALLOW_COPY_AND_ASSIGN(WindowManager);
533};
534
535void WindowManagerConnection::CloseWindow(Id view_id) {
536  window_manager_->CloseWindow(view_id);
537}
538
539void WindowManagerConnection::ShowKeyboard(Id view_id, RectPtr bounds) {
540  window_manager_->ShowKeyboard(view_id, bounds.To<gfx::Rect>());
541}
542
543void WindowManagerConnection::HideKeyboard(Id view_id) {
544  window_manager_->HideKeyboard(view_id);
545}
546
547void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) {
548  window_manager_->DidNavigateLocally(view_id_, url);
549}
550
551void NavigatorHostImpl::RequestNavigate(Target target, URLRequestPtr request) {
552  window_manager_->RequestNavigate(view_id_, target, request.Pass());
553}
554
555}  // namespace examples
556}  // namespace mojo
557
558MojoResult MojoMain(MojoHandle shell_handle) {
559  mojo::ApplicationRunnerChromium runner(new mojo::examples::WindowManager);
560  return runner.Run(shell_handle);
561}
562