diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/ui/views/widget | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/ui/views/widget')
93 files changed, 19918 insertions, 0 deletions
diff --git a/chromium/ui/views/widget/aero_tooltip_manager.cc b/chromium/ui/views/widget/aero_tooltip_manager.cc new file mode 100644 index 00000000000..b1719def27f --- /dev/null +++ b/chromium/ui/views/widget/aero_tooltip_manager.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/aero_tooltip_manager.h" + +#include <windows.h> +#include <commctrl.h> +#include <shlobj.h> + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/win/dpi.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/point.h" + +namespace views { + +/////////////////////////////////////////////////////////////////////////////// +// AeroTooltipManager, public: + +AeroTooltipManager::AeroTooltipManager(Widget* widget) + : TooltipManagerWin(widget), + initial_delay_(0) { +} + +AeroTooltipManager::~AeroTooltipManager() { + if (initial_timer_) + initial_timer_->Disown(); +} + +void AeroTooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) { + if (u_msg == WM_MOUSELEAVE) { + last_mouse_pos_.SetPoint(-1, -1); + UpdateTooltip(); + return; + } + + if (initial_timer_) + initial_timer_->Disown(); + + if (u_msg == WM_MOUSEMOVE || u_msg == WM_NCMOUSEMOVE) { + gfx::Point mouse_pos_in_pixels(l_param); + gfx::Point mouse_pos = ui::win::ScreenToDIPPoint(mouse_pos_in_pixels); + if (u_msg == WM_NCMOUSEMOVE) { + // NC message coordinates are in screen coordinates. + POINT temp = mouse_pos_in_pixels.ToPOINT(); + ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1); + mouse_pos_in_pixels.SetPoint(temp.x, temp.y); + mouse_pos = ui::win::ScreenToDIPPoint(mouse_pos_in_pixels); + } + if (last_mouse_pos_ != mouse_pos) { + last_mouse_pos_ = mouse_pos; + HideKeyboardTooltip(); + UpdateTooltip(mouse_pos); + } + + // Delay opening of the tooltip just in case the user moves their + // mouse to another control. We defer this from Init because we get + // zero if we query it too soon. + if (!initial_delay_) { + initial_delay_ = static_cast<int>( + ::SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_INITIAL, 0)); + } + initial_timer_ = new InitialTimer(this); + initial_timer_->Start(initial_delay_); + } else { + // Hide the tooltip and cancel any timers. + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, false, (LPARAM)&toolinfo_); + return; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// AeroTooltipManager, private: + +void AeroTooltipManager::OnTimer() { + initial_timer_ = NULL; + + POINT pt = last_mouse_pos_.ToPOINT(); + ::ClientToScreen(GetParent(), &pt); + + // Set the position and visibility. + if (!tooltip_showing_) { + ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); + ::SendMessage(tooltip_hwnd_, TTM_TRACKPOSITION, 0, MAKELPARAM(pt.x, pt.y)); + ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, true, (LPARAM)&toolinfo_); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// AeroTooltipManager::InitialTimer + +AeroTooltipManager::InitialTimer::InitialTimer(AeroTooltipManager* manager) + : manager_(manager) { +} + +void AeroTooltipManager::InitialTimer::Start(int time) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&InitialTimer::Execute, this), + base::TimeDelta::FromMilliseconds(time)); +} + +void AeroTooltipManager::InitialTimer::Disown() { + manager_ = NULL; +} + +void AeroTooltipManager::InitialTimer::Execute() { + if (manager_) + manager_->OnTimer(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/aero_tooltip_manager.h b/chromium/ui/views/widget/aero_tooltip_manager.h new file mode 100644 index 00000000000..0f9db20faa4 --- /dev/null +++ b/chromium/ui/views/widget/aero_tooltip_manager.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_ +#define UI_VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_ + +#include "base/memory/ref_counted.h" +#include "ui/views/widget/tooltip_manager_win.h" + +namespace views { + +/////////////////////////////////////////////////////////////////////////////// +// AeroTooltipManager +// +// Default Windows tooltips are broken when using our custom window frame +// - as soon as the tooltip receives a WM_MOUSEMOVE event, it starts spewing +// NCHITTEST messages at its parent window (us). These messages have random +// x/y coordinates and can't be ignored, as the DwmDefWindowProc uses +// NCHITTEST messages to determine how to highlight the caption buttons +// (the buttons then flicker as the hit tests sent by the user's mouse +// trigger different effects to those sent by the tooltip). +// +// So instead, we have to partially implement tooltips ourselves using +// TTF_TRACKed tooltips. +// +// TODO(glen): Resolve this with Microsoft. +class AeroTooltipManager : public TooltipManagerWin { + public: + explicit AeroTooltipManager(Widget* widget); + virtual ~AeroTooltipManager(); + + virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param); + + private: + void OnTimer(); + + class InitialTimer : public base::RefCounted<InitialTimer> { + public: + explicit InitialTimer(AeroTooltipManager* manager); + void Start(int time); + void Disown(); + void Execute(); + + private: + friend class base::RefCounted<InitialTimer>; + + ~InitialTimer() {} + + AeroTooltipManager* manager_; + }; + + int initial_delay_; + scoped_refptr<InitialTimer> initial_timer_; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_ diff --git a/chromium/ui/views/widget/child_window_message_processor.cc b/chromium/ui/views/widget/child_window_message_processor.cc new file mode 100644 index 00000000000..dc497560093 --- /dev/null +++ b/chromium/ui/views/widget/child_window_message_processor.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/child_window_message_processor.h" + +#include "base/logging.h" +#include "ui/base/view_prop.h" + +namespace views { + +static const char* const kChildWindowKey = "__CHILD_WINDOW_MESSAGE_PROCESSOR__"; + +// static +ui::ViewProp* ChildWindowMessageProcessor::Register( + HWND hwnd, + ChildWindowMessageProcessor* processor) { + DCHECK(processor); + return new ui::ViewProp(hwnd, kChildWindowKey, processor); +} + +// static +ChildWindowMessageProcessor* ChildWindowMessageProcessor::Get(HWND hwnd) { + return reinterpret_cast<ChildWindowMessageProcessor*>( + ui::ViewProp::GetValue(hwnd, kChildWindowKey)); +} + +} // namespace diff --git a/chromium/ui/views/widget/child_window_message_processor.h b/chromium/ui/views/widget/child_window_message_processor.h new file mode 100644 index 00000000000..112d9d835d4 --- /dev/null +++ b/chromium/ui/views/widget/child_window_message_processor.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_ +#define UI_VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_ + +#include <windows.h> + +namespace ui { +class ViewProp; +} + +namespace views { + +// Windows sends a handful of messages to the parent window rather than the +// window itself. For example, selection changes of a rich edit (EN_SELCHANGE) +// are sent to the parent, not the window. Typically such message are best +// dealt with by the window rather than the parent. NativeWidgetWin allows for +// registering a ChildWindowMessageProcessor to handle such messages. +class ChildWindowMessageProcessor { + public: + // Registers |processor| for |hwnd|. The caller takes ownership of the + // returned object. + static ui::ViewProp* Register(HWND hwnd, + ChildWindowMessageProcessor* processor); + + // Returns the ChildWindowMessageProcessor for |hwnd|, NULL if there isn't + // one. + static ChildWindowMessageProcessor* Get(HWND hwnd); + + // Invoked for any messages that are sent to the parent and originated from + // the HWND this ChildWindowMessageProcessor was registered for. Returns true + // if the message was handled with a valid result in |result|. Returns false + // if the message was not handled. + virtual bool ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) = 0; + + protected: + virtual ~ChildWindowMessageProcessor() {} +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_ diff --git a/chromium/ui/views/widget/desktop_aura/OWNERS b/chromium/ui/views/widget/desktop_aura/OWNERS new file mode 100644 index 00000000000..89630b157dd --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/OWNERS @@ -0,0 +1,5 @@ +# Elliot is the owner of all the X11 stuff. +per-file *x11.cc=erg@chromium.org +per-file *x11.h=erg@chromium.org +per-file x11*=erg@chromium.org +per-file x11*=erg@chromium.org diff --git a/chromium/ui/views/widget/desktop_aura/README.chromium b/chromium/ui/views/widget/desktop_aura/README.chromium new file mode 100644 index 00000000000..5e0ff711590 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/README.chromium @@ -0,0 +1,2 @@ +This directory contains the views::NativeWidget implementation used for the
+Desktop Aura port, and required supporting infrastructure.
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_activation_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_activation_client.cc new file mode 100644 index 00000000000..dc8f062cc32 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_activation_client.cc @@ -0,0 +1,190 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_activation_client.h" + +#include "base/auto_reset.h" +#include "base/compiler_specific.h" +#include "ui/aura/client/activation_delegate.h" +#include "ui/aura/client/activation_change_observer.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" + +namespace views { + +namespace { + +aura::Window* FindFocusableWindowFor(aura::Window* window) { + while (window && !window->CanFocus()) + window = window->parent(); + return window; +} + +} // namespace + +DesktopActivationClient::DesktopActivationClient(aura::RootWindow* root_window) + : root_window_(root_window), + current_active_(NULL), + updating_activation_(false), + observer_manager_(this) { + aura::client::GetFocusClient(root_window_)->AddObserver(this); + aura::client::SetActivationClient(root_window_, this); + root_window->AddPreTargetHandler(this); +} + +DesktopActivationClient::~DesktopActivationClient() { + root_window_->RemovePreTargetHandler(this); + aura::client::GetFocusClient(root_window_)->RemoveObserver(this); + aura::client::SetActivationClient(root_window_, NULL); +} + +void DesktopActivationClient::AddObserver( + aura::client::ActivationChangeObserver* observer) { + observers_.AddObserver(observer); +} + +void DesktopActivationClient::RemoveObserver( + aura::client::ActivationChangeObserver* observer) { + observers_.RemoveObserver(observer); +} + +void DesktopActivationClient::ActivateWindow(aura::Window* window) { + // Prevent recursion when called from focus. + if (updating_activation_) + return; + + base::AutoReset<bool> in_activate_window(&updating_activation_, true); + // Nothing may actually have changed. + if (current_active_ == window) + return; + // The stacking client may impose rules on what window configurations can be + // activated or deactivated. + if (window && !CanActivateWindow(window)) + return; + // Switch internal focus before we change the activation. Will probably cause + // recursion. + if (window && + !window->Contains(aura::client::GetFocusClient(window)-> + GetFocusedWindow())) { + aura::client::GetFocusClient(window)->FocusWindow(window); + } + + aura::Window* old_active = current_active_; + current_active_ = window; + if (window && !observer_manager_.IsObserving(window)) + observer_manager_.Add(window); + + FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, + observers_, + OnWindowActivated(window, old_active)); + aura::client::ActivationChangeObserver* observer = + aura::client::GetActivationChangeObserver(old_active); + if (observer) + observer->OnWindowActivated(window, old_active); + observer = aura::client::GetActivationChangeObserver(window); + if (observer) + observer->OnWindowActivated(window, old_active); +} + +void DesktopActivationClient::DeactivateWindow(aura::Window* window) { + if (window == current_active_) + current_active_ = NULL; +} + +aura::Window* DesktopActivationClient::GetActiveWindow() { + return current_active_; +} + +aura::Window* DesktopActivationClient::GetActivatableWindow( + aura::Window* window) { + aura::Window* parent = window->parent(); + aura::Window* child = window; + while (parent) { + if (CanActivateWindow(child)) + return child; + // If |child| isn't activatable, but has transient parent, trace + // that path instead. + if (child->transient_parent()) + return GetActivatableWindow(child->transient_parent()); + parent = parent->parent(); + child = child->parent(); + } + return NULL; +} + +aura::Window* DesktopActivationClient::GetToplevelWindow(aura::Window* window) { + aura::Window* parent = window->parent(); + aura::Window* child = window; + aura::Window* root = child->GetRootWindow(); + while (parent) { + if (parent == root) + return child; + parent = parent->parent(); + child = child->parent(); + } + return NULL; +} + +bool DesktopActivationClient::OnWillFocusWindow(aura::Window* window, + const ui::Event* event) { + return CanActivateWindow(GetActivatableWindow(window)); +} + +void DesktopActivationClient::OnWindowDestroying(aura::Window* window) { + if (current_active_ == window) { + current_active_ = NULL; + FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, + observers_, + OnWindowActivated(NULL, window)); + + // ash::ActivationController will also activate the next window here; we + // don't do this because that's the desktop environment's job. + } + observer_manager_.Remove(window); +} + +void DesktopActivationClient::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + if (gained_focus) + ActivateWindow(GetActivatableWindow(gained_focus)); +} + +bool DesktopActivationClient::CanActivateWindow(aura::Window* window) const { + return window && + window->IsVisible() && + (!aura::client::GetActivationDelegate(window) || + aura::client::GetActivationDelegate(window)->ShouldActivate()); +} + +void DesktopActivationClient::OnKeyEvent(ui::KeyEvent* event) { +} + +void DesktopActivationClient::OnMouseEvent(ui::MouseEvent* event) { + if (event->type() == ui::ET_MOUSE_PRESSED) + FocusWindowWithEvent(event); +} + +void DesktopActivationClient::OnScrollEvent(ui::ScrollEvent* event) { +} + +void DesktopActivationClient::OnTouchEvent(ui::TouchEvent* event) { +} + +void DesktopActivationClient::OnGestureEvent(ui::GestureEvent* event) { + if (event->type() == ui::ET_GESTURE_BEGIN && + event->details().touch_points() == 1) { + FocusWindowWithEvent(event); + } +} + +void DesktopActivationClient::FocusWindowWithEvent(const ui::Event* event) { + aura::Window* window = static_cast<aura::Window*>(event->target()); + if (GetActiveWindow() != window) { + aura::client::GetFocusClient(window)->FocusWindow( + FindFocusableWindowFor(window)); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_activation_client.h b/chromium/ui/views/widget/desktop_aura/desktop_activation_client.h new file mode 100644 index 00000000000..b34181792ff --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_activation_client.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESTKOP_ACTIVATION_CLIENT_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESTKOP_ACTIVATION_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "base/scoped_observer.h" +#include "ui/aura/client/activation_client.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/env_observer.h" +#include "ui/aura/root_window_observer.h" +#include "ui/aura/window_observer.h" +#include "ui/views/views_export.h" + +namespace aura { +class FocusManager; +class RootWindow; + +namespace client { +class ActivationChangeObserver; +} +} + +namespace views { + +// An activation client that handles activation events in a single +// RootWindow. Used only on the Desktop where there can be multiple RootWindow +// objects. +class VIEWS_EXPORT DesktopActivationClient + : public aura::client::ActivationClient, + public aura::WindowObserver, + public ui::EventHandler, + public aura::client::FocusChangeObserver { + public: + explicit DesktopActivationClient(aura::RootWindow* root_window); + virtual ~DesktopActivationClient(); + + // ActivationClient: + virtual void AddObserver( + aura::client::ActivationChangeObserver* observer) OVERRIDE; + virtual void RemoveObserver( + aura::client::ActivationChangeObserver* observer) OVERRIDE; + virtual void ActivateWindow(aura::Window* window) OVERRIDE; + virtual void DeactivateWindow(aura::Window* window) OVERRIDE; + virtual aura::Window* GetActiveWindow() OVERRIDE; + virtual aura::Window* GetActivatableWindow(aura::Window* window) OVERRIDE; + virtual aura::Window* GetToplevelWindow(aura::Window* window) OVERRIDE; + virtual bool OnWillFocusWindow(aura::Window* window, + const ui::Event* event) OVERRIDE; + virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE; + + // Overridden from aura::WindowObserver: + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // Overridden from aura::client::FocusChangeObserver: + virtual void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) OVERRIDE; + + // Overridden from ui::EventHandler: + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; + virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + + private: + void FocusWindowWithEvent(const ui::Event* event); + + aura::RootWindow* root_window_; + + // The current active window. + aura::Window* current_active_; + + // True inside ActivateWindow(). Used to prevent recursion of focus + // change notifications causing activation. + bool updating_activation_; + + ObserverList<aura::client::ActivationChangeObserver> observers_; + + ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_; + + DISALLOW_COPY_AND_ASSIGN(DesktopActivationClient); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESTKOP_ACTIVATION_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc new file mode 100644 index 00000000000..bf89a4eb916 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_capture_client.h" + +#include "ui/aura/root_window.h" + +namespace views { + +std::set<DesktopCaptureClient*> DesktopCaptureClient::live_capture_clients_; + +DesktopCaptureClient::DesktopCaptureClient(aura::RootWindow* root_window) + : root_window_(root_window), + capture_window_(NULL) { + aura::client::SetCaptureClient(root_window_, this); + live_capture_clients_.insert(this); +} + +DesktopCaptureClient::~DesktopCaptureClient() { + live_capture_clients_.erase(this); + aura::client::SetCaptureClient(root_window_, NULL); +} + +void DesktopCaptureClient::SetCapture(aura::Window* window) { + if (capture_window_ == window) + return; + if (window) { + // If we're actually starting capture, then cancel any touches/gestures + // that aren't already locked to the new window, and transfer any on the + // old capture window to the new one. When capture is released we have no + // distinction between the touches/gestures that were in the window all + // along (and so shouldn't be canceled) and those that got moved, so + // just leave them all where they are. + for (std::set<DesktopCaptureClient*>::iterator it = + live_capture_clients_.begin(); it != live_capture_clients_.end(); + ++it) { + (*it)->root_window_->gesture_recognizer()->TransferEventsTo( + capture_window_, window); + } + } + aura::Window* old_capture_window = GetCaptureWindow(); + capture_window_ = window; + + if (capture_window_) { + root_window_->SetNativeCapture(); + + for (std::set<DesktopCaptureClient*>::iterator it = + live_capture_clients_.begin(); it != live_capture_clients_.end(); + ++it) { + if (*it != this) + (*it)->OnOtherCaptureClientTookCapture(); + } + } else { + root_window_->ReleaseNativeCapture(); + } + + root_window_->UpdateCapture(old_capture_window, capture_window_); +} + +void DesktopCaptureClient::ReleaseCapture(aura::Window* window) { + if (capture_window_ != window) + return; + SetCapture(NULL); +} + +aura::Window* DesktopCaptureClient::GetCaptureWindow() { + for (std::set<DesktopCaptureClient*>::iterator it = + live_capture_clients_.begin(); it != live_capture_clients_.end(); + ++it) { + if ((*it)->capture_window_) + return (*it)->capture_window_; + } + return NULL; +} + +void DesktopCaptureClient::OnOtherCaptureClientTookCapture() { + if (capture_window_ == NULL) { + // While RootWindow may not technically have capture, it will store state + // that needs to be cleared on capture changed regarding mouse up/down. + root_window_->ClearMouseHandlers(); + } + else { + SetCapture(NULL); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.h b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.h new file mode 100644 index 00000000000..ba126826196 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.h @@ -0,0 +1,44 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CAPTURE_CLIENT_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CAPTURE_CLIENT_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/aura/client/capture_client.h" +#include "ui/views/views_export.h" + +namespace views { + +// A capture client which will collaborate with all other capture clients of +// its class. When capture is changed in an instance of this capture client, +// capture is released in all other windows. +class VIEWS_EXPORT DesktopCaptureClient : public aura::client::CaptureClient { + public: + explicit DesktopCaptureClient(aura::RootWindow* root_window); + virtual ~DesktopCaptureClient(); + + // Overridden from client::CaptureClient: + virtual void SetCapture(aura::Window* window) OVERRIDE; + virtual void ReleaseCapture(aura::Window* window) OVERRIDE; + virtual aura::Window* GetCaptureWindow() OVERRIDE; + + private: + // Called when another instance of the capture client takes capture. + void OnOtherCaptureClientTookCapture(); + + aura::RootWindow* root_window_; + aura::Window* capture_window_; + + static std::set<DesktopCaptureClient*> live_capture_clients_; + + DISALLOW_COPY_AND_ASSIGN(DesktopCaptureClient); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CAPTURE_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_capture_client_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_capture_client_unittest.cc new file mode 100644 index 00000000000..d96c4456724 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_capture_client_unittest.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_capture_client.h" + +#include "ui/aura/root_window.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/test_screen.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/base/events/event.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/view.h" +#include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +typedef ViewsTestBase ViewTest; + +class DesktopCaptureClientTest : public aura::test::AuraTestBase { + public: + virtual void SetUp() OVERRIDE { + AuraTestBase::SetUp(); + desktop_capture_client_.reset(new DesktopCaptureClient(root_window())); + + second_root_.reset(new aura::RootWindow( + aura::RootWindow::CreateParams(gfx::Rect(0, 0, 800, 600)))); + second_root_->Init(); + second_root_->Show(); + second_root_->SetHostSize(gfx::Size(800, 600)); + second_desktop_capture_client_.reset( + new DesktopCaptureClient(second_root_.get())); + + desktop_position_client_.reset(new DesktopScreenPositionClient()); + aura::client::SetScreenPositionClient(root_window(), + desktop_position_client_.get()); + + second_desktop_position_client_.reset(new DesktopScreenPositionClient()); + aura::client::SetScreenPositionClient( + second_root_.get(), + second_desktop_position_client_.get()); + } + + virtual void TearDown() OVERRIDE { + RunAllPendingInMessageLoop(); + + second_desktop_position_client_.reset(); + second_desktop_capture_client_.reset(); + + // Kill any active compositors before we hit the compositor shutdown paths. + second_root_.reset(); + + desktop_position_client_.reset(); + desktop_capture_client_.reset(); + + AuraTestBase::TearDown(); + } + + scoped_ptr<DesktopCaptureClient> desktop_capture_client_; + scoped_ptr<aura::RootWindow> second_root_; + scoped_ptr<DesktopCaptureClient> second_desktop_capture_client_; + scoped_ptr<aura::client::ScreenPositionClient> desktop_position_client_; + scoped_ptr<aura::client::ScreenPositionClient> + second_desktop_position_client_; +}; + +// Makes sure that internal details that are set on mouse down (such as +// mouse_pressed_handler()) are cleared when another root window takes capture. +TEST_F(DesktopCaptureClientTest, ResetMouseEventHandlerOnCapture) { + // Create a window inside the RootWindow. + scoped_ptr<aura::Window> w1(CreateNormalWindow(1, root_window(), NULL)); + + // Make a synthesized mouse down event. Ensure that the RootWindow will + // dispatch further mouse events to |w1|. + ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), + gfx::Point(5, 5), 0); + root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent( + &mouse_pressed_event); + EXPECT_EQ(w1.get(), root_window()->mouse_pressed_handler()); + + // Build a window in the second RootWindow. + scoped_ptr<aura::Window> w2(CreateNormalWindow(2, second_root_.get(), NULL)); + + // The act of having the second window take capture should clear out mouse + // pressed handler in the first RootWindow. + w2->SetCapture(); + EXPECT_EQ(NULL, root_window()->mouse_pressed_handler()); +} + +// Makes sure that when one window gets capture, it forces the release on the +// other. This is needed has to be handled explicitly on Linux, and is a sanity +// check on Windows. +TEST_F(DesktopCaptureClientTest, ResetOtherWindowCaptureOnCapture) { + // Create a window inside the RootWindow. + scoped_ptr<aura::Window> w1(CreateNormalWindow(1, root_window(), NULL)); + w1->SetCapture(); + // Both capture clients should return the same capture window. + EXPECT_EQ(w1.get(), desktop_capture_client_->GetCaptureWindow()); + EXPECT_EQ(w1.get(), second_desktop_capture_client_->GetCaptureWindow()); + + // Build a window in the second RootWindow and give it capture. Both capture + // clients should return the same capture window. + scoped_ptr<aura::Window> w2(CreateNormalWindow(2, second_root_.get(), NULL)); + w2->SetCapture(); + EXPECT_EQ(w2.get(), desktop_capture_client_->GetCaptureWindow()); + EXPECT_EQ(w2.get(), second_desktop_capture_client_->GetCaptureWindow()); +} + +// This class provides functionality to verify whether the View instance +// received the gesture event. +class DesktopViewInputTest : public View { + public: + DesktopViewInputTest() + : received_gesture_event_(false) {} + + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + received_gesture_event_ = true; + return View::OnGestureEvent(event); + } + + // Resets state maintained by this class. + void Reset() { + received_gesture_event_ = false; + } + + bool received_gesture_event() const { return received_gesture_event_; } + + private: + bool received_gesture_event_; + + DISALLOW_COPY_AND_ASSIGN(DesktopViewInputTest); +}; + +// Tests aura::Window capture and whether gesture events are sent to the window +// which has capture. +// The test case creates two visible widgets and sets capture to the underlying +// aura::Windows one by one. It then sends a gesture event and validates whether +// the window which had capture receives the gesture. +TEST_F(ViewTest, CaptureWindowInputEventTest) { + scoped_ptr<DesktopCaptureClient> desktop_capture_client1; + scoped_ptr<DesktopCaptureClient> desktop_capture_client2; + scoped_ptr<aura::client::ScreenPositionClient> desktop_position_client1; + scoped_ptr<aura::client::ScreenPositionClient> desktop_position_client2; + + scoped_ptr<Widget> widget1(new Widget()); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget1->Init(params); + internal::RootView* root1 = + static_cast<internal::RootView*>(widget1->GetRootView()); + + desktop_capture_client1.reset(new DesktopCaptureClient( + widget1->GetNativeView()->GetRootWindow())); + desktop_position_client1.reset(new DesktopScreenPositionClient()); + aura::client::SetScreenPositionClient( + widget1->GetNativeView()->GetRootWindow(), + desktop_position_client1.get()); + + DesktopViewInputTest* v1 = new DesktopViewInputTest(); + v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); + root1->AddChildView(v1); + widget1->Show(); + + scoped_ptr<Widget> widget2(new Widget()); + + params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget2->Init(params); + + internal::RootView* root2 = + static_cast<internal::RootView*>(widget2->GetRootView()); + desktop_capture_client2.reset(new DesktopCaptureClient( + widget2->GetNativeView()->GetRootWindow())); + desktop_position_client2.reset(new DesktopScreenPositionClient()); + aura::client::SetScreenPositionClient( + widget2->GetNativeView()->GetRootWindow(), + desktop_position_client2.get()); + + DesktopViewInputTest* v2 = new DesktopViewInputTest(); + v2->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); + root2->AddChildView(v2); + widget2->Show(); + + EXPECT_FALSE(widget1->GetNativeView()->HasCapture()); + EXPECT_FALSE(widget2->GetNativeView()->HasCapture()); + EXPECT_EQ(desktop_capture_client1->GetCaptureWindow(), + reinterpret_cast<aura::Window*>(0)); + EXPECT_EQ(desktop_capture_client2->GetCaptureWindow(), + reinterpret_cast<aura::Window*>(0)); + + widget1->GetNativeView()->SetCapture(); + EXPECT_TRUE(widget1->GetNativeView()->HasCapture()); + EXPECT_FALSE(widget2->GetNativeView()->HasCapture()); + EXPECT_EQ(desktop_capture_client1->GetCaptureWindow(), + widget1->GetNativeView()); + EXPECT_EQ(desktop_capture_client2->GetCaptureWindow(), + widget1->GetNativeView()); + + ui::GestureEvent g1(ui::ET_GESTURE_LONG_PRESS, 80, 80, 0, + base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, + 0.0f, 0.0f), 0); + root1->DispatchGestureEvent(&g1); + EXPECT_TRUE(v1->received_gesture_event()); + EXPECT_FALSE(v2->received_gesture_event()); + v1->Reset(); + v2->Reset(); + + widget2->GetNativeView()->SetCapture(); + + EXPECT_FALSE(widget1->GetNativeView()->HasCapture()); + EXPECT_TRUE(widget2->GetNativeView()->HasCapture()); + EXPECT_EQ(desktop_capture_client1->GetCaptureWindow(), + widget2->GetNativeView()); + EXPECT_EQ(desktop_capture_client2->GetCaptureWindow(), + widget2->GetNativeView()); + + root2->DispatchGestureEvent(&g1); + EXPECT_TRUE(v2->received_gesture_event()); + EXPECT_FALSE(v1->received_gesture_event()); + + widget1->CloseNow(); + widget2->CloseNow(); + RunPendingMessages(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h new file mode 100644 index 00000000000..c31214200ed --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h @@ -0,0 +1,40 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CURSOR_LOADER_UPDATER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CURSOR_LOADER_UPDATER_H_ + +namespace aura { +class RootWindow; +} + +namespace gfx { +class Display; +} + +namespace ui { +class CursorLoader; +} + +namespace views { + +// An interface to optionally update the state of a cursor loader. Only used on +// desktop AuraX11. +class DesktopCursorLoaderUpdater { + public: + virtual ~DesktopCursorLoaderUpdater() {} + + // Called when a CursorLoader is created. + virtual void OnCreate(aura::RootWindow* window, + ui::CursorLoader* loader) = 0; + + // Called when the display has changed (as we may need to reload the cursor + // assets in response to a device scale factor or rotation change). + virtual void OnDisplayUpdated(const gfx::Display& display, + ui::CursorLoader* loader) = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPLAY_CHANGE_HANDLER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc new file mode 100644 index 00000000000..e771f69be29 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h" + +#include "ui/aura/root_window.h" +#include "ui/base/cursor/cursor_loader.h" +#include "ui/base/cursor/cursors_aura.h" +#include "ui/gfx/display.h" + +namespace views { +namespace { + +// Cursors that we need to supply our own image resources for. This was +// generated from webcursor_gtk.cc; any cursor that either was NOTIMPLEMENTED() +// or already was implemented with a pixmap is on this list. +const int kImageCursorIds[] = { + ui::kCursorNorthEastSouthWestResize, + ui::kCursorNorthWestSouthEastResize, + ui::kCursorVerticalText, + ui::kCursorCell, + ui::kCursorContextMenu, + ui::kCursorAlias, + ui::kCursorNoDrop, + ui::kCursorCopy, + ui::kCursorNotAllowed, + ui::kCursorZoomIn, + ui::kCursorZoomOut, + ui::kCursorGrab, + ui::kCursorGrabbing, +}; + +void LoadImageCursors(float device_scale_factor, ui::CursorLoader* loader) { + int resource_id; + gfx::Point point; + for (size_t i = 0; i < arraysize(kImageCursorIds); ++i) { + bool success = ui::GetCursorDataFor(kImageCursorIds[i], device_scale_factor, + &resource_id, &point); + DCHECK(success); + loader->LoadImageCursor(kImageCursorIds[i], resource_id, point); + } +} + +} // namespace + +DesktopCursorLoaderUpdaterAuraX11::DesktopCursorLoaderUpdaterAuraX11() {} + +DesktopCursorLoaderUpdaterAuraX11::~DesktopCursorLoaderUpdaterAuraX11() {} + +void DesktopCursorLoaderUpdaterAuraX11::OnCreate( + aura::RootWindow* window, + ui::CursorLoader* loader) { + LoadImageCursors(window->compositor()->device_scale_factor(), loader); +} + +void DesktopCursorLoaderUpdaterAuraX11::OnDisplayUpdated( + const gfx::Display& display, + ui::CursorLoader* loader) { + LoadImageCursors(display.device_scale_factor(), loader); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h new file mode 100644 index 00000000000..3018c977a05 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h @@ -0,0 +1,28 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CURSOR_LOADER_UPDATER_AURAX11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_CURSOR_LOADER_UPDATER_AURAX11_H_ + +#include "base/compiler_specific.h" +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" + +namespace views { + +// Loads the subset of aura cursors that X11 doesn't provide. +class DesktopCursorLoaderUpdaterAuraX11 : public DesktopCursorLoaderUpdater { + public: + DesktopCursorLoaderUpdaterAuraX11(); + virtual ~DesktopCursorLoaderUpdaterAuraX11(); + + // Overridden from DesktopCursorLoaderUpdater: + virtual void OnCreate(aura::RootWindow* window, + ui::CursorLoader* loader) OVERRIDE; + virtual void OnDisplayUpdated(const gfx::Display& display, + ui::CursorLoader* loader) OVERRIDE; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPLAY_CHANGE_HANDLER_AURAX11_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc new file mode 100644 index 00000000000..bd4ba0229aa --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" + +#include "base/run_loop.h" + +namespace views { + +DesktopDispatcherClient::DesktopDispatcherClient() {} + +DesktopDispatcherClient::~DesktopDispatcherClient() {} + +void DesktopDispatcherClient::RunWithDispatcher( + base::MessageLoop::Dispatcher* nested_dispatcher, + aura::Window* associated_window, + bool nestable_tasks_allowed) { + // TODO(erg): This class has been copypastad from + // ash/accelerators/nested_dispatcher_controller.cc. I have left my changes + // commented out because I don't entirely understand the implications of the + // change. + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + bool did_allow_task_nesting = loop->NestableTasksAllowed(); + loop->SetNestableTasksAllowed(nestable_tasks_allowed); + + // DefaultAcceleratorDispatcher dispatcher(nested_dispatcher, + // associated_window); + base::RunLoop run_loop(nested_dispatcher); + run_loop.Run(); + loop->SetNestableTasksAllowed(did_allow_task_nesting); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h new file mode 100644 index 00000000000..5e7a517cc71 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ + +#include "base/basictypes.h" +#include "ui/aura/client/dispatcher_client.h" +#include "ui/views/views_export.h" + +namespace views { + +// TODO(erg): I won't lie to you; I have no idea what this is or what it does. +class VIEWS_EXPORT DesktopDispatcherClient + : public aura::client::DispatcherClient { + public: + DesktopDispatcherClient(); + virtual ~DesktopDispatcherClient(); + + virtual void RunWithDispatcher(base::MessageLoop::Dispatcher* dispatcher, + aura::Window* associated_window, + bool nestable_tasks_allowed) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopDispatcherClient); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc new file mode 100644 index 00000000000..1870e11f5b8 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc @@ -0,0 +1,923 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" + +#include <X11/Xatom.h> + +#include "base/event_types.h" +#include "base/message_loop/message_loop.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" +#include "ui/base/events/event.h" +#include "ui/base/x/selection_utils.h" +#include "ui/base/x/x11_util.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" + +using aura::client::DragDropDelegate; +using ui::OSExchangeData; + +namespace { + +const int kMinXdndVersion = 5; + +const int kWillAcceptDrop = 1; +const int kWantFurtherPosEvents = 2; + +const char kXdndActionCopy[] = "XdndActionCopy"; +const char kXdndActionMove[] = "XdndActionMove"; +const char kXdndActionLink[] = "XdndActionLink"; + +const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; +const char kXdndSelection[] = "XdndSelection"; + +const char* kAtomsToCache[] = { + kChromiumDragReciever, + "XdndActionAsk", + kXdndActionCopy, + kXdndActionLink, + "XdndActionList", + kXdndActionMove, + "XdndActionPrivate", + "XdndAware", + "XdndDrop", + "XdndEnter", + "XdndFinished", + "XdndLeave", + "XdndPosition", + "XdndProxy", // Proxy windows? + kXdndSelection, + "XdndStatus", + "XdndTypeList", + NULL +}; + +// Helper class to FindWindowFor which looks for a drag target under the +// cursor. +class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate { + public: + DragTargetWindowFinder(XID ignored_icon_window, + gfx::Point screen_loc) + : ignored_icon_window_(ignored_icon_window), + output_window_(None), + screen_loc_(screen_loc) { + ui::EnumerateTopLevelWindows(this); + } + + virtual ~DragTargetWindowFinder() {} + + XID window() const { return output_window_; } + + protected: + virtual bool ShouldStopIterating(XID window) OVERRIDE { + if (window == ignored_icon_window_) + return false; + + if (!ui::IsWindowVisible(window)) + return false; + + if (!ui::WindowContainsPoint(window, screen_loc_)) + return false; + + if (ui::PropertyExists(window, "WM_STATE")) { + output_window_ = window; + return true; + } + + return false; + } + + private: + XID ignored_icon_window_; + XID output_window_; + gfx::Point screen_loc_; + + DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder); +}; + +// Returns the topmost X11 window at |screen_point| if it is advertising that +// is supports the Xdnd protocol. Will return the window under the pointer as +// |mouse_window|. If there's a Xdnd aware window, it will be returned in +// |dest_window|. +void FindWindowFor(const gfx::Point& screen_point, + ::Window* mouse_window, ::Window* dest_window) { + DragTargetWindowFinder finder(None, screen_point); + *mouse_window = finder.window(); + *dest_window = None; + + if (finder.window() == None) + return; + + // Figure out which window we should test as XdndAware. If mouse_window has + // XdndProxy, it will set that proxy on target, and if not, |target|'s + // original value will remain. + XID target = *mouse_window; + ui::GetXIDProperty(*mouse_window, "XdndProxy", &target); + + int version; + if (ui::GetIntProperty(target, "XdndAware", &version) && + version >= kMinXdndVersion) { + *dest_window = target; + } +} + +} // namespace + +namespace views { + +std::map< ::Window, DesktopDragDropClientAuraX11*> + DesktopDragDropClientAuraX11::g_live_client_map; + +class DesktopDragDropClientAuraX11::X11DragContext : + public base::MessageLoop::Dispatcher { + public: + X11DragContext(ui::X11AtomCache* atom_cache, + ::Window local_window, + const XClientMessageEvent& event); + virtual ~X11DragContext(); + + // When we receive an XdndPosition message, we need to have all the data + // copied from the other window before we process the XdndPosition + // message. If we have that data already, dispatch immediately. Otherwise, + // delay dispatching until we do. + void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, + ::Window source_window, + const gfx::Point& screen_point); + + // Called to request the next target from the source window. This is only + // done on the first XdndPosition; after that, we cache the data offered by + // the source window. + void RequestNextTarget(); + + // Called when XSelection data has been copied to our process. + void OnSelectionNotify(const XSelectionEvent& xselection); + + // Clones the fetched targets. + const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; } + + // Reads the "XdndActionList" property from |source_window| and copies it + // into |actions|. + void ReadActions(); + + // Creates a ui::DragDropTypes::DragOperation representation of the current + // action list. + int GetDragOperation() const; + + private: + // Overridden from MessageLoop::Dispatcher: + virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + + // The atom cache owned by our parent. + ui::X11AtomCache* atom_cache_; + + // The XID of our chrome local aura window handling our events. + ::Window local_window_; + + // The XID of the window that's initiated the drag. + unsigned long source_window_; + + // The client we inform once we're done with requesting data. + DesktopDragDropClientAuraX11* drag_drop_client_; + + // Whether we're blocking the handling of an XdndPosition message by waiting + // for |unfetched_targets_| to be fetched. + bool waiting_to_handle_position_; + + // Where the cursor is on screen. + gfx::Point screen_point_; + + // A SelectionFormatMap of data that we have in our process. + ui::SelectionFormatMap fetched_targets_; + + // The names of various data types offered by the other window that we + // haven't fetched and put in |fetched_targets_| yet. + std::vector<Atom> unfetched_targets_; + + // Possible actions. + std::vector<Atom> actions_; + + DISALLOW_COPY_AND_ASSIGN(X11DragContext); +}; + +DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( + ui::X11AtomCache* atom_cache, + ::Window local_window, + const XClientMessageEvent& event) + : atom_cache_(atom_cache), + local_window_(local_window), + source_window_(event.data.l[0]), + drag_drop_client_(NULL), + waiting_to_handle_position_(false) { + bool get_types = ((event.data.l[1] & 1) != 0); + + if (get_types) { + if (!ui::GetAtomArrayProperty(source_window_, + "XdndTypeList", + &unfetched_targets_)) { + return; + } + } else { + // data.l[2,3,4] contain the first three types. Unused slots can be None. + for (int i = 0; i < 3; ++i) { + if (event.data.l[2+i] != None) { + unfetched_targets_.push_back(event.data.l[2+i]); + } + } + } + + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + // The window doesn't have a DesktopDragDropClientAuraX11, that means it's + // created by some other process. Listen for messages on it. + base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( + this, source_window_); + XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), + source_window_, PropertyChangeMask); + + // We must perform a full sync here because we could be racing + // |source_window_|. + XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); + } else { + // This drag originates from an aura window within our process. This means + // that we can shortcut the X11 server and ask the owning SelectionOwner + // for the data it's offering. + fetched_targets_ = client->GetFormatMap(); + unfetched_targets_.clear(); + } + + ReadActions(); +} + +DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + // Unsubscribe from message events. + base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( + source_window_); + } +} + +void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( + DesktopDragDropClientAuraX11* client, + ::Window source_window, + const gfx::Point& screen_point) { + DCHECK_EQ(source_window_, source_window); + + if (!unfetched_targets_.empty()) { + // We have unfetched targets. That means we need to pause the handling of + // the position message and ask the other window for its data. + screen_point_ = screen_point; + drag_drop_client_ = client; + waiting_to_handle_position_ = true; + + fetched_targets_ = ui::SelectionFormatMap(); + RequestNextTarget(); + } else { + client->CompleteXdndPosition(source_window, screen_point); + } +} + +void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { + ::Atom target = unfetched_targets_.back(); + unfetched_targets_.pop_back(); + + XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(), + atom_cache_->GetAtom(kXdndSelection), + target, + atom_cache_->GetAtom(kChromiumDragReciever), + local_window_, + CurrentTime); +} + +void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( + const XSelectionEvent& event) { + DCHECK(waiting_to_handle_position_); + DCHECK(drag_drop_client_); + DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever)); + + scoped_refptr<base::RefCountedMemory> data; + ::Atom type = None; + if (ui::GetRawBytesOfProperty(local_window_, event.property, + &data, NULL, NULL, &type)) { + fetched_targets_.Insert(event.target, data); + } + + if (!unfetched_targets_.empty()) { + RequestNextTarget(); + } else { + waiting_to_handle_position_ = false; + drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_); + drag_drop_client_ = NULL; + } +} + +void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + std::vector<Atom> atom_array; + if (!ui::GetAtomArrayProperty(source_window_, + "XdndActionList", + &atom_array)) { + actions_.clear(); + } else { + actions_.swap(atom_array); + } + } else { + // We have a property notify set up for other windows in case they change + // their action list. Thankfully, the views interface is static and you + // can't change the action list after you enter StartDragAndDrop(). + actions_ = client->GetOfferedDragOperations(); + } +} + +int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + for (std::vector<Atom>::const_iterator it = actions_.begin(); + it != actions_.end(); ++it) { + if (*it == atom_cache_->GetAtom(kXdndActionCopy)) + drag_operation |= ui::DragDropTypes::DRAG_COPY; + else if (*it == atom_cache_->GetAtom(kXdndActionMove)) + drag_operation |= ui::DragDropTypes::DRAG_MOVE; + else if (*it == atom_cache_->GetAtom(kXdndActionLink)) + drag_operation |= ui::DragDropTypes::DRAG_LINK; + } + + return drag_operation; +} + +bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch( + const base::NativeEvent& event) { + if (event->type == PropertyNotify && + event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) { + ReadActions(); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( + aura::RootWindow* root_window, + views::DesktopNativeCursorManager* cursor_manager, + Display* xdisplay, + ::Window xwindow) + : move_loop_(this), + root_window_(root_window), + xdisplay_(xdisplay), + xwindow_(xwindow), + atom_cache_(xdisplay_, kAtomsToCache), + target_window_(NULL), + source_provider_(NULL), + source_current_window_(None), + drag_drop_in_progress_(false), + drag_operation_(0), + resulting_operation_(0), + grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)), + copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)), + move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)) { + DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end()); + g_live_client_map.insert(std::make_pair(xwindow, this)); + + // Mark that we are aware of drag and drop concepts. + unsigned long xdnd_version = kMinXdndVersion; + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"), + XA_ATOM, 32, PropModeReplace, + reinterpret_cast<unsigned char*>(&xdnd_version), 1); +} + +DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { + g_live_client_map.erase(xwindow_); +} + +// static +DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow( + ::Window window) { + std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it = + g_live_client_map.find(window); + if (it == g_live_client_map.end()) + return NULL; + return it->second; +} + +void DesktopDragDropClientAuraX11::OnXdndEnter( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndEnter"; + + int version = (event.data.l[1] & 0xff000000) >> 24; + if (version < 3) { + LOG(ERROR) << "Received old XdndEnter message."; + return; + } + + // Make sure that we've run ~X11DragContext() before creating another one. + target_current_context_.reset(); + target_current_context_.reset( + new X11DragContext(&atom_cache_, xwindow_, event)); + + // In the Windows implementation, we immediately call DesktopDropTargetWin:: + // Translate(). Here, we wait until we receive an XdndPosition message + // because the enter message doesn't convey any positioning + // information. +} + +void DesktopDragDropClientAuraX11::OnXdndLeave( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndLeave"; + NotifyDragLeave(); + target_current_context_.reset(); +} + +void DesktopDragDropClientAuraX11::OnXdndPosition( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndPosition"; + + unsigned long source_window = event.data.l[0]; + int x_root_window = event.data.l[2] >> 16; + int y_root_window = event.data.l[2] & 0xffff; + + if (!target_current_context_.get()) { + NOTREACHED(); + return; + } + + // If we already have all the data from this drag, we complete it + // immediately. + target_current_context_->OnStartXdndPositionMessage( + this, source_window, gfx::Point(x_root_window, y_root_window)); +} + +void DesktopDragDropClientAuraX11::OnXdndStatus( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndStatus"; + + unsigned long source_window = event.data.l[0]; + int drag_operation = ui::DragDropTypes::DRAG_NONE; + if (event.data.l[1] & 1) { + ::Atom atom_operation = event.data.l[4]; + negotiated_operation_[source_window] = atom_operation; + drag_operation = AtomToDragOperation(atom_operation); + } + + switch (drag_operation) { + case ui::DragDropTypes::DRAG_COPY: + move_loop_.UpdateCursor(copy_grab_cursor_); + break; + case ui::DragDropTypes::DRAG_MOVE: + move_loop_.UpdateCursor(move_grab_cursor_); + break; + default: + move_loop_.UpdateCursor(grab_cursor_); + break; + } + + // Note: event.data.[2,3] specify a rectangle. It is a request by the other + // window to not send further XdndPosition messages while the cursor is + // within it. However, it is considered advisory and (at least according to + // the spec) the other side must handle further position messages within + // it. GTK+ doesn't bother with this, so neither should we. + + waiting_on_status_.erase(source_window); + + if (ContainsKey(pending_drop_, source_window)) { + // We were waiting on the status message so we could send the XdndDrop. + SendXdndDrop(source_window); + return; + } + + NextPositionMap::iterator it = next_position_message_.find(source_window); + if (it != next_position_message_.end()) { + // We were waiting on the status message so we could send off the next + // position message we queued up. + gfx::Point p = it->second.first; + unsigned long time = it->second.second; + next_position_message_.erase(it); + + SendXdndPosition(source_window, p, time); + } +} + +void DesktopDragDropClientAuraX11::OnXdndFinished( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndFinished"; + resulting_operation_ = AtomToDragOperation( + negotiated_operation_[event.data.l[0]]); + move_loop_.EndMoveLoop(); +} + +void DesktopDragDropClientAuraX11::OnXdndDrop( + const XClientMessageEvent& event) { + DVLOG(1) << "XdndDrop"; + + unsigned long source_window = event.data.l[0]; + + int drag_operation = ui::DragDropTypes::DRAG_NONE; + if (target_window_) { + aura::client::DragDropDelegate* delegate = + aura::client::GetDragDropDelegate(target_window_); + if (delegate) { + ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( + xwindow_, target_current_context_->fetched_targets())); + + ui::DropTargetEvent event(data, + target_window_location_, + target_window_root_location_, + target_current_context_->GetDragOperation()); + drag_operation = delegate->OnPerformDrop(event); + } + + target_window_->RemoveObserver(this); + target_window_ = NULL; + } + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished"); + xev.xclient.format = 32; + xev.xclient.window = source_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0; + xev.xclient.data.l[2] = DragOperationToAtom(drag_operation); + + SendXClientEvent(source_window, &xev); +} + +void DesktopDragDropClientAuraX11::OnSelectionNotify( + const XSelectionEvent& xselection) { + if (!target_current_context_) { + NOTIMPLEMENTED(); + return; + } + + target_current_context_->OnSelectionNotify(xselection); +} + +int DesktopDragDropClientAuraX11::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::RootWindow* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + source_current_window_ = None; + drag_drop_in_progress_ = true; + drag_operation_ = operation; + resulting_operation_ = ui::DragDropTypes::DRAG_NONE; + + const ui::OSExchangeData::Provider* provider = &data.provider(); + source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( + provider); + + source_provider_->TakeOwnershipOfSelection(); + + std::vector< ::Atom> actions = GetOfferedDragOperations(); + ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); + + // Windows has a specific method, DoDragDrop(), which performs the entire + // drag. We have to emulate this, so we spin off a nested runloop which will + // track all cursor movement and reroute events to a specific handler. + move_loop_.RunMoveLoop(source_window, grab_cursor_); + + source_provider_ = NULL; + drag_drop_in_progress_ = false; + drag_operation_ = 0; + XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); + + return resulting_operation_; +} + +void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientAuraX11::Drop(aura::Window* target, + const ui::LocatedEvent& event) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientAuraX11::DragCancel() { + move_loop_.EndMoveLoop(); +} + +bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { + return drag_drop_in_progress_; +} + +void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { + DCHECK_EQ(target_window_, window); + target_window_ = NULL; +} + +void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) { + gfx::Point screen_point(event->x_root, event->y_root); + + // Find the current window the cursor is over. + ::Window mouse_window = None; + ::Window dest_window = None; + FindWindowFor(screen_point, &mouse_window, &dest_window); + + if (source_current_window_ != dest_window) { + if (source_current_window_ != None) + SendXdndLeave(source_current_window_); + + source_current_window_ = dest_window; + + if (source_current_window_ != None) { + negotiated_operation_.erase(source_current_window_); + SendXdndEnter(source_current_window_); + } + } + + if (source_current_window_ != None) { + if (ContainsKey(waiting_on_status_, dest_window)) { + next_position_message_[dest_window] = + std::make_pair(screen_point, event->time); + } else { + SendXdndPosition(dest_window, screen_point, event->time); + } + } +} + +void DesktopDragDropClientAuraX11::OnMouseReleased() { + if (source_current_window_ != None) { + if (ContainsKey(waiting_on_status_, source_current_window_)) { + // If we are waiting for an XdndStatus message, we need to wait for it to + // complete. + pending_drop_.insert(source_current_window_); + return; + } + + std::map< ::Window, ::Atom>::iterator it = + negotiated_operation_.find(source_current_window_); + if (it != negotiated_operation_.end() && it->second != None) { + // We have negotiated an action with the other end. + SendXdndDrop(source_current_window_); + return; + } + + SendXdndLeave(source_current_window_); + source_current_window_ = None; + } + + move_loop_.EndMoveLoop(); +} + +void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { + target_current_context_.reset(); +} + +void DesktopDragDropClientAuraX11::DragTranslate( + const gfx::Point& root_window_location, + scoped_ptr<ui::OSExchangeData>* data, + scoped_ptr<ui::DropTargetEvent>* event, + aura::client::DragDropDelegate** delegate) { + gfx::Point root_location = root_window_location; + root_window_->ConvertPointFromNativeScreen(&root_location); + aura::Window* target_window = + root_window_->GetEventHandlerForPoint(root_location); + bool target_window_changed = false; + if (target_window != target_window_) { + if (target_window_) + NotifyDragLeave(); + target_window_ = target_window; + if (target_window_) + target_window_->AddObserver(this); + target_window_changed = true; + } + *delegate = NULL; + if (!target_window_) + return; + *delegate = aura::client::GetDragDropDelegate(target_window_); + if (!*delegate) + return; + + data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11( + xwindow_, target_current_context_->fetched_targets()))); + gfx::Point location = root_location; + aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); + + target_window_location_ = location; + target_window_root_location_ = root_location; + + event->reset(new ui::DropTargetEvent( + *(data->get()), + location, + root_location, + target_current_context_->GetDragOperation())); + if (target_window_changed) + (*delegate)->OnDragEntered(*event->get()); +} + +void DesktopDragDropClientAuraX11::NotifyDragLeave() { + if (!target_window_) + return; + DragDropDelegate* delegate = + aura::client::GetDragDropDelegate(target_window_); + if (delegate) + delegate->OnDragExited(); + target_window_->RemoveObserver(this); + target_window_ = NULL; +} + +::Atom DesktopDragDropClientAuraX11::DragOperationToAtom( + int drag_operation) { + if (drag_operation & ui::DragDropTypes::DRAG_COPY) + return atom_cache_.GetAtom(kXdndActionCopy); + if (drag_operation & ui::DragDropTypes::DRAG_MOVE) + return atom_cache_.GetAtom(kXdndActionMove); + if (drag_operation & ui::DragDropTypes::DRAG_LINK) + return atom_cache_.GetAtom(kXdndActionLink); + + return None; +} + +int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { + if (atom == atom_cache_.GetAtom(kXdndActionCopy)) + return ui::DragDropTypes::DRAG_COPY; + if (atom == atom_cache_.GetAtom(kXdndActionMove)) + return ui::DragDropTypes::DRAG_MOVE; + if (atom == atom_cache_.GetAtom(kXdndActionLink)) + return ui::DragDropTypes::DRAG_LINK; + + return ui::DragDropTypes::DRAG_NONE; +} + +std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() { + std::vector< ::Atom> operations; + if (drag_operation_ & ui::DragDropTypes::DRAG_COPY) + operations.push_back(atom_cache_.GetAtom(kXdndActionCopy)); + if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE) + operations.push_back(atom_cache_.GetAtom(kXdndActionMove)); + if (drag_operation_ & ui::DragDropTypes::DRAG_LINK) + operations.push_back(atom_cache_.GetAtom(kXdndActionLink)); + return operations; +} + +ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const { + return source_provider_ ? source_provider_->GetFormatMap() : + ui::SelectionFormatMap(); +} + +void DesktopDragDropClientAuraX11::CompleteXdndPosition( + ::Window source_window, + const gfx::Point& screen_point) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + scoped_ptr<ui::OSExchangeData> data; + scoped_ptr<ui::DropTargetEvent> drop_target_event; + DragDropDelegate* delegate = NULL; + DragTranslate(screen_point, &data, &drop_target_event, &delegate); + if (delegate) + drag_operation = delegate->OnDragUpdated(*drop_target_event); + + // Sends an XdndStatus message back to the source_window. l[2,3] + // theoretically represent an area in the window where the current action is + // the same as what we're returning, but I can't find any implementation that + // actually making use of this. A client can return (0, 0) and/or set the + // first bit of l[1] to disable the feature, and it appears that gtk neither + // sets this nor respects it if set. + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus"); + xev.xclient.format = 32; + xev.xclient.window = source_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (drag_operation != 0) ? + (kWantFurtherPosEvents | kWillAcceptDrop) : 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation); + + SendXClientEvent(source_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number. + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + std::vector<Atom> targets; + source_provider_->RetrieveTargets(&targets); + + if (targets.size() > 3) { + xev.xclient.data.l[1] |= 1; + ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets); + } else { + // Pack the targets into the enter message. + for (size_t i = 0; i < targets.size(); ++i) + xev.xclient.data.l[2 + i] = targets[i]; + } + + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) { + // If we're sending a leave message, don't wait for status messages anymore. + waiting_on_status_.erase(dest_window); + NextPositionMap::iterator it = next_position_message_.find(dest_window); + if (it != next_position_message_.end()) + next_position_message_.erase(it); + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndPosition( + ::Window dest_window, + const gfx::Point& screen_point, + unsigned long time) { + waiting_on_status_.insert(dest_window); + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y(); + xev.xclient.data.l[3] = time; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_); + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = CurrentTime; + xev.xclient.data.l[3] = None; + xev.xclient.data.l[4] = None; + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid, + XEvent* xev) { + DCHECK_EQ(ClientMessage, xev->type); + + // Don't send messages to the X11 message queue if we can help it. + DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid); + if (short_circuit) { + Atom message_type = xev->xclient.message_type; + if (message_type == atom_cache_.GetAtom("XdndEnter")) { + short_circuit->OnXdndEnter(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { + short_circuit->OnXdndLeave(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { + short_circuit->OnXdndPosition(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { + short_circuit->OnXdndStatus(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { + short_circuit->OnXdndFinished(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { + short_circuit->OnXdndDrop(xev->xclient); + return; + } + } + + // I don't understand why the GTK+ code is doing what it's doing here. It + // goes out of its way to send the XEvent so that it receives a callback on + // success or failure, and when it fails, it then sends an internal + // GdkEvent about the failed drag. (And sending this message doesn't appear + // to go through normal xlib machinery, but instead passes through the low + // level xProto (the x11 wire format) that I don't understand. + // + // I'm unsure if I have to jump through those hoops, or if XSendEvent is + // sufficient. + XSendEvent(xdisplay_, xid, False, 0, xev); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h new file mode 100644 index 00000000000..2dee7305f2f --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -0,0 +1,230 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_ + +#include <X11/Xlib.h> + +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include <set> + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/window_observer.h" +#include "ui/base/x/x11_atom_cache.h" +#include "ui/gfx/point.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h" + +namespace aura { +class RootWindow; +namespace client { +class DragDropDelegate; +} +} + +namespace gfx { +class Point; +} + +namespace ui { +class DragSource; +class DropTargetEvent; +class OSExchangeData; +class OSExchangeDataProviderAuraX11; +class RootWindow; +class SelectionFormatMap; +} + +namespace views { +class DesktopNativeCursorManager; + +// Implements drag and drop on X11 for aura. On one side, this class takes raw +// X11 events forwarded from DesktopRootWindowHostLinux, while on the other, it +// handles the views drag events. +class VIEWS_EXPORT DesktopDragDropClientAuraX11 + : public aura::client::DragDropClient, + public aura::WindowObserver, + public X11WholeScreenMoveLoopDelegate { + public: + DesktopDragDropClientAuraX11( + aura::RootWindow* root_window, + views::DesktopNativeCursorManager* cursor_manager, + Display* xdisplay, + ::Window xwindow); + virtual ~DesktopDragDropClientAuraX11(); + + // We maintain a mapping of live DesktopDragDropClientAuraX11 objects to + // their ::Windows. We do this so that we're able to short circuit sending + // X11 messages to windows in our process. + static DesktopDragDropClientAuraX11* GetForWindow(::Window window); + + // These methods handle the various X11 client messages from the platform. + void OnXdndEnter(const XClientMessageEvent& event); + void OnXdndLeave(const XClientMessageEvent& event); + void OnXdndPosition(const XClientMessageEvent& event); + void OnXdndStatus(const XClientMessageEvent& event); + void OnXdndFinished(const XClientMessageEvent& event); + void OnXdndDrop(const XClientMessageEvent& event); + + // Called when XSelection data has been copied to our process. + void OnSelectionNotify(const XSelectionEvent& xselection); + + // Overridden from aura::client::DragDropClient: + virtual int StartDragAndDrop( + const ui::OSExchangeData& data, + aura::RootWindow* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) OVERRIDE; + virtual void Drop(aura::Window* target, + const ui::LocatedEvent& event) OVERRIDE; + virtual void DragCancel() OVERRIDE; + virtual bool IsDragDropInProgress() OVERRIDE; + + // Overridden from aura::WindowObserver: + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; + + // Overridden from X11WholeScreenMoveLoopDelegate: + virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE; + virtual void OnMouseReleased() OVERRIDE; + virtual void OnMoveLoopEnded() OVERRIDE; + + private: + typedef std::map< ::Window, std::pair<gfx::Point, unsigned long> > + NextPositionMap; + + // When we receive an position x11 message, we need to translate that into + // the underlying aura::Window representation, as moves internal to the X11 + // window can cause internal drag leave and enter messages. + void DragTranslate(const gfx::Point& root_window_location, + scoped_ptr<ui::OSExchangeData>* data, + scoped_ptr<ui::DropTargetEvent>* event, + aura::client::DragDropDelegate** delegate); + + // Called when we need to notify the current aura::Window that we're no + // longer dragging over it. + void NotifyDragLeave(); + + // Converts our bitfield of actions into an Atom that represents what action + // we're most likely to take on drop. + ::Atom DragOperationToAtom(int drag_operation); + + // Converts a single action atom to a drag operation. + int AtomToDragOperation(::Atom atom); + + // During the blocking StartDragAndDrop() call, this converts the views-style + // |drag_operation_| bitfield into a vector of Atoms to offer to other + // processes. + std::vector< ::Atom> GetOfferedDragOperations(); + + // This returns a representation of the data we're offering in this + // drag. This is done to bypass an asynchronous roundtrip with the X11 + // server. + ui::SelectionFormatMap GetFormatMap() const; + + // Handling XdndPosition can be paused while waiting for more data; this is + // called either synchronously from OnXdndPosition, or asynchronously after + // we've received data requested from the other window. + void CompleteXdndPosition(::Window source_window, + const gfx::Point& screen_point); + + void SendXdndEnter(::Window dest_window); + void SendXdndLeave(::Window dest_window); + void SendXdndPosition(::Window dest_window, + const gfx::Point& screen_point, + unsigned long time); + void SendXdndDrop(::Window dest_window); + + // Sends |xev| to |xid|, optionally short circuiting the round trip to the X + // server. + void SendXClientEvent(::Window xid, XEvent* xev); + + // A nested message loop that notifies this object of events through the + // X11WholeScreenMoveLoopDelegate interface. + X11WholeScreenMoveLoop move_loop_; + + aura::RootWindow* root_window_; + + Display* xdisplay_; + ::Window xwindow_; + + ui::X11AtomCache atom_cache_; + + // Target side information. + class X11DragContext; + scoped_ptr<X11DragContext> target_current_context_; + + // The Aura window that is currently under the cursor. We need to manually + // keep track of this because Windows will only call our drag enter method + // once when the user enters the associated X Window. But inside that X + // Window there could be multiple aura windows, so we need to generate drag + // enter events for them. + aura::Window* target_window_; + + // Because Xdnd messages don't contain the position in messages other than + // the XdndPosition message, we must manually keep track of the last position + // change. + gfx::Point target_window_location_; + gfx::Point target_window_root_location_; + + // In the Xdnd protocol, we aren't supposed to send another XdndPosition + // message until we have received a confirming XdndStatus message. + std::set< ::Window> waiting_on_status_; + + // If we would send an XdndPosition message while we're waiting for an + // XdndStatus response, we need to cache the latest details we'd send. + NextPositionMap next_position_message_; + + // Source side information. + ui::OSExchangeDataProviderAuraX11 const* source_provider_; + ::Window source_current_window_; + + bool drag_drop_in_progress_; + + // The operation bitfield as requested by StartDragAndDrop. + int drag_operation_; + + // The operation performed. Is initialized to None at the start of + // StartDragAndDrop(), and is set only during the asynchronous XdndFinished + // message. + int resulting_operation_; + + // This window will be receiving a drop as soon as we receive an XdndStatus + // from it. + std::set< ::Window> pending_drop_; + + // We offer the other window a list of possible operations, + // XdndActionsList. This is the requested action from the other window. This + // is None if we haven't sent out an XdndPosition message yet, haven't yet + // received an XdndStatus or if the other window has told us that there's no + // action that we can agree on. + // + // This is a map instead of a simple variable because of the case where we + // put an XdndLeave in the queue at roughly the same time that the other + // window responds to an XdndStatus. + std::map< ::Window, ::Atom> negotiated_operation_; + + // We use these cursors while dragging. + gfx::NativeCursor grab_cursor_; + gfx::NativeCursor copy_grab_cursor_; + gfx::NativeCursor move_grab_cursor_; + + static std::map< ::Window, DesktopDragDropClientAuraX11*> g_live_client_map; + + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc new file mode 100644 index 00000000000..840e38221b4 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" + +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drag_source_win.h" +#include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_win.h" +#include "ui/views/widget/drop_target_win.h" + +namespace views { + +DesktopDragDropClientWin::DesktopDragDropClientWin( + aura::RootWindow* root_window, + HWND window) + : drag_drop_in_progress_(false), + drag_operation_(0) { + drop_target_ = new DesktopDropTargetWin(root_window, window); +} + +DesktopDragDropClientWin::~DesktopDragDropClientWin() { +} + +int DesktopDragDropClientWin::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::RootWindow* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + drag_drop_in_progress_ = true; + drag_operation_ = operation; + + drag_source_ = new ui::DragSourceWin; + DWORD effect; + HRESULT result = DoDragDrop( + ui::OSExchangeDataProviderWin::GetIDataObject(data), + drag_source_, + ui::DragDropTypes::DragOperationToDropEffect(operation), + &effect); + + drag_drop_in_progress_ = false; + + if (result != DRAGDROP_S_DROP) + effect = DROPEFFECT_NONE; + + return ui::DragDropTypes::DropEffectToDragOperation(effect); +} + +void DesktopDragDropClientWin::DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) { +} + +void DesktopDragDropClientWin::Drop(aura::Window* target, + const ui::LocatedEvent& event) { +} + +void DesktopDragDropClientWin::DragCancel() { + drag_source_->CancelDrag(); + drag_operation_ = 0; +} + +bool DesktopDragDropClientWin::IsDragDropInProgress() { + return drag_drop_in_progress_; +} + +void DesktopDragDropClientWin::OnNativeWidgetDestroying(HWND window) { + if (drop_target_.get()) { + RevokeDragDrop(window); + drop_target_ = NULL; + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h new file mode 100644 index 00000000000..7aa4952ab39 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_WIN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_WIN_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/views/views_export.h" + +namespace ui { +class DragSourceWin; +class RootWindow; +} + +namespace views { +class DesktopDragDragSourceWin; +class DesktopDropTargetWin; + +class VIEWS_EXPORT DesktopDragDropClientWin + : public aura::client::DragDropClient { + public: + DesktopDragDropClientWin(aura::RootWindow* root_window, HWND window); + virtual ~DesktopDragDropClientWin(); + + // Overridden from aura::client::DragDropClient: + virtual int StartDragAndDrop( + const ui::OSExchangeData& data, + aura::RootWindow* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) OVERRIDE; + virtual void Drop(aura::Window* target, + const ui::LocatedEvent& event) OVERRIDE; + virtual void DragCancel() OVERRIDE; + virtual bool IsDragDropInProgress() OVERRIDE; + + void OnNativeWidgetDestroying(HWND window); + + private: + bool drag_drop_in_progress_; + + int drag_operation_; + + scoped_refptr<ui::DragSourceWin> drag_source_; + + scoped_refptr<DesktopDropTargetWin> drop_target_; + + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_WIN_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc new file mode 100644 index 00000000000..d3a01f3c208 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" + +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/window.h" +#include "ui/aura/root_window.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/base/events/event.h" + +using aura::client::DragDropDelegate; +using ui::OSExchangeData; +using ui::OSExchangeDataProviderWin; + +namespace views { + +DesktopDropTargetWin::DesktopDropTargetWin(aura::RootWindow* root_window, + HWND window) + : ui::DropTargetWin(window), + root_window_(root_window), + target_window_(NULL) { +} + +DesktopDropTargetWin::~DesktopDropTargetWin() { + if (target_window_) + target_window_->RemoveObserver(this); +} + +DWORD DesktopDropTargetWin::OnDragEnter(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) { + scoped_ptr<OSExchangeData> data; + scoped_ptr<ui::DropTargetEvent> event; + DragDropDelegate* delegate; + // Translate will call OnDragEntered. + Translate(data_object, key_state, position, effect, &data, &event, &delegate); + return ui::DragDropTypes::DragOperationToDropEffect( + ui::DragDropTypes::DRAG_NONE); +} + +DWORD DesktopDropTargetWin::OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + scoped_ptr<OSExchangeData> data; + scoped_ptr<ui::DropTargetEvent> event; + DragDropDelegate* delegate; + Translate(data_object, key_state, position, effect, &data, &event, &delegate); + if (delegate) + drag_operation = delegate->OnDragUpdated(*event); + return ui::DragDropTypes::DragOperationToDropEffect(drag_operation); +} + +void DesktopDropTargetWin::OnDragLeave(IDataObject* data_object) { + NotifyDragLeave(); +} + +DWORD DesktopDropTargetWin::OnDrop(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + scoped_ptr<OSExchangeData> data; + scoped_ptr<ui::DropTargetEvent> event; + DragDropDelegate* delegate; + Translate(data_object, key_state, position, effect, &data, &event, &delegate); + if (delegate) + drag_operation = delegate->OnPerformDrop(*event); + if (target_window_) { + target_window_->RemoveObserver(this); + target_window_ = NULL; + } + return ui::DragDropTypes::DragOperationToDropEffect(drag_operation); +} + +void DesktopDropTargetWin::OnWindowDestroyed(aura::Window* window) { + DCHECK(window == target_window_); + target_window_ = NULL; +} + +void DesktopDropTargetWin::Translate( + IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect, + scoped_ptr<OSExchangeData>* data, + scoped_ptr<ui::DropTargetEvent>* event, + DragDropDelegate** delegate) { + gfx::Point location(position.x, position.y); + gfx::Point root_location = location; + root_window_->ConvertPointFromNativeScreen(&root_location); + aura::Window* target_window = + root_window_->GetEventHandlerForPoint(root_location); + bool target_window_changed = false; + if (target_window != target_window_) { + if (target_window_) + NotifyDragLeave(); + target_window_ = target_window; + if (target_window_) + target_window_->AddObserver(this); + target_window_changed = true; + } + *delegate = NULL; + if (!target_window_) + return; + *delegate = aura::client::GetDragDropDelegate(target_window_); + if (!*delegate) + return; + + data->reset(new OSExchangeData(new OSExchangeDataProviderWin(data_object))); + location = root_location; + aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); + event->reset(new ui::DropTargetEvent( + *(data->get()), + location, + root_location, + ui::DragDropTypes::DropEffectToDragOperation(effect))); + if (target_window_changed) + (*delegate)->OnDragEntered(*event->get()); +} + +void DesktopDropTargetWin::NotifyDragLeave() { + if (!target_window_) + return; + DragDropDelegate* delegate = + aura::client::GetDragDropDelegate(target_window_); + if (delegate) + delegate->OnDragExited(); + target_window_->RemoveObserver(this); + target_window_ = NULL; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.h new file mode 100644 index 00000000000..3fde71f0a3c --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.h @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTIOP_DROP_TARGET_WIN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTIOP_DROP_TARGET_WIN_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window_observer.h" +#include "ui/base/dragdrop/drop_target_win.h" + +namespace aura { +class RootWindow; +namespace client { +class DragDropDelegate; +} +} + +namespace ui { +class DropTargetEvent; +class OSExchangeData; +} + +namespace views { + +// DesktopDropTargetWin takes care of managing drag and drop for +// DesktopRootWindowHostWin. It converts Windows OLE drop messages into +// aura::client::DragDropDelegate calls. +class DesktopDropTargetWin : public ui::DropTargetWin, + public aura::WindowObserver { + public: + DesktopDropTargetWin(aura::RootWindow* root_window, HWND window); + virtual ~DesktopDropTargetWin(); + + private: + // ui::DropTargetWin implementation: + virtual DWORD OnDragEnter(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) OVERRIDE; + virtual DWORD OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) OVERRIDE; + virtual void OnDragLeave(IDataObject* data_object) OVERRIDE; + virtual DWORD OnDrop(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) OVERRIDE; + + // aura::WindowObserver implementation: + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; + + // Common functionality for the ui::DropTargetWin methods to translate from + // COM data types to Aura ones. + void Translate(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect, + scoped_ptr<ui::OSExchangeData>* data, + scoped_ptr<ui::DropTargetEvent>* event, + aura::client::DragDropDelegate** delegate); + + void NotifyDragLeave(); + + // The root window associated with this drop target. + aura::RootWindow* root_window_; + + // The Aura window that is currently under the cursor. We need to manually + // keep track of this because Windows will only call our drag enter method + // once when the user enters the associated HWND. But inside that HWND there + // could be multiple aura windows, so we need to generate drag enter events + // for them. + aura::Window* target_window_; + + DISALLOW_COPY_AND_ASSIGN(DesktopDropTargetWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTIOP_DROP_TARGET_WIN_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc new file mode 100644 index 00000000000..54894f8dc9f --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_focus_rules.h" + +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" + +namespace views { + +DesktopFocusRules::DesktopFocusRules() {} +DesktopFocusRules::~DesktopFocusRules() {} + +bool DesktopFocusRules::SupportsChildActivation(aura::Window* window) const { + // In Desktop-Aura, only children of the RootWindow are activatable. + return window->GetRootWindow() == window; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h new file mode 100644 index 00000000000..a7db8c2504f --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h @@ -0,0 +1,26 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_FOCUS_RULES_H_ +#define UI_VIEWS_WIDGET_DESKTOP_FOCUS_RULES_H_ + +#include "ui/views/corewm/base_focus_rules.h" + +namespace views { + +class DesktopFocusRules : public corewm::BaseFocusRules { + public: + DesktopFocusRules(); + virtual ~DesktopFocusRules(); + + private: + // Overridden from corewm::BaseFocusRules: + virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(DesktopFocusRules); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_FOCUS_RULES_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.cc b/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.cc new file mode 100644 index 00000000000..f376e0b495c --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_layout_manager.h" + +#include "ui/aura/root_window.h" +#include "ui/gfx/rect.h" + +namespace views { + +DesktopLayoutManager::DesktopLayoutManager(aura::RootWindow* root_window) + : root_window_(root_window), + main_window_(NULL) { +} + +DesktopLayoutManager::~DesktopLayoutManager() {} + +void DesktopLayoutManager::OnWindowResized() { + if (main_window_) + SetMainWindowSize(); +} + +void DesktopLayoutManager::OnWindowAddedToLayout(aura::Window* child) { + if (!main_window_) { + main_window_ = child; + + SetMainWindowSize(); + } +} + +void DesktopLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) { + if (main_window_ == child) + main_window_ = NULL; +} + +void DesktopLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { +} + +void DesktopLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child, + bool visible) { +} + +void DesktopLayoutManager::SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) { + if (main_window_ != child) + SetChildBoundsDirect(child, requested_bounds); +} + +void DesktopLayoutManager::SetMainWindowSize() { + gfx::Rect bounds; + bounds.set_size(root_window_->GetHostSize()); + SetChildBoundsDirect(main_window_, bounds); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.h b/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.h new file mode 100644 index 00000000000..c74202e2f34 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_layout_manager.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_LAYOUT_MANAGER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_LAYOUT_MANAGER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/aura/layout_manager.h" +#include "ui/views/views_export.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace views { + +// A LayoutManager that by default makes the first Window added to a +// RootWindow the full size, and will be resized as the RootWindow is resized. +class VIEWS_EXPORT DesktopLayoutManager : public aura::LayoutManager { + public: + explicit DesktopLayoutManager(aura::RootWindow* root_window); + virtual ~DesktopLayoutManager(); + + // Overridden from aura::LayoutManager: + virtual void OnWindowResized() OVERRIDE; + virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; + virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; + virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE; + virtual void OnChildWindowVisibilityChanged(aura::Window* child, + bool visible) OVERRIDE; + virtual void SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) OVERRIDE; + + private: + // Sets the size of |main_window_| to the internal bounds of |root_window_|. + void SetMainWindowSize(); + + aura::RootWindow* root_window_; + aura::Window* main_window_; + + DISALLOW_COPY_AND_ASSIGN(DesktopLayoutManager); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_LAYOUT_MANAGER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc new file mode 100644 index 00000000000..a9b902dee46 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc @@ -0,0 +1,91 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" + +#include "ui/aura/root_window.h" +#include "ui/base/cursor/cursor_loader.h" +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" + +namespace views { + +DesktopNativeCursorManager::DesktopNativeCursorManager( + aura::RootWindow* window, + scoped_ptr<DesktopCursorLoaderUpdater> cursor_loader_updater) + : root_window_(window), + cursor_loader_updater_(cursor_loader_updater.Pass()), + cursor_loader_(ui::CursorLoader::Create()) { + if (cursor_loader_updater_.get()) + cursor_loader_updater_->OnCreate(root_window_, cursor_loader_.get()); +} + +DesktopNativeCursorManager::~DesktopNativeCursorManager() { +} + +gfx::NativeCursor DesktopNativeCursorManager::GetInitializedCursor(int type) { + gfx::NativeCursor cursor(type); + cursor_loader_->SetPlatformCursor(&cursor); + return cursor; +} + +void DesktopNativeCursorManager::SetDisplay( + const gfx::Display& display, + views::corewm::NativeCursorManagerDelegate* delegate) { + cursor_loader_->UnloadAll(); + cursor_loader_->set_display(display); + + if (cursor_loader_updater_.get()) + cursor_loader_updater_->OnDisplayUpdated(display, cursor_loader_.get()); + + SetCursor(delegate->GetCurrentCursor(), delegate); +} + +void DesktopNativeCursorManager::SetCursor( + gfx::NativeCursor cursor, + views::corewm::NativeCursorManagerDelegate* delegate) { + gfx::NativeCursor new_cursor = cursor; + cursor_loader_->SetPlatformCursor(&new_cursor); + delegate->CommitCursor(new_cursor); + + if (delegate->GetCurrentVisibility()) + root_window_->SetCursor(new_cursor); +} + +void DesktopNativeCursorManager::SetVisibility( + bool visible, + views::corewm::NativeCursorManagerDelegate* delegate) { + delegate->CommitVisibility(visible); + + if (visible) { + SetCursor(delegate->GetCurrentCursor(), delegate); + } else { + gfx::NativeCursor invisible_cursor(ui::kCursorNone); + cursor_loader_->SetPlatformCursor(&invisible_cursor); + root_window_->SetCursor(invisible_cursor); + } + + root_window_->OnCursorVisibilityChanged(visible); +} + + +void DesktopNativeCursorManager::SetScale( + float scale, + views::corewm::NativeCursorManagerDelegate* delegate) { + NOTIMPLEMENTED(); +} + +void DesktopNativeCursorManager::SetMouseEventsEnabled( + bool enabled, + views::corewm::NativeCursorManagerDelegate* delegate) { + delegate->CommitMouseEventsEnabled(enabled); + + // TODO(erg): In the ash version, we set the last mouse location on Env. I'm + // not sure this concept makes sense on the desktop. + + SetVisibility(delegate->GetCurrentVisibility(), delegate); + + root_window_->OnMouseEventsEnableStateChanged(enabled); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h new file mode 100644 index 00000000000..f6a800bfb9f --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_CURSOR_MANAGER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_CURSOR_MANAGER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/corewm/native_cursor_manager.h" +#include "ui/views/views_export.h" + +namespace aura { +class RootWindow; +} + +namespace ui { +class CursorLoader; +} + +namespace views { +class DesktopCursorLoaderUpdater; + +namespace corewm { +class NativeCursorManagerDelegate; +} + +// A NativeCursorManager that interacts with only one RootWindow. (Unlike the +// one in ash, which interacts with all the RootWindows that ash knows about.) +class VIEWS_EXPORT DesktopNativeCursorManager + : public views::corewm::NativeCursorManager { + public: + DesktopNativeCursorManager( + aura::RootWindow* window, + scoped_ptr<DesktopCursorLoaderUpdater> cursor_loader_updater); + virtual ~DesktopNativeCursorManager(); + + // Builds a cursor and sets the internal platform representation. + gfx::NativeCursor GetInitializedCursor(int type); + + private: + // Overridden from views::corewm::NativeCursorManager: + virtual void SetDisplay( + const gfx::Display& display, + views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + virtual void SetCursor( + gfx::NativeCursor cursor, + views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + virtual void SetVisibility( + bool visible, + views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + virtual void SetScale( + float scale, + views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + virtual void SetMouseEventsEnabled( + bool enabled, + views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + + aura::RootWindow* root_window_; + scoped_ptr<DesktopCursorLoaderUpdater> cursor_loader_updater_; + scoped_ptr<ui::CursorLoader> cursor_loader_; + + DISALLOW_COPY_AND_ASSIGN(DesktopNativeCursorManager); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_CURSOR_MANAGER_H_ + diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc new file mode 100644 index 00000000000..207e655e0e6 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -0,0 +1,884 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" + +#include "base/bind.h" +#include "ui/aura/client/activation_client.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/client/stacking_client.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/root_window_host.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/aura/window_property.h" +#include "ui/base/hit_test.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/display.h" +#include "ui/gfx/point_conversions.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/size_conversions.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/corewm/compound_event_filter.h" +#include "ui/views/corewm/corewm_switches.h" +#include "ui/views/corewm/input_method_event_filter.h" +#include "ui/views/corewm/shadow_controller.h" +#include "ui/views/corewm/shadow_types.h" +#include "ui/views/corewm/tooltip_controller.h" +#include "ui/views/corewm/visibility_controller.h" +#include "ui/views/corewm/window_modality_controller.h" +#include "ui/views/drag_utils.h" +#include "ui/views/ime/input_method.h" +#include "ui/views/ime/input_method_bridge.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/widget/drop_helper.h" +#include "ui/views/widget/native_widget_aura_window_observer.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/tooltip_manager_aura.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_aura_utils.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/window_reorderer.h" + +DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, + views::DesktopNativeWidgetAura*); + +namespace views { + +DEFINE_WINDOW_PROPERTY_KEY(DesktopNativeWidgetAura*, + kDesktopNativeWidgetAuraKey, NULL); + +namespace { + +// This class provides functionality to create a top level fullscreen widget to +// host a child window. +class DesktopNativeWidgetFullscreenHandler : public aura::WindowObserver { + public: + // This function creates a full screen widget with the bounds passed in + // which eventually becomes the parent of the child window passed in. + static aura::Window* CreateParentWindow(aura::Window* child_window, + const gfx::Rect& bounds) { + // This instance will get deleted when the fullscreen widget is destroyed. + DesktopNativeWidgetFullscreenHandler* full_screen_handler = + new DesktopNativeWidgetFullscreenHandler; + + child_window->SetBounds(gfx::Rect(bounds.size())); + + Widget::InitParams init_params; + init_params.type = Widget::InitParams::TYPE_WINDOW; + init_params.bounds = bounds; + init_params.ownership = Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET; + init_params.layer_type = ui::LAYER_NOT_DRAWN; + + // This widget instance will get deleted when the fullscreen window is + // destroyed. + full_screen_handler->full_screen_widget_ = new Widget(); + full_screen_handler->full_screen_widget_->Init(init_params); + + full_screen_handler->full_screen_widget_->SetFullscreen(true); + full_screen_handler->full_screen_widget_->Show(); + + aura::Window* native_window = + full_screen_handler->full_screen_widget_->GetNativeView(); + child_window->AddObserver(full_screen_handler); + native_window->AddObserver(full_screen_handler); + return native_window; + } + + // aura::WindowObserver overrides + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { + window->RemoveObserver(this); + + // If the widget is being destroyed by the OS then we should not try and + // destroy it again. + if (full_screen_widget_ && + window == full_screen_widget_->GetNativeView()) { + full_screen_widget_ = NULL; + return; + } + + if (full_screen_widget_) { + DCHECK(full_screen_widget_->GetNativeView()); + full_screen_widget_->GetNativeView()->RemoveObserver(this); + // When we receive a notification that the child of the fullscreen window + // created above is being destroyed we go ahead and initiate the + // destruction of the corresponding widget. + full_screen_widget_->Close(); + full_screen_widget_ = NULL; + } + delete this; + } + + private: + DesktopNativeWidgetFullscreenHandler() + : full_screen_widget_(NULL) {} + + virtual ~DesktopNativeWidgetFullscreenHandler() {} + + Widget* full_screen_widget_; + + DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetFullscreenHandler); +}; + +class DesktopNativeWidgetAuraStackingClient : + public aura::client::StackingClient { + public: + explicit DesktopNativeWidgetAuraStackingClient(aura::RootWindow* root_window) + : root_window_(root_window) { + aura::client::SetStackingClient(root_window_, this); + } + virtual ~DesktopNativeWidgetAuraStackingClient() { + aura::client::SetStackingClient(root_window_, NULL); + } + + // Overridden from client::StackingClient: + virtual aura::Window* GetDefaultParent(aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) OVERRIDE { + if (window->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_FULLSCREEN) { + return DesktopNativeWidgetFullscreenHandler::CreateParentWindow(window, + bounds); + } + return root_window_; + } + + private: + aura::RootWindow* root_window_; + + DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetAuraStackingClient); +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, public: + +DesktopNativeWidgetAura::DesktopNativeWidgetAura( + internal::NativeWidgetDelegate* delegate) + : ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), + close_widget_factory_(this), + can_activate_(true), + desktop_root_window_host_(NULL), + window_(new aura::Window(this)), + native_widget_delegate_(delegate), + last_drop_operation_(ui::DragDropTypes::DRAG_NONE), + restore_focus_on_activate_(false), + cursor_(gfx::kNullCursor) { + window_->SetProperty(kDesktopNativeWidgetAuraKey, this); + aura::client::SetFocusChangeObserver(window_, this); + aura::client::SetActivationChangeObserver(window_, this); +} + +DesktopNativeWidgetAura::~DesktopNativeWidgetAura() { + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete native_widget_delegate_; + else + CloseNow(); + + stacking_client_.reset(); // Uses root_window_ at destruction. + + root_window_->RemoveRootWindowObserver(this); + root_window_.reset(); // Uses input_method_event_filter_ at destruction. + input_method_event_filter_.reset(); +} + +// static +DesktopNativeWidgetAura* DesktopNativeWidgetAura::ForWindow( + aura::Window* window) { + return window->GetProperty(kDesktopNativeWidgetAuraKey); +} + +void DesktopNativeWidgetAura::OnHostClosed() { + root_window_event_filter_->RemoveHandler(input_method_event_filter_.get()); + // This will, through a long list of callbacks, trigger |root_window_| going + // away. See OnWindowDestroyed() + delete window_; +} + +void DesktopNativeWidgetAura::InstallInputMethodEventFilter( + aura::RootWindow* root) { + DCHECK(!input_method_event_filter_.get()); + + // CEF sets focus to the window the user clicks down on. + // TODO(beng): see if we can't do this some other way. CEF seems a heavy- + // handed way of accomplishing focus. + // No event filter for aura::Env. Create CompoundEvnetFilter per RootWindow. + root_window_event_filter_ = new corewm::CompoundEventFilter; + // Pass ownership of the filter to the root_window. + root->SetEventFilter(root_window_event_filter_); + + input_method_event_filter_.reset( + new corewm::InputMethodEventFilter(root->GetAcceleratedWidget())); + input_method_event_filter_->SetInputMethodPropertyInRootWindow(root); + root_window_event_filter_->AddHandler(input_method_event_filter_.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, internal::NativeWidgetPrivate implementation: + +void DesktopNativeWidgetAura::InitNativeWidget( + const Widget::InitParams& params) { + ownership_ = params.ownership; + + window_->set_user_data(this); + window_->SetType(GetAuraWindowTypeForWidgetType(params.type)); + window_->SetTransparent(true); + window_->Init(params.layer_type); + corewm::SetShadowType(window_, corewm::SHADOW_TYPE_NONE); +#if defined(OS_LINUX) // TODO(scottmg): http://crbug.com/180071 + window_->Show(); +#endif + + desktop_root_window_host_ = params.desktop_root_window_host ? + params.desktop_root_window_host : + DesktopRootWindowHost::Create(native_widget_delegate_, + this, params.bounds); + root_window_.reset( + desktop_root_window_host_->Init(window_, params)); + root_window_->AddRootWindowObserver(this); + + stacking_client_.reset( + new DesktopNativeWidgetAuraStackingClient(root_window_.get())); + drop_helper_.reset(new DropHelper( + static_cast<internal::RootView*>(GetWidget()->GetRootView()))); + aura::client::SetDragDropDelegate(window_, this); + + tooltip_manager_.reset(new views::TooltipManagerAura(window_, GetWidget())); + tooltip_controller_.reset( + new corewm::TooltipController(gfx::SCREEN_TYPE_NATIVE)); + aura::client::SetTooltipClient(root_window_.get(), + tooltip_controller_.get()); + root_window_->AddPreTargetHandler(tooltip_controller_.get()); + + if (params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW) { + visibility_controller_.reset(new views::corewm::VisibilityController); + aura::client::SetVisibilityClient(GetNativeView()->GetRootWindow(), + visibility_controller_.get()); + views::corewm::SetChildWindowVisibilityChangesAnimated( + GetNativeView()->GetRootWindow()); + } + + if (params.type == Widget::InitParams::TYPE_WINDOW) { + window_modality_controller_.reset( + new views::corewm::WindowModalityController); + root_window_->AddPreTargetHandler(window_modality_controller_.get()); + } + + window_->Show(); + desktop_root_window_host_->InitFocus(window_); + + aura::client::SetActivationDelegate(window_, this); + + shadow_controller_.reset( + new corewm::ShadowController( + aura::client::GetActivationClient(root_window_.get()))); + + window_reorderer_.reset(new WindowReorderer(window_, + GetWidget()->GetRootView())); +} + +NonClientFrameView* DesktopNativeWidgetAura::CreateNonClientFrameView() { + return desktop_root_window_host_->CreateNonClientFrameView(); +} + +bool DesktopNativeWidgetAura::ShouldUseNativeFrame() const { + return desktop_root_window_host_->ShouldUseNativeFrame(); +} + +void DesktopNativeWidgetAura::FrameTypeChanged() { + desktop_root_window_host_->FrameTypeChanged(); +} + +Widget* DesktopNativeWidgetAura::GetWidget() { + return native_widget_delegate_->AsWidget(); +} + +const Widget* DesktopNativeWidgetAura::GetWidget() const { + return native_widget_delegate_->AsWidget(); +} + +gfx::NativeView DesktopNativeWidgetAura::GetNativeView() const { + return window_; +} + +gfx::NativeWindow DesktopNativeWidgetAura::GetNativeWindow() const { + return window_; +} + +Widget* DesktopNativeWidgetAura::GetTopLevelWidget() { + return GetWidget(); +} + +const ui::Compositor* DesktopNativeWidgetAura::GetCompositor() const { + return window_->layer()->GetCompositor(); +} + +ui::Compositor* DesktopNativeWidgetAura::GetCompositor() { + return window_->layer()->GetCompositor(); +} + +ui::Layer* DesktopNativeWidgetAura::GetLayer() { + return window_->layer(); +} + +void DesktopNativeWidgetAura::ReorderNativeViews() { + window_reorderer_->ReorderChildWindows(); +} + +void DesktopNativeWidgetAura::ViewRemoved(View* view) { +} + +void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name, + void* value) { + window_->SetNativeWindowProperty(name, value); +} + +void* DesktopNativeWidgetAura::GetNativeWindowProperty(const char* name) const { + return window_->GetNativeWindowProperty(name); +} + +TooltipManager* DesktopNativeWidgetAura::GetTooltipManager() const { + return tooltip_manager_.get(); +} + +void DesktopNativeWidgetAura::SetCapture() { + window_->SetCapture(); + // aura::Window doesn't implicitly update capture on the RootWindowHost, so + // we have to do that manually. + if (!desktop_root_window_host_->HasCapture()) + window_->GetRootWindow()->SetNativeCapture(); +} + +void DesktopNativeWidgetAura::ReleaseCapture() { + window_->ReleaseCapture(); + // aura::Window doesn't implicitly update capture on the RootWindowHost, so + // we have to do that manually. + if (desktop_root_window_host_->HasCapture()) + window_->GetRootWindow()->ReleaseNativeCapture(); +} + +bool DesktopNativeWidgetAura::HasCapture() const { + return window_->HasCapture() && desktop_root_window_host_->HasCapture(); +} + +InputMethod* DesktopNativeWidgetAura::CreateInputMethod() { + ui::InputMethod* host = input_method_event_filter_->input_method(); + return new InputMethodBridge(this, host, false); +} + +internal::InputMethodDelegate* + DesktopNativeWidgetAura::GetInputMethodDelegate() { + return this; +} + +void DesktopNativeWidgetAura::CenterWindow(const gfx::Size& size) { + desktop_root_window_host_->CenterWindow(size); +} + +void DesktopNativeWidgetAura::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* maximized) const { + desktop_root_window_host_->GetWindowPlacement(bounds, maximized); +} + +void DesktopNativeWidgetAura::SetWindowTitle(const string16& title) { + desktop_root_window_host_->SetWindowTitle(title); +} + +void DesktopNativeWidgetAura::SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + desktop_root_window_host_->SetWindowIcons(window_icon, app_icon); +} + +void DesktopNativeWidgetAura::InitModalType(ui::ModalType modal_type) { + // 99% of the time, we should not be asked to create a + // DesktopNativeWidgetAura that is modal. The case where this breaks down is + // when there are no browser windows and a background extension tries to + // display a simple alert dialog. (This case was masked because we used to + // have a hidden RootWindow which was the parent of these modal dialogs; they + // weren't displayed to the user.) +} + +gfx::Rect DesktopNativeWidgetAura::GetWindowBoundsInScreen() const { + return desktop_root_window_host_->GetWindowBoundsInScreen(); +} + +gfx::Rect DesktopNativeWidgetAura::GetClientAreaBoundsInScreen() const { + return desktop_root_window_host_->GetClientAreaBoundsInScreen(); +} + +gfx::Rect DesktopNativeWidgetAura::GetRestoredBounds() const { + return desktop_root_window_host_->GetRestoredBounds(); +} + +void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) { + float scale = 1; + aura::RootWindow* root = root_window_.get(); + if (root) { + scale = gfx::Screen::GetScreenFor(root)-> + GetDisplayNearestWindow(root).device_scale_factor(); + } + gfx::Rect bounds_in_pixels( + gfx::ToCeiledPoint(gfx::ScalePoint(bounds.origin(), scale)), + gfx::ToFlooredSize(gfx::ScaleSize(bounds.size(), scale))); + desktop_root_window_host_->AsRootWindowHost()->SetBounds(bounds_in_pixels); +} + +void DesktopNativeWidgetAura::SetSize(const gfx::Size& size) { + desktop_root_window_host_->SetSize(size); +} + +void DesktopNativeWidgetAura::StackAbove(gfx::NativeView native_view) { +} + +void DesktopNativeWidgetAura::StackAtTop() { +} + +void DesktopNativeWidgetAura::StackBelow(gfx::NativeView native_view) { +} + +void DesktopNativeWidgetAura::SetShape(gfx::NativeRegion shape) { + desktop_root_window_host_->SetShape(shape); +} + +void DesktopNativeWidgetAura::Close() { + desktop_root_window_host_->Close(); + if (window_) + window_->SuppressPaint(); +} + +void DesktopNativeWidgetAura::CloseNow() { + desktop_root_window_host_->CloseNow(); +} + +void DesktopNativeWidgetAura::Show() { + desktop_root_window_host_->AsRootWindowHost()->Show(); + window_->Show(); +} + +void DesktopNativeWidgetAura::Hide() { + desktop_root_window_host_->AsRootWindowHost()->Hide(); + if (window_) + window_->Hide(); +} + +void DesktopNativeWidgetAura::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + desktop_root_window_host_->ShowMaximizedWithBounds(restored_bounds); + window_->Show(); +} + +void DesktopNativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { + desktop_root_window_host_->ShowWindowWithState(state); + window_->Show(); +} + +bool DesktopNativeWidgetAura::IsVisible() const { + return desktop_root_window_host_->IsVisible(); +} + +void DesktopNativeWidgetAura::Activate() { + desktop_root_window_host_->Activate(); +} + +void DesktopNativeWidgetAura::Deactivate() { + desktop_root_window_host_->Deactivate(); +} + +bool DesktopNativeWidgetAura::IsActive() const { + return desktop_root_window_host_->IsActive(); +} + +void DesktopNativeWidgetAura::SetAlwaysOnTop(bool always_on_top) { + desktop_root_window_host_->SetAlwaysOnTop(always_on_top); +} + +void DesktopNativeWidgetAura::Maximize() { + desktop_root_window_host_->Maximize(); +} + +void DesktopNativeWidgetAura::Minimize() { + desktop_root_window_host_->Minimize(); +} + +bool DesktopNativeWidgetAura::IsMaximized() const { + return desktop_root_window_host_->IsMaximized(); +} + +bool DesktopNativeWidgetAura::IsMinimized() const { + return desktop_root_window_host_->IsMinimized(); +} + +void DesktopNativeWidgetAura::Restore() { + desktop_root_window_host_->Restore(); +} + +void DesktopNativeWidgetAura::SetFullscreen(bool fullscreen) { + desktop_root_window_host_->SetFullscreen(fullscreen); +} + +bool DesktopNativeWidgetAura::IsFullscreen() const { + return desktop_root_window_host_->IsFullscreen(); +} + +void DesktopNativeWidgetAura::SetOpacity(unsigned char opacity) { + desktop_root_window_host_->SetOpacity(opacity); +} + +void DesktopNativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { +} + +void DesktopNativeWidgetAura::FlashFrame(bool flash_frame) { + desktop_root_window_host_->FlashFrame(flash_frame); +} + +void DesktopNativeWidgetAura::RunShellDrag( + View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + views::RunShellDrag(window_, data, location, operation, source); +} + +void DesktopNativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) { + if (window_) + window_->SchedulePaintInRect(rect); +} + +void DesktopNativeWidgetAura::SetCursor(gfx::NativeCursor cursor) { + cursor_ = cursor; + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window_->GetRootWindow()); + if (cursor_client) + cursor_client->SetCursor(cursor); +} + +bool DesktopNativeWidgetAura::IsMouseEventsEnabled() const { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window_->GetRootWindow()); + return cursor_client ? cursor_client->IsMouseEventsEnabled() : true; +} + +void DesktopNativeWidgetAura::ClearNativeFocus() { + desktop_root_window_host_->ClearNativeFocus(); + + if (ShouldActivate()) { + aura::client::GetFocusClient(window_)-> + ResetFocusWithinActiveWindow(window_); + } +} + +gfx::Rect DesktopNativeWidgetAura::GetWorkAreaBoundsInScreen() const { + return desktop_root_window_host_->GetWorkAreaBoundsInScreen(); +} + +void DesktopNativeWidgetAura::SetInactiveRenderingDisabled(bool value) { + if (!value) { + active_window_observer_.reset(); + } else { + active_window_observer_.reset( + new NativeWidgetAuraWindowObserver(window_, native_widget_delegate_)); + } + desktop_root_window_host_->SetInactiveRenderingDisabled(value); +} + +Widget::MoveLoopResult DesktopNativeWidgetAura::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) { + return desktop_root_window_host_->RunMoveLoop(drag_offset, source); +} + +void DesktopNativeWidgetAura::EndMoveLoop() { + desktop_root_window_host_->EndMoveLoop(); +} + +void DesktopNativeWidgetAura::SetVisibilityChangedAnimationsEnabled( + bool value) { + desktop_root_window_host_->SetVisibilityChangedAnimationsEnabled(value); +} + +ui::NativeTheme* DesktopNativeWidgetAura::GetNativeTheme() const { + return DesktopRootWindowHost::GetNativeTheme(window_); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::WindowDelegate implementation: + +gfx::Size DesktopNativeWidgetAura::GetMinimumSize() const { + return native_widget_delegate_->GetMinimumSize(); +} + +gfx::Size DesktopNativeWidgetAura::GetMaximumSize() const { + return native_widget_delegate_->GetMaximumSize(); +} + +void DesktopNativeWidgetAura::OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + if (old_bounds.origin() != new_bounds.origin()) + native_widget_delegate_->OnNativeWidgetMove(); + if (old_bounds.size() != new_bounds.size()) + native_widget_delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); +} + +gfx::NativeCursor DesktopNativeWidgetAura::GetCursor(const gfx::Point& point) { + return cursor_; +} + +int DesktopNativeWidgetAura::GetNonClientComponent( + const gfx::Point& point) const { + return native_widget_delegate_->GetNonClientComponent(point); +} + +bool DesktopNativeWidgetAura::ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) { + views::WidgetDelegate* widget_delegate = GetWidget()->widget_delegate(); + return !widget_delegate || + widget_delegate->ShouldDescendIntoChildForEventHandling(child, location); +} + +bool DesktopNativeWidgetAura::CanFocus() { + return true; +} + +void DesktopNativeWidgetAura::OnCaptureLost() { + native_widget_delegate_->OnMouseCaptureLost(); +} + +void DesktopNativeWidgetAura::OnPaint(gfx::Canvas* canvas) { + native_widget_delegate_->OnNativeWidgetPaint(canvas); +} + +void DesktopNativeWidgetAura::OnDeviceScaleFactorChanged( + float device_scale_factor) { +} + +void DesktopNativeWidgetAura::OnWindowDestroying() { + // DesktopRootWindowHost owns the ActivationController which ShadowController + // references. Make sure we destroy ShadowController early on. + shadow_controller_.reset(); + // The DesktopRootWindowHost implementation sends OnNativeWidgetDestroying(). + tooltip_manager_.reset(); + if (tooltip_controller_.get()) { + root_window_->RemovePreTargetHandler(tooltip_controller_.get()); + tooltip_controller_.reset(); + aura::client::SetTooltipClient(root_window_.get(), NULL); + } + if (window_modality_controller_) { + root_window_->RemovePreTargetHandler(window_modality_controller_.get()); + window_modality_controller_.reset(); + } +} + +void DesktopNativeWidgetAura::OnWindowDestroyed() { + window_ = NULL; + native_widget_delegate_->OnNativeWidgetDestroyed(); + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete this; +} + +void DesktopNativeWidgetAura::OnWindowTargetVisibilityChanged(bool visible) { +} + +bool DesktopNativeWidgetAura::HasHitTestMask() const { + return native_widget_delegate_->HasHitTestMask(); +} + +void DesktopNativeWidgetAura::GetHitTestMask(gfx::Path* mask) const { + native_widget_delegate_->GetHitTestMask(mask); +} + +scoped_refptr<ui::Texture> DesktopNativeWidgetAura::CopyTexture() { + // The layer we create doesn't have an external texture, so this should never + // get invoked. + NOTREACHED(); + return scoped_refptr<ui::Texture>(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, ui::EventHandler implementation: + +void DesktopNativeWidgetAura::OnKeyEvent(ui::KeyEvent* event) { + if (event->is_char()) { + // If a ui::InputMethod object is attached to the root window, character + // events are handled inside the object and are not passed to this function. + // If such object is not attached, character events might be sent (e.g. on + // Windows). In this case, we just skip these. + return; + } + // Renderer may send a key event back to us if the key event wasn't handled, + // and the window may be invisible by that time. + if (!window_->IsVisible()) + return; + + native_widget_delegate_->OnKeyEvent(event); + if (event->handled()) + return; + + if (GetWidget()->HasFocusManager() && + !GetWidget()->GetFocusManager()->OnKeyEvent(*event)) + event->SetHandled(); +} + +void DesktopNativeWidgetAura::OnMouseEvent(ui::MouseEvent* event) { + DCHECK(window_->IsVisible()); + if (tooltip_manager_.get()) + tooltip_manager_->UpdateTooltip(); + native_widget_delegate_->OnMouseEvent(event); + // WARNING: we may have been deleted. +} + +void DesktopNativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) { + if (event->type() == ui::ET_SCROLL) { + native_widget_delegate_->OnScrollEvent(event); + if (event->handled()) + return; + + // Convert unprocessed scroll events into wheel events. + ui::MouseWheelEvent mwe(*static_cast<ui::ScrollEvent*>(event)); + native_widget_delegate_->OnMouseEvent(&mwe); + if (mwe.handled()) + event->SetHandled(); + } else { + native_widget_delegate_->OnScrollEvent(event); + } +} + +void DesktopNativeWidgetAura::OnTouchEvent(ui::TouchEvent* event) { + native_widget_delegate_->OnTouchEvent(event); +} + +void DesktopNativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { + native_widget_delegate_->OnGestureEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::client::ActivationDelegate implementation: + +bool DesktopNativeWidgetAura::ShouldActivate() const { + return can_activate_ && native_widget_delegate_->CanActivate(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::client::ActivationChangeObserver +// implementation: + +void DesktopNativeWidgetAura::OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) { + DCHECK(window_ == gained_active || window_ == lost_active); + if ((window_ == gained_active || window_ == lost_active) && + IsVisible() && GetWidget()->non_client_view()) { + GetWidget()->non_client_view()->SchedulePaint(); + } + if (gained_active == window_ && restore_focus_on_activate_) { + restore_focus_on_activate_ = false; + GetWidget()->GetFocusManager()->RestoreFocusedView(); + } else if (lost_active == window_ && GetWidget()->HasFocusManager()) { + bool store_focused_view = corewm::UseFocusControllerOnDesktop(); + if (!store_focused_view) { + // If we're losing focus to a window that is a top level (such as a + // bubble) store the focus. Such a window shares the same + // RootWindowHost, so that such a change won't trigger an activation + // change (which calls StoreFocusedView()). Without this the focused + // view is never told it lost focus. + aura::Window* focused_window = + aura::client::GetFocusClient(window_)->GetFocusedWindow(); + if (focused_window && focused_window != window_) { + Widget* focused_widget = + Widget::GetWidgetForNativeWindow(focused_window); + store_focused_view = focused_widget && focused_widget != GetWidget() && + focused_widget->is_top_level(); + } + } + if (store_focused_view) { + DCHECK(!restore_focus_on_activate_); + restore_focus_on_activate_ = true; + // Pass in false so that ClearNativeFocus() isn't invoked. + GetWidget()->GetFocusManager()->StoreFocusedView(false); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::client::FocusChangeObserver implementation: + +void DesktopNativeWidgetAura::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + if (window_ == gained_focus) { + desktop_root_window_host_->OnNativeWidgetFocus(); + native_widget_delegate_->OnNativeFocus(lost_focus); + + // If focus is moving from a descendant Window to |window_| then native + // activation hasn't changed. We still need to inform the InputMethod we've + // been focused though. + InputMethod* input_method = GetWidget()->GetInputMethod(); + if (input_method) + input_method->OnFocus(); + } else if (window_ == lost_focus) { + desktop_root_window_host_->OnNativeWidgetBlur(); + native_widget_delegate_->OnNativeBlur( + aura::client::GetFocusClient(window_)->GetFocusedWindow()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, views::internal::InputMethodDelegate: + +void DesktopNativeWidgetAura::DispatchKeyEventPostIME(const ui::KeyEvent& key) { + FocusManager* focus_manager = + native_widget_delegate_->AsWidget()->GetFocusManager(); + native_widget_delegate_->OnKeyEvent(const_cast<ui::KeyEvent*>(&key)); + if (key.handled() || !focus_manager) + return; + focus_manager->OnKeyEvent(key); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::WindowDragDropDelegate implementation: + +void DesktopNativeWidgetAura::OnDragEntered(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + last_drop_operation_ = drop_helper_->OnDragOver(event.data(), + event.location(), event.source_operations()); +} + +int DesktopNativeWidgetAura::OnDragUpdated(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + last_drop_operation_ = drop_helper_->OnDragOver(event.data(), + event.location(), event.source_operations()); + return last_drop_operation_; +} + +void DesktopNativeWidgetAura::OnDragExited() { + DCHECK(drop_helper_.get() != NULL); + drop_helper_->OnDragExit(); +} + +int DesktopNativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + return drop_helper_->OnDrop(event.data(), event.location(), + last_drop_operation_); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, aura::RootWindowObserver implementation: + +void DesktopNativeWidgetAura::OnRootWindowHostCloseRequested( + const aura::RootWindow* root) { + Close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopNativeWidgetAura, NativeWidget implementation: + +ui::EventHandler* DesktopNativeWidgetAura::GetEventHandler() { + return this; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h new file mode 100644 index 00000000000..b98e1ccc7f9 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -0,0 +1,264 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_WIDGET_AURA_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_WIDGET_AURA_H_ + +#include "base/memory/weak_ptr.h" +#include "ui/aura/client/activation_change_observer.h" +#include "ui/aura/client/activation_delegate.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/root_window_observer.h" +#include "ui/aura/window_delegate.h" +#include "ui/views/ime/input_method_delegate.h" +#include "ui/views/widget/native_widget_private.h" + +namespace aura { +class RootWindow; +namespace client { +class StackingClient; +} +} + +namespace views { + +namespace corewm { +class CompoundEventFilter; +class InputMethodEventFilter; +class ShadowController; +class TooltipController; +class VisibilityController; +class WindowModalityController; +} + +class DesktopRootWindowHost; +class DropHelper; +class NativeWidgetAuraWindowObserver; +class TooltipManagerAura; +class WindowReorderer; + +class VIEWS_EXPORT DesktopNativeWidgetAura + : public internal::NativeWidgetPrivate, + public aura::WindowDelegate, + public aura::client::ActivationDelegate, + public aura::client::ActivationChangeObserver, + public aura::client::FocusChangeObserver, + public views::internal::InputMethodDelegate, + public aura::client::DragDropDelegate, + public aura::RootWindowObserver { + public: + explicit DesktopNativeWidgetAura(internal::NativeWidgetDelegate* delegate); + virtual ~DesktopNativeWidgetAura(); + + // Maps from window to DesktopNativeWidgetAura. + static DesktopNativeWidgetAura* ForWindow(aura::Window* window); + + // Called by our DesktopRootWindowHost after it has deleted native resources; + // this is the signal that we should start our shutdown. + void OnHostClosed(); + + // Installs the input method filter on |root|. This is intended to be invoked + // by the DesktopRootWindowHost implementation during Init(). + void InstallInputMethodEventFilter(aura::RootWindow* root); + corewm::InputMethodEventFilter* input_method_event_filter() { + return input_method_event_filter_.get(); + } + corewm::CompoundEventFilter* root_window_event_filter() { + return root_window_event_filter_; + } + + // Overridden from NativeWidget: + virtual ui::EventHandler* GetEventHandler() OVERRIDE; + + protected: + // Overridden from internal::NativeWidgetPrivate: + virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual void FrameTypeChanged() OVERRIDE; + virtual Widget* GetWidget() OVERRIDE; + virtual const Widget* GetWidget() const OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE; + virtual Widget* GetTopLevelWidget() OVERRIDE; + virtual const ui::Compositor* GetCompositor() const OVERRIDE; + virtual ui::Compositor* GetCompositor() OVERRIDE; + virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; + virtual void ViewRemoved(View* view) OVERRIDE; + virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; + virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; + virtual TooltipManager* GetTooltipManager() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual bool HasCapture() const OVERRIDE; + virtual InputMethod* CreateInputMethod() OVERRIDE; + virtual internal::InputMethodDelegate* GetInputMethodDelegate() OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* maximized) const OVERRIDE; + virtual void SetWindowTitle(const string16& title) OVERRIDE; + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) OVERRIDE; + virtual void InitModalType(ui::ModalType modal_type) OVERRIDE; + virtual gfx::Rect GetWindowBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetClientAreaBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetRestoredBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void StackAbove(gfx::NativeView native_view) OVERRIDE; + virtual void StackAtTop() OVERRIDE; + virtual void StackBelow(gfx::NativeView native_view) OVERRIDE; + virtual void SetShape(gfx::NativeRegion shape) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void CloseNow() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) OVERRIDE; + virtual void ShowWithWindowState(ui::WindowShowState state) OVERRIDE; + virtual bool IsVisible() const OVERRIDE; + virtual void Activate() OVERRIDE; + virtual void Deactivate() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual bool IsMaximized() const OVERRIDE; + virtual bool IsMinimized() const OVERRIDE; + virtual void Restore() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; + virtual void SetOpacity(unsigned char opacity) OVERRIDE; + virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE; + virtual void FlashFrame(bool flash_frame) OVERRIDE; + virtual void RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; + virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; + virtual bool IsMouseEventsEnabled() const OVERRIDE; + virtual void ClearNativeFocus() OVERRIDE; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE; + virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; + + // Overridden from aura::WindowDelegate: + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; + virtual void OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE; + virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE; + virtual bool ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) OVERRIDE; + virtual bool CanFocus() OVERRIDE; + virtual void OnCaptureLost() OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void OnWindowDestroying() OVERRIDE; + virtual void OnWindowDestroyed() OVERRIDE; + virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; + virtual bool HasHitTestMask() const OVERRIDE; + virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; + virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + + // Overridden from ui::EventHandler: + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; + virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + + // Overridden from aura::client::ActivationDelegate: + virtual bool ShouldActivate() const OVERRIDE; + + // Overridden from aura::client::ActivationChangeObserver: + virtual void OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) OVERRIDE; + + // Overridden from aura::client::FocusChangeObserver: + virtual void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) OVERRIDE; + + // Overridden from views::internal::InputMethodDelegate: + virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) OVERRIDE; + + // Overridden from aura::client::DragDropDelegate: + virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE; + virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE; + virtual void OnDragExited() OVERRIDE; + virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; + + // Overridden from aura::RootWindowObserver: + virtual void OnRootWindowHostCloseRequested( + const aura::RootWindow* root) OVERRIDE; + + private: + // See class documentation for Widget in widget.h for a note about ownership. + Widget::InitParams::Ownership ownership_; + + // The NativeWidget owns the RootWindow. Required because the RootWindow owns + // its RootWindowHost, so DesktopRootWindowHost can't own it. + scoped_ptr<aura::RootWindow> root_window_; + + // The following factory is used for calls to close the NativeWidgetAura + // instance. + base::WeakPtrFactory<DesktopNativeWidgetAura> close_widget_factory_; + + scoped_ptr<NativeWidgetAuraWindowObserver> active_window_observer_; + + // Can we be made active? + bool can_activate_; + + // Ownership passed to RootWindow on Init. + DesktopRootWindowHost* desktop_root_window_host_; + aura::Window* window_; + internal::NativeWidgetDelegate* native_widget_delegate_; + + scoped_ptr<aura::client::StackingClient> stacking_client_; + + // Toplevel event filter which dispatches to other event filters. + corewm::CompoundEventFilter* root_window_event_filter_; + + scoped_ptr<corewm::InputMethodEventFilter> input_method_event_filter_; + + scoped_ptr<DropHelper> drop_helper_; + int last_drop_operation_; + + scoped_ptr<corewm::TooltipController> tooltip_controller_; + scoped_ptr<TooltipManagerAura> tooltip_manager_; + + scoped_ptr<views::corewm::VisibilityController> visibility_controller_; + + scoped_ptr<views::corewm::WindowModalityController> + window_modality_controller_; + + // See comments in OnLostActive(). + bool restore_focus_on_activate_; + + gfx::NativeCursor cursor_; + + scoped_ptr<corewm::ShadowController> shadow_controller_; + + // Reorders child windows of |window_| associated with a view based on the + // order of the associated views in the widget's view hierarchy. + scoped_ptr<WindowReorderer> window_reorderer_; + + DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetAura); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_WIDGET_AURA_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host.h b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host.h new file mode 100644 index 00000000000..afc412aad65 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host.h @@ -0,0 +1,123 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_H_ + +#include "ui/base/ui_base_types.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/widget.h" + +namespace aura { +class RootWindowHost; +class Window; +} + +namespace gfx { +class ImageSkia; +class Rect; +} + +namespace ui { +class NativeTheme; +} + +namespace views { +class DesktopNativeWidgetAura; +namespace internal { +class NativeWidgetDelegate; +} + +class VIEWS_EXPORT DesktopRootWindowHost { + public: + virtual ~DesktopRootWindowHost() {} + + static DesktopRootWindowHost* Create( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds); + + // Return the NativeTheme to use for |window|. WARNING: |window| may be NULL. + static ui::NativeTheme* GetNativeTheme(aura::Window* window); + + // Creates the aura resources associated with the native window we built. + // Caller takes ownership of returned RootWindow. + virtual aura::RootWindow* Init(aura::Window* content_window, + const Widget::InitParams& params) = 0; + virtual void InitFocus(aura::Window* window) = 0; + + virtual void Close() = 0; + virtual void CloseNow() = 0; + + virtual aura::RootWindowHost* AsRootWindowHost() = 0; + + virtual void ShowWindowWithState(ui::WindowShowState show_state) = 0; + virtual void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) = 0; + + virtual bool IsVisible() const = 0; + + virtual void SetSize(const gfx::Size& size) = 0; + virtual void CenterWindow(const gfx::Size& size) = 0; + virtual void GetWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state) const = 0; + virtual gfx::Rect GetWindowBoundsInScreen() const = 0; + virtual gfx::Rect GetClientAreaBoundsInScreen() const = 0; + virtual gfx::Rect GetRestoredBounds() const = 0; + + virtual gfx::Rect GetWorkAreaBoundsInScreen() const = 0; + + virtual void SetShape(gfx::NativeRegion native_region) = 0; + + virtual void Activate() = 0; + virtual void Deactivate() = 0; + virtual bool IsActive() const = 0; + virtual void Maximize() = 0; + virtual void Minimize() = 0; + virtual void Restore() = 0; + virtual bool IsMaximized() const = 0; + virtual bool IsMinimized() const = 0; + + virtual bool HasCapture() const = 0; + + virtual void SetAlwaysOnTop(bool always_on_top) = 0; + + virtual void SetWindowTitle(const string16& title) = 0; + + virtual void ClearNativeFocus() = 0; + + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) = 0; + virtual void EndMoveLoop() = 0; + + virtual void SetVisibilityChangedAnimationsEnabled(bool value) = 0; + + virtual bool ShouldUseNativeFrame() = 0; + virtual void FrameTypeChanged() = 0; + virtual NonClientFrameView* CreateNonClientFrameView() = 0; + + virtual void SetFullscreen(bool fullscreen) = 0; + virtual bool IsFullscreen() const = 0; + + virtual void SetOpacity(unsigned char opacity) = 0; + + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) = 0; + + virtual void InitModalType(ui::ModalType modal_type) = 0; + + virtual void FlashFrame(bool flash_frame) = 0; + + // Called when the DesktopNativeWidgetAura's aura::Window is focused and + // blurred. + virtual void OnNativeWidgetFocus() = 0; + virtual void OnNativeWidgetBlur() = 0; + + // Paints the host window as activated depending on the bool passed in. + virtual void SetInactiveRenderingDisabled(bool disable_inactive) = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h new file mode 100644 index 00000000000..f430b38c20d --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_OBSERVER_X11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_OBSERVER_X11_H_ + +#include "ui/views/views_export.h" + +namespace views { + +// Allows for the observation of lower level window events. +class VIEWS_EXPORT DesktopRootWindowHostObserverX11 { + public: + virtual ~DesktopRootWindowHostObserverX11() {} + + // Called after we receive a MapNotify event (the X11 server has allocated + // resources for it). + virtual void OnWindowMapped(unsigned long xid) = 0; + + // Called after we receive an UnmapNotify event (the X11 server has freed + // resources for it). + virtual void OnWindowUnmapped(unsigned long xid) = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_OBSERVER_X11_H_ + diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc new file mode 100644 index 00000000000..6b0de6a4628 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc @@ -0,0 +1,885 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_root_window_host_win.h" + +#include "base/win/metro.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window_property.h" +#include "ui/base/cursor/cursor_loader_win.h" +#include "ui/base/ime/input_method.h" +#include "ui/base/ime/win/tsf_bridge.h" +#include "ui/base/win/dpi.h" +#include "ui/base/win/shell.h" +#include "ui/gfx/insets.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/path_win.h" +#include "ui/native_theme/native_theme_aura.h" +#include "ui/native_theme/native_theme_win.h" +#include "ui/views/corewm/compound_event_filter.h" +#include "ui/views/corewm/corewm_switches.h" +#include "ui/views/corewm/cursor_manager.h" +#include "ui/views/corewm/focus_controller.h" +#include "ui/views/corewm/input_method_event_filter.h" +#include "ui/views/corewm/window_animations.h" +#include "ui/views/ime/input_method_bridge.h" +#include "ui/views/widget/desktop_aura/desktop_activation_client.h" +#include "ui/views/widget/desktop_aura/desktop_capture_client.h" +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" +#include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" +#include "ui/views/widget/desktop_aura/desktop_focus_rules.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_hwnd_utils.h" +#include "ui/views/win/fullscreen_handler.h" +#include "ui/views/win/hwnd_message_handler.h" +#include "ui/views/window/native_frame_view.h" + +namespace views { + +DEFINE_WINDOW_PROPERTY_KEY(aura::Window*, kContentWindowForRootWindow, NULL); + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, public: + +DesktopRootWindowHostWin::DesktopRootWindowHostWin( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds) + : root_window_(NULL), + message_handler_(new HWNDMessageHandler(this)), + native_widget_delegate_(native_widget_delegate), + desktop_native_widget_aura_(desktop_native_widget_aura), + root_window_host_delegate_(NULL), + content_window_(NULL), + should_animate_window_close_(false), + pending_close_(false), + has_non_client_view_(false) { +} + +DesktopRootWindowHostWin::~DesktopRootWindowHostWin() { + if (corewm::UseFocusControllerOnDesktop()) { + aura::client::SetFocusClient(root_window_, NULL); + aura::client::SetActivationClient(root_window_, NULL); + } +} + +// static +aura::Window* DesktopRootWindowHostWin::GetContentWindowForHWND(HWND hwnd) { + aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(hwnd); + return root ? root->GetProperty(kContentWindowForRootWindow) : NULL; +} + +// static +ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { + // Use NativeThemeWin for windows shown on the desktop, those not on the + // desktop come from Ash and get NativeThemeAura. + aura::RootWindow* root = window ? window->GetRootWindow() : NULL; + if (root) { + HWND root_hwnd = root->GetAcceleratedWidget(); + if (root_hwnd && + DesktopRootWindowHostWin::GetContentWindowForHWND(root_hwnd)) { + return ui::NativeThemeWin::instance(); + } + } + return ui::NativeThemeAura::instance(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, DesktopRootWindowHost implementation: + +aura::RootWindow* DesktopRootWindowHostWin::Init( + aura::Window* content_window, + const Widget::InitParams& params) { + // TODO(beng): SetInitParams(). + content_window_ = content_window; + + ConfigureWindowStyles(message_handler_.get(), params, + GetWidget()->widget_delegate(), + native_widget_delegate_); + + HWND parent_hwnd = NULL; + aura::Window* parent_window = params.parent; + if (parent_window) + parent_hwnd = parent_window->GetRootWindow()->GetAcceleratedWidget(); + + message_handler_->set_remove_standard_frame(params.remove_standard_frame); + + has_non_client_view_ = Widget::RequiresNonClientView(params.type); + + gfx::Rect pixel_bounds = ui::win::DIPToScreenRect(params.bounds); + message_handler_->Init(parent_hwnd, pixel_bounds); + + aura::RootWindow::CreateParams rw_params(params.bounds); + rw_params.host = this; + root_window_ = new aura::RootWindow(rw_params); + + // TODO(beng): We probably need to move these two calls to some function that + // can change depending on the native-ness of the frame. For right + // now in the hack-n-slash days of win-aura, we can just + // unilaterally turn this on. + root_window_->compositor()->SetHostHasTransparentBackground(true); + root_window_->SetTransparent(true); + + root_window_->Init(); + root_window_->AddChild(content_window_); + + capture_client_.reset(new views::DesktopCaptureClient(root_window_)); + aura::client::SetCaptureClient(root_window_, capture_client_.get()); + + if (corewm::UseFocusControllerOnDesktop()) { + corewm::FocusController* focus_controller = + new corewm::FocusController(new DesktopFocusRules); + focus_client_.reset(focus_controller); + aura::client::SetFocusClient(root_window_, focus_controller); + aura::client::SetActivationClient(root_window_, focus_controller); + root_window_->AddPreTargetHandler(focus_controller); + } else { + focus_client_.reset(new aura::FocusManager); + aura::client::SetFocusClient(root_window_, focus_client_.get()); + activation_client_.reset(new DesktopActivationClient(root_window_)); + } + + dispatcher_client_.reset(new DesktopDispatcherClient); + aura::client::SetDispatcherClient(root_window_, + dispatcher_client_.get()); + + cursor_client_.reset( + new views::corewm::CursorManager( + scoped_ptr<corewm::NativeCursorManager>( + new views::DesktopNativeCursorManager( + root_window_, + scoped_ptr<DesktopCursorLoaderUpdater>())))); + aura::client::SetCursorClient(root_window_, + cursor_client_.get()); + + position_client_.reset(new DesktopScreenPositionClient()); + aura::client::SetScreenPositionClient(root_window_, + position_client_.get()); + + desktop_native_widget_aura_->InstallInputMethodEventFilter(root_window_); + + drag_drop_client_.reset(new DesktopDragDropClientWin(root_window_, + GetHWND())); + aura::client::SetDragDropClient(root_window_, drag_drop_client_.get()); + + focus_client_->FocusWindow(content_window_); + root_window_->SetProperty(kContentWindowForRootWindow, content_window_); + + aura::client::SetAnimationHost(content_window_, this); + + should_animate_window_close_ = + content_window_->type() != aura::client::WINDOW_TYPE_NORMAL && + !views::corewm::WindowAnimationsDisabled(content_window_); + + return root_window_; +} + +void DesktopRootWindowHostWin::InitFocus(aura::Window* window) { + focus_client_->FocusWindow(window); +} + +void DesktopRootWindowHostWin::Close() { + if (should_animate_window_close_) { + pending_close_ = true; + content_window_->Hide(); + const bool is_animating = + content_window_->layer()->GetAnimator()->IsAnimatingProperty( + ui::LayerAnimationElement::VISIBILITY); + // Animation may not start for a number of reasons. + if (!is_animating) + message_handler_->Close(); + // else case, OnWindowHidingAnimationCompleted does the actual Close. + } else { + message_handler_->Close(); + } +} + +void DesktopRootWindowHostWin::CloseNow() { + message_handler_->CloseNow(); +} + +aura::RootWindowHost* DesktopRootWindowHostWin::AsRootWindowHost() { + return this; +} + +void DesktopRootWindowHostWin::ShowWindowWithState( + ui::WindowShowState show_state) { + message_handler_->ShowWindowWithState(show_state); +} + +void DesktopRootWindowHostWin::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + gfx::Rect pixel_bounds = ui::win::DIPToScreenRect(restored_bounds); + message_handler_->ShowMaximizedWithBounds(pixel_bounds); +} + +bool DesktopRootWindowHostWin::IsVisible() const { + return message_handler_->IsVisible(); +} + +void DesktopRootWindowHostWin::SetSize(const gfx::Size& size) { + gfx::Size size_in_pixels = ui::win::DIPToScreenSize(size); + message_handler_->SetSize(size_in_pixels); +} + +void DesktopRootWindowHostWin::CenterWindow(const gfx::Size& size) { + gfx::Size size_in_pixels = ui::win::DIPToScreenSize(size); + message_handler_->CenterWindow(size_in_pixels); +} + +void DesktopRootWindowHostWin::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + message_handler_->GetWindowPlacement(bounds, show_state); + *bounds = ui::win::ScreenToDIPRect(*bounds); +} + +gfx::Rect DesktopRootWindowHostWin::GetWindowBoundsInScreen() const { + gfx::Rect pixel_bounds = message_handler_->GetWindowBoundsInScreen(); + return ui::win::ScreenToDIPRect(pixel_bounds); +} + +gfx::Rect DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() const { + gfx::Rect pixel_bounds = message_handler_->GetClientAreaBoundsInScreen(); + return ui::win::ScreenToDIPRect(pixel_bounds); +} + +gfx::Rect DesktopRootWindowHostWin::GetRestoredBounds() const { + gfx::Rect pixel_bounds = message_handler_->GetRestoredBounds(); + return ui::win::ScreenToDIPRect(pixel_bounds); +} + +gfx::Rect DesktopRootWindowHostWin::GetWorkAreaBoundsInScreen() const { + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfo(MonitorFromWindow(message_handler_->hwnd(), + MONITOR_DEFAULTTONEAREST), + &monitor_info); + gfx::Rect pixel_bounds = gfx::Rect(monitor_info.rcWork); + return ui::win::ScreenToDIPRect(pixel_bounds); +} + +void DesktopRootWindowHostWin::SetShape(gfx::NativeRegion native_region) { + SkPath path; + native_region->getBoundaryPath(&path); + message_handler_->SetRegion(gfx::CreateHRGNFromSkPath(path)); +} + +void DesktopRootWindowHostWin::Activate() { + message_handler_->Activate(); +} + +void DesktopRootWindowHostWin::Deactivate() { + message_handler_->Deactivate(); +} + +bool DesktopRootWindowHostWin::IsActive() const { + return message_handler_->IsActive(); +} + +void DesktopRootWindowHostWin::Maximize() { + message_handler_->Maximize(); +} + +void DesktopRootWindowHostWin::Minimize() { + message_handler_->Minimize(); +} + +void DesktopRootWindowHostWin::Restore() { + message_handler_->Restore(); +} + +bool DesktopRootWindowHostWin::IsMaximized() const { + return message_handler_->IsMaximized(); +} + +bool DesktopRootWindowHostWin::IsMinimized() const { + return message_handler_->IsMinimized(); +} + +bool DesktopRootWindowHostWin::HasCapture() const { + return message_handler_->HasCapture(); +} + +void DesktopRootWindowHostWin::SetAlwaysOnTop(bool always_on_top) { + message_handler_->SetAlwaysOnTop(always_on_top); +} + +void DesktopRootWindowHostWin::SetWindowTitle(const string16& title) { + message_handler_->SetTitle(title); +} + +void DesktopRootWindowHostWin::ClearNativeFocus() { + message_handler_->ClearNativeFocus(); +} + +Widget::MoveLoopResult DesktopRootWindowHostWin::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) { + return message_handler_->RunMoveLoop(drag_offset) ? + Widget::MOVE_LOOP_SUCCESSFUL : Widget::MOVE_LOOP_CANCELED; +} + +void DesktopRootWindowHostWin::EndMoveLoop() { + message_handler_->EndMoveLoop(); +} + +void DesktopRootWindowHostWin::SetVisibilityChangedAnimationsEnabled( + bool value) { + message_handler_->SetVisibilityChangedAnimationsEnabled(value); + content_window_->SetProperty(aura::client::kAnimationsDisabledKey, !value); +} + +bool DesktopRootWindowHostWin::ShouldUseNativeFrame() { + return ui::win::IsAeroGlassEnabled(); +} + +void DesktopRootWindowHostWin::FrameTypeChanged() { + message_handler_->FrameTypeChanged(); +} + +NonClientFrameView* DesktopRootWindowHostWin::CreateNonClientFrameView() { + return GetWidget()->ShouldUseNativeFrame() ? + new NativeFrameView(GetWidget()) : NULL; +} + +void DesktopRootWindowHostWin::SetFullscreen(bool fullscreen) { + message_handler_->fullscreen_handler()->SetFullscreen(fullscreen); +} + +bool DesktopRootWindowHostWin::IsFullscreen() const { + return message_handler_->fullscreen_handler()->fullscreen(); +} + +void DesktopRootWindowHostWin::SetOpacity(unsigned char opacity) { + message_handler_->SetOpacity(static_cast<BYTE>(opacity)); + content_window_->layer()->SetOpacity(opacity / 255.0); + GetWidget()->GetRootView()->SchedulePaint(); +} + +void DesktopRootWindowHostWin::SetWindowIcons( + const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { + message_handler_->SetWindowIcons(window_icon, app_icon); +} + +void DesktopRootWindowHostWin::InitModalType(ui::ModalType modal_type) { + message_handler_->InitModalType(modal_type); +} + +void DesktopRootWindowHostWin::FlashFrame(bool flash_frame) { + message_handler_->FlashFrame(flash_frame); +} + +void DesktopRootWindowHostWin::OnNativeWidgetFocus() { + // HWNDMessageHandler will perform the proper updating on its own. +} + +void DesktopRootWindowHostWin::OnNativeWidgetBlur() { +} + +void DesktopRootWindowHostWin::SetInactiveRenderingDisabled( + bool disable_inactive) { + // Force the non-client area (most notably the title bar) to paint as either + // active or inactive, depending on the input. + SendMessage(message_handler_->hwnd(), WM_NCACTIVATE, !!disable_inactive, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, RootWindowHost implementation: + + +void DesktopRootWindowHostWin::SetDelegate( + aura::RootWindowHostDelegate* delegate) { + root_window_host_delegate_ = delegate; +} + +aura::RootWindow* DesktopRootWindowHostWin::GetRootWindow() { + return root_window_; +} + +gfx::AcceleratedWidget DesktopRootWindowHostWin::GetAcceleratedWidget() { + return message_handler_->hwnd(); +} + +void DesktopRootWindowHostWin::Show() { + message_handler_->Show(); +} + +void DesktopRootWindowHostWin::Hide() { + if (!pending_close_) + message_handler_->Hide(); +} + +void DesktopRootWindowHostWin::ToggleFullScreen() { +} + +// GetBounds and SetBounds work in pixel coordinates, whereas other get/set +// methods work in DIP. + +gfx::Rect DesktopRootWindowHostWin::GetBounds() const { + // Match the logic in HWNDMessageHandler::ClientAreaSizeChanged(). + gfx::Rect bounds(WidgetSizeIsClientSize() ? + message_handler_->GetClientAreaBoundsInScreen() : + message_handler_->GetWindowBoundsInScreen()); + gfx::Rect without_expansion(bounds.x() - window_expansion_.x(), + bounds.y() - window_expansion_.y(), + bounds.width() - window_expansion_.width(), + bounds.height() - window_expansion_.height()); + return without_expansion; +} + +void DesktopRootWindowHostWin::SetBounds(const gfx::Rect& bounds) { + gfx::Rect expanded(bounds.x() + window_expansion_.x(), + bounds.y() + window_expansion_.y(), + bounds.width() + window_expansion_.width(), + bounds.height() + window_expansion_.height()); + message_handler_->SetBounds(expanded); +} + +gfx::Insets DesktopRootWindowHostWin::GetInsets() const { + return gfx::Insets(); +} + +void DesktopRootWindowHostWin::SetInsets(const gfx::Insets& insets) { +} + +gfx::Point DesktopRootWindowHostWin::GetLocationOnNativeScreen() const { + return GetBounds().origin(); +} + +void DesktopRootWindowHostWin::SetCapture() { + message_handler_->SetCapture(); +} + +void DesktopRootWindowHostWin::ReleaseCapture() { + message_handler_->ReleaseCapture(); +} + +void DesktopRootWindowHostWin::SetCursor(gfx::NativeCursor cursor) { + ui::CursorLoaderWin cursor_loader; + cursor_loader.SetPlatformCursor(&cursor); + + message_handler_->SetCursor(cursor.platform()); +} + +bool DesktopRootWindowHostWin::QueryMouseLocation(gfx::Point* location_return) { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(GetRootWindow()); + if (cursor_client && !cursor_client->IsMouseEventsEnabled()) { + *location_return = gfx::Point(0, 0); + return false; + } + POINT pt = {0}; + ::GetCursorPos(&pt); + *location_return = + gfx::Point(static_cast<int>(pt.x), static_cast<int>(pt.y)); + return true; +} + +bool DesktopRootWindowHostWin::ConfineCursorToRootWindow() { + return false; +} + +void DesktopRootWindowHostWin::UnConfineCursor() { +} + +void DesktopRootWindowHostWin::OnCursorVisibilityChanged(bool show) { +} + +void DesktopRootWindowHostWin::MoveCursorTo(const gfx::Point& location) { + POINT cursor_location = location.ToPOINT(); + ::ClientToScreen(GetHWND(), &cursor_location); + ::SetCursorPos(cursor_location.x, cursor_location.y); +} + +void DesktopRootWindowHostWin::SetFocusWhenShown(bool focus_when_shown) { +} + +void DesktopRootWindowHostWin::PostNativeEvent( + const base::NativeEvent& native_event) { +} + +void DesktopRootWindowHostWin::OnDeviceScaleFactorChanged( + float device_scale_factor) { +} + +void DesktopRootWindowHostWin::PrepareForShutdown() { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, aura::AnimationHost implementation: + +void DesktopRootWindowHostWin::SetHostTransitionBounds( + const gfx::Rect& bounds) { + gfx::Rect bounds_without_expansion = GetBounds(); + window_expansion_ = bounds; + SetBounds(bounds_without_expansion); +} + +void DesktopRootWindowHostWin::OnWindowHidingAnimationCompleted() { + if (pending_close_) + message_handler_->Close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, HWNDMessageHandlerDelegate implementation: + +bool DesktopRootWindowHostWin::IsWidgetWindow() const { + return has_non_client_view_; +} + +bool DesktopRootWindowHostWin::IsUsingCustomFrame() const { + return !GetWidget()->ShouldUseNativeFrame(); +} + +void DesktopRootWindowHostWin::SchedulePaint() { + GetWidget()->GetRootView()->SchedulePaint(); +} + +void DesktopRootWindowHostWin::EnableInactiveRendering() { + native_widget_delegate_->EnableInactiveRendering(); +} + +bool DesktopRootWindowHostWin::IsInactiveRenderingDisabled() { + return native_widget_delegate_->IsInactiveRenderingDisabled(); +} + +bool DesktopRootWindowHostWin::CanResize() const { + return GetWidget()->widget_delegate()->CanResize(); +} + +bool DesktopRootWindowHostWin::CanMaximize() const { + return GetWidget()->widget_delegate()->CanMaximize(); +} + +bool DesktopRootWindowHostWin::CanActivate() const { + return native_widget_delegate_->CanActivate(); +} + +bool DesktopRootWindowHostWin::WidgetSizeIsClientSize() const { + const Widget* widget = GetWidget()->GetTopLevelWidget(); + return IsMaximized() || (widget && widget->ShouldUseNativeFrame()); +} + +bool DesktopRootWindowHostWin::CanSaveFocus() const { + return GetWidget()->is_top_level(); +} + +void DesktopRootWindowHostWin::SaveFocusOnDeactivate() { + GetWidget()->GetFocusManager()->StoreFocusedView(true); +} + +void DesktopRootWindowHostWin::RestoreFocusOnActivate() { + RestoreFocusOnEnable(); +} + +void DesktopRootWindowHostWin::RestoreFocusOnEnable() { + GetWidget()->GetFocusManager()->RestoreFocusedView(); +} + +bool DesktopRootWindowHostWin::IsModal() const { + return native_widget_delegate_->IsModal(); +} + +int DesktopRootWindowHostWin::GetInitialShowState() const { + return SW_SHOWNORMAL; +} + +bool DesktopRootWindowHostWin::WillProcessWorkAreaChange() const { + return GetWidget()->widget_delegate()->WillProcessWorkAreaChange(); +} + +int DesktopRootWindowHostWin::GetNonClientComponent( + const gfx::Point& point) const { + gfx::Point dip_position = ui::win::ScreenToDIPPoint(point); + return native_widget_delegate_->GetNonClientComponent(dip_position); +} + +void DesktopRootWindowHostWin::GetWindowMask(const gfx::Size& size, + gfx::Path* path) { + if (GetWidget()->non_client_view()) + GetWidget()->non_client_view()->GetWindowMask(size, path); +} + +bool DesktopRootWindowHostWin::GetClientAreaInsets(gfx::Insets* insets) const { + return false; +} + +void DesktopRootWindowHostWin::GetMinMaxSize(gfx::Size* min_size, + gfx::Size* max_size) const { + *min_size = native_widget_delegate_->GetMinimumSize(); + *max_size = native_widget_delegate_->GetMaximumSize(); +} + +gfx::Size DesktopRootWindowHostWin::GetRootViewSize() const { + return GetWidget()->GetRootView()->size(); +} + +void DesktopRootWindowHostWin::ResetWindowControls() { + GetWidget()->non_client_view()->ResetWindowControls(); +} + +void DesktopRootWindowHostWin::PaintLayeredWindow(gfx::Canvas* canvas) { + GetWidget()->GetRootView()->Paint(canvas); +} + +gfx::NativeViewAccessible DesktopRootWindowHostWin::GetNativeViewAccessible() { + return GetWidget()->GetRootView()->GetNativeViewAccessible(); +} + +InputMethod* DesktopRootWindowHostWin::GetInputMethod() { + return GetWidget()->GetInputMethodDirect(); +} + +bool DesktopRootWindowHostWin::ShouldHandleSystemCommands() const { + return GetWidget()->widget_delegate()->ShouldHandleSystemCommands(); +} + +void DesktopRootWindowHostWin::HandleAppDeactivated() { + native_widget_delegate_->EnableInactiveRendering(); +} + +void DesktopRootWindowHostWin::HandleActivationChanged(bool active) { + if (active) + root_window_host_delegate_->OnHostActivated(); + native_widget_delegate_->OnNativeWidgetActivationChanged(active); + // If we're not active we need to deactivate the corresponding aura::Window. + // This way if a child widget is active it gets correctly deactivated (child + // widgets don't get native desktop activation changes, only aura activation + // changes). + if (!active) { + aura::client::ActivationClient* activation_client = + aura::client::GetActivationClient(root_window_); + if (activation_client) { + aura::Window* active_window = activation_client->GetActiveWindow(); + if (active_window) + activation_client->DeactivateWindow(active_window); + } + } +} + +bool DesktopRootWindowHostWin::HandleAppCommand(short command) { + // We treat APPCOMMAND ids as an extension of our command namespace, and just + // let the delegate figure out what to do... + return GetWidget()->widget_delegate() && + GetWidget()->widget_delegate()->ExecuteWindowsCommand(command); +} + +void DesktopRootWindowHostWin::HandleCancelMode() { + root_window_host_delegate_->OnHostCancelMode(); +} + +void DesktopRootWindowHostWin::HandleCaptureLost() { + root_window_host_delegate_->OnHostLostWindowCapture(); + native_widget_delegate_->OnMouseCaptureLost(); +} + +void DesktopRootWindowHostWin::HandleClose() { + GetWidget()->Close(); +} + +bool DesktopRootWindowHostWin::HandleCommand(int command) { + return GetWidget()->widget_delegate()->ExecuteWindowsCommand(command); +} + +void DesktopRootWindowHostWin::HandleAccelerator( + const ui::Accelerator& accelerator) { + GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator); +} + +void DesktopRootWindowHostWin::HandleCreate() { + // TODO(beng): moar + NOTIMPLEMENTED(); + + native_widget_delegate_->OnNativeWidgetCreated(true); + + // 1. Window property association + // 2. MouseWheel. +} + +void DesktopRootWindowHostWin::HandleDestroying() { + drag_drop_client_->OnNativeWidgetDestroying(GetHWND()); + native_widget_delegate_->OnNativeWidgetDestroying(); +} + +void DesktopRootWindowHostWin::HandleDestroyed() { + desktop_native_widget_aura_->OnHostClosed(); +} + +bool DesktopRootWindowHostWin::HandleInitialFocus() { + return GetWidget()->SetInitialFocus(); +} + +void DesktopRootWindowHostWin::HandleDisplayChange() { + GetWidget()->widget_delegate()->OnDisplayChanged(); +} + +void DesktopRootWindowHostWin::HandleBeginWMSizeMove() { + native_widget_delegate_->OnNativeWidgetBeginUserBoundsChange(); +} + +void DesktopRootWindowHostWin::HandleEndWMSizeMove() { + native_widget_delegate_->OnNativeWidgetEndUserBoundsChange(); +} + +void DesktopRootWindowHostWin::HandleMove() { + native_widget_delegate_->OnNativeWidgetMove(); + if (root_window_host_delegate_) + root_window_host_delegate_->OnHostMoved(GetBounds().origin()); +} + +void DesktopRootWindowHostWin::HandleWorkAreaChanged() { + GetWidget()->widget_delegate()->OnWorkAreaChanged(); +} + +void DesktopRootWindowHostWin::HandleVisibilityChanged(bool visible) { + native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); +} + +void DesktopRootWindowHostWin::HandleClientSizeChanged( + const gfx::Size& new_size) { + gfx::Size without_expansion(new_size.width() - window_expansion_.width(), + new_size.height() - window_expansion_.height()); + if (root_window_host_delegate_) + root_window_host_delegate_->OnHostResized(new_size); + // TODO(beng): replace with a layout manager?? + gfx::Size dip_size = ui::win::ScreenToDIPSize(without_expansion); + content_window_->SetBounds(gfx::Rect(dip_size)); + native_widget_delegate_->OnNativeWidgetSizeChanged(dip_size); +} + +void DesktopRootWindowHostWin::HandleFrameChanged() { + // Replace the frame and layout the contents. + GetWidget()->non_client_view()->UpdateFrame(true); +} + +void DesktopRootWindowHostWin::HandleNativeFocus(HWND last_focused_window) { + // TODO(beng): inform the native_widget_delegate_. + InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->OnFocus(); +} + +void DesktopRootWindowHostWin::HandleNativeBlur(HWND focused_window) { + // TODO(beng): inform the native_widget_delegate_. + InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->OnBlur(); +} + +bool DesktopRootWindowHostWin::HandleMouseEvent(const ui::MouseEvent& event) { + if (base::win::IsTSFAwareRequired() && event.IsAnyButton()) + ui::TSFBridge::GetInstance()->CancelComposition(); + return root_window_host_delegate_->OnHostMouseEvent( + const_cast<ui::MouseEvent*>(&event)); +} + +bool DesktopRootWindowHostWin::HandleKeyEvent(const ui::KeyEvent& event) { + return false; +} + +bool DesktopRootWindowHostWin::HandleUntranslatedKeyEvent( + const ui::KeyEvent& event) { + scoped_ptr<ui::KeyEvent> duplicate_event(event.Copy()); + return static_cast<aura::RootWindowHostDelegate*>(root_window_)-> + OnHostKeyEvent(duplicate_event.get()); +} + +bool DesktopRootWindowHostWin::HandleTouchEvent( + const ui::TouchEvent& event) { + return root_window_host_delegate_->OnHostTouchEvent( + const_cast<ui::TouchEvent*>(&event)); +} + +bool DesktopRootWindowHostWin::HandleIMEMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + MSG msg = {}; + msg.hwnd = GetHWND(); + msg.message = message; + msg.wParam = w_param; + msg.lParam = l_param; + return desktop_native_widget_aura_->input_method_event_filter()-> + input_method()->OnUntranslatedIMEMessage(msg, result); +} + +void DesktopRootWindowHostWin::HandleInputLanguageChange( + DWORD character_set, + HKL input_language_id) { + desktop_native_widget_aura_->input_method_event_filter()-> + input_method()->OnInputLocaleChanged(); +} + +bool DesktopRootWindowHostWin::HandlePaintAccelerated( + const gfx::Rect& invalid_rect) { + return native_widget_delegate_->OnNativeWidgetPaintAccelerated(invalid_rect); +} + +void DesktopRootWindowHostWin::HandlePaint(gfx::Canvas* canvas) { + root_window_host_delegate_->OnHostPaint(gfx::Rect()); +} + +bool DesktopRootWindowHostWin::HandleTooltipNotify(int w_param, + NMHDR* l_param, + LRESULT* l_result) { + return false; +} + +void DesktopRootWindowHostWin::HandleTooltipMouseMove(UINT message, + WPARAM w_param, + LPARAM l_param) { +} + +bool DesktopRootWindowHostWin::PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + return false; +} + +void DesktopRootWindowHostWin::PostHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param) { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostWin, private: + +Widget* DesktopRootWindowHostWin::GetWidget() { + return native_widget_delegate_->AsWidget(); +} + +const Widget* DesktopRootWindowHostWin::GetWidget() const { + return native_widget_delegate_->AsWidget(); +} + +HWND DesktopRootWindowHostWin::GetHWND() const { + return message_handler_->hwnd(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHost, public: + +// static +DesktopRootWindowHost* DesktopRootWindowHost::Create( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds) { + return new DesktopRootWindowHostWin(native_widget_delegate, + desktop_native_widget_aura, + initial_bounds); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.h new file mode 100644 index 00000000000..13f043d81c2 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.h @@ -0,0 +1,266 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_WIN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_WIN_H_ + +#include "ui/aura/client/animation_host.h" +#include "ui/aura/root_window_host.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/win/hwnd_message_handler_delegate.h" + +namespace aura { +namespace client { +class FocusClient; +class ScreenPositionClient; +} +} + +namespace views { +class DesktopActivationClient; +class DesktopCaptureClient; +class DesktopCursorClient; +class DesktopDispatcherClient; +class DesktopDragDropClientWin; +class HWNDMessageHandler; + +namespace corewm { +class CursorManager; +} + +class VIEWS_EXPORT DesktopRootWindowHostWin + : public DesktopRootWindowHost, + public aura::client::AnimationHost, + public aura::RootWindowHost, + public HWNDMessageHandlerDelegate { + public: + DesktopRootWindowHostWin( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds); + virtual ~DesktopRootWindowHostWin(); + + // A way of converting an HWND into a content window. + static aura::Window* GetContentWindowForHWND(HWND hwnd); + + protected: + // Overridden from DesktopRootWindowHost: + virtual aura::RootWindow* Init(aura::Window* content_window, + const Widget::InitParams& params) OVERRIDE; + virtual void InitFocus(aura::Window* window) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void CloseNow() OVERRIDE; + virtual aura::RootWindowHost* AsRootWindowHost() OVERRIDE; + virtual void ShowWindowWithState(ui::WindowShowState show_state) OVERRIDE; + virtual void ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) OVERRIDE; + virtual bool IsVisible() const OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const OVERRIDE; + virtual gfx::Rect GetWindowBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetClientAreaBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetRestoredBounds() const OVERRIDE; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE; + virtual void SetShape(gfx::NativeRegion native_region) OVERRIDE; + virtual void Activate() OVERRIDE; + virtual void Deactivate() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual void Restore() OVERRIDE; + virtual bool IsMaximized() const OVERRIDE; + virtual bool IsMinimized() const OVERRIDE; + virtual bool HasCapture() const OVERRIDE; + virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; + virtual void SetWindowTitle(const string16& title) OVERRIDE; + virtual void ClearNativeFocus() OVERRIDE; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual bool ShouldUseNativeFrame() OVERRIDE; + virtual void FrameTypeChanged() OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; + virtual void SetOpacity(unsigned char opacity) OVERRIDE; + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) OVERRIDE; + virtual void InitModalType(ui::ModalType modal_type) OVERRIDE; + virtual void FlashFrame(bool flash_frame) OVERRIDE; + virtual void OnNativeWidgetFocus() OVERRIDE; + virtual void OnNativeWidgetBlur() OVERRIDE; + virtual void SetInactiveRenderingDisabled(bool disable_inactive) OVERRIDE; + + // Overridden from aura::RootWindowHost: + virtual void SetDelegate(aura::RootWindowHostDelegate* delegate) OVERRIDE; + virtual aura::RootWindow* GetRootWindow() OVERRIDE; + virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void ToggleFullScreen() OVERRIDE; + virtual gfx::Rect GetBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual gfx::Insets GetInsets() const OVERRIDE; + virtual void SetInsets(const gfx::Insets& insets) OVERRIDE; + virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; + virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE; + virtual bool ConfineCursorToRootWindow() OVERRIDE; + virtual void UnConfineCursor() OVERRIDE; + virtual void OnCursorVisibilityChanged(bool show) OVERRIDE; + virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE; + virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE; + virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void PrepareForShutdown() OVERRIDE; + + // Overridden from aura::client::AnimationHost + virtual void SetHostTransitionBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void OnWindowHidingAnimationCompleted() OVERRIDE; + + // Overridden from HWNDMessageHandlerDelegate: + virtual bool IsWidgetWindow() const OVERRIDE; + virtual bool IsUsingCustomFrame() const OVERRIDE; + virtual void SchedulePaint() OVERRIDE; + virtual void EnableInactiveRendering() OVERRIDE; + virtual bool IsInactiveRenderingDisabled() OVERRIDE; + virtual bool CanResize() const OVERRIDE; + virtual bool CanMaximize() const OVERRIDE; + virtual bool CanActivate() const OVERRIDE; + virtual bool WidgetSizeIsClientSize() const OVERRIDE; + virtual bool CanSaveFocus() const OVERRIDE; + virtual void SaveFocusOnDeactivate() OVERRIDE; + virtual void RestoreFocusOnActivate() OVERRIDE; + virtual void RestoreFocusOnEnable() OVERRIDE; + virtual bool IsModal() const OVERRIDE; + virtual int GetInitialShowState() const OVERRIDE; + virtual bool WillProcessWorkAreaChange() const OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE; + virtual void GetWindowMask(const gfx::Size& size, gfx::Path* path) OVERRIDE; + virtual bool GetClientAreaInsets(gfx::Insets* insets) const OVERRIDE; + virtual void GetMinMaxSize(gfx::Size* min_size, + gfx::Size* max_size) const OVERRIDE; + virtual gfx::Size GetRootViewSize() const OVERRIDE; + virtual void ResetWindowControls() OVERRIDE; + virtual void PaintLayeredWindow(gfx::Canvas* canvas) OVERRIDE; + virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE; + virtual bool ShouldHandleSystemCommands() const OVERRIDE; + virtual InputMethod* GetInputMethod() OVERRIDE; + virtual void HandleAppDeactivated() OVERRIDE; + virtual void HandleActivationChanged(bool active) OVERRIDE; + virtual bool HandleAppCommand(short command) OVERRIDE; + virtual void HandleCancelMode() OVERRIDE; + virtual void HandleCaptureLost() OVERRIDE; + virtual void HandleClose() OVERRIDE; + virtual bool HandleCommand(int command) OVERRIDE; + virtual void HandleAccelerator(const ui::Accelerator& accelerator) OVERRIDE; + virtual void HandleCreate() OVERRIDE; + virtual void HandleDestroying() OVERRIDE; + virtual void HandleDestroyed() OVERRIDE; + virtual bool HandleInitialFocus() OVERRIDE; + virtual void HandleDisplayChange() OVERRIDE; + virtual void HandleBeginWMSizeMove() OVERRIDE; + virtual void HandleEndWMSizeMove() OVERRIDE; + virtual void HandleMove() OVERRIDE; + virtual void HandleWorkAreaChanged() OVERRIDE; + virtual void HandleVisibilityChanged(bool visible) OVERRIDE; + virtual void HandleClientSizeChanged(const gfx::Size& new_size) OVERRIDE; + virtual void HandleFrameChanged() OVERRIDE; + virtual void HandleNativeFocus(HWND last_focused_window) OVERRIDE; + virtual void HandleNativeBlur(HWND focused_window) OVERRIDE; + virtual bool HandleMouseEvent(const ui::MouseEvent& event) OVERRIDE; + virtual bool HandleKeyEvent(const ui::KeyEvent& event) OVERRIDE; + virtual bool HandleUntranslatedKeyEvent(const ui::KeyEvent& event) OVERRIDE; + virtual bool HandleTouchEvent(const ui::TouchEvent& event) OVERRIDE; + virtual bool HandleIMEMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) OVERRIDE; + virtual void HandleInputLanguageChange(DWORD character_set, + HKL input_language_id) OVERRIDE; + virtual bool HandlePaintAccelerated(const gfx::Rect& invalid_rect) OVERRIDE; + virtual void HandlePaint(gfx::Canvas* canvas) OVERRIDE; + virtual bool HandleTooltipNotify(int w_param, + NMHDR* l_param, + LRESULT* l_result) OVERRIDE; + virtual void HandleTooltipMouseMove(UINT message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + virtual bool PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) OVERRIDE; + virtual void PostHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + + Widget* GetWidget(); + const Widget* GetWidget() const; + HWND GetHWND() const; + + private: + // We are owned by the RootWindow, but we have to have a back pointer to it. + aura::RootWindow* root_window_; + + scoped_ptr<HWNDMessageHandler> message_handler_; + scoped_ptr<DesktopCaptureClient> capture_client_; + scoped_ptr<DesktopDispatcherClient> dispatcher_client_; + scoped_ptr<aura::client::FocusClient> focus_client_; + // Depends on focus_manager_. + scoped_ptr<DesktopActivationClient> activation_client_; + + // TODO(beng): Consider providing an interface to DesktopNativeWidgetAura + // instead of providing this route back to Widget. + internal::NativeWidgetDelegate* native_widget_delegate_; + + DesktopNativeWidgetAura* desktop_native_widget_aura_; + + aura::RootWindowHostDelegate* root_window_host_delegate_; + aura::Window* content_window_; + + // In some cases, we set a screen position client on |root_window_|. If we + // do, we're responsible for the lifetime. + scoped_ptr<aura::client::ScreenPositionClient> position_client_; + + // Controls visibility of the cursor. + scoped_ptr<views::corewm::CursorManager> cursor_client_; + + scoped_ptr<DesktopDragDropClientWin> drag_drop_client_; + + // Extra size added to the host window. Typically, the window size matches + // the contained content, however, when performing a translating or scaling + // animation the window has to be enlarged so that the content is not + // clipped. + gfx::Rect window_expansion_; + + // Whether the window close should be converted to a hide, and then actually + // closed on the completion of the hide animation. This is cached because + // the property is set on the contained window which has a shorter lifetime. + bool should_animate_window_close_; + + // When Close()d and animations are being applied to this window, the close + // of the window needs to be deferred to when the close animation is + // completed. This variable indicates that a Close was converted to a Hide, + // so that when the Hide is completed the host window should be closed. + bool pending_close_; + + // True if the widget is going to have a non_client_view. We cache this value + // rather than asking the Widget for the non_client_view so that we know at + // Init time, before the Widget has created the NonClientView. + bool has_non_client_view_; + + DISALLOW_COPY_AND_ASSIGN(DesktopRootWindowHostWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_WIN_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc new file mode 100644 index 00000000000..67610684875 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc @@ -0,0 +1,1351 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" + +#include <X11/extensions/shape.h> +#include <X11/extensions/XInput2.h> +#include <X11/Xatom.h> +#include <X11/Xregion.h> +#include <X11/Xutil.h> + +#include "base/message_loop/message_pump_aurax11.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkPath.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/client/user_action_client.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window_property.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" +#include "ui/base/events/event_utils.h" +#include "ui/base/touch/touch_factory_x11.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/insets.h" +#include "ui/gfx/path_x11.h" +#include "ui/linux_ui/linux_ui.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/corewm/compound_event_filter.h" +#include "ui/views/corewm/corewm_switches.h" +#include "ui/views/corewm/cursor_manager.h" +#include "ui/views/corewm/focus_controller.h" +#include "ui/views/ime/input_method.h" +#include "ui/views/widget/desktop_aura/desktop_activation_client.h" +#include "ui/views/widget/desktop_aura/desktop_capture_client.h" +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h" +#include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" +#include "ui/views/widget/desktop_aura/desktop_focus_rules.h" +#include "ui/views/widget/desktop_aura/desktop_layout_manager.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h" +#include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" +#include "ui/views/widget/desktop_aura/x11_desktop_handler.h" +#include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h" +#include "ui/views/widget/desktop_aura/x11_window_event_filter.h" + +namespace views { + +DesktopRootWindowHostX11* DesktopRootWindowHostX11::g_current_capture = + NULL; +std::list<XID>* DesktopRootWindowHostX11::open_windows_ = NULL; + +DEFINE_WINDOW_PROPERTY_KEY( + aura::Window*, kViewsWindowForRootWindow, NULL); + +DEFINE_WINDOW_PROPERTY_KEY( + DesktopRootWindowHostX11*, kHostForRootWindow, NULL); + +namespace { + +// Standard Linux mouse buttons for going back and forward. +const int kBackMouseButton = 8; +const int kForwardMouseButton = 9; + +// Constants that are part of EWMH. +const int k_NET_WM_STATE_ADD = 1; +const int k_NET_WM_STATE_REMOVE = 0; + +const char* kAtomsToCache[] = { + "WM_DELETE_WINDOW", + "WM_PROTOCOLS", + "WM_S0", + "_NET_WM_PID", + "_NET_WM_PING", + "_NET_WM_STATE", + "_NET_WM_STATE_ABOVE", + "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_HIDDEN", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_WINDOW_OPACITY", + "_NET_WM_WINDOW_TYPE", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_NORMAL", + "_NET_WM_WINDOW_TYPE_TOOLTIP", + "XdndActionAsk", + "XdndActionCopy" + "XdndActionLink", + "XdndActionList", + "XdndActionMove", + "XdndActionPrivate", + "XdndAware", + "XdndDrop", + "XdndEnter", + "XdndFinished", + "XdndLeave", + "XdndPosition", + "XdndProxy", // Proxy windows? + "XdndSelection", + "XdndStatus", + "XdndTypeList", + NULL +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostX11, public: + +DesktopRootWindowHostX11::DesktopRootWindowHostX11( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds) + : close_widget_factory_(this), + xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), + xwindow_(0), + x_root_window_(DefaultRootWindow(xdisplay_)), + atom_cache_(xdisplay_, kAtomsToCache), + window_mapped_(false), + focus_when_shown_(false), + current_cursor_(ui::kCursorNull), + native_widget_delegate_(native_widget_delegate), + desktop_native_widget_aura_(desktop_native_widget_aura) { +} + +DesktopRootWindowHostX11::~DesktopRootWindowHostX11() { + root_window_->ClearProperty(kHostForRootWindow); + if (corewm::UseFocusControllerOnDesktop()) { + aura::client::SetFocusClient(root_window_, NULL); + aura::client::SetActivationClient(root_window_, NULL); + } +} + +// static +aura::Window* DesktopRootWindowHostX11::GetContentWindowForXID(XID xid) { + aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); + return root ? root->GetProperty(kViewsWindowForRootWindow) : NULL; +} + +// static +DesktopRootWindowHostX11* DesktopRootWindowHostX11::GetHostForXID(XID xid) { + aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); + return root ? root->GetProperty(kHostForRootWindow) : NULL; +} + +// static +std::vector<aura::Window*> DesktopRootWindowHostX11::GetAllOpenWindows() { + std::vector<aura::Window*> windows(open_windows().size()); + std::transform(open_windows().begin(), + open_windows().end(), + windows.begin(), + GetContentWindowForXID); + return windows; +} + +void DesktopRootWindowHostX11::HandleNativeWidgetActivationChanged( + bool active) { + if (active) + root_window_host_delegate_->OnHostActivated(); + native_widget_delegate_->OnNativeWidgetActivationChanged(active); + // If we're not active we need to deactivate the corresponding aura::Window. + // This way if a child widget is active it gets correctly deactivated (child + // widgets don't get native desktop activation changes, only aura activation + // changes). + if (!active) { + aura::client::ActivationClient* activation_client = + aura::client::GetActivationClient(root_window_); + if (activation_client) { + aura::Window* active_window = activation_client->GetActiveWindow(); + if (active_window) + activation_client->DeactivateWindow(active_window); + } + } + + native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); +} + +void DesktopRootWindowHostX11::AddObserver( + views::DesktopRootWindowHostObserverX11* observer) { + observer_list_.AddObserver(observer); +} + +void DesktopRootWindowHostX11::RemoveObserver( + views::DesktopRootWindowHostObserverX11* observer) { + observer_list_.RemoveObserver(observer); +} + +void DesktopRootWindowHostX11::CleanUpWindowList() { + delete open_windows_; + open_windows_ = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostX11, DesktopRootWindowHost implementation: + +aura::RootWindow* DesktopRootWindowHostX11::Init( + aura::Window* content_window, + const Widget::InitParams& params) { + content_window_ = content_window; + + // TODO(erg): Check whether we *should* be building a RootWindowHost here, or + // whether we should be proxying requests to another DRWHL. + + // In some situations, views tries to make a zero sized window, and that + // makes us crash. Make sure we have valid sizes. + Widget::InitParams sanitized_params = params; + if (sanitized_params.bounds.width() == 0) + sanitized_params.bounds.set_width(100); + if (sanitized_params.bounds.height() == 0) + sanitized_params.bounds.set_height(100); + + InitX11Window(sanitized_params); + return InitRootWindow(sanitized_params); +} + +void DesktopRootWindowHostX11::InitFocus(aura::Window* window) { +} + +void DesktopRootWindowHostX11::Close() { + // TODO(erg): Might need to do additional hiding tasks here. + + if (!close_widget_factory_.HasWeakPtrs()) { + // And we delay the close so that if we are called from an ATL callback, + // we don't destroy the window before the callback returned (as the caller + // may delete ourselves on destroy and the ATL callback would still + // dereference us when the callback returns). + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DesktopRootWindowHostX11::CloseNow, + close_widget_factory_.GetWeakPtr())); + } +} + +void DesktopRootWindowHostX11::CloseNow() { + if (xwindow_ == None) + return; + + native_widget_delegate_->OnNativeWidgetDestroying(); + + // Remove the event listeners we've installed. We need to remove these + // because otherwise we get assert during ~RootWindow(). + desktop_native_widget_aura_->root_window_event_filter()->RemoveHandler( + x11_window_event_filter_.get()); + + open_windows().remove(xwindow_); + // Actually free our native resources. + base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(xwindow_); + XDestroyWindow(xdisplay_, xwindow_); + xwindow_ = None; + + desktop_native_widget_aura_->OnHostClosed(); +} + +aura::RootWindowHost* DesktopRootWindowHostX11::AsRootWindowHost() { + return this; +} + +void DesktopRootWindowHostX11::ShowWindowWithState( + ui::WindowShowState show_state) { + if (show_state != ui::SHOW_STATE_DEFAULT && + show_state != ui::SHOW_STATE_NORMAL) { + // Only forwarding to Show(). + NOTIMPLEMENTED(); + } + + Show(); +} + +void DesktopRootWindowHostX11::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + restored_bounds_ = restored_bounds; + Maximize(); + Show(); +} + +bool DesktopRootWindowHostX11::IsVisible() const { + return window_mapped_; +} + +void DesktopRootWindowHostX11::SetSize(const gfx::Size& size) { + // TODO(erg): + NOTIMPLEMENTED(); +} + +void DesktopRootWindowHostX11::CenterWindow(const gfx::Size& size) { + gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen(); + + // If |window_|'s transient parent bounds are big enough to contain |size|, + // use them instead. + if (content_window_->transient_parent()) { + gfx::Rect transient_parent_rect = + content_window_->transient_parent()->GetBoundsInScreen(); + if (transient_parent_rect.height() >= size.height() && + transient_parent_rect.width() >= size.width()) { + parent_bounds = transient_parent_rect; + } + } + + gfx::Rect window_bounds( + parent_bounds.x() + (parent_bounds.width() - size.width()) / 2, + parent_bounds.y() + (parent_bounds.height() - size.height()) / 2, + size.width(), + size.height()); + // Don't size the window bigger than the parent, otherwise the user may not be + // able to close or move it. + window_bounds.AdjustToFit(parent_bounds); + + SetBounds(window_bounds); +} + +void DesktopRootWindowHostX11::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + *bounds = bounds_; + + // TODO(erg): This needs a better implementation. For now, we're just pass + // back the normal state until we keep track of this. + *show_state = ui::SHOW_STATE_NORMAL; +} + +gfx::Rect DesktopRootWindowHostX11::GetWindowBoundsInScreen() const { + return bounds_; +} + +gfx::Rect DesktopRootWindowHostX11::GetClientAreaBoundsInScreen() const { + // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its + // needed for View::ConvertPointToScreen() to work + // correctly. DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() just + // asks windows what it thinks the client rect is. + // + // Attempts to calculate the rect by asking the NonClientFrameView what it + // thought its GetBoundsForClientView() were broke combobox drop down + // placement. + return bounds_; +} + +gfx::Rect DesktopRootWindowHostX11::GetRestoredBounds() const { + // We can't reliably track the restored bounds of a window, but we can get + // the 90% case down. When *chrome* is the process that requests maximizing + // or restoring bounds, we can record the current bounds before we request + // maximization, and clear it when we detect a state change. + if (!restored_bounds_.IsEmpty()) + return restored_bounds_; + + return GetWindowBoundsInScreen(); +} + +gfx::Rect DesktopRootWindowHostX11::GetWorkAreaBoundsInScreen() const { + std::vector<int> value; + if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && + value.size() >= 4) { + return gfx::Rect(value[0], value[1], value[2], value[3]); + } + + // Fetch the geometry of the root window. + Window root; + int x, y; + unsigned int width, height; + unsigned int border_width, depth; + if (!XGetGeometry(xdisplay_, x_root_window_, &root, &x, &y, + &width, &height, &border_width, &depth)) { + NOTIMPLEMENTED(); + return gfx::Rect(0, 0, 10, 10); + } + + return gfx::Rect(x, y, width, height); +} + +void DesktopRootWindowHostX11::SetShape(gfx::NativeRegion native_region) { + SkPath path; + native_region->getBoundaryPath(&path); + Region region = gfx::CreateRegionFromSkPath(path); + XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding, 0, 0, region, false); + XDestroyRegion(region); +} + +void DesktopRootWindowHostX11::Activate() { + X11DesktopHandler::get()->ActivateWindow(xwindow_); +} + +void DesktopRootWindowHostX11::Deactivate() { + // Deactivating a window means activating nothing. + X11DesktopHandler::get()->ActivateWindow(None); +} + +bool DesktopRootWindowHostX11::IsActive() const { + return X11DesktopHandler::get()->IsActiveWindow(xwindow_); +} + +void DesktopRootWindowHostX11::Maximize() { + // When we're the process requesting the maximizing, we can accurately keep + // track of our restored bounds instead of relying on the heuristics that are + // in the PropertyNotify and ConfigureNotify handlers. + restored_bounds_ = bounds_; + + SetWMSpecState(true, + atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), + atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); +} + +void DesktopRootWindowHostX11::Minimize() { + XIconifyWindow(xdisplay_, xwindow_, 0); +} + +void DesktopRootWindowHostX11::Restore() { + SetWMSpecState(false, + atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), + atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); +} + +bool DesktopRootWindowHostX11::IsMaximized() const { + return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") || + HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ")); +} + +bool DesktopRootWindowHostX11::IsMinimized() const { + return HasWMSpecProperty("_NET_WM_STATE_HIDDEN"); +} + + +bool DesktopRootWindowHostX11::HasCapture() const { + return g_current_capture == this; +} + +void DesktopRootWindowHostX11::SetAlwaysOnTop(bool always_on_top) { + SetWMSpecState(always_on_top, + atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"), + None); +} + +void DesktopRootWindowHostX11::SetWindowTitle(const string16& title) { + XStoreName(xdisplay_, xwindow_, UTF16ToUTF8(title).c_str()); +} + +void DesktopRootWindowHostX11::ClearNativeFocus() { + // This method is weird and misnamed. Instead of clearing the native focus, + // it sets the focus to our |content_window_|, which will trigger a cascade + // of focus changes into views. + if (content_window_ && aura::client::GetFocusClient(content_window_) && + content_window_->Contains( + aura::client::GetFocusClient(content_window_)->GetFocusedWindow())) { + aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_); + } +} + +Widget::MoveLoopResult DesktopRootWindowHostX11::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) { + SetCapture(); + + aura::client::WindowMoveSource window_move_source = + source == Widget::MOVE_LOOP_SOURCE_MOUSE ? + aura::client::WINDOW_MOVE_SOURCE_MOUSE : + aura::client::WINDOW_MOVE_SOURCE_TOUCH; + if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset, + window_move_source) == aura::client::MOVE_SUCCESSFUL) + return Widget::MOVE_LOOP_SUCCESSFUL; + + return Widget::MOVE_LOOP_CANCELED; +} + +void DesktopRootWindowHostX11::EndMoveLoop() { + x11_window_move_client_->EndMoveLoop(); +} + +void DesktopRootWindowHostX11::SetVisibilityChangedAnimationsEnabled( + bool value) { + // Much like the previous NativeWidgetGtk, we don't have anything to do here. +} + +bool DesktopRootWindowHostX11::ShouldUseNativeFrame() { + return false; +} + +void DesktopRootWindowHostX11::FrameTypeChanged() { + // Replace the frame and layout the contents. Even though we don't have a + // swapable glass frame like on Windows, we still replace the frame because + // the button assets don't update otherwise. + native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame(true); +} + +NonClientFrameView* DesktopRootWindowHostX11::CreateNonClientFrameView() { + return NULL; +} + +void DesktopRootWindowHostX11::SetFullscreen(bool fullscreen) { + SetWMSpecState(fullscreen, + atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"), + None); +} + +bool DesktopRootWindowHostX11::IsFullscreen() const { + return HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN"); +} + +void DesktopRootWindowHostX11::SetOpacity(unsigned char opacity) { + // X server opacity is in terms of 32 bit unsigned int space, and counts from + // the opposite direction. + unsigned int cardinality = opacity * 0x1010101; + + if (cardinality == 0xffffffff) { + XDeleteProperty(xdisplay_, xwindow_, + atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY")); + } else { + XChangeProperty(xdisplay_, xwindow_, + atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"), + XA_CARDINAL, 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&cardinality), 1); + } +} + +void DesktopRootWindowHostX11::SetWindowIcons( + const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { + // TODO(erg): + NOTIMPLEMENTED(); +} + +void DesktopRootWindowHostX11::InitModalType(ui::ModalType modal_type) { + // TODO(erg): + NOTIMPLEMENTED(); +} + +void DesktopRootWindowHostX11::FlashFrame(bool flash_frame) { + // TODO(erg): + NOTIMPLEMENTED(); +} + +void DesktopRootWindowHostX11::OnNativeWidgetFocus() { + native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus(); +} + +void DesktopRootWindowHostX11::OnNativeWidgetBlur() { + if (xwindow_) + native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur(); +} + +void DesktopRootWindowHostX11::SetInactiveRenderingDisabled( + bool disable_inactive) { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostX11, aura::RootWindowHost implementation: + +void DesktopRootWindowHostX11::SetDelegate( + aura::RootWindowHostDelegate* delegate) { + root_window_host_delegate_ = delegate; +} + +aura::RootWindow* DesktopRootWindowHostX11::GetRootWindow() { + return root_window_; +} + +gfx::AcceleratedWidget DesktopRootWindowHostX11::GetAcceleratedWidget() { + return xwindow_; +} + +void DesktopRootWindowHostX11::Show() { + if (!window_mapped_) { + // Before we map the window, set size hints. Otherwise, some window managers + // will ignore toplevel XMoveWindow commands. + XSizeHints size_hints; + size_hints.flags = PPosition; + size_hints.x = bounds_.x(); + size_hints.y = bounds_.y(); + XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); + + XMapWindow(xdisplay_, xwindow_); + + // We now block until our window is mapped. Some X11 APIs will crash and + // burn if passed |xwindow_| before the window is mapped, and XMapWindow is + // asynchronous. + base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped(xwindow_); + window_mapped_ = true; + } +} + +void DesktopRootWindowHostX11::Hide() { + if (window_mapped_) { + XWithdrawWindow(xdisplay_, xwindow_, 0); + window_mapped_ = false; + } +} + +void DesktopRootWindowHostX11::ToggleFullScreen() { + NOTIMPLEMENTED(); +} + +gfx::Rect DesktopRootWindowHostX11::GetBounds() const { + return bounds_; +} + +void DesktopRootWindowHostX11::SetBounds(const gfx::Rect& bounds) { + bool origin_changed = bounds_.origin() != bounds.origin(); + bool size_changed = bounds_.size() != bounds.size(); + XWindowChanges changes = {0}; + unsigned value_mask = 0; + + if (size_changed) { + // X11 will send an XError at our process if have a 0 sized window. + DCHECK_GT(bounds.width(), 0); + DCHECK_GT(bounds.height(), 0); + + changes.width = bounds.width(); + changes.height = bounds.height(); + value_mask |= CWHeight | CWWidth; + } + + if (origin_changed) { + changes.x = bounds.x(); + changes.y = bounds.y(); + value_mask |= CWX | CWY; + } + if (value_mask) + XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); + + // Assume that the resize will go through as requested, which should be the + // case if we're running without a window manager. If there's a window + // manager, it can modify or ignore the request, but (per ICCCM) we'll get a + // (possibly synthetic) ConfigureNotify about the actual size and correct + // |bounds_| later. + bounds_ = bounds; + + if (origin_changed) + native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); + if (size_changed) + root_window_host_delegate_->OnHostResized(bounds.size()); + else + root_window_host_delegate_->OnHostPaint(gfx::Rect(bounds.size())); +} + +gfx::Insets DesktopRootWindowHostX11::GetInsets() const { + return gfx::Insets(); +} + +void DesktopRootWindowHostX11::SetInsets(const gfx::Insets& insets) { +} + +gfx::Point DesktopRootWindowHostX11::GetLocationOnNativeScreen() const { + return bounds_.origin(); +} + +void DesktopRootWindowHostX11::SetCapture() { + // This is vaguely based on the old NativeWidgetGtk implementation. + // + // X11's XPointerGrab() shouldn't be used for everything; it doesn't map + // cleanly to Windows' SetCapture(). GTK only provides a separate concept of + // a grab that wasn't the X11 pointer grab, but was instead a manual + // redirection of the event. (You need to drop into GDK if you want to + // perform a raw X11 grab). + + if (g_current_capture) + g_current_capture->OnCaptureReleased(); + + g_current_capture = this; + + // TODO(erg): In addition to the above, NativeWidgetGtk performs a full X + // pointer grab when our NativeWidget is of type Menu. However, things work + // without it. Clicking inside a chrome window causes a release capture, and + // clicking outside causes an activation change. Since previous attempts at + // using XPointerGrab() to implement this have locked my X server, I'm going + // to skip this for now. +} + +void DesktopRootWindowHostX11::ReleaseCapture() { + if (g_current_capture) + g_current_capture->OnCaptureReleased(); +} + +void DesktopRootWindowHostX11::SetCursor(gfx::NativeCursor cursor) { + XDefineCursor(xdisplay_, xwindow_, cursor.platform()); +} + +bool DesktopRootWindowHostX11::QueryMouseLocation( + gfx::Point* location_return) { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(GetRootWindow()); + if (cursor_client && !cursor_client->IsMouseEventsEnabled()) { + *location_return = gfx::Point(0, 0); + return false; + } + + ::Window root_return, child_return; + int root_x_return, root_y_return, win_x_return, win_y_return; + unsigned int mask_return; + XQueryPointer(xdisplay_, + xwindow_, + &root_return, + &child_return, + &root_x_return, &root_y_return, + &win_x_return, &win_y_return, + &mask_return); + *location_return = gfx::Point( + std::max(0, std::min(bounds_.width(), win_x_return)), + std::max(0, std::min(bounds_.height(), win_y_return))); + return (win_x_return >= 0 && win_x_return < bounds_.width() && + win_y_return >= 0 && win_y_return < bounds_.height()); +} + +bool DesktopRootWindowHostX11::ConfineCursorToRootWindow() { + NOTIMPLEMENTED(); + return false; +} + +void DesktopRootWindowHostX11::UnConfineCursor() { + NOTIMPLEMENTED(); +} + +void DesktopRootWindowHostX11::OnCursorVisibilityChanged(bool show) { + // TODO(erg): Conditional on us enabling touch on desktop linux builds, do + // the same tap-to-click disabling here that chromeos does. +} + +void DesktopRootWindowHostX11::MoveCursorTo(const gfx::Point& location) { + XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0, + bounds_.x() + location.x(), bounds_.y() + location.y()); +} + +void DesktopRootWindowHostX11::SetFocusWhenShown(bool focus_when_shown) { + static const char* k_NET_WM_USER_TIME = "_NET_WM_USER_TIME"; + focus_when_shown_ = focus_when_shown; + if (IsWindowManagerPresent() && !focus_when_shown_) { + ui::SetIntProperty(xwindow_, + k_NET_WM_USER_TIME, + k_NET_WM_USER_TIME, + 0); + } +} + +void DesktopRootWindowHostX11::PostNativeEvent( + const base::NativeEvent& native_event) { + DCHECK(xwindow_); + DCHECK(xdisplay_); + XEvent xevent = *native_event; + xevent.xany.display = xdisplay_; + xevent.xany.window = xwindow_; + + switch (xevent.type) { + case EnterNotify: + case LeaveNotify: + case MotionNotify: + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: { + // The fields used below are in the same place for all of events + // above. Using xmotion from XEvent's unions to avoid repeating + // the code. + xevent.xmotion.root = x_root_window_; + xevent.xmotion.time = CurrentTime; + + gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); + root_window_->ConvertPointToNativeScreen(&point); + xevent.xmotion.x_root = point.x(); + xevent.xmotion.y_root = point.y(); + } + default: + break; + } + XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); +} + +void DesktopRootWindowHostX11::OnDeviceScaleFactorChanged( + float device_scale_factor) { +} + +void DesktopRootWindowHostX11::PrepareForShutdown() { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostX11, private: + +void DesktopRootWindowHostX11::InitX11Window( + const Widget::InitParams& params) { + unsigned long attribute_mask = CWBackPixmap; + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.background_pixmap = None; + + ::Atom window_type; + switch (params.type) { + case Widget::InitParams::TYPE_MENU: + swa.override_redirect = True; + attribute_mask |= CWOverrideRedirect; + window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"); + break; + case Widget::InitParams::TYPE_TOOLTIP: + window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP"); + break; + default: + window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); + break; + } + + xwindow_ = XCreateWindow( + xdisplay_, x_root_window_, + params.bounds.x(), params.bounds.y(), + params.bounds.width(), params.bounds.height(), + 0, // border width + CopyFromParent, // depth + InputOutput, + CopyFromParent, // visual + attribute_mask, + &swa); + base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, xwindow_); + + // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). + + long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | + KeyPressMask | KeyReleaseMask | + EnterWindowMask | LeaveWindowMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | PropertyChangeMask | + PointerMotionMask; + XSelectInput(xdisplay_, xwindow_, event_mask); + XFlush(xdisplay_); + + if (base::MessagePumpForUI::HasXInput2()) + ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); + + // TODO(erg): We currently only request window deletion events. We also + // should listen for activation events and anything else that GTK+ listens + // for, and do something useful. + ::Atom protocols[2]; + protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); + protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); + XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); + + // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with + // the desktop environment. + XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + // Likewise, the X server needs to know this window's pid so it knows which + // program to kill if the window hangs. + pid_t pid = getpid(); + XChangeProperty(xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_PID"), + XA_CARDINAL, + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&pid), 1); + + XChangeProperty(xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE"), + XA_ATOM, + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&window_type), 1); + + // Remove popup windows from taskbar. + if (params.type == Widget::InitParams::TYPE_POPUP || + params.type == Widget::InitParams::TYPE_BUBBLE) { + Atom atom = atom_cache_.GetAtom("_NET_WM_STATE_SKIP_TASKBAR"); + + // Setting _NET_WM_STATE by sending a message to the root_window (with + // SetWMSpecState) has no effect here since the window has not yet been + // mapped. So we manually change the state. + XChangeProperty (xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_STATE"), + XA_ATOM, + 32, + PropModeAppend, + reinterpret_cast<unsigned char*>(&atom), 1); + } +} + +// TODO(erg): This method should basically be everything I need form +// RootWindowHostX11::RootWindowHostX11(). +aura::RootWindow* DesktopRootWindowHostX11::InitRootWindow( + const Widget::InitParams& params) { + bounds_ = params.bounds; + + aura::RootWindow::CreateParams rw_params(bounds_); + rw_params.host = this; + root_window_ = new aura::RootWindow(rw_params); + root_window_->Init(); + root_window_->AddChild(content_window_); + root_window_->SetLayoutManager(new DesktopLayoutManager(root_window_)); + root_window_->SetProperty(kViewsWindowForRootWindow, content_window_); + root_window_->SetProperty(kHostForRootWindow, this); + root_window_host_delegate_ = root_window_; + + // If we're given a parent, we need to mark ourselves as transient to another + // window. Otherwise activation gets screwy. + gfx::NativeView parent = params.parent; + if (!params.child && params.parent) + parent->AddTransientChild(content_window_); + + native_widget_delegate_->OnNativeWidgetCreated(true); + + capture_client_.reset(new views::DesktopCaptureClient(root_window_)); + aura::client::SetCaptureClient(root_window_, capture_client_.get()); + + // Ensure that the X11DesktopHandler exists so that it dispatches activation + // messages to us. + X11DesktopHandler::get(); + + if (corewm::UseFocusControllerOnDesktop()) { + corewm::FocusController* focus_controller = + new corewm::FocusController(new DesktopFocusRules); + focus_client_.reset(focus_controller); + aura::client::SetFocusClient(root_window_, focus_controller); + aura::client::SetActivationClient(root_window_, focus_controller); + root_window_->AddPreTargetHandler(focus_controller); + } else { + focus_client_.reset(new aura::FocusManager); + aura::client::SetFocusClient(root_window_, focus_client_.get()); + activation_client_.reset(new DesktopActivationClient(root_window_)); + } + + dispatcher_client_.reset(new DesktopDispatcherClient); + aura::client::SetDispatcherClient(root_window_, + dispatcher_client_.get()); + + views::DesktopNativeCursorManager* desktop_native_cursor_manager = + new views::DesktopNativeCursorManager( + root_window_, + scoped_ptr<DesktopCursorLoaderUpdater>( + new DesktopCursorLoaderUpdaterAuraX11)); + cursor_client_.reset( + new views::corewm::CursorManager( + scoped_ptr<corewm::NativeCursorManager>( + desktop_native_cursor_manager))); + aura::client::SetCursorClient(root_window_, + cursor_client_.get()); + + position_client_.reset(new DesktopScreenPositionClient); + aura::client::SetScreenPositionClient(root_window_, + position_client_.get()); + + desktop_native_widget_aura_->InstallInputMethodEventFilter(root_window_); + + drag_drop_client_.reset(new DesktopDragDropClientAuraX11( + root_window_, desktop_native_cursor_manager, xdisplay_, xwindow_)); + aura::client::SetDragDropClient(root_window_, drag_drop_client_.get()); + + // TODO(erg): Unify this code once the other consumer goes away. + x11_window_event_filter_.reset( + new X11WindowEventFilter(root_window_, activation_client_.get())); + x11_window_event_filter_->SetUseHostWindowBorders(false); + desktop_native_widget_aura_->root_window_event_filter()->AddHandler( + x11_window_event_filter_.get()); + + x11_window_move_client_.reset(new X11DesktopWindowMoveClient); + aura::client::SetWindowMoveClient(root_window_, + x11_window_move_client_.get()); + + focus_client_->FocusWindow(content_window_); + return root_window_; +} + +bool DesktopRootWindowHostX11::IsWindowManagerPresent() { + // Per ICCCM 2.8, "Manager Selections", window managers should take ownership + // of WM_Sn selections (where n is a screen number). + return XGetSelectionOwner( + xdisplay_, atom_cache_.GetAtom("WM_S0")) != None; +} + +void DesktopRootWindowHostX11::SetWMSpecState(bool enabled, + ::Atom state1, + ::Atom state2) { + XEvent xclient; + memset(&xclient, 0, sizeof(xclient)); + xclient.type = ClientMessage; + xclient.xclient.window = xwindow_; + xclient.xclient.message_type = atom_cache_.GetAtom("_NET_WM_STATE"); + xclient.xclient.format = 32; + xclient.xclient.data.l[0] = + enabled ? k_NET_WM_STATE_ADD : k_NET_WM_STATE_REMOVE; + xclient.xclient.data.l[1] = state1; + xclient.xclient.data.l[2] = state2; + xclient.xclient.data.l[3] = 1; + xclient.xclient.data.l[4] = 0; + + XSendEvent(xdisplay_, x_root_window_, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xclient); +} + +bool DesktopRootWindowHostX11::HasWMSpecProperty(const char* property) const { + return window_properties_.find(atom_cache_.GetAtom(property)) != + window_properties_.end(); +} + +void DesktopRootWindowHostX11::OnCaptureReleased() { + native_widget_delegate_->OnMouseCaptureLost(); + g_current_capture = NULL; +} + +void DesktopRootWindowHostX11::DispatchMouseEvent(ui::MouseEvent* event) { + if (!g_current_capture || g_current_capture == this) { + root_window_host_delegate_->OnHostMouseEvent(event); + } else { + // Another DesktopRootWindowHostX11 has installed itself as + // capture. Translate the event's location and dispatch to the other. + event->ConvertLocationToTarget(root_window_, + g_current_capture->root_window_); + g_current_capture->root_window_host_delegate_->OnHostMouseEvent(event); + } +} + +std::list<XID>& DesktopRootWindowHostX11::open_windows() { + if (!open_windows_) + open_windows_ = new std::list<XID>(); + return *open_windows_; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostX11, MessageLoop::Dispatcher implementation: + +bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { + XEvent* xev = event; + + // May want to factor CheckXEventForConsistency(xev); into a common location + // since it is called here. + switch (xev->type) { + case EnterNotify: + case LeaveNotify: { + ui::MouseEvent mouse_event(xev); + DispatchMouseEvent(&mouse_event); + break; + } + case Expose: { + gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, + xev->xexpose.width, xev->xexpose.height); + root_window_host_delegate_->OnHostPaint(damage_rect); + break; + } + case KeyPress: { + ui::KeyEvent keydown_event(xev, false); + root_window_host_delegate_->OnHostKeyEvent(&keydown_event); + break; + } + case KeyRelease: { + ui::KeyEvent keyup_event(xev, false); + root_window_host_delegate_->OnHostKeyEvent(&keyup_event); + break; + } + case ButtonPress: { + if (static_cast<int>(xev->xbutton.button) == kBackMouseButton || + static_cast<int>(xev->xbutton.button) == kForwardMouseButton) { + aura::client::UserActionClient* gesture_client = + aura::client::GetUserActionClient(root_window_); + if (gesture_client) { + gesture_client->OnUserAction( + static_cast<int>(xev->xbutton.button) == kBackMouseButton ? + aura::client::UserActionClient::BACK : + aura::client::UserActionClient::FORWARD); + } + break; + } + } // fallthrough + case ButtonRelease: { + ui::MouseEvent mouseev(xev); + DispatchMouseEvent(&mouseev); + break; + } + case FocusOut: + if (xev->xfocus.mode != NotifyGrab) { + ReleaseCapture(); + root_window_host_delegate_->OnHostLostWindowCapture(); + } else { + root_window_host_delegate_->OnHostLostMouseGrab(); + } + break; + case ConfigureNotify: { + DCHECK_EQ(xwindow_, xev->xconfigure.window); + DCHECK_EQ(xwindow_, xev->xconfigure.event); + // It's possible that the X window may be resized by some other means than + // from within aura (e.g. the X window manager can change the size). Make + // sure the root window size is maintained properly. + int translated_x = xev->xconfigure.x; + int translated_y = xev->xconfigure.y; + if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) { + Window unused; + XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_, + 0, 0, &translated_x, &translated_y, &unused); + } + gfx::Rect bounds(translated_x, translated_y, + xev->xconfigure.width, xev->xconfigure.height); + bool size_changed = bounds_.size() != bounds.size(); + bool origin_changed = bounds_.origin() != bounds.origin(); + previous_bounds_ = bounds_; + bounds_ = bounds; + if (size_changed) + root_window_host_delegate_->OnHostResized(bounds.size()); + if (origin_changed) + root_window_host_delegate_->OnHostMoved(bounds_.origin()); + break; + } + case GenericEvent: { + ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); + if (!factory->ShouldProcessXI2Event(xev)) + break; + + ui::EventType type = ui::EventTypeFromNative(xev); + XEvent last_event; + int num_coalesced = 0; + + switch (type) { + // case ui::ET_TOUCH_MOVED: + // num_coalesced = CoalescePendingMotionEvents(xev, &last_event); + // if (num_coalesced > 0) + // xev = &last_event; + // // fallthrough + // case ui::ET_TOUCH_PRESSED: + // case ui::ET_TOUCH_RELEASED: { + // ui::TouchEvent touchev(xev); + // root_window_host_delegate_->OnHostTouchEvent(&touchev); + // break; + // } + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_DRAGGED: + case ui::ET_MOUSE_PRESSED: + case ui::ET_MOUSE_RELEASED: + case ui::ET_MOUSE_ENTERED: + case ui::ET_MOUSE_EXITED: { + if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) { + // If this is a motion event, we want to coalesce all pending motion + // events that are at the top of the queue. + num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); + if (num_coalesced > 0) + xev = &last_event; + } else if (type == ui::ET_MOUSE_PRESSED) { + XIDeviceEvent* xievent = + static_cast<XIDeviceEvent*>(xev->xcookie.data); + int button = xievent->detail; + if (button == kBackMouseButton || button == kForwardMouseButton) { + aura::client::UserActionClient* gesture_client = + aura::client::GetUserActionClient( + root_window_host_delegate_->AsRootWindow()); + if (gesture_client) { + bool reverse_direction = + ui::IsTouchpadEvent(xev) && ui::IsNaturalScrollEnabled(); + gesture_client->OnUserAction( + (button == kBackMouseButton && !reverse_direction) || + (button == kForwardMouseButton && reverse_direction) ? + aura::client::UserActionClient::BACK : + aura::client::UserActionClient::FORWARD); + } + break; + } + } else if (type == ui::ET_MOUSE_RELEASED) { + XIDeviceEvent* xievent = + static_cast<XIDeviceEvent*>(xev->xcookie.data); + int button = xievent->detail; + if (button == kBackMouseButton || button == kForwardMouseButton) { + // We've already passed the back/forward mouse down to the user + // action client; we want to swallow the corresponding release. + break; + } + } + ui::MouseEvent mouseev(xev); + DispatchMouseEvent(&mouseev); + break; + } + case ui::ET_MOUSEWHEEL: { + ui::MouseWheelEvent mouseev(xev); + DispatchMouseEvent(&mouseev); + break; + } + case ui::ET_SCROLL_FLING_START: + case ui::ET_SCROLL_FLING_CANCEL: + case ui::ET_SCROLL: { + ui::ScrollEvent scrollev(xev); + root_window_host_delegate_->OnHostScrollEvent(&scrollev); + break; + } + case ui::ET_UNKNOWN: + break; + default: + NOTREACHED(); + } + + // If we coalesced an event we need to free its cookie. + if (num_coalesced > 0) + XFreeEventData(xev->xgeneric.display, &last_event.xcookie); + break; + } + case MapNotify: { + // If there's no window manager running, we need to assign the X input + // focus to our host window. + if (!IsWindowManagerPresent() && focus_when_shown_) + XSetInputFocus(xdisplay_, xwindow_, RevertToNone, CurrentTime); + + FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, + observer_list_, + OnWindowMapped(xwindow_)); + break; + } + case UnmapNotify: { + FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, + observer_list_, + OnWindowUnmapped(xwindow_)); + break; + } + case ClientMessage: { + Atom message_type = xev->xclient.message_type; + if (message_type == atom_cache_.GetAtom("WM_PROTOCOLS")) { + Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]); + if (protocol == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { + // We have received a close message from the window manager. + root_window_->OnRootWindowHostCloseRequested(); + } else if (protocol == atom_cache_.GetAtom("_NET_WM_PING")) { + XEvent reply_event = *xev; + reply_event.xclient.window = x_root_window_; + + XSendEvent(xdisplay_, + reply_event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &reply_event); + } + } else if (message_type == atom_cache_.GetAtom("XdndEnter")) { + drag_drop_client_->OnXdndEnter(xev->xclient); + } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { + drag_drop_client_->OnXdndLeave(xev->xclient); + } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { + drag_drop_client_->OnXdndPosition(xev->xclient); + } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { + drag_drop_client_->OnXdndStatus(xev->xclient); + } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { + drag_drop_client_->OnXdndFinished(xev->xclient); + } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { + drag_drop_client_->OnXdndDrop(xev->xclient); + } + break; + } + case MappingNotify: { + switch (xev->xmapping.request) { + case MappingModifier: + case MappingKeyboard: + XRefreshKeyboardMapping(&xev->xmapping); + root_window_->OnKeyboardMappingChanged(); + break; + case MappingPointer: + ui::UpdateButtonMap(); + break; + default: + NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; + break; + } + break; + } + case MotionNotify: { + // Discard all but the most recent motion event that targets the same + // window with unchanged state. + XEvent last_event; + while (XPending(xev->xany.display)) { + XEvent next_event; + XPeekEvent(xev->xany.display, &next_event); + if (next_event.type == MotionNotify && + next_event.xmotion.window == xev->xmotion.window && + next_event.xmotion.subwindow == xev->xmotion.subwindow && + next_event.xmotion.state == xev->xmotion.state) { + XNextEvent(xev->xany.display, &last_event); + xev = &last_event; + } else { + break; + } + } + + ui::MouseEvent mouseev(xev); + DispatchMouseEvent(&mouseev); + break; + } + case PropertyNotify: { + // Get our new window property state if the WM has told us its changed. + ::Atom state = atom_cache_.GetAtom("_NET_WM_STATE"); + + std::vector< ::Atom> atom_list; + if (xev->xproperty.atom == state && + ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) { + window_properties_.clear(); + std::copy(atom_list.begin(), atom_list.end(), + inserter(window_properties_, window_properties_.begin())); + + if (!restored_bounds_.IsEmpty() && !IsMaximized()) { + // If we have restored bounds, but WM_STATE no longer claims to be + // maximized, we should clear our restored bounds. + restored_bounds_ = gfx::Rect(); + } else if (IsMaximized() && restored_bounds_.IsEmpty()) { + // The request that we become maximized originated from a different + // process. |bounds_| already contains our maximized bounds. Do a + // best effort attempt to get restored bounds by setting it to our + // previously set bounds (and if we get this wrong, we aren't any + // worse off since we'd otherwise be returning our maximized bounds). + restored_bounds_ = previous_bounds_; + } + + // Now that we have different window properties, we may need to + // relayout the window. (The windows code doesn't need this because + // their window change is synchronous.) + // + // TODO(erg): While this does work, there's a quick flash showing the + // tabstrip/toolbar/etc. when going into fullscreen mode before hiding + // those parts of the UI because we receive the sizing event from the + // window manager before we receive the event that changes the + // fullscreen state. Unsure what to do about that. + Widget* widget = native_widget_delegate_->AsWidget(); + NonClientView* non_client_view = widget->non_client_view(); + // non_client_view may be NULL, especially during creation. + if (non_client_view) { + non_client_view->client_view()->InvalidateLayout(); + non_client_view->InvalidateLayout(); + } + widget->GetRootView()->Layout(); + } + break; + } + case SelectionNotify: { + drag_drop_client_->OnSelectionNotify(xev->xselection); + break; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHost, public: + +// static +DesktopRootWindowHost* DesktopRootWindowHost::Create( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds) { + return new DesktopRootWindowHostX11(native_widget_delegate, + desktop_native_widget_aura, + initial_bounds); +} + +// static +ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { + const ui::LinuxUI* linux_ui = ui::LinuxUI::instance(); + if (linux_ui) { + ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(); + if (native_theme) + return native_theme; + } + + return ui::NativeTheme::instance(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h new file mode 100644 index 00000000000..c3630293dc8 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h @@ -0,0 +1,269 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_X11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_X11_H_ + +#include <X11/Xlib.h> + +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/root_window_host.h" +#include "ui/base/cursor/cursor_loader_x11.h" +#include "ui/base/x/x11_atom_cache.h" +#include "ui/gfx/rect.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" + +namespace aura { +namespace client { +class FocusClient; +class ScreenPositionClient; +} +} + +namespace views { +class DesktopActivationClient; +class DesktopCaptureClient; +class DesktopDragDropClientAuraX11; +class DesktopDispatcherClient; +class DesktopRootWindowHostObserverX11; +class X11DesktopWindowMoveClient; +class X11WindowEventFilter; + +namespace corewm { +class CursorManager; +} + +class VIEWS_EXPORT DesktopRootWindowHostX11 : + public DesktopRootWindowHost, + public aura::RootWindowHost, + public base::MessageLoop::Dispatcher { + public: + DesktopRootWindowHostX11( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura, + const gfx::Rect& initial_bounds); + virtual ~DesktopRootWindowHostX11(); + + // A way of converting an X11 |xid| host window into a |content_window_|. + static aura::Window* GetContentWindowForXID(XID xid); + + // A way of converting an X11 |xid| host window into this object. + static DesktopRootWindowHostX11* GetHostForXID(XID xid); + + // Get all open top-level windows. This includes windows that may not be + // visible. + static std::vector<aura::Window*> GetAllOpenWindows(); + + // Called by X11DesktopHandler to notify us that the native windowing system + // has changed our activation. + void HandleNativeWidgetActivationChanged(bool active); + + void AddObserver(views::DesktopRootWindowHostObserverX11* observer); + void RemoveObserver(views::DesktopRootWindowHostObserverX11* observer); + + // Deallocates the internal list of open windows. + static void CleanUpWindowList(); + + protected: + // Overridden from DesktopRootWindowHost: + virtual aura::RootWindow* Init(aura::Window* content_window, + const Widget::InitParams& params) OVERRIDE; + virtual void InitFocus(aura::Window* window) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void CloseNow() OVERRIDE; + virtual aura::RootWindowHost* AsRootWindowHost() OVERRIDE; + virtual void ShowWindowWithState(ui::WindowShowState show_state) OVERRIDE; + virtual void ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) OVERRIDE; + virtual bool IsVisible() const OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const OVERRIDE; + virtual gfx::Rect GetWindowBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetClientAreaBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetRestoredBounds() const OVERRIDE; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE; + virtual void SetShape(gfx::NativeRegion native_region) OVERRIDE; + virtual void Activate() OVERRIDE; + virtual void Deactivate() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual void Restore() OVERRIDE; + virtual bool IsMaximized() const OVERRIDE; + virtual bool IsMinimized() const OVERRIDE; + virtual bool HasCapture() const OVERRIDE; + virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; + virtual void SetWindowTitle(const string16& title) OVERRIDE; + virtual void ClearNativeFocus() OVERRIDE; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual bool ShouldUseNativeFrame() OVERRIDE; + virtual void FrameTypeChanged() OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; + virtual void SetOpacity(unsigned char opacity) OVERRIDE; + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) OVERRIDE; + virtual void InitModalType(ui::ModalType modal_type) OVERRIDE; + virtual void FlashFrame(bool flash_frame) OVERRIDE; + virtual void OnNativeWidgetFocus() OVERRIDE; + virtual void OnNativeWidgetBlur() OVERRIDE; + virtual void SetInactiveRenderingDisabled(bool disable_inactive) OVERRIDE; + + // Overridden from aura::RootWindowHost: + virtual void SetDelegate(aura::RootWindowHostDelegate* delegate) OVERRIDE; + virtual aura::RootWindow* GetRootWindow() OVERRIDE; + virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void ToggleFullScreen() OVERRIDE; + virtual gfx::Rect GetBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual gfx::Insets GetInsets() const OVERRIDE; + virtual void SetInsets(const gfx::Insets& insets) OVERRIDE; + virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; + virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE; + virtual bool ConfineCursorToRootWindow() OVERRIDE; + virtual void UnConfineCursor() OVERRIDE; + virtual void OnCursorVisibilityChanged(bool show) OVERRIDE; + virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE; + virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE; + virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void PrepareForShutdown() OVERRIDE; + +private: + // Initializes our X11 surface to draw on. This method performs all + // initialization related to talking to the X11 server. + void InitX11Window(const Widget::InitParams& params); + + // Creates an aura::RootWindow to contain the |content_window|, along with + // all aura client objects that direct behavior. + aura::RootWindow* InitRootWindow(const Widget::InitParams& params); + + // Returns true if there's an X window manager present... in most cases. Some + // window managers (notably, ion3) don't implement enough of ICCCM for us to + // detect that they're there. + bool IsWindowManagerPresent(); + + // Sends a message to the x11 window manager, enabling or disabling the + // states |state1| and |state2|. + void SetWMSpecState(bool enabled, ::Atom state1, ::Atom state2); + + // Checks if the window manager has set a specific state. + bool HasWMSpecProperty(const char* property) const; + + // Called when another DRWHL takes capture, or when capture is released + // entirely. + void OnCaptureReleased(); + + // Dispatches a mouse event, taking mouse capture into account. If a + // different host has capture, we translate the event to its coordinate space + // and dispatch it to that host instead. + void DispatchMouseEvent(ui::MouseEvent* event); + + // See comment for variable open_windows_. + static std::list<XID>& open_windows(); + + // Overridden from Dispatcher: + virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + + base::WeakPtrFactory<DesktopRootWindowHostX11> close_widget_factory_; + + // X11 things + // The display and the native X window hosting the root window. + Display* xdisplay_; + ::Window xwindow_; + + // The native root window. + ::Window x_root_window_; + + ui::X11AtomCache atom_cache_; + + // Is the window mapped to the screen? + bool window_mapped_; + + // The bounds of |xwindow_|. + gfx::Rect bounds_; + + // Whenever the bounds are set, we keep the previous set of bounds around so + // we can have a better chance of getting the real |restored_bounds_|. Window + // managers tend to send a Configure message with the maximized bounds, and + // then set the window maximized property. (We don't rely on this for when we + // request that the window be maximized, only when we detect that some other + // process has requested that we become the maximized window.) + gfx::Rect previous_bounds_; + + // The bounds of our window before we were maximized. + gfx::Rect restored_bounds_; + + // True if the window should be focused when the window is shown. + bool focus_when_shown_; + + // The window manager state bits. + std::set< ::Atom> window_properties_; + + // We are owned by the RootWindow, but we have to have a back pointer to it. + aura::RootWindow* root_window_; + + // aura:: objects that we own. + scoped_ptr<DesktopCaptureClient> capture_client_; + scoped_ptr<aura::client::FocusClient> focus_client_; + scoped_ptr<DesktopActivationClient> activation_client_; + scoped_ptr<views::corewm::CursorManager> cursor_client_; + scoped_ptr<DesktopDispatcherClient> dispatcher_client_; + scoped_ptr<aura::client::ScreenPositionClient> position_client_; + scoped_ptr<DesktopDragDropClientAuraX11> drag_drop_client_; + + // Current Aura cursor. + gfx::NativeCursor current_cursor_; + + scoped_ptr<X11WindowEventFilter> x11_window_event_filter_; + scoped_ptr<X11DesktopWindowMoveClient> x11_window_move_client_; + + // TODO(beng): Consider providing an interface to DesktopNativeWidgetAura + // instead of providing this route back to Widget. + internal::NativeWidgetDelegate* native_widget_delegate_; + + DesktopNativeWidgetAura* desktop_native_widget_aura_; + + aura::RootWindowHostDelegate* root_window_host_delegate_; + aura::Window* content_window_; + + ObserverList<DesktopRootWindowHostObserverX11> observer_list_; + + // The current root window host that has capture. While X11 has something + // like Windows SetCapture()/ReleaseCapture(), it is entirely implicit and + // there are no notifications when this changes. We need to track this so we + // can notify widgets when they have lost capture, which controls a bunch of + // things in views like hiding menus. + static DesktopRootWindowHostX11* g_current_capture; + + // A list of all (top-level) windows that have been created but not yet + // destroyed. + static std::list<XID>* open_windows_; + + DISALLOW_COPY_AND_ASSIGN(DesktopRootWindowHostX11); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_X11_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen.h b/chromium/ui/views/widget/desktop_aura/desktop_screen.h new file mode 100644 index 00000000000..ad66bd41575 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen.h @@ -0,0 +1,22 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_ + +#include "ui/views/views_export.h" + +namespace gfx { +class Screen; +} + +namespace views { + +// Creates a Screen that represents the screen of the environment that hosts +// a RootWindowHost. Caller owns the result. +VIEWS_EXPORT gfx::Screen* CreateDesktopScreen(); + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.cc new file mode 100644 index 00000000000..2ca556c3979 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" + +#include "ui/aura/root_window.h" +#include "ui/gfx/display.h" +#include "ui/gfx/point_conversions.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" + +namespace views { + +namespace { + +gfx::Point GetOrigin(const aura::RootWindow* root_window) { + gfx::Point origin_in_pixels = root_window->GetHostOrigin(); + aura::RootWindow* window = const_cast<aura::RootWindow*>(root_window); + float scale = gfx::Screen::GetScreenFor(window)-> + GetDisplayNearestWindow(window).device_scale_factor(); + return gfx::ToFlooredPoint( + gfx::ScalePoint(origin_in_pixels, 1 / scale)); +} + +// Returns true if bounds passed to window are treated as though they are in +// screen coordinates. +bool PositionWindowInScreenCoordinates(aura::Window* window) { + if (window->type() == aura::client::WINDOW_TYPE_POPUP || + window->type() == aura::client::WINDOW_TYPE_TOOLTIP) + return true; + + Widget* widget = Widget::GetWidgetForNativeView(window); + return widget && widget->is_top_level(); +} + +} // namespace + +DesktopScreenPositionClient::DesktopScreenPositionClient() { +} + +DesktopScreenPositionClient::~DesktopScreenPositionClient() { +} + +void DesktopScreenPositionClient::ConvertPointToScreen( + const aura::Window* window, gfx::Point* point) { + const aura::RootWindow* root_window = window->GetRootWindow(); + aura::Window::ConvertPointToTarget(window, root_window, point); + gfx::Point origin = GetOrigin(root_window); + point->Offset(origin.x(), origin.y()); +} + +void DesktopScreenPositionClient::ConvertPointFromScreen( + const aura::Window* window, gfx::Point* point) { + const aura::RootWindow* root_window = window->GetRootWindow(); + gfx::Point origin = GetOrigin(root_window); + point->Offset(-origin.x(), -origin.y()); + aura::Window::ConvertPointToTarget(root_window, window, point); +} + +void DesktopScreenPositionClient::ConvertHostPointToScreen( + aura::RootWindow* window, gfx::Point* point) { + ConvertPointToScreen(window, point); +} + +void DesktopScreenPositionClient::SetBounds( + aura::Window* window, + const gfx::Rect& bounds, + const gfx::Display& display) { + // TODO: Use the 3rd parameter, |display|. + aura::RootWindow* root = window->GetRootWindow(); + + if (PositionWindowInScreenCoordinates(window)) { + // The caller expects windows we consider "embedded" to be placed in the + // screen coordinate system. So we need to offset the root window's + // position (which is in screen coordinates) from these bounds. + + gfx::Point origin = bounds.origin(); + aura::Window::ConvertPointToTarget(window->parent(), root, &origin); + + gfx::Point host_origin = GetOrigin(root); + origin.Offset(-host_origin.x(), -host_origin.y()); + window->SetBounds(gfx::Rect(origin, bounds.size())); + return; + } + + DesktopNativeWidgetAura* desktop_native_widget = + DesktopNativeWidgetAura::ForWindow(window); + if (desktop_native_widget) { + root->SetHostBounds(bounds); + // Setting bounds of root resizes |window|. + } else { + window->SetBounds(bounds); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.h new file mode 100644 index 00000000000..479a1cc4b3d --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_POSITION_CLIENT_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_POSITION_CLIENT_H_ + +#include "ui/aura/client/screen_position_client.h" +#include "ui/views/views_export.h" + +namespace views { + +// Client that always offsets by the toplevel RootWindow of the passed +// in child NativeWidgetAura. +class VIEWS_EXPORT DesktopScreenPositionClient + : public aura::client::ScreenPositionClient { + public: + DesktopScreenPositionClient(); + virtual ~DesktopScreenPositionClient(); + + // aura::client::ScreenPositionClient overrides: + virtual void ConvertPointToScreen(const aura::Window* window, + gfx::Point* point) OVERRIDE; + virtual void ConvertPointFromScreen(const aura::Window* window, + gfx::Point* point) OVERRIDE; + virtual void ConvertHostPointToScreen(aura::RootWindow* window, + gfx::Point* point) OVERRIDE; + virtual void SetBounds(aura::Window* window, + const gfx::Rect& bounds, + const gfx::Display& display) OVERRIDE; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_POSITION_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc new file mode 100644 index 00000000000..bb1cd8b08ac --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client_unittest.cc @@ -0,0 +1,85 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_delegate.h" + +namespace views { + +typedef ViewsTestBase DesktopScreenPositionClientTest; + +// Verifies setting the bounds of a dialog parented to a Widget with a +// DesktopNativeWidgetAura is positioned correctly. +TEST_F(DesktopScreenPositionClientTest, PositionDialog) { + Widget parent_widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.bounds = gfx::Rect(10, 11, 200, 200); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&parent_widget); + parent_widget.Init(init_params); + // parent_widget.Show(); + + // Owned by |dialog|. + DialogDelegateView* dialog_delegate_view = new DialogDelegateView; + // Owned by |parent_widget|. + Widget* dialog = DialogDelegate::CreateDialogWidget( + dialog_delegate_view, + NULL, + parent_widget.GetNativeView()); + dialog->SetBounds(gfx::Rect(11, 12, 200, 200)); + EXPECT_EQ("11,12", dialog->GetWindowBoundsInScreen().origin().ToString()); +} + +// Verifies that setting the bounds of a control parented to something other +// than the root window is positioned correctly. +TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) { + Widget widget1; + Widget widget2; + Widget widget3; + gfx::Point origin = gfx::Point(16, 16); + + // Use a custom frame type. By default we will choose a native frame when + // aero glass is enabled, and this complicates the logic surrounding origin + // computation, making it difficult to compute the expected origin location. + widget1.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + widget2.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + widget3.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM); + + // Create 3 windows. A root window, an arbitrary window parented to the root + // but NOT positioned at (0,0) relative to the root, and then a third window + // parented to the second, also not positioned at (0,0). + Widget::InitParams params1 = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params1.bounds = gfx::Rect(origin, gfx::Size(700, 600)); + params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params1.native_widget = new DesktopNativeWidgetAura(&widget1); + widget1.Init(params1); + + Widget::InitParams params2 = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params2.bounds = gfx::Rect(origin, gfx::Size(600, 500)); + params2.parent = widget1.GetNativeView(); + params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params2.child = true; + widget2.Init(params2); + + Widget::InitParams params3 = + CreateParams(Widget::InitParams::TYPE_CONTROL); + params3.parent = widget2.GetNativeView(); + params3.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params3.child = true; + params3.bounds = gfx::Rect(origin, gfx::Size(500, 400)); + widget3.Init(params3); + + // The origin of the 3rd window should be the sum of all parent origins. + gfx::Point expected_origin(origin.x() * 3, origin.y() * 3); + gfx::Rect expected_bounds(expected_origin, gfx::Size(500, 400)); + gfx::Rect actual_bounds(widget3.GetWindowBoundsInScreen()); + EXPECT_EQ(expected_bounds.ToString(), actual_bounds.ToString()); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc new file mode 100644 index 00000000000..93721bce353 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_screen_win.h" + +#include "base/logging.h" +#include "ui/aura/root_window.h" +#include "ui/aura/root_window_host.h" +#include "ui/gfx/display.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_win.h" +#include "ui/views/widget/desktop_aura/desktop_screen.h" + +namespace { + +MONITORINFO GetMonitorInfoForMonitor(HMONITOR monitor) { + MONITORINFO monitor_info = { 0 }; + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfo(monitor, &monitor_info); + return monitor_info; +} + +gfx::Display GetDisplay(MONITORINFO& monitor_info) { + // TODO(oshima): Implement ID and Observer. + gfx::Display display(0, gfx::Rect(monitor_info.rcMonitor)); + display.set_work_area(gfx::Rect(monitor_info.rcWork)); + return display; +} + +} // namespace + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// DesktopScreenWin, public: + +DesktopScreenWin::DesktopScreenWin() { +} + +DesktopScreenWin::~DesktopScreenWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopScreenWin, gfx::ScreenWin implementation: + +bool DesktopScreenWin::IsDIPEnabled() { + return true; +} + +gfx::Display DesktopScreenWin::GetDisplayMatching( + const gfx::Rect& match_rect) const { + return GetDisplayNearestPoint(match_rect.CenterPoint()); +} + +HWND DesktopScreenWin::GetHWNDFromNativeView(gfx::NativeView window) const { + aura::RootWindow* root_window = window->GetRootWindow(); + return root_window ? root_window->GetAcceleratedWidget() : NULL; +} + +gfx::NativeWindow DesktopScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { + return (::IsWindow(hwnd)) ? + DesktopRootWindowHostWin::GetContentWindowForHWND(hwnd) : NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +gfx::Screen* CreateDesktopScreen() { + return new DesktopScreenWin; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h new file mode 100644 index 00000000000..3225d61709e --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ + +#include "ui/gfx/screen_win.h" +#include "ui/views/views_export.h" + +namespace views { + +class VIEWS_EXPORT DesktopScreenWin : public gfx::ScreenWin { +public: + DesktopScreenWin(); + virtual ~DesktopScreenWin(); + + private: + // Overridden from gfx::ScreenWin: + virtual bool IsDIPEnabled() OVERRIDE; + virtual gfx::Display GetDisplayMatching( + const gfx::Rect& match_rect) const OVERRIDE; + virtual HWND GetHWNDFromNativeView(gfx::NativeView window) const OVERRIDE; + virtual gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(DesktopScreenWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc new file mode 100644 index 00000000000..77dbaa497a7 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/desktop_screen.h" + +#include <X11/Xlib.h> + +// It clashes with out RootWindow. +#undef RootWindow + +#include "base/logging.h" +#include "ui/aura/root_window.h" +#include "ui/aura/root_window_host.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/display.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/screen.h" + +namespace { + +// TODO(erg): This method is a temporary hack, until we can reliably extract +// location data out of XRandR. +gfx::Size GetPrimaryDisplaySize() { + ::Display* display = ui::GetXDisplay(); + ::Screen* screen = DefaultScreenOfDisplay(display); + int width = WidthOfScreen(screen); + int height = HeightOfScreen(screen); + + return gfx::Size(width, height); +} + +class DesktopScreenX11 : public gfx::Screen { + public: + DesktopScreenX11(); + virtual ~DesktopScreenX11(); + + // Overridden from gfx::Screen: + virtual bool IsDIPEnabled() OVERRIDE; + virtual gfx::Point GetCursorScreenPoint() OVERRIDE; + virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE; + virtual int GetNumDisplays() OVERRIDE; + virtual gfx::Display GetDisplayNearestWindow( + gfx::NativeView window) const OVERRIDE; + virtual gfx::Display GetDisplayNearestPoint( + const gfx::Point& point) const OVERRIDE; + virtual gfx::Display GetDisplayMatching( + const gfx::Rect& match_rect) const OVERRIDE; + virtual gfx::Display GetPrimaryDisplay() const OVERRIDE; + virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE; + virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11); +}; + +//////////////////////////////////////////////////////////////////////////////// +// DesktopScreenX11, public: + +DesktopScreenX11::DesktopScreenX11() { +} + +DesktopScreenX11::~DesktopScreenX11() { +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopScreenX11, gfx::Screen implementation: + +bool DesktopScreenX11::IsDIPEnabled() { + return false; +} + +gfx::Point DesktopScreenX11::GetCursorScreenPoint() { + Display* display = ui::GetXDisplay(); + + ::Window root, child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + XQueryPointer(display, + DefaultRootWindow(display), + &root, + &child, + &root_x, + &root_y, + &win_x, + &win_y, + &mask); + + return gfx::Point(root_x, root_y); +} + +gfx::NativeWindow DesktopScreenX11::GetWindowAtCursorScreenPoint() { + // TODO(erg): Implement using the discussion at + // http://codereview.chromium.org/10279005/ + return NULL; +} + +int DesktopScreenX11::GetNumDisplays() { + // TODO(erg): Figure this out with oshima or piman because I have no clue + // about the XRandR implications here. + return 1; +} + +gfx::Display DesktopScreenX11::GetDisplayNearestWindow( + gfx::NativeView window) const { + // TODO(erg): Do the right thing once we know what that is. + return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); +} + +gfx::Display DesktopScreenX11::GetDisplayNearestPoint( + const gfx::Point& point) const { + // TODO(erg): Do the right thing once we know what that is. + return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); +} + +gfx::Display DesktopScreenX11::GetDisplayMatching( + const gfx::Rect& match_rect) const { + // TODO(erg): Do the right thing once we know what that is. + return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); +} + +gfx::Display DesktopScreenX11::GetPrimaryDisplay() const { + // TODO(erg): Do the right thing once we know what that is. + return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); +} + +void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) { + // TODO(erg|oshima): Do the right thing once we know what that is. + // crbug.com/122863 +} +void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { + // TODO(erg|oshima): Do the right thing once we know what that is. + // crbug.com/122863 +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +namespace views { + +gfx::Screen* CreateDesktopScreen() { + return new DesktopScreenX11; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc new file mode 100644 index 00000000000..46906992fce --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/x11_desktop_handler.h" + +#include "base/message_loop/message_loop.h" +#include "ui/aura/env.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/base/x/x11_util.h" +#include "ui/views/widget/desktop_aura/desktop_activation_client.h" + +#if !defined(OS_CHROMEOS) +#include "ui/views/ime/input_method.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" +#endif + +namespace { + +const char* kAtomsToCache[] = { + "_NET_ACTIVE_WINDOW", + NULL +}; + +// Our global instance. Deleted when our Env() is deleted. +views::X11DesktopHandler* g_handler = NULL; + +} // namespace + +namespace views { + +// static +X11DesktopHandler* X11DesktopHandler::get() { + if (!g_handler) + g_handler = new X11DesktopHandler; + + return g_handler; +} + +X11DesktopHandler::X11DesktopHandler() + : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), + x_root_window_(DefaultRootWindow(xdisplay_)), + current_window_(None), + atom_cache_(xdisplay_, kAtomsToCache) { + base::MessagePumpAuraX11::Current()->AddDispatcherForRootWindow(this); + aura::Env::GetInstance()->AddObserver(this); + + XWindowAttributes attr; + XGetWindowAttributes(xdisplay_, x_root_window_, &attr); + XSelectInput(xdisplay_, x_root_window_, + attr.your_event_mask | PropertyChangeMask | + StructureNotifyMask | SubstructureNotifyMask); +} + +X11DesktopHandler::~X11DesktopHandler() { + aura::Env::GetInstance()->RemoveObserver(this); + base::MessagePumpAuraX11::Current()->RemoveDispatcherForRootWindow(this); +} + +void X11DesktopHandler::ActivateWindow(::Window window) { + DCHECK_EQ(base::MessagePumpAuraX11::GetDefaultXDisplay(), xdisplay_); + + XEvent xclient; + memset(&xclient, 0, sizeof(xclient)); + xclient.type = ClientMessage; + xclient.xclient.window = window; + xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); + xclient.xclient.format = 32; + xclient.xclient.data.l[0] = 1; // Specified we are an app. + xclient.xclient.data.l[1] = CurrentTime; + xclient.xclient.data.l[2] = None; + xclient.xclient.data.l[3] = 0; + xclient.xclient.data.l[4] = 0; + + XSendEvent(xdisplay_, x_root_window_, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xclient); +} + +bool X11DesktopHandler::IsActiveWindow(::Window window) const { + return window == current_window_; +} + +bool X11DesktopHandler::Dispatch(const base::NativeEvent& event) { + // Check for a change to the active window. + switch (event->type) { + case PropertyNotify: { + ::Atom active_window = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); + + if (event->xproperty.window == x_root_window_ && + event->xproperty.atom == active_window) { + int window; + if (ui::GetIntProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) && + window) { + OnActiveWindowChanged(static_cast< ::Window>(window)); + } + } + break; + } + } + + return true; +} + +void X11DesktopHandler::OnWindowInitialized(aura::Window* window) { +} + +void X11DesktopHandler::OnWillDestroyEnv() { + g_handler = NULL; + delete this; +} + +void X11DesktopHandler::OnActiveWindowChanged(::Window xid) { + DesktopRootWindowHostX11* old_host = + views::DesktopRootWindowHostX11::GetHostForXID(current_window_); + if (old_host) + old_host->HandleNativeWidgetActivationChanged(false); + + DesktopRootWindowHostX11* new_host = + views::DesktopRootWindowHostX11::GetHostForXID(xid); + if (new_host) + new_host->HandleNativeWidgetActivationChanged(true); + + current_window_ = xid; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h new file mode 100644 index 00000000000..8cda9eeaaab --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_HANDLER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_HANDLER_H_ + +#include <X11/Xlib.h> +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/message_loop/message_loop.h" +#include "ui/aura/env_observer.h" +#include "ui/base/x/x11_atom_cache.h" +#include "ui/views/views_export.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace views { + +// A singleton that owns global objects related to the desktop and listens for +// X11 events on the X11 root window. Destroys itself when aura::Env is +// deleted. +class VIEWS_EXPORT X11DesktopHandler : public base::MessageLoop::Dispatcher, + public aura::EnvObserver { + public: + // Returns the singleton handler. + static X11DesktopHandler* get(); + + // Sends a request to the window manager to activate |window|. + void ActivateWindow(::Window window); + + // Checks if the current active window is |window|. + bool IsActiveWindow(::Window window) const; + + // Overridden from MessageLoop::Dispatcher: + virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + + // Overridden from aura::EnvObserver: + virtual void OnWindowInitialized(aura::Window* window) OVERRIDE; + virtual void OnWillDestroyEnv() OVERRIDE; + + private: + explicit X11DesktopHandler(); + virtual ~X11DesktopHandler(); + + // Handles changes in activation. + void OnActiveWindowChanged(::Window window); + + // The display and the native X window hosting the root window. + Display* xdisplay_; + + // The native root window. + ::Window x_root_window_; + + // The activated window. + ::Window current_window_; + + ui::X11AtomCache atom_cache_; + + DISALLOW_COPY_AND_ASSIGN(X11DesktopHandler); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_HANDLER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc new file mode 100644 index 00000000000..d28de216dce --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h" + +#include <X11/Xlib.h> +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/debug/stack_trace.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_pump_aurax11.h" +#include "base/run_loop.h" +#include "ui/aura/env.h" +#include "ui/aura/root_window.h" +#include "ui/base/events/event.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/screen.h" + +namespace views { + +X11DesktopWindowMoveClient::X11DesktopWindowMoveClient() + : move_loop_(this), + root_window_(NULL) { +} + +X11DesktopWindowMoveClient::~X11DesktopWindowMoveClient() {} + +void X11DesktopWindowMoveClient::OnMouseMovement(XMotionEvent* event) { + gfx::Point cursor_point(event->x_root, event->y_root); + gfx::Point system_loc = cursor_point - window_offset_; + root_window_->SetHostBounds(gfx::Rect( + system_loc, root_window_->GetHostSize())); +} + +void X11DesktopWindowMoveClient::OnMouseReleased() { + EndMoveLoop(); +} + +void X11DesktopWindowMoveClient::OnMoveLoopEnded() { + root_window_ = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation: + +aura::client::WindowMoveResult X11DesktopWindowMoveClient::RunMoveLoop( + aura::Window* source, + const gfx::Vector2d& drag_offset, + aura::client::WindowMoveSource move_source) { + window_offset_ = drag_offset; + root_window_ = source->GetRootWindow(); + + bool success = move_loop_.RunMoveLoop(source, root_window_->last_cursor()); + return success ? aura::client::MOVE_SUCCESSFUL : aura::client::MOVE_CANCELED; +} + +void X11DesktopWindowMoveClient::EndMoveLoop() { + move_loop_.EndMoveLoop(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h new file mode 100644 index 00000000000..1a8b5884ac1 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ + +#include <X11/Xlib.h> + +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "ui/aura/client/window_move_client.h" +#include "ui/gfx/point.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h" + +namespace aura { +class RootWindow; +} + +namespace views { + +// When we're dragging tabs, we need to manually position our window. +class VIEWS_EXPORT X11DesktopWindowMoveClient : + public views::X11WholeScreenMoveLoopDelegate, + public aura::client::WindowMoveClient { + public: + X11DesktopWindowMoveClient(); + virtual ~X11DesktopWindowMoveClient(); + + // Overridden from X11WholeScreenMoveLoopDelegate: + virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE; + virtual void OnMouseReleased() OVERRIDE; + virtual void OnMoveLoopEnded() OVERRIDE; + + // Overridden from aura::client::WindowMoveClient: + virtual aura::client::WindowMoveResult RunMoveLoop( + aura::Window* window, + const gfx::Vector2d& drag_offset, + aura::client::WindowMoveSource move_source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + + private: + X11WholeScreenMoveLoop move_loop_; + + // We need to keep track of this so we can actually move it when reacting to + // mouse events. + aura::RootWindow* root_window_; + + // Our cursor offset from the top left window origin when the drag + // started. Used to calculate the window's new bounds relative to the current + // location of the cursor. + gfx::Vector2d window_offset_; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc new file mode 100644 index 00000000000..d04dd88cb2f --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" + +#include <X11/Xlib.h> +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/debug/stack_trace.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_pump_aurax11.h" +#include "base/run_loop.h" +#include "ui/aura/env.h" +#include "ui/aura/root_window.h" +#include "ui/base/events/event.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/screen.h" + +namespace views { + +X11WholeScreenMoveLoop::X11WholeScreenMoveLoop( + X11WholeScreenMoveLoopDelegate* delegate) + : delegate_(delegate), + in_move_loop_(false), + grab_input_window_(None) /*, + root_window_(NULL)*/ { +} + +X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostLinux, MessageLoop::Dispatcher implementation: + +bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) { + XEvent* xev = event; + + // Note: the escape key is handled in the tab drag controller, which has + // keyboard focus even though we took pointer grab. + switch (xev->type) { + case MotionNotify: { + delegate_->OnMouseMovement(&xev->xmotion); + break; + } + case ButtonRelease: { + if (xev->xbutton.button == Button1) { + // Assume that drags are being done with the left mouse button. Only + // break the drag if the left mouse button was released. + delegate_->OnMouseReleased(); + } + break; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation: + +bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, + gfx::NativeCursor cursor) { + DCHECK(!in_move_loop_); // Can only handle one nested loop at a time. + in_move_loop_ = true; + + Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); + + // Creates an invisible, InputOnly toplevel window. This window will receive + // all mouse movement for drags. It turns out that normal windows doing a + // grab doesn't redirect pointer motion events if the pointer isn't over the + // grabbing window. But InputOnly windows are able to grab everything. This + // is what GTK+ does, and I found a patch to KDE that did something similar. + unsigned long attribute_mask = CWEventMask | CWOverrideRedirect; + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | + StructureNotifyMask; + swa.override_redirect = True; + grab_input_window_ = XCreateWindow( + display, + DefaultRootWindow(display), + -100, -100, 10, 10, + 0, 0, InputOnly, CopyFromParent, + attribute_mask, &swa); + base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( + this, grab_input_window_); + + // Wait for the window to be mapped. If we don't, XGrabPointer fails. + XMapRaised(display, grab_input_window_); + base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped( + grab_input_window_); + + if (!GrabPointerWithCursor(cursor)) + return false; + + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); + base::RunLoop run_loop(aura::Env::GetInstance()->GetDispatcher()); + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + return true; +} + +void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) { + DCHECK(in_move_loop_); + GrabPointerWithCursor(cursor); +} + +void X11WholeScreenMoveLoop::EndMoveLoop() { + if (!in_move_loop_) + return; + + // TODO(erg): Is this ungrab the cause of having to click to give input focus + // on drawn out windows? Not ungrabbing here screws the X server until I kill + // the chrome process. + + // Ungrab before we let go of the window. + Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); + XUngrabPointer(display, CurrentTime); + + base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( + grab_input_window_); + delegate_->OnMoveLoopEnded(); + XDestroyWindow(display, grab_input_window_); + + in_move_loop_ = false; + quit_closure_.Run(); +} + +bool X11WholeScreenMoveLoop::GrabPointerWithCursor(gfx::NativeCursor cursor) { + Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); + XGrabServer(display); + XUngrabPointer(display, CurrentTime); + int ret = XGrabPointer( + display, + grab_input_window_, + False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, + GrabModeAsync, + None, + cursor.platform(), + CurrentTime); + XUngrabServer(display); + if (ret != GrabSuccess) { + DLOG(ERROR) << "Grabbing new tab for dragging failed: " + << ui::GetX11ErrorString(display, ret); + return false; + } + + return true; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h new file mode 100644 index 00000000000..ceff84f9fb2 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h @@ -0,0 +1,62 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ + +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h" + +namespace aura { +class Window; +} + +namespace views { + +// Runs a nested message loop and grabs the mouse. This is used to implement +// dragging. +class X11WholeScreenMoveLoop : public base::MessageLoop::Dispatcher { + public: + explicit X11WholeScreenMoveLoop(X11WholeScreenMoveLoopDelegate* delegate); + virtual ~X11WholeScreenMoveLoop(); + + // Overridden from base::MessageLoop::Dispatcher: + virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + + // Runs the nested message loop. While the mouse is grabbed, use |cursor| as + // the mouse cursor. Returns true if there we were able to grab the pointer + // and run the move loop. + bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor); + + // Updates the cursor while the move loop is running. + void UpdateCursor(gfx::NativeCursor cursor); + + // Ends the RunMoveLoop() that's currently in progress. + void EndMoveLoop(); + + private: + // Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if + // the grab was successful. + bool GrabPointerWithCursor(gfx::NativeCursor cursor); + + X11WholeScreenMoveLoopDelegate* delegate_; + + // Are we running a nested message loop from RunMoveLoop()? + bool in_move_loop_; + + // An invisible InputOnly window . We create this window so we can track the + // cursor wherever it goes on screen during a drag, since normal windows + // don't receive pointer motion events outside of their bounds. + ::Window grab_input_window_; + + base::Closure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(X11WholeScreenMoveLoop); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h new file mode 100644 index 00000000000..69f63be1287 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h @@ -0,0 +1,31 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_DELEGATE_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_DELEGATE_H_ + +#include <X11/Xlib.h> +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +namespace views { + +// Receives mouse events while the X11WholeScreenMoveLoop is tracking a drag on +// the screen. +class X11WholeScreenMoveLoopDelegate { + public: + // Called when we receive a motion event. + virtual void OnMouseMovement(XMotionEvent* event) = 0; + + // Called when the mouse button is released. + virtual void OnMouseReleased() = 0; + + // Called when the user has released the mouse button. The move loop will + // release the grab after this has been called. + virtual void OnMoveLoopEnded() = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_DELEGATE_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc new file mode 100644 index 00000000000..3495b04bc58 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/desktop_aura/x11_window_event_filter.h" + +#include <X11/extensions/XInput.h> +#include <X11/extensions/XInput2.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> + +#include "base/message_loop/message_pump_aurax11.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window_delegate.h" +#include "ui/base/events/event.h" +#include "ui/base/events/event_utils.h" +#include "ui/base/hit_test.h" +#include "ui/views/widget/desktop_aura/desktop_activation_client.h" +#include "ui/views/widget/native_widget_aura.h" + +namespace { + +// These constants are defined in the Extended Window Manager Hints +// standard...and aren't in any header that I can find. +const int k_NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; +const int k_NET_WM_MOVERESIZE_SIZE_TOP = 1; +const int k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; +const int k_NET_WM_MOVERESIZE_SIZE_RIGHT = 3; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; +const int k_NET_WM_MOVERESIZE_SIZE_LEFT = 7; +const int k_NET_WM_MOVERESIZE_MOVE = 8; + +// This data structure represents additional hints that we send to the window +// manager and has a direct lineage back to Motif, which defined this de facto +// standard. This struct doesn't seem 64-bit safe though, but it's what GDK +// does. +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +} MotifWmHints; + +// The bitflag in |flags| in MotifWmHints that signals that the reader should +// pay attention to the value in |decorations|. +const unsigned long kHintsDecorations = (1L << 1); + +const char* kAtomsToCache[] = { + "_MOTIF_WM_HINTS", + "_NET_WM_MOVERESIZE", + NULL +}; + +} // namespace + +namespace views { + +X11WindowEventFilter::X11WindowEventFilter( + aura::RootWindow* root_window, + DesktopActivationClient* activation_client) + : activation_client_(activation_client), + xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), + xwindow_(root_window->GetAcceleratedWidget()), + x_root_window_(DefaultRootWindow(xdisplay_)), + atom_cache_(xdisplay_, kAtomsToCache), + is_active_(false) { +} + +X11WindowEventFilter::~X11WindowEventFilter() { +} + +void X11WindowEventFilter::SetUseHostWindowBorders(bool use_os_border) { + MotifWmHints motif_hints; + memset(&motif_hints, 0, sizeof(motif_hints)); + motif_hints.flags = kHintsDecorations; + motif_hints.decorations = use_os_border ? 1 : 0; + + ::Atom hint_atom = atom_cache_.GetAtom("_MOTIF_WM_HINTS"); + XChangeProperty(base::MessagePumpAuraX11::GetDefaultXDisplay(), + xwindow_, + hint_atom, + hint_atom, + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&motif_hints), + sizeof(MotifWmHints)/sizeof(long)); +} + +void X11WindowEventFilter::OnMouseEvent(ui::MouseEvent* event) { + if (event->type() != ui::ET_MOUSE_PRESSED) + return; + + if (!event->IsLeftMouseButton()) + return; + + aura::Window* target = static_cast<aura::Window*>(event->target()); + int component = + target->delegate()->GetNonClientComponent(event->location()); + if (component == HTCLIENT) + return; + + // Get the |x_root_window_| location out of the native event. + if (event->native_event()) { + const gfx::Point x_root_location = + ui::EventSystemLocationFromNative(event->native_event()); + if (DispatchHostWindowDragMovement(component, x_root_location)) + event->StopPropagation(); + } +} + +bool X11WindowEventFilter::DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& screen_location) { + int direction = -1; + switch (hittest) { + case HTBOTTOM: + direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + case HTBOTTOMLEFT: + direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + case HTBOTTOMRIGHT: + direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + case HTCAPTION: + direction = k_NET_WM_MOVERESIZE_MOVE; + break; + case HTLEFT: + direction = k_NET_WM_MOVERESIZE_SIZE_LEFT; + break; + case HTRIGHT: + direction = k_NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + case HTTOP: + direction = k_NET_WM_MOVERESIZE_SIZE_TOP; + break; + case HTTOPLEFT: + direction = k_NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + case HTTOPRIGHT: + direction = k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + default: + return false; + } + + // We most likely have an implicit grab right here. We need to dump it + // because what we're about to do is tell the window manager + // that it's now responsible for moving the window around; it immediately + // grabs when it receives the event below. + XUngrabPointer(xdisplay_, CurrentTime); + + XEvent event; + memset(&event, 0, sizeof(event)); + event.xclient.type = ClientMessage; + event.xclient.display = xdisplay_; + event.xclient.window = xwindow_; + event.xclient.message_type = atom_cache_.GetAtom("_NET_WM_MOVERESIZE"); + event.xclient.format = 32; + event.xclient.data.l[0] = screen_location.x(); + event.xclient.data.l[1] = screen_location.y(); + event.xclient.data.l[2] = direction; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + XSendEvent(xdisplay_, x_root_window_, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &event); + + return true; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h new file mode 100644 index 00000000000..a6bdce8d3c8 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WINDOW_EVENT_FILTER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WINDOW_EVENT_FILTER_H_ + +#include <X11/Xlib.h> +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "ui/base/events/event_handler.h" +#include "ui/base/x/x11_atom_cache.h" +#include "ui/views/views_export.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace gfx { +class Point; +} + +namespace views { +class DesktopActivationClient; +class NativeWidgetAura; + +// An EventFilter that sets properties on X11 windows. +class VIEWS_EXPORT X11WindowEventFilter : public ui::EventHandler { + public: + explicit X11WindowEventFilter(aura::RootWindow* root_window, + DesktopActivationClient* activation_client); + virtual ~X11WindowEventFilter(); + + // Changes whether borders are shown on this |root_window|. + void SetUseHostWindowBorders(bool use_os_border); + + // Overridden from ui::EventHandler: + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + + private: + // Dispatches a _NET_WM_MOVERESIZE message to the window manager to tell it + // to act as if a border or titlebar drag occurred. + bool DispatchHostWindowDragMovement(int hittest, + const gfx::Point& screen_location); + + DesktopActivationClient* activation_client_; + + // The display and the native X window hosting the root window. + Display* xdisplay_; + ::Window xwindow_; + + // The native root window. + ::Window x_root_window_; + + ui::X11AtomCache atom_cache_; + + // True if |xwindow_| is the current _NET_ACTIVE_WINDOW. + bool is_active_; + + DISALLOW_COPY_AND_ASSIGN(X11WindowEventFilter); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WINDOW_EVENT_FILTER_H_ diff --git a/chromium/ui/views/widget/drop_helper.cc b/chromium/ui/views/widget/drop_helper.cc new file mode 100644 index 00000000000..e4493a30b48 --- /dev/null +++ b/chromium/ui/views/widget/drop_helper.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/drop_helper.h" + +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +DropHelper::DropHelper(View* root_view) + : root_view_(root_view), + target_view_(NULL), + deepest_view_(NULL) { +} + +DropHelper::~DropHelper() { +} + +void DropHelper::ResetTargetViewIfEquals(View* view) { + if (target_view_ == view) + target_view_ = NULL; + if (deepest_view_ == view) + deepest_view_ = NULL; +} + +int DropHelper::OnDragOver(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation) { + View* view = CalculateTargetViewImpl(root_view_location, data, true, + &deepest_view_); + + if (view != target_view_) { + // Target changed notify old drag exited, then new drag entered. + NotifyDragExit(); + target_view_ = view; + NotifyDragEntered(data, root_view_location, drag_operation); + } + + return NotifyDragOver(data, root_view_location, drag_operation); +} + +void DropHelper::OnDragExit() { + NotifyDragExit(); + deepest_view_ = target_view_ = NULL; +} + +int DropHelper::OnDrop(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation) { + View* drop_view = target_view_; + deepest_view_ = target_view_ = NULL; + if (!drop_view) + return ui::DragDropTypes::DRAG_NONE; + + if (drag_operation == ui::DragDropTypes::DRAG_NONE) { + drop_view->OnDragExited(); + return ui::DragDropTypes::DRAG_NONE; + } + + gfx::Point view_location(root_view_location); + View* root_view = drop_view->GetWidget()->GetRootView(); + View::ConvertPointToTarget(root_view, drop_view, &view_location); + ui::DropTargetEvent drop_event(data, view_location, view_location, + drag_operation); + return drop_view->OnPerformDrop(drop_event); +} + +View* DropHelper::CalculateTargetView( + const gfx::Point& root_view_location, + const OSExchangeData& data, + bool check_can_drop) { + return CalculateTargetViewImpl(root_view_location, data, check_can_drop, + NULL); +} + +View* DropHelper::CalculateTargetViewImpl( + const gfx::Point& root_view_location, + const OSExchangeData& data, + bool check_can_drop, + View** deepest_view) { + View* view = root_view_->GetEventHandlerForPoint(root_view_location); + if (view == deepest_view_) { + // The view the mouse is over hasn't changed; reuse the target. + return target_view_; + } + if (deepest_view) + *deepest_view = view; + // TODO(sky): for the time being these are separate. Once I port chrome menu + // I can switch to the #else implementation and nuke the OS_WIN + // implementation. +#if defined(OS_WIN) + // View under mouse changed, which means a new view may want the drop. + // Walk the tree, stopping at target_view_ as we know it'll accept the + // drop. + while (view && view != target_view_ && + (!view->enabled() || !view->CanDrop(data))) { + view = view->parent(); + } +#elif !defined(OS_MACOSX) + int formats = 0; + std::set<OSExchangeData::CustomFormat> custom_formats; + while (view && view != target_view_) { + if (view->enabled() && + view->GetDropFormats(&formats, &custom_formats) && + data.HasAnyFormat(formats, custom_formats) && + (!check_can_drop || view->CanDrop(data))) { + // Found the view. + return view; + } + formats = 0; + custom_formats.clear(); + view = view->parent(); + } +#endif + return view; +} + +void DropHelper::NotifyDragEntered(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation) { + if (!target_view_) + return; + + gfx::Point target_view_location(root_view_location); + View::ConvertPointToTarget(root_view_, target_view_, &target_view_location); + ui::DropTargetEvent enter_event(data, + target_view_location, + target_view_location, + drag_operation); + target_view_->OnDragEntered(enter_event); +} + +int DropHelper::NotifyDragOver(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation) { + if (!target_view_) + return ui::DragDropTypes::DRAG_NONE; + + gfx::Point target_view_location(root_view_location); + View::ConvertPointToTarget(root_view_, target_view_, &target_view_location); + ui::DropTargetEvent enter_event(data, + target_view_location, + target_view_location, + drag_operation); + return target_view_->OnDragUpdated(enter_event); +} + +void DropHelper::NotifyDragExit() { + if (target_view_) + target_view_->OnDragExited(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/drop_helper.h b/chromium/ui/views/widget/drop_helper.h new file mode 100644 index 00000000000..9247e1e7d32 --- /dev/null +++ b/chromium/ui/views/widget/drop_helper.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DROP_HELPER_H_ +#define UI_VIEWS_WIDGET_DROP_HELPER_H_ + +#include "base/basictypes.h" + +namespace gfx { +class Point; +} // namespace gfx + +namespace ui { +class OSExchangeData; +} // namespace ui +using ui::OSExchangeData; + +namespace views { + +class RootView; +class View; + +// DropHelper provides support for managing the view a drop is going to occur +// at during dnd as well as sending the view the appropriate dnd methods. +// DropHelper is intended to be used by a class that interacts with the system +// drag and drop. The system class invokes OnDragOver as the mouse moves, +// then either OnDragExit or OnDrop when the drop is done. +class DropHelper { + public: + explicit DropHelper(View* root_view); + ~DropHelper(); + + // Current view drop events are targeted at, may be NULL. + View* target_view() const { return target_view_; } + + // Returns the RootView the DropHelper was created with. + View* root_view() const { return root_view_; } + + // Resets the target_view_ to NULL if it equals view. + // + // This is invoked when a View is removed from the RootView to make sure + // we don't target a view that was removed during dnd. + void ResetTargetViewIfEquals(View* view); + + // Invoked when a the mouse is dragged over the root view during a drag and + // drop operation. This method returns a bitmask of the types in DragDropTypes + // for the target view. If no view wants the drop, DRAG_NONE is returned. + int OnDragOver(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation); + + // Invoked when a the mouse is dragged out of the root view during a drag and + // drop operation. + void OnDragExit(); + + // Invoked when the user drops data on the root view during a drag and drop + // operation. See OnDragOver for details on return type. + // + // NOTE: implementations must invoke OnDragOver before invoking this, + // supplying the return value from OnDragOver as the drag_operation. + int OnDrop(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation); + + // Calculates the target view for a drop given the specified location in + // the coordinate system of the rootview. This tries to avoid continually + // querying CanDrop by returning target_view_ if the mouse is still over + // target_view_. + View* CalculateTargetView(const gfx::Point& root_view_location, + const OSExchangeData& data, + bool check_can_drop); + + private: + // Implementation of CalculateTargetView. If |deepest_view| is non-NULL it is + // set to the deepest descendant of the RootView that contains the point + // |root_view_location| + View* CalculateTargetViewImpl(const gfx::Point& root_view_location, + const OSExchangeData& data, + bool check_can_drop, + View** deepest_view); + + // Methods to send the appropriate drop notification to the targeted view. + // These do nothing if the target view is NULL. + void NotifyDragEntered(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation); + int NotifyDragOver(const OSExchangeData& data, + const gfx::Point& root_view_location, + int drag_operation); + void NotifyDragExit(); + + // RootView we were created for. + View* root_view_; + + // View we're targeting events at. + View* target_view_; + + // The deepest view under the current drop coordinate. + View* deepest_view_; + + DISALLOW_COPY_AND_ASSIGN(DropHelper); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DROP_HELPER_H_ diff --git a/chromium/ui/views/widget/drop_target_win.cc b/chromium/ui/views/widget/drop_target_win.cc new file mode 100644 index 00000000000..dfe15f9af23 --- /dev/null +++ b/chromium/ui/views/widget/drop_target_win.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/drop_target_win.h" + +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/gfx/point.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.h" + +using ui::OSExchangeData; +using ui::OSExchangeDataProviderWin; + +namespace views { + +DropTargetWin::DropTargetWin(internal::RootView* root_view) + : ui::DropTargetWin(root_view->GetWidget()->GetNativeView()), + helper_(root_view) { +} + +DropTargetWin::~DropTargetWin() { +} + +void DropTargetWin::ResetTargetViewIfEquals(View* view) { + helper_.ResetTargetViewIfEquals(view); +} + +DWORD DropTargetWin::OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect) { + gfx::Point root_view_location(cursor_position.x, cursor_position.y); + View::ConvertPointToTarget(NULL, helper_.root_view(), &root_view_location); + OSExchangeData data(new OSExchangeDataProviderWin(data_object)); + int drop_operation = + helper_.OnDragOver(data, root_view_location, + ui::DragDropTypes::DropEffectToDragOperation(effect)); + return ui::DragDropTypes::DragOperationToDropEffect(drop_operation); +} + +void DropTargetWin::OnDragLeave(IDataObject* data_object) { + helper_.OnDragExit(); +} + +DWORD DropTargetWin::OnDrop(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect) { + gfx::Point root_view_location(cursor_position.x, cursor_position.y); + View::ConvertPointToTarget(NULL, helper_.root_view(), &root_view_location); + + OSExchangeData data(new OSExchangeDataProviderWin(data_object)); + int drop_operation = ui::DragDropTypes::DropEffectToDragOperation(effect); + drop_operation = helper_.OnDragOver(data, root_view_location, + drop_operation); + drop_operation = helper_.OnDrop(data, root_view_location, drop_operation); + return ui::DragDropTypes::DragOperationToDropEffect(drop_operation); +} + +} // namespace views diff --git a/chromium/ui/views/widget/drop_target_win.h b/chromium/ui/views/widget/drop_target_win.h new file mode 100644 index 00000000000..742675122df --- /dev/null +++ b/chromium/ui/views/widget/drop_target_win.h @@ -0,0 +1,55 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_DROP_TARGET_WIN_H_ +#define UI_VIEWS_WIDGET_DROP_TARGET_WIN_H_ + +#include "ui/base/dragdrop/drop_target_win.h" +#include "ui/views/widget/drop_helper.h" + +namespace views { + +class View; +namespace internal { +class RootView; +} + +// DropTargetWin takes care of managing drag and drop for NativeWidgetWin. It +// converts Windows OLE drop messages into Views drop messages. +// +// DropTargetWin uses DropHelper to manage the appropriate view to target +// drop messages at. +class DropTargetWin : public ui::DropTargetWin { + public: + explicit DropTargetWin(internal::RootView* root_view); + virtual ~DropTargetWin(); + + // If a drag and drop is underway and view is the current drop target, the + // drop target is set to null. + // This is invoked when a View is removed from the RootView to make sure + // we don't target a view that was removed during dnd. + void ResetTargetViewIfEquals(View* view); + + protected: + virtual DWORD OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect); + + virtual void OnDragLeave(IDataObject* data_object); + + virtual DWORD OnDrop(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect); + + private: + views::DropHelper helper_; + + DISALLOW_COPY_AND_ASSIGN(DropTargetWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DROP_TARGET_WIN_H_ diff --git a/chromium/ui/views/widget/monitor_win.cc b/chromium/ui/views/widget/monitor_win.cc new file mode 100644 index 00000000000..c183cb0f6db --- /dev/null +++ b/chromium/ui/views/widget/monitor_win.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/monitor_win.h" + +#include <shellapi.h> + +#include "base/logging.h" +#include "ui/gfx/rect.h" + +namespace views { + +gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect) { + RECT p_rect = rect.ToRECT(); + HMONITOR monitor = MonitorFromRect(&p_rect, MONITOR_DEFAULTTONEAREST); + if (monitor) { + MONITORINFO mi = {0}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + return gfx::Rect(mi.rcWork); + } + NOTREACHED(); + return gfx::Rect(); +} + +HWND GetTopmostAutoHideTaskbarForEdge(UINT edge, HMONITOR monitor) { + // NOTE: this may be invoked on a background thread. + APPBARDATA taskbar_data = { sizeof(APPBARDATA), NULL, 0, edge }; + HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, + &taskbar_data)); + return (::IsWindow(taskbar) && (monitor != NULL) && + (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && + (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST)) ? + taskbar : NULL; +} + +} // namespace views diff --git a/chromium/ui/views/widget/monitor_win.h b/chromium/ui/views/widget/monitor_win.h new file mode 100644 index 00000000000..87bdc4e00c2 --- /dev/null +++ b/chromium/ui/views/widget/monitor_win.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_MONITOR_WIN_H_ +#define UI_VIEWS_WIDGET_MONITOR_WIN_H_ + +#include <windows.h> + +#include "ui/views/views_export.h" + +namespace gfx { +class Rect; +} + +namespace views { + +// Returns the bounds for the monitor that contains the largest area of +// intersection with the specified rectangle. +VIEWS_EXPORT gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect); + +// Returns the always-on-top auto-hiding taskbar for edge |edge| (one of +// ABE_LEFT, TOP, RIGHT, or BOTTOM) of monitor |monitor|. NULL is returned +// if nothing is found. +// +// WARNING: this function spawns a nested message loop. Use Appbar instead if +// possible. +VIEWS_EXPORT HWND GetTopmostAutoHideTaskbarForEdge(UINT edge, HMONITOR monitor); + +} // namespace views + +#endif // UI_VIEWS_WIDGET_MONITOR_WIN_H_ diff --git a/chromium/ui/views/widget/native_widget.h b/chromium/ui/views/widget/native_widget.h new file mode 100644 index 00000000000..39255ffc9c1 --- /dev/null +++ b/chromium/ui/views/widget/native_widget.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ + +#include "ui/views/widget/widget.h" + +namespace ui { +class EventHandler; +} + +namespace views { +namespace internal { +class NativeWidgetPrivate; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidget interface +// +// An interface that serves as the public API base for the +// internal::NativeWidget interface that Widget uses to communicate with a +// backend-specific native widget implementation. This is the only component of +// this interface that is publicly visible, and exists solely for exposure via +// Widget's native_widget() accessor, which code occasionally static_casts to +// a known implementation in platform-specific code. +// +class VIEWS_EXPORT NativeWidget { + public: + virtual ~NativeWidget() {} + + // Retrieves the event handler + virtual ui::EventHandler* GetEventHandler() = 0; + + private: + friend class Widget; + + virtual internal::NativeWidgetPrivate* AsNativeWidgetPrivate() = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc new file mode 100644 index 00000000000..8c9ad02b899 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -0,0 +1,1078 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/native_widget_aura.h" + +#include "base/bind.h" +#include "base/strings/string_util.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/aura/client/activation_client.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/client/stacking_client.h" +#include "ui/aura/client/window_move_client.h" +#include "ui/aura/client/window_types.h" +#include "ui/aura/env.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/events/event.h" +#include "ui/base/ui_base_types.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/font.h" +#include "ui/gfx/screen.h" +#include "ui/native_theme/native_theme_aura.h" +#include "ui/views/drag_utils.h" +#include "ui/views/ime/input_method_bridge.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/drop_helper.h" +#include "ui/views/widget/native_widget_aura_window_observer.h" +#include "ui/views/widget/native_widget_delegate.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/tooltip_manager_aura.h" +#include "ui/views/widget/widget_aura_utils.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/window_reorderer.h" + +#if defined(OS_WIN) +#include "base/win/scoped_gdi_object.h" +#include "base/win/win_util.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_win.h" +#endif + +#if defined(USE_X11) && !defined(OS_CHROMEOS) +#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" +#endif + +#if !defined(OS_CHROMEOS) +#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#endif + +namespace views { + +namespace { + +void SetRestoreBounds(aura::Window* window, const gfx::Rect& bounds) { + window->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds)); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, public: + +NativeWidgetAura::NativeWidgetAura(internal::NativeWidgetDelegate* delegate) + : delegate_(delegate), + window_(new aura::Window(this)), + ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), + close_widget_factory_(this), + can_activate_(true), + destroying_(false), + cursor_(gfx::kNullCursor), + saved_window_state_(ui::SHOW_STATE_DEFAULT) { + aura::client::SetFocusChangeObserver(window_, this); + aura::client::SetActivationChangeObserver(window_, this); +} + +// static +gfx::Font NativeWidgetAura::GetWindowTitleFont() { +#if defined(OS_WIN) + NONCLIENTMETRICS ncm; + base::win::GetNonClientMetrics(&ncm); + l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); + base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); + return gfx::Font(caption_font); +#else + return gfx::Font(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, internal::NativeWidgetPrivate implementation: + +void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { + // Aura needs to know which desktop (Ash or regular) will manage this widget. + // See Widget::InitParams::context for details. + DCHECK(params.parent || params.context); + + ownership_ = params.ownership; + + window_->set_user_data(this); + window_->SetType(GetAuraWindowTypeForWidgetType(params.type)); + window_->SetProperty(aura::client::kShowStateKey, params.show_state); + if (params.type == Widget::InitParams::TYPE_BUBBLE) + aura::client::SetHideOnDeactivate(window_, true); + window_->SetTransparent( + params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW); + window_->Init(params.layer_type); + if (params.type == Widget::InitParams::TYPE_CONTROL) + window_->Show(); + + delegate_->OnNativeWidgetCreated(false); + + gfx::Rect window_bounds = params.bounds; + gfx::NativeView parent = params.parent; + gfx::NativeView context = params.context; + if (!params.child) { + // Set up the transient child before the window is added. This way the + // LayoutManager knows the window has a transient parent. + if (parent && parent->type() != aura::client::WINDOW_TYPE_UNKNOWN) { + parent->AddTransientChild(window_); + if (!context) + context = parent; + parent = NULL; + } + // SetAlwaysOnTop before SetParent so that always-on-top container is used. + SetAlwaysOnTop(params.keep_on_top); + // Make sure we have a real |window_bounds|. + if (parent && window_bounds == gfx::Rect()) { + // If a parent is specified but no bounds are given, + // use the origin of the parent's display so that the widget + // will be added to the same display as the parent. + gfx::Rect bounds = gfx::Screen::GetScreenFor(parent)-> + GetDisplayNearestWindow(parent).bounds(); + window_bounds.set_origin(bounds.origin()); + } + } + + if (parent) { + parent->AddChild(window_); + } else { + window_->SetDefaultParentByRootWindow(context->GetRootWindow(), + window_bounds); + } + + // Wait to set the bounds until we have a parent. That way we can know our + // true state/bounds (the LayoutManager may enforce a particular + // state/bounds). + if (IsMaximized()) + SetRestoreBounds(window_, window_bounds); + else + SetBounds(window_bounds); + window_->set_ignore_events(!params.accept_events); + can_activate_ = params.can_activate && + params.type != Widget::InitParams::TYPE_CONTROL && + params.type != Widget::InitParams::TYPE_TOOLTIP; + DCHECK(GetWidget()->GetRootView()); +#if !defined(OS_MACOSX) + if (params.type != Widget::InitParams::TYPE_TOOLTIP) + tooltip_manager_.reset(new views::TooltipManagerAura(window_, GetWidget())); +#endif // !defined(OS_MACOSX) + + drop_helper_.reset(new DropHelper(GetWidget()->GetRootView())); + if (params.type != Widget::InitParams::TYPE_TOOLTIP && + params.type != Widget::InitParams::TYPE_POPUP) { + aura::client::SetDragDropDelegate(window_, this); + } + + aura::client::SetActivationDelegate(window_, this); + + window_->SetProperty(aura::client::kCanMaximizeKey, + GetWidget()->widget_delegate()->CanMaximize()); + window_->SetProperty(aura::client::kCanResizeKey, + GetWidget()->widget_delegate()->CanResize()); + + window_reorderer_.reset(new WindowReorderer(window_, + GetWidget()->GetRootView())); +} + +NonClientFrameView* NativeWidgetAura::CreateNonClientFrameView() { + return NULL; +} + +bool NativeWidgetAura::ShouldUseNativeFrame() const { + // There is only one frame type for aura. + return false; +} + +void NativeWidgetAura::FrameTypeChanged() { + // This is called when the Theme has changed; forward the event to the root + // widget. + GetWidget()->ThemeChanged(); + GetWidget()->GetRootView()->SchedulePaint(); +} + +Widget* NativeWidgetAura::GetWidget() { + return delegate_->AsWidget(); +} + +const Widget* NativeWidgetAura::GetWidget() const { + return delegate_->AsWidget(); +} + +gfx::NativeView NativeWidgetAura::GetNativeView() const { + return window_; +} + +gfx::NativeWindow NativeWidgetAura::GetNativeWindow() const { + return window_; +} + +Widget* NativeWidgetAura::GetTopLevelWidget() { + NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView()); + return native_widget ? native_widget->GetWidget() : NULL; +} + +const ui::Compositor* NativeWidgetAura::GetCompositor() const { + return window_->layer()->GetCompositor(); +} + +ui::Compositor* NativeWidgetAura::GetCompositor() { + return window_->layer()->GetCompositor(); +} + +ui::Layer* NativeWidgetAura::GetLayer() { + return window_->layer(); +} + +void NativeWidgetAura::ReorderNativeViews() { + window_reorderer_->ReorderChildWindows(); +} + +void NativeWidgetAura::ViewRemoved(View* view) { + DCHECK(drop_helper_.get() != NULL); + drop_helper_->ResetTargetViewIfEquals(view); +} + +void NativeWidgetAura::SetNativeWindowProperty(const char* name, void* value) { + if (window_) + window_->SetNativeWindowProperty(name, value); +} + +void* NativeWidgetAura::GetNativeWindowProperty(const char* name) const { + return window_ ? window_->GetNativeWindowProperty(name) : NULL; +} + +TooltipManager* NativeWidgetAura::GetTooltipManager() const { + return tooltip_manager_.get(); +} + +void NativeWidgetAura::SetCapture() { + window_->SetCapture(); +} + +void NativeWidgetAura::ReleaseCapture() { + window_->ReleaseCapture(); +} + +bool NativeWidgetAura::HasCapture() const { + return window_->HasCapture(); +} + +InputMethod* NativeWidgetAura::CreateInputMethod() { + aura::RootWindow* root_window = window_->GetRootWindow(); + ui::InputMethod* host = + root_window->GetProperty(aura::client::kRootWindowInputMethodKey); + return new InputMethodBridge(this, host, true); +} + +internal::InputMethodDelegate* NativeWidgetAura::GetInputMethodDelegate() { + return this; +} + +void NativeWidgetAura::CenterWindow(const gfx::Size& size) { + gfx::Rect parent_bounds(window_->parent()->GetBoundsInRootWindow()); + // When centering window, we take the intersection of the host and + // the parent. We assume the root window represents the visible + // rect of a single screen. + gfx::Rect work_area = gfx::Screen::GetScreenFor(window_)-> + GetDisplayNearestWindow(window_).work_area(); + + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(window_->GetRootWindow()); + if (screen_position_client) { + gfx::Point origin = work_area.origin(); + screen_position_client->ConvertPointFromScreen(window_->GetRootWindow(), + &origin); + work_area.set_origin(origin); + } + + parent_bounds.Intersect(work_area); + + // If |window_|'s transient parent's bounds are big enough to fit it, then we + // center it with respect to the transient parent. + if (window_->transient_parent()) { + gfx::Rect transient_parent_rect = window_->transient_parent()-> + GetBoundsInRootWindow(); + transient_parent_rect.Intersect(work_area); + if (transient_parent_rect.height() >= size.height() && + transient_parent_rect.width() >= size.width()) + parent_bounds = transient_parent_rect; + } + + gfx::Rect window_bounds( + parent_bounds.x() + (parent_bounds.width() - size.width()) / 2, + parent_bounds.y() + (parent_bounds.height() - size.height()) / 2, + size.width(), + size.height()); + // Don't size the window bigger than the parent, otherwise the user may not be + // able to close or move it. + window_bounds.AdjustToFit(parent_bounds); + + // Convert the bounds back relative to the parent. + gfx::Point origin = window_bounds.origin(); + aura::Window::ConvertPointToTarget(window_->GetRootWindow(), + window_->parent(), &origin); + window_bounds.set_origin(origin); + window_->SetBounds(window_bounds); +} + +void NativeWidgetAura::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + // The interface specifies returning restored bounds, not current bounds. + *bounds = GetRestoredBounds(); + *show_state = window_->GetProperty(aura::client::kShowStateKey); +} + +void NativeWidgetAura::SetWindowTitle(const string16& title) { + window_->set_title(title); +} + +void NativeWidgetAura::SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + // Aura doesn't have window icons. +} + +void NativeWidgetAura::InitModalType(ui::ModalType modal_type) { + if (modal_type != ui::MODAL_TYPE_NONE) + window_->SetProperty(aura::client::kModalKey, modal_type); +} + +gfx::Rect NativeWidgetAura::GetWindowBoundsInScreen() const { + return window_->GetBoundsInScreen(); +} + +gfx::Rect NativeWidgetAura::GetClientAreaBoundsInScreen() const { + // View-to-screen coordinate system transformations depend on this returning + // the full window bounds, for example View::ConvertPointToScreen(). + return window_->GetBoundsInScreen(); +} + +gfx::Rect NativeWidgetAura::GetRestoredBounds() const { + // Restored bounds should only be relevant if the window is minimized or + // maximized. However, in some places the code expects GetRestoredBounds() + // to return the current window bounds if the window is not in either state. + if (IsMinimized() || IsMaximized() || IsFullscreen()) { + // Restore bounds are in screen coordinates, no need to convert. + gfx::Rect* restore_bounds = + window_->GetProperty(aura::client::kRestoreBoundsKey); + if (restore_bounds) + return *restore_bounds; + } + return window_->GetBoundsInScreen(); +} + +void NativeWidgetAura::SetBounds(const gfx::Rect& bounds) { + aura::RootWindow* root = window_->GetRootWindow(); + if (root) { + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root); + if (screen_position_client) { + gfx::Display dst_display = + gfx::Screen::GetScreenFor(window_)->GetDisplayMatching(bounds); + screen_position_client->SetBounds(window_, bounds, dst_display); + return; + } + } + window_->SetBounds(bounds); +} + +void NativeWidgetAura::SetSize(const gfx::Size& size) { + window_->SetBounds(gfx::Rect(window_->bounds().origin(), size)); +} + +void NativeWidgetAura::StackAbove(gfx::NativeView native_view) { + if (window_->parent() && window_->parent() == native_view->parent()) + window_->parent()->StackChildAbove(window_, native_view); +} + +void NativeWidgetAura::StackAtTop() { + window_->parent()->StackChildAtTop(window_); +} + +void NativeWidgetAura::StackBelow(gfx::NativeView native_view) { + if (window_->parent() && window_->parent() == native_view->parent()) + window_->parent()->StackChildBelow(window_, native_view); +} + +void NativeWidgetAura::SetShape(gfx::NativeRegion region) { + // No need for this. Just delete and ignore. + delete region; +} + +void NativeWidgetAura::Close() { + // |window_| may already be deleted by parent window. This can happen + // when this widget is child widget or has transient parent + // and ownership is WIDGET_OWNS_NATIVE_WIDGET. + DCHECK(window_ || + ownership_ == Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET); + if (window_) { + window_->SuppressPaint(); + Hide(); + window_->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE); + } + + if (!close_widget_factory_.HasWeakPtrs()) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&NativeWidgetAura::CloseNow, + close_widget_factory_.GetWeakPtr())); + } +} + +void NativeWidgetAura::CloseNow() { + delete window_; +} + +void NativeWidgetAura::Show() { + ShowWithWindowState(ui::SHOW_STATE_INACTIVE); +} + +void NativeWidgetAura::Hide() { + window_->Hide(); +} + +void NativeWidgetAura::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + SetRestoreBounds(window_, restored_bounds); + ShowWithWindowState(ui::SHOW_STATE_MAXIMIZED); +} + +void NativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { + if (state == ui::SHOW_STATE_MAXIMIZED || state == ui::SHOW_STATE_FULLSCREEN) + window_->SetProperty(aura::client::kShowStateKey, state); + window_->Show(); + if (can_activate_) { + if (state != ui::SHOW_STATE_INACTIVE) + Activate(); + // SetInitialFocus() should be always be called, even for + // SHOW_STATE_INACTIVE. When a frameless modal dialog is created by + // a widget of TYPE_WINDOW_FRAMELESS, Widget::Show() will call into + // this function with the window state SHOW_STATE_INACTIVE, + // SetInitialFoucs() has to be called so that the dialog can get focus. + // This also matches NativeWidgetWin which invokes SetInitialFocus + // regardless of show state. + SetInitialFocus(); + } +} + +bool NativeWidgetAura::IsVisible() const { + return window_->IsVisible(); +} + +void NativeWidgetAura::Activate() { + // We don't necessarily have a root window yet. This can happen with + // constrained windows. + if (window_->GetRootWindow()) { + aura::client::GetActivationClient(window_->GetRootWindow())->ActivateWindow( + window_); + } + if (window_->GetProperty(aura::client::kDrawAttentionKey)) + window_->SetProperty(aura::client::kDrawAttentionKey, false); +} + +void NativeWidgetAura::Deactivate() { + aura::client::GetActivationClient(window_->GetRootWindow())->DeactivateWindow( + window_); +} + +bool NativeWidgetAura::IsActive() const { + return aura::client::GetActivationClient(window_->GetRootWindow())-> + GetActiveWindow() == window_; +} + +void NativeWidgetAura::SetAlwaysOnTop(bool on_top) { + window_->SetProperty(aura::client::kAlwaysOnTopKey, on_top); +} + +void NativeWidgetAura::Maximize() { + window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); +} + +void NativeWidgetAura::Minimize() { + window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); +} + +bool NativeWidgetAura::IsMaximized() const { + return window_->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_MAXIMIZED; +} + +bool NativeWidgetAura::IsMinimized() const { + return window_->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_MINIMIZED; +} + +void NativeWidgetAura::Restore() { + window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); +} + +void NativeWidgetAura::SetFullscreen(bool fullscreen) { + if (IsFullscreen() == fullscreen) + return; // Nothing to do. + + // Save window state before entering full screen so that it could restored + // when exiting full screen. + if (fullscreen) + saved_window_state_ = window_->GetProperty(aura::client::kShowStateKey); + + window_->SetProperty( + aura::client::kShowStateKey, + fullscreen ? ui::SHOW_STATE_FULLSCREEN : saved_window_state_); +} + +bool NativeWidgetAura::IsFullscreen() const { + return window_->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_FULLSCREEN; +} + +void NativeWidgetAura::SetOpacity(unsigned char opacity) { + window_->layer()->SetOpacity(opacity / 255.0); +} + +void NativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { + NOTIMPLEMENTED(); +} + +void NativeWidgetAura::FlashFrame(bool flash) { + window_->SetProperty(aura::client::kDrawAttentionKey, flash); +} + +void NativeWidgetAura::RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + views::RunShellDrag(window_, data, location, operation, source); +} + +void NativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) { + if (window_) + window_->SchedulePaintInRect(rect); +} + +void NativeWidgetAura::SetCursor(gfx::NativeCursor cursor) { + cursor_ = cursor; + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window_->GetRootWindow()); + if (cursor_client) + cursor_client->SetCursor(cursor); +} + +bool NativeWidgetAura::IsMouseEventsEnabled() const { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window_->GetRootWindow()); + return cursor_client ? cursor_client->IsMouseEventsEnabled() : true; +} + +void NativeWidgetAura::ClearNativeFocus() { + aura::client::FocusClient* client = aura::client::GetFocusClient(window_); + if (window_ && client && window_->Contains(client->GetFocusedWindow())) + client->ResetFocusWithinActiveWindow(window_); +} + +gfx::Rect NativeWidgetAura::GetWorkAreaBoundsInScreen() const { + return gfx::Screen::GetScreenFor(GetNativeView())-> + GetDisplayNearestWindow(GetNativeView()).work_area(); +} + +void NativeWidgetAura::SetInactiveRenderingDisabled(bool value) { + if (!value) { + active_window_observer_.reset(); + } else { + active_window_observer_.reset( + new NativeWidgetAuraWindowObserver(window_, delegate_)); + } +} + +Widget::MoveLoopResult NativeWidgetAura::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) { + if (window_->parent() && + aura::client::GetWindowMoveClient(window_->parent())) { + SetCapture(); + aura::client::WindowMoveSource window_move_source = + source == Widget::MOVE_LOOP_SOURCE_MOUSE ? + aura::client::WINDOW_MOVE_SOURCE_MOUSE : + aura::client::WINDOW_MOVE_SOURCE_TOUCH; + if (aura::client::GetWindowMoveClient(window_->parent())->RunMoveLoop( + window_, drag_offset, window_move_source) == + aura::client::MOVE_SUCCESSFUL) { + return Widget::MOVE_LOOP_SUCCESSFUL; + } + } + return Widget::MOVE_LOOP_CANCELED; +} + +void NativeWidgetAura::EndMoveLoop() { + if (window_->parent() && + aura::client::GetWindowMoveClient(window_->parent())) { + aura::client::GetWindowMoveClient(window_->parent())->EndMoveLoop(); + } +} + +void NativeWidgetAura::SetVisibilityChangedAnimationsEnabled(bool value) { + window_->SetProperty(aura::client::kAnimationsDisabledKey, !value); +} + +ui::NativeTheme* NativeWidgetAura::GetNativeTheme() const { +#if !defined(OS_CHROMEOS) + return DesktopRootWindowHost::GetNativeTheme(window_); +#else + return ui::NativeThemeAura::instance(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, views::InputMethodDelegate implementation: + +void NativeWidgetAura::DispatchKeyEventPostIME(const ui::KeyEvent& key) { + FocusManager* focus_manager = GetWidget()->GetFocusManager(); + delegate_->OnKeyEvent(const_cast<ui::KeyEvent*>(&key)); + if (key.handled() || !focus_manager) + return; + focus_manager->OnKeyEvent(key); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, aura::WindowDelegate implementation: + +gfx::Size NativeWidgetAura::GetMinimumSize() const { + return delegate_->GetMinimumSize(); +} + +gfx::Size NativeWidgetAura::GetMaximumSize() const { + return delegate_->GetMaximumSize(); +} + +void NativeWidgetAura::OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + if (old_bounds.origin() != new_bounds.origin()) + delegate_->OnNativeWidgetMove(); + if (old_bounds.size() != new_bounds.size()) + delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); +} + +gfx::NativeCursor NativeWidgetAura::GetCursor(const gfx::Point& point) { + return cursor_; +} + +int NativeWidgetAura::GetNonClientComponent(const gfx::Point& point) const { + return delegate_->GetNonClientComponent(point); +} + +bool NativeWidgetAura::ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) { + views::WidgetDelegate* widget_delegate = GetWidget()->widget_delegate(); + if (widget_delegate && + !widget_delegate->ShouldDescendIntoChildForEventHandling(child, location)) + return false; + + // Don't descend into |child| if there is a view with a Layer that contains + // the point and is stacked above |child|s layer. + typedef std::vector<ui::Layer*> Layers; + const Layers& root_layers(delegate_->GetRootLayers()); + if (root_layers.empty()) + return true; + + Layers::const_iterator child_layer_iter( + std::find(window_->layer()->children().begin(), + window_->layer()->children().end(), child->layer())); + if (child_layer_iter == window_->layer()->children().end()) + return true; + + for (std::vector<ui::Layer*>::const_reverse_iterator i = root_layers.rbegin(); + i != root_layers.rend(); ++i) { + ui::Layer* layer = *i; + if (layer->visible() && layer->bounds().Contains(location)) { + Layers::const_iterator root_layer_iter( + std::find(window_->layer()->children().begin(), + window_->layer()->children().end(), layer)); + if (root_layer_iter > child_layer_iter) + return false; + } + } + return true; +} + +bool NativeWidgetAura::CanFocus() { + return can_activate_; +} + +void NativeWidgetAura::OnCaptureLost() { + delegate_->OnMouseCaptureLost(); +} + +void NativeWidgetAura::OnPaint(gfx::Canvas* canvas) { + delegate_->OnNativeWidgetPaint(canvas); +} + +void NativeWidgetAura::OnDeviceScaleFactorChanged(float device_scale_factor) { + // Repainting with new scale factor will paint the content at the right scale. +} + +void NativeWidgetAura::OnWindowDestroying() { + delegate_->OnNativeWidgetDestroying(); + + // If the aura::Window is destroyed, we can no longer show tooltips. + tooltip_manager_.reset(); +} + +void NativeWidgetAura::OnWindowDestroyed() { + window_ = NULL; + delegate_->OnNativeWidgetDestroyed(); + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete this; +} + +void NativeWidgetAura::OnWindowTargetVisibilityChanged(bool visible) { + delegate_->OnNativeWidgetVisibilityChanged(visible); +} + +bool NativeWidgetAura::HasHitTestMask() const { + return delegate_->HasHitTestMask(); +} + +void NativeWidgetAura::GetHitTestMask(gfx::Path* mask) const { + DCHECK(mask); + delegate_->GetHitTestMask(mask); +} + +scoped_refptr<ui::Texture> NativeWidgetAura::CopyTexture() { + // The layer we create doesn't have an external texture, so this should never + // get invoked. + NOTREACHED(); + return scoped_refptr<ui::Texture>(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, ui::EventHandler implementation: + +void NativeWidgetAura::OnKeyEvent(ui::KeyEvent* event) { + if (event->is_char()) { + // If a ui::InputMethod object is attached to the root window, character + // events are handled inside the object and are not passed to this function. + // If such object is not attached, character events might be sent (e.g. on + // Windows). In this case, we just skip these. + return; + } + // Renderer may send a key event back to us if the key event wasn't handled, + // and the window may be invisible by that time. + if (!window_->IsVisible()) + return; + GetWidget()->GetInputMethod()->DispatchKeyEvent(*event); + event->SetHandled(); +} + +void NativeWidgetAura::OnMouseEvent(ui::MouseEvent* event) { + DCHECK(window_->IsVisible()); + if (event->type() == ui::ET_MOUSEWHEEL) { + delegate_->OnMouseEvent(event); + if (event->handled()) + return; + } + + if (tooltip_manager_.get()) + tooltip_manager_->UpdateTooltip(); + delegate_->OnMouseEvent(event); +} + +void NativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) { + delegate_->OnScrollEvent(event); +} + +void NativeWidgetAura::OnTouchEvent(ui::TouchEvent* event) { + DCHECK(window_->IsVisible()); + delegate_->OnTouchEvent(event); +} + +void NativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { + DCHECK(window_->IsVisible()); + delegate_->OnGestureEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, aura::client::ActivationDelegate implementation: + +bool NativeWidgetAura::ShouldActivate() const { + return can_activate_ && delegate_->CanActivate(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, aura::client::ActivationChangeObserver implementation: + +void NativeWidgetAura::OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) { + DCHECK(window_ == gained_active || window_ == lost_active); + if (GetWidget()->GetFocusManager()) { + if (window_ == gained_active) + GetWidget()->GetFocusManager()->RestoreFocusedView(); + else if (window_ == lost_active) + GetWidget()->GetFocusManager()->StoreFocusedView(true); + } + delegate_->OnNativeWidgetActivationChanged(window_ == gained_active); + if (IsVisible() && GetWidget()->non_client_view()) + GetWidget()->non_client_view()->SchedulePaint(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, aura::client::FocusChangeObserver: + +void NativeWidgetAura::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + if (window_ == gained_focus) { + // In aura, it is possible for child native widgets to take input and focus, + // this differs from the behavior on windows. + if (GetWidget()->GetInputMethod()) // Null in tests. + GetWidget()->GetInputMethod()->OnFocus(); + delegate_->OnNativeFocus(lost_focus); + } else if (window_ == lost_focus) { + // GetInputMethod() recreates the input method if it's previously been + // destroyed. If we get called during destruction, the input method will be + // gone, and creating a new one and telling it that we lost the focus will + // trigger a DCHECK (the new input method doesn't think that we have the + // focus and doesn't expect a blur). OnBlur() shouldn't be called during + // destruction unless WIDGET_OWNS_NATIVE_WIDGET is set (which is just the + // case in tests). + if (!destroying_) { + if (GetWidget()->GetInputMethod()) + GetWidget()->GetInputMethod()->OnBlur(); + } else { + DCHECK_EQ(ownership_, Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET); + } + + aura::client::FocusClient* client = aura::client::GetFocusClient(window_); + if (client) // NULL during destruction of aura::Window. + delegate_->OnNativeBlur(client->GetFocusedWindow()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, aura::WindowDragDropDelegate implementation: + +void NativeWidgetAura::OnDragEntered(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + last_drop_operation_ = drop_helper_->OnDragOver(event.data(), + event.location(), event.source_operations()); +} + +int NativeWidgetAura::OnDragUpdated(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + last_drop_operation_ = drop_helper_->OnDragOver(event.data(), + event.location(), event.source_operations()); + return last_drop_operation_; +} + +void NativeWidgetAura::OnDragExited() { + DCHECK(drop_helper_.get() != NULL); + drop_helper_->OnDragExit(); +} + +int NativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_.get() != NULL); + return drop_helper_->OnDrop(event.data(), event.location(), + last_drop_operation_); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, NativeWidget implementation: + +ui::EventHandler* NativeWidgetAura::GetEventHandler() { + return this; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, protected: + +NativeWidgetAura::~NativeWidgetAura() { + destroying_ = true; + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete delegate_; + else + CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetAura, private: + +void NativeWidgetAura::SetInitialFocus() { + // The window does not get keyboard messages unless we focus it. + if (!GetWidget()->SetInitialFocus()) + window_->Focus(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +// static +void Widget::NotifyLocaleChanged() { + // Deliberately not implemented. +} + +namespace { +void CloseWindow(aura::Window* window) { + if (window) { + Widget* widget = Widget::GetWidgetForNativeView(window); + if (widget && widget->is_secondary_widget()) + // To avoid the delay in shutdown caused by using Close which may wait + // for animations, use CloseNow. Because this is only used on secondary + // widgets it seems relatively safe to skip the extra processing of + // Close. + widget->CloseNow(); + } +} +#if defined(OS_WIN) +BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) { + aura::Window* root_window = + DesktopRootWindowHostWin::GetContentWindowForHWND(hwnd); + CloseWindow(root_window); + return TRUE; +} +#endif +} // namespace + +// static +void Widget::CloseAllSecondaryWidgets() { +#if defined(OS_WIN) + EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0); +#endif + +#if defined(USE_X11) && !defined(OS_CHROMEOS) + std::vector<aura::Window*> open_windows = + DesktopRootWindowHostX11::GetAllOpenWindows(); + std::for_each(open_windows.begin(), open_windows.end(), CloseWindow); + DesktopRootWindowHostX11::CleanUpWindowList(); +#endif +} + +bool Widget::ConvertRect(const Widget* source, + const Widget* target, + gfx::Rect* rect) { + return false; +} + +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// internal::NativeWidgetPrivate, public: + +// static +NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget( + internal::NativeWidgetDelegate* delegate) { + return new NativeWidgetAura(delegate); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + return reinterpret_cast<NativeWidgetPrivate*>(native_view->user_data()); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + return reinterpret_cast<NativeWidgetPrivate*>(native_window->user_data()); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( + gfx::NativeView native_view) { + aura::Window* window = native_view; + NativeWidgetPrivate* top_level_native_widget = NULL; + while (window) { + NativeWidgetPrivate* native_widget = GetNativeWidgetForNativeView(window); + if (native_widget) + top_level_native_widget = native_widget; + window = window->parent(); + } + return top_level_native_widget; +} + +// static +void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, + Widget::Widgets* children) { + { + // Code expects widget for |native_view| to be added to |children|. + NativeWidgetPrivate* native_widget = static_cast<NativeWidgetPrivate*>( + GetNativeWidgetForNativeView(native_view)); + if (native_widget && native_widget->GetWidget()) + children->insert(native_widget->GetWidget()); + } + + const aura::Window::Windows& child_windows = native_view->children(); + for (aura::Window::Windows::const_iterator i = child_windows.begin(); + i != child_windows.end(); ++i) { + NativeWidgetAura* native_widget = + static_cast<NativeWidgetAura*>(GetNativeWidgetForNativeView(*i)); + if (native_widget) + children->insert(native_widget->GetWidget()); + } +} + +// static +void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent) { + DCHECK(native_view != new_parent); + + gfx::NativeView previous_parent = native_view->parent(); + if (previous_parent == new_parent) + return; + + Widget::Widgets widgets; + GetAllChildWidgets(native_view, &widgets); + + // First notify all the widgets that they are being disassociated + // from their previous parent. + for (Widget::Widgets::iterator it = widgets.begin(); + it != widgets.end(); ++it) { + (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent); + } + + if (new_parent) { + new_parent->AddChild(native_view); + } else { + // The following looks weird, but it's the equivalent of what aura has + // always done. (The previous behaviour of aura::Window::SetParent() used + // NULL as a special value that meant ask the StackingClient where things + // should go.) + // + // This probably isn't strictly correct, but its an invariant that a Window + // in use will be attached to a RootWindow, so we can't just call + // RemoveChild here. The only possible thing that could assign a RootWindow + // in this case is the stacking client of the current RootWindow. This + // matches our previous behaviour; the global stacking client would almost + // always reattach the window to the same RootWindow. + aura::RootWindow* root_window = native_view->GetRootWindow(); + native_view->SetDefaultParentByRootWindow( + root_window, root_window->GetBoundsInScreen()); + } + + // And now, notify them that they have a brand new parent. + for (Widget::Widgets::iterator it = widgets.begin(); + it != widgets.end(); ++it) { + (*it)->NotifyNativeViewHierarchyChanged(true, new_parent); + } +} + +// static +bool NativeWidgetPrivate::IsMouseButtonDown() { + return aura::Env::GetInstance()->is_mouse_button_down(); +} + +// static +bool NativeWidgetPrivate::IsTouchDown() { + return aura::Env::GetInstance()->is_touch_down(); +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h new file mode 100644 index 00000000000..57271e81a98 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -0,0 +1,228 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_ + +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "ui/aura/client/activation_change_observer.h" +#include "ui/aura/client/activation_delegate.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/window_delegate.h" +#include "ui/base/events/event_constants.h" +#include "ui/views/ime/input_method_delegate.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/native_widget_private.h" + +namespace aura { +class Window; +} +namespace gfx { +class Font; +} + +namespace views { + +class DropHelper; +class NativeWidgetAuraWindowObserver; +class TooltipManagerAura; +class WindowReorderer; + +class VIEWS_EXPORT NativeWidgetAura + : public internal::NativeWidgetPrivate, + public internal::InputMethodDelegate, + public aura::WindowDelegate, + public aura::client::ActivationDelegate, + public aura::client::ActivationChangeObserver, + public aura::client::FocusChangeObserver, + public aura::client::DragDropDelegate { + public: + explicit NativeWidgetAura(internal::NativeWidgetDelegate* delegate); + + // TODO(beng): Find a better place for this, and the similar method on + // NativeWidgetWin. + static gfx::Font GetWindowTitleFont(); + + // Overridden from internal::NativeWidgetPrivate: + virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual void FrameTypeChanged() OVERRIDE; + virtual Widget* GetWidget() OVERRIDE; + virtual const Widget* GetWidget() const OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE; + virtual Widget* GetTopLevelWidget() OVERRIDE; + virtual const ui::Compositor* GetCompositor() const OVERRIDE; + virtual ui::Compositor* GetCompositor() OVERRIDE; + virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; + virtual void ViewRemoved(View* view) OVERRIDE; + virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; + virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; + virtual TooltipManager* GetTooltipManager() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual bool HasCapture() const OVERRIDE; + virtual InputMethod* CreateInputMethod() OVERRIDE; + virtual internal::InputMethodDelegate* GetInputMethodDelegate() OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* maximized) const OVERRIDE; + virtual void SetWindowTitle(const string16& title) OVERRIDE; + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) OVERRIDE; + virtual void InitModalType(ui::ModalType modal_type) OVERRIDE; + virtual gfx::Rect GetWindowBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetClientAreaBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetRestoredBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void StackAbove(gfx::NativeView native_view) OVERRIDE; + virtual void StackAtTop() OVERRIDE; + virtual void StackBelow(gfx::NativeView native_view) OVERRIDE; + virtual void SetShape(gfx::NativeRegion shape) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void CloseNow() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) OVERRIDE; + virtual void ShowWithWindowState(ui::WindowShowState state) OVERRIDE; + virtual bool IsVisible() const OVERRIDE; + virtual void Activate() OVERRIDE; + virtual void Deactivate() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual bool IsMaximized() const OVERRIDE; + virtual bool IsMinimized() const OVERRIDE; + virtual void Restore() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; + virtual void SetOpacity(unsigned char opacity) OVERRIDE; + virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE; + virtual void FlashFrame(bool flash_frame) OVERRIDE; + virtual void RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; + virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; + virtual bool IsMouseEventsEnabled() const OVERRIDE; + virtual void ClearNativeFocus() OVERRIDE; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE; + virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; + + // Overridden from views::InputMethodDelegate: + virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) OVERRIDE; + + // Overridden from aura::WindowDelegate: + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; + virtual void OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE; + virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE; + virtual bool ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) OVERRIDE; + virtual bool CanFocus() OVERRIDE; + virtual void OnCaptureLost() OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void OnWindowDestroying() OVERRIDE; + virtual void OnWindowDestroyed() OVERRIDE; + virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; + virtual bool HasHitTestMask() const OVERRIDE; + virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; + virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + + // Overridden from ui::EventHandler: + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; + virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + + // Overridden from aura::client::ActivationDelegate: + virtual bool ShouldActivate() const OVERRIDE; + + // Overridden from aura::client::ActivationChangeObserver: + virtual void OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) OVERRIDE; + + // Overridden from aura::client::FocusChangeObserver: + virtual void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) OVERRIDE; + + // Overridden from aura::client::DragDropDelegate: + virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE; + virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE; + virtual void OnDragExited() OVERRIDE; + virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; + + // Overridden from NativeWidget: + virtual ui::EventHandler* GetEventHandler() OVERRIDE; + + protected: + virtual ~NativeWidgetAura(); + + internal::NativeWidgetDelegate* delegate() { return delegate_; } + + private: + class ActiveWindowObserver; + + void SetInitialFocus(); + + internal::NativeWidgetDelegate* delegate_; + + aura::Window* window_; + + // See class documentation for Widget in widget.h for a note about ownership. + Widget::InitParams::Ownership ownership_; + + // The following factory is used for calls to close the NativeWidgetAura + // instance. + base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_; + + // Can we be made active? + bool can_activate_; + + // Are we in the destructor? + bool destroying_; + + gfx::NativeCursor cursor_; + + // The saved window state for exiting full screen state. + ui::WindowShowState saved_window_state_; + + scoped_ptr<TooltipManagerAura> tooltip_manager_; + + // Reorders child windows of |window_| associated with a view based on the + // order of the associated views in the widget's view hierarchy. + scoped_ptr<WindowReorderer> window_reorderer_; + + scoped_ptr<NativeWidgetAuraWindowObserver> active_window_observer_; + + scoped_ptr<DropHelper> drop_helper_; + int last_drop_operation_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetAura); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_ diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc new file mode 100644 index 00000000000..612616bc2c0 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc @@ -0,0 +1,412 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/native_widget_aura.h" + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/env.h" +#include "ui/aura/layout_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/test/aura_test_helper.h" +#include "ui/aura/window.h" +#include "ui/base/events/event.h" +#include "ui/gfx/screen.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { +namespace { + +NativeWidgetAura* Init(aura::Window* parent, Widget* widget) { + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.parent = parent; + widget->Init(params); + return static_cast<NativeWidgetAura*>(widget->native_widget()); +} + +class NativeWidgetAuraTest : public testing::Test { + public: + NativeWidgetAuraTest() {} + virtual ~NativeWidgetAuraTest() {} + + // testing::Test overrides: + virtual void SetUp() OVERRIDE { + aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); + aura_test_helper_->SetUp(); + root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); + root_window()->SetHostSize(gfx::Size(640, 480)); + } + virtual void TearDown() OVERRIDE { + message_loop_.RunUntilIdle(); + aura_test_helper_->TearDown(); + } + + protected: + aura::RootWindow* root_window() { return aura_test_helper_->root_window(); } + + private: + base::MessageLoopForUI message_loop_; + scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest); +}; + +TEST_F(NativeWidgetAuraTest, CenterWindowLargeParent) { + // Make a parent window larger than the host represented by rootwindow. + scoped_ptr<aura::Window> parent(new aura::Window(NULL)); + parent->Init(ui::LAYER_NOT_DRAWN); + parent->SetBounds(gfx::Rect(0, 0, 1024, 800)); + scoped_ptr<Widget> widget(new Widget()); + NativeWidgetAura* window = Init(parent.get(), widget.get()); + + window->CenterWindow(gfx::Size(100, 100)); + EXPECT_EQ(gfx::Rect( (640 - 100) / 2, + (480 - 100) / 2, + 100, 100), + window->GetNativeWindow()->bounds()); + widget->CloseNow(); +} + +TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) { + // Make a parent window smaller than the host represented by rootwindow. + scoped_ptr<aura::Window> parent(new aura::Window(NULL)); + parent->Init(ui::LAYER_NOT_DRAWN); + parent->SetBounds(gfx::Rect(0, 0, 480, 320)); + scoped_ptr<Widget> widget(new Widget()); + NativeWidgetAura* window = Init(parent.get(), widget.get()); + + window->CenterWindow(gfx::Size(100, 100)); + EXPECT_EQ(gfx::Rect( (480 - 100) / 2, + (320 - 100) / 2, + 100, 100), + window->GetNativeWindow()->bounds()); + widget->CloseNow(); +} + +// Verifies CenterWindow() constrains to parent size. +TEST_F(NativeWidgetAuraTest, CenterWindowSmallParentNotAtOrigin) { + // Make a parent window smaller than the host represented by rootwindow and + // offset it slightly from the origin. + scoped_ptr<aura::Window> parent(new aura::Window(NULL)); + parent->Init(ui::LAYER_NOT_DRAWN); + parent->SetBounds(gfx::Rect(20, 40, 480, 320)); + scoped_ptr<Widget> widget(new Widget()); + NativeWidgetAura* window = Init(parent.get(), widget.get()); + window->CenterWindow(gfx::Size(500, 600)); + + // |window| should be no bigger than |parent|. + EXPECT_EQ("20,40 480x320", window->GetNativeWindow()->bounds().ToString()); + widget->CloseNow(); +} + +// Used by ShowMaximizedDoesntBounceAround. See it for details. +class TestLayoutManager : public aura::LayoutManager { + public: + TestLayoutManager() {} + + virtual void OnWindowResized() OVERRIDE { + } + virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { + // This simulates what happens when adding a maximized window. + SetChildBoundsDirect(child, gfx::Rect(0, 0, 300, 300)); + } + virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE { + } + virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE { + } + virtual void OnChildWindowVisibilityChanged(aura::Window* child, + bool visible) OVERRIDE { + } + virtual void SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) OVERRIDE { + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestLayoutManager); +}; + +// This simulates BrowserView, which creates a custom RootView so that +// OnNativeWidgetSizeChanged that is invoked during Init matters. +class TestWidget : public views::Widget { + public: + TestWidget() : did_size_change_more_than_once_(false) { + } + + // Returns true if the size changes to a non-empty size, and then to another + // size. + bool did_size_change_more_than_once() const { + return did_size_change_more_than_once_; + } + + virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) OVERRIDE { + if (last_size_.IsEmpty()) + last_size_ = new_size; + else if (!did_size_change_more_than_once_ && new_size != last_size_) + did_size_change_more_than_once_ = true; + Widget::OnNativeWidgetSizeChanged(new_size); + } + + private: + bool did_size_change_more_than_once_; + gfx::Size last_size_; + + DISALLOW_COPY_AND_ASSIGN(TestWidget); +}; + +// Verifies the size of the widget doesn't change more than once during Init if +// the window ends up maximized. This is important as otherwise +// RenderWidgetHostViewAura ends up getting resized during construction, which +// leads to noticable flashes. +TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) { + root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); + root_window()->SetLayoutManager(new TestLayoutManager); + scoped_ptr<TestWidget> widget(new TestWidget()); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.parent = NULL; + params.context = root_window(); + params.show_state = ui::SHOW_STATE_MAXIMIZED; + params.bounds = gfx::Rect(10, 10, 100, 200); + widget->Init(params); + EXPECT_FALSE(widget->did_size_change_more_than_once()); + widget->CloseNow(); +} + +TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) { + // Create a widget. + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.context = root_window(); + params.bounds.SetRect(10, 20, 300, 400); + scoped_ptr<Widget> widget(new Widget()); + widget->Init(params); + + // For Aura, client area bounds match window bounds. + gfx::Rect client_bounds = widget->GetClientAreaBoundsInScreen(); + EXPECT_EQ(10, client_bounds.x()); + EXPECT_EQ(20, client_bounds.y()); + EXPECT_EQ(300, client_bounds.width()); + EXPECT_EQ(400, client_bounds.height()); +} + +namespace { + +// View subclass that tracks whether it has gotten a gesture event. +class GestureTrackingView : public views::View { + public: + GestureTrackingView() + : got_gesture_event_(false), + consume_gesture_event_(true) {} + + void set_consume_gesture_event(bool value) { + consume_gesture_event_ = value; + } + + void clear_got_gesture_event() { + got_gesture_event_ = false; + } + bool got_gesture_event() const { + return got_gesture_event_; + } + + // View overrides: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + got_gesture_event_ = true; + if (consume_gesture_event_) + event->StopPropagation(); + } + + private: + // Was OnGestureEvent() invoked? + bool got_gesture_event_; + + // Dictates what OnGestureEvent() returns. + bool consume_gesture_event_; + + DISALLOW_COPY_AND_ASSIGN(GestureTrackingView); +}; + +} // namespace + +// Verifies a capture isn't set on touch press and that the view that gets +// the press gets the release. +TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { + // Create two views (both sized the same). |child| is configured not to + // consume the gesture event. + GestureTrackingView* view = new GestureTrackingView(); + GestureTrackingView* child = new GestureTrackingView(); + child->set_consume_gesture_event(false); + view->SetLayoutManager(new FillLayout); + view->AddChildView(child); + scoped_ptr<TestWidget> widget(new TestWidget()); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.context = root_window(); + params.bounds = gfx::Rect(0, 0, 100, 200); + widget->Init(params); + widget->SetContentsView(view); + widget->Show(); + + ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), 1, + base::TimeDelta()); + root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press); + // Both views should get the press. + EXPECT_TRUE(view->got_gesture_event()); + EXPECT_TRUE(child->got_gesture_event()); + view->clear_got_gesture_event(); + child->clear_got_gesture_event(); + // Touch events should not automatically grab capture. + EXPECT_FALSE(widget->HasCapture()); + + // Release touch. Only |view| should get the release since that it consumed + // the press. + ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), 1, + base::TimeDelta()); + root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release); + EXPECT_TRUE(view->got_gesture_event()); + EXPECT_FALSE(child->got_gesture_event()); + view->clear_got_gesture_event(); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + widget->Close(); +} + +TEST_F(NativeWidgetAuraTest, ReleaseCaptureOnTouchRelease) { + GestureTrackingView* view = new GestureTrackingView(); + scoped_ptr<TestWidget> widget(new TestWidget()); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.context = root_window(); + params.bounds = gfx::Rect(0, 0, 100, 200); + widget->Init(params); + widget->SetContentsView(view); + widget->Show(); + + ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), 1, + base::TimeDelta()); + root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press); + EXPECT_TRUE(view->got_gesture_event()); + view->clear_got_gesture_event(); + // Set the capture. + widget->SetCapture(view); + EXPECT_TRUE(widget->HasCapture()); + + // Generate a release, this should trigger releasing capture. + ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(41, 51), 1, + base::TimeDelta()); + root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release); + EXPECT_TRUE(view->got_gesture_event()); + view->clear_got_gesture_event(); + EXPECT_FALSE(widget->HasCapture()); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + widget->Close(); +} + +// Verifies views with layers are targeted for events properly. +TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { + // Create two widgets: |parent| and |child|. |child| is a child of |parent|. + views::View* parent_root = new views::View; + scoped_ptr<Widget> parent(new Widget()); + Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + parent_params.ownership = + views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + parent_params.context = root_window(); + parent->Init(parent_params); + parent->SetContentsView(parent_root); + parent->SetBounds(gfx::Rect(0, 0, 400, 400)); + parent->Show(); + + scoped_ptr<Widget> child(new Widget()); + Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL); + child_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + child_params.parent = parent->GetNativeWindow(); + child->Init(child_params); + child->SetBounds(gfx::Rect(0, 0, 200, 200)); + child->Show(); + + // Point is over |child|. + EXPECT_EQ(child->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(50, 50))); + + // Create a view with a layer and stack it at the bottom (below |child|). + views::View* view_with_layer = new views::View; + parent_root->AddChildView(view_with_layer); + view_with_layer->SetBounds(0, 0, 50, 50); + view_with_layer->SetPaintToLayer(true); + + // Make sure that |child| still gets the event. + EXPECT_EQ(child->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(20, 20))); + + // Move |view_with_layer| to the top and make sure it gets the + // event when the point is within |view_with_layer|'s bounds. + view_with_layer->layer()->parent()->StackAtTop( + view_with_layer->layer()); + EXPECT_EQ(parent->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(20, 20))); + + // Point is over |child|, it should get the event. + EXPECT_EQ(child->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(70, 70))); + + delete view_with_layer; + view_with_layer = NULL; + + EXPECT_EQ(child->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(20, 20))); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +// Verifies that widget->FlashFrame() sets aura::client::kDrawAttentionKey, +// and activating the window clears it. +TEST_F(NativeWidgetAuraTest, FlashFrame) { + scoped_ptr<Widget> widget(new Widget()); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.context = root_window(); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + aura::Window* window = widget->GetNativeWindow(); + EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); + widget->FlashFrame(true); + EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey)); + widget->FlashFrame(false); + EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); + widget->FlashFrame(true); + EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey)); + widget->Activate(); + EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); +} + +TEST_F(NativeWidgetAuraTest, NoCrashOnThemeAfterClose) { + scoped_ptr<aura::Window> parent(new aura::Window(NULL)); + parent->Init(ui::LAYER_NOT_DRAWN); + parent->SetBounds(gfx::Rect(0, 0, 480, 320)); + scoped_ptr<Widget> widget(new Widget()); + NativeWidgetAura* window = Init(parent.get(), widget.get()); + window->Show(); + window->Close(); + base::MessageLoop::current()->RunUntilIdle(); + widget->GetNativeTheme(); // Shouldn't crash. +} + +} // namespace +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_aura_window_observer.cc b/chromium/ui/views/widget/native_widget_aura_window_observer.cc new file mode 100644 index 00000000000..3be33dd818c --- /dev/null +++ b/chromium/ui/views/widget/native_widget_aura_window_observer.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/native_widget_aura_window_observer.h" + +#include "ui/aura/client/activation_client.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" + +namespace views { + +NativeWidgetAuraWindowObserver::NativeWidgetAuraWindowObserver( + gfx::NativeView native_view, + internal::NativeWidgetDelegate* delegate) + : native_view_(native_view), + delegate_(delegate) { + native_view_->GetRootWindow()->AddObserver(this); + native_view_->AddObserver(this); + aura::client::GetActivationClient(native_view_->GetRootWindow())-> + AddObserver(this); +} + +NativeWidgetAuraWindowObserver::~NativeWidgetAuraWindowObserver() { + CleanUpObservers(); +} + +void NativeWidgetAuraWindowObserver::OnWindowActivated( + aura::Window* gained_active, + aura::Window* lost_active) { + if (!gained_active || gained_active->transient_parent() != native_view_) + delegate_->EnableInactiveRendering(); +} + +void NativeWidgetAuraWindowObserver::OnWindowRemovingFromRootWindow( + aura::Window* window) { + if (window != native_view_) + return; + CleanUpObservers(); +} + +void NativeWidgetAuraWindowObserver::CleanUpObservers() { + if (!native_view_) + return; + native_view_->GetRootWindow()->RemoveObserver(this); + native_view_->RemoveObserver(this); + aura::client::GetActivationClient(native_view_->GetRootWindow())-> + RemoveObserver(this); + native_view_ = NULL; +} + +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_aura_window_observer.h b/chromium/ui/views/widget/native_widget_aura_window_observer.h new file mode 100644 index 00000000000..70a6037b3a9 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_aura_window_observer.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_WINDOW_OBSERVER_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_WINDOW_OBSERVER_H_ + +#include "base/compiler_specific.h" +#include "ui/aura/client/activation_change_observer.h" +#include "ui/aura/window_observer.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/widget/native_widget_private.h" + +namespace views { + +// A helper class shared between NativeWidgetAura implementations. Used when +// SetInactiveRenderingDisabled() is invoked to track when active status +// changes in such a way that we should enable inactive rendering. +class NativeWidgetAuraWindowObserver + : public aura::WindowObserver, + public aura::client::ActivationChangeObserver { + public: + explicit NativeWidgetAuraWindowObserver( + gfx::NativeView native_view, + internal::NativeWidgetDelegate* delegate); + virtual ~NativeWidgetAuraWindowObserver(); + + // Overridden from aura::client::ActivationChangeObserver: + virtual void OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) OVERRIDE; + + // Overridden from aura::WindowObserver: + virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE; + + private: + void CleanUpObservers(); + + gfx::NativeView native_view_; + internal::NativeWidgetDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraWindowObserver); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_WINDOW_OBSERVER_H_ diff --git a/chromium/ui/views/widget/native_widget_delegate.h b/chromium/ui/views/widget/native_widget_delegate.h new file mode 100644 index 00000000000..c1fd073370e --- /dev/null +++ b/chromium/ui/views/widget/native_widget_delegate.h @@ -0,0 +1,145 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_ + +#include <vector> + +#include "ui/base/events/event_constants.h" +#include "ui/views/views_export.h" + +namespace gfx { +class Canvas; +class Path; +class Point; +class Size; +} + +namespace ui { +class GestureEvent; +class KeyEvent; +class Layer; +class MouseEvent; +class TouchEvent; +class ScrollEvent; +} + +namespace views { +class InputMethod; +class Widget; + +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetDelegate +// +// An interface implemented by the object that handles events sent by a +// NativeWidget implementation. +// +class VIEWS_EXPORT NativeWidgetDelegate { + public: + virtual ~NativeWidgetDelegate() {} + + // Returns true if the window is modal. + virtual bool IsModal() const = 0; + + // Returns true if the window is a dialog box. + virtual bool IsDialogBox() const = 0; + + // Returns true if the window can be activated. + virtual bool CanActivate() const = 0; + + virtual bool IsInactiveRenderingDisabled() const = 0; + virtual void EnableInactiveRendering() = 0; + + // Called when the activation state of a window has changed. + virtual void OnNativeWidgetActivationChanged(bool active) = 0; + + // Called when native focus moves from one native view to another. + virtual void OnNativeFocus(gfx::NativeView focused_view) = 0; + virtual void OnNativeBlur(gfx::NativeView focused_view) = 0; + + // Called when the window is shown/hidden. + virtual void OnNativeWidgetVisibilityChanged(bool visible) = 0; + + // Called when the native widget is created. + // The |desktop_widget| bool is true for widgets created in the desktop and + // false for widgets created in the shell. + virtual void OnNativeWidgetCreated(bool desktop_widget) = 0; + + // Called just before the native widget is destroyed. This is the delegate's + // last chance to do anything with the native widget handle. + virtual void OnNativeWidgetDestroying() = 0; + + // Called just after the native widget is destroyed. + virtual void OnNativeWidgetDestroyed() = 0; + + // Returns the smallest size the window can be resized to by the user. + virtual gfx::Size GetMinimumSize() = 0; + + // Returns the largest size the window can be resized to by the user. + virtual gfx::Size GetMaximumSize() = 0; + + // Called when the NativeWidget changed position. + virtual void OnNativeWidgetMove() = 0; + + // Called when the NativeWidget changed size to |new_size|. + virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) = 0; + + // Called when the user begins/ends to change the bounds of the window. + virtual void OnNativeWidgetBeginUserBoundsChange() = 0; + virtual void OnNativeWidgetEndUserBoundsChange() = 0; + + // Returns true if the delegate has a FocusManager. + virtual bool HasFocusManager() const = 0; + + // Paints the widget using acceleration. If the widget is not using + // accelerated painting this returns false and does nothing. + virtual bool OnNativeWidgetPaintAccelerated( + const gfx::Rect& dirty_region) = 0; + + // Paints the rootview in the canvas. This will also refresh the compositor + // tree if necessary when accelerated painting is enabled. + virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) = 0; + + // Returns the non-client component (see ui/base/hit_test.h) containing + // |point|, in client coordinates. + virtual int GetNonClientComponent(const gfx::Point& point) = 0; + + // Mouse and key event handlers. + virtual void OnKeyEvent(ui::KeyEvent* event) = 0; + virtual void OnMouseEvent(ui::MouseEvent* event) = 0; + virtual void OnMouseCaptureLost() = 0; + + virtual void OnTouchEvent(ui::TouchEvent* event) = 0; + virtual void OnScrollEvent(ui::ScrollEvent* event) = 0; + virtual void OnGestureEvent(ui::GestureEvent* event) = 0; + + // Runs the specified native command. Returns true if the command is handled. + virtual bool ExecuteCommand(int command_id) = 0; + + // Returns the input method of the widget this delegate is associated with. + // Note that this does not use the top level widget, so may return NULL + // if the widget doesn't have input method. + virtual InputMethod* GetInputMethodDirect() = 0; + + // Returns the child Layers of the Widgets layer that were created by Views. + virtual const std::vector<ui::Layer*>& GetRootLayers() = 0; + + // Returns true if window has a hit-test mask. + virtual bool HasHitTestMask() const = 0; + + // Provides the hit-test mask if HasHitTestMask above returns true. + virtual void GetHitTestMask(gfx::Path* mask) const = 0; + + // + virtual Widget* AsWidget() = 0; + virtual const Widget* AsWidget() const = 0; +}; + +} // namespace internal +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_ diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h new file mode 100644 index 00000000000..3bdd6810cfc --- /dev/null +++ b/chromium/ui/views/widget/native_widget_private.h @@ -0,0 +1,225 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_ + +#include "base/strings/string16.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/ime/input_method_delegate.h" +#include "ui/views/widget/native_widget.h" + +namespace gfx { +class ImageSkia; +class Rect; +} + +namespace ui { +class NativeTheme; +class OSExchangeData; +} + +namespace views { +class InputMethod; +class TooltipManager; +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetPrivate interface +// +// A NativeWidget subclass internal to views that provides Widget a conduit for +// communication with a backend-specific native widget implementation. +// +// Many of the methods here are pass-thrus for Widget, and as such there is no +// documentation for them here. In that case, see methods of the same name in +// widget.h. +// +// IMPORTANT: This type is intended for use only by the views system and for +// NativeWidget implementations. This file should not be included +// in code that does not fall into one of these use cases. +// +class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { + public: + virtual ~NativeWidgetPrivate() {} + + // Creates an appropriate default NativeWidgetPrivate implementation for the + // current OS/circumstance. + static NativeWidgetPrivate* CreateNativeWidget( + internal::NativeWidgetDelegate* delegate); + + static NativeWidgetPrivate* GetNativeWidgetForNativeView( + gfx::NativeView native_view); + static NativeWidgetPrivate* GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window); + + // Retrieves the top NativeWidgetPrivate in the hierarchy containing the given + // NativeView, or NULL if there is no NativeWidgetPrivate that contains it. + static NativeWidgetPrivate* GetTopLevelNativeWidget( + gfx::NativeView native_view); + + static void GetAllChildWidgets(gfx::NativeView native_view, + Widget::Widgets* children); + static void ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent); + + // Returns true if any mouse button is currently down. + static bool IsMouseButtonDown(); + + // Returns true if any touch device is currently down. + static bool IsTouchDown(); + + // Initializes the NativeWidget. + virtual void InitNativeWidget(const Widget::InitParams& params) = 0; + + // Returns a NonClientFrameView for the widget's NonClientView, or NULL if + // the NativeWidget wants no special NonClientFrameView. + virtual NonClientFrameView* CreateNonClientFrameView() = 0; + + virtual bool ShouldUseNativeFrame() const = 0; + virtual void FrameTypeChanged() = 0; + + // Returns the Widget associated with this NativeWidget. This function is + // guaranteed to return non-NULL for the lifetime of the NativeWidget. + virtual Widget* GetWidget() = 0; + virtual const Widget* GetWidget() const = 0; + + // Returns the NativeView/Window associated with this NativeWidget. + virtual gfx::NativeView GetNativeView() const = 0; + virtual gfx::NativeWindow GetNativeWindow() const = 0; + + // Returns the topmost Widget in a hierarchy. + virtual Widget* GetTopLevelWidget() = 0; + + // Returns the Compositor, or NULL if there isn't one associated with this + // NativeWidget. + virtual const ui::Compositor* GetCompositor() const = 0; + virtual ui::Compositor* GetCompositor() = 0; + + // Returns the NativeWidget's layer, if any. + virtual ui::Layer* GetLayer() = 0; + + // Reorders the widget's child NativeViews which are associated to the view + // tree (eg via a NativeViewHost) to match the z-order of the views in the + // view tree. The z-order of views with layers relative to views with + // associated NativeViews is used to reorder the NativeView layers. This + // method assumes that the widget's child layers which are owned by a view are + // already in the correct z-order relative to each other and does no + // reordering if there are no views with an associated NativeView. + virtual void ReorderNativeViews() = 0; + + // Notifies the NativeWidget that a view was removed from the Widget's view + // hierarchy. + virtual void ViewRemoved(View* view) = 0; + + // Sets/Gets a native window property on the underlying native window object. + // Returns NULL if the property does not exist. Setting the property value to + // NULL removes the property. + virtual void SetNativeWindowProperty(const char* name, void* value) = 0; + virtual void* GetNativeWindowProperty(const char* name) const = 0; + + // Returns the native widget's tooltip manager. Called from the View hierarchy + // to update tooltips. + virtual TooltipManager* GetTooltipManager() const = 0; + + // Sets or releases event capturing for this native widget. + virtual void SetCapture() = 0; + virtual void ReleaseCapture() = 0; + + // Returns true if this native widget is capturing events. + virtual bool HasCapture() const = 0; + + // Returns the InputMethod for this native widget. + // Note that all widgets in a widget hierarchy share the same input method. + // TODO(suzhe): rename to GetInputMethod() when NativeWidget implementation + // class doesn't inherit Widget anymore. + virtual InputMethod* CreateInputMethod() = 0; + + // Returns the InputMethodDelegate for this native widget. + virtual InputMethodDelegate* GetInputMethodDelegate() = 0; + + + // Centers the window and sizes it to the specified size. + virtual void CenterWindow(const gfx::Size& size) = 0; + + // Retrieves the window's current restored bounds and "show" state, for + // persisting. + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const = 0; + + // Sets the NativeWindow title. + virtual void SetWindowTitle(const string16& title) = 0; + + // Sets the Window icons. |window_icon| is a 16x16 icon suitable for use in + // a title bar. |app_icon| is a larger size for use in the host environment + // app switching UI. + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) = 0; + + // Initializes the modal type of the window to |modal_type|. Called from + // NativeWidgetDelegate::OnNativeWidgetCreated() before the widget is + // initially parented. + virtual void InitModalType(ui::ModalType modal_type) = 0; + + // See method documentation in Widget. + virtual gfx::Rect GetWindowBoundsInScreen() const = 0; + virtual gfx::Rect GetClientAreaBoundsInScreen() const = 0; + virtual gfx::Rect GetRestoredBounds() const = 0; + virtual void SetBounds(const gfx::Rect& bounds) = 0; + virtual void SetSize(const gfx::Size& size) = 0; + virtual void StackAbove(gfx::NativeView native_view) = 0; + virtual void StackAtTop() = 0; + virtual void StackBelow(gfx::NativeView native_view) = 0; + virtual void SetShape(gfx::NativeRegion shape) = 0; + virtual void Close() = 0; + virtual void CloseNow() = 0; + virtual void Show() = 0; + virtual void Hide() = 0; + // Invoked if the initial show should maximize the window. |restored_bounds| + // is the bounds of the window when not maximized. + virtual void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) = 0; + virtual void ShowWithWindowState(ui::WindowShowState show_state) = 0; + virtual bool IsVisible() const = 0; + virtual void Activate() = 0; + virtual void Deactivate() = 0; + virtual bool IsActive() const = 0; + virtual void SetAlwaysOnTop(bool always_on_top) = 0; + virtual void Maximize() = 0; + virtual void Minimize() = 0; + virtual bool IsMaximized() const = 0; + virtual bool IsMinimized() const = 0; + virtual void Restore() = 0; + virtual void SetFullscreen(bool fullscreen) = 0; + virtual bool IsFullscreen() const = 0; + virtual void SetOpacity(unsigned char opacity) = 0; + virtual void SetUseDragFrame(bool use_drag_frame) = 0; + virtual void FlashFrame(bool flash) = 0; + virtual void RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) = 0; + virtual void SchedulePaintInRect(const gfx::Rect& rect) = 0; + virtual void SetCursor(gfx::NativeCursor cursor) = 0; + virtual bool IsMouseEventsEnabled() const = 0; + virtual void ClearNativeFocus() = 0; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const = 0; + virtual void SetInactiveRenderingDisabled(bool value) = 0; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) = 0; + virtual void EndMoveLoop() = 0; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) = 0; + virtual ui::NativeTheme* GetNativeTheme() const = 0; + + // Overridden from NativeWidget: + virtual internal::NativeWidgetPrivate* AsNativeWidgetPrivate() OVERRIDE; + virtual ui::EventHandler* GetEventHandler() = 0; +}; + +} // namespace internal +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_ diff --git a/chromium/ui/views/widget/native_widget_unittest.cc b/chromium/ui/views/widget/native_widget_unittest.cc new file mode 100644 index 00000000000..133f8be04a5 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/native/native_view_host.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/view.h" +#include "ui/views/widget/native_widget_private.h" +#include "ui/views/widget/widget.h" + +namespace views { + +class ScopedTestWidget { + public: + explicit ScopedTestWidget(internal::NativeWidgetPrivate* native_widget) + : native_widget_(native_widget) { + } + ~ScopedTestWidget() { + // |CloseNow| deletes both |native_widget_| and its associated + // |Widget|. + native_widget_->GetWidget()->CloseNow(); + } + + internal::NativeWidgetPrivate* operator->() const { + return native_widget_; + } + internal::NativeWidgetPrivate* get() const { return native_widget_; } + + private: + internal::NativeWidgetPrivate* native_widget_; + DISALLOW_COPY_AND_ASSIGN(ScopedTestWidget); +}; + +class NativeWidgetTest : public ViewsTestBase { + public: + NativeWidgetTest() {} + virtual ~NativeWidgetTest() {} + + internal::NativeWidgetPrivate* CreateNativeWidgetOfType( + Widget::InitParams::Type type) { + Widget* widget = new Widget; + Widget::InitParams params = CreateParams(type); + params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET; + params.child = false; // Implicitly set to true by ctor with TYPE_CONTROL. + params.bounds = gfx::Rect(10, 10, 200, 200); + widget->Init(params); + return widget->native_widget_private(); + } + + internal::NativeWidgetPrivate* CreateNativeWidget() { + return CreateNativeWidgetOfType(Widget::InitParams::TYPE_POPUP); + } + + internal::NativeWidgetPrivate* CreateNativeSubWidget() { + return CreateNativeWidgetOfType(Widget::InitParams::TYPE_CONTROL); + } + + private: + DISALLOW_COPY_AND_ASSIGN(NativeWidgetTest); +}; + +TEST_F(NativeWidgetTest, CreateNativeWidget) { + ScopedTestWidget widget(CreateNativeWidget()); + EXPECT_TRUE(widget->GetWidget()->GetNativeView() != NULL); +} + +TEST_F(NativeWidgetTest, GetNativeWidgetForNativeView) { + ScopedTestWidget widget(CreateNativeWidget()); + EXPECT_EQ(widget.get(), + internal::NativeWidgetPrivate::GetNativeWidgetForNativeView( + widget->GetWidget()->GetNativeView())); +} + +// |widget| has the toplevel NativeWidget. +TEST_F(NativeWidgetTest, GetTopLevelNativeWidget1) { + ScopedTestWidget widget(CreateNativeWidget()); + EXPECT_EQ(widget.get(), + internal::NativeWidgetPrivate::GetTopLevelNativeWidget( + widget->GetWidget()->GetNativeView())); +} + +// |toplevel_widget| has the toplevel NativeWidget. +TEST_F(NativeWidgetTest, GetTopLevelNativeWidget2) { + ScopedTestWidget toplevel_widget(CreateNativeWidget()); + + // |toplevel_widget| owns |child_host|. + NativeViewHost* child_host = new NativeViewHost; + toplevel_widget->GetWidget()->SetContentsView(child_host); + + // |child_host| owns |child_widget|. + internal::NativeWidgetPrivate* child_widget = CreateNativeSubWidget(); + child_host->Attach(child_widget->GetWidget()->GetNativeView()); + + EXPECT_EQ(toplevel_widget.get(), + internal::NativeWidgetPrivate::GetTopLevelNativeWidget( + child_widget->GetWidget()->GetNativeView())); +} + +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_win.cc b/chromium/ui/views/widget/native_widget_win.cc new file mode 100644 index 00000000000..d6ebdd6d664 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_win.cc @@ -0,0 +1,1040 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/native_widget_win.h" + +#include <dwmapi.h> +#include <shellapi.h> + +#include <algorithm> + +#include "base/bind.h" +#include "base/strings/string_util.h" +#include "base/win/scoped_gdi_object.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drag_source_win.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/base/events/event.h" +#include "ui/base/ime/input_method_factory.h" +#include "ui/base/keycodes/keyboard_code_conversion_win.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/theme_provider.h" +#include "ui/base/view_prop.h" +#include "ui/base/win/dpi.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/base/win/mouse_wheel_util.h" +#include "ui/base/win/shell.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia_paint.h" +#include "ui/gfx/path.h" +#include "ui/gfx/point_conversions.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/size_conversions.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/controls/native_control_win.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/drag_utils.h" +#include "ui/views/focus/accelerator_handler.h" +#include "ui/views/focus/view_storage.h" +#include "ui/views/focus/widget_focus_manager.h" +#include "ui/views/ime/input_method_bridge.h" +#include "ui/views/widget/aero_tooltip_manager.h" +#include "ui/views/widget/drop_target_win.h" +#include "ui/views/widget/monitor_win.h" +#include "ui/views/widget/native_widget_delegate.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_hwnd_utils.h" +#include "ui/views/win/fullscreen_handler.h" +#include "ui/views/win/hwnd_message_handler.h" +#include "ui/views/window/native_frame_view.h" + +#pragma comment(lib, "dwmapi.lib") + +using ui::ViewProp; + +namespace views { + +namespace { + +// Enumeration callback for NativeWidget::GetAllChildWidgets(). Called for each +// child HWND beneath the original HWND. +BOOL CALLBACK EnumerateChildWindowsForNativeWidgets(HWND hwnd, LPARAM l_param) { + Widget* widget = Widget::GetWidgetForNativeView(hwnd); + if (widget) { + Widget::Widgets* widgets = reinterpret_cast<Widget::Widgets*>(l_param); + widgets->insert(widget); + } + return TRUE; +} + +// Links the HWND to its NativeWidget. +const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__"; + +const int kDragFrameWindowAlpha = 200; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, public: + +NativeWidgetWin::NativeWidgetWin(internal::NativeWidgetDelegate* delegate) + : delegate_(delegate), + ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), + drag_frame_saved_window_style_(0), + drag_frame_saved_window_ex_style_(0), + has_non_client_view_(false), + message_handler_(new HWNDMessageHandler(this)) { +} + +NativeWidgetWin::~NativeWidgetWin() { + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete delegate_; + else + CloseNow(); + message_handler_.reset(); +} + +// static +gfx::Font NativeWidgetWin::GetWindowTitleFont() { + NONCLIENTMETRICS ncm; + base::win::GetNonClientMetrics(&ncm); + l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); + base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); + return gfx::Font(caption_font); +} + +void NativeWidgetWin::Show(int show_state) { + message_handler_->Show(show_state); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, NativeWidget implementation: + +void NativeWidgetWin::InitNativeWidget(const Widget::InitParams& params) { + gfx::Rect pixel_bounds = ui::win::DIPToScreenRect(params.bounds); + Widget::InitParams params_in_pixel(params); + params_in_pixel.bounds = pixel_bounds; + SetInitParams(params_in_pixel); + message_handler_->Init(params.parent, pixel_bounds); +} + +NonClientFrameView* NativeWidgetWin::CreateNonClientFrameView() { + return GetWidget()->ShouldUseNativeFrame() ? + new NativeFrameView(GetWidget()) : NULL; +} + +bool NativeWidgetWin::ShouldUseNativeFrame() const { + return ui::win::IsAeroGlassEnabled(); +} + +void NativeWidgetWin::FrameTypeChanged() { + message_handler_->FrameTypeChanged(); +} + +Widget* NativeWidgetWin::GetWidget() { + return delegate_->AsWidget(); +} + +const Widget* NativeWidgetWin::GetWidget() const { + return delegate_->AsWidget(); +} + +gfx::NativeView NativeWidgetWin::GetNativeView() const { + return message_handler_->hwnd(); +} + +gfx::NativeWindow NativeWidgetWin::GetNativeWindow() const { + return message_handler_->hwnd(); +} + +Widget* NativeWidgetWin::GetTopLevelWidget() { + NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView()); + return native_widget ? native_widget->GetWidget() : NULL; +} + +const ui::Compositor* NativeWidgetWin::GetCompositor() const { + return NULL; +} + +ui::Compositor* NativeWidgetWin::GetCompositor() { + return NULL; +} + +ui::Layer* NativeWidgetWin::GetLayer() { + return NULL; +} + +void NativeWidgetWin::ReorderNativeViews() { +} + +void NativeWidgetWin::ViewRemoved(View* view) { + if (drop_target_.get()) + drop_target_->ResetTargetViewIfEquals(view); +} + +void NativeWidgetWin::SetNativeWindowProperty(const char* name, void* value) { + // Remove the existing property (if any). + for (ViewProps::iterator i = props_.begin(); i != props_.end(); ++i) { + if ((*i)->Key() == name) { + props_.erase(i); + break; + } + } + + if (value) + props_.push_back(new ViewProp(GetNativeView(), name, value)); +} + +void* NativeWidgetWin::GetNativeWindowProperty(const char* name) const { + return ViewProp::GetValue(GetNativeView(), name); +} + +TooltipManager* NativeWidgetWin::GetTooltipManager() const { + return tooltip_manager_.get(); +} + +void NativeWidgetWin::SetCapture() { + message_handler_->SetCapture(); +} + +void NativeWidgetWin::ReleaseCapture() { + message_handler_->ReleaseCapture(); +} + +bool NativeWidgetWin::HasCapture() const { + return message_handler_->HasCapture(); +} + +InputMethod* NativeWidgetWin::CreateInputMethod() { + return new InputMethodBridge(GetMessageHandler(), ui::GetSharedInputMethod(), + true); +} + +internal::InputMethodDelegate* NativeWidgetWin::GetInputMethodDelegate() { + return message_handler_.get(); +} + +void NativeWidgetWin::CenterWindow(const gfx::Size& size) { + gfx::Size size_in_pixels = ui::win::DIPToScreenSize(size); + message_handler_->CenterWindow(size_in_pixels); +} + +void NativeWidgetWin::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + message_handler_->GetWindowPlacement(bounds, show_state); + *bounds = ui::win::ScreenToDIPRect(*bounds); +} + +void NativeWidgetWin::SetWindowTitle(const string16& title) { + message_handler_->SetTitle(title); +} + +void NativeWidgetWin::SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + message_handler_->SetWindowIcons(window_icon, app_icon); +} + +void NativeWidgetWin::InitModalType(ui::ModalType modal_type) { + message_handler_->InitModalType(modal_type); +} + +gfx::Rect NativeWidgetWin::GetWindowBoundsInScreen() const { + gfx::Rect bounds_in_pixels = message_handler_->GetWindowBoundsInScreen(); + return ui::win::ScreenToDIPRect(bounds_in_pixels); +} + +gfx::Rect NativeWidgetWin::GetClientAreaBoundsInScreen() const { + gfx::Rect bounds_in_pixels = message_handler_->GetClientAreaBoundsInScreen(); + return ui::win::ScreenToDIPRect(bounds_in_pixels); +} + +gfx::Rect NativeWidgetWin::GetRestoredBounds() const { + gfx::Rect bounds_in_pixels = message_handler_->GetRestoredBounds(); + return ui::win::ScreenToDIPRect(bounds_in_pixels); +} + +void NativeWidgetWin::SetBounds(const gfx::Rect& bounds) { + float scale = ui::win::GetDeviceScaleFactor(); + gfx::Rect bounds_in_pixels( + gfx::ToCeiledPoint(gfx::ScalePoint(bounds.origin(), scale)), + gfx::ToFlooredSize(gfx::ScaleSize(bounds.size(), scale))); + message_handler_->SetBounds(bounds_in_pixels); +} + +void NativeWidgetWin::SetSize(const gfx::Size& size) { + message_handler_->SetSize(size); +} + +void NativeWidgetWin::StackAbove(gfx::NativeView native_view) { + message_handler_->StackAbove(native_view); +} + +void NativeWidgetWin::StackAtTop() { + message_handler_->StackAtTop(); +} + +void NativeWidgetWin::StackBelow(gfx::NativeView native_view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWin::SetShape(gfx::NativeRegion region) { + message_handler_->SetRegion(region); +} + +void NativeWidgetWin::Close() { + message_handler_->Close(); +} + +void NativeWidgetWin::CloseNow() { + message_handler_->CloseNow(); +} + +void NativeWidgetWin::Show() { + message_handler_->Show(); +} + +void NativeWidgetWin::Hide() { + message_handler_->Hide(); +} + +void NativeWidgetWin::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + gfx::Rect pixel_bounds = ui::win::DIPToScreenRect(restored_bounds); + message_handler_->ShowMaximizedWithBounds(pixel_bounds); +} + +void NativeWidgetWin::ShowWithWindowState(ui::WindowShowState show_state) { + message_handler_->ShowWindowWithState(show_state); +} + +bool NativeWidgetWin::IsVisible() const { + return message_handler_->IsVisible(); +} + +void NativeWidgetWin::Activate() { + message_handler_->Activate(); +} + +void NativeWidgetWin::Deactivate() { + message_handler_->Deactivate(); +} + +bool NativeWidgetWin::IsActive() const { + return message_handler_->IsActive(); +} + +void NativeWidgetWin::SetAlwaysOnTop(bool on_top) { + message_handler_->SetAlwaysOnTop(on_top); +} + +void NativeWidgetWin::Maximize() { + message_handler_->Maximize(); +} + +void NativeWidgetWin::Minimize() { + message_handler_->Minimize(); +} + +bool NativeWidgetWin::IsMaximized() const { + return message_handler_->IsMaximized(); +} + +bool NativeWidgetWin::IsMinimized() const { + return message_handler_->IsMinimized(); +} + +void NativeWidgetWin::Restore() { + message_handler_->Restore(); +} + +void NativeWidgetWin::SetFullscreen(bool fullscreen) { + message_handler_->fullscreen_handler()->SetFullscreen(fullscreen); +} + +void NativeWidgetWin::SetMetroSnapFullscreen(bool metro_snap) { + message_handler_->fullscreen_handler()->SetMetroSnap(metro_snap); +} + +bool NativeWidgetWin::IsFullscreen() const { + return message_handler_->fullscreen_handler()->fullscreen(); +} + +bool NativeWidgetWin::IsInMetroSnapMode() const { + return message_handler_->fullscreen_handler()->metro_snap(); +} + +void NativeWidgetWin::SetCanUpdateLayeredWindow(bool can_update) { + message_handler_->set_can_update_layered_window(can_update); +} + +void NativeWidgetWin::SetOpacity(unsigned char opacity) { + message_handler_->SetOpacity(static_cast<BYTE>(opacity)); + GetWidget()->GetRootView()->SchedulePaint(); +} + +void NativeWidgetWin::SetUseDragFrame(bool use_drag_frame) { + if (use_drag_frame) { + // Make the frame slightly transparent during the drag operation. + drag_frame_saved_window_style_ = GetWindowLong(GetNativeView(), GWL_STYLE); + drag_frame_saved_window_ex_style_ = + GetWindowLong(GetNativeView(), GWL_EXSTYLE); + SetWindowLong(GetNativeView(), GWL_EXSTYLE, + drag_frame_saved_window_ex_style_ | WS_EX_LAYERED); + // Remove the captions tyle so the window doesn't have window controls for a + // more "transparent" look. + SetWindowLong(GetNativeView(), GWL_STYLE, + drag_frame_saved_window_style_ & ~WS_CAPTION); + SetLayeredWindowAttributes(GetNativeView(), RGB(0xFF, 0xFF, 0xFF), + kDragFrameWindowAlpha, LWA_ALPHA); + } else { + SetWindowLong(GetNativeView(), GWL_STYLE, drag_frame_saved_window_style_); + SetWindowLong(GetNativeView(), GWL_EXSTYLE, + drag_frame_saved_window_ex_style_); + } +} + +void NativeWidgetWin::FlashFrame(bool flash) { + message_handler_->FlashFrame(flash); +} + +void NativeWidgetWin::RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + views::RunShellDrag(NULL, data, location, operation, source); +} + +void NativeWidgetWin::SchedulePaintInRect(const gfx::Rect& rect) { + gfx::Rect pixel_rect = ui::win::DIPToScreenRect(rect); + message_handler_->SchedulePaintInRect(pixel_rect); +} + +void NativeWidgetWin::SetCursor(gfx::NativeCursor cursor) { + message_handler_->SetCursor(cursor); +} + +bool NativeWidgetWin::IsMouseEventsEnabled() const { + return true; +} + +void NativeWidgetWin::ClearNativeFocus() { + message_handler_->ClearNativeFocus(); +} + +gfx::Rect NativeWidgetWin::GetWorkAreaBoundsInScreen() const { + return ui::win::ScreenToDIPRect( + gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow( + GetNativeView()).work_area()); +} + +void NativeWidgetWin::SetInactiveRenderingDisabled(bool value) { +} + +Widget::MoveLoopResult NativeWidgetWin::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) { + return message_handler_->RunMoveLoop(drag_offset) ? + Widget::MOVE_LOOP_SUCCESSFUL : Widget::MOVE_LOOP_CANCELED; +} + +void NativeWidgetWin::EndMoveLoop() { + message_handler_->EndMoveLoop(); +} + +void NativeWidgetWin::SetVisibilityChangedAnimationsEnabled(bool value) { + message_handler_->SetVisibilityChangedAnimationsEnabled(value); +} + +ui::NativeTheme* NativeWidgetWin::GetNativeTheme() const { + return ui::NativeTheme::instance(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, NativeWidget implementation: + +ui::EventHandler* NativeWidgetWin::GetEventHandler() { + NOTIMPLEMENTED(); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, protected: + +void NativeWidgetWin::OnFinalMessage(HWND window) { + // We don't destroy props in WM_DESTROY as we may still get messages after + // WM_DESTROY that assume the properties are still valid (such as WM_CLOSE). + props_.clear(); + delegate_->OnNativeWidgetDestroyed(); + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete this; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, protected: + +HWNDMessageHandler* NativeWidgetWin::GetMessageHandler() { + return message_handler_.get(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, HWNDMessageHandlerDelegate implementation: + +bool NativeWidgetWin::IsWidgetWindow() const { + // We don't NULL check GetWidget()->non_client_view() here because this + // function can be called before the widget is fully constructed. + return has_non_client_view_; +} + +bool NativeWidgetWin::IsUsingCustomFrame() const { + return !GetWidget()->ShouldUseNativeFrame(); +} + +void NativeWidgetWin::SchedulePaint() { + GetWidget()->GetRootView()->SchedulePaint(); +} + +void NativeWidgetWin::EnableInactiveRendering() { + delegate_->EnableInactiveRendering(); +} + +bool NativeWidgetWin::IsInactiveRenderingDisabled() { + return delegate_->IsInactiveRenderingDisabled(); +} + +bool NativeWidgetWin::CanResize() const { + return GetWidget()->widget_delegate()->CanResize(); +} + +bool NativeWidgetWin::CanMaximize() const { + return GetWidget()->widget_delegate()->CanMaximize(); +} + +bool NativeWidgetWin::CanActivate() const { + return delegate_->CanActivate(); +} + +bool NativeWidgetWin::WidgetSizeIsClientSize() const { + const Widget* widget = GetWidget()->GetTopLevelWidget(); + return IsZoomed(GetNativeView()) || + (widget && widget->ShouldUseNativeFrame()); +} + +bool NativeWidgetWin::CanSaveFocus() const { + return GetWidget()->is_top_level(); +} + +void NativeWidgetWin::SaveFocusOnDeactivate() { + GetWidget()->GetFocusManager()->StoreFocusedView(true); +} + +void NativeWidgetWin::RestoreFocusOnActivate() { + // Mysteriously, this only appears to be needed support restoration of focus + // to a child hwnd when restoring its top level window from the minimized + // state. If we don't do this, then ::SetFocus() to that child HWND returns + // ERROR_INVALID_PARAMETER, despite both HWNDs being of the same thread. + // See http://crbug.com/125976 and + // chrome/browser/ui/views/native_widget_win_interactive_uitest.cc . + { + // Since this is a synthetic reset, we don't need to tell anyone about it. + AutoNativeNotificationDisabler disabler; + GetWidget()->GetFocusManager()->ClearFocus(); + } + RestoreFocusOnEnable(); +} + +void NativeWidgetWin::RestoreFocusOnEnable() { + GetWidget()->GetFocusManager()->RestoreFocusedView(); +} + +bool NativeWidgetWin::IsModal() const { + return delegate_->IsModal(); +} + +int NativeWidgetWin::GetInitialShowState() const { + return SW_SHOWNORMAL; +} + +bool NativeWidgetWin::WillProcessWorkAreaChange() const { + return GetWidget()->widget_delegate()->WillProcessWorkAreaChange(); +} + +int NativeWidgetWin::GetNonClientComponent(const gfx::Point& point) const { + gfx::Point point_in_dip = ui::win::ScreenToDIPPoint(point); + return delegate_->GetNonClientComponent(point_in_dip); +} + +void NativeWidgetWin::GetWindowMask(const gfx::Size& size, gfx::Path* path) { + if (GetWidget()->non_client_view()) + GetWidget()->non_client_view()->GetWindowMask(size, path); +} + +bool NativeWidgetWin::GetClientAreaInsets(gfx::Insets* insets) const { + return false; +} + +void NativeWidgetWin::GetMinMaxSize(gfx::Size* min_size, + gfx::Size* max_size) const { + *min_size = ui::win::ScreenToDIPSize(delegate_->GetMinimumSize()); + *max_size = ui::win::ScreenToDIPSize(delegate_->GetMaximumSize()); +} + +gfx::Size NativeWidgetWin::GetRootViewSize() const { + gfx::Size pixel_size = GetWidget()->GetRootView()->size(); + return ui::win::ScreenToDIPSize(pixel_size); +} + +void NativeWidgetWin::ResetWindowControls() { + GetWidget()->non_client_view()->ResetWindowControls(); +} + +void NativeWidgetWin::PaintLayeredWindow(gfx::Canvas* canvas) { + GetWidget()->GetRootView()->Paint(canvas); +} + +InputMethod* NativeWidgetWin::GetInputMethod() { + return GetWidget()->GetInputMethodDirect(); +} + +gfx::NativeViewAccessible NativeWidgetWin::GetNativeViewAccessible() { + return GetWidget()->GetRootView()->GetNativeViewAccessible(); +} + +bool NativeWidgetWin::ShouldHandleSystemCommands() const { + return GetWidget()->widget_delegate()->ShouldHandleSystemCommands(); +} + +void NativeWidgetWin::HandleAppDeactivated() { + if (IsInactiveRenderingDisabled()) { + delegate_->EnableInactiveRendering(); + } else { + // TODO(pkotwicz): Remove need for SchedulePaint(). crbug.com/165841 + View* non_client_view = GetWidget()->non_client_view(); + if (non_client_view) + non_client_view->SchedulePaint(); + } +} + +void NativeWidgetWin::HandleActivationChanged(bool active) { + delegate_->OnNativeWidgetActivationChanged(active); +} + +bool NativeWidgetWin::HandleAppCommand(short command) { + // We treat APPCOMMAND ids as an extension of our command namespace, and just + // let the delegate figure out what to do... + return GetWidget()->widget_delegate() && + GetWidget()->widget_delegate()->ExecuteWindowsCommand(command); +} + +void NativeWidgetWin::HandleCancelMode() { +} + +void NativeWidgetWin::HandleCaptureLost() { + delegate_->OnMouseCaptureLost(); +} + +void NativeWidgetWin::HandleClose() { + GetWidget()->Close(); +} + +bool NativeWidgetWin::HandleCommand(int command) { + return GetWidget()->widget_delegate()->ExecuteWindowsCommand(command); +} + +void NativeWidgetWin::HandleAccelerator(const ui::Accelerator& accelerator) { + GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator); +} + +void NativeWidgetWin::HandleCreate() { + // TODO(beng): much of this could/should maybe move to HWNDMessageHandler. + + SetNativeWindowProperty(kNativeWidgetKey, this); + CHECK_EQ(this, GetNativeWidgetForNativeView(GetNativeView())); + + props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(GetNativeView())); + + drop_target_ = new DropTargetWin( + static_cast<internal::RootView*>(GetWidget()->GetRootView())); + + // Windows special DWM window frame requires a special tooltip manager so + // that window controls in Chrome windows don't flicker when you move your + // mouse over them. See comment in aero_tooltip_manager.h. + Widget* widget = GetWidget()->GetTopLevelWidget(); + if (widget && widget->ShouldUseNativeFrame()) { + tooltip_manager_.reset(new AeroTooltipManager(GetWidget())); + } else { + tooltip_manager_.reset(new TooltipManagerWin(GetWidget())); + } + if (!tooltip_manager_->Init()) { + // There was a problem creating the TooltipManager. Common error is 127. + // See 82193 for details. + LOG_GETLASTERROR(WARNING) << "tooltip creation failed, disabling tooltips"; + tooltip_manager_.reset(); + } + + delegate_->OnNativeWidgetCreated(true); +} + +void NativeWidgetWin::HandleDestroying() { + delegate_->OnNativeWidgetDestroying(); + if (drop_target_.get()) { + RevokeDragDrop(GetNativeView()); + drop_target_ = NULL; + } +} + +void NativeWidgetWin::HandleDestroyed() { + OnFinalMessage(GetNativeView()); +} + +bool NativeWidgetWin::HandleInitialFocus() { + return GetWidget()->SetInitialFocus(); +} + +void NativeWidgetWin::HandleDisplayChange() { + GetWidget()->widget_delegate()->OnDisplayChanged(); +} + +void NativeWidgetWin::HandleBeginWMSizeMove() { + delegate_->OnNativeWidgetBeginUserBoundsChange(); +} + +void NativeWidgetWin::HandleEndWMSizeMove() { + delegate_->OnNativeWidgetEndUserBoundsChange(); +} + +void NativeWidgetWin::HandleMove() { + delegate_->OnNativeWidgetMove(); +} + +void NativeWidgetWin::HandleWorkAreaChanged() { + GetWidget()->widget_delegate()->OnWorkAreaChanged(); +} + +void NativeWidgetWin::HandleVisibilityChanged(bool visible) { + delegate_->OnNativeWidgetVisibilityChanged(visible); +} + +void NativeWidgetWin::HandleClientSizeChanged(const gfx::Size& new_size) { + gfx::Size size_in_dip = ui::win::ScreenToDIPSize(new_size); + delegate_->OnNativeWidgetSizeChanged(size_in_dip); +} + +void NativeWidgetWin::HandleFrameChanged() { + // Replace the frame and layout the contents. + GetWidget()->non_client_view()->UpdateFrame(true); +} + +void NativeWidgetWin::HandleNativeFocus(HWND last_focused_window) { + delegate_->OnNativeFocus(last_focused_window); + InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->OnFocus(); +} + +void NativeWidgetWin::HandleNativeBlur(HWND focused_window) { + delegate_->OnNativeBlur(focused_window); + InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->OnBlur(); +} + +bool NativeWidgetWin::HandleMouseEvent(const ui::MouseEvent& event) { + static gfx::Transform scale_transform( + 1/ui::win::GetDeviceScaleFactor(), 0.0, + 0.0, 1/ui::win::GetDeviceScaleFactor(), + 0.0, 0.0); + if (event.IsMouseWheelEvent()) { + ui::MouseWheelEvent dpi_event( + static_cast<const ui::MouseWheelEvent&>(event)); + dpi_event.UpdateForRootTransform(scale_transform); + delegate_->OnMouseEvent(&dpi_event); + return dpi_event.handled(); + } else if (event.IsMouseEvent()) { + CHECK(!event.IsScrollEvent()); // Scroll events don't happen in Windows. + ui::MouseEvent dpi_event(event); + if (!(dpi_event.flags() & ui::EF_IS_NON_CLIENT)) + dpi_event.UpdateForRootTransform(scale_transform); + delegate_->OnMouseEvent(&dpi_event); + return dpi_event.handled(); + } + NOTREACHED(); + return false; +} + +bool NativeWidgetWin::HandleKeyEvent(const ui::KeyEvent& event) { + delegate_->OnKeyEvent(const_cast<ui::KeyEvent*>(&event)); + return event.handled(); +} + +bool NativeWidgetWin::HandleUntranslatedKeyEvent(const ui::KeyEvent& event) { + InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->DispatchKeyEvent(event); + return !!input_method; +} + +bool NativeWidgetWin::HandleTouchEvent(const ui::TouchEvent& event) { + NOTREACHED() << "Touch events are not supported"; + return false; +} + +bool NativeWidgetWin::HandleIMEMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + InputMethod* input_method = GetInputMethod(); + if (!input_method || input_method->IsMock()) { + *result = 0; + return false; + } + + MSG msg = {}; + msg.hwnd = message_handler_->hwnd(); + msg.message = message; + msg.wParam = w_param; + msg.lParam = l_param; + return input_method->OnUntranslatedIMEMessage(msg, result); +} + +void NativeWidgetWin::HandleInputLanguageChange(DWORD character_set, + HKL input_language_id) { + InputMethod* input_method = GetInputMethod(); + if (input_method && !input_method->IsMock()) { + input_method->OnInputLocaleChanged(); + } +} + +bool NativeWidgetWin::HandlePaintAccelerated(const gfx::Rect& invalid_rect) { + gfx::Rect dpi_rect = ui::win::ScreenToDIPRect(invalid_rect); + return delegate_->OnNativeWidgetPaintAccelerated(dpi_rect); +} + +void NativeWidgetWin::HandlePaint(gfx::Canvas* canvas) { + delegate_->OnNativeWidgetPaint(canvas); +} + +bool NativeWidgetWin::HandleTooltipNotify(int w_param, + NMHDR* l_param, + LRESULT* l_result) { + // We can be sent this message before the tooltip manager is created, if a + // subclass overrides OnCreate and creates some kind of Windows control there + // that sends WM_NOTIFY messages. + if (tooltip_manager_.get()) { + bool handled; + *l_result = tooltip_manager_->OnNotify(w_param, l_param, &handled); + return handled; + } + return false; +} + +void NativeWidgetWin::HandleTooltipMouseMove(UINT message, + WPARAM w_param, + LPARAM l_param) { + if (tooltip_manager_.get()) + tooltip_manager_->OnMouse(message, w_param, l_param); +} + +bool NativeWidgetWin::PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + return false; +} + +void NativeWidgetWin::PostHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param) { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, private: + +void NativeWidgetWin::SetInitParams(const Widget::InitParams& params) { + // Set non-style attributes. + ownership_ = params.ownership; + + ConfigureWindowStyles(message_handler_.get(), params, + GetWidget()->widget_delegate(), delegate_); + + has_non_client_view_ = Widget::RequiresNonClientView(params.type); + message_handler_->set_remove_standard_frame(params.remove_standard_frame); + message_handler_->set_use_system_default_icon(params.use_system_default_icon); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +// static +void Widget::NotifyLocaleChanged() { + NOTIMPLEMENTED(); +} + +namespace { +BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) { + Widget* widget = Widget::GetWidgetForNativeView(hwnd); + if (widget && widget->is_secondary_widget()) + widget->Close(); + return TRUE; +} +} // namespace + +// static +void Widget::CloseAllSecondaryWidgets() { + EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0); +} + +bool Widget::ConvertRect(const Widget* source, + const Widget* target, + gfx::Rect* rect) { + DCHECK(source); + DCHECK(target); + DCHECK(rect); + + HWND source_hwnd = source->GetNativeView(); + HWND target_hwnd = target->GetNativeView(); + if (source_hwnd == target_hwnd) + return true; + + RECT win_rect = ui::win::DIPToScreenRect(*rect).ToRECT(); + if (::MapWindowPoints(source_hwnd, target_hwnd, + reinterpret_cast<LPPOINT>(&win_rect), + sizeof(RECT)/sizeof(POINT))) { + *rect = ui::win::ScreenToDIPRect(gfx::Rect(win_rect)); + return true; + } + return false; +} + +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// internal::NativeWidgetPrivate, public: + +// static +NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget( + internal::NativeWidgetDelegate* delegate) { + return new NativeWidgetWin(delegate); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + return reinterpret_cast<NativeWidgetWin*>( + ViewProp::GetValue(native_view, kNativeWidgetKey)); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + return GetNativeWidgetForNativeView(native_window); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( + gfx::NativeView native_view) { + if (!native_view) + return NULL; + + // First, check if the top-level window is a Widget. + HWND root = ::GetAncestor(native_view, GA_ROOT); + if (!root) + return NULL; + + NativeWidgetPrivate* widget = GetNativeWidgetForNativeView(root); + if (widget) + return widget; + + // Second, try to locate the last Widget window in the parent hierarchy. + HWND parent_hwnd = native_view; + // If we fail to find the native widget pointer for the root then it probably + // means that the root belongs to a different process in which case we walk up + // the native view chain looking for a parent window which corresponds to a + // valid native widget. We only do this if we fail to find the native widget + // for the current native view which means it is being destroyed. + if (!widget && !GetNativeWidgetForNativeView(native_view)) { + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT); + if (!parent_hwnd) + return NULL; + } + NativeWidgetPrivate* parent_widget; + do { + parent_widget = GetNativeWidgetForNativeView(parent_hwnd); + if (parent_widget) { + widget = parent_widget; + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT); + } + } while (parent_hwnd != NULL && parent_widget != NULL); + + return widget; +} + +// static +void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, + Widget::Widgets* children) { + if (!native_view) + return; + + Widget* widget = Widget::GetWidgetForNativeView(native_view); + if (widget) + children->insert(widget); + EnumChildWindows(native_view, EnumerateChildWindowsForNativeWidgets, + reinterpret_cast<LPARAM>(children)); +} + +// static +void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent) { + if (!native_view) + return; + + HWND previous_parent = ::GetParent(native_view); + if (previous_parent == new_parent) + return; + + Widget::Widgets widgets; + GetAllChildWidgets(native_view, &widgets); + + // First notify all the widgets that they are being disassociated + // from their previous parent. + for (Widget::Widgets::iterator it = widgets.begin(); + it != widgets.end(); ++it) { + // TODO(beng): Rename this notification to NotifyNativeViewChanging() + // and eliminate the bool parameter. + (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent); + } + + ::SetParent(native_view, new_parent); + + // And now, notify them that they have a brand new parent. + for (Widget::Widgets::iterator it = widgets.begin(); + it != widgets.end(); ++it) { + (*it)->NotifyNativeViewHierarchyChanged(true, new_parent); + } +} + +// static +bool NativeWidgetPrivate::IsMouseButtonDown() { + return (GetKeyState(VK_LBUTTON) & 0x80) || + (GetKeyState(VK_RBUTTON) & 0x80) || + (GetKeyState(VK_MBUTTON) & 0x80) || + (GetKeyState(VK_XBUTTON1) & 0x80) || + (GetKeyState(VK_XBUTTON2) & 0x80); +} + +// static +bool NativeWidgetPrivate::IsTouchDown() { + // This currently isn't necessary because we're not generating touch events on + // windows. When we do, this will need to be updated. + return false; +} + +} // namespace internal + +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_win.h b/chromium/ui/views/widget/native_widget_win.h new file mode 100644 index 00000000000..81fa718ef41 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_win.h @@ -0,0 +1,274 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/win/scoped_comptr.h" +#include "base/win/win_util.h" +#include "ui/base/win/window_impl.h" +#include "ui/views/widget/native_widget_private.h" +#include "ui/views/win/hwnd_message_handler_delegate.h" + +namespace ui { +class Compositor; +class ViewProp; +} + +namespace gfx { +class Canvas; +class Font; +class Rect; +} + +namespace views { + +class DropTargetWin; +class HWNDMessageHandler; +class InputMethodDelegate; +class RootView; +class TooltipManagerWin; + +/////////////////////////////////////////////////////////////////////////////// +// +// NativeWidgetWin +// A Widget for a views hierarchy used to represent anything that can be +// contained within an HWND, e.g. a control, a window, etc. Specializations +// suitable for specific tasks, e.g. top level window, are derived from this. +// +// This Widget contains a RootView which owns the hierarchy of views within it. +// As long as views are part of this tree, they will be deleted automatically +// when the RootView is destroyed. If you remove a view from the tree, you are +// then responsible for cleaning up after it. +// +/////////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT NativeWidgetWin : public internal::NativeWidgetPrivate, + public HWNDMessageHandlerDelegate { + public: + explicit NativeWidgetWin(internal::NativeWidgetDelegate* delegate); + virtual ~NativeWidgetWin(); + + // Returns the system set window title font. + static gfx::Font GetWindowTitleFont(); + + // Show the window with the specified show command. + void Show(int show_state); + + // Places the window in a pseudo-fullscreen mode where it looks and acts as + // like a fullscreen window except that it remains within the boundaries + // of the metro snap divider. + void SetMetroSnapFullscreen(bool metro_snap); + bool IsInMetroSnapMode() const; + + void SetCanUpdateLayeredWindow(bool can_update); + + // Overridden from internal::NativeWidgetPrivate: + virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual void FrameTypeChanged() OVERRIDE; + virtual Widget* GetWidget() OVERRIDE; + virtual const Widget* GetWidget() const OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE; + virtual Widget* GetTopLevelWidget() OVERRIDE; + virtual const ui::Compositor* GetCompositor() const OVERRIDE; + virtual ui::Compositor* GetCompositor() OVERRIDE; + virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; + virtual void ViewRemoved(View* view) OVERRIDE; + virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; + virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; + virtual TooltipManager* GetTooltipManager() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual bool HasCapture() const OVERRIDE; + virtual InputMethod* CreateInputMethod() OVERRIDE; + virtual internal::InputMethodDelegate* GetInputMethodDelegate() OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const OVERRIDE; + virtual void SetWindowTitle(const string16& title) OVERRIDE; + virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) OVERRIDE; + virtual void InitModalType(ui::ModalType modal_type) OVERRIDE; + virtual gfx::Rect GetWindowBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetClientAreaBoundsInScreen() const OVERRIDE; + virtual gfx::Rect GetRestoredBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void StackAbove(gfx::NativeView native_view) OVERRIDE; + virtual void StackAtTop() OVERRIDE; + virtual void StackBelow(gfx::NativeView native_view) OVERRIDE; + virtual void SetShape(gfx::NativeRegion shape) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void CloseNow() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) OVERRIDE; + virtual void ShowWithWindowState(ui::WindowShowState show_state) OVERRIDE; + virtual bool IsVisible() const OVERRIDE; + virtual void Activate() OVERRIDE; + virtual void Deactivate() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual bool IsMaximized() const OVERRIDE; + virtual bool IsMinimized() const OVERRIDE; + virtual void Restore() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; + virtual void SetOpacity(unsigned char opacity) OVERRIDE; + virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE; + virtual void FlashFrame(bool flash) OVERRIDE; + virtual void RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; + virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; + virtual bool IsMouseEventsEnabled() const OVERRIDE; + virtual void ClearNativeFocus() OVERRIDE; + virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE; + virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE; + virtual Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; + + // Overridden from NativeWidget: + virtual ui::EventHandler* GetEventHandler() OVERRIDE; + + protected: + // Deletes this window as it is destroyed, override to provide different + // behavior. + virtual void OnFinalMessage(HWND window); + + HWNDMessageHandler* GetMessageHandler(); + + // Overridden from HWNDMessageHandlerDelegate: + virtual bool IsWidgetWindow() const OVERRIDE; + virtual bool IsUsingCustomFrame() const OVERRIDE; + virtual void SchedulePaint() OVERRIDE; + virtual void EnableInactiveRendering() OVERRIDE; + virtual bool IsInactiveRenderingDisabled() OVERRIDE; + virtual bool CanResize() const OVERRIDE; + virtual bool CanMaximize() const OVERRIDE; + virtual bool CanActivate() const OVERRIDE; + virtual bool WidgetSizeIsClientSize() const OVERRIDE; + virtual bool CanSaveFocus() const OVERRIDE; + virtual void SaveFocusOnDeactivate() OVERRIDE; + virtual void RestoreFocusOnActivate() OVERRIDE; + virtual void RestoreFocusOnEnable() OVERRIDE; + virtual bool IsModal() const OVERRIDE; + virtual int GetInitialShowState() const OVERRIDE; + virtual bool WillProcessWorkAreaChange() const OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE; + virtual void GetWindowMask(const gfx::Size& size, gfx::Path* path) OVERRIDE; + virtual bool GetClientAreaInsets(gfx::Insets* insets) const OVERRIDE; + virtual void GetMinMaxSize(gfx::Size* min_size, + gfx::Size* max_size) const OVERRIDE; + virtual gfx::Size GetRootViewSize() const OVERRIDE; + virtual void ResetWindowControls() OVERRIDE; + virtual void PaintLayeredWindow(gfx::Canvas* canvas) OVERRIDE; + virtual InputMethod* GetInputMethod() OVERRIDE; + virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE; + virtual bool ShouldHandleSystemCommands() const OVERRIDE; + virtual void HandleAppDeactivated() OVERRIDE; + virtual void HandleActivationChanged(bool active) OVERRIDE; + virtual bool HandleAppCommand(short command) OVERRIDE; + virtual void HandleCancelMode() OVERRIDE; + virtual void HandleCaptureLost() OVERRIDE; + virtual void HandleClose() OVERRIDE; + virtual bool HandleCommand(int command) OVERRIDE; + virtual void HandleAccelerator(const ui::Accelerator& accelerator) OVERRIDE; + virtual void HandleCreate() OVERRIDE; + virtual void HandleDestroying() OVERRIDE; + virtual void HandleDestroyed() OVERRIDE; + virtual bool HandleInitialFocus() OVERRIDE; + virtual void HandleDisplayChange() OVERRIDE; + virtual void HandleBeginWMSizeMove() OVERRIDE; + virtual void HandleEndWMSizeMove() OVERRIDE; + virtual void HandleMove() OVERRIDE; + virtual void HandleWorkAreaChanged() OVERRIDE; + virtual void HandleVisibilityChanged(bool visible) OVERRIDE; + virtual void HandleClientSizeChanged(const gfx::Size& new_size) OVERRIDE; + virtual void HandleFrameChanged() OVERRIDE; + virtual void HandleNativeFocus(HWND last_focused_window) OVERRIDE; + virtual void HandleNativeBlur(HWND focused_window) OVERRIDE; + virtual bool HandleMouseEvent(const ui::MouseEvent& event) OVERRIDE; + virtual bool HandleKeyEvent(const ui::KeyEvent& event) OVERRIDE; + virtual bool HandleUntranslatedKeyEvent(const ui::KeyEvent& event) OVERRIDE; + virtual bool HandleTouchEvent(const ui::TouchEvent& event) OVERRIDE; + virtual bool HandleIMEMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) OVERRIDE; + virtual void HandleInputLanguageChange(DWORD character_set, + HKL input_language_id) OVERRIDE; + virtual bool HandlePaintAccelerated(const gfx::Rect& invalid_rect) OVERRIDE; + virtual void HandlePaint(gfx::Canvas* canvas) OVERRIDE; + virtual bool HandleTooltipNotify(int w_param, + NMHDR* l_param, + LRESULT* l_result) OVERRIDE; + virtual void HandleTooltipMouseMove(UINT message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + virtual bool PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) OVERRIDE; + virtual void PostHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + + // The TooltipManager. This is NULL if there is a problem creating the + // underlying tooltip window. + // WARNING: RootView's destructor calls into the TooltipManager. As such, this + // must be destroyed AFTER root_view_. + scoped_ptr<TooltipManagerWin> tooltip_manager_; + + scoped_refptr<DropTargetWin> drop_target_; + + private: + typedef ScopedVector<ui::ViewProp> ViewProps; + + void SetInitParams(const Widget::InitParams& params); + + // A delegate implementation that handles events received here. + // See class documentation for Widget in widget.h for a note about ownership. + internal::NativeWidgetDelegate* delegate_; + + // See class documentation for Widget in widget.h for a note about ownership. + Widget::InitParams::Ownership ownership_; + + ViewProps props_; + + // The window styles before we modified them for the drag frame appearance. + DWORD drag_frame_saved_window_style_; + DWORD drag_frame_saved_window_ex_style_; + + // True if the widget is going to have a non_client_view. We cache this value + // rather than asking the Widget for the non_client_view so that we know at + // Init time, before the Widget has created the NonClientView. + bool has_non_client_view_; + + scoped_ptr<HWNDMessageHandler> message_handler_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ diff --git a/chromium/ui/views/widget/native_widget_win_unittest.cc b/chromium/ui/views/widget/native_widget_win_unittest.cc new file mode 100644 index 00000000000..6a84ca06d70 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_win_unittest.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/native_widget_win.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/win/scoped_ole_initializer.h" + +namespace views { +namespace { + +class NativeWidgetWinTest : public testing::Test { + public: + NativeWidgetWinTest() {} + ~NativeWidgetWinTest() {} + + virtual void TearDown() { + // Flush the message loop because we have pending release tasks + // and these tasks if un-executed would upset Valgrind. + RunPendingMessages(); + } + + // Create a simple widget win. The caller is responsible for taking ownership + // of the returned value. + NativeWidgetWin* CreateNativeWidgetWin(); + + void RunPendingMessages() { + message_loop_.RunUntilIdle(); + } + + private: + base::MessageLoopForUI message_loop_; + ui::ScopedOleInitializer ole_initializer_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetWinTest); +}; + +NativeWidgetWin* NativeWidgetWinTest::CreateNativeWidgetWin() { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + return static_cast<NativeWidgetWin*>(widget.release()->native_widget()); +} + +TEST_F(NativeWidgetWinTest, ZoomWindow) { + scoped_ptr<NativeWidgetWin> window(CreateNativeWidgetWin()); + ShowWindow(window->GetNativeView(), SW_HIDE); + EXPECT_FALSE(window->IsActive()); + ShowWindow(window->GetNativeView(), SW_MAXIMIZE); + EXPECT_TRUE(IsZoomed(window->GetNativeView())); + window->CloseNow(); +} + +TEST_F(NativeWidgetWinTest, SetBoundsForZoomedWindow) { + scoped_ptr<NativeWidgetWin> window(CreateNativeWidgetWin()); + ShowWindow(window->GetNativeView(), SW_MAXIMIZE); + EXPECT_TRUE(IsZoomed(window->GetNativeView())); + + // Create another window, so that it will be active. + scoped_ptr<NativeWidgetWin> window2(CreateNativeWidgetWin()); + ShowWindow(window2->GetNativeView(), SW_MAXIMIZE); + EXPECT_TRUE(window2->IsActive()); + EXPECT_FALSE(window->IsActive()); + + // Verify that setting the bounds of a zoomed window will unzoom it and not + // cause it to be activated. + window->SetBounds(gfx::Rect(50, 50, 650, 650)); + EXPECT_FALSE(IsZoomed(window->GetNativeView())); + EXPECT_FALSE(window->IsActive()); + + // Cleanup. + window->CloseNow(); + window2->CloseNow(); +} + +} // namespace +} // namespace views diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc new file mode 100644 index 00000000000..b304c520b97 --- /dev/null +++ b/chromium/ui/views/widget/root_view.cc @@ -0,0 +1,707 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/root_view.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/events/event.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" +#include "ui/views/focus/view_storage.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_deletion_observer.h" + +namespace views { +namespace internal { + +namespace { + +enum EventType { + EVENT_ENTER, + EVENT_EXIT +}; + +class MouseEnterExitEvent : public ui::MouseEvent { + public: + MouseEnterExitEvent(const ui::MouseEvent& event, ui::EventType type) + : ui::MouseEvent(event, + static_cast<View*>(NULL), + static_cast<View*>(NULL)) { + DCHECK(type == ui::ET_MOUSE_ENTERED || + type == ui::ET_MOUSE_EXITED); + SetType(type); + } + + virtual ~MouseEnterExitEvent() {} +}; + +} // namespace + +// static +const char RootView::kViewClassName[] = "RootView"; + +//////////////////////////////////////////////////////////////////////////////// +// RootView, public: + +// Creation and lifetime ------------------------------------------------------- + +RootView::RootView(Widget* widget) + : widget_(widget), + mouse_pressed_handler_(NULL), + mouse_move_handler_(NULL), + last_click_handler_(NULL), + explicit_mouse_handler_(false), + last_mouse_event_flags_(0), + last_mouse_event_x_(-1), + last_mouse_event_y_(-1), + touch_pressed_handler_(NULL), + gesture_handler_(NULL), + scroll_gesture_handler_(NULL), + focus_search_(this, false, false), + focus_traversable_parent_(NULL), + focus_traversable_parent_view_(NULL), + event_dispatch_target_(NULL) { +} + +RootView::~RootView() { + // If we have children remove them explicitly so to make sure a remove + // notification is sent for each one of them. + if (has_children()) + RemoveAllChildViews(true); +} + +// Tree operations ------------------------------------------------------------- + +void RootView::SetContentsView(View* contents_view) { + DCHECK(contents_view && GetWidget()->native_widget()) << + "Can't be called until after the native widget is created!"; + // The ContentsView must be set up _after_ the window is created so that its + // Widget pointer is valid. + SetLayoutManager(new FillLayout); + if (has_children()) + RemoveAllChildViews(true); + AddChildView(contents_view); + + // Force a layout now, since the attached hierarchy won't be ready for the + // containing window's bounds. Note that we call Layout directly rather than + // calling the widget's size changed handler, since the RootView's bounds may + // not have changed, which will cause the Layout not to be done otherwise. + Layout(); +} + +View* RootView::GetContentsView() { + return child_count() > 0 ? child_at(0) : NULL; +} + +void RootView::NotifyNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view) { + PropagateNativeViewHierarchyChanged(attached, native_view, this); +} + +// Input ----------------------------------------------------------------------- + +void RootView::DispatchKeyEvent(ui::KeyEvent* event) { + View* v = NULL; + if (GetFocusManager()) // NULL in unittests. + v = GetFocusManager()->GetFocusedView(); + // Special case to handle right-click context menus triggered by the + // keyboard. + if (v && v->enabled() && ((event->key_code() == ui::VKEY_APPS) || + (event->key_code() == ui::VKEY_F10 && event->IsShiftDown()))) { + v->ShowContextMenu(v->GetKeyboardContextMenuLocation(), + ui::MENU_SOURCE_KEYBOARD); + event->StopPropagation(); + return; + } + + for (; v && v != this && !event->handled(); v = v->parent()) + DispatchEventToTarget(v, event); +} + +void RootView::DispatchScrollEvent(ui::ScrollEvent* event) { + for (View* v = GetEventHandlerForPoint(event->location()); + v && v != this && !event->stopped_propagation(); v = v->parent()) { + DispatchEventToTarget(v, event); + } + + if (event->handled() || event->type() != ui::ET_SCROLL) + return; + + // Convert unprocessed scroll events into mouse-wheel events. + ui::MouseWheelEvent wheel(*event); + if (OnMouseWheel(wheel)) + event->SetHandled(); +} + +void RootView::DispatchTouchEvent(ui::TouchEvent* event) { + // TODO: this looks all wrong. On a TOUCH_PRESSED we should figure out the + // view and target that view with all touches with the same id until the + // release (or keep it if captured). + + // If touch_pressed_handler_ is non null, we are currently processing + // a touch down on the screen situation. In that case we send the + // event to touch_pressed_handler_ + + if (touch_pressed_handler_) { + ui::TouchEvent touch_event(*event, static_cast<View*>(this), + touch_pressed_handler_); + DispatchEventToTarget(touch_pressed_handler_, &touch_event); + if (touch_event.handled()) + event->SetHandled(); + if (touch_event.stopped_propagation()) + event->StopPropagation(); + return; + } + + // Walk up the tree until we find a view that wants the touch event. + for (touch_pressed_handler_ = GetEventHandlerForPoint(event->location()); + touch_pressed_handler_ && (touch_pressed_handler_ != this); + touch_pressed_handler_ = touch_pressed_handler_->parent()) { + if (!touch_pressed_handler_->enabled()) { + // Disabled views eat events but are treated as not handled. + break; + } + + // See if this view wants to handle the touch + ui::TouchEvent touch_event(*event, static_cast<View*>(this), + touch_pressed_handler_); + DispatchEventToTarget(touch_pressed_handler_, &touch_event); + if (touch_event.handled()) + event->SetHandled(); + if (touch_event.stopped_propagation()) + event->StopPropagation(); + + // The view could have removed itself from the tree when handling + // OnTouchEvent(). So handle as per OnMousePressed. NB: we + // assume that the RootView itself cannot be so removed. + if (!touch_pressed_handler_) + break; + + // The touch event wasn't processed. Go up the view hierarchy and dispatch + // the touch event. + if (!event->handled()) + continue; + + // If a View consumed the event, that means future touch-events should go to + // that View. If the event wasn't consumed, then reset the handler. + if (!event->stopped_propagation()) + touch_pressed_handler_ = NULL; + + return; + } + + // Reset touch_pressed_handler_ to indicate that no processing is occurring. + touch_pressed_handler_ = NULL; + + return; +} + +void RootView::DispatchGestureEvent(ui::GestureEvent* event) { + if (gesture_handler_) { + // |gesture_handler_| (or |scroll_gesture_handler_|) can be deleted during + // processing. + View* handler = scroll_gesture_handler_ && + (event->IsScrollGestureEvent() || event->IsFlingScrollEvent()) ? + scroll_gesture_handler_ : gesture_handler_; + ui::GestureEvent handler_event(*event, static_cast<View*>(this), handler); + DispatchEventToTarget(handler, &handler_event); + + if (event->type() == ui::ET_GESTURE_END && + event->details().touch_points() <= 1) { + // In case a drag was in progress, reset all the handlers. Otherwise, just + // reset the gesture handler. + if (gesture_handler_ == mouse_pressed_handler_) + SetMouseHandler(NULL); + else + gesture_handler_ = NULL; + } + + if (scroll_gesture_handler_ && + (event->type() == ui::ET_GESTURE_SCROLL_END || + event->type() == ui::ET_SCROLL_FLING_START)) { + scroll_gesture_handler_ = NULL; + } + + if (handler_event.stopped_propagation()) { + event->StopPropagation(); + return; + } else if (handler_event.handled()) { + event->SetHandled(); + return; + } + + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN && + !scroll_gesture_handler_) { + // Some view started processing gesture events, however it does not + // process scroll-gesture events. In such case, we allow the event to + // bubble up, and install a different scroll-gesture handler different + // from the default gesture handler. + for (scroll_gesture_handler_ = gesture_handler_->parent(); + scroll_gesture_handler_ && scroll_gesture_handler_ != this; + scroll_gesture_handler_ = scroll_gesture_handler_->parent()) { + ui::GestureEvent gesture_event(*event, static_cast<View*>(this), + scroll_gesture_handler_); + DispatchEventToTarget(scroll_gesture_handler_, &gesture_event); + if (gesture_event.stopped_propagation()) { + event->StopPropagation(); + return; + } else if (gesture_event.handled()) { + event->SetHandled(); + return; + } + } + scroll_gesture_handler_ = NULL; + } + + return; + } + + // If there was no handler for a SCROLL_BEGIN event, then subsequent scroll + // events are not dispatched to any views. + switch (event->type()) { + case ui::ET_GESTURE_SCROLL_UPDATE: + case ui::ET_GESTURE_SCROLL_END: + case ui::ET_SCROLL_FLING_START: + return; + default: + break; + } + + // Walk up the tree until we find a view that wants the gesture event. + for (gesture_handler_ = GetEventHandlerForPoint(event->location()); + gesture_handler_ && (gesture_handler_ != this); + gesture_handler_ = gesture_handler_->parent()) { + if (!gesture_handler_->enabled()) { + // Disabled views eat events but are treated as not handled. + return; + } + + // See if this view wants to handle the Gesture. + ui::GestureEvent gesture_event(*event, static_cast<View*>(this), + gesture_handler_); + DispatchEventToTarget(gesture_handler_, &gesture_event); + + // The view could have removed itself from the tree when handling + // OnGestureEvent(). So handle as per OnMousePressed. NB: we + // assume that the RootView itself cannot be so removed. + if (!gesture_handler_) + return; + + if (gesture_event.handled()) { + if (gesture_event.type() == ui::ET_GESTURE_SCROLL_BEGIN) + scroll_gesture_handler_ = gesture_handler_; + if (gesture_event.stopped_propagation()) + event->StopPropagation(); + else + event->SetHandled(); + return; + } + + // The gesture event wasn't processed. Go up the view hierarchy and + // dispatch the gesture event. + } + + gesture_handler_ = NULL; +} + +// Focus ----------------------------------------------------------------------- + +void RootView::SetFocusTraversableParent(FocusTraversable* focus_traversable) { + DCHECK(focus_traversable != this); + focus_traversable_parent_ = focus_traversable; +} + +void RootView::SetFocusTraversableParentView(View* view) { + focus_traversable_parent_view_ = view; +} + +// System events --------------------------------------------------------------- + +void RootView::ThemeChanged() { + View::PropagateThemeChanged(); +} + +void RootView::LocaleChanged() { + View::PropagateLocaleChanged(); +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, FocusTraversable implementation: + +FocusSearch* RootView::GetFocusSearch() { + return &focus_search_; +} + +FocusTraversable* RootView::GetFocusTraversableParent() { + return focus_traversable_parent_; +} + +View* RootView::GetFocusTraversableParentView() { + return focus_traversable_parent_view_; +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, View overrides: + +const Widget* RootView::GetWidget() const { + return widget_; +} + +Widget* RootView::GetWidget() { + return const_cast<Widget*>(const_cast<const RootView*>(this)->GetWidget()); +} + +bool RootView::IsDrawn() const { + return visible(); +} + +const char* RootView::GetClassName() const { + return kViewClassName; +} + +void RootView::SchedulePaintInRect(const gfx::Rect& rect) { + if (layer()) { + layer()->SchedulePaint(rect); + } else { + gfx::Rect xrect = ConvertRectToParent(rect); + gfx::Rect invalid_rect = gfx::IntersectRects(GetLocalBounds(), xrect); + if (!invalid_rect.IsEmpty()) + widget_->SchedulePaintInRect(invalid_rect); + } +} + +bool RootView::OnMousePressed(const ui::MouseEvent& event) { + UpdateCursor(event); + SetMouseLocationAndFlags(event); + + // If mouse_pressed_handler_ is non null, we are currently processing + // a pressed -> drag -> released session. In that case we send the + // event to mouse_pressed_handler_ + if (mouse_pressed_handler_) { + ui::MouseEvent mouse_pressed_event(event, static_cast<View*>(this), + mouse_pressed_handler_); + drag_info_.Reset(); + DispatchEventToTarget(mouse_pressed_handler_, &mouse_pressed_event); + return true; + } + DCHECK(!explicit_mouse_handler_); + + bool hit_disabled_view = false; + // Walk up the tree until we find a view that wants the mouse event. + for (mouse_pressed_handler_ = GetEventHandlerForPoint(event.location()); + mouse_pressed_handler_ && (mouse_pressed_handler_ != this); + mouse_pressed_handler_ = mouse_pressed_handler_->parent()) { + DVLOG(1) << "OnMousePressed testing " + << mouse_pressed_handler_->GetClassName(); + if (!mouse_pressed_handler_->enabled()) { + // Disabled views should eat events instead of propagating them upwards. + hit_disabled_view = true; + break; + } + + // See if this view wants to handle the mouse press. + ui::MouseEvent mouse_pressed_event(event, static_cast<View*>(this), + mouse_pressed_handler_); + + // Remove the double-click flag if the handler is different than the + // one which got the first click part of the double-click. + if (mouse_pressed_handler_ != last_click_handler_) + mouse_pressed_event.set_flags(event.flags() & ~ui::EF_IS_DOUBLE_CLICK); + + drag_info_.Reset(); + { + WidgetDeletionObserver widget_deletion_observer(widget_); + DispatchEventToTarget(mouse_pressed_handler_, &mouse_pressed_event); + if (!widget_deletion_observer.IsWidgetAlive()) + return mouse_pressed_event.handled(); + } + + // The view could have removed itself from the tree when handling + // OnMousePressed(). In this case, the removal notification will have + // reset mouse_pressed_handler_ to NULL out from under us. Detect this + // case and stop. (See comments in view.h.) + // + // NOTE: Don't return true here, because we don't want the frame to + // forward future events to us when there's no handler. + if (!mouse_pressed_handler_) + break; + + // If the view handled the event, leave mouse_pressed_handler_ set and + // return true, which will cause subsequent drag/release events to get + // forwarded to that view. + if (mouse_pressed_event.handled()) { + last_click_handler_ = mouse_pressed_handler_; + DVLOG(1) << "OnMousePressed handled by " + << mouse_pressed_handler_->GetClassName(); + return true; + } + } + + // Reset mouse_pressed_handler_ to indicate that no processing is occurring. + mouse_pressed_handler_ = NULL; + + // In the event that a double-click is not handled after traversing the + // entire hierarchy (even as a single-click when sent to a different view), + // it must be marked as handled to avoid anything happening from default + // processing if it the first click-part was handled by us. + if (last_click_handler_ && (event.flags() & ui::EF_IS_DOUBLE_CLICK)) + hit_disabled_view = true; + + last_click_handler_ = NULL; + return hit_disabled_view; +} + +bool RootView::OnMouseDragged(const ui::MouseEvent& event) { + if (mouse_pressed_handler_) { + SetMouseLocationAndFlags(event); + + ui::MouseEvent mouse_event(event, static_cast<View*>(this), + mouse_pressed_handler_); + DispatchEventToTarget(mouse_pressed_handler_, &mouse_event); + } + return false; +} + +void RootView::OnMouseReleased(const ui::MouseEvent& event) { + UpdateCursor(event); + + if (mouse_pressed_handler_) { + ui::MouseEvent mouse_released(event, static_cast<View*>(this), + mouse_pressed_handler_); + // We allow the view to delete us from the event dispatch callback. As such, + // configure state such that we're done first, then call View. + View* mouse_pressed_handler = mouse_pressed_handler_; + SetMouseHandler(NULL); + DispatchEventToTarget(mouse_pressed_handler, &mouse_released); + // WARNING: we may have been deleted. + } +} + +void RootView::OnMouseCaptureLost() { + // TODO: this likely needs to reset touch handler too. + + if (mouse_pressed_handler_ || gesture_handler_) { + // Synthesize a release event for UpdateCursor. + if (mouse_pressed_handler_) { + gfx::Point last_point(last_mouse_event_x_, last_mouse_event_y_); + ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, + last_point, last_point, + last_mouse_event_flags_); + UpdateCursor(release_event); + } + // We allow the view to delete us from OnMouseCaptureLost. As such, + // configure state such that we're done first, then call View. + View* mouse_pressed_handler = mouse_pressed_handler_; + View* gesture_handler = gesture_handler_; + SetMouseHandler(NULL); + if (mouse_pressed_handler) + mouse_pressed_handler->OnMouseCaptureLost(); + else + gesture_handler->OnMouseCaptureLost(); + // WARNING: we may have been deleted. + } +} + +void RootView::OnMouseMoved(const ui::MouseEvent& event) { + View* v = GetEventHandlerForPoint(event.location()); + // Find the first enabled view, or the existing move handler, whichever comes + // first. The check for the existing handler is because if a view becomes + // disabled while handling moves, it's wrong to suddenly send ET_MOUSE_EXITED + // and ET_MOUSE_ENTERED events, because the mouse hasn't actually exited yet. + while (v && !v->enabled() && (v != mouse_move_handler_)) + v = v->parent(); + if (v && v != this) { + if (v != mouse_move_handler_) { + if (mouse_move_handler_ != NULL && + (!mouse_move_handler_->notify_enter_exit_on_child() || + !mouse_move_handler_->Contains(v))) { + MouseEnterExitEvent exit(event, ui::ET_MOUSE_EXITED); + DispatchEventToTarget(mouse_move_handler_, &exit); + NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, + mouse_move_handler_, v); + } + View* old_handler = mouse_move_handler_; + mouse_move_handler_ = v; + if (!mouse_move_handler_->notify_enter_exit_on_child() || + !mouse_move_handler_->Contains(old_handler)) { + MouseEnterExitEvent entered(event, ui::ET_MOUSE_ENTERED); + entered.ConvertLocationToTarget(static_cast<View*>(this), + mouse_move_handler_); + DispatchEventToTarget(mouse_move_handler_, &entered); + NotifyEnterExitOfDescendant(entered, ui::ET_MOUSE_ENTERED, v, + old_handler); + } + } + ui::MouseEvent moved_event(event, static_cast<View*>(this), + mouse_move_handler_); + mouse_move_handler_->OnMouseMoved(moved_event); + if (!(moved_event.flags() & ui::EF_IS_NON_CLIENT)) + widget_->SetCursor(mouse_move_handler_->GetCursor(moved_event)); + } else if (mouse_move_handler_ != NULL) { + MouseEnterExitEvent exited(event, ui::ET_MOUSE_EXITED); + DispatchEventToTarget(mouse_move_handler_, &exited); + NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, + mouse_move_handler_, v); + // On Aura the non-client area extends slightly outside the root view for + // some windows. Let the non-client cursor handling code set the cursor + // as we do above. + if (!(event.flags() & ui::EF_IS_NON_CLIENT)) + widget_->SetCursor(gfx::kNullCursor); + mouse_move_handler_ = NULL; + } +} + +void RootView::OnMouseExited(const ui::MouseEvent& event) { + if (mouse_move_handler_ != NULL) { + MouseEnterExitEvent exited(event, ui::ET_MOUSE_EXITED); + DispatchEventToTarget(mouse_move_handler_, &exited); + NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, + mouse_move_handler_, NULL); + mouse_move_handler_ = NULL; + } +} + +bool RootView::OnMouseWheel(const ui::MouseWheelEvent& event) { + for (View* v = GetEventHandlerForPoint(event.location()); + v && v != this && !event.handled(); v = v->parent()) + DispatchEventToTarget(v, const_cast<ui::MouseWheelEvent*>(&event)); + return event.handled(); +} + +void RootView::SetMouseHandler(View* new_mh) { + // If we're clearing the mouse handler, clear explicit_mouse_handler_ as well. + explicit_mouse_handler_ = (new_mh != NULL); + mouse_pressed_handler_ = new_mh; + gesture_handler_ = new_mh; + scroll_gesture_handler_ = new_mh; + drag_info_.Reset(); +} + +void RootView::GetAccessibleState(ui::AccessibleViewState* state) { + state->name = widget_->widget_delegate()->GetAccessibleWindowTitle(); + state->role = widget_->widget_delegate()->GetAccessibleWindowRole(); +} + +void RootView::UpdateParentLayer() { + if (layer()) + ReparentLayer(gfx::Vector2d(GetMirroredX(), y()), widget_->GetLayer()); +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, protected: + +void RootView::ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) { + widget_->ViewHierarchyChanged(details); + + if (!details.is_add) { + if (!explicit_mouse_handler_ && mouse_pressed_handler_ == details.child) + mouse_pressed_handler_ = NULL; + if (mouse_move_handler_ == details.child) + mouse_move_handler_ = NULL; + if (touch_pressed_handler_ == details.child) + touch_pressed_handler_ = NULL; + if (gesture_handler_ == details.child) + gesture_handler_ = NULL; + if (scroll_gesture_handler_ == details.child) + scroll_gesture_handler_ = NULL; + if (event_dispatch_target_ == details.child) + event_dispatch_target_ = NULL; + } +} + +void RootView::VisibilityChanged(View* /*starting_from*/, bool is_visible) { + if (!is_visible) { + // When the root view is being hidden (e.g. when widget is minimized) + // handlers are reset, so that after it is reshown, events are not captured + // by old handlers. + mouse_pressed_handler_ = NULL; + mouse_move_handler_ = NULL; + touch_pressed_handler_ = NULL; + gesture_handler_ = NULL; + scroll_gesture_handler_ = NULL; + event_dispatch_target_ = NULL; + } +} + +void RootView::OnPaint(gfx::Canvas* canvas) { + if (!layer() || !layer()->fills_bounds_opaquely()) + canvas->DrawColor(SK_ColorBLACK, SkXfermode::kClear_Mode); + + // TODO (pkotwicz): Remove this once we switch over to Aura desktop. + // This is needed so that we can set the background behind the RWHV when the + // RWHV is not visible. Not needed once there is a view between the RootView + // and RWHV. + View::OnPaint(canvas); +} + +gfx::Vector2d RootView::CalculateOffsetToAncestorWithLayer( + ui::Layer** layer_parent) { + gfx::Vector2d offset(View::CalculateOffsetToAncestorWithLayer(layer_parent)); + if (!layer() && layer_parent) + *layer_parent = widget_->GetLayer(); + return offset; +} + +View::DragInfo* RootView::GetDragInfo() { + return &drag_info_; +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, private: + +// Input ----------------------------------------------------------------------- + +void RootView::UpdateCursor(const ui::MouseEvent& event) { + if (!(event.flags() & ui::EF_IS_NON_CLIENT)) { + View* v = GetEventHandlerForPoint(event.location()); + ui::MouseEvent me(event, static_cast<View*>(this), v); + widget_->SetCursor(v->GetCursor(me)); + } +} + +void RootView::SetMouseLocationAndFlags(const ui::MouseEvent& event) { + last_mouse_event_flags_ = event.flags(); + last_mouse_event_x_ = event.x(); + last_mouse_event_y_ = event.y(); +} + +void RootView::DispatchEventToTarget(View* target, ui::Event* event) { + View* old_target = event_dispatch_target_; + event_dispatch_target_ = target; + if (DispatchEvent(target, event)) + event_dispatch_target_ = old_target; +} + +void RootView::NotifyEnterExitOfDescendant(const ui::MouseEvent& event, + ui::EventType type, + View* view, + View* sibling) { + for (View* p = view->parent(); p; p = p->parent()) { + if (!p->notify_enter_exit_on_child()) + continue; + if (sibling && p->Contains(sibling)) + break; + // It is necessary to recreate the notify-event for each dispatch, since one + // of the callbacks can mark the event as handled, and that would cause + // incorrect event dispatch. + MouseEnterExitEvent notify_event(event, type); + DispatchEventToTarget(p, ¬ify_event); + } +} + +bool RootView::CanDispatchToTarget(ui::EventTarget* target) { + return event_dispatch_target_ == target; +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/widget/root_view.h b/chromium/ui/views/widget/root_view.h new file mode 100644 index 00000000000..b89ab8fe8eb --- /dev/null +++ b/chromium/ui/views/widget/root_view.h @@ -0,0 +1,226 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_ROOT_VIEW_H_ +#define UI_VIEWS_WIDGET_ROOT_VIEW_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "ui/base/events/event_dispatcher.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/focus/focus_search.h" +#include "ui/views/view.h" + +namespace views { + +namespace test { +class WidgetTest; +} + +class Widget; + +// This is a views-internal API and should not be used externally. +// Widget exposes this object as a View*. +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// RootView class +// +// The RootView is the root of a View hierarchy. A RootView is attached to a +// Widget. The Widget is responsible for receiving events from the host +// environment, converting them to views-compatible events and then forwarding +// them to the RootView for propagation into the View hierarchy. +// +// A RootView can have only one child, called its "Contents View" which is +// sized to fill the bounds of the RootView (and hence the client area of the +// Widget). Call SetContentsView() after the associated Widget has been +// initialized to attach the contents view to the RootView. +// TODO(beng): Enforce no other callers to AddChildView/tree functions by +// overriding those methods as private here. +// TODO(beng): Clean up API further, make Widget a friend. +// TODO(sky): We don't really want to export this class. +// +class VIEWS_EXPORT RootView : public View, + public FocusTraversable, + public ui::EventDispatcherDelegate { + public: + static const char kViewClassName[]; + + // Creation and lifetime ----------------------------------------------------- + explicit RootView(Widget* widget); + virtual ~RootView(); + + // Tree operations ----------------------------------------------------------- + + // Sets the "contents view" of the RootView. This is the single child view + // that is responsible for laying out the contents of the widget. + void SetContentsView(View* contents_view); + View* GetContentsView(); + + // Called when parent of the host changed. + void NotifyNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view); + + // Input --------------------------------------------------------------------- + + // Process a key event. Send the event to the focused view and up the focus + // path, and finally to the default keyboard handler, until someone consumes + // it. Returns whether anyone consumed the event. + void DispatchKeyEvent(ui::KeyEvent* event); + void DispatchScrollEvent(ui::ScrollEvent* event); + void DispatchTouchEvent(ui::TouchEvent* event); + virtual void DispatchGestureEvent(ui::GestureEvent* event); + + // Focus --------------------------------------------------------------------- + + // Used to set the FocusTraversable parent after the view has been created + // (typically when the hierarchy changes and this RootView is added/removed). + virtual void SetFocusTraversableParent(FocusTraversable* focus_traversable); + + // Used to set the View parent after the view has been created. + virtual void SetFocusTraversableParentView(View* view); + + // System events ------------------------------------------------------------- + + // Public API for broadcasting theme change notifications to this View + // hierarchy. + void ThemeChanged(); + + // Public API for broadcasting locale change notifications to this View + // hierarchy. + void LocaleChanged(); + + // Overridden from FocusTraversable: + virtual FocusSearch* GetFocusSearch() OVERRIDE; + virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE; + virtual View* GetFocusTraversableParentView() OVERRIDE; + + // Overridden from View: + virtual const Widget* GetWidget() const OVERRIDE; + virtual Widget* GetWidget() OVERRIDE; + virtual bool IsDrawn() const OVERRIDE; + virtual const char* GetClassName() const OVERRIDE; + virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; + virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE; + virtual void SetMouseHandler(View* new_mouse_handler) OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void UpdateParentLayer() OVERRIDE; + + protected: + // Overridden from View: + virtual void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) OVERRIDE; + virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual gfx::Vector2d CalculateOffsetToAncestorWithLayer( + ui::Layer** layer_parent) OVERRIDE; + virtual View::DragInfo* GetDragInfo() OVERRIDE; + + private: + friend class ::views::View; + friend class ::views::Widget; + friend class ::views::test::WidgetTest; + + // Input --------------------------------------------------------------------- + + // Update the cursor given a mouse event. This is called by non mouse_move + // event handlers to honor the cursor desired by views located under the + // cursor during drag operations. The location of the mouse should be in the + // current coordinate system (i.e. any necessary transformation should be + // applied to the point prior to calling this). + void UpdateCursor(const ui::MouseEvent& event); + + // Updates the last_mouse_* fields from e. The location of the mouse should be + // in the current coordinate system (i.e. any necessary transformation should + // be applied to the point prior to calling this). + void SetMouseLocationAndFlags(const ui::MouseEvent& event); + + void DispatchEventToTarget(View* target, ui::Event* event); + + // |view| is the view receiving |event|. This function sends the event to all + // the Views up the hierarchy that has |notify_enter_exit_on_child_| flag + // turned on, but does not contain |sibling|. + void NotifyEnterExitOfDescendant(const ui::MouseEvent& event, + ui::EventType type, + View* view, + View* sibling); + + // Overridden from ui::EventDispatcherDelegate: + virtual bool CanDispatchToTarget(ui::EventTarget* target) OVERRIDE; + + ////////////////////////////////////////////////////////////////////////////// + + // Tree operations ----------------------------------------------------------- + + // The host Widget + Widget* widget_; + + // Input --------------------------------------------------------------------- + + // The view currently handing down - drag - up + View* mouse_pressed_handler_; + + // The view currently handling enter / exit + View* mouse_move_handler_; + + // The last view to handle a mouse click, so that we can determine if + // a double-click lands on the same view as its single-click part. + View* last_click_handler_; + + // true if mouse_pressed_handler_ has been explicitly set + bool explicit_mouse_handler_; + + // Last position/flag of a mouse press/drag. Used if capture stops and we need + // to synthesize a release. + int last_mouse_event_flags_; + int last_mouse_event_x_; + int last_mouse_event_y_; + + // The view currently handling touch events. + View* touch_pressed_handler_; + + // The view currently handling gesture events. When set, this handler receives + // all gesture events, except when there is an event handler for the specific + // gesture (e.g. scroll). + View* gesture_handler_; + + // The view currently handling scroll gesture events. + View* scroll_gesture_handler_; + + // Focus --------------------------------------------------------------------- + + // The focus search algorithm. + FocusSearch focus_search_; + + // Whether this root view belongs to the current active window. + // bool activated_; + + // The parent FocusTraversable, used for focus traversal. + FocusTraversable* focus_traversable_parent_; + + // The View that contains this RootView. This is used when we have RootView + // wrapped inside native components, and is used for the focus traversal. + View* focus_traversable_parent_view_; + + View* event_dispatch_target_; + + // Drag and drop ------------------------------------------------------------- + + // Tracks drag state for a view. + View::DragInfo drag_info_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RootView); +}; + +} // namespace internal +} // namespace views + +#endif // UI_VIEWS_WIDGET_ROOT_VIEW_H_ diff --git a/chromium/ui/views/widget/tooltip_manager.cc b/chromium/ui/views/widget/tooltip_manager.cc new file mode 100644 index 00000000000..bfcbf02b47d --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/tooltip_manager.h" + +#include <vector> + +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/text/text_elider.h" + +namespace { + +// Maximum number of characters we allow in a tooltip. +const size_t kMaxTooltipLength = 1024; + +// Maximum number of lines we allow in the tooltip. +const size_t kMaxLines = 6; + +} // namespace + +namespace views { + +// static +void TooltipManager::TrimTooltipToFit(string16* text, + int* max_width, + int* line_count, + int x, + int y, + gfx::NativeView context) { + *max_width = 0; + *line_count = 0; + + // Clamp the tooltip length to kMaxTooltipLength so that we don't + // accidentally DOS the user with a mega tooltip. + if (text->length() > kMaxTooltipLength) + *text = text->substr(0, kMaxTooltipLength); + + // Determine the available width for the tooltip. + int available_width = GetMaxWidth(x, y, context); + + // Split the string into at most kMaxLines lines. + std::vector<string16> lines; + base::SplitString(*text, '\n', &lines); + if (lines.size() > kMaxLines) + lines.resize(kMaxLines); + *line_count = static_cast<int>(lines.size()); + + // Format each line to fit. + gfx::Font font = GetDefaultFont(); + string16 result; + for (std::vector<string16>::iterator i = lines.begin(); i != lines.end(); + ++i) { + string16 elided_text = + ui::ElideText(*i, font, available_width, ui::ELIDE_AT_END); + *max_width = std::max(*max_width, font.GetStringWidth(elided_text)); + if (!result.empty()) + result.push_back('\n'); + result.append(elided_text); + } + *text = result; +} + +} // namespace views diff --git a/chromium/ui/views/widget/tooltip_manager.h b/chromium/ui/views/widget/tooltip_manager.h new file mode 100644 index 00000000000..f8601dd6ae3 --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager.h @@ -0,0 +1,71 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_TOOLTIP_MANAGER_H_ +#define UI_VIEWS_WIDGET_TOOLTIP_MANAGER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/views_export.h" + +namespace gfx { +class Font; +} // namespace gfx + +namespace views { + +class View; + +// TooltipManager takes care of the wiring to support tooltips for Views. You +// almost never need to interact directly with TooltipManager, rather look to +// the various tooltip methods on View. +class VIEWS_EXPORT TooltipManager { + public: + // Returns the height of tooltips. This should only be invoked from within + // GetTooltipTextOrigin. + static int GetTooltipHeight(); + + // Returns the default font used by tooltips. + static gfx::Font GetDefaultFont(); + + // Returns the maximum width of the tooltip. |x| and |y| give the location + // the tooltip is to be displayed on in screen coordinates. |context| is + // used to determine which gfx::Screen should be used. + static int GetMaxWidth(int x, int y, gfx::NativeView context); + + TooltipManager() {} + virtual ~TooltipManager() {} + + // Notification that the view hierarchy has changed in some way. + virtual void UpdateTooltip() = 0; + + // Invoked when the tooltip text changes for the specified views. + virtual void TooltipTextChanged(View* view) = 0; + + // Invoked when toolbar icon gets focus. + virtual void ShowKeyboardTooltip(View* view) = 0; + + // Invoked when toolbar loses focus. + virtual void HideKeyboardTooltip() = 0; + + protected: + // Trims the tooltip to fit, setting |text| to the clipped result, + // |max_width| to the width (in pixels) of the clipped text and |line_count| + // to the number of lines of text in the tooltip. |x| and |y| give the + // location of the tooltip in screen coordinates. |context| is used to + // determine which gfx::Screen should be used. + static void TrimTooltipToFit(string16* text, + int* max_width, + int* line_count, + int x, + int y, + gfx::NativeView context); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_TOOLTIP_MANAGER_H_ diff --git a/chromium/ui/views/widget/tooltip_manager_aura.cc b/chromium/ui/views/widget/tooltip_manager_aura.cc new file mode 100644 index 00000000000..f7a47613fc3 --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager_aura.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/tooltip_manager_aura.h" + +#include "base/logging.h" +#include "ui/aura/client/tooltip_client.h" +#include "ui/aura/root_window.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/font.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/widget.h" + +namespace views { + +// static +int TooltipManager::GetTooltipHeight() { + // Not used for linux and chromeos. + NOTIMPLEMENTED(); + return 0; +} + +// static +gfx::Font TooltipManager::GetDefaultFont() { + return ui::ResourceBundle::GetSharedInstance().GetFont( + ui::ResourceBundle::BaseFont); +} + +// static +int TooltipManager::GetMaxWidth(int x, int y, gfx::NativeView context) { + gfx::Rect monitor_bounds = + gfx::Screen::GetScreenFor(context)->GetDisplayNearestPoint( + gfx::Point(x, y)).bounds(); + return (monitor_bounds.width() + 1) / 2; +} + +//////////////////////////////////////////////////////////////////////////////// +// TooltipManagerAura public: + +TooltipManagerAura::TooltipManagerAura(aura::Window* window, Widget* widget) + : window_(window), + widget_(widget) { + aura::client::SetTooltipText(window_, &tooltip_text_); +} + +TooltipManagerAura::~TooltipManagerAura() { + aura::client::SetTooltipText(window_, NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +// TooltipManagerAura, TooltipManager implementation: + +void TooltipManagerAura::UpdateTooltip() { + aura::RootWindow* root_window = window_->GetRootWindow(); + if (aura::client::GetTooltipClient(root_window)) { + gfx::Point view_point = root_window->GetLastMouseLocationInRoot(); + aura::Window::ConvertPointToTarget(root_window, window_, &view_point); + View* view = GetViewUnderPoint(view_point); + UpdateTooltipForTarget(view, view_point, root_window); + } +} + +void TooltipManagerAura::TooltipTextChanged(View* view) { + aura::RootWindow* root_window = window_->GetRootWindow(); + if (aura::client::GetTooltipClient(root_window)) { + gfx::Point view_point = root_window->GetLastMouseLocationInRoot(); + aura::Window::ConvertPointToTarget(root_window, window_, &view_point); + View* target = GetViewUnderPoint(view_point); + if (target != view) + return; + UpdateTooltipForTarget(view, view_point, root_window); + } +} + +void TooltipManagerAura::ShowKeyboardTooltip(View* view) { + NOTREACHED(); +} + +void TooltipManagerAura::HideKeyboardTooltip() { + NOTREACHED(); +} + +View* TooltipManagerAura::GetViewUnderPoint(const gfx::Point& point) { + View* root_view = widget_->GetRootView(); + if (root_view) + return root_view->GetTooltipHandlerForPoint(point); + return NULL; +} + +void TooltipManagerAura::UpdateTooltipForTarget(View* target, + const gfx::Point& point, + aura::RootWindow* root_window) { + if (target) { + gfx::Point view_point = point; + View::ConvertPointFromWidget(target, &view_point); + string16 new_tooltip_text; + if (!target->GetTooltipText(view_point, &new_tooltip_text)) + tooltip_text_.clear(); + else + tooltip_text_ = new_tooltip_text; + } else { + tooltip_text_.clear(); + } + aura::client::GetTooltipClient(root_window)->UpdateTooltip(window_); +} + +} // namespace views. diff --git a/chromium/ui/views/widget/tooltip_manager_aura.h b/chromium/ui/views/widget/tooltip_manager_aura.h new file mode 100644 index 00000000000..dd4b5ade439 --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager_aura.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_TOOLTIP_MANAGER_AURA_H_ +#define UI_VIEWS_WIDGET_TOOLTIP_MANAGER_AURA_H_ + +#include "base/compiler_specific.h" +#include "base/strings/string16.h" +#include "ui/gfx/point.h" +#include "ui/views/widget/tooltip_manager.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace views { + +class Widget; + +// TooltipManager implementation for Aura. +class TooltipManagerAura : public TooltipManager { + public: + TooltipManagerAura(aura::Window* window, Widget* widget); + virtual ~TooltipManagerAura(); + + // TooltipManager. + virtual void UpdateTooltip() OVERRIDE; + virtual void TooltipTextChanged(View* view) OVERRIDE; + virtual void ShowKeyboardTooltip(View* view) OVERRIDE; + virtual void HideKeyboardTooltip() OVERRIDE; + + private: + View* GetViewUnderPoint(const gfx::Point& point); + void UpdateTooltipForTarget(View* target, + const gfx::Point& point, + aura::RootWindow* root_window); + + aura::Window* window_; + Widget* widget_; + string16 tooltip_text_; + + DISALLOW_COPY_AND_ASSIGN(TooltipManagerAura); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_TOOLTIP_MANAGER_AURA_H_ diff --git a/chromium/ui/views/widget/tooltip_manager_win.cc b/chromium/ui/views/widget/tooltip_manager_win.cc new file mode 100644 index 00000000000..fa0ad45d711 --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager_win.cc @@ -0,0 +1,399 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/tooltip_manager_win.h" + +#include <windowsx.h> + +#include <limits> + +#include "base/bind.h" +#include "base/i18n/rtl.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_util.h" +#include "base/win/scoped_hdc.h" +#include "base/win/scoped_select_object.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/win/dpi.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/base/win/scoped_set_map_mode.h" +#include "ui/gfx/font.h" +#include "ui/gfx/screen.h" +#include "ui/views/view.h" +#include "ui/views/widget/monitor_win.h" +#include "ui/views/widget/widget.h" + +namespace views { + +static int tooltip_height_ = 0; + +// Default timeout for the tooltip displayed using keyboard. +// Timeout is measured in milliseconds. +static const int kDefaultTimeout = 4000; + +// static +int TooltipManager::GetTooltipHeight() { + DCHECK_GT(tooltip_height_, 0); + return tooltip_height_; +} + +static gfx::Font DetermineDefaultFont() { + HWND window = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!window) + return gfx::Font(); + HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0)); + gfx::Font font = hfont ? gfx::Font(hfont) : gfx::Font(); + DestroyWindow(window); + return font; +} + +// static +gfx::Font TooltipManager::GetDefaultFont() { + static gfx::Font* font = NULL; + if (!font) + font = new gfx::Font(DetermineDefaultFont()); + return *font; +} + +// static +int TooltipManager::GetMaxWidth(int x, int y, gfx::NativeView context) { + gfx::Rect monitor_bounds = + gfx::Screen::GetScreenFor(context)->GetDisplayNearestPoint( + gfx::Point(x, y)).bounds(); + // Allow the tooltip to be almost as wide as the screen. + // Otherwise, we would truncate important text, since we're not word-wrapping + // the text onto multiple lines. + return monitor_bounds.width() == 0 ? 800 : monitor_bounds.width() - 30; +} + +TooltipManagerWin::TooltipManagerWin(Widget* widget) + : widget_(widget), + tooltip_hwnd_(NULL), + last_mouse_pos_(-1, -1), + tooltip_showing_(false), + last_tooltip_view_(NULL), + last_view_out_of_sync_(false), + tooltip_width_(0), + keyboard_tooltip_hwnd_(NULL), + keyboard_tooltip_factory_(this) { + DCHECK(widget); + DCHECK(widget->GetNativeView()); +} + +TooltipManagerWin::~TooltipManagerWin() { + if (tooltip_hwnd_) + DestroyWindow(tooltip_hwnd_); + if (keyboard_tooltip_hwnd_) + DestroyWindow(keyboard_tooltip_hwnd_); +} + +bool TooltipManagerWin::Init() { + DCHECK(!tooltip_hwnd_); + // Create the tooltip control. + tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, + GetParent(), NULL, NULL, NULL); + if (!tooltip_hwnd_) + return false; + + l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); + + // This effectively turns off clipping of tooltips. We need this otherwise + // multi-line text (\r\n) won't work right. The size doesn't really matter + // (just as long as its bigger than the monitor's width) as we clip to the + // screen size before rendering. + SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, + std::numeric_limits<int16>::max()); + + // Add one tool that is used for all tooltips. + toolinfo_.cbSize = sizeof(toolinfo_); + toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND; + toolinfo_.hwnd = GetParent(); + toolinfo_.uId = reinterpret_cast<UINT_PTR>(GetParent()); + // Setting this tells windows to call GetParent() back (using a WM_NOTIFY + // message) for the actual tooltip contents. + toolinfo_.lpszText = LPSTR_TEXTCALLBACK; + toolinfo_.lpReserved = NULL; + SetRectEmpty(&toolinfo_.rect); + SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_); + return true; +} + +gfx::NativeView TooltipManagerWin::GetParent() { + return widget_->GetNativeView(); +} + +void TooltipManagerWin::UpdateTooltip() { + // Set last_view_out_of_sync_ to indicate the view is currently out of sync. + // This doesn't update the view under the mouse immediately as it may cause + // timing problems. + last_view_out_of_sync_ = true; + last_tooltip_view_ = NULL; + // Hide the tooltip. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); +} + +void TooltipManagerWin::TooltipTextChanged(View* view) { + if (view == last_tooltip_view_) + UpdateTooltip(last_mouse_pos_); +} + +LRESULT TooltipManagerWin::OnNotify(int w_param, + NMHDR* l_param, + bool* handled) { + *handled = false; + if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) { + switch (l_param->code) { + case TTN_GETDISPINFO: { + if (last_view_out_of_sync_) { + // View under the mouse is out of sync, determine it now. + View* root_view = widget_->GetRootView(); + last_tooltip_view_ = + root_view->GetTooltipHandlerForPoint(last_mouse_pos_); + last_view_out_of_sync_ = false; + } + // Tooltip control is asking for the tooltip to display. + NMTTDISPINFOW* tooltip_info = + reinterpret_cast<NMTTDISPINFOW*>(l_param); + // Initialize the string, if we have a valid tooltip the string will + // get reset below. + tooltip_info->szText[0] = TEXT('\0'); + tooltip_text_.clear(); + tooltip_info->lpszText = NULL; + clipped_text_.clear(); + if (last_tooltip_view_ != NULL) { + tooltip_text_.clear(); + // Mouse is over a View, ask the View for its tooltip. + gfx::Point view_loc = last_mouse_pos_; + View::ConvertPointToTarget(widget_->GetRootView(), + last_tooltip_view_, &view_loc); + if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) && + !tooltip_text_.empty()) { + // View has a valid tip, copy it into TOOLTIPINFO. + clipped_text_ = tooltip_text_; + gfx::Point screen_loc = last_mouse_pos_; + View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); + TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, + screen_loc.x(), screen_loc.y(), + widget_->GetNativeView()); + // Adjust the clipped tooltip text for locale direction. + base::i18n::AdjustStringForLocaleDirection(&clipped_text_); + tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str()); + } else { + tooltip_text_.clear(); + } + } + *handled = true; + return 0; + } + case TTN_POP: + tooltip_showing_ = false; + *handled = true; + return 0; + case TTN_SHOW: { + *handled = true; + tooltip_showing_ = true; + // The tooltip is about to show, allow the view to position it + gfx::Point text_origin; + if (tooltip_height_ == 0) + tooltip_height_ = CalcTooltipHeight(); + gfx::Point view_loc = last_mouse_pos_; + View::ConvertPointToTarget(widget_->GetRootView(), + last_tooltip_view_, &view_loc); + if (last_tooltip_view_->GetTooltipTextOrigin(view_loc, &text_origin) && + SetTooltipPosition(text_origin.x(), text_origin.y())) { + // Return true, otherwise the rectangle we specified is ignored. + return TRUE; + } + return 0; + } + default: + // Fall through. + break; + } + } + return 0; +} + +bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) { + // NOTE: this really only tests that the y location fits on screen, but that + // is good enough for our usage. + + // Calculate the bounds the tooltip will get. + gfx::Point view_loc; + View::ConvertPointToScreen(last_tooltip_view_, &view_loc); + view_loc = ui::win::DIPToScreenPoint(view_loc); + RECT bounds = { view_loc.x() + text_x, + view_loc.y() + text_y, + view_loc.x() + text_x + tooltip_width_, + view_loc.y() + line_count_ * GetTooltipHeight() }; + SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds); + + // Make sure the rectangle completely fits on the current monitor. If it + // doesn't, return false so that windows positions the tooltip at the + // default location. + gfx::Rect monitor_bounds = + views::GetMonitorBoundsForRect(gfx::Rect(bounds.left, bounds.right, + 0, 0)); + if (!monitor_bounds.Contains(gfx::Rect(bounds))) { + return false; + } + + ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); + return true; +} + +int TooltipManagerWin::CalcTooltipHeight() { + // Ask the tooltip for its font. + int height; + HFONT hfont = reinterpret_cast<HFONT>( + SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0)); + if (hfont != NULL) { + base::win::ScopedGetDC dc(tooltip_hwnd_); + base::win::ScopedSelectObject font(dc, hfont); + ui::ScopedSetMapMode mode(dc, MM_TEXT); + TEXTMETRIC font_metrics; + GetTextMetrics(dc, &font_metrics); + height = font_metrics.tmHeight; + } else { + // Tooltip is using the system font. Use gfx::Font, which should pick + // up the system font. + height = gfx::Font().GetHeight(); + } + // Get the margins from the tooltip + RECT tooltip_margin; + SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); + return height + tooltip_margin.top + tooltip_margin.bottom; +} + +void TooltipManagerWin::UpdateTooltip(const gfx::Point& mouse_pos) { + View* root_view = widget_->GetRootView(); + View* view = root_view->GetTooltipHandlerForPoint(mouse_pos); + if (view != last_tooltip_view_) { + // NOTE: This *must* be sent regardless of the visibility of the tooltip. + // It triggers Windows to ask for the tooltip again. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + last_tooltip_view_ = view; + } else if (last_tooltip_view_ != NULL) { + // Tooltip is showing, and mouse is over the same view. See if the tooltip + // text has changed. + gfx::Point view_point = mouse_pos; + View::ConvertPointToTarget(root_view, last_tooltip_view_, &view_point); + string16 new_tooltip_text; + bool has_tooltip_text = + last_tooltip_view_->GetTooltipText(view_point, &new_tooltip_text); + if (!has_tooltip_text || (new_tooltip_text != tooltip_text_)) { + // The text has changed, hide the popup. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + if (has_tooltip_text && !new_tooltip_text.empty() && tooltip_showing_) { + // New text is valid, show the popup. + SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); + } + } + } +} + +void TooltipManagerWin::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) { + gfx::Point mouse_pos_in_pixels(l_param); + gfx::Point mouse_pos = ui::win::ScreenToDIPPoint(mouse_pos_in_pixels); + + if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { + // NC message coordinates are in screen coordinates. + POINT temp = mouse_pos_in_pixels.ToPOINT(); + ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1); + mouse_pos_in_pixels.SetPoint(temp.x, temp.y); + mouse_pos = ui::win::ScreenToDIPPoint(mouse_pos_in_pixels); + } + + if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) { + last_mouse_pos_ = mouse_pos; + HideKeyboardTooltip(); + UpdateTooltip(mouse_pos); + } + // Forward the message onto the tooltip. + MSG msg; + msg.hwnd = GetParent(); + msg.message = u_msg; + msg.wParam = w_param; + msg.lParam = l_param; + SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); +} + +void TooltipManagerWin::ShowKeyboardTooltip(View* focused_view) { + if (tooltip_showing_) { + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + tooltip_text_.clear(); + } + HideKeyboardTooltip(); + string16 tooltip_text; + if (!focused_view->GetTooltipText(gfx::Point(), &tooltip_text)) + return; + gfx::Rect focused_bounds = focused_view->bounds(); + gfx::Point screen_point; + focused_view->ConvertPointToScreen(focused_view, &screen_point); + keyboard_tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!keyboard_tooltip_hwnd_) + return; + + SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, + std::numeric_limits<int16>::max()); + int tooltip_width; + int line_count; + TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count, + screen_point.x(), screen_point.y(), + widget_->GetNativeView()); + ReplaceSubstringsAfterOffset(&tooltip_text, 0, L"\n", L"\r\n"); + TOOLINFO keyboard_toolinfo; + memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo)); + keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo); + keyboard_toolinfo.hwnd = GetParent(); + keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND; + keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str()); + SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0, + reinterpret_cast<LPARAM>(&keyboard_toolinfo)); + SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, + reinterpret_cast<LPARAM>(&keyboard_toolinfo)); + if (!tooltip_height_) + tooltip_height_ = CalcTooltipHeight(); + RECT rect_bounds = {screen_point.x(), + screen_point.y() + focused_bounds.height(), + screen_point.x() + tooltip_width, + screen_point.y() + focused_bounds.height() + + line_count * tooltip_height_ }; + gfx::Rect monitor_bounds = + views::GetMonitorBoundsForRect(gfx::Rect(rect_bounds)); + gfx::Rect fitted_bounds = gfx::Rect(rect_bounds); + fitted_bounds.AdjustToFit(monitor_bounds); + rect_bounds = fitted_bounds.ToRECT(); + ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left, + rect_bounds.top, 0, 0, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&TooltipManagerWin::DestroyKeyboardTooltipWindow, + keyboard_tooltip_factory_.GetWeakPtr(), + keyboard_tooltip_hwnd_), + base::TimeDelta::FromMilliseconds(kDefaultTimeout)); +} + +void TooltipManagerWin::HideKeyboardTooltip() { + if (keyboard_tooltip_hwnd_ != NULL) { + SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0); + keyboard_tooltip_hwnd_ = NULL; + } +} + +void TooltipManagerWin::DestroyKeyboardTooltipWindow(HWND window_to_destroy) { + if (keyboard_tooltip_hwnd_ == window_to_destroy) + HideKeyboardTooltip(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/tooltip_manager_win.h b/chromium/ui/views/widget/tooltip_manager_win.h new file mode 100644 index 00000000000..cfb12256a7f --- /dev/null +++ b/chromium/ui/views/widget/tooltip_manager_win.h @@ -0,0 +1,153 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ +#define UI_VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ + +#include <windows.h> +#include <commctrl.h> +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/point.h" +#include "ui/views/widget/tooltip_manager.h" + +namespace gfx { +class Point; +} + +namespace views { + +class View; +class Widget; + +// TooltipManager implementation for Windows. +// +// This class is intended to be used by NativeWidgetWin. To use this, you must +// do the following: +// Add the following to your MSG_MAP: +// +// MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) +// MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, OnMouseRange) +// MSG_WM_NOTIFY(OnNotify) +// +// With the following implementations: +// LRESULT XXX::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param, +// BOOL& handled) { +// tooltip_manager_->OnMouse(u_msg, w_param, l_param); +// handled = FALSE; +// return 0; +// } +// +// LRESULT XXX::OnNotify(int w_param, NMHDR* l_param) { +// bool handled; +// LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled); +// SetMsgHandled(handled); +// return result; +// } +// +// And of course you'll need to create the TooltipManager! +// +// Lastly, you'll need to override GetTooltipManager. +// +// See NativeWidgetWin for an example of this in action. +class TooltipManagerWin : public TooltipManager { + public: + // Creates a TooltipManager for the specified Widget and parent window. + explicit TooltipManagerWin(Widget* widget); + virtual ~TooltipManagerWin(); + + // Initializes the TooltipManager returning whether initialization was + // successful. If this returns false the TooltipManager should be destroyed + // and not used. + bool Init(); + + // Notification that the view hierarchy has changed in some way. + virtual void UpdateTooltip() OVERRIDE; + + // Invoked when the tooltip text changes for the specified views. + virtual void TooltipTextChanged(View* view) OVERRIDE; + + // Invoked when toolbar icon gets focus. + virtual void ShowKeyboardTooltip(View* view) OVERRIDE; + + // Invoked when toolbar loses focus. + virtual void HideKeyboardTooltip() OVERRIDE; + + // Message handlers. These forward to the tooltip control. + virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param); + LRESULT OnNotify(int w_param, NMHDR* l_param, bool* handled); + + protected: + // Returns the Widget we're showing tooltips for. + gfx::NativeView GetParent(); + + // Updates the tooltip for the specified location. + void UpdateTooltip(const gfx::Point& location); + + // Tooltip control window. + HWND tooltip_hwnd_; + + // Tooltip information. + TOOLINFO toolinfo_; + + // Last location of the mouse. This is in the coordinates of the rootview. + gfx::Point last_mouse_pos_; + + // Whether or not the tooltip is showing. + bool tooltip_showing_; + + private: + // Sets the tooltip position based on the x/y position of the text. If the + // tooltip fits, true is returned. + bool SetTooltipPosition(int text_x, int text_y); + + // Calculates the preferred height for tooltips. This always returns a + // positive value. + int CalcTooltipHeight(); + + // Invoked when the timer elapses and tooltip has to be destroyed. + void DestroyKeyboardTooltipWindow(HWND window_to_destroy); + + // Hosting Widget. + Widget* widget_; + + // The View the mouse is under. This is null if the mouse isn't under a + // View. + View* last_tooltip_view_; + + // Whether or not the view under the mouse needs to be refreshed. If this + // is true, when the tooltip is asked for the view under the mouse is + // refreshed. + bool last_view_out_of_sync_; + + // Text for tooltip from the view. + string16 tooltip_text_; + + // The clipped tooltip. + string16 clipped_text_; + + // Number of lines in the tooltip. + int line_count_; + + // Width of the last tooltip. + int tooltip_width_; + + // control window for tooltip displayed using keyboard. + HWND keyboard_tooltip_hwnd_; + + // Used to register DestroyTooltipWindow function with PostDelayedTask + // function. + base::WeakPtrFactory<TooltipManagerWin> keyboard_tooltip_factory_; + + DISALLOW_COPY_AND_ASSIGN(TooltipManagerWin); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc new file mode 100644 index 00000000000..e97c950261b --- /dev/null +++ b/chromium/ui/views/widget/widget.cc @@ -0,0 +1,1410 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/widget.h" + +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/default_theme_provider.h" +#include "ui/base/events/event.h" +#include "ui/base/hit_test.h" +#include "ui/base/l10n/l10n_font_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/compositor.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/screen.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/focus/focus_manager_factory.h" +#include "ui/views/focus/view_storage.h" +#include "ui/views/focus/widget_focus_manager.h" +#include "ui/views/ime/input_method.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/native_widget_private.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/tooltip_manager.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_deletion_observer.h" +#include "ui/views/widget/widget_observer.h" +#include "ui/views/window/custom_frame_view.h" + +#if !defined(OS_MACOSX) +#include "ui/views/controls/menu/menu_controller.h" +#endif + +namespace views { + +namespace { + +// If |view| has a layer the layer is added to |layers|. Else this recurses +// through the children. This is used to build a list of the layers created by +// views that are direct children of the Widgets layer. +void BuildRootLayers(View* view, std::vector<ui::Layer*>* layers) { + if (view->layer()) { + layers->push_back(view->layer()); + } else { + for (int i = 0; i < view->child_count(); ++i) + BuildRootLayers(view->child_at(i), layers); + } +} + +// Create a native widget implementation. +// First, use the supplied one if non-NULL. +// Finally, make a default one. +NativeWidget* CreateNativeWidget(NativeWidget* native_widget, + internal::NativeWidgetDelegate* delegate) { + if (!native_widget) { + native_widget = + internal::NativeWidgetPrivate::CreateNativeWidget(delegate); + } + return native_widget; +} + +} // namespace + +// A default implementation of WidgetDelegate, used by Widget when no +// WidgetDelegate is supplied. +class DefaultWidgetDelegate : public WidgetDelegate { + public: + DefaultWidgetDelegate(Widget* widget, const Widget::InitParams& params) + : widget_(widget), + can_activate_(!params.child && + params.type != Widget::InitParams::TYPE_POPUP) { + } + virtual ~DefaultWidgetDelegate() {} + + // Overridden from WidgetDelegate: + virtual void DeleteDelegate() OVERRIDE { + delete this; + } + virtual Widget* GetWidget() OVERRIDE { + return widget_; + } + virtual const Widget* GetWidget() const OVERRIDE { + return widget_; + } + + virtual bool CanActivate() const OVERRIDE { + return can_activate_; + } + + private: + Widget* widget_; + bool can_activate_; + + DISALLOW_COPY_AND_ASSIGN(DefaultWidgetDelegate); +}; + +//////////////////////////////////////////////////////////////////////////////// +// Widget, InitParams: + +Widget::InitParams::InitParams() + : type(TYPE_WINDOW), + delegate(NULL), + child(false), + opacity((ViewsDelegate::views_delegate && + ViewsDelegate::views_delegate->UseTransparentWindows()) ? + TRANSLUCENT_WINDOW : INFER_OPACITY), + accept_events(true), + can_activate(true), + keep_on_top(false), + ownership(NATIVE_WIDGET_OWNS_WIDGET), + mirror_origin_in_rtl(false), + has_dropshadow(false), + remove_standard_frame(false), + use_system_default_icon(false), + show_state(ui::SHOW_STATE_DEFAULT), + double_buffer(false), + parent(NULL), + native_widget(NULL), + desktop_root_window_host(NULL), + top_level(false), + layer_type(ui::LAYER_TEXTURED), + context(NULL) { +} + +Widget::InitParams::InitParams(Type type) + : type(type), + delegate(NULL), + child(type == TYPE_CONTROL), + opacity(((type == TYPE_WINDOW || type == TYPE_PANEL) && + ViewsDelegate::views_delegate && + ViewsDelegate::views_delegate->UseTransparentWindows()) ? + TRANSLUCENT_WINDOW : INFER_OPACITY), + accept_events(true), + can_activate(type != TYPE_POPUP && type != TYPE_MENU), + keep_on_top(type == TYPE_MENU), + ownership(NATIVE_WIDGET_OWNS_WIDGET), + mirror_origin_in_rtl(false), + has_dropshadow(false), + remove_standard_frame(false), + use_system_default_icon(false), + show_state(ui::SHOW_STATE_DEFAULT), + double_buffer(false), + parent(NULL), + native_widget(NULL), + desktop_root_window_host(NULL), + top_level(false), + layer_type(ui::LAYER_TEXTURED), + context(NULL) { +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +Widget::Widget() + : native_widget_(NULL), + widget_delegate_(NULL), + non_client_view_(NULL), + dragged_view_(NULL), + ownership_(InitParams::NATIVE_WIDGET_OWNS_WIDGET), + is_secondary_widget_(true), + frame_type_(FRAME_TYPE_DEFAULT), + disable_inactive_rendering_(false), + widget_closed_(false), + saved_show_state_(ui::SHOW_STATE_DEFAULT), + focus_on_creation_(true), + is_top_level_(false), + native_widget_initialized_(false), + native_widget_destroyed_(false), + is_mouse_button_pressed_(false), + is_touch_down_(false), + last_mouse_event_was_move_(false), + root_layers_dirty_(false), + movement_disabled_(false) { +} + +Widget::~Widget() { + DestroyRootView(); + if (ownership_ == InitParams::WIDGET_OWNS_NATIVE_WIDGET) { + delete native_widget_; + } else { + DCHECK(native_widget_destroyed_) + << "Destroying a widget with a live native widget. " + << "Widget probably should use WIDGET_OWNS_NATIVE_WIDGET ownership."; + } +} + +// static +Widget* Widget::CreateWindow(WidgetDelegate* delegate) { + return CreateWindowWithBounds(delegate, gfx::Rect()); +} + +// static +Widget* Widget::CreateWindowWithBounds(WidgetDelegate* delegate, + const gfx::Rect& bounds) { + Widget* widget = new Widget; + Widget::InitParams params; + params.bounds = bounds; + params.delegate = delegate; + params.top_level = true; + widget->Init(params); + return widget; +} + +// static +Widget* Widget::CreateWindowWithParent(WidgetDelegate* delegate, + gfx::NativeWindow parent) { + return CreateWindowWithParentAndBounds(delegate, parent, gfx::Rect()); +} + +// static +Widget* Widget::CreateWindowWithParentAndBounds(WidgetDelegate* delegate, + gfx::NativeWindow parent, + const gfx::Rect& bounds) { + Widget* widget = new Widget; + Widget::InitParams params; + params.delegate = delegate; + params.parent = parent; + params.bounds = bounds; + widget->Init(params); + return widget; +} + +// static +Widget* Widget::CreateWindowWithContext(WidgetDelegate* delegate, + gfx::NativeView context) { + return CreateWindowWithContextAndBounds(delegate, context, gfx::Rect()); +} + +// static +Widget* Widget::CreateWindowWithContextAndBounds(WidgetDelegate* delegate, + gfx::NativeView context, + const gfx::Rect& bounds) { + Widget* widget = new Widget; + Widget::InitParams params; + params.delegate = delegate; + params.context = context; + params.bounds = bounds; + widget->Init(params); + return widget; +} + +// static +Widget* Widget::GetWidgetForNativeView(gfx::NativeView native_view) { + internal::NativeWidgetPrivate* native_widget = + internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(native_view); + return native_widget ? native_widget->GetWidget() : NULL; +} + +// static +Widget* Widget::GetWidgetForNativeWindow(gfx::NativeWindow native_window) { + internal::NativeWidgetPrivate* native_widget = + internal::NativeWidgetPrivate::GetNativeWidgetForNativeWindow( + native_window); + return native_widget ? native_widget->GetWidget() : NULL; +} + +// static +Widget* Widget::GetTopLevelWidgetForNativeView(gfx::NativeView native_view) { + internal::NativeWidgetPrivate* native_widget = + internal::NativeWidgetPrivate::GetTopLevelNativeWidget(native_view); + return native_widget ? native_widget->GetWidget() : NULL; +} + + +// static +void Widget::GetAllChildWidgets(gfx::NativeView native_view, + Widgets* children) { + internal::NativeWidgetPrivate::GetAllChildWidgets(native_view, children); +} + +// static +void Widget::ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent) { + internal::NativeWidgetPrivate::ReparentNativeView(native_view, new_parent); +} + +// static +int Widget::GetLocalizedContentsWidth(int col_resource_id) { + return ui::GetLocalizedContentsWidthForFont(col_resource_id, + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); +} + +// static +int Widget::GetLocalizedContentsHeight(int row_resource_id) { + return ui::GetLocalizedContentsHeightForFont(row_resource_id, + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); +} + +// static +gfx::Size Widget::GetLocalizedContentsSize(int col_resource_id, + int row_resource_id) { + return gfx::Size(GetLocalizedContentsWidth(col_resource_id), + GetLocalizedContentsHeight(row_resource_id)); +} + +// static +bool Widget::RequiresNonClientView(InitParams::Type type) { + return type == InitParams::TYPE_WINDOW || + type == InitParams::TYPE_PANEL || + type == InitParams::TYPE_BUBBLE; +} + +void Widget::Init(const InitParams& in_params) { + TRACE_EVENT0("views", "Widget::Init"); + InitParams params = in_params; + + is_top_level_ = params.top_level || + (!params.child && + params.type != InitParams::TYPE_CONTROL && + params.type != InitParams::TYPE_TOOLTIP); + params.top_level = is_top_level_; + if (params.opacity == InitParams::INFER_OPACITY) { +#if defined(OS_WIN) && defined(USE_AURA) + // By default, make all top-level windows but the main window transparent + // initially so that they can be made to fade in. + if (is_top_level_ && params.type != InitParams::TYPE_WINDOW) + params.opacity = InitParams::TRANSLUCENT_WINDOW; + else + params.opacity = InitParams::OPAQUE_WINDOW; +#else + params.opacity = InitParams::OPAQUE_WINDOW; +#endif + } + + if (ViewsDelegate::views_delegate) + ViewsDelegate::views_delegate->OnBeforeWidgetInit(¶ms, this); + + widget_delegate_ = params.delegate ? + params.delegate : new DefaultWidgetDelegate(this, params); + ownership_ = params.ownership; + native_widget_ = CreateNativeWidget(params.native_widget, this)-> + AsNativeWidgetPrivate(); + root_view_.reset(CreateRootView()); + default_theme_provider_.reset(new ui::DefaultThemeProvider); + if (params.type == InitParams::TYPE_MENU) { + is_mouse_button_pressed_ = + internal::NativeWidgetPrivate::IsMouseButtonDown(); + } + native_widget_->InitNativeWidget(params); + if (RequiresNonClientView(params.type)) { + non_client_view_ = new NonClientView; + non_client_view_->SetFrameView(CreateNonClientFrameView()); + // Create the ClientView, add it to the NonClientView and add the + // NonClientView to the RootView. This will cause everything to be parented. + non_client_view_->set_client_view(widget_delegate_->CreateClientView(this)); + non_client_view_->SetOverlayView(widget_delegate_->CreateOverlayView()); + SetContentsView(non_client_view_); + // Initialize the window's title before setting the window's initial bounds; + // the frame view's preferred height may depend on the presence of a title. + UpdateWindowTitle(); + SetInitialBounds(params.bounds); + if (params.show_state == ui::SHOW_STATE_MAXIMIZED) + Maximize(); + else if (params.show_state == ui::SHOW_STATE_MINIMIZED) + Minimize(); + } else if (params.delegate) { + SetContentsView(params.delegate->GetContentsView()); + SetInitialBoundsForFramelessWindow(params.bounds); + } + native_widget_initialized_ = true; +} + +// Unconverted methods (see header) -------------------------------------------- + +gfx::NativeView Widget::GetNativeView() const { + return native_widget_->GetNativeView(); +} + +gfx::NativeWindow Widget::GetNativeWindow() const { + return native_widget_->GetNativeWindow(); +} + +void Widget::AddObserver(WidgetObserver* observer) { + observers_.AddObserver(observer); +} + +void Widget::RemoveObserver(WidgetObserver* observer) { + observers_.RemoveObserver(observer); +} + +bool Widget::HasObserver(WidgetObserver* observer) { + return observers_.HasObserver(observer); +} + +bool Widget::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { + return false; +} + +void Widget::ViewHierarchyChanged( + const View::ViewHierarchyChangedDetails& details) { + if (!details.is_add) { + if (details.child == dragged_view_) + dragged_view_ = NULL; + FocusManager* focus_manager = GetFocusManager(); + if (focus_manager) + focus_manager->ViewRemoved(details.child); + ViewStorage::GetInstance()->ViewRemoved(details.child); + native_widget_->ViewRemoved(details.child); + } +} + +void Widget::NotifyNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view) { + if (!attached) { + FocusManager* focus_manager = GetFocusManager(); + // We are being removed from a window hierarchy. Treat this as + // the root_view_ being removed. + if (focus_manager) + focus_manager->ViewRemoved(root_view_.get()); + } + root_view_->NotifyNativeViewHierarchyChanged(attached, native_view); +} + +// Converted methods (see header) ---------------------------------------------- + +Widget* Widget::GetTopLevelWidget() { + return const_cast<Widget*>( + static_cast<const Widget*>(this)->GetTopLevelWidget()); +} + +const Widget* Widget::GetTopLevelWidget() const { + // GetTopLevelNativeWidget doesn't work during destruction because + // property is gone after gobject gets deleted. Short circuit here + // for toplevel so that InputMethod can remove itself from + // focus manager. + return is_top_level() ? this : native_widget_->GetTopLevelWidget(); +} + +void Widget::SetContentsView(View* view) { + // Do not SetContentsView() again if it is already set to the same view. + if (view == GetContentsView()) + return; + root_view_->SetContentsView(view); + if (non_client_view_ != view) { + // |non_client_view_| can only be non-NULL here if RequiresNonClientView() + // was true when the widget was initialized. Creating widgets with non + // client views and then setting the contents view can cause subtle + // problems on Windows, where the native widget thinks there is still a + // |non_client_view_|. If you get this error, either use a different type + // when initializing the widget, or don't call SetContentsView(). + DCHECK(!non_client_view_); + non_client_view_ = NULL; + } +} + +View* Widget::GetContentsView() { + return root_view_->GetContentsView(); +} + +gfx::Rect Widget::GetWindowBoundsInScreen() const { + return native_widget_->GetWindowBoundsInScreen(); +} + +gfx::Rect Widget::GetClientAreaBoundsInScreen() const { + return native_widget_->GetClientAreaBoundsInScreen(); +} + +gfx::Rect Widget::GetRestoredBounds() const { + return native_widget_->GetRestoredBounds(); +} + +void Widget::SetBounds(const gfx::Rect& bounds) { + native_widget_->SetBounds(bounds); +} + +void Widget::SetSize(const gfx::Size& size) { + native_widget_->SetSize(size); +} + +void Widget::CenterWindow(const gfx::Size& size) { + native_widget_->CenterWindow(size); +} + +void Widget::SetBoundsConstrained(const gfx::Rect& bounds) { + gfx::Rect work_area = + gfx::Screen::GetScreenFor(GetNativeView())->GetDisplayNearestPoint( + bounds.origin()).work_area(); + if (work_area.IsEmpty()) { + SetBounds(bounds); + } else { + // Inset the work area slightly. + work_area.Inset(10, 10, 10, 10); + work_area.AdjustToFit(bounds); + SetBounds(work_area); + } +} + +void Widget::SetVisibilityChangedAnimationsEnabled(bool value) { + native_widget_->SetVisibilityChangedAnimationsEnabled(value); +} + +Widget::MoveLoopResult Widget::RunMoveLoop(const gfx::Vector2d& drag_offset, + MoveLoopSource source) { + return native_widget_->RunMoveLoop(drag_offset, source); +} + +void Widget::EndMoveLoop() { + native_widget_->EndMoveLoop(); +} + +void Widget::StackAboveWidget(Widget* widget) { + native_widget_->StackAbove(widget->GetNativeView()); +} + +void Widget::StackAbove(gfx::NativeView native_view) { + native_widget_->StackAbove(native_view); +} + +void Widget::StackAtTop() { + native_widget_->StackAtTop(); +} + +void Widget::StackBelow(gfx::NativeView native_view) { + native_widget_->StackBelow(native_view); +} + +void Widget::SetShape(gfx::NativeRegion shape) { + native_widget_->SetShape(shape); +} + +void Widget::Close() { + if (widget_closed_) { + // It appears we can hit this code path if you close a modal dialog then + // close the last browser before the destructor is hit, which triggers + // invoking Close again. + return; + } + + bool can_close = true; + if (non_client_view_) + can_close = non_client_view_->CanClose(); + if (can_close) { + SaveWindowPlacement(); + + // During tear-down the top-level focus manager becomes unavailable to + // GTK tabbed panes and their children, so normal deregistration via + // |FormManager::ViewRemoved()| calls are fouled. We clear focus here + // to avoid these redundant steps and to avoid accessing deleted views + // that may have been in focus. + if (is_top_level() && focus_manager_.get()) + focus_manager_->SetFocusedView(NULL); + + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetClosing(this)); + native_widget_->Close(); + widget_closed_ = true; + } +} + +void Widget::CloseNow() { + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetClosing(this)); + native_widget_->CloseNow(); +} + +bool Widget::IsClosed() const { + return widget_closed_; +} + +void Widget::Show() { + TRACE_EVENT0("views", "Widget::Show"); + if (non_client_view_) { +#if defined(OS_MACOSX) + // On the Mac the FullScreenBookmarkBar test is different then for any other + // OS. Since the new maximize logic from ash does not apply to the mac, we + // continue to ignore the fullscreen mode here. + if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED && + !initial_restored_bounds_.IsEmpty()) { + native_widget_->ShowMaximizedWithBounds(initial_restored_bounds_); + } else { + native_widget_->ShowWithWindowState(saved_show_state_); + } +#else + // While initializing, the kiosk mode will go to full screen before the + // widget gets shown. In that case we stay in full screen mode, regardless + // of the |saved_show_state_| member. + if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED && + !initial_restored_bounds_.IsEmpty() && + !IsFullscreen()) { + native_widget_->ShowMaximizedWithBounds(initial_restored_bounds_); + } else { + native_widget_->ShowWithWindowState( + IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : saved_show_state_); + } +#endif + // |saved_show_state_| only applies the first time the window is shown. + // If we don't reset the value the window may be shown maximized every time + // it is subsequently shown after being hidden. + saved_show_state_ = ui::SHOW_STATE_NORMAL; + } else { + native_widget_->Show(); + } +} + +void Widget::Hide() { + native_widget_->Hide(); +} + +void Widget::ShowInactive() { + // If this gets called with saved_show_state_ == ui::SHOW_STATE_MAXIMIZED, + // call SetBounds()with the restored bounds to set the correct size. This + // normally should not happen, but if it does we should avoid showing unsized + // windows. + if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED && + !initial_restored_bounds_.IsEmpty()) { + SetBounds(initial_restored_bounds_); + saved_show_state_ = ui::SHOW_STATE_NORMAL; + } + native_widget_->ShowWithWindowState(ui::SHOW_STATE_INACTIVE); +} + +void Widget::Activate() { + native_widget_->Activate(); +} + +void Widget::Deactivate() { + native_widget_->Deactivate(); +} + +bool Widget::IsActive() const { + return native_widget_->IsActive(); +} + +void Widget::DisableInactiveRendering() { + SetInactiveRenderingDisabled(true); +} + +void Widget::SetAlwaysOnTop(bool on_top) { + native_widget_->SetAlwaysOnTop(on_top); +} + +void Widget::Maximize() { + native_widget_->Maximize(); +} + +void Widget::Minimize() { + native_widget_->Minimize(); +} + +void Widget::Restore() { + native_widget_->Restore(); +} + +bool Widget::IsMaximized() const { + return native_widget_->IsMaximized(); +} + +bool Widget::IsMinimized() const { + return native_widget_->IsMinimized(); +} + +void Widget::SetFullscreen(bool fullscreen) { + native_widget_->SetFullscreen(fullscreen); +} + +bool Widget::IsFullscreen() const { + return native_widget_->IsFullscreen(); +} + +void Widget::SetOpacity(unsigned char opacity) { + native_widget_->SetOpacity(opacity); +} + +void Widget::SetUseDragFrame(bool use_drag_frame) { + native_widget_->SetUseDragFrame(use_drag_frame); +} + +void Widget::FlashFrame(bool flash) { + native_widget_->FlashFrame(flash); +} + +View* Widget::GetRootView() { + return root_view_.get(); +} + +const View* Widget::GetRootView() const { + return root_view_.get(); +} + +bool Widget::IsVisible() const { + return native_widget_->IsVisible(); +} + +ui::ThemeProvider* Widget::GetThemeProvider() const { + const Widget* root_widget = GetTopLevelWidget(); + if (root_widget && root_widget != this) { + // Attempt to get the theme provider, and fall back to the default theme + // provider if not found. + ui::ThemeProvider* provider = root_widget->GetThemeProvider(); + if (provider) + return provider; + + provider = root_widget->default_theme_provider_.get(); + if (provider) + return provider; + } + return default_theme_provider_.get(); +} + +const ui::NativeTheme* Widget::GetNativeTheme() const { + return native_widget_->GetNativeTheme(); +} + +FocusManager* Widget::GetFocusManager() { + Widget* toplevel_widget = GetTopLevelWidget(); + return toplevel_widget ? toplevel_widget->focus_manager_.get() : NULL; +} + +const FocusManager* Widget::GetFocusManager() const { + const Widget* toplevel_widget = GetTopLevelWidget(); + return toplevel_widget ? toplevel_widget->focus_manager_.get() : NULL; +} + +InputMethod* Widget::GetInputMethod() { + return const_cast<InputMethod*>( + const_cast<const Widget*>(this)->GetInputMethod()); +} + +const InputMethod* Widget::GetInputMethod() const { + if (is_top_level()) { + if (!input_method_.get()) + input_method_ = const_cast<Widget*>(this)->CreateInputMethod().Pass(); + return input_method_.get(); + } else { + const Widget* toplevel = GetTopLevelWidget(); + // If GetTopLevelWidget() returns itself which is not toplevel, + // the widget is detached from toplevel widget. + // TODO(oshima): Fix GetTopLevelWidget() to return NULL + // if there is no toplevel. We probably need to add GetTopMostWidget() + // to replace some use cases. + return (toplevel && toplevel != this) ? toplevel->GetInputMethod() : NULL; + } +} + +void Widget::RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + dragged_view_ = view; + native_widget_->RunShellDrag(view, data, location, operation, source); + // If the view is removed during the drag operation, dragged_view_ is set to + // NULL. + if (view && dragged_view_ == view) { + dragged_view_ = NULL; + view->OnDragDone(); + } +} + +void Widget::SchedulePaintInRect(const gfx::Rect& rect) { + native_widget_->SchedulePaintInRect(rect); +} + +void Widget::SetCursor(gfx::NativeCursor cursor) { + native_widget_->SetCursor(cursor); +} + +bool Widget::IsMouseEventsEnabled() const { + return native_widget_->IsMouseEventsEnabled(); +} + +void Widget::SetNativeWindowProperty(const char* name, void* value) { + native_widget_->SetNativeWindowProperty(name, value); +} + +void* Widget::GetNativeWindowProperty(const char* name) const { + return native_widget_->GetNativeWindowProperty(name); +} + +void Widget::UpdateWindowTitle() { + if (!non_client_view_) + return; + + // Update the native frame's text. We do this regardless of whether or not + // the native frame is being used, since this also updates the taskbar, etc. + string16 window_title = widget_delegate_->GetWindowTitle(); + base::i18n::AdjustStringForLocaleDirection(&window_title); + native_widget_->SetWindowTitle(window_title); + non_client_view_->UpdateWindowTitle(); + + // If the non-client view is rendering its own title, it'll need to relayout + // now and to get a paint update later on. + non_client_view_->Layout(); +} + +void Widget::UpdateWindowIcon() { + if (non_client_view_) + non_client_view_->UpdateWindowIcon(); + native_widget_->SetWindowIcons(widget_delegate_->GetWindowIcon(), + widget_delegate_->GetWindowAppIcon()); +} + +FocusTraversable* Widget::GetFocusTraversable() { + return static_cast<internal::RootView*>(root_view_.get()); +} + +void Widget::ThemeChanged() { + root_view_->ThemeChanged(); +} + +void Widget::LocaleChanged() { + root_view_->LocaleChanged(); +} + +void Widget::SetFocusTraversableParent(FocusTraversable* parent) { + root_view_->SetFocusTraversableParent(parent); +} + +void Widget::SetFocusTraversableParentView(View* parent_view) { + root_view_->SetFocusTraversableParentView(parent_view); +} + +void Widget::ClearNativeFocus() { + native_widget_->ClearNativeFocus(); +} + +NonClientFrameView* Widget::CreateNonClientFrameView() { + NonClientFrameView* frame_view = + widget_delegate_->CreateNonClientFrameView(this); + if (!frame_view) + frame_view = native_widget_->CreateNonClientFrameView(); + if (!frame_view && ViewsDelegate::views_delegate) { + frame_view = + ViewsDelegate::views_delegate->CreateDefaultNonClientFrameView(this); + } + if (frame_view) + return frame_view; + + CustomFrameView* custom_frame_view = new CustomFrameView; + custom_frame_view->Init(this); + return custom_frame_view; +} + +bool Widget::ShouldUseNativeFrame() const { + if (frame_type_ != FRAME_TYPE_DEFAULT) + return frame_type_ == FRAME_TYPE_FORCE_NATIVE; + return native_widget_->ShouldUseNativeFrame(); +} + +void Widget::DebugToggleFrameType() { + if (frame_type_ == FRAME_TYPE_DEFAULT) { + frame_type_ = ShouldUseNativeFrame() ? FRAME_TYPE_FORCE_CUSTOM : + FRAME_TYPE_FORCE_NATIVE; + } else { + frame_type_ = frame_type_ == FRAME_TYPE_FORCE_CUSTOM ? + FRAME_TYPE_FORCE_NATIVE : FRAME_TYPE_FORCE_CUSTOM; + } + FrameTypeChanged(); +} + +void Widget::FrameTypeChanged() { + native_widget_->FrameTypeChanged(); +} + +const ui::Compositor* Widget::GetCompositor() const { + return native_widget_->GetCompositor(); +} + +ui::Compositor* Widget::GetCompositor() { + return native_widget_->GetCompositor(); +} + +ui::Layer* Widget::GetLayer() { + return native_widget_->GetLayer(); +} + +void Widget::ReorderNativeViews() { + native_widget_->ReorderNativeViews(); +} + +void Widget::UpdateRootLayers() { + // Calculate the layers requires traversing the tree, and since nearly any + // mutation of the tree can trigger this call we delay until absolutely + // necessary. + root_layers_dirty_ = true; +} + +const NativeWidget* Widget::native_widget() const { + return native_widget_; +} + +NativeWidget* Widget::native_widget() { + return native_widget_; +} + +void Widget::SetCapture(View* view) { + if (internal::NativeWidgetPrivate::IsMouseButtonDown()) + is_mouse_button_pressed_ = true; + if (internal::NativeWidgetPrivate::IsTouchDown()) + is_touch_down_ = true; + root_view_->SetMouseHandler(view); + if (!native_widget_->HasCapture()) + native_widget_->SetCapture(); +} + +void Widget::ReleaseCapture() { + if (native_widget_->HasCapture()) + native_widget_->ReleaseCapture(); +} + +bool Widget::HasCapture() { + return native_widget_->HasCapture(); +} + +void Widget::TooltipTextChanged(View* view) { + TooltipManager* manager = native_widget_private()->GetTooltipManager(); + if (manager) + manager->TooltipTextChanged(view); +} + +bool Widget::SetInitialFocus() { + View* v = widget_delegate_->GetInitiallyFocusedView(); + if (!focus_on_creation_) { + // If not focusing the window now, tell the focus manager which view to + // focus when the window is restored. + if (v) + focus_manager_->SetStoredFocusView(v); + return true; + } + if (v) + v->RequestFocus(); + return !!v; +} + +View* Widget::GetChildViewParent() { + return GetContentsView() ? GetContentsView() : GetRootView(); +} + +gfx::Rect Widget::GetWorkAreaBoundsInScreen() const { + return native_widget_->GetWorkAreaBoundsInScreen(); +} + +void Widget::SynthesizeMouseMoveEvent() { + last_mouse_event_was_move_ = false; + ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, + last_mouse_event_position_, + last_mouse_event_position_, + ui::EF_IS_SYNTHESIZED); + root_view_->OnMouseMoved(mouse_event); +} + +void Widget::OnOwnerClosing() { +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, NativeWidgetDelegate implementation: + +bool Widget::IsModal() const { + return widget_delegate_->GetModalType() != ui::MODAL_TYPE_NONE; +} + +bool Widget::IsDialogBox() const { + return !!widget_delegate_->AsDialogDelegate(); +} + +bool Widget::CanActivate() const { + return widget_delegate_->CanActivate(); +} + +bool Widget::IsInactiveRenderingDisabled() const { + return disable_inactive_rendering_; +} + +void Widget::EnableInactiveRendering() { + SetInactiveRenderingDisabled(false); +} + +void Widget::OnNativeWidgetActivationChanged(bool active) { + // On windows we may end up here before we've completed initialization (from + // an WM_NCACTIVATE). If that happens the WidgetDelegate likely doesn't know + // the Widget and will crash attempting to access it. + if (!active && native_widget_initialized_) + SaveWindowPlacement(); + + FOR_EACH_OBSERVER(WidgetObserver, observers_, + OnWidgetActivationChanged(this, active)); +} + +void Widget::OnNativeFocus(gfx::NativeView old_focused_view) { + WidgetFocusManager::GetInstance()->OnWidgetFocusEvent(old_focused_view, + GetNativeView()); +} + +void Widget::OnNativeBlur(gfx::NativeView new_focused_view) { + WidgetFocusManager::GetInstance()->OnWidgetFocusEvent(GetNativeView(), + new_focused_view); +} + +void Widget::OnNativeWidgetVisibilityChanged(bool visible) { + View* root = GetRootView(); + if (root) + root->PropagateVisibilityNotifications(root, visible); + FOR_EACH_OBSERVER(WidgetObserver, observers_, + OnWidgetVisibilityChanged(this, visible)); + if (GetCompositor() && root && root->layer()) + root->layer()->SetVisible(visible); +} + +void Widget::OnNativeWidgetCreated(bool desktop_widget) { + if (is_top_level()) + focus_manager_.reset(FocusManagerFactory::Create(this, desktop_widget)); + + native_widget_->InitModalType(widget_delegate_->GetModalType()); + + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetCreated(this)); +} + +void Widget::OnNativeWidgetDestroying() { + // Tell the focus manager (if any) that root_view is being removed + // in case that the focused view is under this root view. + if (GetFocusManager()) + GetFocusManager()->ViewRemoved(root_view_.get()); + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetDestroying(this)); + if (non_client_view_) + non_client_view_->WindowClosing(); + widget_delegate_->WindowClosing(); +} + +void Widget::OnNativeWidgetDestroyed() { + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetDestroyed(this)); + widget_delegate_->DeleteDelegate(); + widget_delegate_ = NULL; + native_widget_destroyed_ = true; +} + +gfx::Size Widget::GetMinimumSize() { + return non_client_view_ ? non_client_view_->GetMinimumSize() : gfx::Size(); +} + +gfx::Size Widget::GetMaximumSize() { + return non_client_view_ ? non_client_view_->GetMaximumSize() : gfx::Size(); +} + +void Widget::OnNativeWidgetMove() { + widget_delegate_->OnWidgetMove(); + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetBoundsChanged( + this, + GetWindowBoundsInScreen())); +} + +void Widget::OnNativeWidgetSizeChanged(const gfx::Size& new_size) { + root_view_->SetSize(new_size); + + // Size changed notifications can fire prior to full initialization + // i.e. during session restore. Avoid saving session state during these + // startup procedures. + if (native_widget_initialized_) + SaveWindowPlacement(); + + FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetBoundsChanged( + this, + GetWindowBoundsInScreen())); +} + +void Widget::OnNativeWidgetBeginUserBoundsChange() { + widget_delegate_->OnWindowBeginUserBoundsChange(); +} + +void Widget::OnNativeWidgetEndUserBoundsChange() { + widget_delegate_->OnWindowEndUserBoundsChange(); +} + +bool Widget::HasFocusManager() const { + return !!focus_manager_.get(); +} + +bool Widget::OnNativeWidgetPaintAccelerated(const gfx::Rect& dirty_region) { + ui::Compositor* compositor = GetCompositor(); + if (!compositor) + return false; + + compositor->ScheduleRedrawRect(dirty_region); + return true; +} + +void Widget::OnNativeWidgetPaint(gfx::Canvas* canvas) { + // On Linux Aura, we can get here during Init() because of the + // SetInitialBounds call. + if (native_widget_initialized_) + GetRootView()->Paint(canvas); +} + +int Widget::GetNonClientComponent(const gfx::Point& point) { + int component = non_client_view_ ? + non_client_view_->NonClientHitTest(point) : + HTNOWHERE; + + if (movement_disabled_ && (component == HTCAPTION || component == HTSYSMENU)) + return HTNOWHERE; + + return component; +} + +void Widget::OnKeyEvent(ui::KeyEvent* event) { + static_cast<internal::RootView*>(GetRootView())-> + DispatchKeyEvent(event); +} + +void Widget::OnMouseEvent(ui::MouseEvent* event) { + if (!IsMouseEventsEnabled()) + return; + + View* root_view = GetRootView(); + switch (event->type()) { + case ui::ET_MOUSE_PRESSED: { + last_mouse_event_was_move_ = false; + + // We may get deleted by the time we return from OnMousePressed. So we + // use an observer to make sure we are still alive. + WidgetDeletionObserver widget_deletion_observer(this); + + // Make sure we're still visible before we attempt capture as the mouse + // press processing may have made the window hide (as happens with menus). + + // It is possible for a View to show a context menu on mouse-press. Since + // the menu does a capture and starts a nested message-loop, the release + // would go to the menu. The next click (i.e. both mouse-press and release + // events) also go to the menu. The menu (and the nested message-loop) + // gets closed after this second release event. The code then resumes from + // here. So make sure that the mouse-button is still down before doing a + // capture. + if (root_view && root_view->OnMousePressed(*event) && + widget_deletion_observer.IsWidgetAlive() && IsVisible() && + internal::NativeWidgetPrivate::IsMouseButtonDown()) { + is_mouse_button_pressed_ = true; + if (!native_widget_->HasCapture()) + native_widget_->SetCapture(); + event->SetHandled(); + } + return; + } + case ui::ET_MOUSE_RELEASED: + last_mouse_event_was_move_ = false; + is_mouse_button_pressed_ = false; + // Release capture first, to avoid confusion if OnMouseReleased blocks. + if (native_widget_->HasCapture() && + ShouldReleaseCaptureOnMouseReleased()) { + native_widget_->ReleaseCapture(); + } + if (root_view) + root_view->OnMouseReleased(*event); + if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0) + event->SetHandled(); + return; + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_DRAGGED: + if (native_widget_->HasCapture() && is_mouse_button_pressed_) { + last_mouse_event_was_move_ = false; + if (root_view) + root_view->OnMouseDragged(*event); + } else if (!last_mouse_event_was_move_ || + last_mouse_event_position_ != event->location()) { + last_mouse_event_position_ = event->location(); + last_mouse_event_was_move_ = true; + if (root_view) + root_view->OnMouseMoved(*event); + } + return; + case ui::ET_MOUSE_EXITED: + last_mouse_event_was_move_ = false; + if (root_view) + root_view->OnMouseExited(*event); + return; + case ui::ET_MOUSEWHEEL: + if (root_view && root_view->OnMouseWheel( + static_cast<const ui::MouseWheelEvent&>(*event))) + event->SetHandled(); + return; + default: + return; + } + event->SetHandled(); +} + +void Widget::OnMouseCaptureLost() { + if (is_mouse_button_pressed_ || is_touch_down_) { + View* root_view = GetRootView(); + if (root_view) + root_view->OnMouseCaptureLost(); + } + is_touch_down_ = false; + is_mouse_button_pressed_ = false; +} + +void Widget::OnTouchEvent(ui::TouchEvent* event) { + static_cast<internal::RootView*>(GetRootView())-> + DispatchTouchEvent(event); +} + +void Widget::OnScrollEvent(ui::ScrollEvent* event) { + static_cast<internal::RootView*>(GetRootView())-> + DispatchScrollEvent(event); +} + +void Widget::OnGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + is_touch_down_ = true; + // We explicitly don't capture here. Not capturing enables multiple + // widgets to get tap events at the same time. Views (such as tab + // dragging) may explicitly capture. + break; + + case ui::ET_GESTURE_END: + if (event->details().touch_points() == 1) { + is_touch_down_ = false; + if (ShouldReleaseCaptureOnMouseReleased()) + ReleaseCapture(); + } + break; + + default: + break; + } + static_cast<internal::RootView*>(GetRootView())->DispatchGestureEvent(event); +} + +bool Widget::ExecuteCommand(int command_id) { + return widget_delegate_->ExecuteWindowsCommand(command_id); +} + +InputMethod* Widget::GetInputMethodDirect() { + return input_method_.get(); +} + +const std::vector<ui::Layer*>& Widget::GetRootLayers() { + if (root_layers_dirty_) { + root_layers_dirty_ = false; + root_layers_.clear(); + BuildRootLayers(GetRootView(), &root_layers_); + } + return root_layers_; +} + +bool Widget::HasHitTestMask() const { + return widget_delegate_->WidgetHasHitTestMask(); +} + +void Widget::GetHitTestMask(gfx::Path* mask) const { + DCHECK(mask); + widget_delegate_->GetWidgetHitTestMask(mask); +} + +Widget* Widget::AsWidget() { + return this; +} + +const Widget* Widget::AsWidget() const { + return this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, FocusTraversable implementation: + +FocusSearch* Widget::GetFocusSearch() { + return root_view_->GetFocusSearch(); +} + +FocusTraversable* Widget::GetFocusTraversableParent() { + // We are a proxy to the root view, so we should be bypassed when traversing + // up and as a result this should not be called. + NOTREACHED(); + return NULL; +} + +View* Widget::GetFocusTraversableParentView() { + // We are a proxy to the root view, so we should be bypassed when traversing + // up and as a result this should not be called. + NOTREACHED(); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, protected: + +internal::RootView* Widget::CreateRootView() { + return new internal::RootView(this); +} + +void Widget::DestroyRootView() { + non_client_view_ = NULL; + root_view_.reset(); + // Input method has to be destroyed before focus manager. + input_method_.reset(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, private: + +bool Widget::ShouldReleaseCaptureOnMouseReleased() const { + return true; +} + +void Widget::SetInactiveRenderingDisabled(bool value) { + if (value == disable_inactive_rendering_) + return; + + disable_inactive_rendering_ = value; + if (non_client_view_) + non_client_view_->SetInactiveRenderingDisabled(value); + native_widget_->SetInactiveRenderingDisabled(value); +} + +void Widget::SaveWindowPlacement() { + // The window delegate does the actual saving for us. It seems like (judging + // by go/crash) that in some circumstances we can end up here after + // WM_DESTROY, at which point the window delegate is likely gone. So just + // bail. + if (!widget_delegate_) + return; + + ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL; + gfx::Rect bounds; + native_widget_->GetWindowPlacement(&bounds, &show_state); + widget_delegate_->SaveWindowPlacement(bounds, show_state); +} + +void Widget::SetInitialBounds(const gfx::Rect& bounds) { + if (!non_client_view_) + return; + + gfx::Rect saved_bounds; + if (GetSavedWindowPlacement(&saved_bounds, &saved_show_state_)) { + if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED) { + // If we're going to maximize, wait until Show is invoked to set the + // bounds. That way we avoid a noticeable resize. + initial_restored_bounds_ = saved_bounds; + } else if (!saved_bounds.IsEmpty()) { + // If the saved bounds are valid, use them. + SetBounds(saved_bounds); + } + } else { + if (bounds.IsEmpty()) { + // No initial bounds supplied, so size the window to its content and + // center over its parent. + native_widget_->CenterWindow(non_client_view_->GetPreferredSize()); + } else { + // Use the supplied initial bounds. + SetBoundsConstrained(bounds); + } + } +} + +void Widget::SetInitialBoundsForFramelessWindow(const gfx::Rect& bounds) { + if (bounds.IsEmpty()) { + View* contents_view = GetContentsView(); + DCHECK(contents_view); + // No initial bounds supplied, so size the window to its content and + // center over its parent if preferred size is provided. + gfx::Size size = contents_view->GetPreferredSize(); + if (!size.IsEmpty()) + native_widget_->CenterWindow(size); + } else { + // Use the supplied initial bounds. + SetBoundsConstrained(bounds); + } +} + +bool Widget::GetSavedWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state) { + // First we obtain the window's saved show-style and store it. We need to do + // this here, rather than in Show() because by the time Show() is called, + // the window's size will have been reset (below) and the saved maximized + // state will have been lost. Sadly there's no way to tell on Windows when + // a window is restored from maximized state, so we can't more accurately + // track maximized state independently of sizing information. + + // Restore the window's placement from the controller. + if (widget_delegate_->GetSavedWindowPlacement(bounds, show_state)) { + if (!widget_delegate_->ShouldRestoreWindowSize()) { + bounds->set_size(non_client_view_->GetPreferredSize()); + } else { + gfx::Size minimum_size = GetMinimumSize(); + // Make sure the bounds are at least the minimum size. + if (bounds->width() < minimum_size.width()) + bounds->set_width(minimum_size.width()); + + if (bounds->height() < minimum_size.height()) + bounds->set_height(minimum_size.height()); + } + return true; + } + return false; +} + +scoped_ptr<InputMethod> Widget::CreateInputMethod() { + scoped_ptr<InputMethod> input_method(native_widget_->CreateInputMethod()); + if (input_method.get()) + input_method->Init(this); + return input_method.Pass(); +} + +void Widget::ReplaceInputMethod(InputMethod* input_method) { + input_method_.reset(input_method); + input_method->SetDelegate(native_widget_->GetInputMethodDelegate()); + input_method->Init(this); +} + +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// internal::NativeWidgetPrivate, NativeWidget implementation: + +internal::NativeWidgetPrivate* NativeWidgetPrivate::AsNativeWidgetPrivate() { + return this; +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h new file mode 100644 index 00000000000..f020a0c7de1 --- /dev/null +++ b/chromium/ui/views/widget/widget.h @@ -0,0 +1,858 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_H_ +#define UI_VIEWS_WIDGET_WIDGET_H_ + +#include <set> +#include <stack> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "ui/base/ui_base_types.h" +#include "ui/compositor/layer_type.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/widget/native_widget_delegate.h" +#include "ui/views/window/client_view.h" +#include "ui/views/window/non_client_view.h" + +#if defined(OS_WIN) +// Windows headers define macros for these function names which screw with us. +#if defined(IsMaximized) +#undef IsMaximized +#endif +#if defined(IsMinimized) +#undef IsMinimized +#endif +#if defined(CreateWindow) +#undef CreateWindow +#endif +#endif + +namespace gfx { +class Canvas; +class Point; +class Rect; +} + +namespace ui { +class Accelerator; +class Compositor; +class DefaultThemeProvider; +class Layer; +class NativeTheme; +class OSExchangeData; +class ThemeProvider; +} + +namespace views { + +class DesktopRootWindowHost; +class InputMethod; +class NativeWidget; +class NonClientFrameView; +class View; +class WidgetDelegate; +class WidgetObserver; + +namespace internal { +class NativeWidgetPrivate; +class RootView; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget class +// +// Encapsulates the platform-specific rendering, event receiving and widget +// management aspects of the UI framework. +// +// Owns a RootView and thus a View hierarchy. Can contain child Widgets. +// Widget is a platform-independent type that communicates with a platform or +// context specific NativeWidget implementation. +// +// A special note on ownership: +// +// Depending on the value of the InitParams' ownership field, the Widget +// either owns or is owned by its NativeWidget: +// +// ownership = NATIVE_WIDGET_OWNS_WIDGET (default) +// The Widget instance is owned by its NativeWidget. When the NativeWidget +// is destroyed (in response to a native destruction message), it deletes +// the Widget from its destructor. +// ownership = WIDGET_OWNS_NATIVE_WIDGET (non-default) +// The Widget instance owns its NativeWidget. This state implies someone +// else wants to control the lifetime of this object. When they destroy +// the Widget it is responsible for destroying the NativeWidget (from its +// destructor). +// +class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, + public FocusTraversable { + public: + typedef std::set<Widget*> Widgets; + + enum FrameType { + FRAME_TYPE_DEFAULT, // Use whatever the default would be. + FRAME_TYPE_FORCE_CUSTOM, // Force the custom frame. + FRAME_TYPE_FORCE_NATIVE // Force the native frame. + }; + + // Result from RunMoveLoop(). + enum MoveLoopResult { + // The move loop completed successfully. + MOVE_LOOP_SUCCESSFUL, + + // The user canceled the move loop. + MOVE_LOOP_CANCELED + }; + + // Source that initiated the move loop. + enum MoveLoopSource { + MOVE_LOOP_SOURCE_MOUSE, + MOVE_LOOP_SOURCE_TOUCH, + }; + + struct VIEWS_EXPORT InitParams { + enum Type { + TYPE_WINDOW, // A decorated Window, like a frame window. + // Widgets of TYPE_WINDOW will have a NonClientView. + TYPE_PANEL, // Always on top window managed by PanelManager. + // Widgets of TYPE_PANEL will have a NonClientView. + TYPE_WINDOW_FRAMELESS, + // An undecorated Window. + TYPE_CONTROL, // A control, like a button. + TYPE_POPUP, // An undecorated Window, with transient properties. + TYPE_MENU, // An undecorated Window, with transient properties + // specialized to menus. + TYPE_TOOLTIP, + TYPE_BUBBLE, + }; + + enum WindowOpacity { + // Infer fully opaque or not. For WinAura, top-level windows that are not + // of TYPE_WINDOW are translucent so that they can be made to fade in. In + // all other cases, windows are fully opaque. + INFER_OPACITY, + // Fully opaque. + OPAQUE_WINDOW, + // Possibly translucent/transparent. + TRANSLUCENT_WINDOW, + }; + + enum Ownership { + // Default. Creator is not responsible for managing the lifetime of the + // Widget, it is destroyed when the corresponding NativeWidget is + // destroyed. + NATIVE_WIDGET_OWNS_WIDGET, + // Used when the Widget is owned by someone other than the NativeWidget, + // e.g. a scoped_ptr in tests. + WIDGET_OWNS_NATIVE_WIDGET + }; + + InitParams(); + explicit InitParams(Type type); + + + // Will return the first of the following that isn't NULL: the native view, + // |parent|, |context|. + gfx::NativeView GetContext() const; + + Type type; + // If NULL, a default implementation will be constructed. + WidgetDelegate* delegate; + bool child; + // If TRANSLUCENT_WINDOW, the widget may be fully or partially transparent. + // If OPAQUE_WINDOW, we can perform optimizations based on the widget being + // fully opaque. Defaults to TRANSLUCENT_WINDOW if + // ViewsDelegate::UseTransparentWindows(). Defaults to OPAQUE_WINDOW for + // non-window widgets. + WindowOpacity opacity; + bool accept_events; + bool can_activate; + bool keep_on_top; + Ownership ownership; + bool mirror_origin_in_rtl; + bool has_dropshadow; + // Only used by NativeWidgetWin. Specifies that the system default caption + // and icon should not be rendered, and that the client area should be + // equivalent to the window area. + bool remove_standard_frame; + // Only used by ShellWindow on Windows. Specifies that the default icon of + // packaged app should be the system default icon. + bool use_system_default_icon; + // Whether the widget should be maximized or minimized. + ui::WindowShowState show_state; + // Should the widget be double buffered? Default is false. + bool double_buffer; + gfx::NativeView parent; + // Specifies the initial bounds of the Widget. Default is empty, which means + // the NativeWidget may specify a default size. If the parent is specified, + // |bounds| is in the parent's coordinate system. If the parent is not + // specified, it's in screen's global coordinate system. + gfx::Rect bounds; + // When set, this value is used as the Widget's NativeWidget implementation. + // The Widget will not construct a default one. Default is NULL. + NativeWidget* native_widget; + // Aura-only. Provides a DesktopRootWindowHost implementation to use instead + // of the default one. + // TODO(beng): Figure out if there's a better way to expose this, e.g. get + // rid of NW subclasses and do this all via message handling. + DesktopRootWindowHost* desktop_root_window_host; + // Whether this window is intended to be a toplevel window with no + // attachment to any other window. (This may be a transient window if + // |parent| is set.) + bool top_level; + // Only used by NativeWidgetAura. Specifies the type of layer for the + // aura::Window. Default is LAYER_TEXTURED. + ui::LayerType layer_type; + // Only used by Aura. Provides a context window whose RootWindow is + // consulted during widget creation to determine where in the Window + // hierarchy this widget should be placed. (This is separate from |parent|; + // if you pass a RootWindow to |parent|, your window will be parented to + // |parent|. If you pass a RootWindow to |context|, we ask that RootWindow + // where it wants your window placed.) NULL is not allowed if you are using + // aura. + gfx::NativeView context; + }; + + Widget(); + virtual ~Widget(); + + // Creates a toplevel window with no context. These methods should only be + // used in cases where there is no contextual information because we're + // creating a toplevel window connected to no other event. + // + // If you have any parenting or context information, or can pass that + // information, prefer the WithParent or WithContext versions of these + // methods. + static Widget* CreateWindow(WidgetDelegate* delegate); + static Widget* CreateWindowWithBounds(WidgetDelegate* delegate, + const gfx::Rect& bounds); + + // Creates a decorated window Widget with the specified properties. + static Widget* CreateWindowWithParent(WidgetDelegate* delegate, + gfx::NativeWindow parent); + static Widget* CreateWindowWithParentAndBounds(WidgetDelegate* delegate, + gfx::NativeWindow parent, + const gfx::Rect& bounds); + + // Creates a decorated window Widget in the same desktop context as + // |context|. + static Widget* CreateWindowWithContext(WidgetDelegate* delegate, + gfx::NativeView context); + static Widget* CreateWindowWithContextAndBounds(WidgetDelegate* delegate, + gfx::NativeView context, + const gfx::Rect& bounds); + + + // Enumerates all windows pertaining to us and notifies their + // view hierarchies that the locale has changed. + // TODO(beng): remove post-Aurafication of ChromeOS. + static void NotifyLocaleChanged(); + + // Closes all Widgets that aren't identified as "secondary widgets". Called + // during application shutdown when the last non-secondary widget is closed. + static void CloseAllSecondaryWidgets(); + + // Converts a rectangle from one Widget's coordinate system to another's. + // Returns false if the conversion couldn't be made, because either these two + // Widgets do not have a common ancestor or they are not on the screen yet. + // The value of |*rect| won't be changed when false is returned. + static bool ConvertRect(const Widget* source, + const Widget* target, + gfx::Rect* rect); + + // Retrieves the Widget implementation associated with the given + // NativeView or Window, or NULL if the supplied handle has no associated + // Widget. + static Widget* GetWidgetForNativeView(gfx::NativeView native_view); + static Widget* GetWidgetForNativeWindow(gfx::NativeWindow native_window); + + // Retrieves the top level widget in a native view hierarchy + // starting at |native_view|. Top level widget is a widget with TYPE_WINDOW, + // TYPE_PANEL, TYPE_WINDOW_FRAMELESS, POPUP or MENU and has its own + // focus manager. This may be itself if the |native_view| is top level, + // or NULL if there is no toplevel in a native view hierarchy. + static Widget* GetTopLevelWidgetForNativeView(gfx::NativeView native_view); + + // Returns all Widgets in |native_view|'s hierarchy, including itself if + // it is one. + static void GetAllChildWidgets(gfx::NativeView native_view, + Widgets* children); + + // Re-parent a NativeView and notify all Widgets in |native_view|'s hierarchy + // of the change. + static void ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent); + + // Returns the preferred size of the contents view of this window based on + // its localized size data. The width in cols is held in a localized string + // resource identified by |col_resource_id|, the height in the same fashion. + // TODO(beng): This should eventually live somewhere else, probably closer to + // ClientView. + static int GetLocalizedContentsWidth(int col_resource_id); + static int GetLocalizedContentsHeight(int row_resource_id); + static gfx::Size GetLocalizedContentsSize(int col_resource_id, + int row_resource_id); + + // Returns true if the specified type requires a NonClientView. + static bool RequiresNonClientView(InitParams::Type type); + + void Init(const InitParams& params); + + // Returns the gfx::NativeView associated with this Widget. + gfx::NativeView GetNativeView() const; + + // Returns the gfx::NativeWindow associated with this Widget. This may return + // NULL on some platforms if the widget was created with a type other than + // TYPE_WINDOW or TYPE_PANEL. + gfx::NativeWindow GetNativeWindow() const; + + // Add/remove observer. + void AddObserver(WidgetObserver* observer); + void RemoveObserver(WidgetObserver* observer); + bool HasObserver(WidgetObserver* observer); + + // Returns the accelerator given a command id. Returns false if there is + // no accelerator associated with a given id, which is a common condition. + virtual bool GetAccelerator(int cmd_id, ui::Accelerator* accelerator); + + // Forwarded from the RootView so that the widget can do any cleanup. + void ViewHierarchyChanged(const View::ViewHierarchyChangedDetails& details); + + // Performs any necessary cleanup and forwards to RootView. + void NotifyNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view); + + // Returns the top level widget in a hierarchy (see is_top_level() for + // the definition of top level widget.) Will return NULL if called + // before the widget is attached to the top level widget's hierarchy. + Widget* GetTopLevelWidget(); + const Widget* GetTopLevelWidget() const; + + // Gets/Sets the WidgetDelegate. + WidgetDelegate* widget_delegate() const { return widget_delegate_; } + + // Sets the specified view as the contents of this Widget. There can only + // be one contents view child of this Widget's RootView. This view is sized to + // fit the entire size of the RootView. The RootView takes ownership of this + // View, unless it is set as not being parent-owned. + void SetContentsView(View* view); + View* GetContentsView(); + + // Returns the bounds of the Widget in screen coordinates. + gfx::Rect GetWindowBoundsInScreen() const; + + // Returns the bounds of the Widget's client area in screen coordinates. + gfx::Rect GetClientAreaBoundsInScreen() const; + + // Retrieves the restored bounds for the window. + gfx::Rect GetRestoredBounds() const; + + // Sizes and/or places the widget to the specified bounds, size or position. + void SetBounds(const gfx::Rect& bounds); + void SetSize(const gfx::Size& size); + + // Sizes the window to the specified size and centerizes it. + void CenterWindow(const gfx::Size& size); + + // Like SetBounds(), but ensures the Widget is fully visible on screen, + // resizing and/or repositioning as necessary. This is only useful for + // non-child widgets. + void SetBoundsConstrained(const gfx::Rect& bounds); + + // Sets whether animations that occur when visibility is changed are enabled. + // Default is true. + void SetVisibilityChangedAnimationsEnabled(bool value); + + // Starts a nested message loop that moves the window. This can be used to + // start a window move operation from a mouse or touch event. This returns + // when the move completes. |drag_offset| is the offset from the top left + // corner of the window to the point where the cursor is dragging, and is used + // to offset the bounds of the window from the cursor. + MoveLoopResult RunMoveLoop(const gfx::Vector2d& drag_offset, + MoveLoopSource source); + + // Stops a previously started move loop. This is not immediate. + void EndMoveLoop(); + + // Places the widget in front of the specified widget in z-order. + void StackAboveWidget(Widget* widget); + void StackAbove(gfx::NativeView native_view); + void StackAtTop(); + + // Places the widget below the specified NativeView. + void StackBelow(gfx::NativeView native_view); + + // Sets a shape on the widget. This takes ownership of shape. + void SetShape(gfx::NativeRegion shape); + + // Hides the widget then closes it after a return to the message loop. + virtual void Close(); + + // TODO(beng): Move off public API. + // Closes the widget immediately. Compare to |Close|. This will destroy the + // window handle associated with this Widget, so should not be called from + // any code that expects it to be valid beyond this call. + void CloseNow(); + + // Whether the widget has been asked to close itself. In particular this is + // set to true after Close() has been invoked on the NativeWidget. + bool IsClosed() const; + + // Shows or hides the widget, without changing activation state. + virtual void Show(); + void Hide(); + + // Like Show(), but does not activate the window. + void ShowInactive(); + + // Activates the widget, assuming it already exists and is visible. + void Activate(); + + // Deactivates the widget, making the next window in the Z order the active + // window. + void Deactivate(); + + // Returns whether the Widget is the currently active window. + virtual bool IsActive() const; + + // Prevents the window from being rendered as deactivated. This state is + // reset automatically as soon as the window becomes activated again. There is + // no ability to control the state through this API as this leads to sync + // problems. + void DisableInactiveRendering(); + + // Sets the widget to be on top of all other widgets in the windowing system. + void SetAlwaysOnTop(bool on_top); + + // Maximizes/minimizes/restores the window. + void Maximize(); + void Minimize(); + void Restore(); + + // Whether or not the window is maximized or minimized. + virtual bool IsMaximized() const; + bool IsMinimized() const; + + // Accessors for fullscreen state. + void SetFullscreen(bool fullscreen); + bool IsFullscreen() const; + + // Sets the opacity of the widget. This may allow widgets behind the widget + // in the Z-order to become visible, depending on the capabilities of the + // underlying windowing system. + void SetOpacity(unsigned char opacity); + + // Sets whether or not the window should show its frame as a "transient drag + // frame" - slightly transparent and without the standard window controls. + void SetUseDragFrame(bool use_drag_frame); + + // Flashes the frame of the window to draw attention to it. Currently only + // implemented on Windows for non-Aura. + void FlashFrame(bool flash); + + // Returns the View at the root of the View hierarchy contained by this + // Widget. + View* GetRootView(); + const View* GetRootView() const; + + // A secondary widget is one that is automatically closed (via Close()) when + // all non-secondary widgets are closed. + // Default is true. + // TODO(beng): This is an ugly API, should be handled implicitly via + // transience. + void set_is_secondary_widget(bool is_secondary_widget) { + is_secondary_widget_ = is_secondary_widget; + } + bool is_secondary_widget() const { return is_secondary_widget_; } + + // Returns whether the Widget is visible to the user. + virtual bool IsVisible() const; + + // Returns the ThemeProvider that provides theme resources for this Widget. + virtual ui::ThemeProvider* GetThemeProvider() const; + + ui::NativeTheme* GetNativeTheme() { + return const_cast<ui::NativeTheme*>( + const_cast<const Widget*>(this)->GetNativeTheme()); + } + const ui::NativeTheme* GetNativeTheme() const; + + // Returns the FocusManager for this widget. + // Note that all widgets in a widget hierarchy share the same focus manager. + FocusManager* GetFocusManager(); + const FocusManager* GetFocusManager() const; + + // Returns the InputMethod for this widget. + // Note that all widgets in a widget hierarchy share the same input method. + InputMethod* GetInputMethod(); + const InputMethod* GetInputMethod() const; + + // Starts a drag operation for the specified view. This blocks until the drag + // operation completes. |view| can be NULL. + // If the view is non-NULL it can be accessed during the drag by calling + // dragged_view(). If the view has not been deleted during the drag, + // OnDragDone() is called on it. |location| is in the widget's coordinate + // system. + void RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source); + + // Returns the view that requested the current drag operation via + // RunShellDrag(), or NULL if there is no such view or drag operation. + View* dragged_view() { return dragged_view_; } + + // Adds the specified |rect| in client area coordinates to the rectangle to be + // redrawn. + virtual void SchedulePaintInRect(const gfx::Rect& rect); + + // Sets the currently visible cursor. If |cursor| is NULL, the cursor used + // before the current is restored. + void SetCursor(gfx::NativeCursor cursor); + + // Returns true if and only if mouse events are enabled. + bool IsMouseEventsEnabled() const; + + // Sets/Gets a native window property on the underlying native window object. + // Returns NULL if the property does not exist. Setting the property value to + // NULL removes the property. + void SetNativeWindowProperty(const char* name, void* value); + void* GetNativeWindowProperty(const char* name) const; + + // Tell the window to update its title from the delegate. + void UpdateWindowTitle(); + + // Tell the window to update its icon from the delegate. + void UpdateWindowIcon(); + + // Retrieves the focus traversable for this widget. + FocusTraversable* GetFocusTraversable(); + + // Notifies the view hierarchy contained in this widget that theme resources + // changed. + void ThemeChanged(); + + // Notifies the view hierarchy contained in this widget that locale resources + // changed. + void LocaleChanged(); + + void SetFocusTraversableParent(FocusTraversable* parent); + void SetFocusTraversableParentView(View* parent_view); + + // Clear native focus set to the Widget's NativeWidget. + void ClearNativeFocus(); + + void set_frame_type(FrameType frame_type) { frame_type_ = frame_type; } + FrameType frame_type() const { return frame_type_; } + + // Creates an appropriate NonClientFrameView for this widget. The + // WidgetDelegate is given the first opportunity to create one, followed by + // the NativeWidget implementation. If both return NULL, a default one is + // created. + virtual NonClientFrameView* CreateNonClientFrameView(); + + // Whether we should be using a native frame. + bool ShouldUseNativeFrame() const; + + // Forces the frame into the alternate frame type (custom or native) depending + // on its current state. + void DebugToggleFrameType(); + + // Tell the window that something caused the frame type to change. + void FrameTypeChanged(); + + NonClientView* non_client_view() { + return const_cast<NonClientView*>( + const_cast<const Widget*>(this)->non_client_view()); + } + const NonClientView* non_client_view() const { + return non_client_view_; + } + + ClientView* client_view() { + return const_cast<ClientView*>( + const_cast<const Widget*>(this)->client_view()); + } + const ClientView* client_view() const { + // non_client_view_ may be NULL, especially during creation. + return non_client_view_ ? non_client_view_->client_view() : NULL; + } + + const ui::Compositor* GetCompositor() const; + ui::Compositor* GetCompositor(); + + // Returns the widget's layer, if any. + ui::Layer* GetLayer(); + + // Reorders the widget's child NativeViews which are associated to the view + // tree (eg via a NativeViewHost) to match the z-order of the views in the + // view tree. The z-order of views with layers relative to views with + // associated NativeViews is used to reorder the NativeView layers. This + // method assumes that the widget's child layers which are owned by a view are + // already in the correct z-order relative to each other and does no + // reordering if there are no views with an associated NativeView. + void ReorderNativeViews(); + + // Schedules an update to the root layers. The actual processing occurs when + // GetRootLayers() is invoked. + void UpdateRootLayers(); + + const NativeWidget* native_widget() const; + NativeWidget* native_widget(); + + internal::NativeWidgetPrivate* native_widget_private() { + return native_widget_; + } + const internal::NativeWidgetPrivate* native_widget_private() const { + return native_widget_; + } + + // Sets capture to the specified view. This makes it so that all mouse, touch + // and gesture events go to |view|. + void SetCapture(View* view); + + // Releases capture. + void ReleaseCapture(); + + // Returns true if the widget has capture. + bool HasCapture(); + + // Invoked when the tooltip text changes for the specified views. + void TooltipTextChanged(View* view); + + // Sets-up the focus manager with the view that should have focus when the + // window is shown the first time. Returns true if the initial focus has been + // set or the widget should not set the initial focus, or false if the caller + // should set the initial focus (if any). + bool SetInitialFocus(); + + void set_focus_on_creation(bool focus_on_creation) { + focus_on_creation_ = focus_on_creation; + } + + // Returns a View* that any child Widgets backed by NativeWidgetViews + // are added to. The default implementation returns the contents view + // if it exists and the root view otherwise. + virtual View* GetChildViewParent(); + + // True if the widget is considered top level widget. Top level widget + // is a widget of TYPE_WINDOW, TYPE_PANEL, TYPE_WINDOW_FRAMELESS, BUBBLE, + // POPUP or MENU, and has a focus manager and input method object associated + // with it. TYPE_CONTROL and TYPE_TOOLTIP is not considered top level. + bool is_top_level() const { return is_top_level_; } + + // True when window movement via mouse interaction with the frame is disabled. + bool movement_disabled() const { return movement_disabled_; } + void set_movement_disabled(bool disabled) { movement_disabled_ = disabled; } + + // Returns the work area bounds of the screen the Widget belongs to. + gfx::Rect GetWorkAreaBoundsInScreen() const; + + // Creates and dispatches synthesized mouse move event using the current + // mouse location to refresh hovering status in the widget. + void SynthesizeMouseMoveEvent(); + + // Notification that our owner is closing. + // NOTE: this is not invoked for aura as it's currently not needed there. + // Under aura menus close by way of activation getting reset when the owner + // closes. + virtual void OnOwnerClosing(); + + // Overridden from NativeWidgetDelegate: + virtual bool IsModal() const OVERRIDE; + virtual bool IsDialogBox() const OVERRIDE; + virtual bool CanActivate() const OVERRIDE; + virtual bool IsInactiveRenderingDisabled() const OVERRIDE; + virtual void EnableInactiveRendering() OVERRIDE; + virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE; + virtual void OnNativeFocus(gfx::NativeView old_focused_view) OVERRIDE; + virtual void OnNativeBlur(gfx::NativeView new_focused_view) OVERRIDE; + virtual void OnNativeWidgetVisibilityChanged(bool visible) OVERRIDE; + virtual void OnNativeWidgetCreated(bool desktop_widget) OVERRIDE; + virtual void OnNativeWidgetDestroying() OVERRIDE; + virtual void OnNativeWidgetDestroyed() OVERRIDE; + virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual gfx::Size GetMaximumSize() OVERRIDE; + virtual void OnNativeWidgetMove() OVERRIDE; + virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) OVERRIDE; + virtual void OnNativeWidgetBeginUserBoundsChange() OVERRIDE; + virtual void OnNativeWidgetEndUserBoundsChange() OVERRIDE; + virtual bool HasFocusManager() const OVERRIDE; + virtual bool OnNativeWidgetPaintAccelerated( + const gfx::Rect& dirty_region) OVERRIDE; + virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) OVERRIDE; + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + virtual bool ExecuteCommand(int command_id) OVERRIDE; + virtual InputMethod* GetInputMethodDirect() OVERRIDE; + virtual const std::vector<ui::Layer*>& GetRootLayers() OVERRIDE; + virtual bool HasHitTestMask() const OVERRIDE; + virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; + virtual Widget* AsWidget() OVERRIDE; + virtual const Widget* AsWidget() const OVERRIDE; + + // Overridden from FocusTraversable: + virtual FocusSearch* GetFocusSearch() OVERRIDE; + virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE; + virtual View* GetFocusTraversableParentView() OVERRIDE; + + protected: + // Creates the RootView to be used within this Widget. Subclasses may override + // to create custom RootViews that do specialized event processing. + // TODO(beng): Investigate whether or not this is needed. + virtual internal::RootView* CreateRootView(); + + // Provided to allow the NativeWidget implementations to destroy the RootView + // _before_ the focus manager/tooltip manager. + // TODO(beng): remove once we fold those objects onto this one. + void DestroyRootView(); + + private: + friend class NativeTextfieldViewsTest; + friend class NativeComboboxViewsTest; + + // Returns whether capture should be released on mouse release. + virtual bool ShouldReleaseCaptureOnMouseReleased() const; + + // Sets the value of |disable_inactive_rendering_|. If the value changes, + // both the NonClientView and WidgetDelegate are notified. + void SetInactiveRenderingDisabled(bool value); + + // Persists the window's restored position and "show" state using the + // window delegate. + void SaveWindowPlacement(); + + // Sizes and positions the window just after it is created. + void SetInitialBounds(const gfx::Rect& bounds); + + // Sizes and positions the frameless window just after it is created. + void SetInitialBoundsForFramelessWindow(const gfx::Rect& bounds); + + // Returns the bounds and "show" state from the delegate. Returns true if + // the delegate wants to use a specified bounds. + bool GetSavedWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state); + + // Creates and initializes a new InputMethod and returns it, otherwise null. + scoped_ptr<InputMethod> CreateInputMethod(); + + // Sets a different InputMethod instance to this widget. The instance + // must not be initialized, the ownership will be assumed by the widget. + // It's only for testing purpose. + void ReplaceInputMethod(InputMethod* input_method); + + internal::NativeWidgetPrivate* native_widget_; + + ObserverList<WidgetObserver> observers_; + + // Non-owned pointer to the Widget's delegate. May be NULL if no delegate is + // being used. + WidgetDelegate* widget_delegate_; + + // The root of the View hierarchy attached to this window. + // WARNING: see warning in tooltip_manager_ for ordering dependencies with + // this and tooltip_manager_. + scoped_ptr<internal::RootView> root_view_; + + // The View that provides the non-client area of the window (title bar, + // window controls, sizing borders etc). To use an implementation other than + // the default, this class must be sub-classed and this value set to the + // desired implementation before calling |InitWindow()|. + NonClientView* non_client_view_; + + // The focus manager keeping track of focus for this Widget and any of its + // children. NULL for non top-level widgets. + // WARNING: RootView's destructor calls into the FocusManager. As such, this + // must be destroyed AFTER root_view_. This is enforced in DestroyRootView(). + scoped_ptr<FocusManager> focus_manager_; + + // A theme provider to use when no other theme provider is specified. + scoped_ptr<ui::DefaultThemeProvider> default_theme_provider_; + + // Valid for the lifetime of RunShellDrag(), indicates the view the drag + // started from. + View* dragged_view_; + + // See class documentation for Widget above for a note about ownership. + InitParams::Ownership ownership_; + + // See set_is_secondary_widget(). + bool is_secondary_widget_; + + // The current frame type in use by this window. Defaults to + // FRAME_TYPE_DEFAULT. + FrameType frame_type_; + + // True when the window should be rendered as active, regardless of whether + // or not it actually is. + bool disable_inactive_rendering_; + + // Set to true if the widget is in the process of closing. + bool widget_closed_; + + // The saved "show" state for this window. See note in SetInitialBounds + // that explains why we save this. + ui::WindowShowState saved_show_state_; + + // The restored bounds used for the initial show. This is only used if + // |saved_show_state_| is maximized. + gfx::Rect initial_restored_bounds_; + + // Focus is automatically set to the view provided by the delegate + // when the widget is shown. Set this value to false to override + // initial focus for the widget. + bool focus_on_creation_; + + mutable scoped_ptr<InputMethod> input_method_; + + // See |is_top_level()| accessor. + bool is_top_level_; + + // Tracks whether native widget has been initialized. + bool native_widget_initialized_; + + // Whether native widget has been destroyed. + bool native_widget_destroyed_; + + // TODO(beng): Remove NativeWidgetGtk's dependence on these: + // If true, the mouse is currently down. + bool is_mouse_button_pressed_; + + // If true, a touch device is currently down. + bool is_touch_down_; + + // TODO(beng): Remove NativeWidgetGtk's dependence on these: + // The following are used to detect duplicate mouse move events and not + // deliver them. Displaying a window may result in the system generating + // duplicate move events even though the mouse hasn't moved. + bool last_mouse_event_was_move_; + gfx::Point last_mouse_event_position_; + + // See description in GetRootLayers(). + std::vector<ui::Layer*> root_layers_; + + // Is |root_layers_| out of date? + bool root_layers_dirty_; + + // True when window movement via mouse interaction with the frame should be + // disabled. + bool movement_disabled_; + + DISALLOW_COPY_AND_ASSIGN(Widget); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_H_ diff --git a/chromium/ui/views/widget/widget_aura_utils.cc b/chromium/ui/views/widget/widget_aura_utils.cc new file mode 100644 index 00000000000..76422e0c52f --- /dev/null +++ b/chromium/ui/views/widget/widget_aura_utils.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/widget_aura_utils.h" + +#include "base/logging.h" + +namespace views { + +aura::client::WindowType GetAuraWindowTypeForWidgetType( + Widget::InitParams::Type type) { + switch (type) { + case Widget::InitParams::TYPE_WINDOW: + return aura::client::WINDOW_TYPE_NORMAL; + case Widget::InitParams::TYPE_PANEL: + return aura::client::WINDOW_TYPE_PANEL; + case Widget::InitParams::TYPE_CONTROL: + return aura::client::WINDOW_TYPE_CONTROL; + case Widget::InitParams::TYPE_WINDOW_FRAMELESS: + case Widget::InitParams::TYPE_POPUP: + case Widget::InitParams::TYPE_BUBBLE: + return aura::client::WINDOW_TYPE_POPUP; + case Widget::InitParams::TYPE_MENU: + return aura::client::WINDOW_TYPE_MENU; + case Widget::InitParams::TYPE_TOOLTIP: + return aura::client::WINDOW_TYPE_TOOLTIP; + default: + NOTREACHED() << "Unhandled widget type " << type; + return aura::client::WINDOW_TYPE_UNKNOWN; + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/widget_aura_utils.h b/chromium/ui/views/widget/widget_aura_utils.h new file mode 100644 index 00000000000..8f0bfcc0358 --- /dev/null +++ b/chromium/ui/views/widget/widget_aura_utils.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_AURA_UTILS_H_ +#define UI_VIEWS_WIDGET_WIDGET_AURA_UTILS_H_ + +#include "ui/aura/client/window_types.h" +#include "ui/views/widget/widget.h" + +// Functions shared by native_widget_aura.cc and desktop_native_widget_aura.cc: + +namespace views { + +aura::client::WindowType GetAuraWindowTypeForWidgetType( + Widget::InitParams::Type type); + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_AURA_UTILS_H_ diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc new file mode 100644 index 00000000000..dcb7c361030 --- /dev/null +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/widget_delegate.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/view.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/widget.h" +#include "ui/views/window/client_view.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// WidgetDelegate: + +WidgetDelegate::WidgetDelegate() : default_contents_view_(NULL) { +} + +void WidgetDelegate::OnWidgetMove() { +} + +void WidgetDelegate::OnDisplayChanged() { +} + +void WidgetDelegate::OnWorkAreaChanged() { +} + +View* WidgetDelegate::GetInitiallyFocusedView() { + return NULL; +} + +BubbleDelegateView* WidgetDelegate::AsBubbleDelegate() { + return NULL; +} + +DialogDelegate* WidgetDelegate::AsDialogDelegate() { + return NULL; +} + +bool WidgetDelegate::CanResize() const { + return false; +} + +bool WidgetDelegate::CanMaximize() const { + return false; +} + +bool WidgetDelegate::CanActivate() const { + return true; +} + +ui::ModalType WidgetDelegate::GetModalType() const { + return ui::MODAL_TYPE_NONE; +} + +ui::AccessibilityTypes::Role WidgetDelegate::GetAccessibleWindowRole() const { + return ui::AccessibilityTypes::ROLE_WINDOW; +} + +string16 WidgetDelegate::GetAccessibleWindowTitle() const { + return GetWindowTitle(); +} + +string16 WidgetDelegate::GetWindowTitle() const { + return string16(); +} + +bool WidgetDelegate::ShouldShowWindowTitle() const { + return true; +} + +bool WidgetDelegate::ShouldShowCloseButton() const { + return true; +} + +bool WidgetDelegate::ShouldHandleSystemCommands() const { + const Widget* widget = GetWidget(); + if (!widget) + return false; + + return widget->non_client_view() != NULL; +} + +gfx::ImageSkia WidgetDelegate::GetWindowAppIcon() { + // Use the window icon as app icon by default. + return GetWindowIcon(); +} + +// Returns the icon to be displayed in the window. +gfx::ImageSkia WidgetDelegate::GetWindowIcon() { + return gfx::ImageSkia(); +} + +bool WidgetDelegate::ShouldShowWindowIcon() const { + return false; +} + +bool WidgetDelegate::ExecuteWindowsCommand(int command_id) { + return false; +} + +std::string WidgetDelegate::GetWindowName() const { + return std::string(); +} + +void WidgetDelegate::SaveWindowPlacement(const gfx::Rect& bounds, + ui::WindowShowState show_state) { + std::string window_name = GetWindowName(); + if (!ViewsDelegate::views_delegate || window_name.empty()) + return; + + ViewsDelegate::views_delegate->SaveWindowPlacement( + GetWidget(), window_name, bounds, show_state); +} + +bool WidgetDelegate::GetSavedWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + std::string window_name = GetWindowName(); + if (!ViewsDelegate::views_delegate || window_name.empty()) + return false; + + return ViewsDelegate::views_delegate->GetSavedWindowPlacement( + window_name, bounds, show_state); +} + +bool WidgetDelegate::ShouldRestoreWindowSize() const { + return true; +} + +View* WidgetDelegate::GetContentsView() { + if (!default_contents_view_) + default_contents_view_ = new View; + return default_contents_view_; +} + +ClientView* WidgetDelegate::CreateClientView(Widget* widget) { + return new ClientView(widget, GetContentsView()); +} + +NonClientFrameView* WidgetDelegate::CreateNonClientFrameView(Widget* widget) { + return NULL; +} + +View* WidgetDelegate::CreateOverlayView() { + return NULL; +} + +bool WidgetDelegate::WillProcessWorkAreaChange() const { + return false; +} + +bool WidgetDelegate::WidgetHasHitTestMask() const { + return false; +} + +void WidgetDelegate::GetWidgetHitTestMask(gfx::Path* mask) const { + DCHECK(mask); +} + +bool WidgetDelegate::ShouldDescendIntoChildForEventHandling( + gfx::NativeView child, + const gfx::Point& location) { + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// WidgetDelegateView: + +WidgetDelegateView::WidgetDelegateView() { + // A WidgetDelegate should be deleted on DeleteDelegate. + set_owned_by_client(); +} + +WidgetDelegateView::~WidgetDelegateView() { +} + +void WidgetDelegateView::DeleteDelegate() { + delete this; +} + +Widget* WidgetDelegateView::GetWidget() { + return View::GetWidget(); +} + +const Widget* WidgetDelegateView::GetWidget() const { + return View::GetWidget(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h new file mode 100644 index 00000000000..f465ffb3bc1 --- /dev/null +++ b/chromium/ui/views/widget/widget_delegate.h @@ -0,0 +1,201 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_ +#define UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_ + +#include <string> +#include <vector> + +#include "ui/base/accessibility/accessibility_types.h" +#include "ui/base/ui_base_types.h" +#include "ui/views/view.h" + +namespace gfx { +class ImageSkia; +class Rect; +} + +namespace views { +class BubbleDelegateView; +class ClientView; +class DialogDelegate; +class NonClientFrameView; +class View; +class Widget; + +// Handles events on Widgets in context-specific ways. +class VIEWS_EXPORT WidgetDelegate { + public: + WidgetDelegate(); + + // Called whenever the widget's position changes. + virtual void OnWidgetMove(); + + // Called with the display changes (color depth or resolution). + virtual void OnDisplayChanged(); + + // Called when the work area (the desktop area minus task bars, + // menu bars, etc.) changes in size. + virtual void OnWorkAreaChanged(); + + // Returns the view that should have the focus when the widget is shown. If + // NULL no view is focused. + virtual View* GetInitiallyFocusedView(); + + virtual BubbleDelegateView* AsBubbleDelegate(); + virtual DialogDelegate* AsDialogDelegate(); + + // Returns true if the window can ever be resized. + virtual bool CanResize() const; + + // Returns true if the window can ever be maximized. + virtual bool CanMaximize() const; + + // Returns true if the window can be activated. + virtual bool CanActivate() const; + + // Returns the modal type that applies to the widget. Default is + // ui::MODAL_TYPE_NONE (not modal). + virtual ui::ModalType GetModalType() const; + + virtual ui::AccessibilityTypes::Role GetAccessibleWindowRole() const; + + // Returns the title to be read with screen readers. + virtual string16 GetAccessibleWindowTitle() const; + + // Returns the text to be displayed in the window title. + virtual string16 GetWindowTitle() const; + + // Returns true if the window should show a title in the title bar. + virtual bool ShouldShowWindowTitle() const; + + // Returns true if the window should show a close button in the title bar. + virtual bool ShouldShowCloseButton() const; + + // Returns true if the window should handle standard system commands, such as + // close, minimize, maximize. + virtual bool ShouldHandleSystemCommands() const; + + // Returns the app icon for the window. On Windows, this is the ICON_BIG used + // in Alt-Tab list and Win7's taskbar. + virtual gfx::ImageSkia GetWindowAppIcon(); + + // Returns the icon to be displayed in the window. + virtual gfx::ImageSkia GetWindowIcon(); + + // Returns true if a window icon should be shown. + virtual bool ShouldShowWindowIcon() const; + + // Execute a command in the window's controller. Returns true if the command + // was handled, false if it was not. + virtual bool ExecuteWindowsCommand(int command_id); + + // Returns the window's name identifier. Used to identify this window for + // state restoration. + virtual std::string GetWindowName() const; + + // Saves the window's bounds and "show" state. By default this uses the + // process' local state keyed by window name (See GetWindowName above). This + // behavior can be overridden to provide additional functionality. + virtual void SaveWindowPlacement(const gfx::Rect& bounds, + ui::WindowShowState show_state); + + // Retrieves the window's bounds and "show" states. + // This behavior can be overridden to provide additional functionality. + virtual bool GetSavedWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state) const; + + // Returns true if the window's size should be restored. If this is false, + // only the window's origin is restored and the window is given its + // preferred size. + // Default is true. + virtual bool ShouldRestoreWindowSize() const; + + // Called when the window closes. The delegate MUST NOT delete itself during + // this call, since it can be called afterwards. See DeleteDelegate(). + virtual void WindowClosing() {} + + // Called when the window is destroyed. No events must be sent or received + // after this point. The delegate can use this opportunity to delete itself at + // this time if necessary. + virtual void DeleteDelegate() {} + + // Called when the user begins/ends to change the bounds of the window. + virtual void OnWindowBeginUserBoundsChange() {} + virtual void OnWindowEndUserBoundsChange() {} + + // Returns the Widget associated with this delegate. + virtual Widget* GetWidget() = 0; + virtual const Widget* GetWidget() const = 0; + + // Returns the View that is contained within this Widget. + virtual View* GetContentsView(); + + // Called by the Widget to create the Client View used to host the contents + // of the widget. + virtual ClientView* CreateClientView(Widget* widget); + + // Called by the Widget to create the NonClient Frame View for this widget. + // Return NULL to use the default one. + virtual NonClientFrameView* CreateNonClientFrameView(Widget* widget); + + // Called by the Widget to create the overlay View for this widget. Return + // NULL for no overlay. The overlay View will fill the Widget and sit on top + // of the ClientView and NonClientFrameView (both visually and wrt click + // targeting). + virtual View* CreateOverlayView(); + + // Returns true if the window can be notified with the work area change. + // Otherwise, the work area change for the top window will be processed by + // the default window manager. In some cases, like panel, we would like to + // manage the positions by ourselves. + virtual bool WillProcessWorkAreaChange() const; + + // Returns true if window has a hit-test mask. + virtual bool WidgetHasHitTestMask() const; + + // Provides the hit-test mask if HasHitTestMask above returns true. + virtual void GetWidgetHitTestMask(gfx::Path* mask) const; + + // Returns true if event handling should descend into |child|. + // |location| is in terms of the Window. + virtual bool ShouldDescendIntoChildForEventHandling( + gfx::NativeView child, + const gfx::Point& location); + + // Populates |panes| with accessible panes in this window that can + // be cycled through with keyboard focus. + virtual void GetAccessiblePanes(std::vector<View*>* panes) {} + + protected: + virtual ~WidgetDelegate() {} + + private: + View* default_contents_view_; + + DISALLOW_COPY_AND_ASSIGN(WidgetDelegate); +}; + +// A WidgetDelegate implementation that is-a View. Used to override GetWidget() +// to call View's GetWidget() for the common case where a WidgetDelegate +// implementation is-a View. Note that WidgetDelegateView is not owned by +// view's hierarchy and is expected to be deleted on DeleteDelegate call. +class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View { + public: + WidgetDelegateView(); + virtual ~WidgetDelegateView(); + + // Overridden from WidgetDelegate: + virtual void DeleteDelegate() OVERRIDE; + virtual Widget* GetWidget() OVERRIDE; + virtual const Widget* GetWidget() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(WidgetDelegateView); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_ diff --git a/chromium/ui/views/widget/widget_deletion_observer.cc b/chromium/ui/views/widget/widget_deletion_observer.cc new file mode 100644 index 00000000000..db3a44e7d8e --- /dev/null +++ b/chromium/ui/views/widget/widget_deletion_observer.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/widget_deletion_observer.h" + +#include "ui/views/widget/widget.h" + +namespace views { + +WidgetDeletionObserver::WidgetDeletionObserver(Widget* widget) + : widget_(widget) { + if (widget_) + widget_->AddObserver(this); +} + +WidgetDeletionObserver::~WidgetDeletionObserver() { + CleanupWidget(); +} + +void WidgetDeletionObserver::OnWidgetDestroying(Widget* widget) { + CleanupWidget(); +} + +void WidgetDeletionObserver::CleanupWidget() { + if (widget_) { + widget_->RemoveObserver(this); + widget_ = NULL; + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/widget_deletion_observer.h b/chromium/ui/views/widget/widget_deletion_observer.h new file mode 100644 index 00000000000..1956e7f1aec --- /dev/null +++ b/chromium/ui/views/widget/widget_deletion_observer.h @@ -0,0 +1,37 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_DELETION_OBSERVER_H_ +#define UI_VIEWS_WIDGET_WIDGET_DELETION_OBSERVER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { +class Widget; + +// A simple WidgetObserver that can be probed for the life of a widget. +class WidgetDeletionObserver : public WidgetObserver { + public: + explicit WidgetDeletionObserver(Widget* widget); + virtual ~WidgetDeletionObserver(); + + // Returns true if the widget passed in the constructor is still alive. + bool IsWidgetAlive() { return widget_ != NULL; } + + // Overridden from WidgetObserver. + virtual void OnWidgetDestroying(Widget* widget) OVERRIDE; + + private: + void CleanupWidget(); + + Widget* widget_; + + DISALLOW_COPY_AND_ASSIGN(WidgetDeletionObserver); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_DELETION_OBSERVER_H_ diff --git a/chromium/ui/views/widget/widget_hwnd_utils.cc b/chromium/ui/views/widget/widget_hwnd_utils.cc new file mode 100644 index 00000000000..53588c25e5e --- /dev/null +++ b/chromium/ui/views/widget/widget_hwnd_utils.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/widget_hwnd_utils.h" + +#include <dwmapi.h> + +#include "base/command_line.h" +#include "base/win/windows_version.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/ui_base_switches.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/win/hwnd_message_handler.h" + +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + +namespace views { + +namespace { + +void CalculateWindowStylesFromInitParams( + const Widget::InitParams& params, + WidgetDelegate* widget_delegate, + internal::NativeWidgetDelegate* native_widget_delegate, + DWORD* style, + DWORD* ex_style, + DWORD* class_style) { + *style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + *ex_style = 0; + *class_style = CS_DBLCLKS; + + // Set type-independent style attributes. + if (params.child) + *style |= WS_CHILD; + if (params.show_state == ui::SHOW_STATE_MAXIMIZED) + *style |= WS_MAXIMIZE; + if (params.show_state == ui::SHOW_STATE_MINIMIZED) + *style |= WS_MINIMIZE; + if (!params.accept_events) + *ex_style |= WS_EX_TRANSPARENT; + if (!params.can_activate) + *ex_style |= WS_EX_NOACTIVATE; + if (params.keep_on_top) + *ex_style |= WS_EX_TOPMOST; + if (params.mirror_origin_in_rtl) + *ex_style |= l10n_util::GetExtendedTooltipStyles(); + // Layered windows do not work with Aura. They are basically incompatible + // with Direct3D surfaces. Officially, it should be impossible to achieve + // per-pixel alpha compositing with the desktop and 3D acceleration but it + // has been discovered that since Vista There is a secret handshake between + // user32 and the DMW. If things are set up just right DMW gets out of the + // way; it does not create a backbuffer and simply blends our D3D surface + // and the desktop background. The handshake is as follows: + // 1- Use D3D9Ex to create device/swapchain, etc. You need D3DFMT_A8R8G8B8. + // 2- The window must have WS_EX_COMPOSITED in the extended style. + // 3- The window must have WS_POPUP in its style. + // 4- The windows must not have WM_SIZEBOX, WS_THICKFRAME or WS_CAPTION in its + // style. + // 5- When the window is created but before it is presented, call + // DwmExtendFrameIntoClientArea passing -1 as the margins. + if (params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW) { +#if defined(USE_AURA) + if (ui::win::IsAeroGlassEnabled()) + *ex_style |= WS_EX_COMPOSITED; +#else + *ex_style |= WS_EX_LAYERED; +#endif + } + if (params.has_dropshadow) { + *class_style |= (base::win::GetVersion() < base::win::VERSION_XP) ? + 0 : CS_DROPSHADOW; + } + + // Set type-dependent style attributes. + switch (params.type) { + case Widget::InitParams::TYPE_PANEL: + *ex_style |= WS_EX_TOPMOST; + if (params.remove_standard_frame) { + *style |= WS_POPUP; + break; + } + // Else, no break. Fall through to TYPE_WINDOW. + case Widget::InitParams::TYPE_WINDOW: { + *style |= WS_SYSMENU | WS_CAPTION; + bool can_resize = widget_delegate->CanResize(); + bool can_maximize = widget_delegate->CanMaximize(); + if (can_maximize) { + *style |= WS_OVERLAPPEDWINDOW; + } else if (can_resize || params.remove_standard_frame) { + *style |= WS_OVERLAPPED | WS_THICKFRAME; + } + if (native_widget_delegate->IsDialogBox()) { + *style |= DS_MODALFRAME; + // NOTE: Turning this off means we lose the close button, which is bad. + // Turning it on though means the user can maximize or size the window + // from the system menu, which is worse. We may need to provide our own + // menu to get the close button to appear properly. + // style &= ~WS_SYSMENU; + + // Set the WS_POPUP style for modal dialogs. This ensures that the owner + // window is activated on destruction. This style should not be set for + // non-modal non-top-level dialogs like constrained windows. + *style |= native_widget_delegate->IsModal() ? WS_POPUP : 0; + } + *ex_style |= + native_widget_delegate->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0; + + // See layered window comment above. + if (*ex_style & WS_EX_COMPOSITED) + *style &= ~(WS_THICKFRAME | WS_CAPTION); + break; + } + case Widget::InitParams::TYPE_CONTROL: + *style |= WS_VISIBLE; + break; + case Widget::InitParams::TYPE_WINDOW_FRAMELESS: + *style |= WS_POPUP; + break; + case Widget::InitParams::TYPE_BUBBLE: + *style |= WS_POPUP; + *style |= WS_CLIPCHILDREN; + break; + case Widget::InitParams::TYPE_POPUP: + *style |= WS_POPUP; + *ex_style |= WS_EX_TOOLWINDOW; + break; + case Widget::InitParams::TYPE_MENU: + *style |= WS_POPUP; + break; + default: + NOTREACHED(); + } +} + +} // namespace + +bool DidClientAreaSizeChange(const WINDOWPOS* window_pos) { + return !(window_pos->flags & SWP_NOSIZE) || + window_pos->flags & SWP_FRAMECHANGED; +} + +void ConfigureWindowStyles( + HWNDMessageHandler* handler, + const Widget::InitParams& params, + WidgetDelegate* widget_delegate, + internal::NativeWidgetDelegate* native_widget_delegate) { + // Configure the HWNDMessageHandler with the appropriate + DWORD style = 0; + DWORD ex_style = 0; + DWORD class_style = 0; + CalculateWindowStylesFromInitParams(params, widget_delegate, + native_widget_delegate, &style, &ex_style, + &class_style); + handler->set_initial_class_style(class_style); + handler->set_window_style(handler->window_style() | style); + handler->set_window_ex_style(handler->window_ex_style() | ex_style); +} + +} // namespace views diff --git a/chromium/ui/views/widget/widget_hwnd_utils.h b/chromium/ui/views/widget/widget_hwnd_utils.h new file mode 100644 index 00000000000..2808bab0d48 --- /dev/null +++ b/chromium/ui/views/widget/widget_hwnd_utils.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_HWND_UTILS_H_ +#define UI_VIEWS_WIDGET_WIDGET_HWND_UTILS_H_ + +#include <windows.h> + +#include "ui/views/widget/widget.h" + +// Functions shared by native_widget_win.cc and desktop_root_window_host_win.cc: + +namespace views { +class HWNDMessageHandler; +class WidgetDelegate; +namespace internal { +class NativeWidgetDelegate; +} + +// Returns true if the WINDOWPOS data provided indicates the client area of +// the window may have changed size. This can be caused by the window being +// resized or its frame changing. +bool DidClientAreaSizeChange(const WINDOWPOS* window_pos); + +// Sets styles appropriate for |params| on |handler|. +void ConfigureWindowStyles( + HWNDMessageHandler* handler, + const Widget::InitParams& params, + WidgetDelegate* widget_delegate, + internal::NativeWidgetDelegate* native_widget_delegate); + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_HWND_UTILS_H_ diff --git a/chromium/ui/views/widget/widget_observer.h b/chromium/ui/views/widget/widget_observer.h new file mode 100644 index 00000000000..e156ef80c88 --- /dev/null +++ b/chromium/ui/views/widget/widget_observer.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WIDGET_OBSERVER_H_ +#define UI_VIEWS_WIDGET_WIDGET_OBSERVER_H_ + +#include "ui/views/views_export.h" + +namespace gfx { +class Rect; +} + +namespace views { + +class Widget; + +// Observers can listen to various events on the Widgets. +class VIEWS_EXPORT WidgetObserver { + public: + // The closing notification is sent immediately in response to (i.e. in the + // same call stack as) a request to close the Widget (via Close() or + // CloseNow()). + virtual void OnWidgetClosing(Widget* widget) {} + + // Invoked after notification is received from the event loop that the native + // widget has been created. + virtual void OnWidgetCreated(Widget* widget) {} + + // The destroying event occurs immediately before the widget is destroyed. + // This typically occurs asynchronously with respect the the close request, as + // a result of a later invocation from the event loop. + virtual void OnWidgetDestroying(Widget* widget) {} + + // Invoked after notification is received from the event loop that the native + // widget has been destroyed. + virtual void OnWidgetDestroyed(Widget* widget) {} + + virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) {} + + virtual void OnWidgetActivationChanged(Widget* widget, bool active) {} + + virtual void OnWidgetBoundsChanged(Widget* widget, + const gfx::Rect& new_bounds) {} + + protected: + virtual ~WidgetObserver() {} +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_OBSERVER_H_ diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc new file mode 100644 index 00000000000..1153538b8d8 --- /dev/null +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -0,0 +1,2116 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/events/event_utils.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/point.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/test/test_views_delegate.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/native_widget_delegate.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/window/native_frame_view.h" + +#if defined(USE_AURA) +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/env.h" +#include "ui/aura/root_window.h" +#include "ui/aura/test/test_cursor_client.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" +#include "ui/views/widget/native_widget_aura.h" +#if !defined(OS_CHROMEOS) +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#endif +#elif defined(OS_WIN) +#include "ui/views/widget/native_widget_win.h" +#endif + +namespace views { +namespace test { + +// A generic typedef to pick up relevant NativeWidget implementations. +#if defined(USE_AURA) +typedef NativeWidgetAura NativeWidgetPlatform; +#elif defined(OS_WIN) +typedef NativeWidgetWin NativeWidgetPlatform; +#endif + +// A widget that assumes mouse capture always works. It won't on Aura in +// testing, so we mock it. +#if defined(USE_AURA) +class NativeWidgetCapture : public NativeWidgetPlatform { + public: + explicit NativeWidgetCapture(internal::NativeWidgetDelegate* delegate) + : NativeWidgetPlatform(delegate), + mouse_capture_(false) {} + virtual ~NativeWidgetCapture() {} + + virtual void SetCapture() OVERRIDE { + mouse_capture_ = true; + } + virtual void ReleaseCapture() OVERRIDE { + if (mouse_capture_) + delegate()->OnMouseCaptureLost(); + mouse_capture_ = false; + } + virtual bool HasCapture() const OVERRIDE { + return mouse_capture_; + } + + private: + bool mouse_capture_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetCapture); +}; +#endif + +// A typedef that inserts our mock-capture NativeWidget implementation for +// relevant platforms. +#if defined(USE_AURA) +typedef NativeWidgetCapture NativeWidgetPlatformForTest; +#elif defined(OS_WIN) +typedef NativeWidgetWin NativeWidgetPlatformForTest; +#endif + +// A view that always processes all mouse events. +class MouseView : public View { + public: + MouseView() + : View(), + entered_(0), + exited_(0), + pressed_(0) { + } + virtual ~MouseView() {} + + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { + pressed_++; + return true; + } + + virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { + entered_++; + } + + virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { + exited_++; + } + + // Return the number of OnMouseEntered calls and reset the counter. + int EnteredCalls() { + int i = entered_; + entered_ = 0; + return i; + } + + // Return the number of OnMouseExited calls and reset the counter. + int ExitedCalls() { + int i = exited_; + exited_ = 0; + return i; + } + + int pressed() const { return pressed_; } + + private: + int entered_; + int exited_; + + int pressed_; + + DISALLOW_COPY_AND_ASSIGN(MouseView); +}; + +// A view that keeps track of the events it receives, but consumes no events. +class EventCountView : public View { + public: + EventCountView() {} + virtual ~EventCountView() {} + + int GetEventCount(ui::EventType type) { + return event_count_[type]; + } + + void ResetCounts() { + event_count_.clear(); + } + + protected: + // Overridden from ui::EventHandler: + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { + RecordEvent(*event); + } + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { + RecordEvent(*event); + } + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE { + RecordEvent(*event); + } + virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE { + RecordEvent(*event); + } + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + RecordEvent(*event); + } + + private: + void RecordEvent(const ui::Event& event) { + ++event_count_[event.type()]; + } + + std::map<ui::EventType, int> event_count_; + + DISALLOW_COPY_AND_ASSIGN(EventCountView); +}; + +// A view that keeps track of the events it receives, and consumes all scroll +// gesture events. +class ScrollableEventCountView : public EventCountView { + public: + ScrollableEventCountView() {} + virtual ~ScrollableEventCountView() {} + + private: + // Overridden from ui::EventHandler: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + EventCountView::OnGestureEvent(event); + switch (event->type()) { + case ui::ET_GESTURE_SCROLL_BEGIN: + case ui::ET_GESTURE_SCROLL_UPDATE: + case ui::ET_GESTURE_SCROLL_END: + case ui::ET_SCROLL_FLING_START: + event->SetHandled(); + break; + default: + break; + } + } + + DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView); +}; + +// A view that does a capture on gesture-begin events. +class GestureCaptureView : public View { + public: + GestureCaptureView() {} + virtual ~GestureCaptureView() {} + + private: + // Overridden from View: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + if (event->type() == ui::ET_GESTURE_BEGIN) { + GetWidget()->SetCapture(this); + event->StopPropagation(); + } + } + + DISALLOW_COPY_AND_ASSIGN(GestureCaptureView); +}; + +// A view that implements GetMinimumSize. +class MinimumSizeFrameView : public NativeFrameView { + public: + explicit MinimumSizeFrameView(Widget* frame): NativeFrameView(frame) {} + virtual ~MinimumSizeFrameView() {} + + private: + // Overridden from View: + virtual gfx::Size GetMinimumSize() OVERRIDE { + return gfx::Size(300, 400); + } + + DISALLOW_COPY_AND_ASSIGN(MinimumSizeFrameView); +}; + +// An event handler that simply keeps a count of the different types of events +// it receives. +class EventCountHandler : public ui::EventHandler { + public: + EventCountHandler() {} + virtual ~EventCountHandler() {} + + int GetEventCount(ui::EventType type) { + return event_count_[type]; + } + + void ResetCounts() { + event_count_.clear(); + } + + protected: + // Overridden from ui::EventHandler: + virtual void OnEvent(ui::Event* event) OVERRIDE { + RecordEvent(*event); + ui::EventHandler::OnEvent(event); + } + + private: + void RecordEvent(const ui::Event& event) { + ++event_count_[event.type()]; + } + + std::map<ui::EventType, int> event_count_; + + DISALLOW_COPY_AND_ASSIGN(EventCountHandler); +}; + +// A View that shows a different widget, sets capture on that widget, and +// initiates a nested message-loop when it receives a mouse-press event. +class NestedLoopCaptureView : public View { + public: + explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {} + virtual ~NestedLoopCaptureView() {} + + private: + // Overridden from View: + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { + // Start a nested loop. + widget_->Show(); + widget_->SetCapture(widget_->GetContentsView()); + EXPECT_TRUE(widget_->HasCapture()); + + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + + base::RunLoop run_loop; +#if defined(USE_AURA) + run_loop.set_dispatcher(aura::Env::GetInstance()->GetDispatcher()); +#endif + run_loop.Run(); + return true; + } + + Widget* widget_; + + DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView); +}; + +// A View that closes the Widget and exits the current message-loop when it +// receives a mouse-release event. +class ExitLoopOnRelease : public View { + public: + ExitLoopOnRelease() {} + virtual ~ExitLoopOnRelease() {} + + private: + // Overridden from View: + virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { + GetWidget()->Close(); + base::MessageLoop::current()->QuitNow(); + } + + DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); +}; + +class WidgetTest : public ViewsTestBase { + public: + WidgetTest() {} + virtual ~WidgetTest() {} + + NativeWidget* CreatePlatformNativeWidget( + internal::NativeWidgetDelegate* delegate) { + return new NativeWidgetPlatformForTest(delegate); + } + + Widget* CreateTopLevelPlatformWidget() { + Widget* toplevel = new Widget; + Widget::InitParams toplevel_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + toplevel_params.native_widget = CreatePlatformNativeWidget(toplevel); + toplevel->Init(toplevel_params); + return toplevel; + } + + Widget* CreateTopLevelFramelessPlatformWidget() { + Widget* toplevel = new Widget; + Widget::InitParams toplevel_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + toplevel_params.native_widget = CreatePlatformNativeWidget(toplevel); + toplevel->Init(toplevel_params); + return toplevel; + } + + Widget* CreateChildPlatformWidget(gfx::NativeView parent_native_view) { + Widget* child = new Widget; + Widget::InitParams child_params = + CreateParams(Widget::InitParams::TYPE_CONTROL); + child_params.native_widget = CreatePlatformNativeWidget(child); + child_params.parent = parent_native_view; + child->Init(child_params); + child->SetContentsView(new View); + return child; + } + +#if defined(OS_WIN) && !defined(USE_AURA) + // On Windows, it is possible for us to have a child window that is + // TYPE_POPUP. + Widget* CreateChildPopupPlatformWidget(gfx::NativeView parent_native_view) { + Widget* child = new Widget; + Widget::InitParams child_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + child_params.child = true; + child_params.native_widget = CreatePlatformNativeWidget(child); + child_params.parent = parent_native_view; + child->Init(child_params); + child->SetContentsView(new View); + return child; + } +#endif + + Widget* CreateTopLevelNativeWidget() { + Widget* toplevel = new Widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + toplevel->Init(params); + return toplevel; + } + + Widget* CreateChildNativeWidgetWithParent(Widget* parent) { + Widget* child = new Widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_CONTROL); + params.parent = parent->GetNativeView(); + child->Init(params); + child->SetContentsView(new View); + return child; + } + + Widget* CreateChildNativeWidget() { + return CreateChildNativeWidgetWithParent(NULL); + } + + View* GetMousePressedHandler(internal::RootView* root_view) { + return root_view->mouse_pressed_handler_; + } + + View* GetMouseMoveHandler(internal::RootView* root_view) { + return root_view->mouse_move_handler_; + } + + View* GetGestureHandler(internal::RootView* root_view) { + return root_view->gesture_handler_; + } +}; + +bool WidgetHasMouseCapture(const Widget* widget) { + return static_cast<const internal::NativeWidgetPrivate*>(widget-> + native_widget())->HasCapture(); +} + +ui::WindowShowState GetWidgetShowState(const Widget* widget) { + // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement + // because the former is implemented on all platforms but the latter is not. + return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : + widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED : + widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED : + ui::SHOW_STATE_NORMAL; +} + +TEST_F(WidgetTest, WidgetInitParams) { + ASSERT_FALSE(views_delegate().UseTransparentWindows()); + + // Widgets are not transparent by default. + Widget::InitParams init1; + EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init1.opacity); + + // Non-window widgets are not transparent either. + Widget::InitParams init2(Widget::InitParams::TYPE_MENU); + EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init2.opacity); + + // A ViewsDelegate can set windows transparent by default. + views_delegate().SetUseTransparentWindows(true); + Widget::InitParams init3; + EXPECT_EQ(Widget::InitParams::TRANSLUCENT_WINDOW, init3.opacity); + + // Non-window widgets stay opaque. + Widget::InitParams init4(Widget::InitParams::TYPE_MENU); + EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init4.opacity); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget::GetTopLevelWidget tests. + +TEST_F(WidgetTest, GetTopLevelWidget_Native) { + // Create a hierarchy of native widgets. + Widget* toplevel = CreateTopLevelPlatformWidget(); + gfx::NativeView parent = toplevel->GetNativeView(); + Widget* child = CreateChildPlatformWidget(parent); + + EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget()); + EXPECT_EQ(toplevel, child->GetTopLevelWidget()); + + toplevel->CloseNow(); + // |child| should be automatically destroyed with |toplevel|. +} + +// Tests some grab/ungrab events. +TEST_F(WidgetTest, DISABLED_GrabUngrab) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); + Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); + + toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); + + child1->SetBounds(gfx::Rect(10, 10, 300, 300)); + View* view = new MouseView(); + view->SetBounds(0, 0, 300, 300); + child1->GetRootView()->AddChildView(view); + + child2->SetBounds(gfx::Rect(200, 10, 200, 200)); + view = new MouseView(); + view->SetBounds(0, 0, 200, 200); + child2->GetRootView()->AddChildView(view); + + toplevel->Show(); + RunPendingMessages(); + + // Click on child1 + gfx::Point p1(45, 45); + ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, + ui::EF_LEFT_MOUSE_BUTTON); + toplevel->OnMouseEvent(&pressed); + + EXPECT_TRUE(WidgetHasMouseCapture(toplevel)); + EXPECT_TRUE(WidgetHasMouseCapture(child1)); + EXPECT_FALSE(WidgetHasMouseCapture(child2)); + + ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, + ui::EF_LEFT_MOUSE_BUTTON); + toplevel->OnMouseEvent(&released); + + EXPECT_FALSE(WidgetHasMouseCapture(toplevel)); + EXPECT_FALSE(WidgetHasMouseCapture(child1)); + EXPECT_FALSE(WidgetHasMouseCapture(child2)); + + RunPendingMessages(); + + // Click on child2 + gfx::Point p2(315, 45); + ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, + ui::EF_LEFT_MOUSE_BUTTON); + toplevel->OnMouseEvent(&pressed2); + EXPECT_TRUE(pressed2.handled()); + EXPECT_TRUE(WidgetHasMouseCapture(toplevel)); + EXPECT_TRUE(WidgetHasMouseCapture(child2)); + EXPECT_FALSE(WidgetHasMouseCapture(child1)); + + ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, + ui::EF_LEFT_MOUSE_BUTTON); + toplevel->OnMouseEvent(&released2); + EXPECT_FALSE(WidgetHasMouseCapture(toplevel)); + EXPECT_FALSE(WidgetHasMouseCapture(child1)); + EXPECT_FALSE(WidgetHasMouseCapture(child2)); + + toplevel->CloseNow(); +} + +// Tests mouse move outside of the window into the "resize controller" and back +// will still generate an OnMouseEntered and OnMouseExited event.. +TEST_F(WidgetTest, CheckResizeControllerEvents) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + + toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); + + MouseView* view = new MouseView(); + view->SetBounds(90, 90, 10, 10); + toplevel->GetRootView()->AddChildView(view); + + toplevel->Show(); + RunPendingMessages(); + + // Move to an outside position. + gfx::Point p1(200, 200); + ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE); + toplevel->OnMouseEvent(&moved_out); + EXPECT_EQ(0, view->EnteredCalls()); + EXPECT_EQ(0, view->ExitedCalls()); + + // Move onto the active view. + gfx::Point p2(95, 95); + ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE); + toplevel->OnMouseEvent(&moved_over); + EXPECT_EQ(1, view->EnteredCalls()); + EXPECT_EQ(0, view->ExitedCalls()); + + // Move onto the outer resizing border. + gfx::Point p3(102, 95); + ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE); + toplevel->OnMouseEvent(&moved_resizer); + EXPECT_EQ(0, view->EnteredCalls()); + EXPECT_EQ(1, view->ExitedCalls()); + + // Move onto the view again. + toplevel->OnMouseEvent(&moved_over); + EXPECT_EQ(1, view->EnteredCalls()); + EXPECT_EQ(0, view->ExitedCalls()); + + RunPendingMessages(); + + toplevel->CloseNow(); +} + +// Test if a focus manager and an inputmethod work without CHECK failure +// when window activation changes. +TEST_F(WidgetTest, ChangeActivation) { + Widget* top1 = CreateTopLevelPlatformWidget(); + // CreateInputMethod before activated + top1->GetInputMethod(); + top1->Show(); + RunPendingMessages(); + + Widget* top2 = CreateTopLevelPlatformWidget(); + top2->Show(); + RunPendingMessages(); + + top1->Activate(); + RunPendingMessages(); + + // Create InputMethod after deactivated. + top2->GetInputMethod(); + top2->Activate(); + RunPendingMessages(); + + top1->Activate(); + RunPendingMessages(); + + top1->CloseNow(); + top2->CloseNow(); +} + +// Tests visibility of child widgets. +TEST_F(WidgetTest, Visibility) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + gfx::NativeView parent = toplevel->GetNativeView(); + Widget* child = CreateChildPlatformWidget(parent); + + EXPECT_FALSE(toplevel->IsVisible()); + EXPECT_FALSE(child->IsVisible()); + + child->Show(); + + EXPECT_FALSE(toplevel->IsVisible()); + EXPECT_FALSE(child->IsVisible()); + + toplevel->Show(); + + EXPECT_TRUE(toplevel->IsVisible()); + EXPECT_TRUE(child->IsVisible()); + + toplevel->CloseNow(); + // |child| should be automatically destroyed with |toplevel|. +} + +#if defined(OS_WIN) && !defined(USE_AURA) +// On Windows, it is possible to have child window that are TYPE_POPUP. Unlike +// regular child windows, these should be created as hidden and must be shown +// explicitly. +TEST_F(WidgetTest, Visibility_ChildPopup) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + Widget* child_popup = CreateChildPopupPlatformWidget( + toplevel->GetNativeView()); + + EXPECT_FALSE(toplevel->IsVisible()); + EXPECT_FALSE(child_popup->IsVisible()); + + toplevel->Show(); + + EXPECT_TRUE(toplevel->IsVisible()); + EXPECT_FALSE(child_popup->IsVisible()); + + child_popup->Show(); + + EXPECT_TRUE(child_popup->IsVisible()); + + toplevel->CloseNow(); + // |child_popup| should be automatically destroyed with |toplevel|. +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Widget ownership tests. +// +// Tests various permutations of Widget ownership specified in the +// InitParams::Ownership param. + +// A WidgetTest that supplies a toplevel widget for NativeWidget to parent to. +class WidgetOwnershipTest : public WidgetTest { + public: + WidgetOwnershipTest() {} + virtual ~WidgetOwnershipTest() {} + + virtual void SetUp() { + WidgetTest::SetUp(); + desktop_widget_ = CreateTopLevelPlatformWidget(); + } + + virtual void TearDown() { + desktop_widget_->CloseNow(); + WidgetTest::TearDown(); + } + + private: + Widget* desktop_widget_; + + DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest); +}; + +// A bag of state to monitor destructions. +struct OwnershipTestState { + OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {} + + bool widget_deleted; + bool native_widget_deleted; +}; + +// A platform NativeWidget subclass that updates a bag of state when it is +// destroyed. +class OwnershipTestNativeWidget : public NativeWidgetPlatform { + public: + OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, + OwnershipTestState* state) + : NativeWidgetPlatform(delegate), + state_(state) { + } + virtual ~OwnershipTestNativeWidget() { + state_->native_widget_deleted = true; + } + + private: + OwnershipTestState* state_; + + DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget); +}; + +// A views NativeWidget subclass that updates a bag of state when it is +// destroyed. +class OwnershipTestNativeWidgetPlatform : public NativeWidgetPlatformForTest { + public: + OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate* delegate, + OwnershipTestState* state) + : NativeWidgetPlatformForTest(delegate), + state_(state) { + } + virtual ~OwnershipTestNativeWidgetPlatform() { + state_->native_widget_deleted = true; + } + + private: + OwnershipTestState* state_; + + DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetPlatform); +}; + +// A Widget subclass that updates a bag of state when it is destroyed. +class OwnershipTestWidget : public Widget { + public: + explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {} + virtual ~OwnershipTestWidget() { + state_->widget_deleted = true; + } + + private: + OwnershipTestState* state_; + + DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget); +}; + +// Widget owns its NativeWidget, part 1: NativeWidget is a platform-native +// widget. +TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { + OwnershipTestState state; + + scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + // Now delete the Widget, which should delete the NativeWidget. + widget.reset(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); + + // TODO(beng): write test for this ownership scenario and the NativeWidget + // being deleted out from under the Widget. +} + +// Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget. +TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { + OwnershipTestState state; + + scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + // Now delete the Widget, which should delete the NativeWidget. + widget.reset(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); + + // TODO(beng): write test for this ownership scenario and the NativeWidget + // being deleted out from under the Widget. +} + +// Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget, +// destroy the parent view. +TEST_F(WidgetOwnershipTest, + Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) { + OwnershipTestState state; + + Widget* toplevel = CreateTopLevelPlatformWidget(); + + scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + params.parent = toplevel->GetNativeView(); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + // Now close the toplevel, which deletes the view hierarchy. + toplevel->CloseNow(); + + RunPendingMessages(); + + // This shouldn't delete the widget because it shouldn't be deleted + // from the native side. + EXPECT_FALSE(state.widget_deleted); + EXPECT_FALSE(state.native_widget_deleted); + + // Now delete it explicitly. + widget.reset(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// NativeWidget owns its Widget, part 1: NativeWidget is a platform-native +// widget. +TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { + OwnershipTestState state; + + Widget* widget = new OwnershipTestWidget(&state); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget, &state); + widget->Init(params); + + // Now destroy the native widget. + widget->CloseNow(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget. +TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { + OwnershipTestState state; + + Widget* toplevel = CreateTopLevelPlatformWidget(); + + Widget* widget = new OwnershipTestWidget(&state); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget, &state); + params.parent = toplevel->GetNativeView(); + widget->Init(params); + + // Now destroy the native widget. This is achieved by closing the toplevel. + toplevel->CloseNow(); + + // The NativeWidget won't be deleted until after a return to the message loop + // so we have to run pending messages before testing the destruction status. + RunPendingMessages(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// NativeWidget owns its Widget, part 3: NativeWidget is a platform-native +// widget, destroyed out from under it by the OS. +TEST_F(WidgetOwnershipTest, + Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) { + OwnershipTestState state; + + Widget* widget = new OwnershipTestWidget(&state); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget, &state); + widget->Init(params); + + // Now simulate a destroy of the platform native widget from the OS: +#if defined(USE_AURA) + delete widget->GetNativeView(); +#elif defined(OS_WIN) + DestroyWindow(widget->GetNativeView()); +#endif + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget, +// destroyed by the view hierarchy that contains it. +TEST_F(WidgetOwnershipTest, + Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) { + OwnershipTestState state; + + Widget* toplevel = CreateTopLevelPlatformWidget(); + + Widget* widget = new OwnershipTestWidget(&state); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget, &state); + params.parent = toplevel->GetNativeView(); + widget->Init(params); + + // Destroy the widget (achieved by closing the toplevel). + toplevel->CloseNow(); + + // The NativeWidget won't be deleted until after a return to the message loop + // so we have to run pending messages before testing the destruction status. + RunPendingMessages(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget, +// we close it directly. +TEST_F(WidgetOwnershipTest, + Ownership_ViewsNativeWidgetOwnsWidget_Close) { + OwnershipTestState state; + + Widget* toplevel = CreateTopLevelPlatformWidget(); + + Widget* widget = new OwnershipTestWidget(&state); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget, &state); + params.parent = toplevel->GetNativeView(); + widget->Init(params); + + // Destroy the widget. + widget->Close(); + toplevel->CloseNow(); + + // The NativeWidget won't be deleted until after a return to the message loop + // so we have to run pending messages before testing the destruction status. + RunPendingMessages(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +// Widget owns its NativeWidget and has a WidgetDelegateView as its contents. +TEST_F(WidgetOwnershipTest, + Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) { + OwnershipTestState state; + + WidgetDelegateView* delegate_view = new WidgetDelegateView; + + scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = + new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = delegate_view; + widget->Init(params); + widget->SetContentsView(delegate_view); + + // Now delete the Widget. There should be no crash or use-after-free. + widget.reset(); + + EXPECT_TRUE(state.widget_deleted); + EXPECT_TRUE(state.native_widget_deleted); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget observer tests. +// + +class WidgetObserverTest : public WidgetTest, public WidgetObserver { + public: + WidgetObserverTest() + : active_(NULL), + widget_closed_(NULL), + widget_activated_(NULL), + widget_shown_(NULL), + widget_hidden_(NULL), + widget_bounds_changed_(NULL) { + } + + virtual ~WidgetObserverTest() {} + + // Overridden from WidgetObserver: + virtual void OnWidgetDestroying(Widget* widget) OVERRIDE { + if (active_ == widget) + active_ = NULL; + widget_closed_ = widget; + } + + virtual void OnWidgetActivationChanged(Widget* widget, + bool active) OVERRIDE { + if (active) { + if (widget_activated_) + widget_activated_->Deactivate(); + widget_activated_ = widget; + active_ = widget; + } else { + if (widget_activated_ == widget) + widget_activated_ = NULL; + widget_deactivated_ = widget; + } + } + + virtual void OnWidgetVisibilityChanged(Widget* widget, + bool visible) OVERRIDE { + if (visible) + widget_shown_ = widget; + else + widget_hidden_ = widget; + } + + virtual void OnWidgetBoundsChanged(Widget* widget, + const gfx::Rect& new_bounds) OVERRIDE { + widget_bounds_changed_ = widget; + } + + void reset() { + active_ = NULL; + widget_closed_ = NULL; + widget_activated_ = NULL; + widget_deactivated_ = NULL; + widget_shown_ = NULL; + widget_hidden_ = NULL; + widget_bounds_changed_ = NULL; + } + + Widget* NewWidget() { + Widget* widget = CreateTopLevelNativeWidget(); + widget->AddObserver(this); + return widget; + } + + const Widget* active() const { return active_; } + const Widget* widget_closed() const { return widget_closed_; } + const Widget* widget_activated() const { return widget_activated_; } + const Widget* widget_deactivated() const { return widget_deactivated_; } + const Widget* widget_shown() const { return widget_shown_; } + const Widget* widget_hidden() const { return widget_hidden_; } + const Widget* widget_bounds_changed() const { return widget_bounds_changed_; } + + private: + Widget* active_; + + Widget* widget_closed_; + Widget* widget_activated_; + Widget* widget_deactivated_; + Widget* widget_shown_; + Widget* widget_hidden_; + Widget* widget_bounds_changed_; +}; + +TEST_F(WidgetObserverTest, DISABLED_ActivationChange) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + + Widget* toplevel1 = NewWidget(); + Widget* toplevel2 = NewWidget(); + + toplevel1->Show(); + toplevel2->Show(); + + reset(); + + toplevel1->Activate(); + + RunPendingMessages(); + EXPECT_EQ(toplevel1, widget_activated()); + + toplevel2->Activate(); + RunPendingMessages(); + EXPECT_EQ(toplevel1, widget_deactivated()); + EXPECT_EQ(toplevel2, widget_activated()); + EXPECT_EQ(toplevel2, active()); + + toplevel->CloseNow(); +} + +TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + + Widget* child1 = NewWidget(); + Widget* child2 = NewWidget(); + + toplevel->Show(); + child1->Show(); + child2->Show(); + + reset(); + + child1->Hide(); + EXPECT_EQ(child1, widget_hidden()); + + child2->Hide(); + EXPECT_EQ(child2, widget_hidden()); + + child1->Show(); + EXPECT_EQ(child1, widget_shown()); + + child2->Show(); + EXPECT_EQ(child2, widget_shown()); + + toplevel->CloseNow(); +} + +TEST_F(WidgetObserverTest, DestroyBubble) { + Widget* anchor = CreateTopLevelPlatformWidget(); + anchor->Show(); + + BubbleDelegateView* bubble_delegate = + new BubbleDelegateView(anchor->client_view(), BubbleBorder::NONE); + Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); + bubble_widget->Show(); + bubble_widget->CloseNow(); + + anchor->Hide(); + anchor->CloseNow(); +} + +TEST_F(WidgetObserverTest, WidgetBoundsChanged) { + Widget* child1 = NewWidget(); + Widget* child2 = NewWidget(); + + child1->OnNativeWidgetMove(); + EXPECT_EQ(child1, widget_bounds_changed()); + + child2->OnNativeWidgetMove(); + EXPECT_EQ(child2, widget_bounds_changed()); + + child1->OnNativeWidgetSizeChanged(gfx::Size()); + EXPECT_EQ(child1, widget_bounds_changed()); + + child2->OnNativeWidgetSizeChanged(gfx::Size()); + EXPECT_EQ(child2, widget_bounds_changed()); +} + +#if !defined(USE_AURA) && defined(OS_WIN) +// Aura needs shell to maximize/fullscreen window. +// NativeWidgetGtk doesn't implement GetRestoredBounds. +TEST_F(WidgetTest, GetRestoredBounds) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), + toplevel->GetRestoredBounds().ToString()); + toplevel->Show(); + toplevel->Maximize(); + RunPendingMessages(); + EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), + toplevel->GetRestoredBounds().ToString()); + EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); + EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); + + toplevel->Restore(); + RunPendingMessages(); + EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), + toplevel->GetRestoredBounds().ToString()); + + toplevel->SetFullscreen(true); + RunPendingMessages(); + EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), + toplevel->GetRestoredBounds().ToString()); + EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); + EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); +} +#endif + +// Test that window state is not changed after getting out of full screen. +TEST_F(WidgetTest, ExitFullscreenRestoreState) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + + toplevel->Show(); + RunPendingMessages(); + + // This should be a normal state window. + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); + + toplevel->SetFullscreen(true); + while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) + RunPendingMessages(); + toplevel->SetFullscreen(false); + while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) + RunPendingMessages(); + + // And it should still be in normal state after getting out of full screen. + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); + + // Now, make it maximized. + toplevel->Maximize(); + while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_MAXIMIZED) + RunPendingMessages(); + + toplevel->SetFullscreen(true); + while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) + RunPendingMessages(); + toplevel->SetFullscreen(false); + while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) + RunPendingMessages(); + + // And it stays maximized after getting out of full screen. + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); + + // Clean up. + toplevel->Close(); + RunPendingMessages(); +} + +// Checks that if a mouse-press triggers a capture on a different widget (which +// consumes the mouse-release event), then the target of the press does not have +// capture. +TEST_F(WidgetTest, CaptureWidgetFromMousePress) { + // The test creates two widgets: |first| and |second|. + // The View in |first| makes |second| visible, sets capture on it, and starts + // a nested loop (like a menu does). The View in |second| terminates the + // nested loop and closes the widget. + // The test sends a mouse-press event to |first|, and posts a task to send a + // release event to |second|, to make sure that the release event is + // dispatched after the nested loop starts. + + Widget* first = CreateTopLevelFramelessPlatformWidget(); + Widget* second = CreateTopLevelFramelessPlatformWidget(); + + View* container = new NestedLoopCaptureView(second); + first->SetContentsView(container); + + second->SetContentsView(new ExitLoopOnRelease()); + + first->SetSize(gfx::Size(100, 100)); + first->Show(); + + gfx::Point location(20, 20); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&Widget::OnMouseEvent, + base::Unretained(second), + base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, + location, + location, + ui::EF_LEFT_MOUSE_BUTTON)))); + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, + ui::EF_LEFT_MOUSE_BUTTON); + first->OnMouseEvent(&press); + EXPECT_FALSE(first->HasCapture()); + first->Close(); + RunPendingMessages(); +} + +TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { + Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); + View* container = new View; + toplevel->SetContentsView(container); + + View* gesture = new GestureCaptureView; + gesture->SetBounds(0, 0, 30, 30); + container->AddChildView(gesture); + + MouseView* mouse = new MouseView; + mouse->SetBounds(30, 0, 30, 30); + container->AddChildView(mouse); + + toplevel->SetSize(gfx::Size(100, 100)); + toplevel->Show(); + + // Start a gesture on |gesture|. + ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, + 15, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); + ui::GestureEvent end(ui::ET_GESTURE_END, + 15, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); + toplevel->OnGestureEvent(&begin); + + // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| + // will not receive the event. + gfx::Point click_location(45, 15); + + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, + ui::EF_LEFT_MOUSE_BUTTON); + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, + ui::EF_LEFT_MOUSE_BUTTON); + + toplevel->OnMouseEvent(&press); + toplevel->OnMouseEvent(&release); + EXPECT_EQ(0, mouse->pressed()); + + // The end of the gesture should release the capture, and pressing on |mouse| + // should now reach |mouse|. + toplevel->OnGestureEvent(&end); + toplevel->OnMouseEvent(&press); + toplevel->OnMouseEvent(&release); + EXPECT_EQ(1, mouse->pressed()); + + toplevel->Close(); + RunPendingMessages(); +} + +#if defined(USE_AURA) +// The key-event propagation from Widget happens differently on aura and +// non-aura systems because of the difference in IME. So this test works only on +// aura. +TEST_F(WidgetTest, KeyboardInputEvent) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + View* container = toplevel->client_view(); + + Textfield* textfield = new Textfield(); + textfield->SetText(ASCIIToUTF16("some text")); + container->AddChildView(textfield); + toplevel->Show(); + textfield->RequestFocus(); + + // The press gets handled. The release doesn't have an effect. + ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, 0, false); + toplevel->OnKeyEvent(&backspace_p); + EXPECT_TRUE(backspace_p.stopped_propagation()); + ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, 0, false); + toplevel->OnKeyEvent(&backspace_r); + EXPECT_FALSE(backspace_r.handled()); + + toplevel->Close(); +} + +// Verifies bubbles result in a focus lost when shown. +// TODO(msw): this tests relies on focus, it needs to be in +// interactive_ui_tests. +TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { + // Create a widget, show and activate it and focus the contents view. + View* contents_view = new View; + contents_view->set_focusable(true); + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.bounds = gfx::Rect(0, 0, 200, 200); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; +#if !defined(OS_CHROMEOS) + init_params.native_widget = new DesktopNativeWidgetAura(&widget); +#endif + widget.Init(init_params); + widget.SetContentsView(contents_view); + widget.Show(); + widget.Activate(); + contents_view->RequestFocus(); + EXPECT_TRUE(contents_view->HasFocus()); + + // Show a bubble. + BubbleDelegateView* bubble_delegate_view = + new BubbleDelegateView(contents_view, BubbleBorder::TOP_LEFT); + bubble_delegate_view->set_focusable(true); + BubbleDelegateView::CreateBubble(bubble_delegate_view)->Show(); + bubble_delegate_view->RequestFocus(); + + // |contents_view_| should no longer have focus. + EXPECT_FALSE(contents_view->HasFocus()); + EXPECT_TRUE(bubble_delegate_view->HasFocus()); + + bubble_delegate_view->GetWidget()->CloseNow(); + + // Closing the bubble should result in focus going back to the contents view. + EXPECT_TRUE(contents_view->HasFocus()); +} + +// Desktop native widget Aura tests are for non Chrome OS platforms. +#if !defined(OS_CHROMEOS) +// Test to ensure that after minimize, view width is set to zero. +TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { + // Create a widget. + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.show_state = ui::SHOW_STATE_NORMAL; + gfx::Rect initial_bounds(0, 0, 300, 400); + init_params.bounds = initial_bounds; + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + NonClientView* non_client_view = widget.non_client_view(); + NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); + non_client_view->SetFrameView(frame_view); + widget.Show(); + widget.Minimize(); + EXPECT_EQ(0, frame_view->width()); +} + +// This class validates whether paints are received for a visible Widget. +// To achieve this it overrides the Show and Close methods on the Widget class +// and sets state whether subsequent paints are expected. +class DesktopAuraTestValidPaintWidget : public views::Widget { + public: + DesktopAuraTestValidPaintWidget() + : expect_paint_(true), + received_paint_while_hidden_(false) { + } + + virtual ~DesktopAuraTestValidPaintWidget() { + } + + virtual void Show() OVERRIDE { + expect_paint_ = true; + views::Widget::Show(); + } + + virtual void Close() OVERRIDE { + expect_paint_ = false; + views::Widget::Close(); + } + + void Hide() { + expect_paint_ = false; + views::Widget::Hide(); + } + + virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE { + EXPECT_TRUE(expect_paint_); + if (!expect_paint_) + received_paint_while_hidden_ = true; + views::Widget::OnNativeWidgetPaint(canvas); + } + + bool received_paint_while_hidden() const { + return received_paint_while_hidden_; + } + + private: + bool expect_paint_; + bool received_paint_while_hidden_; +}; + +TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) { + View* contents_view = new View; + contents_view->set_focusable(true); + DesktopAuraTestValidPaintWidget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.bounds = gfx::Rect(0, 0, 200, 200); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + widget.SetContentsView(contents_view); + widget.Show(); + widget.Activate(); + RunPendingMessages(); + widget.SchedulePaintInRect(init_params.bounds); + widget.Close(); + RunPendingMessages(); + EXPECT_FALSE(widget.received_paint_while_hidden()); +} + +TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) { + View* contents_view = new View; + contents_view->set_focusable(true); + DesktopAuraTestValidPaintWidget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.bounds = gfx::Rect(0, 0, 200, 200); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + widget.SetContentsView(contents_view); + widget.Show(); + widget.Activate(); + RunPendingMessages(); + widget.SchedulePaintInRect(init_params.bounds); + widget.Hide(); + RunPendingMessages(); + EXPECT_FALSE(widget.received_paint_while_hidden()); + widget.Close(); +} + +// This class provides functionality to test whether the destruction of full +// screen child windows occurs correctly in desktop AURA without crashing. +// It provides facilities to test the following cases:- +// 1. Child window destroyed which should lead to the destruction of the +// parent. +// 2. Parent window destroyed which should lead to the child being destroyed. +class DesktopAuraFullscreenChildWindowDestructionTest + : public views::TestViewsDelegate, + public aura::WindowObserver { + public: + DesktopAuraFullscreenChildWindowDestructionTest() + : full_screen_widget_(NULL), + child_window_(NULL), + parent_destroyed_(false), + child_destroyed_(false) {} + + virtual ~DesktopAuraFullscreenChildWindowDestructionTest() { + EXPECT_TRUE(parent_destroyed_); + EXPECT_TRUE(child_destroyed_); + full_screen_widget_ = NULL; + child_window_ = NULL; + } + + // views::TestViewsDelegate overrides. + virtual void OnBeforeWidgetInit( + Widget::InitParams* params, + internal::NativeWidgetDelegate* delegate) OVERRIDE { + if (!params->native_widget) + params->native_widget = new views::DesktopNativeWidgetAura(delegate); + } + + void CreateFullscreenChildWindow(const gfx::Rect& bounds) { + Widget::InitParams init_params; + init_params.type = Widget::InitParams::TYPE_WINDOW; + init_params.bounds = bounds; + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.layer_type = ui::LAYER_NOT_DRAWN; + + widget_.Init(init_params); + + child_window_ = new aura::Window(&child_window_delegate_); + child_window_->SetType(aura::client::WINDOW_TYPE_NORMAL); + child_window_->Init(ui::LAYER_TEXTURED); + child_window_->SetName("TestFullscreenChildWindow"); + child_window_->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_FULLSCREEN); + child_window_->SetDefaultParentByRootWindow( + widget_.GetNativeView()->GetRootWindow(), gfx::Rect(0, 0, 1900, 1600)); + child_window_->Show(); + child_window_->AddObserver(this); + + ASSERT_TRUE(child_window_->parent() != NULL); + child_window_->parent()->AddObserver(this); + + full_screen_widget_ = + views::Widget::GetWidgetForNativeView(child_window_->parent()); + ASSERT_TRUE(full_screen_widget_ != NULL); + } + + void DestroyChildWindow() { + ASSERT_TRUE(child_window_ != NULL); + delete child_window_; + } + + void DestroyParentWindow() { + ASSERT_TRUE(full_screen_widget_ != NULL); + full_screen_widget_->CloseNow(); + } + + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { + window->RemoveObserver(this); + if (window == child_window_) { + child_destroyed_ = true; + } else if (window == full_screen_widget_->GetNativeView()) { + parent_destroyed_ = true; + } else { + ADD_FAILURE() << "Unexpected window destroyed callback: " << window; + } + } + + private: + views::Widget widget_; + views::Widget* full_screen_widget_; + aura::Window* child_window_; + bool parent_destroyed_; + bool child_destroyed_; + aura::test::TestWindowDelegate child_window_delegate_; + + DISALLOW_COPY_AND_ASSIGN(DesktopAuraFullscreenChildWindowDestructionTest); +}; + +TEST_F(WidgetTest, DesktopAuraFullscreenChildDestroyedBeforeParentTest) { + ViewsDelegate::views_delegate = NULL; + DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test; + ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow( + gfx::Rect(0, 0, 200, 200))); + + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyChildWindow()); + RunPendingMessages(); +} + +TEST_F(WidgetTest, DesktopAuraFullscreenChildParentDestroyed) { + ViewsDelegate::views_delegate = NULL; + + DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test; + ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow( + gfx::Rect(0, 0, 200, 200))); + + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyParentWindow()); + RunPendingMessages(); +} + +// Test to ensure that the aura Window's visiblity state is set to visible if +// the underlying widget is hidden and then shown. +TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { + // Create a widget. + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.show_state = ui::SHOW_STATE_NORMAL; + gfx::Rect initial_bounds(0, 0, 300, 400); + init_params.bounds = initial_bounds; + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + NonClientView* non_client_view = widget.non_client_view(); + NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); + non_client_view->SetFrameView(frame_view); + + widget.Hide(); + EXPECT_FALSE(widget.GetNativeView()->IsVisible()); + widget.Show(); + EXPECT_TRUE(widget.GetNativeView()->IsVisible()); +} + +#endif // !defined(OS_CHROMEOS) + +// Tests that wheel events generated from scroll events are targetted to the +// views under the cursor when the focused view does not processed them. +TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) { + EventCountView* cursor_view = new EventCountView; + cursor_view->SetBounds(60, 0, 50, 40); + + Widget* widget = CreateTopLevelPlatformWidget(); + widget->GetRootView()->AddChildView(cursor_view); + + // Generate a scroll event on the cursor view. + ui::ScrollEvent scroll(ui::ET_SCROLL, + gfx::Point(65, 5), + ui::EventTimeForNow(), + 0, + 0, 20, + 0, 20, + 2); + widget->OnScrollEvent(&scroll); + + EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); + + cursor_view->ResetCounts(); + + ui::ScrollEvent scroll2(ui::ET_SCROLL, + gfx::Point(5, 5), + ui::EventTimeForNow(), + 0, + 0, 20, + 0, 20, + 2); + widget->OnScrollEvent(&scroll2); + + EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); + + widget->CloseNow(); +} + +#endif // defined(USE_AURA) + +// Tests that if a scroll-begin gesture is not handled, then subsequent scroll +// events are not dispatched to any view. +TEST_F(WidgetTest, GestureScrollEventDispatching) { + EventCountView* noscroll_view = new EventCountView; + EventCountView* scroll_view = new ScrollableEventCountView; + + noscroll_view->SetBounds(0, 0, 50, 40); + scroll_view->SetBounds(60, 0, 40, 40); + + Widget* widget = CreateTopLevelPlatformWidget(); + widget->GetRootView()->AddChildView(noscroll_view); + widget->GetRootView()->AddChildView(scroll_view); + + { + ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, + 5, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), + 1); + widget->OnGestureEvent(&begin); + ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, + 25, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), + 1); + widget->OnGestureEvent(&update); + ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, + 25, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), + 1); + widget->OnGestureEvent(&end); + + EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); + EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); + EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); + } + + { + ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN, + 65, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), + 1); + widget->OnGestureEvent(&begin); + ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE, + 85, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), + 1); + widget->OnGestureEvent(&update); + ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END, + 85, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), + 1); + widget->OnGestureEvent(&end); + + EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); + EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); + EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); + } + + widget->CloseNow(); +} + +// Tests that event-handlers installed on the RootView get triggered correctly. +TEST_F(WidgetTest, EventHandlersOnRootView) { + Widget* widget = CreateTopLevelNativeWidget(); + View* root_view = widget->GetRootView(); + + EventCountView* view = new EventCountView; + view->SetBounds(0, 0, 20, 20); + root_view->AddChildView(view); + + EventCountHandler h1; + root_view->AddPreTargetHandler(&h1); + + EventCountHandler h2; + root_view->AddPostTargetHandler(&h2); + + widget->SetBounds(gfx::Rect(0, 0, 100, 100)); + widget->Show(); + + ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED, + gfx::Point(10, 10), + 0, 0, + ui::EventTimeForNow(), + 1.0, 0.0, 1.0, 0.0); + widget->OnTouchEvent(&pressed); + EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_PRESSED)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_PRESSED)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_PRESSED)); + + ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, + 5, 5, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); + ui::GestureEvent end(ui::ET_GESTURE_END, + 5, 5, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); + widget->OnGestureEvent(&begin); + EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_BEGIN)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_BEGIN)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_BEGIN)); + + ui::TouchEvent released(ui::ET_TOUCH_RELEASED, + gfx::Point(10, 10), + 0, 0, + ui::EventTimeForNow(), + 1.0, 0.0, 1.0, 0.0); + widget->OnTouchEvent(&released); + EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_RELEASED)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_RELEASED)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_RELEASED)); + + widget->OnGestureEvent(&end); + EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_END)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_END)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_END)); + + ui::ScrollEvent scroll(ui::ET_SCROLL, + gfx::Point(5, 5), + ui::EventTimeForNow(), + 0, + 0, 20, + 0, 20, + 2); + widget->OnScrollEvent(&scroll); + EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_SCROLL)); + + widget->CloseNow(); +} + +TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { + Widget* widget = CreateTopLevelNativeWidget(); + View* root_view = widget->GetRootView(); + + EventCountView* v1 = new EventCountView(); + v1->SetBounds(0, 0, 10, 10); + root_view->AddChildView(v1); + EventCountView* v2 = new EventCountView(); + v2->SetBounds(0, 10, 10, 10); + root_view->AddChildView(v2); + + gfx::Point cursor_location(5, 5); + ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, + ui::EF_NONE); + widget->OnMouseEvent(&move); + + EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + + delete v1; + v2->SetBounds(0, 0, 10, 10); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + + widget->SynthesizeMouseMoveEvent(); + EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); +} + +TEST_F(WidgetTest, MouseEventsHandled) { + Widget* widget = CreateTopLevelNativeWidget(); + View* root_view = widget->GetRootView(); + +#if defined(USE_AURA) + aura::test::TestCursorClient cursor_client( + widget->GetNativeView()->GetRootWindow()); +#endif + + EventCountView* v1 = new EventCountView(); + v1->SetBounds(0, 0, 10, 10); + root_view->AddChildView(v1); + EventCountView* v2 = new EventCountView(); + v2->SetBounds(0, 10, 10, 10); + root_view->AddChildView(v2); + + gfx::Point cursor_location1(5, 5); + ui::MouseEvent move1(ui::ET_MOUSE_MOVED, cursor_location1, cursor_location1, + ui::EF_NONE); + widget->OnMouseEvent(&move1); + EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); + v1->ResetCounts(); + v2->ResetCounts(); + + gfx::Point cursor_location2(5, 15); + ui::MouseEvent move2(ui::ET_MOUSE_MOVED, cursor_location2, cursor_location2, + ui::EF_NONE); + widget->OnMouseEvent(&move2); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_EXITED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); + v1->ResetCounts(); + v2->ResetCounts(); + +#if defined(USE_AURA) + // In Aura, we suppress mouse events if mouse events are disabled. + cursor_client.DisableMouseEvents(); + + widget->OnMouseEvent(&move1); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); + v1->ResetCounts(); + v2->ResetCounts(); + + widget->OnMouseEvent(&move2); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_EXITED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_EXITED)); +#endif +} + +// Used by SingleWindowClosing to count number of times WindowClosing() has +// been invoked. +class ClosingDelegate : public WidgetDelegate { + public: + ClosingDelegate() : count_(0), widget_(NULL) {} + + int count() const { return count_; } + + void set_widget(views::Widget* widget) { widget_ = widget; } + + // WidgetDelegate overrides: + virtual Widget* GetWidget() OVERRIDE { return widget_; } + virtual const Widget* GetWidget() const OVERRIDE { return widget_; } + virtual void WindowClosing() OVERRIDE { + count_++; + } + + private: + int count_; + views::Widget* widget_; + + DISALLOW_COPY_AND_ASSIGN(ClosingDelegate); +}; + +// Verifies WindowClosing() is invoked correctly on the delegate when a Widget +// is closed. +TEST_F(WidgetTest, SingleWindowClosing) { + scoped_ptr<ClosingDelegate> delegate(new ClosingDelegate()); + Widget* widget = new Widget(); // Destroyed by CloseNow() below. + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.bounds = gfx::Rect(0, 0, 200, 200); + init_params.delegate = delegate.get(); +#if defined(USE_AURA) && !defined(OS_CHROMEOS) + init_params.native_widget = new DesktopNativeWidgetAura(widget); +#endif + widget->Init(init_params); + EXPECT_EQ(0, delegate->count()); + widget->CloseNow(); + EXPECT_EQ(1, delegate->count()); +} + +// Used by SetTopLevelCorrectly to track calls to OnBeforeWidgetInit(). +class VerifyTopLevelDelegate : public TestViewsDelegate { + public: + VerifyTopLevelDelegate() + : on_before_init_called_(false), + is_top_level_(false) { + } + + bool on_before_init_called() const { return on_before_init_called_; } + bool is_top_level() const { return is_top_level_; } + + virtual void OnBeforeWidgetInit( + Widget::InitParams* params, + internal::NativeWidgetDelegate* delegate) OVERRIDE { + on_before_init_called_ = true; + is_top_level_ = params->top_level; + } + + private: + bool on_before_init_called_; + bool is_top_level_; + + DISALLOW_COPY_AND_ASSIGN(VerifyTopLevelDelegate); +}; + +// Verifies |top_level| is correctly passed to +// ViewsDelegate::OnBeforeWidgetInit(). +TEST_F(WidgetTest, SetTopLevelCorrectly) { + set_views_delegate(NULL); + VerifyTopLevelDelegate* delegate = new VerifyTopLevelDelegate; + set_views_delegate(delegate); // ViewsTestBase takes ownership. + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + EXPECT_TRUE(delegate->on_before_init_called()); + EXPECT_TRUE(delegate->is_top_level()); +} + +// A scumbag View that deletes its owning widget OnMousePressed. +class WidgetDeleterView : public View { + public: + WidgetDeleterView() : View() {} + + // Overridden from View. + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { + delete GetWidget(); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(WidgetDeleterView); +}; + +TEST_F(WidgetTest, TestWidgetDeletedInOnMousePressed) { + Widget* widget = new Widget; + Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + widget->SetContentsView(new WidgetDeleterView); + + widget->SetSize(gfx::Size(100, 100)); + widget->Show(); + + gfx::Point click_location(45, 15); + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, + ui::EF_LEFT_MOUSE_BUTTON); + widget->OnMouseEvent(&press); + + // Yay we did not crash! +} + +// See description of RunGetNativeThemeFromDestructor() for details. +class GetNativeThemeFromDestructorView : public WidgetDelegateView { + public: + GetNativeThemeFromDestructorView() {} + virtual ~GetNativeThemeFromDestructorView() { + VerifyNativeTheme(); + } + + virtual View* GetContentsView() OVERRIDE { + return this; + } + + private: + void VerifyNativeTheme() { + ASSERT_TRUE(GetNativeTheme() != NULL); + } + + DISALLOW_COPY_AND_ASSIGN(GetNativeThemeFromDestructorView); +}; + +// Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't +// crash. |is_first_run| is true if this is the first call. A return value of +// true indicates this should be run again with a value of false. +// First run uses DesktopNativeWidgetAura (if possible). Second run doesn't. +bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, + bool is_first_run) { + bool needs_second_run = false; + // Destroyed by CloseNow() below. + Widget* widget = new Widget; + Widget::InitParams params(in_params); + // Deletes itself when the Widget is destroyed. + params.delegate = new GetNativeThemeFromDestructorView; +#if defined(USE_AURA) && !defined(OS_CHROMEOS) + if (is_first_run) { + params.native_widget = new DesktopNativeWidgetAura(widget); + needs_second_run = true; + } +#endif + widget->Init(params); + widget->CloseNow(); + return needs_second_run; +} + +// See description of RunGetNativeThemeFromDestructor() for details. +TEST_F(WidgetTest, GetNativeThemeFromDestructor) { + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + if (RunGetNativeThemeFromDestructor(params, true)) + RunGetNativeThemeFromDestructor(params, false); +} + +// Used by HideCloseDestroy. Allows setting a boolean when the widget is +// destroyed. +class CloseDestroysWidget : public Widget { + public: + explicit CloseDestroysWidget(bool* destroyed) + : destroyed_(destroyed) { + } + + virtual ~CloseDestroysWidget() { + if (destroyed_) { + *destroyed_ = true; + base::MessageLoop::current()->QuitNow(); + } + } + + void Detach() { destroyed_ = NULL; } + + private: + // If non-null set to true from destructor. + bool* destroyed_; + + DISALLOW_COPY_AND_ASSIGN(CloseDestroysWidget); +}; + +// Verifies Close() results in destroying. +TEST_F(WidgetTest, CloseDestroys) { + bool destroyed = false; + CloseDestroysWidget* widget = new CloseDestroysWidget(&destroyed); + Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_MENU); + params.opacity = Widget::InitParams::OPAQUE_WINDOW; +#if defined(USE_AURA) && !defined(OS_CHROMEOS) + params.native_widget = new DesktopNativeWidgetAura(widget); +#endif + widget->Init(params); + widget->Show(); + widget->Hide(); + widget->Close(); + // Run the message loop as Close() asynchronously deletes. + RunPendingMessages(); + EXPECT_TRUE(destroyed); + // Close() should destroy the widget. If not we'll cleanup to avoid leaks. + if (!destroyed) { + widget->Detach(); + widget->CloseNow(); + } +} + +// A view that consumes mouse-pressed event and gesture-tap-down events. +class RootViewTestView : public View { + public: + RootViewTestView(): View() {} + + private: + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { + return true; + } + + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + if (event->type() == ui::ET_GESTURE_TAP_DOWN) + event->SetHandled(); + } +}; + +// Checks if RootView::*_handler_ fields are unset when widget is hidden. +TEST_F(WidgetTest, TestRootViewHandlersWhenHidden) { + Widget* widget = CreateTopLevelNativeWidget(); + widget->SetBounds(gfx::Rect(0, 0, 300, 300)); + View* view = new RootViewTestView(); + view->SetBounds(0, 0, 300, 300); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget->GetRootView()); + root_view->AddChildView(view); + + // Check RootView::mouse_pressed_handler_. + widget->Show(); + EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); + gfx::Point click_location(45, 15); + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, + ui::EF_LEFT_MOUSE_BUTTON); + widget->OnMouseEvent(&press); + EXPECT_EQ(view, GetMousePressedHandler(root_view)); + widget->Hide(); + EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); + + // Check RootView::mouse_move_handler_. + widget->Show(); + EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); + gfx::Point move_location(45, 15); + ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0); + widget->OnMouseEvent(&move); + EXPECT_EQ(view, GetMouseMoveHandler(root_view)); + widget->Hide(); + EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); + + // Check RootView::gesture_handler_. + widget->Show(); + EXPECT_EQ(NULL, GetGestureHandler(root_view)); + ui::GestureEvent tap_down( + ui::ET_GESTURE_TAP_DOWN, + 15, + 15, + 0, + base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), + 1); + widget->OnGestureEvent(&tap_down); + EXPECT_EQ(view, GetGestureHandler(root_view)); + widget->Hide(); + EXPECT_EQ(NULL, GetGestureHandler(root_view)); + + widget->Close(); +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/widget/window_reorderer.cc b/chromium/ui/views/widget/window_reorderer.cc new file mode 100644 index 00000000000..dcf51c83c8a --- /dev/null +++ b/chromium/ui/views/widget/window_reorderer.cc @@ -0,0 +1,201 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/window_reorderer.h" + +#include <map> +#include <vector> + +#include "ui/aura/window.h" +#include "ui/views/view.h" +#include "ui/views/view_constants_aura.h" + +namespace views { + +namespace { + +// Sets |hosted_windows| to a mapping of the views with an associated window to +// the window that they are associated to. Only views associated to a child of +// |parent_window| are returned. +void GetViewsWithAssociatedWindow( + const aura::Window& parent_window, + std::map<views::View*, aura::Window*>* hosted_windows) { + const std::vector<aura::Window*>& child_windows = parent_window.children(); + for (size_t i = 0; i < child_windows.size(); ++i) { + aura::Window* child = child_windows[i]; + View* host_view = child->GetProperty(kHostViewKey); + if (host_view) + (*hosted_windows)[host_view] = child; + } +} + +// Sets |order| to the list of views whose layer / associated window's layer +// is a child of |parent_layer|. |order| is sorted in ascending z-order of +// the views. +// |hosts| are the views with an associated window whose layer is a child of +// |parent_layer|. +void GetOrderOfViewsWithLayers( + views::View* view, + ui::Layer* parent_layer, + const std::map<views::View*, aura::Window*>& hosts, + std::vector<views::View*>* order) { + DCHECK(view); + DCHECK(parent_layer); + DCHECK(order); + if (view->layer() && view->layer()->parent() == parent_layer) { + order->push_back(view); + // |hosts| may contain a child of |view|. + } else if (hosts.find(view) != hosts.end()) { + order->push_back(view); + } + + for (int i = 0; i < view->child_count(); ++i) + GetOrderOfViewsWithLayers(view->child_at(i), parent_layer, hosts, order); +} + +} // namespace + +// Class which reorders windows as a result of the kHostViewKey property being +// set on the window. +class WindowReorderer::AssociationObserver : public aura::WindowObserver { + public: + explicit AssociationObserver(WindowReorderer* reorderer); + virtual ~AssociationObserver(); + + // Start/stop observing changes in the kHostViewKey property on |window|. + void StartObserving(aura::Window* window); + void StopObserving(aura::Window* window); + + private: + // aura::WindowObserver overrides: + virtual void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // Not owned. + WindowReorderer* reorderer_; + + std::set<aura::Window*> windows_; + + DISALLOW_COPY_AND_ASSIGN(AssociationObserver); +}; + +WindowReorderer::AssociationObserver::AssociationObserver( + WindowReorderer* reorderer) + : reorderer_(reorderer) { +} + +WindowReorderer::AssociationObserver::~AssociationObserver() { + while (!windows_.empty()) + StopObserving(*windows_.begin()); +} + +void WindowReorderer::AssociationObserver::StartObserving( + aura::Window* window) { + windows_.insert(window); + window->AddObserver(this); +} + +void WindowReorderer::AssociationObserver::StopObserving( + aura::Window* window) { + windows_.erase(window); + window->RemoveObserver(this); +} + +void WindowReorderer::AssociationObserver::OnWindowPropertyChanged( + aura::Window* window, + const void* key, + intptr_t old) { + if (key == kHostViewKey) + reorderer_->ReorderChildWindows(); +} + +void WindowReorderer::AssociationObserver::OnWindowDestroying( + aura::Window* window) { + windows_.erase(window); + window->RemoveObserver(this); +} + +WindowReorderer::WindowReorderer(aura::Window* parent_window, + View* root_view) + : parent_window_(parent_window), + root_view_(root_view), + association_observer_(new AssociationObserver(this)) { + parent_window_->AddObserver(this); + const std::vector<aura::Window*>& windows = parent_window_->children(); + for (size_t i = 0; i < windows.size(); ++i) + association_observer_->StartObserving(windows[i]); + ReorderChildWindows(); +} + +WindowReorderer::~WindowReorderer() { + if (parent_window_) { + parent_window_->RemoveObserver(this); + // |association_observer_| stops observing any windows it is observing upon + // destruction. + } +} + +void WindowReorderer::ReorderChildWindows() { + if (!parent_window_) + return; + + std::map<View*, aura::Window*> hosted_windows; + GetViewsWithAssociatedWindow(*parent_window_, &hosted_windows); + + if (hosted_windows.empty()) { + // Exit early if there are no views with associated windows. + // View::ReorderLayers() should have already reordered the layers owned by + // views. + return; + } + + // Compute the desired z-order of the layers based on the order of the views + // with layers and views with associated windows in the view tree. + std::vector<View*> view_with_layer_order; + GetOrderOfViewsWithLayers(root_view_, parent_window_->layer(), hosted_windows, + &view_with_layer_order); + + // For the sake of simplicity, reorder both the layers owned by views and the + // layers of windows associated with a view. Iterate through + // |view_with_layer_order| backwards and stack windows at the bottom so that + // windows not associated to a view are stacked above windows with an + // associated view. + for (std::vector<View*>::reverse_iterator it = view_with_layer_order.rbegin(); + it != view_with_layer_order.rend(); ++it) { + View* view = *it; + ui::Layer* layer = view->layer(); + aura::Window* window = NULL; + + std::map<View*, aura::Window*>::iterator hosted_window_it = + hosted_windows.find(view); + if (hosted_window_it != hosted_windows.end()) { + window = hosted_window_it->second; + layer = window->layer(); + } + + DCHECK(layer); + if (window) + parent_window_->StackChildAtBottom(window); + parent_window_->layer()->StackAtBottom(layer); + } +} + +void WindowReorderer::OnWindowAdded(aura::Window* new_window) { + association_observer_->StartObserving(new_window); + ReorderChildWindows(); +} + +void WindowReorderer::OnWillRemoveWindow(aura::Window* window) { + association_observer_->StopObserving(window); +} + +void WindowReorderer::OnWindowDestroying(aura::Window* window) { + parent_window_->RemoveObserver(this); + parent_window_ = NULL; + association_observer_.reset(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/window_reorderer.h b/chromium/ui/views/widget/window_reorderer.h new file mode 100644 index 00000000000..b04e48f8904 --- /dev/null +++ b/chromium/ui/views/widget/window_reorderer.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ +#define UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window_observer.h" + +namespace aura { +class Window; +} + +namespace views { +class View; + +// Class which reorders the widget's child windows which have an associated view +// in the widget's view tree according the z-order of the views in the view +// tree. Windows not associated to a view are stacked above windows with an +// associated view. The child windows' layers are additionally reordered +// according to the z-order of the associated views relative to views with +// layers. +class WindowReorderer : public aura::WindowObserver { + public: + WindowReorderer(aura::Window* window, View* root_view); + virtual ~WindowReorderer(); + + // Explicitly reorder the children of |window_| (and their layers). This + // method should be called when the position of a view with an associated + // window changes in the view hierarchy. This method assumes that the + // child layers of |window_| which are owned by views are already in the + // correct z-order relative to each other and does no reordering if there + // are no views with an associated window. + void ReorderChildWindows(); + + private: + // aura::WindowObserver overrides: + virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE; + virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // The window and the root view of the native widget which owns the + // WindowReorderer. + aura::Window* parent_window_; + View* root_view_; + + // Reorders windows as a result of the kHostViewKey being set on a child of + // |parent_window_|. + class AssociationObserver; + scoped_ptr<AssociationObserver> association_observer_; + + DISALLOW_COPY_AND_ASSIGN(WindowReorderer); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ diff --git a/chromium/ui/views/widget/window_reorderer_unittest.cc b/chromium/ui/views/widget/window_reorderer_unittest.cc new file mode 100644 index 00000000000..1d9566c916e --- /dev/null +++ b/chromium/ui/views/widget/window_reorderer_unittest.cc @@ -0,0 +1,263 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/aura/root_window.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/test_windows.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/test/test_layers.h" +#include "ui/views/view.h" +#include "ui/views/view_constants_aura.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace { + +// Creates a control widget with the passed in parameters. +// The caller takes ownership of the returned widget. +Widget* CreateControlWidget(aura::Window* parent, const gfx::Rect& bounds) { + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.parent = parent; + params.bounds = bounds; + Widget* widget = new Widget(); + widget->Init(params); + return widget; +} + +// Sets the name of |window| and |window|'s layer to |name|. +void SetWindowAndLayerName(aura::Window* window, const std::string& name) { + window->SetName(name); + window->layer()->set_name(name); +} + +// Returns a string containing the name of each of the child windows (bottommost +// first) of |parent|. The format of the string is "name1 name2 name3 ...". +std::string ChildWindowNamesAsString(const aura::Window& parent) { + std::string names; + typedef std::vector<aura::Window*> Windows; + for (Windows::const_iterator it = parent.children().begin(); + it != parent.children().end(); ++it) { + if (!names.empty()) + names += " "; + names += (*it)->name(); + } + return names; +} + +typedef aura::test::AuraTestBase WindowReordererTest; + +// Test that views with layers and views with associated windows are reordered +// according to the view hierarchy. +TEST_F(WindowReordererTest, Basic) { + scoped_ptr<Widget> parent(CreateControlWidget(root_window(), + gfx::Rect(0, 0, 100, 100))); + parent->Show(); + aura::Window* parent_window = parent->GetNativeWindow(); + + View* contents_view = new View(); + parent->SetContentsView(contents_view); + + // 1) Test that layers for views and layers for windows associated to a host + // view are stacked below the layers for any windows not associated to a host + // view. + View* v = new View(); + v->SetPaintToLayer(true); + v->layer()->set_name("v"); + contents_view->AddChildView(v); + + scoped_ptr<Widget> w1(CreateControlWidget(parent_window, + gfx::Rect(0, 1, 100, 101))); + SetWindowAndLayerName(w1->GetNativeView(), "w1"); + w1->Show(); + scoped_ptr<Widget> w2(CreateControlWidget(parent_window, + gfx::Rect(0, 2, 100, 102))); + SetWindowAndLayerName(w2->GetNativeView(), "w2"); + w2->Show(); + + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + View* host_view2 = new View(); + contents_view->AddChildView(host_view2); + w2->GetNativeView()->SetProperty(kHostViewKey, host_view2); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + View* host_view1 = new View(); + w1->GetNativeView()->SetProperty(kHostViewKey, host_view1); + contents_view->AddChildViewAt(host_view1, 0); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 v w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 2) Test the z-order of the windows and layers as a result of reordering the + // views. + contents_view->ReorderChildView(host_view1, -1); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + contents_view->ReorderChildView(host_view2, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 3) Test the z-order of the windows and layers as a result of reordering the + // views in situations where the window order remains unchanged. + contents_view->ReorderChildView(v, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 w2 v", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + contents_view->ReorderChildView(host_view2, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 v w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +// Test that different orderings of: +// - adding a window to a parent widget +// - adding a "host" view to a parent widget +// - associating the "host" view and window +// all correctly reorder the child windows and layers. +TEST_F(WindowReordererTest, Association) { + scoped_ptr<Widget> parent(CreateControlWidget(root_window(), + gfx::Rect(0, 0, 100, 100))); + parent->Show(); + aura::Window* parent_window = parent->GetNativeWindow(); + + View* contents_view = new View(); + parent->SetContentsView(contents_view); + + aura::Window* w1 = aura::test::CreateTestWindowWithId(0, + parent->GetNativeWindow()); + SetWindowAndLayerName(w1, "w1"); + + aura::Window* w2 = aura::test::CreateTestWindowWithId(0, NULL); + SetWindowAndLayerName(w2, "w2"); + + View* host_view2 = new View(); + + // 1) Test that parenting the window to the parent widget last results in a + // correct ordering of child windows and layers. + contents_view->AddChildView(host_view2); + w2->SetProperty(views::kHostViewKey, host_view2); + EXPECT_EQ("w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + parent_window->AddChild(w2); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 2) Test that associating the window and "host" view last results in a + // correct ordering of child windows and layers. + View* host_view1 = new View(); + contents_view->AddChildViewAt(host_view1, 0); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + w1->SetProperty(views::kHostViewKey, host_view1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 3) Test that parenting the "host" view to the parent widget last results + // in a correct ordering of child windows and layers. + contents_view->RemoveChildView(host_view2); + contents_view->AddChildViewAt(host_view2, 0); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +// It is possible to associate a window to a view which has a parent layer +// (other than the widget layer). In this case, the parent layer of the host +// view and the parent layer of the associated window are different. Test that +// the layers and windows are properly reordered in this case. +TEST_F(WindowReordererTest, HostViewParentHasLayer) { + scoped_ptr<Widget> parent(CreateControlWidget(root_window(), + gfx::Rect(0, 0, 100, 100))); + parent->Show(); + aura::Window* parent_window = parent->GetNativeWindow(); + + View* contents_view = new View(); + parent->SetContentsView(contents_view); + + // Create the following view hierarchy. (*) denotes views which paint to a + // layer. + // + // contents_view + // +-- v1 + // +-- v11* + // +-- v12 (attached window) + // +-- v13* + // +--v2* + + View* v1 = new View(); + contents_view->AddChildView(v1); + + View* v11 = new View(); + v11->SetPaintToLayer(true); + v11->layer()->set_name("v11"); + v1->AddChildView(v11); + + scoped_ptr<Widget> w(CreateControlWidget(parent_window, + gfx::Rect(0, 1, 100, 101))); + SetWindowAndLayerName(w->GetNativeView(), "w"); + w->Show(); + + View* v12 = new View(); + v1->AddChildView(v12); + w->GetNativeView()->SetProperty(kHostViewKey, v12); + + View* v13 = new View(); + v13->SetPaintToLayer(true); + v13->layer()->set_name("v13"); + v1->AddChildView(v13); + + View* v2 = new View(); + v2->SetPaintToLayer(true); + v2->layer()->set_name("v2"); + contents_view->AddChildView(v2); + + // Test intial state. + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v11 w v13 v2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // |w|'s layer should be stacked above |v1|'s layer. + v1->SetPaintToLayer(true); + v1->layer()->set_name("v1"); + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v1 w v2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Test moving the host view from one view with a layer to another. + v2->AddChildView(v12); + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v1 v2 w", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +} // namespace +} // namespace views |