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