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