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/strings/string_util.h"
7#include "base/strings/utf_string_conversions.h"
8#include "mojo/application/application_runner_chromium.h"
9#include "mojo/common/common_type_converters.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/connect.h"
16#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
17#include "mojo/services/public/cpp/view_manager/view.h"
18#include "mojo/services/public/cpp/view_manager/view_manager.h"
19#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
20#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
21#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
22#include "mojo/views/native_widget_view_manager.h"
23#include "mojo/views/views_init.h"
24#include "ui/aura/client/focus_client.h"
25#include "ui/aura/window.h"
26#include "ui/events/event.h"
27#include "ui/views/background.h"
28#include "ui/views/controls/textfield/textfield.h"
29#include "ui/views/controls/textfield/textfield_controller.h"
30#include "ui/views/focus/focus_manager.h"
31#include "ui/views/layout/layout_manager.h"
32#include "ui/views/widget/widget.h"
33#include "ui/views/widget/widget_delegate.h"
34#include "ui/views/widget/widget_observer.h"
35#include "url/gurl.h"
36
37namespace mojo {
38namespace examples {
39
40class BrowserLayoutManager : public views::LayoutManager {
41 public:
42  BrowserLayoutManager() {}
43  virtual ~BrowserLayoutManager() {}
44
45 private:
46  // Overridden from views::LayoutManager:
47  virtual void Layout(views::View* host) OVERRIDE {
48    // Browser view has one child, a text input field.
49    DCHECK_EQ(1, host->child_count());
50    views::View* text_field = host->child_at(0);
51    gfx::Size ps = text_field->GetPreferredSize();
52    text_field->SetBoundsRect(gfx::Rect(host->width(), ps.height()));
53  }
54  virtual gfx::Size GetPreferredSize(const views::View* host) const OVERRIDE {
55    return gfx::Size();
56  }
57
58  DISALLOW_COPY_AND_ASSIGN(BrowserLayoutManager);
59};
60
61// KeyboardManager handles notifying the windowmanager when views are focused.
62// To use create one and KeyboardManager will take care of all other details.
63//
64// TODO(sky): it would be nice if this were put in NativeWidgetViewManager, but
65// that requires NativeWidgetViewManager to take an IWindowManager. That may be
66// desirable anyway...
67class KeyboardManager
68    : public views::FocusChangeListener,
69      public ui::EventHandler,
70      public views::WidgetObserver {
71 public:
72  KeyboardManager(views::Widget* widget,
73                  IWindowManager* window_manager,
74                  View* view)
75      : widget_(widget),
76        window_manager_(window_manager),
77        view_(view),
78        last_view_id_(0),
79        focused_view_(NULL) {
80    widget_->GetFocusManager()->AddFocusChangeListener(this);
81    widget_->AddObserver(this);
82    widget_->GetNativeView()->AddPostTargetHandler(this);
83  }
84
85 private:
86  virtual ~KeyboardManager() {
87    widget_->GetFocusManager()->RemoveFocusChangeListener(this);
88    widget_->GetNativeView()->RemovePostTargetHandler(this);
89    widget_->RemoveObserver(this);
90
91    HideKeyboard();
92  }
93
94  void ShowKeyboard(views::View* view) {
95    if (focused_view_ == view)
96      return;
97
98    const gfx::Rect bounds_in_widget =
99        view->ConvertRectToWidget(gfx::Rect(view->bounds().size()));
100    last_view_id_ = view_->id();
101    window_manager_->ShowKeyboard(last_view_id_,
102                                  Rect::From(bounds_in_widget));
103    // TODO(sky): listen for view to be removed.
104    focused_view_ = view;
105  }
106
107  void HideKeyboard() {
108    if (!focused_view_)
109      return;
110
111    window_manager_->HideKeyboard(last_view_id_);
112    last_view_id_ = 0;
113    focused_view_ = NULL;
114  }
115
116  // views::FocusChangeListener:
117  virtual void OnWillChangeFocus(views::View* focused_before,
118                                 views::View* focused_now) OVERRIDE {
119  }
120  virtual void OnDidChangeFocus(views::View* focused_before,
121                                views::View* focused_now) OVERRIDE {
122    if (focused_view_ && focused_now != focused_view_)
123      HideKeyboard();
124  }
125
126  // ui::EventHandler:
127  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
128    views::View* focused_now = widget_->GetFocusManager()->GetFocusedView();
129    if (focused_now &&
130        focused_now->GetClassName() == views::Textfield::kViewClassName &&
131        (event->flags() & ui::EF_FROM_TOUCH) != 0) {
132      ShowKeyboard(focused_now);
133    }
134  }
135
136  // views::WidgetObserver:
137  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
138    delete this;
139  }
140
141  views::Widget* widget_;
142  IWindowManager* window_manager_;
143  View* view_;
144  Id last_view_id_;
145  views::View* focused_view_;
146
147  DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
148};
149
150// This is the basics of creating a views widget with a textfield.
151// TODO: cleanup!
152class Browser : public ApplicationDelegate,
153                public ViewManagerDelegate,
154                public views::TextfieldController,
155                public ViewObserver {
156 public:
157  Browser()
158      : view_manager_(NULL),
159        root_(NULL),
160        widget_(NULL) {}
161
162  virtual ~Browser() {
163    if (root_)
164      root_->RemoveObserver(this);
165  }
166
167 private:
168  // Overridden from ApplicationDelegate:
169  virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
170    view_manager_client_factory_.reset(
171        new ViewManagerClientFactory(app->shell(), this));
172    views_init_.reset(new ViewsInit);
173    app->ConnectToService("mojo:mojo_window_manager", &window_manager_);
174 }
175
176  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
177      MOJO_OVERRIDE {
178    connection->AddService(view_manager_client_factory_.get());
179    return true;
180  }
181
182  void CreateWidget(View* view) {
183    views::Textfield* textfield = new views::Textfield;
184    textfield->set_controller(this);
185
186    views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
187    widget_delegate->GetContentsView()->set_background(
188        views::Background::CreateSolidBackground(SK_ColorBLUE));
189    widget_delegate->GetContentsView()->AddChildView(textfield);
190    widget_delegate->GetContentsView()->SetLayoutManager(
191        new BrowserLayoutManager);
192
193    widget_ = new views::Widget;
194    views::Widget::InitParams params(
195        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
196    params.native_widget = new NativeWidgetViewManager(widget_, view);
197    params.delegate = widget_delegate;
198    params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height());
199    widget_->Init(params);
200    // KeyboardManager handles deleting itself when the widget is destroyed.
201    new KeyboardManager(widget_, window_manager_.get(), view);
202    widget_->Show();
203    textfield->RequestFocus();
204  }
205
206  // ViewManagerDelegate:
207  virtual void OnEmbed(ViewManager* view_manager,
208                       View* root,
209                       ServiceProviderImpl* exported_services,
210                       scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
211    // TODO: deal with OnEmbed() being invoked multiple times.
212    ConnectToService(imported_services.get(), &navigator_host_);
213    view_manager_ = view_manager;
214    root_ = root;
215    root_->AddObserver(this);
216    root_->SetFocus();
217    CreateWidget(root_);
218  }
219  virtual void OnViewManagerDisconnected(
220      ViewManager* view_manager) OVERRIDE {
221    DCHECK_EQ(view_manager_, view_manager);
222    view_manager_ = NULL;
223    base::MessageLoop::current()->Quit();
224  }
225
226  // views::TextfieldController:
227  virtual bool HandleKeyEvent(views::Textfield* sender,
228                              const ui::KeyEvent& key_event) OVERRIDE {
229    if (key_event.key_code() == ui::VKEY_RETURN) {
230      GURL url(sender->text());
231      printf("User entered this URL: %s\n", url.spec().c_str());
232      URLRequestPtr request(URLRequest::New());
233      request->url = String::From(url);
234      navigator_host_->RequestNavigate(TARGET_NEW_NODE, request.Pass());
235    }
236    return false;
237  }
238
239  // ViewObserver:
240  virtual void OnViewFocusChanged(View* gained_focus,
241                                  View* lost_focus) OVERRIDE {
242    aura::client::FocusClient* focus_client =
243        aura::client::GetFocusClient(widget_->GetNativeView());
244    if (lost_focus == root_)
245      focus_client->FocusWindow(NULL);
246    else if (gained_focus == root_)
247      focus_client->FocusWindow(widget_->GetNativeView());
248  }
249  virtual void OnViewDestroyed(View* view) OVERRIDE {
250    DCHECK_EQ(root_, view);
251    view->RemoveObserver(this);
252    root_ = NULL;
253  }
254
255  scoped_ptr<ViewsInit> views_init_;
256
257  ViewManager* view_manager_;
258  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
259  View* root_;
260  views::Widget* widget_;
261  NavigatorHostPtr navigator_host_;
262  IWindowManagerPtr window_manager_;
263
264  DISALLOW_COPY_AND_ASSIGN(Browser);
265};
266
267}  // namespace examples
268}  // namespace mojo
269
270MojoResult MojoMain(MojoHandle shell_handle) {
271  mojo::ApplicationRunnerChromium runner(new mojo::examples::Browser);
272  return runner.Run(shell_handle);
273}
274